1
mirror of https://invent.kde.org/network/falkon.git synced 2024-12-19 10:16:34 +01:00

Added support for Proxy Auto-Config (PAC).

.pac files will be downloaded and executed to get
proxy configuration for each url.

Closes #747
This commit is contained in:
nowrep 2013-03-29 19:22:55 +01:00
parent 953ca726bd
commit a9326eadc5
18 changed files with 1366 additions and 162 deletions

View File

@ -1,5 +1,6 @@
Version 1.5.0
* development version
* added support for Proxy Auto-Config (PAC)
* added option to open another private window from private window
* added delete action in edit context menu on page

View File

@ -212,7 +212,9 @@ SOURCES += \
navigation/navigationcontainer.cpp \
tools/horizontallistwidget.cpp \
tools/mactoolbutton.cpp \
tools/actioncopy.cpp
tools/actioncopy.cpp \
network/pac/proxyautoconfig.cpp \
network/pac/pacmanager.cpp
HEADERS += \
webview/tabpreview.h \
@ -384,7 +386,10 @@ HEADERS += \
tools/horizontallistwidget.h \
tools/mactoolbutton.h \
tools/qzregexp.h \
tools/actioncopy.h
tools/actioncopy.h \
network/pac/proxyautoconfig.h \
network/pac/pacmanager.h \
network/pac/pacdatetime.h
FORMS += \
preferences/autofillmanager.ui \

View File

