Browse Source

FEATURE: Better proxy support and preferences remodeling

adaptive-webui-19844
Christophe Dumez 15 years ago
parent
commit
d5a09674ae
  1. 1
      Changelog
  2. 163
      src/bittorrent.cpp
  3. 3
      src/bittorrent.h
  4. 42
      src/eventmanager.cpp
  5. 131
      src/options_imp.cpp
  6. 28
      src/options_imp.h
  7. 76
      src/preferences.h
  8. 114
      src/ui/options.ui

1
Changelog

@ -6,6 +6,7 @@
- FEATURE: Append !.qB extension to incomplete files option (libtorrent >= v0.15 only) - FEATURE: Append !.qB extension to incomplete files option (libtorrent >= v0.15 only)
- FEATURE: Torrent files/folders can be renamed - FEATURE: Torrent files/folders can be renamed
- FEATURE: uTorrent compatible tracker list support (use torrentz.com url as a default) - FEATURE: uTorrent compatible tracker list support (use torrentz.com url as a default)
- FEATURE: Better proxy support and preferences remodeling
- COSMETIC: Use checkboxes to filter torrent content instead of comboboxes - COSMETIC: Use checkboxes to filter torrent content instead of comboboxes
- COSMETIC: Use alternating row colors in transfer list (set in program preferences) - COSMETIC: Use alternating row colors in transfer list (set in program preferences)
- COSMETIC: Added a spin box to speed limiting dialog for manual input - COSMETIC: Added a spin box to speed limiting dialog for manual input

163
src/bittorrent.cpp

