2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / file-downloader.cpp
blobad9d23a4db690ad6a9697e2e35cd942d3f6d22b5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * file-downloader.cpp: File Downloader class.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
15 #include <config.h>
17 #include <glib/gstdio.h>
18 #include <fcntl.h>
19 #include <errno.h>
21 #include "file-downloader.h"
22 #include "zip/unzip.h"
23 #include "utils.h"
24 #include "error.h"
26 //TODO: Move all the zip related semantics in here to clean up downloader.cpp
28 FileDownloader::FileDownloader (Downloader *dl) : InternalDownloader (dl, Type::FILEDOWNLOADER)
30 filename = NULL;
31 unzipdir = NULL;
32 uri = NULL;
34 unzipped = false;
35 unlinkit = false;
38 FileDownloader::~FileDownloader ()
40 CleanupUnzipDir ();
42 if (filename) {
43 if (unlinkit)
44 unlink (filename);
45 g_free (filename);
49 void
50 FileDownloader::CleanupUnzipDir ()
52 if (!unzipdir)
53 return;
55 RemoveDir (unzipdir);
56 g_free (unzipdir);
57 unzipped = false;
58 unzipdir = NULL;
61 bool
62 FileDownloader::DownloadedFileIsZipped ()
64 unzFile zipfile;
66 if (!filename)
67 return false;
69 if (!(zipfile = unzOpen (filename)))
70 return false;
72 unzClose (zipfile);
74 return true;
77 char *
78 FileDownloader::GetResponseText (const char *partname, gint64 *size)
80 TextStream *stream;
81 char buffer[4096];
82 GByteArray *buf;
83 struct stat st;
84 ssize_t nread;
85 char *data;
86 char *path;
88 if (!(path = GetDownloadedFilename (partname)))
89 return NULL;
91 if (g_stat (path, &st) == -1) {
92 g_free (path);
93 return NULL;
96 if (st.st_size > 0) {
97 stream = new TextStream ();
99 if (!stream->OpenFile (path, true)) {
100 delete stream;
101 g_free (path);
102 return NULL;
105 g_free (path);
107 buf = g_byte_array_new ();
108 while ((nread = stream->Read (buffer, sizeof (buffer))) > 0)
109 g_byte_array_append (buf, (const guint8 *) buffer, nread);
111 *size = buf->len;
113 g_byte_array_append (buf, (const guint8 *) "", 1);
114 data = (char *) buf->data;
116 g_byte_array_free (buf, false);
117 delete stream;
118 } else {
119 data = g_strdup ("");
120 *size = 0;
123 return data;
126 const char *
127 FileDownloader::GetDownloadedFile ()
129 return filename;
132 char *
133 FileDownloader::GetDownloadedFilename (const char *partname)
135 char *dirname, *path, *part;
136 unzFile zipfile;
137 struct stat st;
138 int rv, fd;
140 if (!filename)
141 return NULL;
143 if (!partname || !partname[0])
144 return g_strdup (filename);
146 if (!DownloadedFileIsZipped ())
147 return NULL;
149 if (!unzipdir && !(unzipdir = CreateTempDir (filename)))
150 return NULL;
152 part = g_ascii_strdown (partname, -1);
153 path = g_build_filename (unzipdir, part, NULL);
154 if ((rv = g_stat (path, &st)) == -1 && errno == ENOENT) {
155 if (strchr (part, '/') != NULL) {
156 // create the directory path
157 dirname = g_path_get_dirname (path);
158 rv = g_mkdir_with_parents (dirname, 0700);
159 g_free (dirname);
161 if (rv == -1 && errno != EEXIST)
162 goto exception1;
165 // open the zip archive...
166 if (!(zipfile = unzOpen (filename)))
167 goto exception1;
169 // locate the file we want to extract... (2 = case-insensitive)
170 if (unzLocateFile (zipfile, partname, 2) != UNZ_OK)
171 goto exception2;
173 // open the requested part within the zip file
174 if (unzOpenCurrentFile (zipfile) != UNZ_OK)
175 goto exception2;
177 // open the output file
178 if ((fd = g_open (path, O_CREAT | O_WRONLY | O_TRUNC, 0600)) == -1)
179 goto exception3;
181 // extract the file from the zip archive... (closes the fd on success and fail)
182 if (!ExtractFile (zipfile, fd))
183 goto exception3;
185 unzCloseCurrentFile (zipfile);
186 unzClose (zipfile);
187 } else if (rv == -1) {
188 // irrecoverable error
189 goto exception0;
192 g_free (part);
194 return path;
196 exception3:
198 unzCloseCurrentFile (zipfile);
200 exception2:
202 unzClose (zipfile);
204 exception1:
206 g_free (part);
208 exception0:
210 g_free (path);
212 return NULL;
215 const char *
216 FileDownloader::GetUnzippedPath ()
218 char filename[256], *p;
219 unz_file_info info;
220 const char *name;
221 GString *path;
222 unzFile zip;
223 size_t len;
224 int fd;
226 if (!this->filename)
227 return NULL;
229 if (!DownloadedFileIsZipped ())
230 return this->filename;
232 if (!unzipdir && !(unzipdir = CreateTempDir (this->filename)))
233 return NULL;
235 if (unzipped)
236 return unzipdir;
238 // open the zip archive...
239 if (!(zip = unzOpen (this->filename)))
240 return NULL;
242 path = g_string_new (unzipdir);
243 g_string_append_c (path, G_DIR_SEPARATOR);
244 len = path->len;
246 unzipped = true;
248 // extract all the parts
249 do {
250 if (unzOpenCurrentFile (zip) != UNZ_OK)
251 break;
253 unzGetCurrentFileInfo (zip, &info, filename, sizeof (filename),
254 NULL, 0, NULL, 0);
256 // convert filename to lowercase
257 for (p = filename; *p; p++) {
258 if (*p >= 'A' && *p <= 'Z')
259 *p += 0x20;
262 if ((name = strrchr (filename, '/'))) {
263 // make sure the full directory path exists, if not create it
264 g_string_append_len (path, filename, name - filename);
265 g_mkdir_with_parents (path->str, 0700);
266 g_string_append (path, name);
267 } else {
268 g_string_append (path, filename);
271 if ((fd = g_open (path->str, O_WRONLY | O_CREAT | O_EXCL, 0600)) != -1) {
272 if (!ExtractFile (zip, fd))
273 unzipped = false;
274 } else if (errno != EEXIST) {
275 unzipped = false;
278 g_string_truncate (path, len);
279 unzCloseCurrentFile (zip);
280 } while (unzGoToNextFile (zip) == UNZ_OK);
282 g_string_free (path, true);
283 unzClose (zip);
285 return unzipdir;
288 void
289 FileDownloader::Open (const char *verb, const char *uri)
291 CleanupUnzipDir ();
293 if (filename) {
294 if (unlinkit)
295 unlink (filename);
296 g_free (filename);
299 unlinkit = false;
300 unzipped = false;
302 filename = NULL;
304 dl->InternalOpen (verb, uri);
307 void
308 FileDownloader::Write (void *buf, gint32 offset, gint32 n)
310 dl->InternalWrite (buf, offset, n);