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
BFileRequest(const BUrl & url,BDataIO * output,BUrlProtocolListener * listener,BUrlContext * context)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
~BFileRequest()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&
Result() const41 BFileRequest::Result() const
42 {
43 return fResult;
44 }
45
46
47 status_t
_ProtocolLoop()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