xref: /haiku/src/kits/network/libnetservices/FileRequest.cpp (revision 04171cfc5c10c98b9ba3c7233a271f6165cdd36f)
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