* Copyright 2013-2014 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/
#include <assert.h>
#include <stdlib.h>
#include <Directory.h>
#include <File.h>
#include <FileRequest.h>
#include <NodeInfo.h>
#include <Path.h>
using namespace BPrivate::Network;
BFileRequest::BFileRequest(const BUrl& url, BDataIO* output,
BUrlProtocolListener* listener, BUrlContext* context)
:
BUrlRequest(url, output, listener, context, "BUrlProtocol.File", "file"),
fResult()
{
}
BFileRequest::~BFileRequest()
{
status_t status = Stop();
if (status == B_OK)
wait_for_thread(fThreadId, &status);
}
const BUrlResult&
BFileRequest::Result() const
{
return fResult;
}
status_t
BFileRequest::_ProtocolLoop()
{
BNode node(fUrl.Path().String());
if (node.IsSymLink()) {
BEntry entry(fUrl.Path().String(), true);
node = BNode(&entry);
}
ssize_t transferredSize = 0;
if (node.IsFile()) {
BFile file(fUrl.Path().String(), B_READ_ONLY);
status_t error = file.InitCheck();
if (error != B_OK)
return error;
BNodeInfo info(&file);
char mimeType[B_MIME_TYPE_LENGTH + 1];
if (info.GetType(mimeType) != B_OK)
update_mime_info(fUrl.Path().String(), false, true, false);
if (info.GetType(mimeType) == B_OK)
fResult.SetContentType(mimeType);
if (fListener != NULL)
fListener->ConnectionOpened(this);
off_t size = 0;
error = file.GetSize(&size);
if (error != B_OK)
return error;
fResult.SetLength(size);
if (fListener != NULL)
fListener->HeadersReceived(this);
if (fOutput != NULL) {
ssize_t chunkSize = 0;
char chunk[4096];
while (!fQuit) {
chunkSize = file.Read(chunk, sizeof(chunk));
if (chunkSize > 0) {
size_t written = 0;
error = fOutput->WriteExactly(chunk, chunkSize, &written);
if (fListener != NULL && written > 0)
fListener->BytesWritten(this, written);
if (error != B_OK)
return error;
transferredSize += chunkSize;
if (fListener != NULL)
fListener->DownloadProgress(this, transferredSize,
size);
} else
break;
}
if (fQuit)
return B_INTERRUPTED;
if (transferredSize != size) {
if (chunkSize < 0)
return (status_t)chunkSize;
else
return B_IO_ERROR;
}
}
return B_OK;
}
node_ref ref;
status_t error = node.GetNodeRef(&ref);
if (error != B_OK)
return error;
assert(node.IsDirectory());
BDirectory directory(&ref);
fResult.SetContentType("application/x-ftp-directory; charset=utf-8");
if (fListener != NULL) {
fListener->ConnectionOpened(this);
fListener->HeadersReceived(this);
}
if (fOutput != NULL) {
size_t written = 0;
error = fOutput->WriteExactly("+/,\t..\r\n", 8, &written);
if (fListener != NULL && written > 0)
fListener->BytesWritten(this, written);
if (error != B_OK)
return error;
transferredSize += written;
if (fListener != NULL)
fListener->DownloadProgress(this, transferredSize, 0);
char name[B_FILE_NAME_LENGTH];
BEntry entry;
while (!fQuit && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) {
BString eplf("+");
if (entry.IsFile() || entry.IsSymLink()) {
eplf += "r,";
off_t fileSize;
if (entry.GetSize(&fileSize) == B_OK)
eplf << "s" << fileSize << ",";
} else if (entry.IsDirectory())
eplf += "/,";
time_t modification;
if (entry.GetModificationTime(&modification) == B_OK)
eplf << "m" << modification << ",";
mode_t permissions;
if (entry.GetPermissions(&permissions) == B_OK)
eplf << "up" << BString().SetToFormat("%03o", permissions) << ",";
node_ref ref;
if (entry.GetNodeRef(&ref) == B_OK)
eplf << "i" << ref.device << "." << ref.node << ",";
entry.GetName(name);
eplf << "\t" << name << "\r\n";
size_t written = 0;
error = fOutput->WriteExactly(eplf.String(), eplf.Length(),
&written);
if (fListener != NULL && written > 0)
fListener->BytesWritten(this, written);
if (error != B_OK)
return error;
transferredSize += written;
if (fListener != NULL)
fListener->DownloadProgress(this, transferredSize, 0);
}
if (!fQuit)
fResult.SetLength(transferredSize);
}
return fQuit ? B_INTERRUPTED : B_OK;
}