@ -596,6 +596,11 @@ void NetworkManager::addLocalCertificate(const QSslCertificate &cert)
}
}
NetworkProxyFactory* NetworkManager::proxyFactory() const
{
return m_proxyFactory;
}
bool NetworkManager::registerSchemeHandler(const QString &scheme, SchemeHandler* handler)
{
if (m_schemeHandlers.contains(scheme)) {

View File

@ -52,6 +52,8 @@ public:
void setIgnoreAllWarnings(bool state) { m_ignoreAllWarnings = state; }
bool isIgnoringAllWarnings() { return m_ignoreAllWarnings; }
NetworkProxyFactory* proxyFactory() const;
bool registerSchemeHandler(const QString &scheme, SchemeHandler* handler);
void disconnectObjects();

View File

@ -1,6 +1,6 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
* Copyright (C) 2010-2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,9 +18,11 @@
#include "networkproxyfactory.h"
#include "mainapplication.h"
#include "settings.h"
#include "pac/pacmanager.h"
NetworkProxyFactory::NetworkProxyFactory()
: QNetworkProxyFactory()
, m_pacManager(new PacManager)
, m_proxyPreference(SystemProxy)
{
}
@ -45,6 +47,13 @@ void NetworkProxyFactory::loadSettings()
m_proxyExceptions = settings.value("ProxyExceptions", QStringList() << "localhost" << "127.0.0.1").toStringList();
settings.endGroup();
m_pacManager->loadSettings();
}
PacManager* NetworkProxyFactory::pacManager() const
{
return m_pacManager;
}
QList<QNetworkProxy> NetworkProxyFactory::queryProxy(const QNetworkProxyQuery &query)
@ -63,6 +72,9 @@ QList<QNetworkProxy> NetworkProxyFactory::queryProxy(const QNetworkProxyQuery &q
proxy = QNetworkProxy::NoProxy;
break;
case ProxyAutoConfig:
return m_pacManager->queryProxy(query.url());
case DefinedProxy:
proxy = m_proxyType;

View File

@ -1,6 +1,6 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
* Copyright (C) 2010-2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,17 +23,23 @@
#include "qz_namespace.h"
class PacManager;
class QT_QUPZILLA_EXPORT NetworkProxyFactory : public QNetworkProxyFactory
{
public:
enum ProxyPreference { SystemProxy, NoProxy, DefinedProxy };
enum ProxyPreference { SystemProxy, NoProxy, ProxyAutoConfig, DefinedProxy };
explicit NetworkProxyFactory();
void loadSettings();
PacManager* pacManager() const;
QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery());
private:
PacManager* m_pacManager;
ProxyPreference m_proxyPreference;
QNetworkProxy::ProxyType m_proxyType;

View File

@ -0,0 +1,186 @@
/*
* The following subset of Javascript code was taken from Mozilla (http://www.mozilla.org)
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Akhil Arora <akhil.arora@sun.com>
* Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#define PAC_DATETIME_JAVASCRIPT \
"var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" \
"var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n"\
"function weekdayRange() {\n" \
" function getDay(weekday) {\n" \
" if (weekday in wdays) {\n" \
" return wdays[weekday];\n" \
" }\n" \
" return -1;\n" \
" }\n" \
" var date = new Date();\n" \
" var argc = arguments.length;\n" \
" var wday;\n" \
" if (argc < 1)\n" \
" return false;\n" \
" if (arguments[argc - 1] == 'GMT') {\n" \
" argc--;\n" \
" wday = date.getUTCDay();\n" \
" } else {\n" \
" wday = date.getDay();\n" \
" }\n" \
" var wd1 = getDay(arguments[0]);\n" \
" var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" \
" return (wd1 == -1 || wd2 == -1) ? false\n" \
" : (wd1 <= wday && wday <= wd2);\n" \
"}\n" \
"function dateRange() {\n" \
" function getMonth(name) {\n" \
" if (name in months) {\n" \
" return months[name];\n" \
" }\n" \
" return -1;\n" \
" }\n" \
" var date = new Date();\n" \
" var argc = arguments.length;\n" \
" if (argc < 1) {\n" \
" return false;\n" \
" }\n" \
" var isGMT = (arguments[argc - 1] == 'GMT');\n" \
" if (isGMT) {\n" \
" argc--;\n" \
" }\n" \
" if (argc == 1) {\n" \
" var tmp = parseInt(arguments[0]);\n" \
" if (isNaN(tmp)) {\n" \
" return ((isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0]));\n" \
" } else if (tmp < 32) {\n" \
" return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" \
" } else {\n" \
" return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp);\n" \
" }\n" \
" }\n" \
" var year = date.getFullYear();\n" \
" var date1, date2;\n" \
" date1 = new Date(year, 0, 1, 0, 0, 0);\n" \
" date2 = new Date(year, 11, 31, 23, 59, 59);\n" \
" var adjustMonth = false;\n" \
" for (var i = 0; i < (argc >> 1); i++) {\n" \
" var tmp = parseInt(arguments[i]);\n" \
" if (isNaN(tmp)) {\n" \
" var mon = getMonth(arguments[i]);\n" \
" date1.setMonth(mon);\n" \
" } else if (tmp < 32) {\n" \
" adjustMonth = (argc <= 2);\n" \
" date1.setDate(tmp);\n" \
" } else {\n" \
" date1.setFullYear(tmp);\n" \
" }\n" \
" }\n" \
" for (var i = (argc >> 1); i < argc; i++) {\n" \
" var tmp = parseInt(arguments[i]);\n" \
" if (isNaN(tmp)) {\n" \
" var mon = getMonth(arguments[i]);\n" \
" date2.setMonth(mon);\n" \
" } else if (tmp < 32) {\n" \
" date2.setDate(tmp);\n" \
" } else {\n" \
" date2.setFullYear(tmp);\n" \
" }\n" \
" }\n" \
" if (adjustMonth) {\n" \
" date1.setMonth(date.getMonth());\n" \
" date2.setMonth(date.getMonth());\n" \
" }\n" \
" if (isGMT) {\n" \
" var tmp = date;\n" \
" tmp.setFullYear(date.getUTCFullYear());\n" \
" tmp.setMonth(date.getUTCMonth());\n" \
" tmp.setDate(date.getUTCDate());\n" \
" tmp.setHours(date.getUTCHours());\n" \
" tmp.setMinutes(date.getUTCMinutes());\n" \
" tmp.setSeconds(date.getUTCSeconds());\n" \
" date = tmp;\n" \
" }\n" \
" return ((date1 <= date) && (date <= date2));\n" \
"}\n" \
"function timeRange() {\n" \
" var argc = arguments.length;\n" \
" var date = new Date();\n" \
" var isGMT= false;\n" \
" if (argc < 1) {\n" \
" return false;\n" \
" }\n" \
" if (arguments[argc - 1] == 'GMT') {\n" \
" isGMT = true;\n" \
" argc--;\n" \
" }\n" \
" var hour = isGMT ? date.getUTCHours() : date.getHours();\n" \
" var date1, date2;\n" \
" date1 = new Date();\n" \
" date2 = new Date();\n" \
" if (argc == 1) {\n" \
" return (hour == arguments[0]);\n" \
" } else if (argc == 2) {\n" \
" return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" \
" } else {\n" \
" switch (argc) {\n" \
" case 6:\n" \
" date1.setSeconds(arguments[2]);\n" \
" date2.setSeconds(arguments[5]);\n" \
" case 4:\n" \
" var middle = argc >> 1;\n" \
" date1.setHours(arguments[0]);\n" \
" date1.setMinutes(arguments[1]);\n" \
" date2.setHours(arguments[middle]);\n" \
" date2.setMinutes(arguments[middle + 1]);\n" \
" if (middle == 2) {\n" \
" date2.setSeconds(59);\n" \
" }\n" \
" break;\n" \
" default:\n" \
" throw 'timeRange: bad number of arguments'\n" \
" }\n" \
" }\n" \
" if (isGMT) {\n" \
" date.setFullYear(date.getUTCFullYear());\n" \
" date.setMonth(date.getUTCMonth());\n" \
" date.setDate(date.getUTCDate());\n" \
" date.setHours(date.getUTCHours());\n" \
" date.setMinutes(date.getUTCMinutes());\n" \
" date.setSeconds(date.getUTCSeconds());\n" \
" }\n" \
" return ((date1 <= date) && (date <= date2));\n" \
"}\n" \
""

View File

@ -0,0 +1,158 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "pacmanager.h"
#include "proxyautoconfig.h"
#include "mainapplication.h"
#include "networkmanager.h"
#include "followredirectreply.h"
#include "settings.h"
#include "qztools.h"
#include <QNetworkProxy>
#include <QStringList>
#include <QUrl>
#include <QFile>
#include <QDebug>
PacManager::PacManager(QObject* parent)
: QObject(parent)
, m_pacrunner(0)
, m_reply(0)
, m_loaded(false)
{
}
void PacManager::loadSettings()
{
QUrl oldUrl = m_url;
Settings settings;
settings.beginGroup("Web-Proxy");
m_url = settings.value("PacUrl", QUrl()).toUrl();
settings.endGroup();
if (m_loaded && oldUrl != m_url) {
downloadPacFile();
}
m_loaded = true;
}
void PacManager::downloadPacFile()
{
if (m_reply) {
qWarning() << "PacManager: PAC file is already being downloaded!";
return;
}
m_reply = new FollowRedirectReply(m_url, mApp->networkManager());
connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
}
QList<QNetworkProxy> PacManager::queryProxy(const QUrl &url)
{
if (!m_pacrunner) {
reloadScript();
}
QString proxyString = m_pacrunner->findProxyForUrl(url.toEncoded(), url.host());
return parseProxies(proxyString.trimmed());
}
void PacManager::replyFinished()
{
if (m_reply->error() != QNetworkReply::NoError) {
qWarning() << "PacManager: Cannot download PAC file from" << m_url;
m_reply->deleteLater();
m_reply = 0;
return;
}
QByteArray data = m_reply->readAll();
m_reply->deleteLater();
m_reply = 0;
QFile file(mApp->currentProfilePath() + "proxy.pac");
if (!file.open(QFile::WriteOnly)) {
qWarning() << "PacManager: Cannot open PAC file for writing" << file.fileName();
return;
}
file.write(data);
file.close();
reloadScript();
}
void PacManager::reloadScript()
{
if (!m_pacrunner) {
m_pacrunner = new ProxyAutoConfig(this);
}
QFile file(mApp->currentProfilePath() + "proxy.pac");
if (!file.open(QFile::ReadOnly)) {
qWarning() << "PacManager: Cannot open PAC file for reading" << file.fileName();
return;
}
m_pacrunner->setConfig(file.readAll());
}
QList<QNetworkProxy> PacManager::parseProxies(const QString &string)
{
QList<QNetworkProxy> proxies;
string.trimmed();
if (string.isEmpty()) {
return proxies;
}
QStringList parts = string.split(QLatin1Char(';'), QString::SkipEmptyParts);
if (parts.isEmpty()) {
parts.append(string);
}
foreach (const QString &s, parts) {
QStringList l = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
if (l.count() != 2) {
if (l.count() == 1 && l.at(0) == QLatin1String("DIRECT")) {
proxies.append(QNetworkProxy::NoProxy);
}
continue;
}
QString type = l.at(0);
QUrl url = QUrl::fromEncoded("proxy://" + l.at(1).toUtf8());
if (type == QLatin1String("PROXY")) {
QNetworkProxy proxy(QNetworkProxy::HttpProxy, url.host(), url.port(8080));
proxies.append(proxy);
}
else if (type == QLatin1String("SOCKS")) {
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(), url.port(1080));
proxies.append(proxy);
}
}
return proxies;
}

View File

@ -0,0 +1,57 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#ifndef PACMANAGER_H
#define PACMANAGER_H
#include <QObject>
#include <QList>
#include <QUrl>
#include "qz_namespace.h"
class QNetworkProxy;
class FollowRedirectReply;
class ProxyAutoConfig;
class QT_QUPZILLA_EXPORT PacManager : public QObject
{
Q_OBJECT
public:
explicit PacManager(QObject* parent = 0);
void loadSettings();
void downloadPacFile();
QList<QNetworkProxy> queryProxy(const QUrl &url);
private slots:
void replyFinished();
private:
void reloadScript();
QList<QNetworkProxy> parseProxies(const QString &string);
ProxyAutoConfig* m_pacrunner;
FollowRedirectReply* m_reply;
bool m_loaded;
QUrl m_url;
};
#endif // PACMANAGER_H

View File

@ -0,0 +1,272 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "proxyautoconfig.h"
#include "pacdatetime.h"
#include "qztools.h"
#include "qzregexp.h"
#include <QScriptEngine>
#include <QDebug>
#include <QNetworkInterface>
#include <QHostAddress>
#include <QHostInfo>
#include <QRegExp>
/**
* Class implementing the proxy auto-configuration (PAC) JavaScript api.
*
* Based on qt-examples: https://gitorious.org/qt-examples/qt-examples/blobs/master/pac-files
*/
ProxyAutoConfig::ProxyAutoConfig(QObject* parent)
: QObject(parent)
, m_engine(new QScriptEngine(this))
{
install();
}
void ProxyAutoConfig::setConfig(const QString &config)
{
m_engine->evaluate(config);
}
// string findProxyForUrl url host
QString ProxyAutoConfig::findProxyForUrl(const QString &url, const QString &host)
{
QScriptValue global = m_engine->globalObject();
QScriptValue fun = global.property("FindProxyForURL");
if (!fun.isFunction()) {
return QString("DIRECT");
}
QScriptValueList args;
args << m_engine->toScriptValue(url) << m_engine->toScriptValue(host);
QScriptValue val = fun.call(global, args);
if (val.isError()) {
qWarning() << "PAC Error:" << val.toString();
return QString("DIRECT");
}
return val.toString();
}
QScriptValue ProxyAutoConfig::evaluate(const QString &source)
{
return m_engine->evaluate(source);
}
void ProxyAutoConfig::install()
{
QScriptValue globalObject = m_engine->globalObject();
QScriptValue fun;
fun = m_engine->newFunction(debug);
globalObject.setProperty("debug", fun);
fun = m_engine->newFunction(isPlainHostName);
globalObject.setProperty("isPlainHostName", fun);
fun = m_engine->newFunction(dnsDomainIs);
globalObject.setProperty("dnsDomainIs", fun);
fun = m_engine->newFunction(localHostOrDomainIs);
globalObject.setProperty("localHostOrDomainIs", fun);
fun = m_engine->newFunction(isResolvable);
globalObject.setProperty("isResolvable", fun);
fun = m_engine->newFunction(isInNet);
globalObject.setProperty("isInNet", fun);
fun = m_engine->newFunction(dnsResolve);
globalObject.setProperty("dnsResolve", fun);
fun = m_engine->newFunction(myIpAddress);
globalObject.setProperty("myIpAddress", fun);
fun = m_engine->newFunction(dnsDomainLevels);
globalObject.setProperty("dnsDomainLevels", fun);
fun = m_engine->newFunction(shExpMatch);
globalObject.setProperty("shExpMatch", fun);
m_engine->evaluate(PAC_DATETIME_JAVASCRIPT);
}
QScriptValue ProxyAutoConfig::debug(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 1) {
return context->throwError("Debug takes one argument");
}
qDebug() << context->argument(0).toString();
return engine->undefinedValue();
}
// bool isPlainHostName host
QScriptValue ProxyAutoConfig::isPlainHostName(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 1) {
return context->throwError("isPlainHostName takes one argument");
}
bool ret = !context->argument(0).toString().contains(QLatin1Char('.'));
return QScriptValue(engine, ret);
}
// bool dnsDomainIs host domain
QScriptValue ProxyAutoConfig::dnsDomainIs(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 2) {
return context->throwError("dnsDomainIs takes two arguments");
}
QString host = context->argument(0).toString();
QString domain = context->argument(1).toString();
if (host.startsWith(QLatin1Char('.'))) {
host = host.mid(1);
}
if (domain.startsWith(QLatin1Char('.'))) {
domain = domain.mid(1);
}
return QScriptValue(engine, QzTools::matchDomain(domain, host));
}
// bool localHostOrDomainIs host hostdom
QScriptValue ProxyAutoConfig::localHostOrDomainIs(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 2) {
return context->throwError("localHostOrDomainIs takes two arguments");
}
QString host = context->argument(0).toString();
QString hostdom = context->argument(1).toString();
bool ret = !host.contains(QLatin1Char('.')) ? hostdom.startsWith(host) : host == hostdom;
return QScriptValue(engine, ret);
}
static QList<QHostAddress> hostResolve(const QString &host)
{
QHostInfo info = QHostInfo::fromName(host);
return info.addresses();
}
// bool isResolvable host
QScriptValue ProxyAutoConfig::isResolvable(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 1) {
return context->throwError("isResolvable takes one arguments");
}
QString host = context->argument(0).toString();
return QScriptValue(engine, !hostResolve(host).isEmpty());
}
// bool isInNet host pattern mask
QScriptValue ProxyAutoConfig::isInNet(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 3) {
return context->throwError("isInNet takes three arguments");
}
QHostAddress host(context->argument(0).toString());
QHostAddress pattern(context->argument(1).toString());
QHostAddress mask(context->argument(2).toString());
if (host.isNull()) {
QList<QHostAddress> addresses = hostResolve(context->argument(0).toString());
host = addresses.isEmpty() ? QHostAddress() : addresses.first();
}
if ((pattern.toIPv4Address() & mask.toIPv4Address()) == (host.toIPv4Address() & mask.toIPv4Address())) {
return QScriptValue(engine, true);
}
return QScriptValue(engine, false);
}
// string dnsResolve hostname
QScriptValue ProxyAutoConfig::dnsResolve(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 1) {
return context->throwError("dnsResolve takes one arguments");
}
QString host = context->argument(0).toString();
QList<QHostAddress> addresses = hostResolve(host);
if (addresses.isEmpty()) {
return engine->nullValue();
}
return QScriptValue(engine, addresses.first().toString());
}
// string myIpAddress
QScriptValue ProxyAutoConfig::myIpAddress(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 0) {
return context->throwError("myIpAddress takes no arguments");
}
foreach (QHostAddress address, QNetworkInterface::allAddresses()) {
if (address != QHostAddress::LocalHost
&& address != QHostAddress::LocalHostIPv6) {
return QScriptValue(engine, address.toString());
}
}
return engine->undefinedValue();
}
// int dnsDomainLevels host
QScriptValue ProxyAutoConfig::dnsDomainLevels(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 1) {
return context->throwError("dnsDomainLevels takes one argument");
}
QString host = context->argument(0).toString();
return QScriptValue(engine, host.count(QLatin1Char('.')));
}
// bool shExpMatch str shexp
QScriptValue ProxyAutoConfig::shExpMatch(QScriptContext* context, QScriptEngine* engine)
{
if (context->argumentCount() != 2) {
return context->throwError("shExpMatch takes two arguments");
}
QString str = context->argument(0).toString();
QString shexp = context->argument(1).toString();
shexp.replace(QLatin1Char('.'), QLatin1String("\\."))
.replace(QLatin1Char('*'), QLatin1String(".*"))
.replace(QLatin1Char('?'), QLatin1Char('.'));
shexp = QString("^%1$").arg(shexp);
QzRegExp re(shexp);
bool ret = re.indexIn(str) != -1;
return QScriptValue(engine, ret);
}

