source: wkhtmltox/trunk/fuentes/src/lib/multipageloader.cc @ 51

Last change on this file since 51 was 51, checked in by mabarracus, 4 years ago

wip

File size: 23.2 KB
Line 
1// -*- mode: c++; tab-width: 4; indent-tabs-mode: t; eval: (progn (c-set-style "stroustrup") (c-set-offset 'innamespace 0)); -*-
2// vi:set ts=4 sts=4 sw=4 noet :
3//
4// Copyright 2010, 2011 wkhtmltopdf authors
5//
6// This file is part of wkhtmltopdf.
7//
8// wkhtmltopdf is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Lesser General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12//
13// wkhtmltopdf is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU Lesser General Public License
19// along with wkhtmltopdf.  If not, see <http://www.gnu.org/licenses/>.
20
21
22#include "multipageloader_p.hh"
23#include <QFile>
24#include <QFileInfo>
25#include <QNetworkCookie>
26#include <QNetworkDiskCache>
27#include <QTimer>
28#include <QUuid>
29#if QT_VERSION >= 0x050000
30#include <QUrlQuery>
31#endif
32
33namespace wkhtmltopdf {
34/*!
35  \file multipageloader.hh
36  \brief Defines the MultiPageLoader class
37*/
38
39/*!
40  \file multipageloader_p.hh
41  \brief Defines the MultiPageLoaderPrivate class
42*/
43
44
45LoaderObject::LoaderObject(QWebPage & p): page(p), skip(false) {};
46
47MyNetworkAccessManager::MyNetworkAccessManager(const settings::LoadPage & s): 
48        disposed(false),
49        settings(s) {
50
51        if ( !s.cacheDir.isEmpty() ){
52                QNetworkDiskCache *cache = new QNetworkDiskCache(this);
53                cache->setCacheDirectory(s.cacheDir);
54                QNetworkAccessManager::setCache(cache);
55        }
56}
57
58void MyNetworkAccessManager::dispose() {
59        disposed = true;
60}
61
62void MyNetworkAccessManager::allow(QString path) {
63        QString x = QFileInfo(path).canonicalFilePath();
64        if (x.isEmpty()) return;
65        allowed.insert(x);
66}
67
68QNetworkReply * MyNetworkAccessManager::createRequest(Operation op, const QNetworkRequest & req, QIODevice * outgoingData) {
69
70        if (disposed)
71        {
72                emit warning("Received createRequest signal on a disposed ResourceObject's NetworkAccessManager. "
73                             "This might be an indication of an iframe taking too long to load.");
74                // Needed to avoid race conditions by spurious network requests
75                // by scripts or iframes taking too long to load.
76                QNetworkRequest r2 = req;
77                r2.setUrl(QUrl("about:blank"));
78                return QNetworkAccessManager::createRequest(op, r2, outgoingData);
79        }
80
81        bool isLocalFileAccess = req.url().scheme().length() <= 1 || req.url().scheme() == "file";
82        if (isLocalFileAccess && settings.blockLocalFileAccess) {
83                bool ok=false;
84                QString path = QFileInfo(req.url().toLocalFile()).canonicalFilePath();
85                QString old = "";
86                while (path != old) {
87                        if (allowed.contains(path)) {
88                                ok=true;
89                                break;
90                        }
91                        old = path;
92                        path = QFileInfo(path).path();
93                }
94                if (!ok) {
95                        QNetworkRequest r2 = req;
96                        emit warning(QString("Blocked access to file %1").arg(QFileInfo(req.url().toLocalFile()).canonicalFilePath()));
97                        r2.setUrl(QUrl("about:blank"));
98                        return QNetworkAccessManager::createRequest(op, r2, outgoingData);
99                }
100        }
101        QNetworkRequest r3 = req;
102        if (settings.repeatCustomHeaders) {
103                typedef QPair<QString, QString> HT;
104                foreach (const HT & j, settings.customHeaders)
105                        r3.setRawHeader(j.first.toLatin1(), j.second.toLatin1());
106        }
107        return QNetworkAccessManager::createRequest(op, r3, outgoingData);
108}
109
110MyNetworkProxyFactory::MyNetworkProxyFactory (QNetworkProxy proxy, QList<QString> bph):
111        bypassHosts(bph),
112        originalProxy(QList<QNetworkProxy>() << proxy),
113        noProxy(QList<QNetworkProxy>() << QNetworkProxy(QNetworkProxy::DefaultProxy)){}
114
115QList<QNetworkProxy> MyNetworkProxyFactory::queryProxy (const QNetworkProxyQuery & query) {
116        QString host = query.url().host();
117        foreach (const QString & bypassHost, bypassHosts) {
118                if (host.compare(bypassHost, Qt::CaseInsensitive) == 0)
119                        return noProxy;
120        }
121        return originalProxy;
122}
123
124MyQWebPage::MyQWebPage(ResourceObject & res): resource(res) {}
125
126void MyQWebPage::javaScriptAlert(QWebFrame *, const QString & msg) {
127        resource.warning(QString("Javascript alert: %1").arg(msg));
128}
129
130bool MyQWebPage::javaScriptConfirm(QWebFrame *, const QString & msg) {
131        resource.warning(QString("Javascript confirm: %1 (answered yes)").arg(msg));
132        return true;
133}
134
135bool MyQWebPage::javaScriptPrompt(QWebFrame *, const QString & msg, const QString & defaultValue, QString * result) {
136        resource.warning(QString("Javascript prompt: %1 (answered %2)").arg(msg,defaultValue));
137        result = (QString*)&defaultValue;
138        Q_UNUSED(result);
139        return true;
140}
141
142void MyQWebPage::javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) {
143        if (resource.settings.debugJavascript)
144                resource.warning(QString("%1:%2 %3").arg(sourceID).arg(lineNumber).arg(message));
145}
146
147bool MyQWebPage::shouldInterruptJavaScript() {
148        if (resource.settings.stopSlowScripts) {
149                resource.warning("A slow script was stopped");
150                return true;
151        }
152        return false;
153}
154
155ResourceObject::ResourceObject(MultiPageLoaderPrivate & mpl, const QUrl & u, const settings::LoadPage & s):
156        networkAccessManager(s),
157        url(u),
158        loginTry(0),
159        progress(0),
160        finished(false),
161        signalPrint(false),
162        multiPageLoader(mpl),
163        webPage(*this),
164        lo(webPage),
165        httpErrorCode(0),
166        settings(s) {
167
168        connect(&networkAccessManager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator *)),this,
169                SLOT(handleAuthenticationRequired(QNetworkReply *, QAuthenticator *)));
170        foreach (const QString & path, s.allowed)
171                networkAccessManager.allow(path);
172        if (url.scheme() == "file")
173                networkAccessManager.allow(url.toLocalFile());
174
175        connect(&webPage, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
176        connect(&webPage, SIGNAL(loadProgress(int)), this, SLOT(loadProgress(int)));
177        connect(&webPage, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
178        connect(&webPage, SIGNAL(printRequested(QWebFrame*)), this, SLOT(printRequested(QWebFrame*)));
179
180        //If some ssl error occurs we want sslErrors to be called, so the we can ignore it
181        connect(&networkAccessManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),this,
182                SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&)));
183
184        connect(&networkAccessManager, SIGNAL(finished (QNetworkReply *)),
185                        this, SLOT(amfinished (QNetworkReply *) ) );
186
187        connect(&networkAccessManager, SIGNAL(warning(const QString &)),
188                        this, SLOT(warning(const QString &)));
189
190        networkAccessManager.setCookieJar(multiPageLoader.cookieJar);
191
192        //If we must use a proxy, create a host of objects
193        if (!settings.proxy.host.isEmpty()) {
194                QNetworkProxy proxy;
195                proxy.setHostName(settings.proxy.host);
196                proxy.setPort(settings.proxy.port);
197                proxy.setType(settings.proxy.type);
198                // to retrieve a web page, it's not needed to use a fully transparent
199                // http proxy. Moreover, the CONNECT() method is frequently disabled
200                // by proxies administrators.
201                if (settings.proxy.type == QNetworkProxy::HttpProxy)
202                        proxy.setCapabilities(QNetworkProxy::CachingCapability |
203                                              QNetworkProxy::TunnelingCapability);
204                if (!settings.proxy.user.isEmpty())
205                        proxy.setUser(settings.proxy.user);
206                if (!settings.proxy.password.isEmpty())
207                        proxy.setPassword(settings.proxy.password);
208                if (!settings.bypassProxyForHosts.isEmpty())
209                        networkAccessManager.setProxyFactory(
210                                new MyNetworkProxyFactory(proxy, settings.bypassProxyForHosts));
211                else
212                        networkAccessManager.setProxy(proxy);
213        }
214
215        webPage.setNetworkAccessManager(&networkAccessManager);
216        webPage.mainFrame()->setZoomFactor(settings.zoomFactor);
217}
218
219/*!
220 * Once loading starting, this is called
221 */
222void ResourceObject::loadStarted() {
223        if (finished == true) {
224                ++multiPageLoader.loading;
225                finished = false;
226        }
227        if (multiPageLoader.loadStartedEmitted) return;
228        multiPageLoader.loadStartedEmitted=true;
229        emit multiPageLoader.outer.loadStarted();
230}
231
232
233/*!
234 * Called when the page is loading, display some progress to the using
235 * \param progress the loading progress in percent
236 */
237void ResourceObject::loadProgress(int p) {
238        // If we are finished, ignore this signal.
239        if (finished || multiPageLoader.resources.size() <= 0) {
240                warning("A finished ResourceObject received a loading progress signal. "
241                        "This might be an indication of an iframe taking too long to load.");
242                return;
243        }
244
245        multiPageLoader.progressSum -= progress;
246        progress = p;
247        multiPageLoader.progressSum += progress;
248
249        emit multiPageLoader.outer.loadProgress(multiPageLoader.progressSum / multiPageLoader.resources.size());
250}
251
252
253void ResourceObject::loadFinished(bool ok) {
254        // If we are finished, this might be a potential bug.
255        if (finished || multiPageLoader.resources.size() <= 0) {
256                warning("A finished ResourceObject received a loading finished signal. "
257                        "This might be an indication of an iframe taking too long to load.");
258                return;
259        }
260
261        multiPageLoader.hasError = multiPageLoader.hasError || (!ok && settings.loadErrorHandling == settings::LoadPage::abort);
262        if (!ok) {
263                if (settings.loadErrorHandling == settings::LoadPage::abort)
264                        error(QString("Failed loading page ") + url.toString() + " (sometimes it will work just to ignore this error with --load-error-handling ignore)");
265                else if (settings.loadErrorHandling == settings::LoadPage::skip) {
266                        warning(QString("Failed loading page ") + url.toString() + " (skipped)");
267                        lo.skip = true;
268                } else
269                        warning(QString("Failed loading page ") + url.toString() + " (ignored)");
270        }
271
272        bool isMain = multiPageLoader.isMainLoader;
273
274        // Evaluate extra user supplied javascript for the main loader
275        if (isMain)
276                foreach (const QString & str, settings.runScript)
277                        webPage.mainFrame()->evaluateJavaScript(str);
278
279        // XXX: If loading failed there's no need to wait
280        //      for javascript on this resource.
281        if (!ok || signalPrint || settings.jsdelay == 0) loadDone();
282        else if (isMain && !settings.windowStatus.isEmpty()) waitWindowStatus();
283        else QTimer::singleShot(settings.jsdelay, this, SLOT(loadDone()));
284}
285
286void ResourceObject::waitWindowStatus() {
287        QString windowStatus = webPage.mainFrame()->evaluateJavaScript("window.status").toString();
288        //warning(QString("window.status:" + windowStatus + " settings.windowStatus:" + settings.windowStatus));
289        if (windowStatus != settings.windowStatus) {
290                QTimer::singleShot(50, this, SLOT(waitWindowStatus()));
291        } else {
292                QTimer::singleShot(settings.jsdelay, this, SLOT(loadDone()));
293        }
294}
295
296void ResourceObject::printRequested(QWebFrame *) {
297        signalPrint=true;
298        loadDone();
299}
300
301void ResourceObject::loadDone() {
302        if (finished) return;
303        finished=true;
304
305        // Ensure no more loading goes..
306        webPage.triggerAction(QWebPage::Stop);
307        webPage.triggerAction(QWebPage::StopScheduledPageRefresh);
308        networkAccessManager.dispose();
309        //disconnect(this, 0, 0, 0);
310
311        --multiPageLoader.loading;
312        if (multiPageLoader.loading == 0)
313                multiPageLoader.loadDone();
314}
315
316/*!
317 * Called when the page requires authentication, fills in the username
318 * and password supplied on the command line
319 */
320void ResourceObject::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) {
321        Q_UNUSED(reply);
322
323        // XXX: Avoid calling 'reply->abort()' from within this signal.
324        //      As stated by doc, request would be finished when no
325        //      user/pass properties are assigned to authenticator object.
326        // See: http://qt-project.org/doc/qt-5.0/qtnetwork/qnetworkaccessmanager.html#authenticationRequired
327
328        if (settings.username.isEmpty()) {
329                //If no username is given, complain the such is required
330                error("Authentication Required");
331        } else if (loginTry >= 2) {
332                //If the login has failed a sufficient number of times,
333                //the username or password must be wrong
334                error("Invalid username or password");
335        } else {
336                authenticator->setUser(settings.username);
337                authenticator->setPassword(settings.password);
338                ++loginTry;
339        }
340}
341
342void ResourceObject::warning(const QString & str) {
343        emit multiPageLoader.outer.warning(str);
344}
345
346void ResourceObject::error(const QString & str) {
347        emit multiPageLoader.outer.error(str);
348}
349
350/*!
351 * Track and handle network errors
352 * \param reply The networkreply that has finished
353 */
354void ResourceObject::amfinished(QNetworkReply * reply) {
355        int networkStatus = reply->error();
356        int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
357        if ((networkStatus != 0 && networkStatus != 5) || (httpStatus > 399 && httpErrorCode == 0))
358        {
359                QFileInfo fi(reply->url().toString());
360                bool mediaFile = settings::LoadPage::mediaFilesExtensions.contains(fi.completeSuffix().toLower());
361                if ( ! mediaFile) {
362                        // XXX: Notify network errors as higher priority than HTTP errors.
363                        //      QT's QNetworkReply::NetworkError enum uses values overlapping
364                        //      HTTP status codes, so adding 1000 to QT's codes will avoid
365                        //      confusion. Also a network error at this point will probably mean
366                        //      no HTTP access at all, so we want network errors to be reported
367                        //      with a higher priority than HTTP ones.
368                        //      See: http://doc-snapshot.qt-project.org/4.8/qnetworkreply.html#NetworkError-enum
369                        httpErrorCode = networkStatus > 0 ? (networkStatus + 1000) : httpStatus;
370                        return;
371                }
372                if (settings.mediaLoadErrorHandling == settings::LoadPage::abort)
373                {
374                        httpErrorCode = networkStatus > 0 ? (networkStatus + 1000) : httpStatus;
375                        error(QString("Failed to load ") + reply->url().toString() + ", with code: " + QString::number(httpErrorCode) + 
376                                " (sometimes it will work just to ignore this error with --load-media-error-handling ignore)");
377                }
378                else {
379                        warning(QString("Failed to load %1 (%2)")
380                                        .arg(reply->url().toString())
381                                        .arg(settings::loadErrorHandlingToStr(settings.mediaLoadErrorHandling))
382                                        );
383                }
384        }
385}
386
387/*!
388 * Handle any ssl error by ignoring
389 */
390void ResourceObject::sslErrors(QNetworkReply *reply, const QList<QSslError> &) {
391        //We ignore any ssl error, as it is next to impossible to send or receive
392        //any private information with wkhtmltopdf anyhow, seeing as you cannot authenticate
393        reply->ignoreSslErrors();
394        warning("SSL error ignored");
395}
396
397void ResourceObject::load() {
398        finished=false;
399        ++multiPageLoader.loading;
400
401        bool hasFiles=false;
402        foreach (const settings::PostItem & pi, settings.post) hasFiles |= pi.file;
403        QByteArray postData;
404        QString boundary;
405        if (hasFiles) {
406                boundary = QUuid::createUuid().toString().remove('-').remove('{').remove('}');
407                foreach (const settings::PostItem & pi, settings.post) {
408                        //TODO escape values here
409                        postData.append("--");
410                        postData.append(boundary);
411                        postData.append("\ncontent-disposition: form-data; name=\"");
412                        postData.append(pi.name);
413                        postData.append('\"');
414                        if (pi.file) {
415                                QFile f(pi.value);
416                                if (!f.open(QIODevice::ReadOnly) ) {
417                                        error(QString("Unable to open file ")+pi.value);
418                                        multiPageLoader.fail();
419                                }
420                                postData.append("; filename=\"");
421                                postData.append( QFileInfo(pi.value).fileName());
422                                postData.append("\"\n\n");
423                                postData.append( f.readAll() );
424                                //TODO ADD MIME TYPE
425                        } else {
426                                postData.append("\n\n");
427                                postData.append(pi.value);
428                        }
429                        postData.append('\n');
430                }
431                if (!postData.isEmpty()) {
432                        postData.append("--");
433                        postData.append(boundary);
434                        postData.append("--\n");
435                }
436        } else {
437#if QT_VERSION >= 0x050000
438                QUrlQuery q;
439                foreach (const settings::PostItem & pi, settings.post)
440                        q.addQueryItem(pi.name, pi.value);
441                postData = q.query(QUrl::FullyEncoded).toLocal8Bit();
442#else
443                QUrl u;
444                foreach (const settings::PostItem & pi, settings.post)
445                        u.addQueryItem(pi.name, pi.value);
446                postData = u.encodedQuery();
447#endif
448        }
449
450
451        typedef QPair<QString, QString> SSP;
452        foreach (const SSP & pair, settings.cookies)
453                multiPageLoader.cookieJar->useCookie(url, pair.first, pair.second);
454
455        QNetworkRequest r = QNetworkRequest(url);
456        typedef QPair<QString, QString> HT;
457        foreach (const HT & j, settings.customHeaders)
458                r.setRawHeader(j.first.toLatin1(), j.second.toLatin1());
459
460        if (postData.isEmpty())
461                webPage.mainFrame()->load(r);
462        else {
463                if (hasFiles)
464                        r.setHeader(QNetworkRequest::ContentTypeHeader, QString("multipart/form-data, boundary=")+boundary);
465                webPage.mainFrame()->load(r, QNetworkAccessManager::PostOperation, postData);
466        }
467}
468
469void MyCookieJar::useCookie(const QUrl &, const QString & name, const QString & value) {
470        extraCookies.push_back(QNetworkCookie(name.toUtf8(), value.toUtf8()));
471}
472
473QList<QNetworkCookie> MyCookieJar::cookiesForUrl(const QUrl & url) const {
474        QList<QNetworkCookie> list = QNetworkCookieJar::cookiesForUrl(url);
475        list.append(extraCookies);
476        return list;
477}
478
479void MyCookieJar::loadFromFile(const QString & path) {
480        QFile cookieJar(path);
481        if (cookieJar.open(QIODevice::ReadOnly | QIODevice::Text) )
482                setAllCookies(QNetworkCookie::parseCookies(cookieJar.readAll()));
483}
484
485void MyCookieJar::saveToFile(const QString & path) {
486        QFile cookieJar(path);
487        if (cookieJar.open(QIODevice::WriteOnly | QIODevice::Text) )
488                foreach (const QNetworkCookie & cookie, allCookies()) {
489                        cookieJar.write(cookie.toRawForm());
490                        cookieJar.write(";\n");
491                }
492}
493
494void MultiPageLoaderPrivate::loadDone() {
495         if (!settings.cookieJar.isEmpty())
496                cookieJar->saveToFile(settings.cookieJar);
497
498        if (!finishedEmitted) {
499                finishedEmitted = true;
500                emit outer.loadFinished(!hasError);
501        }
502}
503
504
505
506/*!
507 * Copy a file from some place to another
508 * \param src The source to copy from
509 * \param dst The destination to copy to
510 */
511bool MultiPageLoader::copyFile(QFile & src, QFile & dst) {
512//      TODO enable again when
513//      http://bugreports.qt.nokia.com/browse/QTBUG-6894
514//      is fixed
515//      QByteArray buf(1024*1024*5,0);
516//      while ( qint64 r=src.read(buf.data(),buf.size())) {
517//          if (r == -1) return false;
518//          if (dst.write(buf.data(),r) != r) return false;
519//      }
520
521    if (dst.write( src.readAll() ) == -1) return false;
522
523        src.close();
524        dst.close();
525        return true;
526}
527
528MultiPageLoaderPrivate::MultiPageLoaderPrivate(const settings::LoadGlobal & s, MultiPageLoader & o):
529        outer(o), settings(s) {
530
531        cookieJar = new MyCookieJar();
532
533        if (!settings.cookieJar.isEmpty())
534                cookieJar->loadFromFile(settings.cookieJar);
535}
536
537MultiPageLoaderPrivate::~MultiPageLoaderPrivate() {
538        clearResources();
539}
540
541LoaderObject * MultiPageLoaderPrivate::addResource(const QUrl & url, const settings::LoadPage & page) {
542        ResourceObject * ro = new ResourceObject(*this, url, page);
543        resources.push_back(ro);
544
545        return &ro->lo;
546}
547
548void MultiPageLoaderPrivate::load() {
549        progressSum=0;
550        loadStartedEmitted=false;
551        finishedEmitted=false;
552        hasError=false;
553        loading=0;
554
555        for (int i=0; i < resources.size(); ++i)
556                resources[i]->load();
557
558        if (resources.size() == 0) loadDone();
559}
560
561void MultiPageLoaderPrivate::clearResources() {
562        while (resources.size() > 0)
563        {
564                // XXX: Using deleteLater() to dispose
565                // resources, to avoid race conditions with
566                // pending signals reaching a deleted resource.
567                // Also, and we must avoid calling clear()
568                // on resources list, is it tries to delete
569                // each objet on removal.
570                ResourceObject *tmp = resources.takeFirst();
571                tmp->deleteLater();
572        }
573        tempIn.removeAll();
574}
575
576void MultiPageLoaderPrivate::cancel() {
577        //foreach (QWebPage * page, pages)
578        //      page->triggerAction(QWebPage::Stop);
579}
580
581void MultiPageLoaderPrivate::fail() {
582        hasError = true;
583        cancel();
584        clearResources();
585}
586
587/*!
588  \brief Construct a multipage loader object, load settings read from the supplied settings
589  \param s The settings to be used while loading pages
590*/
591MultiPageLoader::MultiPageLoader(settings::LoadGlobal & s, bool mainLoader):
592        d(new MultiPageLoaderPrivate(s, *this)) {
593        d->isMainLoader = mainLoader;
594}
595
596MultiPageLoader::~MultiPageLoader() {
597        MultiPageLoaderPrivate *tmp = d;
598        d = 0;
599        tmp->deleteLater();
600}
601
602/*!
603  \brief Add a resource, to be loaded described by a string
604  @param string Url describing the resource to load
605*/
606LoaderObject * MultiPageLoader::addResource(const QString & string, const settings::LoadPage & s, const QString * data) {
607        QString url=string;
608        if (data && !data->isEmpty()) {
609                url = d->tempIn.create(".html");
610                QFile tmp(url);
611                if (!tmp.open(QIODevice::WriteOnly) || tmp.write(data->toUtf8())==0) {
612                        emit error("Unable to create temporary file");
613                        return NULL;
614                }
615        } else if (url == "-") {
616                QFile in;
617                in.open(stdin,QIODevice::ReadOnly);
618                url = d->tempIn.create(".html");
619                QFile tmp(url);
620                if (!tmp.open(QIODevice::WriteOnly) || !copyFile(in, tmp)) {
621                        emit error("Unable to create temporary file");
622                        return NULL;
623                }
624        }
625        return addResource(guessUrlFromString(url), s);
626}
627
628/*!
629  \brief Add a page to be loaded
630  @param url Url of the page to load
631*/
632LoaderObject * MultiPageLoader::addResource(const QUrl & url, const settings::LoadPage & s) {
633        return d->addResource(url, s);
634}
635
636/*!
637  \brief Guess a url, by looking at a string
638
639  (shamelessly copied from Arora Project)
640  \param string The string the is suppose to be some kind of url
641*/
642QUrl MultiPageLoader::guessUrlFromString(const QString &string) {
643        QString urlStr = string.trimmed();
644
645        // check if the string is just a host with a port
646        QRegExp hostWithPort(QLatin1String("^[a-zA-Z\\.]+\\:[0-9]*$"));
647        if (hostWithPort.exactMatch(urlStr))
648                urlStr = QLatin1String("http://") + urlStr;
649
650        // Check if it looks like a qualified URL. Try parsing it and see.
651        QRegExp test(QLatin1String("^[a-zA-Z]+\\://.*"));
652        bool hasSchema = test.exactMatch(urlStr);
653        if (hasSchema) {
654                bool isAscii = true;
655                foreach (const QChar &c, urlStr) {
656                        if (c >= 0x80) {
657                                isAscii = false;
658                                break;
659                        }
660                }
661
662                QUrl url;
663                if (isAscii) {
664                        url = QUrl::fromEncoded(urlStr.toLatin1(), QUrl::TolerantMode);
665                } else {
666                        url = QUrl(urlStr, QUrl::TolerantMode);
667                }
668                if (url.isValid())
669                        return url;
670        }
671
672        // Might be a file.
673        if (QFile::exists(urlStr)) {
674                QFileInfo info(urlStr);
675                return QUrl::fromLocalFile(info.absoluteFilePath());
676        }
677
678        // Might be a shorturl - try to detect the schema.
679        if (!hasSchema) {
680                int dotIndex = urlStr.indexOf(QLatin1Char('.'));
681                if (dotIndex != -1) {
682                        QString prefix = urlStr.left(dotIndex).toLower();
683                        QString schema = (prefix == QLatin1String("ftp")) ? prefix : QLatin1String("http");
684                        QUrl url(schema + QLatin1String("://") + urlStr, QUrl::TolerantMode);
685                        if (url.isValid())
686                                return url;
687                }
688        }
689
690        // Fall back to QUrl's own tolerant parser.
691        QUrl url = QUrl(string, QUrl::TolerantMode);
692
693        // finally for cases where the user just types in a hostname add http
694        if (url.scheme().isEmpty())
695                url = QUrl(QLatin1String("http://") + string, QUrl::TolerantMode);
696        return url;
697}
698
699/*!
700  \brief Return the most severe http error code returned during loading
701 */
702int MultiPageLoader::httpErrorCode() {
703        int res=0;
704        foreach (const ResourceObject * ro, d->resources)
705                if (ro->httpErrorCode > res) res = ro->httpErrorCode;
706        return res;
707}
708
709/*!
710  \brief Begin loading all the resources added
711*/
712void MultiPageLoader::load() {
713        d->load();
714}
715
716/*!
717  \brief Clear all the resources
718*/
719void MultiPageLoader::clearResources() {
720        d->clearResources();
721}
722
723/*!
724  \brief Cancel the loading of the pages
725*/
726void MultiPageLoader::cancel() {
727        d->cancel();
728}
729
730/*!
731  \fn MultiPageLoader::loadFinished(bool ok)
732  \brief Signal emitted when all pages have been loaded
733  \param ok True if all the pages have been loaded sucessfully
734*/
735
736/*!
737  \fn MultiPageLoader::loadProgress(int progress)
738  \brief Signal emitted once load has progressed
739  \param progress Progress in percent
740*/
741
742/*!
743  \fn MultiPageLoader::loadStarted()
744  \brief Signal emitted when loading has started
745*/
746
747/*!
748  \fn void MultiPageLoader::warning(QString text)
749  \brief Signal emitted when a none fatal warning has occured
750  \param text A string describing the warning
751*/
752
753/*!
754  \fn void MultiPageLoader::error(QString text)
755  \brief Signal emitted when a fatal error has occured
756  \param text A string describing the error
757*/
758}
Note: See TracBrowser for help on using the repository browser.