"Fossies" - the Fresh Open Source Software Archive

Member "filezilla-3.44.2/src/interface/sitemanager_site.cpp" (14 Aug 2019, 56216 Bytes) of package /linux/misc/FileZilla_3.44.2_src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sitemanager_site.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.44.1_vs_3.44.2.

    1 #include <filezilla.h>
    2 #include "sitemanager_site.h"
    3 
    4 #include "filezillaapp.h"
    5 #include "fzputtygen_interface.h"
    6 #include "Options.h"
    7 #if USE_MAC_SANDBOX
    8 #include "osx_sandbox_userdirs.h"
    9 #endif
   10 #include "sitemanager_dialog.h"
   11 #if ENABLE_STORJ
   12 #include "storj_key_interface.h"
   13 #endif
   14 #include "xrc_helper.h"
   15 
   16 #include <s3sse.h>
   17 
   18 #include <libfilezilla/translate.hpp>
   19 
   20 #include <wx/dcclient.h>
   21 #include <wx/gbsizer.h>
   22 #include <wx/hyperlink.h>
   23 #include <wx/statline.h>
   24 
   25 #ifdef __WXMSW__
   26 #include "commctrl.h"
   27 #endif
   28 
   29 #include <array>
   30 
   31 BEGIN_EVENT_TABLE(CSiteManagerSite, wxNotebook)
   32 EVT_CHOICE(XRCID("ID_PROTOCOL"), CSiteManagerSite::OnProtocolSelChanged)
   33 EVT_CHOICE(XRCID("ID_LOGONTYPE"), CSiteManagerSite::OnLogontypeSelChanged)
   34 EVT_RADIOBUTTON(XRCID("ID_CHARSET_AUTO"), CSiteManagerSite::OnCharsetChange)
   35 EVT_RADIOBUTTON(XRCID("ID_CHARSET_UTF8"), CSiteManagerSite::OnCharsetChange)
   36 EVT_RADIOBUTTON(XRCID("ID_CHARSET_CUSTOM"), CSiteManagerSite::OnCharsetChange)
   37 EVT_CHECKBOX(XRCID("ID_LIMITMULTIPLE"), CSiteManagerSite::OnLimitMultipleConnectionsChanged)
   38 EVT_BUTTON(XRCID("ID_BROWSE"), CSiteManagerSite::OnRemoteDirBrowse)
   39 EVT_BUTTON(XRCID("ID_KEYFILE_BROWSE"), CSiteManagerSite::OnKeyFileBrowse)
   40 EVT_BUTTON(XRCID("ID_ENCRYPTIONKEY_GENERATE"), CSiteManagerSite::OnGenerateEncryptionKey)
   41 END_EVENT_TABLE()
   42 
   43 namespace {
   44 struct ProtocolGroup {
   45     std::wstring name;
   46     std::vector<std::pair<ServerProtocol, std::wstring>> protocols;
   47 };
   48 
   49 std::array<ProtocolGroup, 2> const& protocolGroups()
   50 {
   51     static auto const groups = std::array<ProtocolGroup, 2>{{
   52         {
   53             fztranslate("FTP - File Transfer Protocol"), {
   54                 { FTP, fztranslate("Use explicit FTP over TLS if available") },
   55                 { FTPES, fztranslate("Require explicit FTP over TLS") },
   56                 { FTPS, fztranslate("Require implicit FTP over TLS") },
   57                 { INSECURE_FTP, fztranslate("Only use plain FTP (insecure)") }
   58             }
   59         },
   60         {
   61             fztranslate("WebDAV"), {
   62                 { WEBDAV, fztranslate("Using secure HTTPS") },
   63                 { INSECURE_WEBDAV, fztranslate("Using insecure HTTP") }
   64             }
   65         }
   66     }};
   67     return groups;
   68 }
   69 
   70 std::pair<std::array<ProtocolGroup, 2>::const_iterator, std::vector<std::pair<ServerProtocol, std::wstring>>::const_iterator> findGroup(ServerProtocol protocol)
   71 {
   72     auto const& groups = protocolGroups();
   73     for (auto group = groups.cbegin(); group != groups.cend(); ++group) {
   74         for (auto entry = group->protocols.cbegin(); entry != group->protocols.cend(); ++entry) {
   75             if (entry->first == protocol) {
   76                 return std::make_pair(group, entry);
   77             }
   78         }
   79     }
   80 
   81     return std::make_pair(groups.cend(), std::vector<std::pair<ServerProtocol, std::wstring>>::const_iterator());
   82 }
   83 }
   84 
   85 CSiteManagerSite::CSiteManagerSite(CSiteManagerDialog &sitemanager)
   86     : sitemanager_(sitemanager)
   87 {
   88 }
   89 
   90 bool CSiteManagerSite::Load(wxWindow* parent)
   91 {
   92     Create(parent, -1);
   93 
   94     DialogLayout lay(static_cast<wxTopLevelWindow*>(wxGetTopLevelParent(parent)));
   95 
   96     {
   97         wxPanel* generalPage = new wxPanel(this);
   98         AddPage(generalPage, _("General"));
   99 
  100         auto* main = lay.createMain(generalPage, 1);
  101         main->AddGrowableCol(0);
  102 
  103         auto * bag = lay.createGridBag(2);
  104         bag->AddGrowableCol(1);
  105         main->Add(bag, 0, wxGROW);
  106 
  107         lay.gbNewRow(bag);
  108         lay.gbAdd(bag, new wxStaticText(generalPage, -1, _("Pro&tocol:")), lay.valign);
  109         lay.gbAdd(bag, new wxChoice(generalPage, XRCID("ID_PROTOCOL")), lay.valigng);
  110 
  111         lay.gbNewRow(bag);
  112         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_HOST_DESC"), _("&Host:")), lay.valign);
  113         auto * row = lay.createFlex(0, 1);
  114         row->AddGrowableCol(0);
  115         lay.gbAdd(bag, row, lay.valigng);
  116         row->Add(new wxTextCtrl(generalPage, XRCID("ID_HOST")), lay.valigng);
  117         row->Add(new wxStaticText(generalPage, -1, _("&Port:")), lay.valign);
  118         auto* port = new wxTextCtrl(generalPage, XRCID("ID_PORT"), wxString(), wxDefaultPosition, wxSize(lay.dlgUnits(27), -1));
  119         port->SetMaxLength(5);
  120         row->Add(port, lay.valign);
  121 
  122         lay.gbNewRow(bag);
  123         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_ENCRYPTION_DESC"), _("&Encryption:")), lay.valign);
  124         auto brow = new wxBoxSizer(wxHORIZONTAL);
  125         lay.gbAdd(bag, brow, lay.valigng);
  126         brow->Add(new wxChoice(generalPage, XRCID("ID_ENCRYPTION")), 1);
  127         brow->Add(new wxHyperlinkCtrl(generalPage, XRCID("ID_SIGNUP"), _("Signup"), L"https://app.storj.io/#/signup"), lay.valign)->Show(false);
  128         brow->AddSpacer(0);
  129 
  130         lay.gbNewRow(bag);
  131         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_EXTRA_HOST_DESC"), L""), lay.valign)->Show(false);
  132         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_EXTRA_HOST")), lay.valigng)->Show(false);
  133 
  134         lay.gbAddRow(bag, new wxStaticLine(generalPage), lay.grow);
  135 
  136         lay.gbNewRow(bag);
  137         lay.gbAdd(bag, new wxStaticText(generalPage, -1, _("&Logon Type:")), lay.valign);
  138         lay.gbAdd(bag, new wxChoice(generalPage, XRCID("ID_LOGONTYPE")), lay.valigng);
  139 
  140         lay.gbNewRow(bag);
  141         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_USER_DESC"), _("&User:")), lay.valign);
  142         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_USER")), lay.valigng);
  143 
  144         lay.gbNewRow(bag);
  145         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_EXTRA_USER_DESC"), L""), lay.valign)->Show(false);
  146         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_EXTRA_USER")), lay.valigng)->Show(false);
  147 
  148         lay.gbNewRow(bag);
  149         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_PASS_DESC"), _("Pass&word:")), lay.valign);
  150         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_PASS"), L"", wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD), lay.valigng);
  151 
  152         lay.gbNewRow(bag);
  153         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_ACCOUNT_DESC"), _("&Account:")), lay.valign);
  154         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_ACCOUNT")), lay.valigng);
  155 
  156         lay.gbNewRow(bag);
  157         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_KEYFILE_DESC"), _("&Key file:")), lay.valign)->Show(false);
  158         row = lay.createFlex(0, 1);
  159         row->AddGrowableCol(0);
  160         lay.gbAdd(bag, row, lay.valigng);
  161         row->Add(new wxTextCtrl(generalPage, XRCID("ID_KEYFILE")), lay.valigng)->Show(false);
  162         row->Add(new wxButton(generalPage, XRCID("ID_KEYFILE_BROWSE"), _("Browse...")), lay.valign)->Show(false);
  163 
  164         lay.gbNewRow(bag);
  165         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_ENCRYPTIONKEY_DESC"), _("Encryption &key:")), lay.valign)->Show(false);
  166         row = lay.createFlex(0, 1);
  167         row->AddGrowableCol(0);
  168         lay.gbAdd(bag, row, lay.valigng);
  169         row->Add(new wxTextCtrl(generalPage, XRCID("ID_ENCRYPTIONKEY")), lay.valigng)->Show(false);
  170         row->Add(new wxButton(generalPage, XRCID("ID_ENCRYPTIONKEY_GENERATE"), _("Generate...")), lay.valign)->Show(false);
  171 
  172         lay.gbNewRow(bag);
  173         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_EXTRA_CREDENTIALS_DESC"), L""), lay.valign)->Show(false);
  174         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_EXTRA_CREDENTIALS"), L"", wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD), lay.valigng)->Show(false);
  175 
  176         lay.gbNewRow(bag);
  177         lay.gbAdd(bag, new wxStaticText(generalPage, XRCID("ID_EXTRA_EXTRA_DESC"), L""), lay.valign)->Show(false);
  178         lay.gbAdd(bag, new wxTextCtrl(generalPage, XRCID("ID_EXTRA_EXTRA")), lay.valigng)->Show(false);
  179 
  180         main->Add(new wxStaticLine(generalPage), lay.grow);
  181 
  182         row = lay.createFlex(0, 1);
  183         main->Add(row);
  184         row->Add(new wxStaticText(generalPage, -1, _("&Background color:")), lay.valign);
  185         row->Add(new wxChoice(generalPage, XRCID("ID_COLOR")), lay.valign);
  186 
  187         main->Add(new wxStaticText(generalPage, -1, _("Co&mments:")));
  188         main->Add(new wxTextCtrl(generalPage, XRCID("ID_COMMENTS"), L"", wxDefaultPosition, wxSize(-1, lay.dlgUnits(43)), wxTE_MULTILINE), 1, wxGROW);
  189         main->AddGrowableRow(main->GetEffectiveRowsCount() - 1);
  190     }
  191 
  192     {
  193         wxPanel* advancedPage = new wxPanel(this);
  194         AddPage(advancedPage, _("Advanced"));
  195 
  196         auto * main = lay.createMain(advancedPage, 1);
  197         main->AddGrowableCol(0);
  198         auto* row = lay.createFlex(0, 1);
  199         main->Add(row);
  200 
  201         row->Add(new wxStaticText(advancedPage, XRCID("ID_SERVERTYPE_LABEL"), _("Server &type:")), lay.valign);
  202         row->Add(new wxChoice(advancedPage, XRCID("ID_SERVERTYPE")), lay.valign);
  203         main->AddSpacer(0);
  204         main->Add(new wxCheckBox(advancedPage, XRCID("ID_BYPASSPROXY"), _("B&ypass proxy")));
  205 
  206         main->Add(new wxStaticLine(advancedPage), lay.grow);
  207 
  208         main->Add(new wxStaticText(advancedPage, -1, _("Default &local directory:")));
  209 
  210         row = lay.createFlex(0, 1);
  211         main->Add(row, lay.grow);
  212         row->AddGrowableCol(0);
  213         row->Add(new wxTextCtrl(advancedPage, XRCID("ID_LOCALDIR")), lay.valigng);
  214         row->Add(new wxButton(advancedPage, XRCID("ID_BROWSE"), _("&Browse...")), lay.valign);
  215         main->AddSpacer(0);
  216         main->Add(new wxStaticText(advancedPage, -1, _("Default r&emote directory:")));
  217         main->Add(new wxTextCtrl(advancedPage, XRCID("ID_REMOTEDIR")), lay.grow);
  218         main->AddSpacer(0);
  219         main->Add(new wxCheckBox(advancedPage, XRCID("ID_SYNC"), _("&Use synchronized browsing")));
  220         main->Add(new wxCheckBox(advancedPage, XRCID("ID_COMPARISON"), _("Directory comparison")));
  221 
  222         main->Add(new wxStaticLine(advancedPage), lay.grow);
  223 
  224         main->Add(new wxStaticText(advancedPage, -1, _("&Adjust server time, offset by:")));
  225         row = lay.createFlex(0, 1);
  226         main->Add(row);
  227         auto* hours = new wxSpinCtrl(advancedPage, XRCID("ID_TIMEZONE_HOURS"), wxString(), wxDefaultPosition, wxSize(lay.dlgUnits(26), -1));
  228         hours->SetRange(-24, 24);
  229         row->Add(hours, lay.valign);
  230         row->Add(new wxStaticText(advancedPage, -1, _("Hours,")), lay.valign);
  231         auto* minutes = new wxSpinCtrl(advancedPage, XRCID("ID_TIMEZONE_MINUTES"), wxString(), wxDefaultPosition, wxSize(lay.dlgUnits(26), -1));
  232         minutes->SetRange(-59, 59);
  233         row->Add(minutes, lay.valign);
  234         row->Add(new wxStaticText(advancedPage, -1, _("Minutes")), lay.valign);
  235     }
  236 
  237     {
  238         wxPanel* transferPage = new wxPanel(this);
  239         AddPage(transferPage, _("Transfer Settings"));
  240 
  241         auto * main = lay.createMain(transferPage, 1);
  242         main->Add(new wxStaticText(transferPage, XRCID("ID_TRANSFERMODE_LABEL"), _("&Transfer mode:")));
  243         auto * row = lay.createFlex(0, 1);
  244         main->Add(row);
  245         row->Add(new wxRadioButton(transferPage, XRCID("ID_TRANSFERMODE_DEFAULT"), _("D&efault"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP), lay.valign);
  246         row->Add(new wxRadioButton(transferPage, XRCID("ID_TRANSFERMODE_ACTIVE"), _("&Active")), lay.valign);
  247         row->Add(new wxRadioButton(transferPage, XRCID("ID_TRANSFERMODE_PASSIVE"), _("&Passive")), lay.valign);
  248         main->AddSpacer(0);
  249 
  250         main->Add(new wxCheckBox(transferPage, XRCID("ID_LIMITMULTIPLE"), _("&Limit number of simultaneous connections")));
  251         row = lay.createFlex(0, 1);
  252         main->Add(row, 0, wxLEFT, lay.dlgUnits(10));
  253         row->Add(new wxStaticText(transferPage, -1, _("&Maximum number of connections:")), lay.valign);
  254         auto * spin = new wxSpinCtrl(transferPage, XRCID("ID_MAXMULTIPLE"), wxString(), wxDefaultPosition, wxSize(lay.dlgUnits(26), -1));
  255         spin->SetRange(1, 10);
  256         row->Add(spin, lay.valign);
  257     }
  258 
  259     {
  260         m_pCharsetPage = new wxPanel(this);
  261         AddPage(m_pCharsetPage, _("Charset"));
  262 
  263         auto * main = lay.createMain(m_pCharsetPage, 1);
  264         main->Add(new wxStaticText(m_pCharsetPage, -1, _("The server uses following charset encoding for filenames:")));
  265         main->Add(new wxRadioButton(m_pCharsetPage, XRCID("ID_CHARSET_AUTO"), _("&Autodetect"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
  266         main->Add(new wxStaticText(m_pCharsetPage, -1, _("Uses UTF-8 if the server supports it, else uses local charset.")), 0, wxLEFT, 18);
  267         main->Add(new wxRadioButton(m_pCharsetPage, XRCID("ID_CHARSET_UTF8"), _("Force &UTF-8")));
  268         main->Add(new wxRadioButton(m_pCharsetPage, XRCID("ID_CHARSET_CUSTOM"), _("Use &custom charset")));
  269         auto * row = lay.createFlex(0, 1);
  270         row->Add(new wxStaticText(m_pCharsetPage, -1, _("&Encoding:")), lay.valign);
  271         row->Add(new wxTextCtrl(m_pCharsetPage, XRCID("ID_ENCODING")), 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 18);
  272         main->Add(row);
  273         main->AddSpacer(lay.dlgUnits(6));
  274         main->Add(new wxStaticText(m_pCharsetPage, -1, _("Using the wrong charset can result in filenames not displaying properly.")));
  275     }
  276 
  277     {
  278         m_pS3Page = new wxPanel(this);
  279         AddPage(m_pS3Page, L"S3");
  280 
  281         auto * main = lay.createMain(m_pS3Page, 1);
  282         main->AddGrowableCol(0);
  283         main->Add(new wxStaticText(m_pS3Page, -1, _("Server Side Encryption:")));
  284 
  285         main->Add(new wxRadioButton(m_pS3Page, XRCID("ID_S3_NOENCRYPTION"), _("N&o encryption"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
  286 
  287         main->Add(new wxRadioButton(m_pS3Page, XRCID("ID_S3_AES256"), _("&AWS S3 encryption")));
  288 
  289         main->Add(new wxRadioButton(m_pS3Page, XRCID("ID_S3_AWSKMS"), _("AWS &KMS encryption")));
  290         auto * row = lay.createFlex(2);
  291         row->AddGrowableCol(1);
  292         main->Add(row, 0, wxLEFT|wxGROW, lay.dlgUnits(10));
  293         row->Add(new wxStaticText(m_pS3Page, -1, _("&Select a key:")), lay.valign);
  294         auto * choice = new wxChoice(m_pS3Page, XRCID("ID_S3_KMSKEY"));
  295         choice->Append(_("Default (AWS/S3)"));
  296         choice->Append(_("Custom KMS ARN"));
  297         row->Add(choice, lay.valigng);
  298         row->Add(new wxStaticText(m_pS3Page, -1, _("C&ustom KMS ARN:")), lay.valign);
  299         row->Add(new wxTextCtrl(m_pS3Page, XRCID("ID_S3_CUSTOM_KMS")), lay.valigng);
  300 
  301         main->Add(new wxRadioButton(m_pS3Page, XRCID("ID_S3_CUSTOMER_ENCRYPTION"), _("Cu&stomer encryption")));
  302         row = lay.createFlex(2);
  303         row->AddGrowableCol(1);
  304         main->Add(row, 0, wxLEFT | wxGROW, lay.dlgUnits(10));
  305         row->Add(new wxStaticText(m_pS3Page, -1, _("Cus&tomer Key:")), lay.valign);
  306         row->Add(new wxTextCtrl(m_pS3Page, XRCID("ID_S3_CUSTOMER_KEY")), lay.valigng);
  307     }
  308 
  309     extraParameters_[ParameterSection::host].emplace_back(XRCCTRL(*this, "ID_EXTRA_HOST_DESC", wxStaticText), XRCCTRL(*this, "ID_EXTRA_HOST", wxTextCtrl));
  310     extraParameters_[ParameterSection::user].emplace_back(XRCCTRL(*this, "ID_EXTRA_USER_DESC", wxStaticText), XRCCTRL(*this, "ID_EXTRA_USER", wxTextCtrl));
  311     extraParameters_[ParameterSection::credentials].emplace_back(XRCCTRL(*this, "ID_EXTRA_CREDENTIALS_DESC", wxStaticText), XRCCTRL(*this, "ID_EXTRA_CREDENTIALS", wxTextCtrl));
  312     extraParameters_[ParameterSection::extra].emplace_back(XRCCTRL(*this, "ID_EXTRA_EXTRA_DESC", wxStaticText), XRCCTRL(*this, "ID_EXTRA_EXTRA", wxTextCtrl));
  313 
  314     InitProtocols();
  315 
  316     m_totalPages = GetPageCount();
  317 
  318     int const charsetPageIndex = FindPage(m_pCharsetPage);
  319     m_charsetPageText = GetPageText(charsetPageIndex);
  320     wxGetApp().GetWrapEngine()->WrapRecursive(m_pCharsetPage, 1.3);
  321 
  322     auto generalSizer = static_cast<wxGridBagSizer*>(xrc_call(*this, "ID_PROTOCOL", &wxWindow::GetContainingSizer));
  323     generalSizer->SetEmptyCellSize(wxSize(-generalSizer->GetHGap(), -generalSizer->GetVGap()));
  324 
  325     GetPage(0)->GetSizer()->Fit(GetPage(0));
  326 
  327 #ifdef __WXMSW__
  328     // Make pages at least wide enough to fit all tabs
  329     HWND hWnd = (HWND)GetHandle();
  330 
  331     int width = 4;
  332     for (unsigned int i = 0; i < GetPageCount(); ++i) {
  333         RECT tab_rect{};
  334         if (TabCtrl_GetItemRect(hWnd, i, &tab_rect)) {
  335             width += tab_rect.right - tab_rect.left;
  336         }
  337     }
  338 #else
  339     // Make pages at least wide enough to fit all tabs
  340     int width = 10; // Guessed
  341     wxClientDC dc(this);
  342     for (unsigned int i = 0; i < GetPageCount(); ++i) {
  343         wxCoord w, h;
  344         dc.GetTextExtent(GetPageText(i), &w, &h);
  345 
  346         width += w;
  347 #ifdef __WXMAC__
  348         width += 20; // Guessed
  349 #else
  350         width += 20;
  351 #endif
  352     }
  353 #endif
  354 
  355     wxSize const descSize = XRCCTRL(*this, "ID_ENCRYPTION_DESC", wxWindow)->GetSize();
  356     wxSize const encSize = XRCCTRL(*this, "ID_ENCRYPTION", wxWindow)->GetSize();
  357 
  358     int dataWidth = std::max(encSize.GetWidth(), XRCCTRL(*this, "ID_PROTOCOL", wxWindow)->GetSize().GetWidth());
  359 
  360     width = std::max(width, static_cast<int>(descSize.GetWidth() * 2 + dataWidth + generalSizer->GetHGap() * 3));
  361 
  362     wxSize page_min_size = GetPage(0)->GetSizer()->GetMinSize();
  363     if (page_min_size.x < width) {
  364         page_min_size.x = width;
  365         GetPage(0)->GetSizer()->SetMinSize(page_min_size);
  366     }
  367 
  368     // Set min height of general page sizer
  369     generalSizer->SetMinSize(generalSizer->GetMinSize());
  370 
  371     // Set min height of encryption row
  372     auto encSizer = xrc_call(*this, "ID_ENCRYPTION", &wxWindow::GetContainingSizer);
  373     encSizer->GetItem(encSizer->GetItemCount() - 1)->SetMinSize(0, std::max(descSize.GetHeight(), encSize.GetHeight()));
  374 
  375     return true;
  376 }
  377 
  378 void CSiteManagerSite::InitProtocols()
  379 {
  380     wxChoice *pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice);
  381     if (!pProtocol) {
  382         return;
  383     }
  384 
  385     for (auto const& proto : CServer::GetDefaultProtocols()) {
  386         auto const entry = findGroup(proto);
  387         if (entry.first != protocolGroups().cend()) {
  388             if (entry.second == entry.first->protocols.cbegin()) {
  389                 mainProtocolListIndex_[proto] = pProtocol->Append(entry.first->name);
  390             }
  391             else {
  392                 mainProtocolListIndex_[proto] = mainProtocolListIndex_[entry.first->protocols.front().first];
  393             }
  394         }
  395         else {
  396             mainProtocolListIndex_[proto] = pProtocol->Append(CServer::GetProtocolName(proto));
  397         }
  398     }
  399 
  400     wxChoice *pChoice = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice);
  401     wxASSERT(pChoice);
  402     for (int i = 0; i < SERVERTYPE_MAX; ++i) {
  403         pChoice->Append(CServer::GetNameFromServerType(static_cast<ServerType>(i)));
  404     }
  405 
  406     pChoice = XRCCTRL(*this, "ID_LOGONTYPE", wxChoice);
  407     wxASSERT(pChoice);
  408     for (int i = 0; i < static_cast<int>(LogonType::count); ++i) {
  409         pChoice->Append(GetNameFromLogonType(static_cast<LogonType>(i)));
  410     }
  411 
  412     wxChoice* pColors = XRCCTRL(*this, "ID_COLOR", wxChoice);
  413     if (pColors) {
  414         for (int i = 0; ; ++i) {
  415             wxString name = CSiteManager::GetColourName(i);
  416             if (name.empty()) {
  417                 break;
  418             }
  419             pColors->AppendString(wxGetTranslation(name));
  420         }
  421     }
  422 }
  423 
  424 void CSiteManagerSite::SetProtocol(ServerProtocol protocol)
  425 {
  426     wxChoice* pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice);
  427     wxChoice* pEncryption = XRCCTRL(*this, "ID_ENCRYPTION", wxChoice);
  428     wxStaticText* pEncryptionDesc = XRCCTRL(*this, "ID_ENCRYPTION_DESC", wxStaticText);
  429 
  430     auto const entry = findGroup(protocol);
  431     if (entry.first != protocolGroups().cend()) {
  432         pEncryption->Clear();
  433         for (auto const& prot : entry.first->protocols) {
  434             std::wstring name = prot.second;
  435             if (!CServer::ProtocolHasFeature(prot.first, ProtocolFeature::Security)) {
  436                 name += ' ';
  437                 name += 0x26a0; // Unicode's warning emoji
  438                 name += 0xfe0f; // Variant selector, makes it colorful
  439             }
  440             pEncryption->AppendString(name);
  441         }
  442         pEncryption->Show();
  443         pEncryptionDesc->Show();
  444         pEncryption->SetSelection(entry.second - entry.first->protocols.cbegin());
  445     }
  446     else {
  447         pEncryption->Hide();
  448         pEncryptionDesc->Hide();
  449     }
  450 
  451     auto const protoIt = mainProtocolListIndex_.find(protocol);
  452     if (protoIt != mainProtocolListIndex_.cend()) {
  453         pProtocol->SetSelection(protoIt->second);
  454     }
  455     else if (protocol != ServerProtocol::UNKNOWN) {
  456         auto const entry = findGroup(protocol);
  457         if (entry.first != protocolGroups().cend()) {
  458             mainProtocolListIndex_[protocol] = pProtocol->Append(entry.first->name);
  459             for (auto const& sub : entry.first->protocols) {
  460                 mainProtocolListIndex_[sub.first] = mainProtocolListIndex_[protocol];
  461             }
  462         }
  463         else {
  464             mainProtocolListIndex_[protocol] = pProtocol->Append(CServer::GetProtocolName(protocol));
  465         }
  466 
  467         pProtocol->SetSelection(mainProtocolListIndex_[protocol]);
  468     }
  469     else {
  470         pProtocol->SetSelection(mainProtocolListIndex_[FTP]);
  471     }
  472     UpdateHostFromDefaults(GetProtocol());
  473 
  474     previousProtocol_ = protocol;
  475 }
  476 
  477 ServerProtocol CSiteManagerSite::GetProtocol() const
  478 {
  479     int const sel = xrc_call(*this, "ID_PROTOCOL", &wxChoice::GetSelection);
  480 
  481     ServerProtocol protocol = UNKNOWN;
  482     for (auto const it : mainProtocolListIndex_) {
  483         if (it.second == sel) {
  484             protocol = it.first;
  485             break;
  486         }
  487     }
  488 
  489     auto const group = findGroup(protocol);
  490     if (group.first != protocolGroups().cend()) {
  491         int encSel = xrc_call(*this, "ID_ENCRYPTION", &wxChoice::GetSelection);
  492         if (encSel < 0 || encSel >= static_cast<int>(group.first->protocols.size())) {
  493             encSel = 0;
  494         }
  495         protocol = group.first->protocols[encSel].first;
  496     }
  497 
  498     return protocol;
  499 }
  500 
  501 void CSiteManagerSite::SetControlVisibility(ServerProtocol protocol, LogonType type)
  502 {
  503     auto const group = findGroup(protocol);
  504     bool const isFtp = group.first != protocolGroups().cend() && group.first->protocols.front().first == FTP;
  505 
  506     xrc_call(*this, "ID_ENCRYPTION_DESC", &wxStaticText::Show, group.first != protocolGroups().cend());
  507     xrc_call(*this, "ID_ENCRYPTION", &wxChoice::Show, group.first != protocolGroups().cend());
  508 
  509     xrc_call(*this, "ID_SIGNUP", &wxControl::Show, protocol == STORJ);
  510 
  511     auto const supportedlogonTypes = GetSupportedLogonTypes(protocol);
  512     assert(!supportedlogonTypes.empty());
  513 
  514     auto choice = XRCCTRL(*this, "ID_LOGONTYPE", wxChoice);
  515     choice->Clear();
  516 
  517     if (std::find(supportedlogonTypes.cbegin(), supportedlogonTypes.cend(), type) == supportedlogonTypes.cend()) {
  518         type = supportedlogonTypes.front();
  519     }
  520 
  521     for (auto const supportedLogonType : supportedlogonTypes) {
  522         choice->Append(GetNameFromLogonType(supportedLogonType));
  523         if (supportedLogonType == type) {
  524             choice->SetSelection(choice->GetCount() - 1);
  525         }
  526     }
  527 
  528     bool const hasUser = ProtocolHasUser(protocol) && type != LogonType::anonymous;
  529 
  530     xrc_call(*this, "ID_USER_DESC", &wxStaticText::Show, hasUser);
  531     xrc_call(*this, "ID_USER", &wxTextCtrl::Show, hasUser);
  532     xrc_call(*this, "ID_PASS_DESC", &wxStaticText::Show, type != LogonType::anonymous && type != LogonType::interactive  && (protocol != SFTP || type != LogonType::key));
  533     xrc_call(*this, "ID_PASS", &wxTextCtrl::Show, type != LogonType::anonymous && type != LogonType::interactive && (protocol != SFTP || type != LogonType::key));
  534     xrc_call(*this, "ID_ACCOUNT_DESC", &wxStaticText::Show, isFtp && type == LogonType::account);
  535     xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::Show, isFtp && type == LogonType::account);
  536     xrc_call(*this, "ID_KEYFILE_DESC", &wxStaticText::Show, protocol == SFTP && type == LogonType::key);
  537     xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::Show, protocol == SFTP && type == LogonType::key);
  538     xrc_call(*this, "ID_KEYFILE_BROWSE", &wxButton::Show, protocol == SFTP && type == LogonType::key);
  539 
  540     xrc_call(*this, "ID_ENCRYPTIONKEY_DESC", &wxStaticText::Show, protocol == STORJ);
  541     xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::Show, protocol == STORJ);
  542     xrc_call(*this, "ID_ENCRYPTIONKEY_GENERATE", &wxButton::Show, protocol == STORJ);
  543 
  544     wxString hostLabel = _("&Host:");
  545     wxString hostHint;
  546     wxString userHint;
  547     wxString userLabel = _("&User:");
  548     wxString passLabel = _("Pass&word:");
  549     switch (protocol) {
  550     case S3:
  551         // @translator: Keep short
  552         userLabel = _("&Access key ID:");
  553         // @translator: Keep short
  554         passLabel = _("Secret Access &Key:");
  555         break;
  556     case AZURE_FILE:
  557     case AZURE_BLOB:
  558         // @translator: Keep short
  559         userLabel = _("Storage &account:");
  560         passLabel = _("Access &Key:");
  561         break;
  562     case GOOGLE_CLOUD:
  563         userLabel = _("Pro&ject ID:");
  564         break;
  565     case SWIFT:
  566         // @translator: Keep short
  567         hostLabel = _("Identity &host:");
  568         // @translator: Keep short
  569         hostHint = _("Host name of identity service");
  570         userLabel = _("Pro&ject:");
  571         // @translator: Keep short
  572         userHint = _("Project (or tenant) name or ID");
  573         break;
  574     case B2:
  575         // @translator: Keep short
  576         userLabel = _("&Account ID:");
  577         // @translator: Keep short
  578         passLabel = _("Application &Key:");
  579         break;
  580     default:
  581         break;
  582     }
  583     xrc_call(*this, "ID_HOST_DESC", &wxStaticText::SetLabel, hostLabel);
  584     xrc_call(*this, "ID_HOST", &wxTextCtrl::SetHint, hostHint);
  585     xrc_call(*this, "ID_USER_DESC", &wxStaticText::SetLabel, userLabel);
  586     xrc_call(*this, "ID_PASS_DESC", &wxStaticText::SetLabel, passLabel);
  587     xrc_call(*this, "ID_USER", &wxTextCtrl::SetHint, userHint);
  588 
  589     auto InsertRow = [this](std::vector<std::pair<wxStaticText*, wxTextCtrl*>> & rows, bool password) {
  590 
  591         if (rows.empty()) {
  592             return rows.end();
  593         }
  594 
  595         wxGridBagSizer* sizer = dynamic_cast<wxGridBagSizer*>(rows.back().first->GetContainingSizer());
  596         if (!sizer) {
  597             return rows.end();
  598         }
  599         auto pos = sizer->GetItemPosition(rows.back().first);
  600 
  601         for (int row = sizer->GetRows() - 1; row > pos.GetRow(); --row) {
  602             auto left = sizer->FindItemAtPosition(wxGBPosition(row, 0));
  603             auto right = sizer->FindItemAtPosition(wxGBPosition(row, 1));
  604             if (!left) {
  605                 break;
  606             }
  607             left->SetPos(wxGBPosition(row + 1, 0));
  608             if (right) {
  609                 right->SetPos(wxGBPosition(row + 1, 1));
  610             }
  611         }
  612         auto label = new wxStaticText(rows.back().first->GetParent(), wxID_ANY, L"");
  613         auto text = new wxTextCtrl(rows.back().first->GetParent(), wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, password ? wxTE_PASSWORD : 0);
  614         sizer->Add(label, wxGBPosition(pos.GetRow() + 1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
  615         sizer->Add(text, wxGBPosition(pos.GetRow() + 1, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL|wxGROW);
  616 
  617         rows.emplace_back(label, text);
  618         return rows.end() - 1;
  619     };
  620 
  621     auto SetLabel = [](wxStaticText & label, ServerProtocol const, std::string const& name) {
  622         if (name == "email") {
  623             label.SetLabel(_("E-&mail account:"));
  624         }
  625         else if (name == "identpath") {
  626             // @translator: Keep short
  627             label.SetLabel(_("Identity service path:"));
  628         }
  629         else if (name == "identuser") {
  630             label.SetLabel(_("&User:"));
  631         }
  632         else {
  633             label.SetLabel(name);
  634         }
  635     };
  636 
  637     std::vector<std::pair<wxStaticText*, wxTextCtrl*>>::iterator paramIt[ParameterSection::section_count];
  638     for (int i = 0; i < ParameterSection::section_count; ++i) {
  639         paramIt[i] = extraParameters_[i].begin();
  640     }
  641 
  642     std::vector<ParameterTraits> const& parameterTraits = ExtraServerParameterTraits(protocol);
  643     for (auto const& trait : parameterTraits) {
  644         if (trait.section_ == ParameterSection::custom) {
  645             continue;
  646         }
  647         auto & parameters = extraParameters_[trait.section_];
  648         auto & it = paramIt[trait.section_];
  649 
  650         if (it == parameters.cend()) {
  651             it = InsertRow(parameters, trait.section_ == ParameterSection::credentials);
  652         }
  653 
  654         if (it == parameters.cend()) {
  655             continue;
  656         }
  657         it->first->Show();
  658         it->second->Show();
  659         SetLabel(*it->first, protocol, trait.name_);
  660         it->second->SetHint(trait.hint_);
  661 
  662         ++it;
  663     }
  664 
  665     auto encSizer = xrc_call(*this, "ID_ENCRYPTION", &wxWindow::GetContainingSizer);
  666     encSizer->Show(encSizer->GetItemCount() - 1, paramIt[ParameterSection::host] == extraParameters_[ParameterSection::host].cbegin());
  667 
  668     for (int i = 0; i < ParameterSection::section_count; ++i) {
  669         for (; paramIt[i] != extraParameters_[i].cend(); ++paramIt[i]) {
  670             paramIt[i]->first->Hide();
  671             paramIt[i]->second->Hide();
  672         }
  673     }
  674 
  675     auto keyfileSizer = xrc_call(*this, "ID_KEYFILE_DESC", &wxStaticText::GetContainingSizer);
  676     if (keyfileSizer) {
  677         keyfileSizer->CalcMin();
  678         keyfileSizer->Layout();
  679     }
  680 
  681     auto encryptionkeySizer = xrc_call(*this, "ID_ENCRYPTIONKEY_DESC", &wxStaticText::GetContainingSizer);
  682     if (encryptionkeySizer) {
  683         encryptionkeySizer->CalcMin();
  684         encryptionkeySizer->Layout();
  685     }
  686 
  687     bool const hasServerType = CServer::ProtocolHasFeature(protocol, ProtocolFeature::ServerType);
  688     xrc_call(*this, "ID_SERVERTYPE_LABEL", &wxWindow::Show, hasServerType);
  689     xrc_call(*this, "ID_SERVERTYPE", &wxWindow::Show, hasServerType);
  690     auto * serverTypeSizer = xrc_call(*this, "ID_SERVERTYPE_LABEL", &wxWindow::GetContainingSizer)->GetContainingWindow()->GetSizer();
  691     serverTypeSizer->CalcMin();
  692     serverTypeSizer->Layout();
  693 
  694     bool const hasTransferMode = CServer::ProtocolHasFeature(protocol, ProtocolFeature::TransferMode);
  695     xrc_call(*this, "ID_TRANSFERMODE_DEFAULT", &wxWindow::Show, hasTransferMode);
  696     xrc_call(*this, "ID_TRANSFERMODE_ACTIVE", &wxWindow::Show, hasTransferMode);
  697     xrc_call(*this, "ID_TRANSFERMODE_PASSIVE", &wxWindow::Show, hasTransferMode);
  698     auto* transferModeLabel = XRCCTRL(*this, "ID_TRANSFERMODE_LABEL", wxStaticText);
  699     transferModeLabel->Show(hasTransferMode);
  700     transferModeLabel->GetContainingSizer()->CalcMin();
  701     transferModeLabel->GetContainingSizer()->Layout();
  702 
  703     if (CServer::ProtocolHasFeature(protocol, ProtocolFeature::Charset)) {
  704         if (FindPage(m_pCharsetPage) == wxNOT_FOUND) {
  705             AddPage(m_pCharsetPage, m_charsetPageText);
  706             wxGetApp().GetWrapEngine()->WrapRecursive(XRCCTRL(*this, "ID_CHARSET_AUTO", wxWindow)->GetParent(), 1.3);
  707         }
  708     }
  709     else {
  710         int const charsetPageIndex = FindPage(m_pCharsetPage);
  711         if (charsetPageIndex != wxNOT_FOUND) {
  712             RemovePage(charsetPageIndex);
  713         }
  714     }
  715 
  716     if (protocol == S3) {
  717         if (FindPage(m_pS3Page) == wxNOT_FOUND) {
  718             AddPage(m_pS3Page, L"S3");
  719         }
  720     }
  721     else {
  722         int const s3PageIndex = FindPage(m_pS3Page);
  723         if (s3PageIndex != wxNOT_FOUND) {
  724             RemovePage(s3PageIndex);
  725         }
  726     }
  727 
  728     GetPage(0)->GetSizer()->Fit(GetPage(0));
  729 }
  730 
  731 
  732 void CSiteManagerSite::SetLogonTypeCtrlState()
  733 {
  734     LogonType const t = GetLogonType();
  735     xrc_call(*this, "ID_USER", &wxTextCtrl::Enable, !predefined_ && t != LogonType::anonymous);
  736     xrc_call(*this, "ID_PASS", &wxTextCtrl::Enable, !predefined_ && (t == LogonType::normal || t == LogonType::account));
  737     xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::Enable, !predefined_ && t == LogonType::account);
  738     xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::Enable, !predefined_ && t == LogonType::key);
  739     xrc_call(*this, "ID_KEYFILE_BROWSE", &wxButton::Enable, !predefined_ && t == LogonType::key);
  740     xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::Enable, !predefined_ && t == LogonType::normal);
  741     xrc_call(*this, "ID_ENCRYPTIONKEY_GENERATE", &wxButton::Enable, !predefined_ && t == LogonType::normal);
  742 
  743     for (int i = 0; i < ParameterSection::section_count; ++i) {
  744         for (auto & pair : extraParameters_[i]) {
  745             pair.second->Enable(!predefined_);
  746         }
  747     }
  748 }
  749 
  750 LogonType CSiteManagerSite::GetLogonType() const
  751 {
  752     return GetLogonTypeFromName(xrc_call(*this, "ID_LOGONTYPE", &wxChoice::GetStringSelection).ToStdWstring());
  753 }
  754 
  755 bool CSiteManagerSite::Verify(bool predefined)
  756 {
  757     std::wstring const host = xrc_call(*this, "ID_HOST", &wxTextCtrl::GetValue).ToStdWstring();
  758     if (host.empty()) {
  759         XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus();
  760         wxMessageBoxEx(_("You have to enter a hostname."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  761         return false;
  762     }
  763 
  764     auto logon_type = GetLogonType();
  765 
  766     ServerProtocol protocol = GetProtocol();
  767     wxASSERT(protocol != UNKNOWN);
  768 
  769     if (protocol == SFTP &&
  770             logon_type == LogonType::account)
  771     {
  772         XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetFocus();
  773         wxMessageBoxEx(_("'Account' logontype not supported by selected protocol"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  774         return false;
  775     }
  776 
  777     if (COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 0 &&
  778             !predefined &&
  779             (logon_type == LogonType::account || logon_type == LogonType::normal))
  780     {
  781         XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetFocus();
  782         wxString msg;
  783         if (COptions::Get()->OptionFromFzDefaultsXml(OPTION_DEFAULT_KIOSKMODE) && COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 0) {
  784             msg = _("Saving of password has been disabled by your system administrator.");
  785         }
  786         else {
  787             msg = _("Saving of passwords has been disabled by you.");
  788         }
  789         msg += _T("\n");
  790         msg += _("'Normal' and 'Account' logontypes are not available. Your entry has been changed to 'Ask for password'.");
  791         XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(GetNameFromLogonType(LogonType::ask));
  792         XRCCTRL(*this, "ID_PASS", wxTextCtrl)->ChangeValue(wxString());
  793         logon_type = LogonType::ask;
  794         wxMessageBoxEx(msg, _("Site Manager - Cannot remember password"), wxICON_INFORMATION, this);
  795     }
  796 
  797     // Set selected type
  798     Site site;
  799     site.SetLogonType(logon_type);
  800     site.server.SetProtocol(protocol);
  801 
  802     std::wstring port = xrc_call(*this, "ID_PORT", &wxTextCtrl::GetValue).ToStdWstring();
  803     CServerPath path;
  804     std::wstring error;
  805     if (!site.ParseUrl(host, port, std::wstring(), std::wstring(), error, path, protocol)) {
  806         XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus();
  807         wxMessageBoxEx(error, _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  808         return false;
  809     }
  810 
  811     XRCCTRL(*this, "ID_HOST", wxTextCtrl)->ChangeValue(site.Format(ServerFormat::host_only));
  812     if (site.server.GetPort() != CServer::GetDefaultPort(site.server.GetProtocol())) {
  813         XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString::Format(_T("%d"), site.server.GetPort()));
  814     }
  815     else {
  816         XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString());
  817     }
  818 
  819     SetProtocol(site.server.GetProtocol());
  820 
  821     if (XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue()) {
  822         if (XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->GetValue().empty()) {
  823             XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->SetFocus();
  824             wxMessageBoxEx(_("Need to specify a character encoding"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  825             return false;
  826         }
  827     }
  828 
  829     // Require username for non-anonymous, non-ask logon type
  830     const wxString user = XRCCTRL(*this, "ID_USER", wxTextCtrl)->GetValue();
  831     if (logon_type != LogonType::anonymous &&
  832             logon_type != LogonType::ask &&
  833             logon_type != LogonType::interactive &&
  834             user.empty())
  835     {
  836         XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetFocus();
  837         wxMessageBoxEx(_("You have to specify a user name"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  838         return false;
  839     }
  840 
  841     // We don't allow username of only spaces, confuses both users and XML libraries
  842     if (!user.empty()) {
  843         bool space_only = true;
  844         for (unsigned int i = 0; i < user.Len(); ++i) {
  845             if (user[i] != ' ') {
  846                 space_only = false;
  847                 break;
  848             }
  849         }
  850         if (space_only) {
  851             XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetFocus();
  852             wxMessageBoxEx(_("Username cannot be a series of spaces"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  853             return false;
  854         }
  855     }
  856 
  857     // Require account for account logon type
  858     if (logon_type == LogonType::account &&
  859             XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->GetValue().empty())
  860     {
  861         XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->SetFocus();
  862         wxMessageBoxEx(_("You have to enter an account name"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  863         return false;
  864     }
  865 
  866     // In key file logon type, check that the provided key file exists
  867     if (logon_type == LogonType::key) {
  868         std::wstring keyFile = xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::GetValue).ToStdWstring();
  869         if (keyFile.empty()) {
  870             wxMessageBoxEx(_("You have to enter a key file path"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  871             xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
  872             return false;
  873         }
  874 
  875         // Check (again) that the key file is in the correct format since it might have been introduced manually
  876         CFZPuttyGenInterface cfzg(this);
  877 
  878         std::wstring keyFileComment, keyFileData;
  879         if (cfzg.LoadKeyFile(keyFile, false, keyFileComment, keyFileData)) {
  880             xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::ChangeValue, keyFile);
  881         }
  882         else {
  883             xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
  884             return false;
  885         }
  886     }
  887 
  888     if (protocol == STORJ && logon_type == LogonType::normal) {
  889         std::wstring pw = xrc_call(*this, "ID_PASS", &wxTextCtrl::GetValue).ToStdWstring();
  890         std::wstring encryptionKey = xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::GetValue).ToStdWstring();
  891 
  892         bool encrypted = !xrc_call(*this, "ID_PASS", &wxTextCtrl::GetHint).empty();
  893         if (encrypted) {
  894             if (pw.empty() != encryptionKey.empty()) {
  895                 wxMessageBoxEx(_("You cannot change password and encryption key individually if using a master password."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  896                 xrc_call(*this, "ID_ENCRYPTIONKEY", &wxWindow::SetFocus);
  897                 return false;
  898             }
  899         }
  900 #if ENABLE_STORJ
  901         if (!encryptionKey.empty() || !encrypted) {
  902             CStorjKeyInterface validator(this);
  903             if (!validator.ValidateKey(encryptionKey, false)) {
  904                 wxMessageBoxEx(_("You have to enter a valid encryption key"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  905                 xrc_call(*this, "ID_ENCRYPTIONKEY", &wxWindow::SetFocus);
  906                 return false;
  907             }
  908         }
  909 #endif
  910     }
  911 
  912     std::wstring const remotePathRaw = XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->GetValue().ToStdWstring();
  913     if (!remotePathRaw.empty()) {
  914         std::wstring serverType = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->GetStringSelection().ToStdWstring();
  915 
  916         CServerPath remotePath;
  917         remotePath.SetType(CServer::GetServerTypeFromName(serverType));
  918         if (!remotePath.SetPath(remotePathRaw)) {
  919             XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->SetFocus();
  920             wxMessageBoxEx(_("Default remote path cannot be parsed. Make sure it is a valid absolute path for the selected server type."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  921             return false;
  922         }
  923     }
  924 
  925     std::wstring const localPath = XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue().ToStdWstring();
  926     if (XRCCTRL(*this, "ID_SYNC", wxCheckBox)->GetValue()) {
  927         if (remotePathRaw.empty() || localPath.empty()) {
  928             XRCCTRL(*this, "ID_SYNC", wxCheckBox)->SetFocus();
  929             wxMessageBoxEx(_("You need to enter both a local and a remote path to enable synchronized browsing for this site."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  930             return false;
  931         }
  932     }
  933 
  934     std::vector<std::pair<wxStaticText*, wxTextCtrl*>>::iterator paramIt[ParameterSection::section_count];
  935     for (int i = 0; i < ParameterSection::section_count; ++i) {
  936         paramIt[i] = extraParameters_[i].begin();
  937     }
  938 
  939     std::vector<ParameterTraits> const& parameterTraits = ExtraServerParameterTraits(protocol);
  940     for (auto const& trait : parameterTraits) {
  941         if (trait.section_ == ParameterSection::custom) {
  942             continue;
  943         }
  944         assert(paramIt[trait.section_] != extraParameters_[trait.section_].cend());
  945 
  946         if (!(trait.flags_ & ParameterTraits::optional)) {
  947             auto & controls = *paramIt[trait.section_];
  948             if (controls.second->GetValue().empty()) {
  949                 controls.second->SetFocus();
  950                 wxMessageBoxEx(_("You need to enter a value."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
  951                 return false;
  952             }
  953         }
  954 
  955         ++paramIt[trait.section_];
  956     }
  957 
  958     return true;
  959 }
  960 
  961 void CSiteManagerSite::UpdateSite(Site &site)
  962 {
  963     ServerProtocol const protocol = GetProtocol();
  964     wxASSERT(protocol != UNKNOWN);
  965     site.server.SetProtocol(protocol);
  966 
  967     unsigned long port;
  968     if (!xrc_call(*this, "ID_PORT", &wxTextCtrl::GetValue).ToULong(&port) || !port || port > 65535) {
  969         port = CServer::GetDefaultPort(protocol);
  970     }
  971     std::wstring host = xrc_call(*this, "ID_HOST", &wxTextCtrl::GetValue).ToStdWstring();
  972     // SetHost does not accept URL syntax
  973     if (!host.empty() && host[0] == '[') {
  974         host = host.substr(1, host.size() - 2);
  975     }
  976     site.server.SetHost(host, port);
  977 
  978     auto logon_type = GetLogonType();
  979     site.SetLogonType(logon_type);
  980 
  981     site.SetUser(xrc_call(*this, "ID_USER", &wxTextCtrl::GetValue).ToStdWstring());
  982     auto pw = xrc_call(*this, "ID_PASS", &wxTextCtrl::GetValue).ToStdWstring();
  983 
  984     if (protocol == STORJ && logon_type == LogonType::normal && (!pw.empty() || !site.credentials.encrypted_)) {
  985         pw += '|';
  986         pw += xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::GetValue).ToStdWstring();
  987     }
  988 
  989     if (site.credentials.encrypted_) {
  990         if (!pw.empty()) {
  991             site.credentials.encrypted_ = fz::public_key();
  992             site.credentials.SetPass(pw);
  993         }
  994     }
  995     else {
  996         site.credentials.SetPass(pw);
  997     }
  998     site.credentials.account_ = xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::GetValue).ToStdWstring();
  999 
 1000     site.credentials.keyFile_ = xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::GetValue).ToStdWstring();
 1001 
 1002     site.comments_ = xrc_call(*this, "ID_COMMENTS", &wxTextCtrl::GetValue).ToStdWstring();
 1003     site.m_colour = CSiteManager::GetColourFromIndex(xrc_call(*this, "ID_COLOR", &wxChoice::GetSelection));
 1004 
 1005     std::wstring const serverType = xrc_call(*this, "ID_SERVERTYPE", &wxChoice::GetStringSelection).ToStdWstring();
 1006     site.server.SetType(CServer::GetServerTypeFromName(serverType));
 1007 
 1008     site.m_default_bookmark.m_localDir = xrc_call(*this, "ID_LOCALDIR", &wxTextCtrl::GetValue).ToStdWstring();
 1009     site.m_default_bookmark.m_remoteDir = CServerPath();
 1010     site.m_default_bookmark.m_remoteDir.SetType(site.server.GetType());
 1011     site.m_default_bookmark.m_remoteDir.SetPath(xrc_call(*this, "ID_REMOTEDIR", &wxTextCtrl::GetValue).ToStdWstring());
 1012     site.m_default_bookmark.m_sync = xrc_call(*this, "ID_SYNC", &wxCheckBox::GetValue);
 1013     site.m_default_bookmark.m_comparison = xrc_call(*this, "ID_COMPARISON", &wxCheckBox::GetValue);
 1014 
 1015     int hours = xrc_call(*this, "ID_TIMEZONE_HOURS", &wxSpinCtrl::GetValue);
 1016     int minutes = xrc_call(*this, "ID_TIMEZONE_MINUTES", &wxSpinCtrl::GetValue);
 1017 
 1018     site.server.SetTimezoneOffset(hours * 60 + minutes);
 1019 
 1020     if (xrc_call(*this, "ID_TRANSFERMODE_ACTIVE", &wxRadioButton::GetValue)) {
 1021         site.server.SetPasvMode(MODE_ACTIVE);
 1022     }
 1023     else if (xrc_call(*this, "ID_TRANSFERMODE_PASSIVE", &wxRadioButton::GetValue)) {
 1024         site.server.SetPasvMode(MODE_PASSIVE);
 1025     }
 1026     else {
 1027         site.server.SetPasvMode(MODE_DEFAULT);
 1028     }
 1029 
 1030     if (xrc_call(*this, "ID_LIMITMULTIPLE", &wxCheckBox::GetValue)) {
 1031         site.server.MaximumMultipleConnections(xrc_call(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::GetValue));
 1032     }
 1033     else {
 1034         site.server.MaximumMultipleConnections(0);
 1035     }
 1036 
 1037     if (xrc_call(*this, "ID_CHARSET_UTF8", &wxRadioButton::GetValue))
 1038         site.server.SetEncodingType(ENCODING_UTF8);
 1039     else if (xrc_call(*this, "ID_CHARSET_CUSTOM", &wxRadioButton::GetValue)) {
 1040         std::wstring encoding = xrc_call(*this, "ID_ENCODING", &wxTextCtrl::GetValue).ToStdWstring();
 1041         site.server.SetEncodingType(ENCODING_CUSTOM, encoding);
 1042     }
 1043     else {
 1044         site.server.SetEncodingType(ENCODING_AUTO);
 1045     }
 1046 
 1047     if (xrc_call(*this, "ID_BYPASSPROXY", &wxCheckBox::GetValue)) {
 1048         site.server.SetBypassProxy(true);
 1049     }
 1050     else {
 1051         site.server.SetBypassProxy(false);
 1052     }
 1053 
 1054     UpdateExtraParameters(site.server);
 1055 }
 1056 
 1057 void CSiteManagerSite::UpdateExtraParameters(CServer & server)
 1058 {
 1059     server.ClearExtraParameters();
 1060 
 1061     std::vector<std::pair<wxStaticText*, wxTextCtrl*>>::iterator paramIt[ParameterSection::section_count];
 1062     for (int i = 0; i < ParameterSection::section_count; ++i) {
 1063         paramIt[i] = extraParameters_[i].begin();
 1064     }
 1065     auto const& traits = ExtraServerParameterTraits(server.GetProtocol());
 1066     for (auto const& trait : traits) {
 1067         if (trait.section_ == ParameterSection::credentials || trait.section_ == ParameterSection::custom) {
 1068             continue;
 1069         }
 1070 
 1071         server.SetExtraParameter(trait.name_, paramIt[trait.section_]->second->GetValue().ToStdWstring());
 1072         ++paramIt[trait.section_];
 1073     }
 1074 
 1075     if (server.GetProtocol() == S3) {
 1076         if (xrc_call(*this, "ID_S3_NOENCRYPTION", &wxRadioButton::GetValue)) {
 1077             server.ClearExtraParameter("ssealgorithm");
 1078         }
 1079         else if (xrc_call(*this, "ID_S3_AES256", &wxRadioButton::GetValue)) {
 1080             server.SetExtraParameter("ssealgorithm", L"AES256");
 1081         }
 1082         else if (xrc_call(*this, "ID_S3_AWSKMS", &wxRadioButton::GetValue)) {
 1083             server.SetExtraParameter("ssealgorithm", L"aws:kms");
 1084             if (xrc_call(*this, "ID_S3_KMSKEY", &wxChoice::GetSelection) == static_cast<int>(s3_sse::KmsKey::CUSTOM)) {
 1085                 server.SetExtraParameter("ssekmskey", fz::to_wstring(xrc_call(*this, "ID_S3_CUSTOM_KMS", &wxTextCtrl::GetValue)));
 1086             }
 1087         }
 1088         else if (xrc_call(*this, "ID_S3_CUSTOMER_ENCRYPTION", &wxRadioButton::GetValue)) {
 1089             server.SetExtraParameter("ssealgorithm", L"customer");
 1090             server.SetExtraParameter("ssecustomerkey", fz::to_wstring(xrc_call(*this, "ID_S3_CUSTOMER_KEY", &wxTextCtrl::GetValue)));
 1091         }
 1092     }
 1093 
 1094 }
 1095 
 1096 void CSiteManagerSite::SetSite(Site const& site, bool predefined)
 1097 {
 1098     predefined_ = predefined;
 1099 
 1100     xrc_call(*this, "ID_HOST", &wxWindow::Enable, !predefined);
 1101     xrc_call(*this, "ID_PORT", &wxWindow::Enable, !predefined);
 1102     xrc_call(*this, "ID_PROTOCOL", &wxWindow::Enable, !predefined);
 1103     xrc_call(*this, "ID_ENCRYPTION", &wxWindow::Enable, !predefined);
 1104     xrc_call(*this, "ID_TRANSFERMODE_ACTIVE", &wxWindow::Enable, !predefined);
 1105     xrc_call(*this, "ID_TRANSFERMODE_PASSIVE", &wxWindow::Enable, !predefined);
 1106     xrc_call(*this, "ID_TRANSFERMODE_DEFAULT", &wxWindow::Enable, !predefined);
 1107     xrc_call(*this, "ID_SYNC", &wxCheckBox::Enable, !predefined);
 1108     xrc_call(*this, "ID_REMOTEDIR", &wxWindow::Enable, !predefined);
 1109     xrc_call(*this, "ID_LOCALDIR", &wxWindow::Enable, !predefined);
 1110     xrc_call(*this, "ID_SERVERTYPE", &wxWindow::Enable, !predefined);
 1111     xrc_call(*this, "ID_LOGONTYPE", &wxWindow::Enable, !predefined);
 1112     xrc_call(*this, "ID_COMMENTS", &wxWindow::Enable, !predefined);
 1113     xrc_call(*this, "ID_COLOR", &wxWindow::Enable, !predefined);
 1114     xrc_call(*this, "ID_COMPARISON", &wxCheckBox::Enable, !predefined);
 1115     xrc_call(*this, "ID_TIMEZONE_HOURS", &wxWindow::Enable, !predefined);
 1116     xrc_call(*this, "ID_TIMEZONE_MINUTES", &wxWindow::Enable, !predefined);
 1117     xrc_call(*this, "ID_LIMITMULTIPLE", &wxWindow::Enable, !predefined);
 1118     xrc_call(*this, "ID_CHARSET_AUTO", &wxWindow::Enable, !predefined);
 1119     xrc_call(*this, "ID_CHARSET_UTF8", &wxWindow::Enable, !predefined);
 1120     xrc_call(*this, "ID_CHARSET_CUSTOM", &wxWindow::Enable, !predefined);
 1121 
 1122     if (!site) {
 1123         // Empty all site information
 1124         xrc_call(*this, "ID_HOST", &wxTextCtrl::ChangeValue, wxString());
 1125         xrc_call(*this, "ID_PORT", &wxTextCtrl::ChangeValue, wxString());
 1126         SetProtocol(FTP);
 1127         xrc_call(*this, "ID_BYPASSPROXY", &wxCheckBox::SetValue, false);
 1128         bool const kiosk_mode = COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 0;
 1129         auto const logonType = kiosk_mode ? LogonType::ask : LogonType::normal;
 1130         xrc_call(*this, "ID_LOGONTYPE", &wxChoice::SetStringSelection, GetNameFromLogonType(logonType));
 1131         xrc_call(*this, "ID_USER", &wxTextCtrl::ChangeValue, wxString());
 1132         xrc_call(*this, "ID_PASS", &wxTextCtrl::ChangeValue, wxString());
 1133         xrc_call(*this, "ID_PASS", &wxTextCtrl::SetHint, wxString());
 1134         xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::ChangeValue, wxString());
 1135         xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::ChangeValue, wxString());
 1136         xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::ChangeValue, wxString());
 1137         xrc_call(*this, "ID_COMMENTS", &wxTextCtrl::ChangeValue, wxString());
 1138         xrc_call(*this, "ID_COLOR", &wxChoice::Select, 0);
 1139 
 1140         SetControlVisibility(FTP, logonType);
 1141         SetLogonTypeCtrlState();
 1142 
 1143         xrc_call(*this, "ID_SERVERTYPE", &wxChoice::SetSelection, 0);
 1144         xrc_call(*this, "ID_LOCALDIR", &wxTextCtrl::ChangeValue, wxString());
 1145         xrc_call(*this, "ID_REMOTEDIR", &wxTextCtrl::ChangeValue, wxString());
 1146         xrc_call(*this, "ID_SYNC", &wxCheckBox::SetValue, false);
 1147         xrc_call<wxSpinCtrl, int>(*this, "ID_TIMEZONE_HOURS", &wxSpinCtrl::SetValue, 0);
 1148         xrc_call<wxSpinCtrl, int>(*this, "ID_TIMEZONE_MINUTES", &wxSpinCtrl::SetValue, 0);
 1149 
 1150         xrc_call(*this, "ID_TRANSFERMODE_DEFAULT", &wxRadioButton::SetValue, true);
 1151         xrc_call(*this, "ID_LIMITMULTIPLE", &wxCheckBox::SetValue, false);
 1152         xrc_call(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::Enable, false);
 1153         xrc_call<wxSpinCtrl, int>(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::SetValue, 1);
 1154 
 1155         xrc_call(*this, "ID_CHARSET_AUTO", &wxRadioButton::SetValue, true);
 1156         xrc_call(*this, "ID_ENCODING", &wxTextCtrl::ChangeValue, wxString());
 1157         xrc_call(*this, "ID_ENCODING", &wxTextCtrl::Enable, false);
 1158     }
 1159     else {
 1160         xrc_call(*this, "ID_HOST", &wxTextCtrl::ChangeValue, site.Format(ServerFormat::host_only));
 1161         unsigned int port = site.server.GetPort();
 1162 
 1163         if (port != CServer::GetDefaultPort(site.server.GetProtocol())) {
 1164             xrc_call(*this, "ID_PORT", &wxTextCtrl::ChangeValue, wxString::Format(_T("%d"), port));
 1165         }
 1166         else {
 1167             xrc_call(*this, "ID_PORT", &wxTextCtrl::ChangeValue, wxString());
 1168         }
 1169 
 1170         ServerProtocol protocol = site.server.GetProtocol();
 1171         SetProtocol(protocol);
 1172         xrc_call(*this, "ID_BYPASSPROXY", &wxCheckBox::SetValue, site.server.GetBypassProxy());
 1173 
 1174         LogonType const logonType = site.credentials.logonType_;
 1175         xrc_call(*this, "ID_LOGONTYPE", &wxChoice::SetStringSelection, GetNameFromLogonType(logonType));
 1176 
 1177         SetControlVisibility(protocol, logonType);
 1178         SetLogonTypeCtrlState();
 1179 
 1180         xrc_call(*this, "ID_USER", &wxTextCtrl::ChangeValue, site.server.GetUser());
 1181         xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::ChangeValue, site.credentials.account_);
 1182 
 1183         std::wstring pass = site.credentials.GetPass();
 1184         std::wstring encryptionKey;
 1185         if (protocol == STORJ) {
 1186             size_t pos = pass.rfind('|');
 1187             if (pos != std::wstring::npos) {
 1188                 encryptionKey = pass.substr(pos + 1);
 1189                 pass = pass.substr(0, pos);
 1190             }
 1191         }
 1192 
 1193         if (site.credentials.encrypted_) {
 1194             xrc_call(*this, "ID_PASS", &wxTextCtrl::ChangeValue, wxString());
 1195             xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::ChangeValue, wxString());
 1196 
 1197             // @translator: Keep this string as short as possible
 1198             xrc_call(*this, "ID_PASS", &wxTextCtrl::SetHint, _("Leave empty to keep existing password."));
 1199             for (auto & control : extraParameters_[ParameterSection::credentials]) {
 1200                 control.second->SetHint(_("Leave empty to keep existing data."));
 1201             }
 1202         }
 1203         else {
 1204             xrc_call(*this, "ID_PASS", &wxTextCtrl::ChangeValue, pass);
 1205             xrc_call(*this, "ID_PASS", &wxTextCtrl::SetHint, wxString());
 1206             xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::ChangeValue, encryptionKey);
 1207 
 1208             auto it = extraParameters_[ParameterSection::credentials].begin();
 1209 
 1210             auto const& traits = ExtraServerParameterTraits(protocol);
 1211             for (auto const& trait : traits) {
 1212                 if (trait.section_ != ParameterSection::credentials) {
 1213                     continue;
 1214                 }
 1215 
 1216                 it->second->ChangeValue(site.credentials.GetExtraParameter(trait.name_));
 1217                 ++it;
 1218             }
 1219         }
 1220 
 1221         SetExtraParameters(site.server);
 1222 
 1223         xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::ChangeValue, site.credentials.keyFile_);
 1224         xrc_call(*this, "ID_COMMENTS", &wxTextCtrl::ChangeValue, site.comments_);
 1225         xrc_call(*this, "ID_COLOR", &wxChoice::Select, CSiteManager::GetColourIndex(site.m_colour));
 1226 
 1227         xrc_call(*this, "ID_SERVERTYPE", &wxChoice::SetSelection, site.server.GetType());
 1228         xrc_call(*this, "ID_LOCALDIR", &wxTextCtrl::ChangeValue, site.m_default_bookmark.m_localDir);
 1229         xrc_call(*this, "ID_REMOTEDIR", &wxTextCtrl::ChangeValue, site.m_default_bookmark.m_remoteDir.GetPath());
 1230         xrc_call(*this, "ID_SYNC", &wxCheckBox::SetValue, site.m_default_bookmark.m_sync);
 1231         xrc_call(*this, "ID_COMPARISON", &wxCheckBox::SetValue, site.m_default_bookmark.m_comparison);
 1232         xrc_call<wxSpinCtrl, int>(*this, "ID_TIMEZONE_HOURS", &wxSpinCtrl::SetValue, site.server.GetTimezoneOffset() / 60);
 1233         xrc_call<wxSpinCtrl, int>(*this, "ID_TIMEZONE_MINUTES", &wxSpinCtrl::SetValue, site.server.GetTimezoneOffset() % 60);
 1234 
 1235         if (CServer::ProtocolHasFeature(site.server.GetProtocol(), ProtocolFeature::TransferMode)) {
 1236             PasvMode pasvMode = site.server.GetPasvMode();
 1237             if (pasvMode == MODE_ACTIVE) {
 1238                 xrc_call(*this, "ID_TRANSFERMODE_ACTIVE", &wxRadioButton::SetValue, true);
 1239             }
 1240             else if (pasvMode == MODE_PASSIVE) {
 1241                 xrc_call(*this, "ID_TRANSFERMODE_PASSIVE", &wxRadioButton::SetValue, true);
 1242             }
 1243             else {
 1244                 xrc_call(*this, "ID_TRANSFERMODE_DEFAULT", &wxRadioButton::SetValue, true);
 1245             }
 1246         }
 1247 
 1248         int const maxMultiple = site.server.MaximumMultipleConnections();
 1249         xrc_call(*this, "ID_LIMITMULTIPLE", &wxCheckBox::SetValue, maxMultiple != 0);
 1250         if (maxMultiple != 0) {
 1251             xrc_call(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::Enable, !predefined);
 1252             xrc_call<wxSpinCtrl, int>(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::SetValue, maxMultiple);
 1253         }
 1254         else {
 1255             xrc_call(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::Enable, false);
 1256             xrc_call<wxSpinCtrl, int>(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::SetValue, 1);
 1257         }
 1258 
 1259         switch (site.server.GetEncodingType()) {
 1260         default:
 1261         case ENCODING_AUTO:
 1262             xrc_call(*this, "ID_CHARSET_AUTO", &wxRadioButton::SetValue, true);
 1263             break;
 1264         case ENCODING_UTF8:
 1265             xrc_call(*this, "ID_CHARSET_UTF8", &wxRadioButton::SetValue, true);
 1266             break;
 1267         case ENCODING_CUSTOM:
 1268             xrc_call(*this, "ID_CHARSET_CUSTOM", &wxRadioButton::SetValue, true);
 1269             break;
 1270         }
 1271         xrc_call(*this, "ID_ENCODING", &wxTextCtrl::Enable, !predefined && site.server.GetEncodingType() == ENCODING_CUSTOM);
 1272         xrc_call(*this, "ID_ENCODING", &wxTextCtrl::ChangeValue, site.server.GetCustomEncoding());
 1273 
 1274         xrc_call(*this, "ID_S3_KMSKEY", &wxChoice::SetSelection, static_cast<int>(s3_sse::KmsKey::DEFAULT));
 1275         auto ssealgorithm = site.server.GetExtraParameter("ssealgorithm");
 1276         if (ssealgorithm.empty()) {
 1277             xrc_call(*this, "ID_S3_NOENCRYPTION", &wxRadioButton::SetValue, true);
 1278         }
 1279         else if (ssealgorithm == "AES256") {
 1280             xrc_call(*this, "ID_S3_AES256", &wxRadioButton::SetValue, true);
 1281         }
 1282         else if (ssealgorithm == "aws:kms") {
 1283             xrc_call(*this, "ID_S3_AWSKMS", &wxRadioButton::SetValue, true);
 1284             auto sseKmsKey = site.server.GetExtraParameter("ssekmskey");
 1285             if (!sseKmsKey.empty()) {
 1286                 xrc_call(*this, "ID_S3_KMSKEY", &wxChoice::SetSelection, static_cast<int>(s3_sse::KmsKey::CUSTOM));
 1287                 xrc_call(*this, "ID_S3_CUSTOM_KMS", &wxTextCtrl::ChangeValue, sseKmsKey);
 1288             }
 1289         }
 1290         else if (ssealgorithm == "customer") {
 1291             xrc_call(*this, "ID_S3_CUSTOMER_ENCRYPTION", &wxRadioButton::SetValue, true);
 1292             auto customerKey = site.server.GetExtraParameter("ssecustomerkey");
 1293             xrc_call(*this, "ID_S3_CUSTOMER_KEY", &wxTextCtrl::ChangeValue, customerKey);
 1294         }
 1295     }
 1296 }
 1297 
 1298 void CSiteManagerSite::SetExtraParameters(CServer const& server)
 1299 {
 1300     std::vector<std::pair<wxStaticText*, wxTextCtrl*>>::iterator paramIt[ParameterSection::section_count];
 1301     for (int i = 0; i < ParameterSection::section_count; ++i) {
 1302         paramIt[i] = extraParameters_[i].begin();
 1303     }
 1304     auto const& traits = ExtraServerParameterTraits(server.GetProtocol());
 1305     for (auto const& trait : traits) {
 1306         if (trait.section_ == ParameterSection::credentials || trait.section_ == ParameterSection::custom) {
 1307             continue;
 1308         }
 1309 
 1310         std::wstring value = server.GetExtraParameter(trait.name_);
 1311         paramIt[trait.section_]->second->ChangeValue(value.empty() ? trait.default_ : value);
 1312         ++paramIt[trait.section_];
 1313     }
 1314 }
 1315 
 1316 void CSiteManagerSite::OnProtocolSelChanged(wxCommandEvent&)
 1317 {
 1318     auto const protocol = GetProtocol();
 1319     UpdateHostFromDefaults(protocol);
 1320 
 1321     CServer server;
 1322     if (previousProtocol_ != UNKNOWN) {
 1323         server.SetProtocol(previousProtocol_);
 1324         UpdateExtraParameters(server);
 1325     }
 1326     server.SetProtocol(protocol);
 1327     SetExtraParameters(server);
 1328 
 1329     auto const logonType = GetLogonType();
 1330     SetControlVisibility(protocol, logonType);
 1331     SetLogonTypeCtrlState();
 1332 
 1333     SetProtocol(protocol);
 1334 }
 1335 
 1336 void CSiteManagerSite::OnLogontypeSelChanged(wxCommandEvent&)
 1337 {
 1338     LogonType const t = GetLogonType();
 1339     SetControlVisibility(GetProtocol(), t);
 1340     SetLogonTypeCtrlState();
 1341 }
 1342 
 1343 void CSiteManagerSite::OnCharsetChange(wxCommandEvent&)
 1344 {
 1345     bool checked = xrc_call(*this, "ID_CHARSET_CUSTOM", &wxRadioButton::GetValue);
 1346     xrc_call(*this, "ID_ENCODING", &wxTextCtrl::Enable, checked);
 1347 }
 1348 
 1349 void CSiteManagerSite::OnLimitMultipleConnectionsChanged(wxCommandEvent& event)
 1350 {
 1351     XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(event.IsChecked());
 1352 }
 1353 
 1354 void CSiteManagerSite::OnRemoteDirBrowse(wxCommandEvent&)
 1355 {
 1356     wxDirDialog dlg(this, _("Choose the default local directory"), XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue(), wxDD_NEW_DIR_BUTTON);
 1357     if (dlg.ShowModal() == wxID_OK) {
 1358         XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->ChangeValue(dlg.GetPath());
 1359     }
 1360 }
 1361 
 1362 void CSiteManagerSite::OnKeyFileBrowse(wxCommandEvent&)
 1363 {
 1364     wxString wildcards(_T("PPK files|*.ppk|PEM files|*.pem|All files|*.*"));
 1365     wxFileDialog dlg(this, _("Choose a key file"), wxString(), wxString(), wildcards, wxFD_OPEN|wxFD_FILE_MUST_EXIST);
 1366 
 1367     if (dlg.ShowModal() == wxID_OK) {
 1368         std::wstring keyFilePath = dlg.GetPath().ToStdWstring();
 1369         // If the selected file was a PEM file, LoadKeyFile() will automatically convert it to PPK
 1370         // and tell us the new location.
 1371         CFZPuttyGenInterface fzpg(this);
 1372 
 1373         std::wstring keyFileComment, keyFileData;
 1374         if (fzpg.LoadKeyFile(keyFilePath, false, keyFileComment, keyFileData)) {
 1375             XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->ChangeValue(keyFilePath);
 1376 #if USE_MAC_SANDBOX
 1377             OSXSandboxUserdirs::Get().AddFile(keyFilePath);
 1378 #endif
 1379 
 1380         }
 1381         else {
 1382             xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
 1383         }
 1384     }
 1385 }
 1386 
 1387 void CSiteManagerSite::OnGenerateEncryptionKey(wxCommandEvent&)
 1388 {
 1389 #if ENABLE_STORJ
 1390     CStorjKeyInterface generator(this);
 1391     std::wstring key = generator.GenerateKey();
 1392     if (!key.empty()) {
 1393         xrc_call(*this, "ID_ENCRYPTIONKEY", &wxTextCtrl::ChangeValue, wxString(key));
 1394         xrc_call(*this, "ID_ENCRYPTIONKEY", &wxWindow::SetFocus);
 1395 
 1396         wxDialogEx dlg;
 1397         if (dlg.Load(this, "ID_STORJ_GENERATED_KEY")) {
 1398             dlg.WrapRecursive(&dlg, 2.5);
 1399             dlg.GetSizer()->Fit(&dlg);
 1400             dlg.GetSizer()->SetSizeHints(&dlg);
 1401             xrc_call(dlg, "ID_KEY", &wxTextCtrl::ChangeValue, wxString(key));
 1402             dlg.ShowModal();
 1403         }
 1404     }
 1405 #endif
 1406 }
 1407 
 1408 void CSiteManagerSite::UpdateHostFromDefaults(ServerProtocol const protocol)
 1409 {
 1410     if (protocol != previousProtocol_) {
 1411         auto const oldDefault = std::get<0>(GetDefaultHost(previousProtocol_));
 1412         auto const newDefault = GetDefaultHost(protocol);
 1413 
 1414         std::wstring const host = xrc_call(*this, "ID_HOST", &wxTextCtrl::GetValue).ToStdWstring();
 1415         if (host.empty() || host == oldDefault) {
 1416             xrc_call(*this, "ID_HOST", &wxTextCtrl::ChangeValue, std::get<0>(newDefault));
 1417         }
 1418         xrc_call(*this, "ID_HOST", &wxTextCtrl::SetHint, std::get<1>(newDefault));
 1419     }
 1420 }