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