source: filezilla/trunk/fuentes/src/interface/Options.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

File size: 22.2 KB
Line 
1#include <filezilla.h>
2#include "Options.h"
3#include "filezillaapp.h"
4#include <wx/tokenzr.h>
5#include "ipcmutex.h"
6#include <option_change_event_handler.h>
7#include "sizeformatting.h"
8
9#include <algorithm>
10#include <string>
11
12#ifdef __WXMSW__
13        #include <shlobj.h>
14
15        // Needed for MinGW:
16        #ifndef SHGFP_TYPE_CURRENT
17                #define SHGFP_TYPE_CURRENT 0
18        #endif
19#endif
20
21COptions* COptions::m_theOptions = 0;
22
23enum Type
24{
25        string,
26        number
27};
28
29enum Flags
30{
31        normal,
32        internal,
33        default_only,
34        default_priority // If that option is given in fzdefaults.xml, it overrides any user option
35};
36
37struct t_Option
38{
39        const char name[30];
40        const Type type;
41        const wxString defaultValue; // Default values are stored as string even for numerical options
42        const Flags flags; // internal items won't get written to settings file nor loaded from there
43};
44
45#ifdef __WXMSW__
46//case insensitive
47#define DEFAULT_FILENAME_SORT   _T("0")
48#else
49//case sensitive
50#define DEFAULT_FILENAME_SORT   _T("1")
51#endif
52
53// In C++14 we should be able to use this instead:
54//   static_assert(OPTIONS_NUM <= changed_options_t().size());
55static_assert(static_cast<int>(OPTIONS_NUM) <= static_cast<int>(changed_options_size), "OPTIONS_NUM too big for changed_options_t");
56
57static const t_Option options[OPTIONS_NUM] =
58{
59        // Note: A few options are versioned due to a changed
60        // option syntax or past, unhealthy defaults
61
62        // Engine settings
63        { "Use Pasv mode", number, _T("1"), normal },
64        { "Limit local ports", number, _T("0"), normal },
65        { "Limit ports low", number, _T("6000"), normal },
66        { "Limit ports high", number, _T("7000"), normal },
67        { "Limit ports offset", number, _T("0"), normal },
68        { "External IP mode", number, _T("0"), normal },
69        { "External IP", string, _T(""), normal },
70        { "External address resolver", string, _T("http://ip.filezilla-project.org/ip.php"), normal },
71        { "Last resolved IP", string, _T(""), normal },
72        { "No external ip on local conn", number, _T("1"), normal },
73        { "Pasv reply fallback mode", number, _T("0"), normal },
74        { "Timeout", number, _T("20"), normal },
75        { "Logging Debug Level", number, _T("0"), normal },
76        { "Logging Raw Listing", number, _T("0"), normal },
77        { "fzsftp executable", string, _T(""), internal },
78        { "Allow transfermode fallback", number, _T("1"), normal },
79        { "Reconnect count", number, _T("2"), normal },
80        { "Reconnect delay", number, _T("5"), normal },
81        { "Enable speed limits", number, _T("0"), normal },
82        { "Speedlimit inbound", number, _T("100"), normal },
83        { "Speedlimit outbound", number, _T("20"), normal },
84        { "Speedlimit burst tolerance", number, _T("0"), normal },
85        { "Preallocate space", number, _T("0"), normal },
86        { "View hidden files", number, _T("0"), normal },
87        { "Preserve timestamps", number, _T("0"), normal },
88        { "Socket recv buffer size (v2)", number, _T("4194304"), normal }, // Make it large enough by default
89                                                                                                                 // to enable a large TCP window scale
90        { "Socket send buffer size (v2)", number, _T("262144"), normal },
91        { "FTP Keep-alive commands", number, _T("0"), normal },
92        { "FTP Proxy type", number, _T("0"), normal },
93        { "FTP Proxy host", string, _T(""), normal },
94        { "FTP Proxy user", string, _T(""), normal },
95        { "FTP Proxy password", string, _T(""), normal },
96        { "FTP Proxy login sequence", string, _T(""), normal },
97        { "SFTP keyfiles", string, _T(""), normal },
98        { "SFTP compression", number, _T(""), normal },
99        { "Proxy type", number, _T("0"), normal },
100        { "Proxy host", string, _T(""), normal },
101        { "Proxy port", number, _T("0"), normal },
102        { "Proxy user", string, _T(""), normal },
103        { "Proxy password", string, _T(""), normal },
104        { "Logging file", string, _T(""), normal },
105        { "Logging filesize limit", number, _T("10"), normal },
106        { "Logging show detailed logs", number, _T("0"), internal },
107        { "Size format", number, _T("3"), normal },
108        { "Size thousands separator", number, _T("1"), normal },
109        { "Size decimal places", number, _T("1"), normal },
110
111        // Interface settings
112        { "Number of Transfers", number, _T("2"), normal },
113        { "Ascii Binary mode", number, _T("0"), normal },
114        { "Auto Ascii files", string, _T("am|asp|bat|c|cfm|cgi|conf|cpp|css|dhtml|diz|h|hpp|htm|html|in|inc|java|js|jsp|lua|m4|mak|md5|nfo|nsi|pas|patch|php|phtml|pl|po|py|qmail|sh|sha1|sha256|sha512|shtml|sql|svg|tcl|tpl|txt|vbs|xhtml|xml|xrc"), normal },
115        { "Auto Ascii no extension", number, _T("1"), normal },
116        { "Auto Ascii dotfiles", number, _T("1"), normal },
117        { "Theme", string, _T("opencrystal/"), normal },
118        { "Language Code", string, _T(""), normal },
119        { "Last Server Path", string, _T(""), normal },
120        { "Concurrent download limit", number, _T("0"), normal },
121        { "Concurrent upload limit", number, _T("0"), normal },
122        { "Update Check", number, _T("1"), normal },
123        { "Update Check Interval", number, _T("7"), normal },
124        { "Last automatic update check", string, _T(""), normal },
125        { "Update Check New Version", string, _T(""), normal },
126        { "Update Check Check Beta", number, _T("0"), normal },
127        { "Show debug menu", number, _T("0"), normal },
128        { "File exists action download", number, _T("0"), normal },
129        { "File exists action upload", number, _T("0"), normal },
130        { "Allow ascii resume", number, _T("0"), normal },
131        { "Greeting version", string, _T(""), normal },
132        { "Onetime Dialogs", string, _T(""), normal },
133        { "Show Tree Local", number, _T("1"), normal },
134        { "Show Tree Remote", number, _T("1"), normal },
135        { "File Pane Layout", number, _T("0"), normal },
136        { "File Pane Swap", number, _T("0"), normal },
137        { "Last local directory", string, _T(""), normal },
138        { "Filelist directory sort", number, _T("0"), normal },
139        { "Filelist name sort", number, DEFAULT_FILENAME_SORT, normal },
140        { "Queue successful autoclear", number, _T("0"), normal },
141        { "Queue column widths", string, _T(""), normal },
142        { "Local filelist colwidths", string, _T(""), normal },
143        { "Remote filelist colwidths", string, _T(""), normal },
144        { "Window position and size", string, _T(""), normal },
145        { "Splitter positions (v2)", string, _T(""), normal },
146        { "Local filelist sortorder", string, _T(""), normal },
147        { "Remote filelist sortorder", string, _T(""), normal },
148        { "Time Format", string, _T(""), normal },
149        { "Date Format", string, _T(""), normal },
150        { "Show message log", number, _T("1"), normal },
151        { "Show queue", number, _T("1"), normal },
152        { "Default editor", string, _T(""), normal },
153        { "Always use default editor", number, _T("0"), normal },
154        { "Inherit system associations", number, _T("1"), normal },
155        { "Custom file associations", string, _T(""), normal },
156        { "Comparison mode", number, _T("1"), normal },
157        { "Comparison threshold", number, _T("1"), normal },
158        { "Site Manager position", string, _T(""), normal },
159        { "Theme icon size", string, _T(""), normal },
160        { "Timestamp in message log", number, _T("0"), normal },
161        { "Sitemanager last selected", string, _T(""), normal },
162        { "Local filelist shown columns", string, _T(""), normal },
163        { "Remote filelist shown columns", string, _T(""), normal },
164        { "Local filelist column order", string, _T(""), normal },
165        { "Remote filelist column order", string, _T(""), normal },
166        { "Filelist status bar", number, _T("1"), normal },
167        { "Filter toggle state", number, _T("0"), normal },
168        { "Show quickconnect bar", number, _T("1"), normal },
169        { "Messagelog position", number, _T("0"), normal },
170        { "Last connected site", string, _T(""), normal },
171        { "File doubleclock action", number, _T("0"), normal },
172        { "Dir doubleclock action", number, _T("0"), normal },
173        { "Minimize to tray", number, _T("0"), normal },
174        { "Search column widths", string, _T(""), normal },
175        { "Search column shown", string, _T(""), normal },
176        { "Search column order", string, _T(""), normal },
177        { "Search window size", string, _T(""), normal },
178        { "Comparison hide identical", number, _T("0"), normal },
179        { "Search sort order", string, _T(""), normal },
180        { "Edit track local", number, _T("1"), normal },
181        { "Prevent idle sleep", number, _T("1"), normal },
182        { "Filteredit window size", string, _T(""), normal },
183        { "Enable invalid char filter", number, _T("1"), normal },
184        { "Invalid char replace", string, _T("_"), normal },
185        { "Already connected choice", number, _T("0"), normal },
186        { "Edit status dialog size", string, _T(""), normal },
187        { "Display current speed", number, _T("0"), normal },
188        { "Toolbar hidden", number, _T("0"), normal },
189        { "Strip VMS revisions", number, _T("0"), normal },
190        { "Show Site Manager on startup", number, _T("0"), normal },
191        { "Prompt password change", number, _T("0"), normal },
192        { "Persistent Choices", number, _T("0"), normal },
193        { "Show notification bubbles", number, _T("1"), normal },
194
195        // Default/internal options
196        { "Config Location", string, _T(""), default_only },
197        { "Kiosk mode", number, _T("0"), default_priority },
198        { "Disable update check", number, _T("0"), default_only }
199};
200
201BEGIN_EVENT_TABLE(COptions, wxEvtHandler)
202EVT_TIMER(wxID_ANY, COptions::OnTimer)
203END_EVENT_TABLE()
204
205t_OptionsCache& t_OptionsCache::operator=(wxString const& v)
206{
207        strValue = v;
208        long l;
209        if (v.ToLong(&l)) {
210                numValue = l;
211        }
212        else {
213                numValue = 0;
214        }
215        return *this;
216}
217
218t_OptionsCache& t_OptionsCache::operator=(int v)
219{
220        numValue = v;
221        strValue.Printf("%d", v);
222        return *this;
223}
224
225COptions::COptions()
226{
227        m_theOptions = this;
228        m_pXmlFile = 0;
229        m_pLastServer = 0;
230
231        SetDefaultValues();
232
233        m_save_timer.SetOwner(this);
234
235        auto const nameOptionMap = GetNameOptionMap();
236        LoadGlobalDefaultOptions(nameOptionMap);
237
238        CLocalPath const dir = InitSettingsDir();
239
240        CInterProcessMutex mutex(MUTEX_OPTIONS);
241        m_pXmlFile = new CXmlFile(dir.GetPath() + _T("filezilla.xml"));
242        if (!m_pXmlFile->Load()) {
243                wxString msg = m_pXmlFile->GetError() + _T("\n\n") + _("For this session the default settings will be used. Any changes to the settings will not be saved.");
244                wxMessageBoxEx(msg, _("Error loading xml file"), wxICON_ERROR);
245                delete m_pXmlFile;
246                m_pXmlFile = 0;
247        }
248        else
249                CreateSettingsXmlElement();
250
251        LoadOptions(nameOptionMap);
252}
253
254std::map<std::string, unsigned int> COptions::GetNameOptionMap() const
255{
256        std::map<std::string, unsigned int> ret;
257        for (unsigned int i = 0; i < OPTIONS_NUM; ++i) {
258                if (options[i].flags != internal)
259                        ret.insert(std::make_pair(std::string(options[i].name), i));
260        }
261        return ret;
262}
263
264COptions::~COptions()
265{
266        COptionChangeEventHandler::UnregisterAllHandlers();
267
268        delete m_pLastServer;
269        delete m_pXmlFile;
270}
271
272int COptions::GetOptionVal(unsigned int nID)
273{
274        if (nID >= OPTIONS_NUM)
275                return 0;
276
277        fz::scoped_lock l(m_sync_);
278        return m_optionsCache[nID].numValue;
279}
280
281wxString COptions::GetOption(unsigned int nID)
282{
283        if (nID >= OPTIONS_NUM)
284                return wxString();
285
286        fz::scoped_lock l(m_sync_);
287        return m_optionsCache[nID].strValue;
288}
289
290bool COptions::SetOption(unsigned int nID, int value)
291{
292        if (nID >= OPTIONS_NUM)
293                return false;
294
295        if (options[nID].type != number)
296                return false;
297
298        ContinueSetOption(nID, value);
299        return true;
300}
301
302bool COptions::SetOption(unsigned int nID, wxString const& value)
303{
304        if (nID >= OPTIONS_NUM)
305                return false;
306
307        if (options[nID].type != string) {
308                long tmp;
309                if (!value.ToLong(&tmp))
310                        return false;
311
312                return SetOption(nID, tmp);
313        }
314
315        ContinueSetOption(nID, value);
316        return true;
317}
318
319template<typename T>
320void COptions::ContinueSetOption(unsigned int nID, T const& value)
321{
322        T validated = Validate(nID, value);
323
324        {
325                fz::scoped_lock l(m_sync_);
326                if (m_optionsCache[nID] == validated) {
327                        // Nothing to do
328                        return;
329                }
330                m_optionsCache[nID] = validated;
331        }
332
333        // Fixme: Setting options from other threads
334        if (!wxIsMainThread())
335                return;
336
337        if (options[nID].flags == normal || options[nID].flags == default_priority) {
338                SetXmlValue(nID, validated);
339
340                if (!m_save_timer.IsRunning())
341                        m_save_timer.Start(15000, true);
342        }
343
344        if (changedOptions_.none()) {
345                CallAfter(&COptions::NotifyChangedOptions);
346        }
347        changedOptions_.set(nID);
348}
349
350void COptions::NotifyChangedOptions()
351{
352        // Reset prior to notifying to correctly handle the case of an option being set while notifying
353        auto changedOptions = changedOptions_;
354        changedOptions_.reset();
355        COptionChangeEventHandler::DoNotify(changedOptions);
356}
357
358bool COptions::OptionFromFzDefaultsXml(unsigned int nID)
359{
360        if (nID >= OPTIONS_NUM)
361                return false;
362
363        fz::scoped_lock l(m_sync_);
364        return m_optionsCache[nID].from_default;
365}
366
367pugi::xml_node COptions::CreateSettingsXmlElement()
368{
369        if (!m_pXmlFile)
370                return pugi::xml_node();
371
372        auto element = m_pXmlFile->GetElement();
373        if (!element) {
374                return element;
375        }
376
377        auto settings = element.child("Settings");
378        if (settings) {
379                return settings;
380        }
381
382        settings = element.append_child("Settings");
383        for (int i = 0; i < OPTIONS_NUM; ++i) {
384                if (options[i].type == string) {
385                        SetXmlValue(i, GetOption(i));
386                }
387                else {
388                        SetXmlValue(i, GetOptionVal(i));
389                }
390        }
391
392        return settings;
393}
394
395void COptions::SetXmlValue(unsigned int nID, int value)
396{
397        SetXmlValue(nID, wxString::Format(_T("%d"), value));
398}
399
400void COptions::SetXmlValue(unsigned int nID, wxString const& value)
401{
402        if (!m_pXmlFile)
403                return;
404
405        // No checks are made about the validity of the value, that's done in SetOption
406
407        wxScopedCharBuffer utf8 = value.utf8_str();
408        if (!utf8)
409                return;
410
411        auto settings = CreateSettingsXmlElement();
412        if (settings) {
413                pugi::xml_node setting;
414                for (setting = settings.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
415                        const char *attribute = setting.attribute("name").value();
416                        if (!attribute)
417                                continue;
418                        if (!strcmp(attribute, options[nID].name))
419                                break;
420                }
421                if (!setting) {
422                        setting = settings.append_child("Setting");
423                        SetTextAttribute(setting, "name", options[nID].name);
424                }
425                setting.text() = utf8;
426        }
427}
428
429int COptions::Validate(unsigned int nID, int value)
430{
431        switch (nID)
432        {
433        case OPTION_UPDATECHECK_INTERVAL:
434                if (value < 1 || value > 7)
435                        value = 7;
436                break;
437        case OPTION_LOGGING_DEBUGLEVEL:
438                if (value < 0 || value > 4)
439                        value = 0;
440                break;
441        case OPTION_RECONNECTCOUNT:
442                if (value < 0 || value > 99)
443                        value = 5;
444                break;
445        case OPTION_RECONNECTDELAY:
446                if (value < 0 || value > 999)
447                        value = 5;
448                break;
449        case OPTION_FILEPANE_LAYOUT:
450                if (value < 0 || value > 3)
451                        value = 0;
452                break;
453        case OPTION_SPEEDLIMIT_INBOUND:
454        case OPTION_SPEEDLIMIT_OUTBOUND:
455                if (value < 0)
456                        value = 0;
457                break;
458        case OPTION_SPEEDLIMIT_BURSTTOLERANCE:
459                if (value < 0 || value > 2)
460                        value = 0;
461                break;
462        case OPTION_FILELIST_DIRSORT:
463        case OPTION_FILELIST_NAMESORT:
464                if (value < 0 || value > 2)
465                        value = 0;
466                break;
467        case OPTION_SOCKET_BUFFERSIZE_RECV:
468                if (value != -1 && (value < 4096 || value > 4096 * 1024))
469                        value = -1;
470                break;
471        case OPTION_SOCKET_BUFFERSIZE_SEND:
472                if (value != -1 && (value < 4096 || value > 4096 * 1024))
473                        value = 131072;
474                break;
475        case OPTION_COMPARISONMODE:
476                if (value < 0 || value > 0)
477                        value = 1;
478                break;
479        case OPTION_COMPARISON_THRESHOLD:
480                if (value < 0 || value > 1440)
481                        value = 1;
482                break;
483        case OPTION_SIZE_DECIMALPLACES:
484                if (value < 0 || value > 3)
485                        value = 0;
486                break;
487        case OPTION_MESSAGELOG_POSITION:
488                if (value < 0 || value > 2)
489                        value = 0;
490                break;
491        case OPTION_DOUBLECLICK_ACTION_FILE:
492        case OPTION_DOUBLECLICK_ACTION_DIRECTORY:
493                if (value < 0 || value > 3)
494                        value = 0;
495                break;
496        case OPTION_SIZE_FORMAT:
497                if (value < 0 || value >= CSizeFormat::formats_count)
498                        value = 0;
499                break;
500        case OPTION_TIMEOUT:
501                if (value <= 0) {
502                        value = 0;
503                }
504                else if (value < 10) {
505                        value = 10;
506                }
507                else if (value > 9999) {
508                        value = 9999;
509                }
510                break;
511        }
512        return value;
513}
514
515wxString COptions::Validate(unsigned int nID, wxString const& value)
516{
517        if (nID == OPTION_INVALID_CHAR_REPLACE) {
518                if (value.Len() > 1)
519                        return _T("_");
520        }
521        return value;
522}
523
524void COptions::SetServer(wxString path, const CServer& server)
525{
526        if (!m_pXmlFile)
527                return;
528
529        if (path.empty())
530                return;
531
532        auto element = m_pXmlFile->GetElement();
533
534        while (!path.empty()) {
535                wxString sub;
536                int pos = path.Find('/');
537                if (pos != -1) {
538                        sub = path.Left(pos);
539                        path = path.Mid(pos + 1);
540                }
541                else {
542                        sub = path;
543                        path = _T("");
544                }
545                wxScopedCharBuffer utf8 = sub.utf8_str();
546                if (!utf8)
547                        return;
548                auto newElement = element.child(utf8);
549                if (newElement)
550                        element = newElement;
551                else {
552                        element = element.append_child(utf8);
553                }
554        }
555
556        ::SetServer(element, server);
557
558        if (GetOptionVal(OPTION_DEFAULT_KIOSKMODE) == 2)
559                return;
560
561        CInterProcessMutex mutex(MUTEX_OPTIONS);
562        m_pXmlFile->Save(true);
563}
564
565bool COptions::GetServer(wxString path, CServer& server)
566{
567        if (path.empty())
568                return false;
569
570        if (!m_pXmlFile)
571                return false;
572        auto element = m_pXmlFile->GetElement();
573
574        while (!path.empty()) {
575                wxString sub;
576                int pos = path.Find('/');
577                if (pos != -1) {
578                        sub = path.Left(pos);
579                        path = path.Mid(pos + 1);
580                }
581                else {
582                        sub = path;
583                        path = _T("");
584                }
585                wxScopedCharBuffer utf8 = sub.utf8_str();
586                if (!utf8)
587                        return false;
588                element = element.child(utf8);
589                if (!element)
590                        return false;
591        }
592
593        bool res = ::GetServer(element, server);
594
595        return res;
596}
597
598void COptions::SetLastServer(const CServer& server)
599{
600        if (!m_pLastServer)
601                m_pLastServer = new CServer(server);
602        else
603                *m_pLastServer = server;
604        SetServer(_T("Settings/LastServer"), server);
605}
606
607bool COptions::GetLastServer(CServer& server)
608{
609        if (!m_pLastServer) {
610                bool res = GetServer(_T("Settings/LastServer"), server);
611                if (res)
612                        m_pLastServer = new CServer(server);
613                return res;
614        }
615        else {
616                server = *m_pLastServer;
617                if (server == CServer())
618                        return false;
619
620                return true;
621        }
622}
623
624void COptions::Init()
625{
626        if (!m_theOptions)
627                new COptions(); // It sets m_theOptions internally itself
628}
629
630void COptions::Destroy()
631{
632        if (!m_theOptions)
633                return;
634
635        delete m_theOptions;
636        m_theOptions = 0;
637}
638
639COptions* COptions::Get()
640{
641        return m_theOptions;
642}
643
644void COptions::Import(pugi::xml_node element)
645{
646        LoadOptions(GetNameOptionMap(), element);
647        if (!m_save_timer.IsRunning())
648                m_save_timer.Start(15000, true);
649}
650
651void COptions::LoadOptions(std::map<std::string, unsigned int> const& nameOptionMap, pugi::xml_node settings)
652{
653        if (!settings) {
654                settings = CreateSettingsXmlElement();
655                if (!settings) {
656                        return;
657                }
658        }
659
660        for (auto setting = settings.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
661                LoadOptionFromElement(setting, nameOptionMap, false);
662        }
663}
664
665void COptions::LoadOptionFromElement(pugi::xml_node option, std::map<std::string, unsigned int> const& nameOptionMap, bool allowDefault)
666{
667        const char* name = option.attribute("name").value();
668        if (!name)
669                return;
670
671        auto const iter = nameOptionMap.find(name);
672        if (iter != nameOptionMap.end()) {
673                if (!allowDefault && options[iter->second].flags == default_only)
674                        return;
675                wxString value = GetTextElement(option);
676                if (options[iter->second].flags == default_priority) {
677                        if (allowDefault) {
678                                fz::scoped_lock l(m_sync_);
679                                m_optionsCache[iter->second].from_default = true;
680                        }
681                        else {
682                                fz::scoped_lock l(m_sync_);
683                                if (m_optionsCache[iter->second].from_default)
684                                        return;
685                        }
686                }
687
688                if (options[iter->second].type == number) {
689                        long numValue = 0;
690                        value.ToLong(&numValue);
691                        numValue = Validate(iter->second, numValue);
692                        fz::scoped_lock l(m_sync_);
693                        m_optionsCache[iter->second] = numValue;
694                }
695                else {
696                        value = Validate(iter->second, value);
697                        fz::scoped_lock l(m_sync_);
698                        m_optionsCache[iter->second] = value;
699                }
700        }
701}
702
703void COptions::LoadGlobalDefaultOptions(std::map<std::string, unsigned int> const& nameOptionMap)
704{
705        CLocalPath const defaultsDir = wxGetApp().GetDefaultsDir();
706        if (defaultsDir.empty())
707                return;
708
709        CXmlFile file(defaultsDir.GetPath() + _T("fzdefaults.xml"));
710        if (!file.Load())
711                return;
712
713        auto element = file.GetElement();
714        if (!element)
715                return;
716
717        element = element.child("Settings");
718        if (!element)
719                return;
720
721        for (auto setting = element.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
722                LoadOptionFromElement(setting, nameOptionMap, true);
723        }
724}
725
726void COptions::OnTimer(wxTimerEvent&)
727{
728        Save();
729}
730
731void COptions::Save()
732{
733        if (GetOptionVal(OPTION_DEFAULT_KIOSKMODE) == 2)
734                return;
735
736        if (!m_pXmlFile)
737                return;
738
739        CInterProcessMutex mutex(MUTEX_OPTIONS);
740        m_pXmlFile->Save(true);
741}
742
743void COptions::SaveIfNeeded()
744{
745        if (!m_save_timer.IsRunning())
746                return;
747
748        m_save_timer.Stop();
749        Save();
750}
751
752namespace {
753#ifndef __WXMSW__
754wxString TryDirectory( wxString path, wxString const& suffix, bool check_exists )
755{
756        if( !path.empty() && path[0] == '/' ) {
757                if( path[path.size() - 1] != '/' ) {
758                        path += '/';
759                }
760
761                path += suffix;
762
763                if( check_exists ) {
764                        if( !wxFileName::DirExists(path) ) {
765                                path.clear();
766                        }
767                }
768        }
769        else {
770                path.clear();
771        }
772        return path;
773}
774#endif
775
776wxString GetEnv(wxString const& env)
777{
778        wxString ret;
779        if( !wxGetEnv(env, &ret) ) {
780                ret.clear();
781        }
782        return ret;
783}
784}
785
786CLocalPath COptions::GetUnadjustedSettingsDir()
787{
788        wxFileName fn;
789#ifdef __WXMSW__
790        wxChar buffer[MAX_PATH * 2 + 1];
791
792        if (SUCCEEDED(SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, buffer))) {
793                fn = wxFileName(buffer, _T(""));
794                fn.AppendDir(_T("FileZilla"));
795        }
796        else {
797                // Fall back to directory where the executable is
798                if (GetModuleFileName(0, buffer, MAX_PATH * 2))
799                        fn = buffer;
800        }
801#else
802        wxString cfg = TryDirectory(GetEnv(_T("XDG_CONFIG_HOME")), _T("filezilla/"), true);
803        if( cfg.empty() ) {
804                cfg = TryDirectory(wxGetHomeDir(), _T(".config/filezilla/"), true);
805        }
806        if( cfg.empty() ) {
807                cfg = TryDirectory(wxGetHomeDir(), _T(".filezilla/"), true);
808        }
809        if( cfg.empty() ) {
810                cfg = TryDirectory(GetEnv(_T("XDG_CONFIG_HOME")), _T("filezilla/"), false);
811        }
812        if( cfg.empty() ) {
813                cfg = TryDirectory(wxGetHomeDir(), _T(".config/filezilla/"), false);
814        }
815        if( cfg.empty() ) {
816                cfg = TryDirectory(wxGetHomeDir(), _T(".filezilla/"), false);
817        }
818        fn = wxFileName(cfg, _T(""));
819#endif
820        return CLocalPath(fn.GetPath());
821}
822
823CLocalPath COptions::InitSettingsDir()
824{
825        CLocalPath p;
826
827        wxString dir(GetOption(OPTION_DEFAULT_SETTINGSDIR));
828        if (!dir.empty()) {
829                wxStringTokenizer tokenizer(dir, _T("/\\"), wxTOKEN_RET_EMPTY_ALL);
830                dir = _T("");
831                while (tokenizer.HasMoreTokens()) {
832                        wxString token = tokenizer.GetNextToken();
833                        if (!token.empty() && token[0] == '$') {
834                                if (token.size() > 1 && token[1] == '$')
835                                        token = token.Mid(1);
836                                else {
837                                        token = GetEnv(token.Mid(1));
838                                }
839                        }
840                        dir += token;
841                        const wxChar delimiter = tokenizer.GetLastDelimiter();
842                        if (delimiter)
843                                dir += delimiter;
844                }
845
846                p.SetPath(wxGetApp().GetDefaultsDir().GetPath());
847                p.ChangePath(dir);
848        }
849        else {
850                p = GetUnadjustedSettingsDir();
851        }
852
853        if (!p.empty() && !p.Exists())
854                wxFileName::Mkdir( p.GetPath(), 0700, wxPATH_MKDIR_FULL );
855
856        SetOption(OPTION_DEFAULT_SETTINGSDIR, p.GetPath());
857
858        return p;
859}
860
861void COptions::SetDefaultValues()
862{
863        fz::scoped_lock l(m_sync_);
864        for (int i = 0; i < OPTIONS_NUM; ++i) {
865                m_optionsCache[i] = options[i].defaultValue;
866                m_optionsCache[i].from_default = false;
867        }
868}
Note: See TracBrowser for help on using the repository browser.