@ -60,7 +60,7 @@
#define MAX_TRACKER_ERRORS 2 #define MAX_TRACKER_ERRORS 2
#define MAX_RATIO 100. #define MAX_RATIO 100.
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4}; enum ProxyType {HTTP, SOCKS4, SOCKS5, HTTP_PW, SOCKS5_PW};
// Main constructor // Main constructor
Bittorrent::Bittorrent() : preAllocateAll(false), addInPause(false), ratio_limit(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), geoipDBLoaded(false), exiting(false) { Bittorrent::Bittorrent() : preAllocateAll(false), addInPause(false), ratio_limit(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), geoipDBLoaded(false), exiting(false) {
@ -437,69 +437,61 @@ void Bittorrent::configureSession() {
} }
// * Proxy settings // * Proxy settings
proxy_settings proxySettings; proxy_settings proxySettings;
if(Preferences::isProxyEnabled()) { if(Preferences::isPeerProxyEnabled()) {
qDebug("Enabling P2P proxy"); qDebug("Enabling P2P proxy");
proxySettings.hostname = Preferences::getProxyIp().toStdString(); proxySettings.hostname = Preferences::getPeerProxyIp().toStdString();
qDebug("hostname is %s", proxySettings.hostname.c_str()); qDebug("hostname is %s", proxySettings.hostname.c_str());
proxySettings.port = Preferences::getProxyPort(); proxySettings.port = Preferences::getPeerProxyPort();
qDebug("port is %d", proxySettings.port); qDebug("port is %d", proxySettings.port);
if(Preferences::isProxyAuthEnabled()) { if(Preferences::isPeerProxyAuthEnabled()) {
proxySettings.username = Preferences::getProxyUsername().toStdString(); proxySettings.username = Preferences::getPeerProxyUsername().toStdString();
proxySettings.password = Preferences::getProxyPassword().toStdString(); proxySettings.password = Preferences::getPeerProxyPassword().toStdString();
qDebug("username is %s", proxySettings.username.c_str()); qDebug("username is %s", proxySettings.username.c_str());
qDebug("password is %s", proxySettings.password.c_str()); qDebug("password is %s", proxySettings.password.c_str());
} }
switch(Preferences::getProxyType()) {
case HTTP:
qDebug("type: http");
proxySettings.type = proxy_settings::http;
break;
case HTTP_PW:
qDebug("type: http_pw");
proxySettings.type = proxy_settings::http_pw;
break;
case SOCKS5:
qDebug("type: socks5");
proxySettings.type = proxy_settings::socks5;
break;
default:
qDebug("type: socks5_pw");
proxySettings.type = proxy_settings::socks5_pw;
break;
}
setProxySettings(proxySettings, Preferences::useProxyForTrackers(), Preferences::useProxyForPeers(), Preferences::useProxyForWebseeds(), Preferences::useProxyForDHT());
} else {
qDebug("Disabling P2P proxy");
setProxySettings(proxySettings, false, false, false, false);
}
if(Preferences::isHTTPProxyEnabled()) {
qDebug("Enabling Search HTTP proxy");
// HTTP Proxy
QString proxy_str;
switch(Preferences::getHTTPProxyType()) {
case HTTP_PW:
proxy_str = "http://"+Preferences::getHTTPProxyUsername()+":"+Preferences::getHTTPProxyPassword()+"@"+Preferences::getHTTPProxyIp()+":"+QString::number(Preferences::getHTTPProxyPort());
break;
default:
proxy_str = "http://"+Preferences::getHTTPProxyIp()+":"+QString::number(Preferences::getHTTPProxyPort());
}
// We need this for urllib in search engine plugins
#ifdef Q_WS_WIN
char proxystr[512];
snprintf(proxystr, 512, "http_proxy=%s", proxy_str.toLocal8Bit().data());
putenv(proxystr);
#else
qDebug("HTTP: proxy string: %s", proxy_str.toLocal8Bit().data());
setenv("http_proxy", proxy_str.toLocal8Bit().data(), 1);
#endif
} else {
qDebug("Disabling search proxy");
#ifdef Q_WS_WIN
putenv("http_proxy=");
#else
unsetenv("http_proxy");
#endif
} }
switch(Preferences::getPeerProxyType()) {
case HTTP:
qDebug("type: http");
proxySettings.type = proxy_settings::http;
break;
case HTTP_PW:
qDebug("type: http_pw");
proxySettings.type = proxy_settings::http_pw;
break;
case SOCKS4:
proxySettings.type = proxy_settings::socks4;
case SOCKS5:
qDebug("type: socks5");
proxySettings.type = proxy_settings::socks5;
break;
case SOCKS5_PW:
qDebug("type: socks5_pw");
proxySettings.type = proxy_settings::socks5_pw;
break;
default:
proxySettings.type = proxy_settings::none;
}
setPeerProxySettings(proxySettings);
// HTTP Proxy
proxy_settings http_proxySettings;
switch(Preferences::getHTTPProxyType()) {
case HTTP_PW:
http_proxySettings.type = proxy_settings::http_pw;
http_proxySettings.username = Preferences::getHTTPProxyUsername().toStdString();
http_proxySettings.password = Preferences::getHTTPProxyPassword().toStdString();
http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString();
http_proxySettings.port = Preferences::getHTTPProxyPort();
break;
case HTTP:
http_proxySettings.type = proxy_settings::http;
http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString();
http_proxySettings.port = Preferences::getHTTPProxyPort();
break;
default:
http_proxySettings.type = proxy_settings::none;
}
setHTTPProxySettings(http_proxySettings);
qDebug("Session configured"); qDebug("Session configured");
} }
@ -1545,32 +1537,39 @@ void Bittorrent::setSessionSettings(session_settings sessionSettings) {
} }
// Set Proxy // Set Proxy
void Bittorrent::setProxySettings(proxy_settings proxySettings, bool trackers, bool peers, bool web_seeds, bool dht) { void Bittorrent::setPeerProxySettings(proxy_settings proxySettings) {
qDebug("Set Proxy settings"); qDebug("Set Peer Proxy settings");
proxy_settings ps_null; s->set_peer_proxy(proxySettings);
ps_null.type = proxy_settings::none; s->set_dht_proxy(proxySettings);
qDebug("Setting trackers proxy"); }
if(trackers)
s->set_tracker_proxy(proxySettings); void Bittorrent::setHTTPProxySettings(proxy_settings proxySettings) {
else s->set_tracker_proxy(proxySettings);
s->set_tracker_proxy(ps_null); s->set_web_seed_proxy(proxySettings);
qDebug("Setting peers proxy"); QString proxy_str;
if(peers) switch(proxySettings.type) {
s->set_peer_proxy(proxySettings); case proxy_settings::http:
else proxy_str = "http://"+misc::toQString(proxySettings.username)+":"+misc::toQString(proxySettings.password)+"@"+misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
s->set_peer_proxy(ps_null); case proxy_settings::http_pw:
qDebug("Setting web seeds proxy"); proxy_str = "http://"+misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
if(web_seeds) default:
s->set_web_seed_proxy(proxySettings); qDebug("Disabling search proxy");
else #ifdef Q_WS_WIN
s->set_web_seed_proxy(ps_null); putenv("http_proxy=");
if(DHTEnabled) { #else
qDebug("Setting DHT proxy, %d", dht); unsetenv("http_proxy");
if(dht) #endif
s->set_dht_proxy(proxySettings); return;
else
s->set_dht_proxy(ps_null);
} }
// We need this for urllib in search engine plugins
#ifdef Q_WS_WIN
char proxystr[512];
snprintf(proxystr, 512, "http_proxy=%s", proxy_str.toLocal8Bit().data());
putenv(proxystr);
#else
qDebug("HTTP: proxy string: %s", proxy_str.toLocal8Bit().data());
setenv("http_proxy", proxy_str.toLocal8Bit().data(), 1);
#endif
} }
// Read alerts sent by the Bittorrent session // Read alerts sent by the Bittorrent session

