1/*
2 * Copyright 2011-2021, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler <axeld@pinc-software.de>
7 *		Rene Gollent <rene@gollent.com>
8 *		Oliver Tappe <zooey@hirschkaefer.de>
9 *		Stephan A��mus <superstippi@gmx.de>
10 */
11
12
13#include "FetchFileJob.h"
14
15#include <stdio.h>
16#include <sys/wait.h>
17
18#include <Path.h>
19
20#ifdef HAIKU_TARGET_PLATFORM_HAIKU
21#	include <HttpRequest.h>
22#	include <UrlRequest.h>
23#	include <UrlProtocolRoster.h>
24using namespace BPrivate::Network;
25#endif
26
27#include "FetchUtils.h"
28
29
30namespace BPackageKit {
31
32namespace BPrivate {
33
34
35#ifdef HAIKU_TARGET_PLATFORM_HAIKU
36
37FetchFileJob::FetchFileJob(const BContext& context, const BString& title,
38	const BString& fileURL, const BEntry& targetEntry)
39	:
40	inherited(context, title),
41	fFileURL(fileURL),
42	fTargetEntry(targetEntry),
43	fTargetFile(&targetEntry, B_CREATE_FILE | B_WRITE_ONLY),
44	fError(B_ERROR),
45	fDownloadProgress(0.0)
46{
47}
48
49
50FetchFileJob::~FetchFileJob()
51{
52}
53
54
55float
56FetchFileJob::DownloadProgress() const
57{
58	return fDownloadProgress;
59}
60
61
62const char*
63FetchFileJob::DownloadURL() const
64{
65	return fFileURL.String();
66}
67
68
69const char*
70FetchFileJob::DownloadFileName() const
71{
72	return fTargetEntry.Name();
73}
74
75
76off_t
77FetchFileJob::DownloadBytes() const
78{
79	return fBytes;
80}
81
82
83off_t
84FetchFileJob::DownloadTotalBytes() const
85{
86	return fTotalBytes;
87}
88
89
90status_t
91FetchFileJob::Execute()
92{
93	status_t result = fTargetFile.InitCheck();
94	if (result != B_OK)
95		return result;
96
97	result = FetchUtils::SetFileType(fTargetFile,
98		"application/x-vnd.haiku-package");
99	if (result != B_OK) {
100		fprintf(stderr, "failed to set file type for '%s': %s\n",
101			DownloadFileName(), strerror(result));
102	}
103
104	do {
105		BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fFileURL.String(),
106			&fTargetFile, this);
107		if (request == NULL)
108			return B_BAD_VALUE;
109
110		// Try to resume the download where we left off
111		off_t currentPosition;
112		BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
113		if (http != NULL && fTargetFile.GetSize(&currentPosition) == B_OK
114			&& currentPosition > 0) {
115			http->SetRangeStart(currentPosition);
116			fTargetFile.Seek(0, SEEK_END);
117		}
118
119		thread_id thread = request->Run();
120		wait_for_thread(thread, NULL);
121
122		if (fError != B_IO_ERROR && fError != B_DEV_TIMEOUT && fError != B_OK) {
123			// Something went wrong with the download and it's not just a
124			// timeout. Remove whatever we wrote to the file, since the content
125			// returned by the server was probably not part of the file.
126			fTargetFile.SetSize(currentPosition);
127		}
128	} while (fError == B_IO_ERROR || fError == B_DEV_TIMEOUT);
129
130	if (fError == B_OK) {
131		result = FetchUtils::MarkDownloadComplete(fTargetFile);
132		if (result != B_OK) {
133			fprintf(stderr, "failed to mark download '%s' as complete: %s\n",
134				DownloadFileName(), strerror(result));
135		}
136	}
137
138	return fError;
139}
140
141
142void
143FetchFileJob::DownloadProgress(BUrlRequest*, off_t bytesReceived,
144	off_t bytesTotal)
145{
146	if (bytesTotal != 0) {
147		fBytes = bytesReceived;
148		fTotalBytes = bytesTotal;
149		fDownloadProgress = (float)bytesReceived/bytesTotal;
150		NotifyStateListeners();
151	}
152}
153
154
155void
156FetchFileJob::RequestCompleted(BUrlRequest* request, bool success)
157{
158	fError = request->Status();
159
160	if (success) {
161		const BHttpResult* httpResult = dynamic_cast<const BHttpResult*>
162			(&request->Result());
163		if (httpResult != NULL) {
164			uint16 code = httpResult->StatusCode();
165			uint16 codeClass = BHttpRequest::StatusCodeClass(code);
166
167			switch (codeClass) {
168				case B_HTTP_STATUS_CLASS_CLIENT_ERROR:
169				case B_HTTP_STATUS_CLASS_SERVER_ERROR:
170					fError = B_IO_ERROR;
171					break;
172				default:
173					fError = B_OK;
174					break;
175			}
176			switch (code) {
177				case B_HTTP_STATUS_OK:
178				case B_HTTP_STATUS_PARTIAL_CONTENT:
179					fError = B_OK;
180					break;
181				case B_HTTP_STATUS_REQUEST_TIMEOUT:
182				case B_HTTP_STATUS_GATEWAY_TIMEOUT:
183					fError = B_DEV_TIMEOUT;
184					break;
185				case B_HTTP_STATUS_NOT_IMPLEMENTED:
186					fError = B_NOT_SUPPORTED;
187					break;
188				case B_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:
189					fError = B_UNKNOWN_MIME_TYPE;
190					break;
191				case B_HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE:
192					fError = B_RESULT_NOT_REPRESENTABLE; // alias for ERANGE
193					break;
194				case B_HTTP_STATUS_UNAUTHORIZED:
195					fError = B_PERMISSION_DENIED;
196					break;
197				case B_HTTP_STATUS_FORBIDDEN:
198				case B_HTTP_STATUS_METHOD_NOT_ALLOWED:
199				case B_HTTP_STATUS_NOT_ACCEPTABLE:
200					fError = B_NOT_ALLOWED;
201					break;
202				case B_HTTP_STATUS_NOT_FOUND:
203					fError = B_NAME_NOT_FOUND;
204					break;
205				case B_HTTP_STATUS_BAD_GATEWAY:
206					fError = B_BAD_DATA;
207					break;
208				default:
209					break;
210			}
211		}
212	}
213}
214
215
216void
217FetchFileJob::Cleanup(status_t jobResult)
218{
219	if (jobResult != B_OK)
220		fTargetEntry.Remove();
221}
222
223
224#else // HAIKU_TARGET_PLATFORM_HAIKU
225
226
227FetchFileJob::FetchFileJob(const BContext& context, const BString& title,
228	const BString& fileURL, const BEntry& targetEntry)
229	:
230	inherited(context, title),
231	fFileURL(fileURL),
232	fTargetEntry(targetEntry),
233	fTargetFile(&targetEntry, B_CREATE_FILE | B_WRITE_ONLY),
234	fDownloadProgress(0.0)
235{
236}
237
238
239FetchFileJob::~FetchFileJob()
240{
241}
242
243
244float
245FetchFileJob::DownloadProgress() const
246{
247	return fDownloadProgress;
248}
249
250
251const char*
252FetchFileJob::DownloadURL() const
253{
254	return fFileURL.String();
255}
256
257
258const char*
259FetchFileJob::DownloadFileName() const
260{
261	return fTargetEntry.Name();
262}
263
264
265off_t
266FetchFileJob::DownloadBytes() const
267{
268	return fBytes;
269}
270
271
272off_t
273FetchFileJob::DownloadTotalBytes() const
274{
275	return fTotalBytes;
276}
277
278
279status_t
280FetchFileJob::Execute()
281{
282	return B_UNSUPPORTED;
283}
284
285
286void
287FetchFileJob::Cleanup(status_t jobResult)
288{
289	if (jobResult != B_OK)
290		fTargetEntry.Remove();
291}
292
293
294#endif // HAIKU_TARGET_PLATFORM_HAIKU
295
296}	// namespace BPrivate
297
298}	// namespace BPackageKit
299