1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2014 - 2016 Piotr Wójcik <chocimier@tlen.pl>
4 * Copyright (C) 2015 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20 
21 #include "QtWebKitFtpListingNetworkReply.h"
22 #include "../../../../core/Utils.h"
23 
24 #include <QtCore/QCoreApplication>
25 #include <QtCore/QMimeDatabase>
26 
27 namespace Otter
28 {
29 
QtWebKitFtpListingNetworkReply(const QNetworkRequest & request,QObject * parent)30 QtWebKitFtpListingNetworkReply::QtWebKitFtpListingNetworkReply(const QNetworkRequest &request, QObject *parent) : ListingNetworkReply(request, parent),
31 	m_ftp(new QFtp(this)),
32 	m_offset(0)
33 {
34 	connect(m_ftp, &QFtp::listInfo, this, &QtWebKitFtpListingNetworkReply::addEntry);
35 	connect(m_ftp, &QFtp::readyRead, this, &QtWebKitFtpListingNetworkReply::processData);
36 	connect(m_ftp, &QFtp::commandFinished, this, &QtWebKitFtpListingNetworkReply::processCommand);
37 	connect(m_ftp, &QFtp::dataTransferProgress, this, &QtWebKitFtpListingNetworkReply::downloadProgress);
38 
39 	m_ftp->connectToHost(request.url().host());
40 }
41 
processCommand(int command,bool isError)42 void QtWebKitFtpListingNetworkReply::processCommand(int command, bool isError)
43 {
44 	Q_UNUSED(command)
45 
46 	if (isError)
47 	{
48 		open(ReadOnly | Unbuffered);
49 
50 		ErrorPageInformation::PageAction reloadAction;
51 		reloadAction.name = QLatin1String("reloadPage");
52 		reloadAction.title = QCoreApplication::translate("utils", "Try Again");
53 		reloadAction.type = ErrorPageInformation::MainAction;
54 
55 		ErrorPageInformation information;
56 		information.url = request().url();
57 		information.description = QStringList(m_ftp->errorString());
58 		information.actions.append(reloadAction);
59 
60 		if (m_ftp->error() == QFtp::HostNotFound)
61 		{
62 			information.type = ErrorPageInformation::ServerNotFoundError;
63 		}
64 		else if (m_ftp->error() == QFtp::ConnectionRefused)
65 		{
66 			information.type = ErrorPageInformation::ConnectionRefusedError;
67 		}
68 		else if (m_ftp->replyCode() > 0)
69 		{
70 			information.title = tr("Network error %1").arg(m_ftp->replyCode());
71 		}
72 
73 		m_content = Utils::createErrorPage(information).toUtf8();
74 
75 		setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QLatin1String("text/html; charset=UTF-8")));
76 		setHeader(QNetworkRequest::ContentLengthHeader, QVariant(m_content.size()));
77 
78 		emit listingError();
79 		emit readyRead();
80 		emit finished();
81 
82 		if (m_ftp->error() != QFtp::NotConnected)
83 		{
84 			m_ftp->close();
85 		}
86 
87 		setError(ContentNotFoundError, tr("Unknown command"));
88 
89 		emit error(ContentNotFoundError);
90 
91 		return;
92 	}
93 
94 	switch (m_ftp->currentCommand())
95 	{
96 		case QFtp::ConnectToHost:
97 			m_ftp->login();
98 
99 			break;
100 		case QFtp::Login:
101 			m_ftp->list(Utils::normalizeUrl(request().url()).path());
102 
103 			break;
104 		case QFtp::List:
105 			if (m_directories.isEmpty() && ((m_files.count() == 1 && m_symlinks.isEmpty() && request().url().path().endsWith(m_files.first().name())) || (m_symlinks.count() == 1 && m_files.isEmpty() && request().url().path().endsWith(m_symlinks.first().name()))))
106 			{
107 				m_ftp->get(Utils::normalizeUrl(request().url()).path());
108 			}
109 			else
110 			{
111 				open(ReadOnly | Unbuffered);
112 
113 				QUrl url(request().url());
114 				QMimeDatabase mimeDatabase;
115 				QVector<ListingEntry> entries;
116 				QVector<NavigationEntry> navigation;
117 				const QVector<QUrlInfo> rawEntries(m_symlinks + m_directories + m_files);
118 
119 				if (url.path().isEmpty())
120 				{
121 					url.setPath(QLatin1String("/"));
122 				}
123 
124 				while (true)
125 				{
126 					const bool isRoot(url.path() == QLatin1String("/"));
127 
128 					url = url.adjusted(QUrl::StripTrailingSlash);
129 
130 					NavigationEntry entry;
131 					entry.name = (isRoot ? url.toString() : url.fileName() + QLatin1Char('/'));
132 					entry.url = url.url();
133 
134 					navigation.prepend(entry);
135 
136 					if (isRoot)
137 					{
138 						break;
139 					}
140 
141 					url = url.adjusted(QUrl::RemoveFilename);
142 				}
143 
144 				for (int i = 0; i < rawEntries.count(); ++i)
145 				{
146 					ListingEntry entry;
147 					entry.name = rawEntries.at(i).name();
148 					entry.url = Utils::normalizeUrl(request().url()).url() + QLatin1Char('/') + rawEntries.at(i).name();
149 					entry.timeModified = rawEntries.at(i).lastModified();
150 					entry.type = (rawEntries.at(i).isSymLink() ? ListingEntry::UnknownType : (rawEntries.at(i).isDir() ? ListingEntry::DirectoryType : ListingEntry::FileType));
151 					entry.size = rawEntries.at(i).size();
152 					entry.isSymlink = rawEntries.at(i).isSymLink();
153 
154 					if (rawEntries.at(i).isSymLink())
155 					{
156 						entry.mimeType = mimeDatabase.mimeTypeForName(QLatin1String("text/uri-list"));
157 					}
158 					else if (rawEntries.at(i).isDir())
159 					{
160 						entry.mimeType = mimeDatabase.mimeTypeForName(QLatin1String("inode/directory"));
161 					}
162 					else
163 					{
164 						entry.mimeType = mimeDatabase.mimeTypeForUrl(request().url().url() + rawEntries.at(i).name());
165 					}
166 
167 					entries.append(entry);
168 				}
169 
170 				m_content = createListing(request().url().toString() + (request().url().path().endsWith(QLatin1Char('/')) ? QChar() : QLatin1Char('/')), navigation, entries);
171 
172 				setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QLatin1String("text/html; charset=UTF-8")));
173 				setHeader(QNetworkRequest::ContentLengthHeader, QVariant(m_content.size()));
174 
175 				emit readyRead();
176 				emit finished();
177 
178 				m_ftp->close();
179 			}
180 
181 			break;
182 		case QFtp::Get:
183 			open(QIODevice::ReadOnly | QIODevice::Unbuffered);
184 			setHeader(QNetworkRequest::ContentLengthHeader, QVariant(m_content.size()));
185 
186 			emit readyRead();
187 			emit finished();
188 
189 			m_ftp->close();
190 
191 			break;
192 		default:
193 			break;
194 	}
195 }
196 
addEntry(const QUrlInfo & entry)197 void QtWebKitFtpListingNetworkReply::addEntry(const QUrlInfo &entry)
198 {
199 	if (entry.isSymLink())
200 	{
201 		m_symlinks.append(entry);
202 	}
203 	else if (entry.isDir())
204 	{
205 		m_directories.append(entry);
206 	}
207 	else
208 	{
209 		m_files.append(entry);
210 	}
211 }
212 
processData()213 void QtWebKitFtpListingNetworkReply::processData()
214 {
215 	m_content += m_ftp->readAll();
216 
217 	emit readyRead();
218 }
219 
abort()220 void QtWebKitFtpListingNetworkReply::abort()
221 {
222 	m_ftp->close();
223 
224 	emit finished();
225 }
226 
bytesAvailable() const227 qint64 QtWebKitFtpListingNetworkReply::bytesAvailable() const
228 {
229 	return (m_content.size() - m_offset);
230 }
231 
readData(char * data,qint64 maxSize)232 qint64 QtWebKitFtpListingNetworkReply::readData(char *data, qint64 maxSize)
233 {
234 	if (m_offset < m_content.size())
235 	{
236 		const qint64 number(qMin(maxSize, (m_content.size() - m_offset)));
237 
238 		memcpy(data, (m_content.constData() + m_offset), static_cast<size_t>(number));
239 
240 		m_offset += number;
241 
242 		return number;
243 	}
244 
245 	return -1;
246 }
247 
isSequential() const248 bool QtWebKitFtpListingNetworkReply::isSequential() const
249 {
250 	return true;
251 }
252 
253 }
254