summaryrefslogtreecommitdiffstats
path: root/WebCore/loader/FTPDirectoryDocument.cpp
diff options
context:
space:
mode:
authorUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
committerUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
commitd8543bb6618c17b12da906afa77d216f58cf4058 (patch)
treec58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/loader/FTPDirectoryDocument.cpp
downloadexternal_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip
external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz
external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2
external/webkit r30707
Diffstat (limited to 'WebCore/loader/FTPDirectoryDocument.cpp')
-rw-r--r--WebCore/loader/FTPDirectoryDocument.cpp493
1 files changed, 493 insertions, 0 deletions
diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp
new file mode 100644
index 0000000..9e1f391
--- /dev/null
+++ b/WebCore/loader/FTPDirectoryDocument.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(FTPDIR)
+#include "FTPDirectoryDocument.h"
+
+#include "CharacterNames.h"
+#include "CString.h"
+#include "HTMLNames.h"
+#include "HTMLTableElement.h"
+#include "HTMLTokenizer.h"
+#include "LocalizedStrings.h"
+#include "Logging.h"
+#include "FTPDirectoryParser.h"
+#include "SegmentedString.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include "Text.h"
+
+// On Win, the threadsafe *_r functions need to be gotten from pthreads.
+#if COMPILER(MSVC) && USE(PTHREADS)
+#include <pthread.h>
+#endif
+
+#if PLATFORM(QT)
+#include <QDateTime>
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+class FTPDirectoryTokenizer : public HTMLTokenizer {
+public:
+ FTPDirectoryTokenizer(HTMLDocument*);
+
+ virtual bool write(const SegmentedString&, bool appendData);
+ virtual void finish();
+
+ virtual bool isWaitingForScripts() const { return false; }
+
+ inline void checkBuffer(int len = 10)
+ {
+ if ((m_dest - m_buffer) > m_size - len) {
+ // Enlarge buffer
+ int newSize = max(m_size * 2, m_size + len);
+ int oldOffset = m_dest - m_buffer;
+ m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar)));
+ m_dest = m_buffer + oldOffset;
+ m_size = newSize;
+ }
+ }
+
+private:
+ // The tokenizer will attempt to load the document template specified via the preference
+ // Failing that, it will fall back and create the basic document which will have a minimal
+ // table for presenting the FTP directory in a useful manner
+ bool loadDocumentTemplate();
+ void createBasicDocument();
+
+ void parseAndAppendOneLine(const String&);
+ void appendEntry(const String& name, const String& size, const String& date, bool isDirectory);
+ PassRefPtr<Element> createTDForFilename(const String&);
+
+ Document* m_doc;
+ RefPtr<HTMLTableElement> m_tableElement;
+
+ bool m_skipLF;
+ bool m_parsedTemplate;
+
+ int m_size;
+ UChar* m_buffer;
+ UChar* m_dest;
+ String m_carryOver;
+
+ ListState m_listState;
+};
+
+FTPDirectoryTokenizer::FTPDirectoryTokenizer(HTMLDocument* doc)
+ : HTMLTokenizer(doc, false)
+ , m_doc(doc)
+ , m_skipLF(false)
+ , m_parsedTemplate(false)
+ , m_size(254)
+ , m_buffer(static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size)))
+ , m_dest(m_buffer)
+{
+}
+
+void FTPDirectoryTokenizer::appendEntry(const String& filename, const String& size, const String& date, bool isDirectory)
+{
+ ExceptionCode ec;
+
+ RefPtr<Element> rowElement = m_tableElement->insertRow(-1, ec);
+ rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec);
+
+ RefPtr<Element> element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec);
+ element->appendChild(new Text(m_doc, String(&noBreakSpace, 1)), ec);
+ if (isDirectory)
+ element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeDirectory", ec);
+ else
+ element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeFile", ec);
+ rowElement->appendChild(element, ec);
+
+ element = createTDForFilename(filename);
+ element->setAttribute("class", "ftpDirectoryFileName", ec);
+ rowElement->appendChild(element, ec);
+
+ element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec);
+ element->appendChild(new Text(m_doc, date), ec);
+ element->setAttribute("class", "ftpDirectoryFileDate", ec);
+ rowElement->appendChild(element, ec);
+
+ element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec);
+ element->appendChild(new Text(m_doc, size), ec);
+ element->setAttribute("class", "ftpDirectoryFileSize", ec);
+ rowElement->appendChild(element, ec);
+}
+
+PassRefPtr<Element> FTPDirectoryTokenizer::createTDForFilename(const String& filename)
+{
+ ExceptionCode ec;
+
+ String fullURL = m_doc->baseURL().string();
+ if (fullURL[fullURL.length() - 1] == '/')
+ fullURL.append(filename);
+ else
+ fullURL.append("/" + filename);
+
+ RefPtr<Element> anchorElement = m_doc->createElementNS(xhtmlNamespaceURI, "a", ec);
+ anchorElement->setAttribute("href", fullURL, ec);
+ anchorElement->appendChild(new Text(m_doc, filename), ec);
+
+ RefPtr<Element> tdElement = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec);
+ tdElement->appendChild(anchorElement, ec);
+
+ return tdElement.release();
+}
+
+static String processFilesizeString(const String& size, bool isDirectory)
+{
+ if (isDirectory)
+ return "--";
+
+ bool valid;
+ int64_t bytes = size.toUInt64(&valid);
+ if (!valid)
+ return unknownFileSizeText();
+
+ if (bytes < 1000000)
+ return String::format("%.2f KB", static_cast<float>(bytes)/1000);
+
+ if (bytes < 1000000000)
+ return String::format("%.2f MB", static_cast<float>(bytes)/1000000);
+
+ return String::format("%.2f GB", static_cast<float>(bytes)/1000000000);
+}
+
+static bool wasLastDayOfMonth(int year, int month, int day)
+{
+ static int lastDays[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ if (month < 0 || month > 11)
+ return false;
+
+ if (month == 2) {
+ if (year % 4 == 0 && (year % 100 || year % 400 == 0)) {
+ if (day == 29)
+ return true;
+ return false;
+ }
+
+ if (day == 28)
+ return true;
+ return false;
+ }
+
+ return lastDays[month] == day;
+}
+
+#if PLATFORM(QT)
+
+/*!
+ Replacement for localtime_r() which is not available on MinGW.
+
+ We use this on all of Qt's platforms for portability.
+ */
+struct tm gmtimeQt(const QDateTime &input)
+{
+ tm result;
+
+ const QDate date(input.date());
+ result.tm_year = date.year() - 1900;
+ result.tm_mon = date.month();
+ result.tm_mday = date.day();
+ result.tm_wday = date.dayOfWeek();
+ result.tm_yday = date.dayOfYear();
+
+ const QTime time(input.time());
+ result.tm_sec = time.second();
+ result.tm_min = time.minute();
+ result.tm_hour = time.hour();
+
+ return result;
+}
+
+static struct tm *localTimeQt(const time_t *const timep, struct tm *result)
+{
+ const QDateTime dt(QDateTime::fromTime_t(*timep));
+ *result = WebCore::gmtimeQt(dt.toLocalTime());
+ return result;
+}
+
+#define localtime_r(x, y) localTimeQt(x, y)
+#endif
+
+static String processFileDateString(const FTPTime& fileTime)
+{
+ // FIXME: Need to localize this string?
+
+ String timeOfDay;
+
+ if (!(fileTime.tm_hour == 0 && fileTime.tm_min == 0 && fileTime.tm_sec == 0)) {
+ int hour = fileTime.tm_hour;
+ ASSERT(hour >= 0 && hour < 24);
+
+ if (hour < 12) {
+ if (hour == 0)
+ hour = 12;
+ timeOfDay = String::format(", %i:%02i AM", hour, fileTime.tm_min);
+ } else {
+ hour = hour - 12;
+ if (hour == 0)
+ hour = 12;
+ timeOfDay = String::format(", %i:%02i PM", hour, fileTime.tm_min);
+ }
+ }
+
+ // If it was today or yesterday, lets just do that - but we have to compare to the current time
+ struct tm now;
+ time_t now_t = time(NULL);
+ localtime_r(&now_t, &now);
+
+ // localtime does "year = current year - 1900", compensate for that for readability and comparison purposes
+ now.tm_year += 1900;
+
+ if (fileTime.tm_year == now.tm_year) {
+ if (fileTime.tm_mon == now.tm_mon) {
+ if (fileTime.tm_mday == now.tm_mday)
+ return "Today" + timeOfDay;
+ if (fileTime.tm_mday == now.tm_mday - 1)
+ return "Yesterday" + timeOfDay;
+ }
+
+ if (now.tm_mday == 1 && (now.tm_mon == fileTime.tm_mon + 1 || now.tm_mon == 0 && fileTime.tm_mon == 11) &&
+ wasLastDayOfMonth(fileTime.tm_year, fileTime.tm_mon, fileTime.tm_mday))
+ return "Yesterday" + timeOfDay;
+ }
+
+ if (fileTime.tm_year == now.tm_year - 1 && fileTime.tm_mon == 12 && fileTime.tm_mday == 31 && now.tm_mon == 1 && now.tm_mday == 1)
+ return "Yesterday" + timeOfDay;
+
+ static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" };
+
+ int month = fileTime.tm_mon;
+ if (month < 0 || month > 11)
+ month = 12;
+
+ String dateString;
+
+ if (fileTime.tm_year > -1)
+ dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, fileTime.tm_year);
+ else
+ dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, now.tm_year);
+
+ return dateString + timeOfDay;
+}
+
+void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine)
+{
+ ListResult result;
+
+ FTPEntryType typeResult = parseOneFTPLine(inputLine.latin1().data(), m_listState, result);
+
+ // FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases
+ if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry)
+ return;
+
+ String filename(result.filename, result.filenameLength);
+ if (result.type == FTPDirectoryEntry) {
+ filename.append("/");
+
+ // We have no interest in linking to "current directory"
+ if (filename == "./")
+ return;
+ }
+
+ LOG(FTP, "Appending entry - %s, %s", filename.ascii().data(), result.fileSize.ascii().data());
+
+ appendEntry(filename, processFilesizeString(result.fileSize, result.type == FTPDirectoryEntry), processFileDateString(result.modifiedTime), result.type == FTPDirectoryEntry);
+}
+
+bool FTPDirectoryTokenizer::loadDocumentTemplate()
+{
+ static RefPtr<SharedBuffer> templateDocumentData;
+ // FIXME: Instead of storing the data, we'd rather actually parse the template data into the template Document once,
+ // store that document, then "copy" it whenever we get an FTP directory listing. There are complexities with this
+ // approach that make it worth putting this off.
+
+ if (!templateDocumentData) {
+ Settings* settings = m_doc->settings();
+ if (settings)
+ templateDocumentData = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath());
+ if (templateDocumentData)
+ LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", templateDocumentData->size());
+ }
+
+ if (!templateDocumentData) {
+ LOG_ERROR("Could not load templateData");
+ return false;
+ }
+
+ // Tokenize the template as an HTML document synchronously
+ setForceSynchronous(true);
+ HTMLTokenizer::write(String(templateDocumentData->data(), templateDocumentData->size()), true);
+ setForceSynchronous(false);
+
+ RefPtr<Element> tableElement = m_doc->getElementById("ftpDirectoryTable");
+ if (!tableElement)
+ LOG_ERROR("Unable to find element by id \"ftpDirectoryTable\" in the template document.");
+ else if (!tableElement->hasTagName(tableTag))
+ LOG_ERROR("Element of id \"ftpDirectoryTable\" is not a table element");
+ else
+ m_tableElement = static_cast<HTMLTableElement*>(tableElement.get());
+
+ // Bail if we found the table element
+ if (m_tableElement)
+ return true;
+
+ // Otherwise create one manually
+ ExceptionCode ec;
+ tableElement = m_doc->createElementNS(xhtmlNamespaceURI, "table", ec);
+ m_tableElement = static_cast<HTMLTableElement*>(tableElement.get());
+ m_tableElement->setAttribute("id", "ftpDirectoryTable", ec);
+
+ // If we didn't find the table element, lets try to append our own to the body
+ // If that fails for some reason, cram it on the end of the document as a last
+ // ditch effort
+ if (Element* body = m_doc->body())
+ body->appendChild(m_tableElement, ec);
+ else
+ m_doc->appendChild(m_tableElement, ec);
+
+ return true;
+}
+
+void FTPDirectoryTokenizer::createBasicDocument()
+{
+ LOG(FTP, "Creating a basic FTP document structure as no template was loaded");
+
+ // FIXME: Make this "basic document" more acceptable
+
+ ExceptionCode ec;
+
+ RefPtr<Element> bodyElement = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec);
+
+ m_doc->appendChild(bodyElement, ec);
+
+ RefPtr<Element> tableElement = m_doc->createElementNS(xhtmlNamespaceURI, "table", ec);
+ m_tableElement = static_cast<HTMLTableElement*>(tableElement.get());
+ m_tableElement->setAttribute("id", "ftpDirectoryTable", ec);
+
+ bodyElement->appendChild(m_tableElement, ec);
+}
+
+bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool appendData)
+{
+ // Make sure we have the table element to append to by loading the template set in the pref, or
+ // creating a very basic document with the appropriate table
+ if (!m_tableElement) {
+ if (!loadDocumentTemplate())
+ createBasicDocument();
+ ASSERT(m_tableElement);
+ }
+
+ bool foundNewLine = false;
+
+ m_dest = m_buffer;
+ SegmentedString str = s;
+ while (!str.isEmpty()) {
+ UChar c = *str;
+
+ if (c == '\r') {
+ *m_dest++ = '\n';
+ foundNewLine = true;
+ // possibly skip an LF in the case of an CRLF sequence
+ m_skipLF = true;
+ } else if (c == '\n') {
+ if (!m_skipLF)
+ *m_dest++ = c;
+ else
+ m_skipLF = false;
+ } else {
+ *m_dest++ = c;
+ m_skipLF = false;
+ }
+
+ str.advance();
+
+ // Maybe enlarge the buffer
+ checkBuffer();
+ }
+
+ if (!foundNewLine) {
+ m_dest = m_buffer;
+ return false;
+ }
+
+ UChar* start = m_buffer;
+ UChar* cursor = start;
+
+ while (cursor < m_dest) {
+ if (*cursor == '\n') {
+ m_carryOver.append(String(start, cursor - start));
+ LOG(FTP, "%s", m_carryOver.ascii().data());
+ parseAndAppendOneLine(m_carryOver);
+ m_carryOver = String();
+
+ start = ++cursor;
+ } else
+ cursor++;
+ }
+
+ // Copy the partial line we have left to the carryover buffer
+ if (cursor - start > 1)
+ m_carryOver.append(String(start, cursor - start - 1));
+
+ return false;
+}
+
+void FTPDirectoryTokenizer::finish()
+{
+ // Possible the last line in the listing had no newline, so try to parse it now
+ if (!m_carryOver.isEmpty()) {
+ parseAndAppendOneLine(m_carryOver);
+ m_carryOver = String();
+ }
+
+ m_tableElement = 0;
+ fastFree(m_buffer);
+
+ HTMLTokenizer::finish();
+}
+
+FTPDirectoryDocument::FTPDirectoryDocument(DOMImplementation* implementation, Frame* frame)
+ : HTMLDocument(implementation, frame)
+{
+#ifndef NDEBUG
+ LogFTP.state = WTFLogChannelOn;
+#endif
+}
+
+Tokenizer* FTPDirectoryDocument::createTokenizer()
+{
+ return new FTPDirectoryTokenizer(this);
+}
+
+}
+
+#endif // ENABLE(FTPDIR)