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