xref: /haiku/src/kits/package/FetchFileJob.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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>
24 using namespace BPrivate::Network;
25 #endif
26 
27 #include "FetchUtils.h"
28 
29 
30 namespace BPackageKit {
31 
32 namespace BPrivate {
33 
34 
35 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
36 
37 FetchFileJob::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 
50 FetchFileJob::~FetchFileJob()
51 {
52 }
53 
54 
55 float
56 FetchFileJob::DownloadProgress() const
57 {
58 	return fDownloadProgress;
59 }
60 
61 
62 const char*
63 FetchFileJob::DownloadURL() const
64 {
65 	return fFileURL.String();
66 }
67 
68 
69 const char*
70 FetchFileJob::DownloadFileName() const
71 {
72 	return fTargetEntry.Name();
73 }
74 
75 
76 off_t
77 FetchFileJob::DownloadBytes() const
78 {
79 	return fBytes;
80 }
81 
82 
83 off_t
84 FetchFileJob::DownloadTotalBytes() const
85 {
86 	return fTotalBytes;
87 }
88 
89 
90 status_t
91 FetchFileJob::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 
142 void
143 FetchFileJob::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 
155 void
156 FetchFileJob::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 
216 void
217 FetchFileJob::Cleanup(status_t jobResult)
218 {
219 	if (jobResult != B_OK)
220 		fTargetEntry.Remove();
221 }
222 
223 
224 #else // HAIKU_TARGET_PLATFORM_HAIKU
225 
226 
227 FetchFileJob::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 
239 FetchFileJob::~FetchFileJob()
240 {
241 }
242 
243 
244 float
245 FetchFileJob::DownloadProgress() const
246 {
247 	return fDownloadProgress;
248 }
249 
250 
251 const char*
252 FetchFileJob::DownloadURL() const
253 {
254 	return fFileURL.String();
255 }
256 
257 
258 const char*
259 FetchFileJob::DownloadFileName() const
260 {
261 	return fTargetEntry.Name();
262 }
263 
264 
265 off_t
266 FetchFileJob::DownloadBytes() const
267 {
268 	return fBytes;
269 }
270 
271 
272 off_t
273 FetchFileJob::DownloadTotalBytes() const
274 {
275 	return fTotalBytes;
276 }
277 
278 
279 status_t
280 FetchFileJob::Execute()
281 {
282 	return B_UNSUPPORTED;
283 }
284 
285 
286 void
287 FetchFileJob::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