Skip to content

Commit

Permalink
Add ability to send custom HTTP headers
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobo1 committed Apr 23, 2020
1 parent f1d02c6 commit bb80b37
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/base/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,26 @@ void Preferences::setWebUiRootFolder(const QString &path)
setValue("Preferences/WebUI/RootFolder", path);
}

bool Preferences::isWebUICustomHTTPHeadersEnabled() const
{
return value("Preferences/WebUI/CustomHTTPHeadersEnabled", false).toBool();
}

void Preferences::setWebUICustomHTTPHeadersEnabled(const bool enabled)
{
setValue("Preferences/WebUI/CustomHTTPHeadersEnabled", enabled);
}

QString Preferences::getWebUICustomHTTPHeaders() const
{
return value("Preferences/WebUI/CustomHTTPHeaders").toString();
}

void Preferences::setWebUICustomHTTPHeaders(const QString &headers)
{
setValue("Preferences/WebUI/CustomHTTPHeaders", headers);
}

bool Preferences::isDynDNSEnabled() const
{
return value("Preferences/DynDNS/Enabled", false).toBool();
Expand Down
6 changes: 6 additions & 0 deletions src/base/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ class Preferences : public QObject
QString getWebUiRootFolder() const;
void setWebUiRootFolder(const QString &path);

// WebUI custom HTTP headers
bool isWebUICustomHTTPHeadersEnabled() const;
void setWebUICustomHTTPHeadersEnabled(bool enabled);
QString getWebUICustomHTTPHeaders() const;
void setWebUICustomHTTPHeaders(const QString &headers);

// Dynamic DNS
bool isDynDNSEnabled() const;
void setDynDNSEnabled(bool enabled);
Expand Down
8 changes: 8 additions & 0 deletions src/gui/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
connect(m_ui->groupWebUIAddCustomHTTPHeaders, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUICustomHTTPHeaders, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
#endif // DISABLE_WEBUI

// RSS tab
Expand Down Expand Up @@ -862,6 +864,9 @@ void OptionsDialog::saveOptions()
// Alternative UI
pref->setAltWebUiEnabled(m_ui->groupAltWebUI->isChecked());
pref->setWebUiRootFolder(m_ui->textWebUIRootFolder->selectedPath());
// Custom HTTP headers
pref->setWebUICustomHTTPHeadersEnabled(m_ui->groupWebUIAddCustomHTTPHeaders->isChecked());
pref->setWebUICustomHTTPHeaders(m_ui->textWebUICustomHTTPHeaders->toPlainText());
}
// End Web UI
// End preferences
Expand Down Expand Up @@ -1242,6 +1247,9 @@ void OptionsDialog::loadOptions()

m_ui->groupAltWebUI->setChecked(pref->isAltWebUiEnabled());
m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUiRootFolder());
// Custom HTTP headers
m_ui->groupWebUIAddCustomHTTPHeaders->setChecked(pref->isWebUICustomHTTPHeadersEnabled());
m_ui->textWebUICustomHTTPHeaders->setPlainText(pref->getWebUICustomHTTPHeaders());
// End Web UI preferences
}

Expand Down
22 changes: 22 additions & 0 deletions src/gui/optionsdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -3220,6 +3220,28 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupWebUIAddCustomHTTPHeaders">
<property name="title">
<string>Add custom HTTP headers</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QPlainTextEdit" name="textWebUICustomHTTPHeaders">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="placeholderText">
<string>Header: value pairs, one per line</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="checkDynDNS">
<property name="title">
Expand Down
8 changes: 8 additions & 0 deletions src/webui/api/appcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ void AppController::preferencesAction()
data["web_ui_csrf_protection_enabled"] = pref->isWebUiCSRFProtectionEnabled();
data["web_ui_secure_cookie_enabled"] = pref->isWebUiSecureCookieEnabled();
data["web_ui_host_header_validation_enabled"] = pref->isWebUIHostHeaderValidationEnabled();
// Custom HTTP headers
data["web_ui_use_custom_http_headers_enabled"] = pref->isWebUICustomHTTPHeadersEnabled();
data["web_ui_custom_http_headers"] = pref->getWebUICustomHTTPHeaders();
// Update my dynamic domain name
data["dyndns_enabled"] = pref->isDynDNSEnabled();
data["dyndns_service"] = pref->getDynDNSService();
Expand Down Expand Up @@ -623,6 +626,11 @@ void AppController::setPreferencesAction()
pref->setWebUiSecureCookieEnabled(it.value().toBool());
if (hasKey("web_ui_host_header_validation_enabled"))
pref->setWebUIHostHeaderValidationEnabled(it.value().toBool());
// Custom HTTP headers
if (hasKey("web_ui_use_custom_http_headers_enabled"))
pref->setWebUICustomHTTPHeadersEnabled(it.value().toBool());
if (hasKey("web_ui_custom_http_headers"))
pref->setWebUICustomHTTPHeaders(it.value().toString());
// Update my dynamic domain name
if (hasKey("dyndns_enabled"))
pref->setDynDNSEnabled(it.value().toBool());
Expand Down
26 changes: 26 additions & 0 deletions src/webui/webapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,27 @@ void WebApplication::configure()
: QLatin1String("default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self';"))
+ (m_isClickjackingProtectionEnabled ? QLatin1String(" frame-ancestors 'self';") : QLatin1String(""))
+ (m_isHttpsEnabled ? QLatin1String(" upgrade-insecure-requests;") : QLatin1String(""));

