1 /* 2 * Copyright 2013-2014 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Adrien Destugues, pulkomandy@pulkomandy.tk 7 */ 8 9 10 #include <assert.h> 11 #include <stdlib.h> 12 13 #include <Directory.h> 14 #include <File.h> 15 #include <FileRequest.h> 16 #include <NodeInfo.h> 17 #include <Path.h> 18 19 20 #ifndef LIBNETAPI_DEPRECATED 21 using namespace BPrivate::Network; 22 #endif 23 24 25 #ifdef LIBNETAPI_DEPRECATED 26 BFileRequest::BFileRequest(const BUrl& url, BUrlProtocolListener* listener, 27 BUrlContext* context) 28 : 29 BUrlRequest(url, listener, context, "BUrlProtocol.File", "file"), 30 fResult() 31 { 32 fUrl.UrlDecode(true); 33 } 34 35 #else 36 37 BFileRequest::BFileRequest(const BUrl& url, BDataIO* output, 38 BUrlProtocolListener* listener, BUrlContext* context) 39 : 40 BUrlRequest(url, output, listener, context, "BUrlProtocol.File", "file"), 41 fResult() 42 { 43 fUrl.UrlDecode(true); 44 } 45 #endif // LIBNETAPI_DEPRECATED 46 47 48 BFileRequest::~BFileRequest() 49 { 50 status_t status = Stop(); 51 if (status == B_OK) 52 wait_for_thread(fThreadId, &status); 53 } 54 55 56 const BUrlResult& 57 BFileRequest::Result() const 58 { 59 return fResult; 60 } 61 62 63 #ifdef LIBNETAPI_DEPRECATED 64 status_t 65 BFileRequest::_ProtocolLoop() 66 { 67 BNode node(fUrl.Path().String()); 68 69 if (node.IsSymLink()) { 70 // Traverse the symlink and start over 71 BEntry entry(fUrl.Path().String(), true); 72 node = BNode(&entry); 73 } 74 75 ssize_t transferredSize = 0; 76 if (node.IsFile()) { 77 BFile file(fUrl.Path().String(), B_READ_ONLY); 78 status_t error = file.InitCheck(); 79 if (error != B_OK) 80 return error; 81 82 BNodeInfo info(&file); 83 char mimeType[B_MIME_TYPE_LENGTH + 1]; 84 if (info.GetType(mimeType) != B_OK) 85 update_mime_info(fUrl.Path().String(), false, true, false); 86 if (info.GetType(mimeType) == B_OK) 87 fResult.SetContentType(mimeType); 88 89 // Send all notifications to listener, if any 90 if (fListener != NULL) { 91 fListener->ConnectionOpened(this); 92 93 off_t size = 0; 94 error = file.GetSize(&size); 95 if (error != B_OK) 96 return error; 97 fResult.SetLength(size); 98 99 fListener->HeadersReceived(this, fResult); 100 101 ssize_t chunkSize = 0; 102 char chunk[4096]; 103 while (!fQuit) { 104 chunkSize = file.Read(chunk, sizeof(chunk)); 105 if (chunkSize > 0) { 106 fListener->DataReceived(this, chunk, transferredSize, 107 chunkSize); 108 transferredSize += chunkSize; 109 fListener->DownloadProgress(this, transferredSize, size); 110 } else 111 break; 112 } 113 if (fQuit) 114 return B_INTERRUPTED; 115 // Return error if we didn't transfer everything 116 if (transferredSize != size) { 117 if (chunkSize < 0) 118 return (status_t)chunkSize; 119 else 120 return B_IO_ERROR; 121 } 122 } 123 124 return B_OK; 125 } 126 127 node_ref ref; 128 status_t error = node.GetNodeRef(&ref); 129 130 // Stop here, and don't hit the assert below, if the file doesn't exist. 131 if (error != B_OK) 132 return error; 133 134 assert(node.IsDirectory()); 135 BDirectory directory(&ref); 136 137 fResult.SetContentType("application/x-ftp-directory; charset=utf-8"); 138 // This tells WebKit to use its FTP directory rendering code. 139 140 if (fListener != NULL) { 141 fListener->ConnectionOpened(this); 142 fListener->HeadersReceived(this, fResult); 143 144 // Add a parent directory entry. 145 fListener->DataReceived(this, "+/,\t..\r\n", transferredSize, 8); 146 fListener->DownloadProgress(this, transferredSize, 0); 147 transferredSize += 8; 148 } 149 150 char name[B_FILE_NAME_LENGTH]; 151 BEntry entry; 152 while (!fQuit && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 153 // We read directories using the EPLF (Easily Parsed List Format) 154 // This happens to be one of the formats that WebKit can understand, 155 // and it is not too hard to parse or generate. 156 // http://tools.ietf.org/html/draft-bernstein-eplf-02 157 BString eplf("+"); 158 if (entry.IsFile() || entry.IsSymLink()) { 159 eplf += "r,"; 160 off_t fileSize; 161 if (entry.GetSize(&fileSize) == B_OK) 162 eplf << "s" << fileSize << ","; 163 164 } else if (entry.IsDirectory()) 165 eplf += "/,"; 166 167 time_t modification; 168 if (entry.GetModificationTime(&modification) == B_OK) 169 eplf << "m" << modification << ","; 170 171 mode_t permissions; 172 if (entry.GetPermissions(&permissions) == B_OK) 173 eplf << "up" << BString().SetToFormat("%03o", permissions) << ","; 174 175 node_ref ref; 176 if (entry.GetNodeRef(&ref) == B_OK) 177 eplf << "i" << ref.device << "." << ref.node << ","; 178 179 entry.GetName(name); 180 eplf << "\t" << name << "\r\n"; 181 if (fListener != NULL) { 182 fListener->DataReceived(this, eplf.String(), transferredSize, 183 eplf.Length()); 184 fListener->DownloadProgress(this, transferredSize, 0); 185 } 186 transferredSize += eplf.Length(); 187 } 188 189 if (!fQuit) 190 fResult.SetLength(transferredSize); 191 192 return fQuit ? B_INTERRUPTED : B_OK; 193 } 194 195 #else 196 197 status_t 198 BFileRequest::_ProtocolLoop() 199 { 200 BNode node(fUrl.Path().String()); 201 202 if (node.IsSymLink()) { 203 // Traverse the symlink and start over 204 BEntry entry(fUrl.Path().String(), true); 205 node = BNode(&entry); 206 } 207 208 ssize_t transferredSize = 0; 209 if (node.IsFile()) { 210 BFile file(fUrl.Path().String(), B_READ_ONLY); 211 status_t error = file.InitCheck(); 212 if (error != B_OK) 213 return error; 214 215 BNodeInfo info(&file); 216 char mimeType[B_MIME_TYPE_LENGTH + 1]; 217 if (info.GetType(mimeType) != B_OK) 218 update_mime_info(fUrl.Path().String(), false, true, false); 219 if (info.GetType(mimeType) == B_OK) 220 fResult.SetContentType(mimeType); 221 222 // Send all notifications to listener, if any 223 if (fListener != NULL) 224 fListener->ConnectionOpened(this); 225 226 off_t size = 0; 227 error = file.GetSize(&size); 228 if (error != B_OK) 229 return error; 230 fResult.SetLength(size); 231 232 if (fListener != NULL) 233 fListener->HeadersReceived(this); 234 235 if (fOutput != NULL) { 236 ssize_t chunkSize = 0; 237 char chunk[4096]; 238 while (!fQuit) { 239 chunkSize = file.Read(chunk, sizeof(chunk)); 240 if (chunkSize > 0) { 241 size_t written = 0; 242 error = fOutput->WriteExactly(chunk, chunkSize, &written); 243 if (fListener != NULL && written > 0) 244 fListener->BytesWritten(this, written); 245 if (error != B_OK) 246 return error; 247 transferredSize += chunkSize; 248 if (fListener != NULL) 249 fListener->DownloadProgress(this, transferredSize, 250 size); 251 } else 252 break; 253 } 254 if (fQuit) 255 return B_INTERRUPTED; 256 // Return error if we didn't transfer everything 257 if (transferredSize != size) { 258 if (chunkSize < 0) 259 return (status_t)chunkSize; 260 else 261 return B_IO_ERROR; 262 } 263 } 264 265 return B_OK; 266 } 267 268 node_ref ref; 269 status_t error = node.GetNodeRef(&ref); 270 271 // Stop here, and don't hit the assert below, if the file doesn't exist. 272 if (error != B_OK) 273 return error; 274 275 assert(node.IsDirectory()); 276 BDirectory directory(&ref); 277 278 fResult.SetContentType("application/x-ftp-directory; charset=utf-8"); 279 // This tells WebKit to use its FTP directory rendering code. 280 281 if (fListener != NULL) { 282 fListener->ConnectionOpened(this); 283 fListener->HeadersReceived(this); 284 } 285 286 if (fOutput != NULL) { 287 // Add a parent directory entry. 288 size_t written = 0; 289 error = fOutput->WriteExactly("+/,\t..\r\n", 8, &written); 290 if (fListener != NULL && written > 0) 291 fListener->BytesWritten(this, written); 292 if (error != B_OK) 293 return error; 294 transferredSize += written; 295 if (fListener != NULL) 296 fListener->DownloadProgress(this, transferredSize, 0); 297 298 char name[B_FILE_NAME_LENGTH]; 299 BEntry entry; 300 while (!fQuit && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 301 // We read directories using the EPLF (Easily Parsed List Format) 302 // This happens to be one of the formats that WebKit can understand, 303 // and it is not too hard to parse or generate. 304 // http://tools.ietf.org/html/draft-bernstein-eplf-02 305 BString eplf("+"); 306 if (entry.IsFile() || entry.IsSymLink()) { 307 eplf += "r,"; 308 off_t fileSize; 309 if (entry.GetSize(&fileSize) == B_OK) 310 eplf << "s" << fileSize << ","; 311 } else if (entry.IsDirectory()) 312 eplf += "/,"; 313 314 time_t modification; 315 if (entry.GetModificationTime(&modification) == B_OK) 316 eplf << "m" << modification << ","; 317 318 mode_t permissions; 319 if (entry.GetPermissions(&permissions) == B_OK) 320 eplf << "up" << BString().SetToFormat("%03o", permissions) << ","; 321 322 node_ref ref; 323 if (entry.GetNodeRef(&ref) == B_OK) 324 eplf << "i" << ref.device << "." << ref.node << ","; 325 326 entry.GetName(name); 327 eplf << "\t" << name << "\r\n"; 328 size_t written = 0; 329 error = fOutput->WriteExactly(eplf.String(), eplf.Length(), 330 &written); 331 if (fListener != NULL && written > 0) 332 fListener->BytesWritten(this, written); 333 if (error != B_OK) 334 return error; 335 transferredSize += written; 336 if (fListener != NULL) 337 fListener->DownloadProgress(this, transferredSize, 0); 338 } 339 340 if (!fQuit) 341 fResult.SetLength(transferredSize); 342 } 343 344 return fQuit ? B_INTERRUPTED : B_OK; 345 } 346 347 #endif // LIBNETAPI_DEPRECATED 348