3
src/bittorrent.h

@ -200,7 +200,8 @@ public slots:
void setGlobalRatio(float ratio); void setGlobalRatio(float ratio);
void setDeleteRatio(float ratio); void setDeleteRatio(float ratio);
void setDHTPort(int dht_port); void setDHTPort(int dht_port);
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true); void setPeerProxySettings(proxy_settings proxySettings);
void setHTTPProxySettings(proxy_settings proxySettings);
void setSessionSettings(session_settings sessionSettings); void setSessionSettings(session_settings sessionSettings);
void startTorrentsInPause(bool b); void startTorrentsInPause(bool b);
void setDefaultTempPath(QString temppath); void setDefaultTempPath(QString temppath);

42
src/eventmanager.cpp

@ -178,17 +178,29 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
Preferences::setEncryptionSetting(m["encryption"].toInt()); Preferences::setEncryptionSetting(m["encryption"].toInt());
// Proxy // Proxy
if(m.contains("proxy_type")) if(m.contains("proxy_type"))
Preferences::setProxyType(m["proxy_type"].toInt()); Preferences::setPeerProxyType(m["proxy_type"].toInt());
if(m.contains("proxy_ip")) if(m.contains("proxy_ip"))
Preferences::setProxyIp(m["proxy_ip"].toString()); Preferences::setPeerProxyIp(m["proxy_ip"].toString());
if(m.contains("proxy_port")) if(m.contains("proxy_port"))
Preferences::setProxyPort(m["proxy_port"].toUInt()); Preferences::setPeerProxyPort(m["proxy_port"].toUInt());
if(m.contains("proxy_auth_enabled")) if(m.contains("proxy_auth_enabled"))
Preferences::setProxyAuthEnabled(m["proxy_auth_enabled"].toBool()); Preferences::setPeerProxyAuthEnabled(m["proxy_auth_enabled"].toBool());
if(m.contains("proxy_username")) if(m.contains("proxy_username"))
Preferences::setProxyUsername(m["proxy_username"].toString()); Preferences::setPeerProxyUsername(m["proxy_username"].toString());
if(m.contains("proxy_password")) if(m.contains("proxy_password"))
Preferences::setProxyPassword(m["proxy_password"].toString()); Preferences::setPeerProxyPassword(m["proxy_password"].toString());
if(m.contains("http_proxy_type"))
Preferences::setHTTPProxyType(m["http_proxy_type"].toInt());
if(m.contains("http_proxy_ip"))
Preferences::setHTTPProxyIp(m["http_proxy_ip"].toString());
if(m.contains("http_proxy_port"))
Preferences::setHTTPProxyPort(m["http_proxy_port"].toUInt());
if(m.contains("http_proxy_auth_enabled"))
Preferences::setHTTPProxyAuthEnabled(m["http_proxy_auth_enabled"].toBool());
if(m.contains("http_proxy_username"))
Preferences::setHTTPProxyUsername(m["proxy_username"].toString());
if(m.contains("http_proxy_password"))
Preferences::setHTTPProxyPassword(m["proxy_password"].toString());
// IP Filter // IP Filter
if(m.contains("ip_filter_enabled")) if(m.contains("ip_filter_enabled"))
Preferences::setFilteringEnabled(m["ip_filter_enabled"].toBool()); Preferences::setFilteringEnabled(m["ip_filter_enabled"].toBool());
@ -238,12 +250,18 @@ QVariantMap EventManager::getGlobalPreferences() const {
data["lsd"] = Preferences::isLSDEnabled(); data["lsd"] = Preferences::isLSDEnabled();
data["encryption"] = Preferences::getEncryptionSetting(); data["encryption"] = Preferences::getEncryptionSetting();
// Proxy // Proxy
data["proxy_type"] = Preferences::getProxyType(); data["proxy_type"] = Preferences::getPeerProxyType();
data["proxy_ip"] = Preferences::getProxyIp(); data["proxy_ip"] = Preferences::getPeerProxyIp();
data["proxy_port"] = Preferences::getProxyPort(); data["proxy_port"] = Preferences::getPeerProxyPort();
data["proxy_auth_enabled"] = Preferences::isProxyAuthEnabled(); data["proxy_auth_enabled"] = Preferences::isPeerProxyAuthEnabled();
data["proxy_username"] = Preferences::getProxyUsername(); data["proxy_username"] = Preferences::getPeerProxyUsername();
data["proxy_password"] = Preferences::getProxyPassword(); data["proxy_password"] = Preferences::getPeerProxyPassword();
data["http_proxy_type"] = Preferences::getHTTPProxyType();
data["http_proxy_ip"] = Preferences::getHTTPProxyIp();
data["http_proxy_port"] = Preferences::getHTTPProxyPort();
data["http_proxy_auth_enabled"] = Preferences::isHTTPProxyAuthEnabled();
data["http_proxy_username"] = Preferences::getHTTPProxyUsername();
data["http_proxy_password"] = Preferences::getHTTPProxyPassword();
// IP Filter // IP Filter
data["ip_filter_enabled"] = Preferences::isFilteringEnabled(); data["ip_filter_enabled"] = Preferences::isFilteringEnabled();
data["ip_filter_path"] = Preferences::getFilter(); data["ip_filter_path"] = Preferences::getFilter();

131
src/options_imp.cpp

@ -163,10 +163,10 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){
connect(checkDHT, SIGNAL(toggled(bool)), this, SLOT(enableDHTSettings(bool))); connect(checkDHT, SIGNAL(toggled(bool)), this, SLOT(enableDHTSettings(bool)));
connect(checkDifferentDHTPort, SIGNAL(toggled(bool)), this, SLOT(enableDHTPortSettings(bool))); connect(checkDifferentDHTPort, SIGNAL(toggled(bool)), this, SLOT(enableDHTPortSettings(bool)));
// Proxy tab // Proxy tab
connect(comboProxyType_http, SIGNAL(currentIndexChanged(int)),this, SLOT(enableProxyHTTP(int))); connect(comboProxyType_http, SIGNAL(currentIndexChanged(int)),this, SLOT(enableHTTPProxy(int)));
connect(checkProxyAuth_http, SIGNAL(toggled(bool)), this, SLOT(enableProxyAuthHTTP(bool))); connect(checkProxyAuth_http, SIGNAL(toggled(bool)), this, SLOT(enableHTTPProxyAuth(bool)));
connect(comboProxyType, SIGNAL(currentIndexChanged(int)),this, SLOT(enableProxy(int))); connect(comboProxyType, SIGNAL(currentIndexChanged(int)),this, SLOT(enablePeerProxy(int)));
connect(checkProxyAuth, SIGNAL(toggled(bool)), this, SLOT(enableProxyAuth(bool))); connect(checkProxyAuth, SIGNAL(toggled(bool)), this, SLOT(enablePeerProxyAuth(bool)));
// Misc tab // Misc tab
connect(checkIPFilter, SIGNAL(toggled(bool)), this, SLOT(enableFilter(bool))); connect(checkIPFilter, SIGNAL(toggled(bool)), this, SLOT(enableFilter(bool)));
connect(checkEnableRSS, SIGNAL(toggled(bool)), this, SLOT(enableRSS(bool))); connect(checkEnableRSS, SIGNAL(toggled(bool)), this, SLOT(enableRSS(bool)));
@ -240,10 +240,6 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){
connect(checkProxyAuth, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkProxyAuth, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(textProxyUsername, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(textProxyUsername, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
connect(textProxyPassword, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(textProxyPassword, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
connect(checkProxyTrackers, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkProxyPeers, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkProxyWebseeds, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkProxyDHT, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
// Misc tab // Misc tab
connect(checkIPFilter, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkIPFilter, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(textFilterPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(textFilterPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
@ -398,23 +394,18 @@ void options_imp::saveOptions(){
settings.setValue(QString::fromUtf8("GlobalUPLimit"), getGlobalBandwidthLimits().second); settings.setValue(QString::fromUtf8("GlobalUPLimit"), getGlobalBandwidthLimits().second);
settings.setValue("ResolvePeerCountries", checkResolveCountries->isChecked()); settings.setValue("ResolvePeerCountries", checkResolveCountries->isChecked());
settings.setValue("ResolvePeerHostNames", checkResolveHosts->isChecked()); settings.setValue("ResolvePeerHostNames", checkResolveHosts->isChecked());
settings.setValue(QString::fromUtf8("ProxyType"), getProxyType()); settings.setValue(QString::fromUtf8("ProxyType"), getPeerProxyType());
//if(isProxyEnabled()) { //if(isProxyEnabled()) {
settings.beginGroup("Proxy"); settings.beginGroup("Proxy");
// Proxy is enabled, save settings // Proxy is enabled, save settings
settings.setValue(QString::fromUtf8("IP"), getProxyIp()); settings.setValue(QString::fromUtf8("IP"), getPeerProxyIp());
settings.setValue(QString::fromUtf8("Port"), getProxyPort()); settings.setValue(QString::fromUtf8("Port"), getPeerProxyPort());
settings.setValue(QString::fromUtf8("Authentication"), isProxyAuthEnabled()); settings.setValue(QString::fromUtf8("Authentication"), isPeerProxyAuthEnabled());
//if(isProxyAuthEnabled()) { //if(isProxyAuthEnabled()) {
// Credentials // Credentials
settings.setValue(QString::fromUtf8("Username"), getProxyUsername()); settings.setValue(QString::fromUtf8("Username"), getPeerProxyUsername());
settings.setValue(QString::fromUtf8("Password"), getProxyPassword()); settings.setValue(QString::fromUtf8("Password"), getPeerProxyPassword());
//} //}
// Affected connections
settings.setValue(QString::fromUtf8("AffectTrackers"), useProxyForTrackers());
settings.setValue(QString::fromUtf8("AffectPeers"), useProxyForPeers());
settings.setValue(QString::fromUtf8("AffectWebSeeds"), useProxyForWebseeds());
settings.setValue(QString::fromUtf8("AffectDHT"), useProxyForDHT());
settings.endGroup(); // End Proxy settings.endGroup(); // End Proxy
//} //}
settings.setValue(QString::fromUtf8("HTTPProxyType"), getHTTPProxyType()); settings.setValue(QString::fromUtf8("HTTPProxyType"), getHTTPProxyType());
@ -497,52 +488,31 @@ bool options_imp::isFilteringEnabled() const{
return checkIPFilter->isChecked(); return checkIPFilter->isChecked();
} }
int options_imp::getProxyType() const{ int options_imp::getPeerProxyType() const{
if(comboProxyType->currentIndex() == HTTP){ switch(comboProxyType->currentIndex()) {
if(isProxyAuthEnabled()){ case 1:
return HTTP_PW; return SOCKS4;
}else{ break;
return HTTP; case 2:
} if(isPeerProxyAuthEnabled()){
}else{ return SOCKS5_PW;
if(comboProxyType->currentIndex() == SOCKS5){
if(isProxyAuthEnabled()){
return SOCKS5_PW;
}else{
return SOCKS5;
}
} }
return SOCKS5;
default:
return -1;
} }
return -1; // disabled
} }
int options_imp::getHTTPProxyType() const { int options_imp::getHTTPProxyType() const {
if(comboProxyType_http->currentIndex() == HTTP){ if(comboProxyType_http->currentIndex() > 0){
if(isHTTPProxyAuthEnabled()){ if(isHTTPProxyAuthEnabled()){
return HTTP_PW; return HTTP_PW;
}else{
return HTTP;
} }
return HTTP;
} }
return -1; // disabled return -1; // disabled
} }
bool options_imp::useProxyForTrackers() const{
return checkProxyTrackers->isChecked();
}
bool options_imp::useProxyForPeers() const{
return checkProxyPeers->isChecked();
}
bool options_imp::useProxyForWebseeds() const{
return checkProxyWebseeds->isChecked();
}
bool options_imp::useProxyForDHT() const{
return checkProxyDHT->isChecked();
}
int options_imp::getStyle() const{ int options_imp::getStyle() const{
return comboStyle->currentIndex(); return comboStyle->currentIndex();
} }
@ -650,7 +620,7 @@ void options_imp::loadOptions(){
checkResolveCountries->setChecked(Preferences::resolvePeerCountries()); checkResolveCountries->setChecked(Preferences::resolvePeerCountries());
checkResolveHosts->setChecked(Preferences::resolvePeerHostNames()); checkResolveHosts->setChecked(Preferences::resolvePeerHostNames());
intValue = Preferences::getProxyType(); intValue = Preferences::getPeerProxyType();
if(intValue <= 0) { if(intValue <= 0) {
intValue = 0; intValue = 0;
} else { } else {
@ -661,20 +631,15 @@ void options_imp::loadOptions(){
} }
} }
comboProxyType->setCurrentIndex(intValue); comboProxyType->setCurrentIndex(intValue);
enableProxy(intValue); enablePeerProxy(intValue);
//if(isProxyEnabled()) { //if(isProxyEnabled()) {
// Proxy is enabled, save settings // Proxy is enabled, save settings
textProxyIP->setText(Preferences::getProxyIp()); textProxyIP->setText(Preferences::getPeerProxyIp());
spinProxyPort->setValue(Preferences::getProxyPort()); spinProxyPort->setValue(Preferences::getPeerProxyPort());
checkProxyAuth->setChecked(Preferences::isProxyAuthEnabled()); checkProxyAuth->setChecked(Preferences::isPeerProxyAuthEnabled());
textProxyUsername->setText(Preferences::getProxyUsername()); textProxyUsername->setText(Preferences::getPeerProxyUsername());
textProxyPassword->setText(Preferences::getProxyPassword()); textProxyPassword->setText(Preferences::getPeerProxyPassword());
enableProxyAuth(checkProxyAuth->isChecked()); enablePeerProxyAuth(checkProxyAuth->isChecked());
// Affected connections
checkProxyTrackers->setChecked(Preferences::useProxyForTrackers());
checkProxyPeers->setChecked(Preferences::useProxyForPeers());
checkProxyWebseeds->setChecked(Preferences::useProxyForWebseeds());
checkProxyDHT->setChecked(Preferences::useProxyForDHT());
//} //}
intValue = Preferences::getHTTPProxyType(); intValue = Preferences::getHTTPProxyType();
if(intValue <= 0) { if(intValue <= 0) {
@ -683,13 +648,13 @@ void options_imp::loadOptions(){
intValue = 1; intValue = 1;
} }
comboProxyType_http->setCurrentIndex(intValue); comboProxyType_http->setCurrentIndex(intValue);
enableProxyHTTP(intValue); enableHTTPProxy(intValue);
textProxyUsername_http->setText(Preferences::getHTTPProxyUsername()); textProxyUsername_http->setText(Preferences::getHTTPProxyUsername());
textProxyPassword_http->setText(Preferences::getHTTPProxyPassword()); textProxyPassword_http->setText(Preferences::getHTTPProxyPassword());
textProxyIP_http->setText(Preferences::getHTTPProxyIp()); textProxyIP_http->setText(Preferences::getHTTPProxyIp());
spinProxyPort_http->setValue(Preferences::getHTTPProxyPort()); spinProxyPort_http->setValue(Preferences::getHTTPProxyPort());
checkProxyAuth_http->setChecked(Preferences::isHTTPProxyAuthEnabled()); checkProxyAuth_http->setChecked(Preferences::isHTTPProxyAuthEnabled());
enableProxyAuthHTTP(checkProxyAuth_http->isChecked()); enableHTTPProxyAuth(checkProxyAuth_http->isChecked());
// End HTTPProxy // End HTTPProxy
// End Connection preferences // End Connection preferences
// Bittorrent preferences // Bittorrent preferences
@ -1141,15 +1106,19 @@ void options_imp::enableDeleteRatio(bool checked){
} }
} }
void options_imp::enableProxy(int index){ void options_imp::enablePeerProxy(int index){
if(index){ if(index){
//enable //enable
lblProxyIP->setEnabled(true); lblProxyIP->setEnabled(true);
textProxyIP->setEnabled(true); textProxyIP->setEnabled(true);
lblProxyPort->setEnabled(true); lblProxyPort->setEnabled(true);
spinProxyPort->setEnabled(true); spinProxyPort->setEnabled(true);
checkProxyAuth->setEnabled(true); if(index > 1) {
ProxyConnecsBox->setEnabled(true); checkProxyAuth->setEnabled(true);
} else {
checkProxyAuth->setEnabled(false);
checkProxyAuth->setChecked(false);
}
}else{ }else{
//disable //disable
lblProxyIP->setEnabled(false); lblProxyIP->setEnabled(false);
@ -1158,11 +1127,11 @@ void options_imp::enableProxy(int index){
spinProxyPort->setEnabled(false); spinProxyPort->setEnabled(false);
checkProxyAuth->setEnabled(false); checkProxyAuth->setEnabled(false);
checkProxyAuth->setEnabled(false); checkProxyAuth->setEnabled(false);
ProxyConnecsBox->setEnabled(false); checkProxyAuth->setChecked(false);
} }
} }
void options_imp::enableProxyHTTP(int index){ void options_imp::enableHTTPProxy(int index){
if(index){ if(index){
//enable //enable
lblProxyIP_http->setEnabled(true); lblProxyIP_http->setEnabled(true);
@ -1181,7 +1150,7 @@ void options_imp::enableProxyHTTP(int index){
} }
} }
void options_imp::enableProxyAuth(bool checked){ void options_imp::enablePeerProxyAuth(bool checked){
if(checked){ if(checked){
lblProxyUsername->setEnabled(true); lblProxyUsername->setEnabled(true);
lblProxyPassword->setEnabled(true); lblProxyPassword->setEnabled(true);
@ -1195,7 +1164,7 @@ void options_imp::enableProxyAuth(bool checked){
} }
} }
void options_imp::enableProxyAuthHTTP(bool checked){ void options_imp::enableHTTPProxyAuth(bool checked){
if(checked){ if(checked){
lblProxyUsername_http->setEnabled(true); lblProxyUsername_http->setEnabled(true);
lblProxyPassword_http->setEnabled(true); lblProxyPassword_http->setEnabled(true);
@ -1240,7 +1209,7 @@ bool options_imp::isDHTPortSameAsBT() const {
} }
// Proxy settings // Proxy settings
bool options_imp::isProxyEnabled() const{ bool options_imp::isPeerProxyEnabled() const{
return comboProxyType->currentIndex(); return comboProxyType->currentIndex();
} }
@ -1248,11 +1217,11 @@ bool options_imp::isHTTPProxyEnabled() const {
return comboProxyType_http->currentIndex(); return comboProxyType_http->currentIndex();
} }
bool options_imp::isProxyAuthEnabled() const{ bool options_imp::isPeerProxyAuthEnabled() const{
return checkProxyAuth->isChecked(); return checkProxyAuth->isChecked();
} }
QString options_imp::getProxyIp() const{ QString options_imp::getPeerProxyIp() const{
QString ip = textProxyIP->text(); QString ip = textProxyIP->text();
ip = ip.trimmed(); ip = ip.trimmed();
return ip; return ip;
@ -1264,7 +1233,7 @@ QString options_imp::getHTTPProxyIp() const{
return ip; return ip;
} }
unsigned short options_imp::getProxyPort() const{ unsigned short options_imp::getPeerProxyPort() const{
return spinProxyPort->value(); return spinProxyPort->value();
} }
@ -1272,7 +1241,7 @@ unsigned short options_imp::getHTTPProxyPort() const{
return spinProxyPort_http->value(); return spinProxyPort_http->value();
} }
QString options_imp::getProxyUsername() const{ QString options_imp::getPeerProxyUsername() const{
QString username = textProxyUsername->text(); QString username = textProxyUsername->text();
username = username.trimmed(); username = username.trimmed();
return username; return username;
@ -1284,7 +1253,7 @@ QString options_imp::getHTTPProxyUsername() const{
return username; return username;
} }
QString options_imp::getProxyPassword() const{ QString options_imp::getPeerProxyPassword() const{
QString password = textProxyPassword->text(); QString password = textProxyPassword->text();
password = password.trimmed(); password = password.trimmed();
return password; return password;

28
src/options_imp.h

@ -34,7 +34,7 @@
#include "ui_options.h" #include "ui_options.h"
#include <libtorrent/ip_filter.hpp> #include <libtorrent/ip_filter.hpp>
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4}; enum ProxyType {HTTP, SOCKS4, SOCKS5, HTTP_PW, SOCKS5_PW};
// actions on double-click on torrents // actions on double-click on torrents
enum DoubleClickAction {TOGGLE_PAUSE, OPEN_DEST}; enum DoubleClickAction {TOGGLE_PAUSE, OPEN_DEST};
@ -109,19 +109,15 @@ protected:
QString getHTTPProxyUsername() const; QString getHTTPProxyUsername() const;
QString getHTTPProxyPassword() const; QString getHTTPProxyPassword() const;
int getHTTPProxyType() const; int getHTTPProxyType() const;
bool isProxyEnabled() const; bool isPeerProxyEnabled() const;
bool isHTTPProxyEnabled() const; bool isHTTPProxyEnabled() const;
bool isProxyAuthEnabled() const; bool isPeerProxyAuthEnabled() const;
bool isHTTPProxyAuthEnabled() const; bool isHTTPProxyAuthEnabled() const;
QString getProxyIp() const; QString getPeerProxyIp() const;
unsigned short getProxyPort() const; unsigned short getPeerProxyPort() const;
QString getProxyUsername() const; QString getPeerProxyUsername() const;
QString getProxyPassword() const; QString getPeerProxyPassword() const;
int getProxyType() const; int getPeerProxyType() const;
bool useProxyForTrackers() const;
bool useProxyForPeers() const;
bool useProxyForWebseeds() const;
bool useProxyForDHT() const;
// IP Filter // IP Filter
bool isFilteringEnabled() const; bool isFilteringEnabled() const;
QString getFilter() const; QString getFilter() const;
@ -140,10 +136,10 @@ protected slots:
void enableDownloadLimit(bool checked); void enableDownloadLimit(bool checked);
void enableTempPathInput(bool checked); void enableTempPathInput(bool checked);
void enableDirScan(bool checked); void enableDirScan(bool checked);
void enableProxy(int comboIndex); void enablePeerProxy(int comboIndex);
void enableProxyAuth(bool checked); void enablePeerProxyAuth(bool checked);
void enableProxyHTTP(int comboIndex); void enableHTTPProxy(int comboIndex);
void enableProxyAuthHTTP(bool checked); void enableHTTPProxyAuth(bool checked);
void enableMaxConnecsLimit(bool checked); void enableMaxConnecsLimit(bool checked);
void enableMaxConnecsLimitPerTorrent(bool checked); void enableMaxConnecsLimitPerTorrent(bool checked);
void enableMaxUploadsLimitPerTorrent(bool checked); void enableMaxUploadsLimitPerTorrent(bool checked);

76
src/preferences.h

@ -294,116 +294,126 @@ public:
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Authentication"), false).toBool(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Authentication"), false).toBool();
} }
static void setHTTPProxyAuthEnabled(bool enabled) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxy/Authentication"), enabled);
}
static QString getHTTPProxyIp() { static QString getHTTPProxyIp() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString();
} }
static void setHTTPProxyIp(QString ip) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), ip);
}
static unsigned short getHTTPProxyPort() { static unsigned short getHTTPProxyPort() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toInt(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toInt();
} }
static void setHTTPProxyPort(unsigned short port) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), port);
}
static QString getHTTPProxyUsername() { static QString getHTTPProxyUsername() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Username"), QString()).toString(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Username"), QString()).toString();
} }
static void setHTTPProxyUsername(QString username) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxy/Username"), username);
}
static QString getHTTPProxyPassword() { static QString getHTTPProxyPassword() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Password"), QString()).toString(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Password"), QString()).toString();
} }
static void setHTTPProxyPassword(QString password) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxy/Password"), password);
}
static int getHTTPProxyType() { static int getHTTPProxyType() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt(); return settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt();
} }
static bool isProxyEnabled() { static void setHTTPProxyType(int type) {
QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), type);
}
static bool isPeerProxyEnabled() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt() > 0; return settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt() > 0;
} }
static bool isProxyAuthEnabled() { static bool isPeerProxyAuthEnabled() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Authentication"), false).toBool(); return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Authentication"), false).toBool();
} }
static void setProxyAuthEnabled(bool enabled) { static void setPeerProxyAuthEnabled(bool enabled) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Authentication"), enabled); settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Authentication"), enabled);
} }
static QString getProxyIp() { static QString getPeerProxyIp() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString(); return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString();
} }
static void setProxyIp(QString ip) { static void setPeerProxyIp(QString ip) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/IP"), ip); settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/IP"), ip);
} }
static unsigned short getProxyPort() { static unsigned short getPeerProxyPort() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toInt(); return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toInt();
} }
static void setProxyPort(unsigned short port) { static void setPeerProxyPort(unsigned short port) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Port"), port); settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Port"), port);
} }
static QString getProxyUsername() { static QString getPeerProxyUsername() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString(); return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString();
} }
static void setProxyUsername(QString username) { static void setPeerProxyUsername(QString username) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Username"), username); settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Username"), username);
} }
static QString getProxyPassword() { static QString getPeerProxyPassword() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString(); return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString();
} }
static void setProxyPassword(QString password) { static void setPeerProxyPassword(QString password) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Password"), password); settings.setValue(QString::fromUtf8("Preferences/Connection/Proxy/Password"), password);
} }
static int getProxyType() { static int getPeerProxyType() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt(); return settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt();
} }
static void setProxyType(int type) { static void setPeerProxyType(int type) {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
settings.setValue(QString::fromUtf8("Preferences/Connection/ProxyType"), type); settings.setValue(QString::fromUtf8("Preferences/Connection/ProxyType"), type);
} }
static bool useProxyForTrackers() {
QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/AffectTrackers"), true).toBool();
}
static bool useProxyForPeers() {
QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/AffectPeers"), true).toBool();
}
static bool useProxyForWebseeds() {
QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/AffectWebSeeds"), true).toBool();
}
static bool useProxyForDHT() {
QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Connection/Proxy/AffectDHT"), true).toBool();
}
// Bittorrent options // Bittorrent options
static int getMaxConnecs() { static int getMaxConnecs() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");

114
src/ui/options.ui

@ -1845,15 +1845,15 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>370</width> <width>620</width>
<height>428</height> <height>495</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_20"> <layout class="QVBoxLayout" name="verticalLayout_16">
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Search engine proxy settings</string> <string>HTTP Communications (trackers, Web seeds, search engine)</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
@ -1885,7 +1885,7 @@ QGroupBox {
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Proxy:</string> <string>Host:</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -2043,7 +2043,7 @@ QGroupBox {
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="title"> <property name="title">
<string>Bittorrent proxy settings</string> <string>Peer Communications</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
@ -2064,7 +2064,7 @@ QGroupBox {
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>HTTP</string> <string>SOCKS4</string>
</property> </property>
</item> </item>
<item> <item>
@ -2080,7 +2080,7 @@ QGroupBox {
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Proxy:</string> <string>Host:</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -2229,100 +2229,18 @@ QGroupBox {
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QGroupBox" name="ProxyConnecsBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>110</height>
</size>
</property>
<property name="title">
<string>Affected connections</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QCheckBox" name="checkProxyTrackers">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use proxy for connections to trackers</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkProxyPeers">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use proxy for connections to regular peers</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkProxyDHT">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use proxy for DHT messages</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkProxyWebseeds">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use proxy for connections to web seeds</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>40</height> <height>180</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -2345,8 +2263,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>290</width> <width>620</width>
<height>124</height> <height>495</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_21"> <layout class="QVBoxLayout" name="verticalLayout_21">
@ -2442,8 +2360,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>219</width> <width>620</width>
<height>221</height> <height>495</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_23"> <layout class="QVBoxLayout" name="verticalLayout_23">
@ -2606,8 +2524,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>452</width> <width>620</width>
<height>192</height> <height>495</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_24"> <layout class="QVBoxLayout" name="verticalLayout_24">

Loading…
Cancel
Save