View File

@ -0,0 +1,77 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#ifndef PROXYAUTOCONFIG_H
#define PROXYAUTOCONFIG_H
#include <QObject>
#include <QScriptValue>
class QScriptContext;
class QScriptEngine;
/**
* Class implementing the proxy auto-configuration (PAC) JavaScript api.
*
* Based on qt-examples: https://gitorious.org/qt-examples/qt-examples/blobs/master/pac-files
*/
class ProxyAutoConfig : public QObject
{
Q_OBJECT
public:
explicit ProxyAutoConfig(QObject* parent = 0);
// Call this to set the script to be executed. Note that the argument should be
// the content of the .pac file to be used, not the URL where it is located.
void setConfig(const QString &config);
// Returns the result
QString findProxyForUrl(const QString &url, const QString &host);
protected:
QScriptValue evaluate(const QString &source);
private:
void install();
// Debug
static QScriptValue debug(QScriptContext* context, QScriptEngine* engine);
// Hostname based conditions
static QScriptValue isPlainHostName(QScriptContext* context, QScriptEngine* engine);
static QScriptValue dnsDomainIs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue localHostOrDomainIs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue isResolvable(QScriptContext* context, QScriptEngine* engine);
static QScriptValue isInNet(QScriptContext* context, QScriptEngine* engine);
// Related utility functions
static QScriptValue dnsResolve(QScriptContext* context, QScriptEngine* engine);
static QScriptValue myIpAddress(QScriptContext* context, QScriptEngine* engine);
static QScriptValue dnsDomainLevels(QScriptContext* context, QScriptEngine* engine);
// URL/hostname based conditions
static QScriptValue shExpMatch(QScriptContext* context, QScriptEngine* engine);
// Time based conditions
// Implemented in JavaScript
private:
QScriptEngine* m_engine;
};
#endif // PROXYAUTOCONFIG_H