m_useCustomHTTPHeaders = pref->isWebUICustomHTTPHeadersEnabled();
m_customHTTPHeaders.clear();
if (m_useCustomHTTPHeaders) {
const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed();
const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts);
m_customHTTPHeaders.reserve(customHeaderLines.size());

for (const QStringRef &line : customHeaderLines) {
const int idx = line.indexOf(':');
if (idx < 0) {
// require separator `:` to be present even if `value` field can be empty
LogMsg(tr("Missing ':' separator in WebUI custom HTTP header: \"%1\"").arg(line.toString()), Log::WARNING);
continue;
}

const QString header = line.left(idx).trimmed().toString();
const QString value = line.mid(idx + 1).trimmed().toString();
m_customHTTPHeaders.push_back({header, value});
}
}
}

void WebApplication::registerAPIController(const QString &scope, APIController *controller)
Expand Down Expand Up @@ -451,6 +472,11 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons
if (!m_contentSecurityPolicy.isEmpty())
header(QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), m_contentSecurityPolicy);

if (m_useCustomHTTPHeaders) {
for (const CustomHTTPHeader &i : asConst(m_customHTTPHeaders))
header(i.name, i.value);
}

return response();
}

Expand Down
9 changes: 9 additions & 0 deletions src/webui/webapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,13 @@ class WebApplication final
bool m_isHostHeaderValidationEnabled;
bool m_isHttpsEnabled;
QString m_contentSecurityPolicy;

// Custom HTTP headers
struct CustomHTTPHeader
{
QString name;
QString value;
};
bool m_useCustomHTTPHeaders;
QVector<CustomHTTPHeader> m_customHTTPHeaders;
};
24 changes: 24 additions & 0 deletions src/webui/www/private/views/preferences.html
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,14 @@
</table>
</fieldset>
</fieldset>

<fieldset class="settings">
<legend>
<input type="checkbox" id="webUIUseCustomHTTPHeadersCheckbox" onclick="qBittorrent.Preferences.updateWebUICustomHTTPHeadersSettings();" />
<label for="webUIUseCustomHTTPHeadersCheckbox">QBT_TR(Add custom HTTP headers)QBT_TR[CONTEXT=OptionsDialog]</label>
</legend>
<textarea id="webUICustomHTTPHeadersTextarea" placeholder="QBT_TR(Header: value pairs, one per line)QBT_TR[CONTEXT=OptionsDialog]" style="width: 90%;"></textarea>
</fieldset>
</fieldset>

<fieldset class="settings">
Expand Down Expand Up @@ -1139,6 +1147,7 @@
updateBypasssAuthSettings: updateBypasssAuthSettings,
updateAlternativeWebUISettings: updateAlternativeWebUISettings,
updateHostHeaderValidationSettings: updateHostHeaderValidationSettings,
updateWebUICustomHTTPHeadersSettings: updateWebUICustomHTTPHeadersSettings,
updateDynDnsSettings: updateDynDnsSettings,
registerDynDns: registerDynDns,
applyPreferences: applyPreferences
Expand Down Expand Up @@ -1381,6 +1390,11 @@
$('webui_domain_textarea').setProperty('disabled', !isHostHeaderValidationEnabled);
};

const updateWebUICustomHTTPHeadersSettings = function() {
const isEnabled = $('webUIUseCustomHTTPHeadersCheckbox').getProperty('checked');
$('webUICustomHTTPHeadersTextarea').setProperty('disabled', !isEnabled);
};

const updateDynDnsSettings = function() {
const isDynDnsEnabled = $('use_dyndns_checkbox').getProperty('checked');
$('dyndns_select').setProperty('disabled', !isDynDnsEnabled);
Expand Down Expand Up @@ -1737,6 +1751,11 @@
$('host_header_validation_checkbox').setProperty('checked', pref.web_ui_host_header_validation_enabled);
updateHostHeaderValidationSettings();

// Custom HTTP headers
$('webUIUseCustomHTTPHeadersCheckbox').setProperty('checked', pref.web_ui_use_custom_http_headers_enabled);
$('webUICustomHTTPHeadersTextarea').setProperty('value', pref.web_ui_custom_http_headers);
updateWebUICustomHTTPHeadersSettings();

// Update my dynamic domain name
$('use_dyndns_checkbox').setProperty('checked', pref.dyndns_enabled);
$('dyndns_select').setProperty('value', pref.dyndns_service);
Expand Down Expand Up @@ -2100,11 +2119,16 @@
settings.set('alternative_webui_enabled', alternative_webui_enabled);
settings.set('alternative_webui_path', webui_files_location_textarea);

// Security
settings.set('web_ui_clickjacking_protection_enabled', $('clickjacking_protection_checkbox').getProperty('checked'));
settings.set('web_ui_csrf_protection_enabled', $('csrf_protection_checkbox').getProperty('checked'));
settings.set('web_ui_secure_cookie_enabled', $('secureCookieCheckbox').getProperty('checked'));
settings.set('web_ui_host_header_validation_enabled', $('host_header_validation_checkbox').getProperty('checked'));

// Custom HTTP headers
settings.set('web_ui_use_custom_http_headers_enabled', $('webUIUseCustomHTTPHeadersCheckbox').getProperty('checked'));
settings.set('web_ui_custom_http_headers', $('webUICustomHTTPHeadersTextarea').getProperty('value'));

// Update my dynamic domain name
settings.set('dyndns_enabled', $('use_dyndns_checkbox').getProperty('checked'));
settings.set('dyndns_service', $('dyndns_select').getProperty('value'));
Expand Down

0 comments on commit bb80b37

Please sign in to comment.