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(¤tPosition) == 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