View File

@ -46,6 +46,7 @@
#include "useragentdialog.h"
#include "registerqappassociation.h"
#include "html5permissions/html5permissionsdialog.h"
#include "pac/pacmanager.h"
#include <QSettings>
#include <QInputDialog>
@ -411,6 +412,7 @@ Preferences::Preferences(QupZilla* mainClass, QWidget* parent)
ui->systemProxy->setChecked(proxyPreference == NetworkProxyFactory::SystemProxy);
ui->noProxy->setChecked(proxyPreference == NetworkProxyFactory::NoProxy);
ui->manualProxy->setChecked(proxyPreference == NetworkProxyFactory::DefinedProxy);
ui->pacProxy->setChecked(proxyPreference == NetworkProxyFactory::ProxyAutoConfig);
if (proxyType == QNetworkProxy::HttpProxy) {
ui->proxyType->setCurrentIndex(0);
}
@ -429,14 +431,18 @@ Preferences::Preferences(QupZilla* mainClass, QWidget* parent)
ui->httpsProxyUsername->setText(settings.value("HttpsUsername", "").toString());
ui->httpsProxyPassword->setText(settings.value("HttpsPassword", "").toString());
ui->pacUrl->setText(settings.value("PacUrl", QUrl()).toUrl().toString());
ui->proxyExceptions->setText(settings.value("ProxyExceptions", QStringList() << "localhost" << "127.0.0.1").toStringList().join(","));
settings.endGroup();
useDifferentProxyForHttpsChanged(ui->useHttpsProxy->isChecked());
setManualProxyConfigurationEnabled(proxyPreference == NetworkProxyFactory::DefinedProxy);
setProxyAutoConfigEnabled(proxyPreference == NetworkProxyFactory::ProxyAutoConfig);
connect(ui->manualProxy, SIGNAL(toggled(bool)), this, SLOT(setManualProxyConfigurationEnabled(bool)));
connect(ui->pacProxy, SIGNAL(toggled(bool)), this, SLOT(setProxyAutoConfigEnabled(bool)));
connect(ui->useHttpsProxy, SIGNAL(toggled(bool)), this, SLOT(useDifferentProxyForHttpsChanged(bool)));
connect(ui->reloadPac, SIGNAL(clicked()), this, SLOT(reloadPacFileClicked()));
//CONNECTS
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
@ -609,6 +615,12 @@ void Preferences::setManualProxyConfigurationEnabled(bool state)
ui->useHttpsProxy->setEnabled(state);
}
void Preferences::setProxyAutoConfigEnabled(bool state)
{
ui->pacUrl->setEnabled(state);
ui->reloadPac->setEnabled(state);
}
void Preferences::saveHistoryChanged(bool stat)
{
ui->deleteHistoryOnClose->setEnabled(stat);
@ -702,6 +714,11 @@ void Preferences::changeCachePathClicked()
ui->cachePath->setText(path);
}
void Preferences::reloadPacFileClicked()
{
mApp->networkManager()->proxyFactory()->pacManager()->downloadPacFile();
}
void Preferences::showPassManager(bool state)
{
m_autoFillManager->setVisible(state);
@ -805,7 +822,7 @@ void Preferences::saveSettings()
switch (ui->newTab->currentIndex()) {
case 0:
settings.setValue("newTabUrl", "");
settings.setValue("newTabUrl", QString());
break;
case 1:
@ -974,6 +991,9 @@ void Preferences::saveSettings()
else if (ui->noProxy->isChecked()) {
proxyPreference = NetworkProxyFactory::NoProxy;
}
else if (ui->pacProxy->isChecked()) {
proxyPreference = NetworkProxyFactory::ProxyAutoConfig;
}
else {
proxyPreference = NetworkProxyFactory::DefinedProxy;
}
@ -1000,6 +1020,7 @@ void Preferences::saveSettings()
settings.setValue("HttpsUsername", ui->httpsProxyUsername->text());
settings.setValue("HttpsPassword", ui->httpsProxyPassword->text());
settings.setValue("PacUrl", ui->pacUrl->text());
settings.setValue("ProxyExceptions", ui->proxyExceptions->text().split(QLatin1Char(','), QString::SkipEmptyParts));
settings.endGroup();

View File

@ -70,10 +70,12 @@ private slots:
void allowCacheChanged(bool state);
void showPassManager(bool state);
void setManualProxyConfigurationEnabled(bool state);
void setProxyAutoConfigEnabled(bool state);
void useExternalDownManagerChanged(bool state);
void useDifferentProxyForHttpsChanged(bool state);
void showTabPreviewsChanged(bool state);
void changeCachePathClicked();
void reloadPacFileClicked();
void newTabChanged(int value);
void afterLaunchChanged(int value);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>500</height>
<height>531</height>
</rect>
</property>
<property name="windowTitle">
@ -153,6 +153,9 @@
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="generalPage">
<layout class="QGridLayout" name="gridLayout_6">
<item row="4" column="2">
@ -1426,10 +1429,108 @@
<attribute name="title">
<string>Proxy Configuration</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_20">
<item row="0" column="0" rowspan="2" colspan="2">
<layout class="QGridLayout" name="manualProxyLayout">
<item row="3" column="1">
<layout class="QGridLayout" name="gridLayout_8">
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<widget class="QLineEdit" name="pacUrl">
<property name="placeholderText">
<string>Proxy Auto-Config (.pac) file</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reloadPac">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_27">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="10" column="0" colspan="2">
<widget class="QLabel" name="label_54">
<property name="text">
<string>&lt;b&gt;Exceptions&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_27">
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>Don't use on:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="proxyExceptions"/>
</item>
</layout>
</item>
<item row="12" column="0">
<spacer name="verticalSpacer_14">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="systemProxy">
<property name="text">
<string>System proxy configuration</string>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer_29">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRadioButton" name="manualProxy">
<property name="text">
<string>Manual configuration</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QComboBox" name="proxyType">
<item>
<property name="text">
@ -1443,54 +1544,51 @@
</item>
</widget>
</item>
<item row="3" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="proxyServer"/>
</item>
<item>
<widget class="QLabel" name="label_37">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="proxyPort">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
<item>
<widget class="QLineEdit" name="proxyServer"/>
</item>
<item row="4" column="1">
<item>
<widget class="QLabel" name="label_37">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="proxyPort">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_23">
<item>
<widget class="QLabel" name="label_38">
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="4" column="2">
<item>
<widget class="QLineEdit" name="proxyUsername"/>
</item>
<item row="5" column="1">
<item>
<widget class="QLabel" name="label_39">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item row="5" column="2">
<item>
<widget class="QLineEdit" name="proxyPassword"/>
</item>
<item row="11" column="2" colspan="2">
<widget class="QLineEdit" name="proxyExceptions"/>
</item>
<item row="4" column="3">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -1503,129 +1601,96 @@
</property>
</spacer>
</item>
<item row="11" column="0" colspan="2">
<widget class="QLabel" name="label_40">
<property name="text">
<string>Don't use on:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QRadioButton" name="manualProxy">
<property name="text">
<string>Manual configuration</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QRadioButton" name="systemProxy">
<property name="text">
<string>System proxy configuration</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QRadioButton" name="noProxy">
<property name="text">
<string>Do not use proxy</string>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="horizontalSpacer_29">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="10" column="0" colspan="4">
<widget class="QLabel" name="label_54">
<property name="text">
<string>&lt;b&gt;Exceptions&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="7" column="1">
</layout>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="useHttpsProxy">
<property name="text">
<string>Use different proxy for https connection</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QLabel" name="label_55">
<property name="text">
<string>Server:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item>
<widget class="QLineEdit" name="httpsProxyServer"/>
</item>
<item>
<widget class="QLabel" name="label_58">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="httpsProxyPort">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_24">
<item>
<widget class="QLabel" name="label_56">
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item>
<widget class="QLineEdit" name="httpsProxyUsername"/>
</item>
<item>
<widget class="QLabel" name="label_57">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLineEdit" name="httpsProxyUsername"/>
</item>
<item row="9" column="2">
<item>
<widget class="QLineEdit" name="httpsProxyPassword"/>
</item>
<item row="7" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QLineEdit" name="httpsProxyServer"/>
</item>
<item>
<widget class="QLabel" name="label_58">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="httpsProxyPort">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1" colspan="3">
<widget class="QCheckBox" name="useHttpsProxy">
<property name="text">
<string>Use different proxy for https connection</string>
<item>
<spacer name="horizontalSpacer_28">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_14">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="8" column="0" colspan="2">
<widget class="QRadioButton" name="pacProxy">
<property name="text">
<string>Use script for automatic configuration:</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="noProxy">
<property name="text">
<string>Do not use proxy</string>
</property>
</spacer>
</widget>
</item>
</layout>
</widget>
@ -2626,13 +2691,9 @@
<tabstop>allowCache</tabstop>
<tabstop>cacheMB</tabstop>
<tabstop>saveHistory</tabstop>
<tabstop>proxyType</tabstop>
<tabstop>deleteHistoryOnClose</tabstop>
<tabstop>proxyServer</tabstop>
<tabstop>proxyExceptions</tabstop>
<tabstop>proxyPort</tabstop>
<tabstop>proxyUsername</tabstop>
<tabstop>proxyPassword</tabstop>
<tabstop>manualProxy</tabstop>
<tabstop>systemProxy</tabstop>
<tabstop>noProxy</tabstop>

View File

@ -51,7 +51,8 @@ HEADERS += \
cookiestest.h \
downloadstest.h \
adblocktest.h \
updatertest.h
updatertest.h \
pactest.h
SOURCES += \
qztoolstest.cpp \
@ -60,4 +61,5 @@ SOURCES += \
cookiestest.cpp \
downloadstest.cpp \
adblocktest.cpp \
updatertest.cpp
updatertest.cpp \
pactest.cpp

View File

@ -21,31 +21,30 @@
#include "downloadstest.h"
#include "adblocktest.h"
#include "updatertest.h"
#include "pactest.h"
#include <QtTest/QtTest>
#define RUN_TEST(X) \
{ \
qDebug() << ""; \
X t; \
int r = QTest::qExec(&t, argc, argv); \
if (r != 0) return 1; \
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTEST_DISABLE_KEYPAD_NAVIGATION;
QzToolsTest qzToolsTest;
QTest::qExec(&qzToolsTest, argc, argv);
FormCompleterTest formCompleterTest;
QTest::qExec(&formCompleterTest, argc, argv);
CookiesTest cookiesTest;
QTest::qExec(&cookiesTest, argc, argv);
DownloadsTest downloadsTest;
QTest::qExec(&downloadsTest, argc, argv);
AdBlockTest adblockTest;
QTest::qExec(&adblockTest, argc, argv);
UpdaterTest updaterTest;
QTest::qExec(&updaterTest, argc, argv);
RUN_TEST(QzToolsTest)
RUN_TEST(FormCompleterTest)
RUN_TEST(CookiesTest)
RUN_TEST(DownloadsTest)
RUN_TEST(AdBlockTest)
RUN_TEST(UpdaterTest)
RUN_TEST(PacTest)
return 0;
}

264
tests/autotests/pactest.cpp Normal file
View File

@ -0,0 +1,264 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "pactest.h"
#include <QtTest/QtTest>
#include <QDateTime>
void PacTest::initTestCase()
{
m_runner = new ProxyAutoConfig_Tst;
}
void PacTest::cleanupTestCase()
{
delete m_runner;
}
// Tests according to
// http://web.archive.org/web/20061218002753/wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html
void PacTest::isPlainHostNameTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<bool>("result");
QTest::newRow("doc1") << "www" << true;
QTest::newRow("doc2") << "www.netscape.com" << false;
}
void PacTest::isPlainHostNameTest()
{
QFETCH(QString, host);
QFETCH(bool, result);
QString source = QString("isPlainHostName('%1')").arg(host);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
void PacTest::dnsDomainIsTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("domain");
QTest::addColumn<bool>("result");
QTest::newRow("doc1") << "www.netscape.com" << ".netscape.com" << true;
QTest::newRow("doc2") << "www" << ".netscape.com" << false;
QTest::newRow("doc3") << "www.mcom.com" << ".netscape.com" << false;
}
void PacTest::dnsDomainIsTest()
{
QFETCH(QString, host);
QFETCH(QString, domain);
QFETCH(bool, result);
QString source = QString("dnsDomainIs('%1','%2')").arg(host, domain);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
void PacTest::localHostOrDomainIs_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("hostdom");
QTest::addColumn<bool>("result");
QTest::newRow("doc1") << "www.netscape.com" << "www.netscape.com" << true;
QTest::newRow("doc2") << "www" << "www.netscape.com" << true;
QTest::newRow("doc3") << "www.mcom.com" << "www.netscape.com" << false;
QTest::newRow("doc4") << "home.netscape.com" << "www.netscape.com" << false;
}
void PacTest::localHostOrDomainIs()
{
QFETCH(QString, host);
QFETCH(QString, hostdom);
QFETCH(bool, result);
QString source = QString("localHostOrDomainIs('%1','%2')").arg(host, hostdom);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
void PacTest::isResolvableTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<bool>("result");
QTest::newRow("doc1") << "www.netscape.com" << true;
QTest::newRow("doc2") << "bogus.domain.foobar" << false;
}
void PacTest::isResolvableTest()
{
QFETCH(QString, host);
QFETCH(bool, result);
QString source = QString("isResolvable('%1')").arg(host);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
void PacTest::isInNetTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("pattern");
QTest::addColumn<QString>("mask");
QTest::addColumn<bool>("result");
// is true if the IP address of host matches exactly 198.95.249.79.
QTest::newRow("doc1") << "198.95.249.79" << "198.95.249.79" << "255.255.255.255" << true;
QTest::newRow("doc1-2") << "198.95.249.80" << "198.95.249.79" << "255.255.255.255" << false;
QTest::newRow("doc1-3") << "198.95.248.79" << "198.95.249.79" << "255.255.255.255" << false;
QTest::newRow("doc1-4") << "198.20.249.80" << "198.95.249.79" << "255.255.255.255" << false;
QTest::newRow("doc1-5") << "123.95.249.80" << "198.95.249.79" << "255.255.255.255" << false;
// is true if the IP address of the host matches 198.95.*.*.
QTest::newRow("doc2") << "198.95.249.79" << "198.95.0.0" << "255.255.0.0" << true;
QTest::newRow("doc2-2") << "198.95.0.0" << "198.95.0.0" << "255.255.0.0" << true;
QTest::newRow("doc2-3") << "198.94.249.79" << "198.95.0.0" << "255.255.0.0" << false;
QTest::newRow("doc2-3") << "198.94.249.79" << "198.95.0.0" << "255.255.0.0" << false;
QTest::newRow("doc2-3") << "148.94.249.79" << "198.95.0.0" << "255.255.0.0" << false;
QTest::newRow("doc2-3") << "128.94.249.79" << "198.95.0.0" << "255.255.0.0" << false;
QTest::newRow("doc2-3") << "23.94.249.79" << "198.95.0.0" << "255.255.0.0" << false;
// is true if the IP address of host matches 173.194.70.* (google.com)
// if host is passed as hostname, the function needs to resolve it
QTest::newRow("resolve1") << "google.com" << "173.194.70.0" << "255.255.255.0" << true;
QTest::newRow("resolve1-2") << "yahoo.com" << "173.194.70.0" << "255.255.255.0" << false;
QTest::newRow("resolve1-3") << "netscape.com" << "173.194.70.0" << "255.255.255.0" << false;
QTest::newRow("resolve1-4") << "mozilla.com" << "173.194.70.0" << "255.255.255.0" << false;
}
void PacTest::isInNetTest()
{
QFETCH(QString, host);
QFETCH(QString, pattern);
QFETCH(QString, mask);
QFETCH(bool, result);
QString source = QString("isInNet('%1','%2','%3')").arg(host, pattern, mask);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
void PacTest::dnsResolveTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("result");
QTest::newRow("localhost") << "localhost" << "127.0.0.1";
QTest::newRow("qz") << "qupzilla.com" << "88.208.118.158"; // This may change...
}
void PacTest::dnsResolveTest()
{
QFETCH(QString, host);
QFETCH(QString, result);
QString source = QString("dnsResolve('%1')").arg(host);
QCOMPARE(m_runner->evaluate(source).toString(), result);
}
void PacTest::dnsDomainLevelsTest_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<int>("result");
QTest::newRow("doc1") << "www" << 0;
QTest::newRow("doc2") << "www.netscape.com" << 2;
}
void PacTest::dnsDomainLevelsTest()
{
QFETCH(QString, host);
QFETCH(int, result);
QString source = QString("dnsDomainLevels('%1')").arg(host);
QCOMPARE(m_runner->evaluate(source).toString().toInt(), result);
}
void PacTest::shExpMatchTest_data()
{
QTest::addColumn<QString>("str");
QTest::addColumn<QString>("shexp");
QTest::addColumn<bool>("result");
QTest::newRow("doc1") << "http://home.netscape.com/people/ari/index.html" << "*/ari/*" << true;
QTest::newRow("doc2") << "http://home.netscape.com/people/montulli/index.html" << "*/ari/*" << false;
QTest::newRow("glob1") << "com/people" << "*om/*" << true;
QTest::newRow("glob2") << "com/people" << "com/*" << true;
QTest::newRow("glob3") << "com/people" << "om/*" << false;
QTest::newRow("char1") << "com/people" << "co?/*" << true;
QTest::newRow("char2") << "com/people" << "?com/*" << false;
QTest::newRow("char3") << "com/people" << "?scom/*" << false;
QTest::newRow("char4") << "com/people" << "com/pe?ple*" << true;
QTest::newRow("dot1") << "com/people.org" << "co?/*.org" << true;
QTest::newRow("dot2") << "com/people.org" << "co?/*.or" << false;
QTest::newRow("dot3") << "com/people.org" << "com/people.*g" << true;
QTest::newRow("dot4") << "com/people.org" << "com/*.*g" << true;
}
void PacTest::shExpMatchTest()
{
QFETCH(QString, str);
QFETCH(QString, shexp);
QFETCH(bool, result);
QString source = QString("shExpMatch('%1','%2')").arg(str, shexp);
QCOMPARE(m_runner->evaluate(source).toBool(), result);
}
static QString dayName(int weekday)
{
switch (weekday) {
case 1: return "MON";
case 2: return "TUE";
case 3: return "WED";
case 4: return "THU";
case 5: return "FRI";
case 6: return "SAT";
case 7: return "SUN";
default: return "MON";
}
}
void PacTest::dateTimeTest()
{
int day = QDateTime::currentDateTime().date().day();
int hour = QDateTime::currentDateTime().time().hour();
int week = QDateTime::currentDateTime().date().dayOfWeek();
QString source = QString("weekdayRange('%1')").arg(dayName(week));
QCOMPARE(m_runner->evaluate(source).toBool(), true);
source = QString("weekdayRange('%1')").arg(dayName(week + 1));
QCOMPARE(m_runner->evaluate(source).toBool(), false);
source = QString("dateRange('%1')").arg(day);
QCOMPARE(m_runner->evaluate(source).toBool(), true);
source = QString("dateRange('%1')").arg(day + 1);
QCOMPARE(m_runner->evaluate(source).toBool(), false);
source = QString("timeRange('%1')").arg(hour);
QCOMPARE(m_runner->evaluate(source).toBool(), true);
source = QString("timeRange('%1')").arg(hour + 1);
QCOMPARE(m_runner->evaluate(source).toBool(), false);
}

74
tests/autotests/pactest.h Normal file
View File

@ -0,0 +1,74 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2013 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#ifndef PACTEST_H
#define PACTEST_H
#include <QObject>
#include "pac/proxyautoconfig.h"
class ProxyAutoConfig_Tst : public ProxyAutoConfig
{
public:
QScriptValue evaluate(const QString &source)
{
return ProxyAutoConfig::evaluate(source);
}
};
class PacTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void isPlainHostNameTest_data();
void isPlainHostNameTest();
void dnsDomainIsTest_data();
void dnsDomainIsTest();
void localHostOrDomainIs_data();
void localHostOrDomainIs();
void isResolvableTest_data();
void isResolvableTest();
void isInNetTest_data();
void isInNetTest();
void dnsResolveTest_data();
void dnsResolveTest();
// myIpAddress - how to test it?
void dnsDomainLevelsTest_data();
void dnsDomainLevelsTest();
void shExpMatchTest_data();
void shExpMatchTest();
void dateTimeTest();
private:
ProxyAutoConfig_Tst *m_runner;
};
#endif // PACTEST_H