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 using namespace BPrivate::Network; 20 21 22 BFileRequest::BFileRequest(const BUrl& url, BDataIO* output, 23 BUrlProtocolListener* listener, BUrlContext* context) 24 : 25 BUrlRequest(url, output, listener, context, "BUrlProtocol.File", "file"), 26 fResult() 27 { 28 fUrl.UrlDecode(true); 29 } 30 31 32 BFileRequest::~BFileRequest() 33 { 34 status_t status = Stop(); 35 if (status == B_OK) 36 wait_for_thread(fThreadId, &status); 37 } 38 39 40 const BUrlResult& 41 BFileRequest::Result() const 42 { 43 return fResult; 44 } 45 46 47 status_t 48 BFileRequest::_ProtocolLoop() 49 { 50 BNode node(fUrl.Path().String()); 51 52 if (node.IsSymLink()) { 53 // Traverse the symlink and start over 54 BEntry entry(fUrl.Path().String(), true); 55 node = BNode(&entry); 56 } 57 58 ssize_t transferredSize = 0; 59 if (node.IsFile()) { 60 BFile file(fUrl.Path().String(), B_READ_ONLY); 61 status_t error = file.InitCheck(); 62 if (error != B_OK) 63 return error; 64 65 BNodeInfo info(&file); 66 char mimeType[B_MIME_TYPE_LENGTH + 1]; 67 if (info.GetType(mimeType) != B_OK) 68 update_mime_info(fUrl.Path().String(), false, true, false); 69 if (info.GetType(mimeType) == B_OK) 70 fResult.SetContentType(mimeType); 71 72 // Send all notifications to listener, if any 73 if (fListener != NULL) 74 fListener->ConnectionOpened(this); 75 76 off_t size = 0; 77 error = file.GetSize(&size); 78 if (error != B_OK) 79 return error; 80 fResult.SetLength(size); 81 82 if (fListener != NULL) 83 fListener->HeadersReceived(this); 84 85 if (fOutput != NULL) { 86 ssize_t chunkSize = 0; 87 char chunk[4096]; 88 while (!fQuit) { 89 chunkSize = file.Read(chunk, sizeof(chunk)); 90 if (chunkSize > 0) { 91 size_t written = 0; 92 error = fOutput->WriteExactly(chunk, chunkSize, &written); 93 if (fListener != NULL && written > 0) 94 fListener->BytesWritten(this, written); 95 if (error != B_OK) 96 return error; 97 transferredSize += chunkSize; 98 if (fListener != NULL) 99 fListener->DownloadProgress(this, transferredSize, 100 size); 101 } else 102 break; 103 } 104 if (fQuit) 105 return B_INTERRUPTED; 106 // Return error if we didn't transfer everything 107 if (transferredSize != size) { 108 if (chunkSize < 0) 109 return (status_t)chunkSize; 110 else 111 return B_IO_ERROR; 112 } 113 } 114 115 return B_OK; 116 } 117 118 node_ref ref; 119 status_t error = node.GetNodeRef(&ref); 120 121 // Stop here, and don't hit the assert below, if the file doesn't exist. 122 if (error != B_OK) 123 return error; 124 125 assert(node.IsDirectory()); 126 BDirectory directory(&ref); 127 128 fResult.SetContentType("application/x-ftp-directory; charset=utf-8"); 129 // This tells WebKit to use its FTP directory rendering code. 130 131 if (fListener != NULL) { 132 fListener->ConnectionOpened(this); 133 fListener->HeadersReceived(this); 134 } 135 136 if (fOutput != NULL) { 137 // Add a parent directory entry. 138 size_t written = 0; 139 error = fOutput->WriteExactly("+/,\t..\r\n", 8, &written); 140 if (fListener != NULL && written > 0) 141 fListener->BytesWritten(this, written); 142 if (error != B_OK) 143 return error; 144 transferredSize += written; 145 if (fListener != NULL) 146 fListener->DownloadProgress(this, transferredSize, 0); 147 148 char name[B_FILE_NAME_LENGTH]; 149 BEntry entry; 150 while (!fQuit && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 151 // We read directories using the EPLF (Easily Parsed List Format) 152 // This happens to be one of the formats that WebKit can understand, 153 // and it is not too hard to parse or generate. 154 // http://tools.ietf.org/html/draft-bernstein-eplf-02 155 BString eplf("+"); 156 if (entry.IsFile() || entry.IsSymLink()) { 157 eplf += "r,"; 158 off_t fileSize; 159 if (entry.GetSize(&fileSize) == B_OK) 160 eplf << "s" << fileSize << ","; 161 } else if (entry.IsDirectory()) 162 eplf += "/,"; 163 164 time_t modification; 165 if (entry.GetModificationTime(&modification) == B_OK) 166 eplf << "m" << modification << ","; 167 168 mode_t permissions; 169 if (entry.GetPermissions(&permissions) == B_OK) 170 eplf << "up" << BString().SetToFormat("%03o", permissions) << ","; 171 172 node_ref ref; 173 if (entry.GetNodeRef(&ref) == B_OK) 174 eplf << "i" << ref.device << "." << ref.node << ","; 175 176 entry.GetName(name); 177 eplf << "\t" << name << "\r\n"; 178 size_t written = 0; 179 error = fOutput->WriteExactly(eplf.String(), eplf.Length(), 180 &written); 181 if (fListener != NULL && written > 0) 182 fListener->BytesWritten(this, written); 183 if (error != B_OK) 184 return error; 185 transferredSize += written; 186 if (fListener != NULL) 187 fListener->DownloadProgress(this, transferredSize, 0); 188 } 189 190 if (!fQuit) 191 fResult.SetLength(transferredSize); 192 } 193 194 return fQuit ? B_INTERRUPTED : B_OK; 195 } 196