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

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 22.1 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
194        // Default/internal options
195        { "Config Location", string, _T(""), default_only },
196        { "Kiosk mode", number, _T("0"), default_priority },
197        { "Disable update check", number, _T("0"), default_only }
198};
199
200BEGIN_EVENT_TABLE(COptions, wxEvtHandler)
201EVT_TIMER(wxID_ANY, COptions::OnTimer)
202END_EVENT_TABLE()
203
204t_OptionsCache& t_OptionsCache::operator=(wxString const& v)
205{
206        strValue = v;
207        long l;
208        if (v.ToLong(&l)) {
209                numValue = l;
210        }
211        else {
212                numValue = 0;
213        }
214        return *this;
215}
216
217t_OptionsCache& t_OptionsCache::operator=(int v)
218{
219        numValue = v;
220        strValue.Printf("%d", v);
221        return *this;
222}
223
224COptions::COptions()
225{
226        m_theOptions = this;
227        m_pXmlFile = 0;
228        m_pLastServer = 0;
229
230        SetDefaultValues();
231
232        m_save_timer.SetOwner(this);
233
234        auto const nameOptionMap = GetNameOptionMap();
235        LoadGlobalDefaultOptions(nameOptionMap);
236
237        CLocalPath const dir = InitSettingsDir();
238
239        CInterProcessMutex mutex(MUTEX_OPTIONS);
240        m_pXmlFile = new CXmlFile(dir.GetPath() + _T("filezilla.xml"));
241        if (!m_pXmlFile->Load()) {
242                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.");
243                wxMessageBoxEx(msg, _("Error loading xml file"), wxICON_ERROR);
244                delete m_pXmlFile;
245                m_pXmlFile = 0;
246        }
247        else
248                CreateSettingsXmlElement();
249
250        LoadOptions(nameOptionMap);
251}
252
253std::map<std::string, unsigned int> COptions::GetNameOptionMap() const
254{
255        std::map<std::string, unsigned int> ret;
256        for (unsigned int i = 0; i < OPTIONS_NUM; ++i) {
257                if (options[i].flags != internal)
258                        ret.insert(std::make_pair(std::string(options[i].name), i));
259        }
260        return ret;
261}
262
263COptions::~COptions()
264{
265        COptionChangeEventHandler::UnregisterAllHandlers();
266
267        delete m_pLastServer;
268        delete m_pXmlFile;
269}
270
271int COptions::GetOptionVal(unsigned int nID)
272{
273        if (nID >= OPTIONS_NUM)
274                return 0;
275
276        scoped_lock l(m_sync_);
277        return m_optionsCache[nID].numValue;
278}
279
280wxString COptions::GetOption(unsigned int nID)
281{
282        if (nID >= OPTIONS_NUM)
283                return wxString();
284
285        scoped_lock l(m_sync_);
286        return m_optionsCache[nID].strValue;
287}
288
289bool COptions::SetOption(unsigned int nID, int value)
290{
291        if (nID >= OPTIONS_NUM)
292                return false;
293
294        if (options[nID].type != number)
295                return false;
296
297        ContinueSetOption(nID, value);
298        return true;
299}
300
301bool COptions::SetOption(unsigned int nID, wxString const& value)
302{
303        if (nID >= OPTIONS_NUM)
304                return false;
305
306        if (options[nID].type != string) {
307                long tmp;
308                if (!value.ToLong(&tmp))
309                        return false;
310
311                return SetOption(nID, tmp);
312        }
313
314        ContinueSetOption(nID, value);
315        return true;
316}
317
318template<typename T>
319void COptions::ContinueSetOption(unsigned int nID, T const& value)
320{
321        T validated = Validate(nID, value);
322
323        {
324                scoped_lock l(m_sync_);
325                if (m_optionsCache[nID] == validated) {
326                        // Nothing to do
327                        return;
328                }
329                m_optionsCache[nID] = validated;
330        }
331
332        // Fixme: Setting options from other threads
333        if (!wxIsMainThread())
334                return;
335
336        if (options[nID].flags == normal || options[nID].flags == default_priority) {
337                SetXmlValue(nID, validated);
338
339                if (!m_save_timer.IsRunning())
340                        m_save_timer.Start(15000, true);
341        }
342
343        if (changedOptions_.none()) {
344                CallAfter(&COptions::NotifyChangedOptions);
345        }
346        changedOptions_.set(nID);
347}
348
349void COptions::NotifyChangedOptions()
350{
351        // Reset prior to notifying to correctly handle the case of an option being set while notifying
352        auto changedOptions = changedOptions_;
353        changedOptions_.reset();
354        COptionChangeEventHandler::DoNotify(changedOptions);
355}
356
357bool COptions::OptionFromFzDefaultsXml(unsigned int nID)
358{
359        if (nID >= OPTIONS_NUM)
360                return false;
361
362        scoped_lock l(m_sync_);
363        return m_optionsCache[nID].from_default;
364}
365
366pugi::xml_node COptions::CreateSettingsXmlElement()
367{
368        if (!m_pXmlFile)
369                return pugi::xml_node();
370
371        auto element = m_pXmlFile->GetElement();
372        if (!element) {
373                return element;
374        }
375
376        auto settings = element.child("Settings");
377        if (settings) {
378                return settings;
379        }
380
381        settings = element.append_child("Settings");
382        for (int i = 0; i < OPTIONS_NUM; ++i) {
383                if (options[i].type == string) {
384                        SetXmlValue(i, GetOption(i));
385                }
386                else {
387                        SetXmlValue(i, GetOptionVal(i));
388                }
389        }
390
391        return settings;
392}
393
394void COptions::SetXmlValue(unsigned int nID, int value)
395{
396        SetXmlValue(nID, wxString::Format(_T("%d"), value));
397}
398
399void COptions::SetXmlValue(unsigned int nID, wxString const& value)
400{
401        if (!m_pXmlFile)
402                return;
403
404        // No checks are made about the validity of the value, that's done in SetOption
405
406        wxScopedCharBuffer utf8 = value.utf8_str();
407        if (!utf8)
408                return;
409
410        auto settings = CreateSettingsXmlElement();
411        if (settings) {
412                pugi::xml_node setting;
413                for (setting = settings.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
414                        const char *attribute = setting.attribute("name").value();
415                        if (!attribute)
416                                continue;
417                        if (!strcmp(attribute, options[nID].name))
418                                break;
419                }
420                if (!setting) {
421                        setting = settings.append_child("Setting");
422                        SetTextAttribute(setting, "name", options[nID].name);
423                }
424                setting.text() = utf8;
425        }
426}
427
428int COptions::Validate(unsigned int nID, int value)
429{
430        switch (nID)
431        {
432        case OPTION_UPDATECHECK_INTERVAL:
433                if (value < 1 || value > 7)
434                        value = 7;
435                break;
436        case OPTION_LOGGING_DEBUGLEVEL:
437                if (value < 0 || value > 4)
438                        value = 0;
439                break;
440        case OPTION_RECONNECTCOUNT:
441                if (value < 0 || value > 99)
442                        value = 5;
443                break;
444        case OPTION_RECONNECTDELAY:
445                if (value < 0 || value > 999)
446                        value = 5;
447                break;
448        case OPTION_FILEPANE_LAYOUT:
449                if (value < 0 || value > 3)
450                        value = 0;
451                break;
452        case OPTION_SPEEDLIMIT_INBOUND:
453        case OPTION_SPEEDLIMIT_OUTBOUND:
454                if (value < 0)
455                        value = 0;
456                break;
457        case OPTION_SPEEDLIMIT_BURSTTOLERANCE:
458                if (value < 0 || value > 2)
459                        value = 0;
460                break;
461        case OPTION_FILELIST_DIRSORT:
462        case OPTION_FILELIST_NAMESORT:
463                if (value < 0 || value > 2)
464                        value = 0;
465                break;
466        case OPTION_SOCKET_BUFFERSIZE_RECV:
467                if (value != -1 && (value < 4096 || value > 4096 * 1024))
468                        value = -1;
469                break;
470        case OPTION_SOCKET_BUFFERSIZE_SEND:
471                if (value != -1 && (value < 4096 || value > 4096 * 1024))
472                        value = 131072;
473                break;
474        case OPTION_COMPARISONMODE:
475                if (value < 0 || value > 0)
476                        value = 1;
477                break;
478        case OPTION_COMPARISON_THRESHOLD:
479                if (value < 0 || value > 1440)
480                        value = 1;
481                break;
482        case OPTION_SIZE_DECIMALPLACES:
483                if (value < 0 || value > 3)
484                        value = 0;
485                break;
486        case OPTION_MESSAGELOG_POSITION:
487                if (value < 0 || value > 2)
488                        value = 0;
489                break;
490        case OPTION_DOUBLECLICK_ACTION_FILE:
491        case OPTION_DOUBLECLICK_ACTION_DIRECTORY:
492                if (value < 0 || value > 3)
493                        value = 0;
494                break;
495        case OPTION_SIZE_FORMAT:
496                if (value < 0 || value >= CSizeFormat::formats_count)
497                        value = 0;
498                break;
499        case OPTION_TIMEOUT:
500                if (value <= 0) {
501                        value = 0;
502                }
503                else if (value < 10) {
504                        value = 10;
505                }
506                else if (value > 9999) {
507                        value = 9999;
508                }
509                break;
510        }
511        return value;
512}
513
514wxString COptions::Validate(unsigned int nID, wxString const& value)
515{
516        if (nID == OPTION_INVALID_CHAR_REPLACE) {
517                if (value.Len() > 1)
518                        return _T("_");
519        }
520        return value;
521}
522
523void COptions::SetServer(wxString path, const CServer& server)
524{
525        if (!m_pXmlFile)
526                return;
527
528        if (path.empty())
529                return;
530
531        auto element = m_pXmlFile->GetElement();
532
533        while (!path.empty()) {
534                wxString sub;
535                int pos = path.Find('/');
536                if (pos != -1) {
537                        sub = path.Left(pos);
538                        path = path.Mid(pos + 1);
539                }
540                else {
541                        sub = path;
542                        path = _T("");
543                }
544                wxScopedCharBuffer utf8 = sub.utf8_str();
545                if (!utf8)
546                        return;
547                auto newElement = element.child(utf8);
548                if (newElement)
549                        element = newElement;
550                else {
551                        element = element.append_child(utf8);
552                }
553        }
554
555        ::SetServer(element, server);
556
557        if (GetOptionVal(OPTION_DEFAULT_KIOSKMODE) == 2)
558                return;
559
560        CInterProcessMutex mutex(MUTEX_OPTIONS);
561        m_pXmlFile->Save(true);
562}
563
564bool COptions::GetServer(wxString path, CServer& server)
565{
566        if (path.empty())
567                return false;
568
569        if (!m_pXmlFile)
570                return false;
571        auto element = m_pXmlFile->GetElement();
572
573        while (!path.empty()) {
574                wxString sub;
575                int pos = path.Find('/');
576                if (pos != -1) {
577                        sub = path.Left(pos);
578                        path = path.Mid(pos + 1);
579                }
580                else {
581                        sub = path;
582                        path = _T("");
583                }
584                wxScopedCharBuffer utf8 = sub.utf8_str();
585                if (!utf8)
586                        return false;
587                element = element.child(utf8);
588                if (!element)
589                        return false;
590        }
591
592        bool res = ::GetServer(element, server);
593
594        return res;
595}
596
597void COptions::SetLastServer(const CServer& server)
598{
599        if (!m_pLastServer)
600                m_pLastServer = new CServer(server);
601        else
602                *m_pLastServer = server;
603        SetServer(_T("Settings/LastServer"), server);
604}
605
606bool COptions::GetLastServer(CServer& server)
607{
608        if (!m_pLastServer) {
609                bool res = GetServer(_T("Settings/LastServer"), server);
610                if (res)
611                        m_pLastServer = new CServer(server);
612                return res;
613        }
614        else {
615                server = *m_pLastServer;
616                if (server == CServer())
617                        return false;
618
619                return true;
620        }
621}
622
623void COptions::Init()
624{
625        if (!m_theOptions)
626                new COptions(); // It sets m_theOptions internally itself
627}
628
629void COptions::Destroy()
630{
631        if (!m_theOptions)
632                return;
633
634        delete m_theOptions;
635        m_theOptions = 0;
636}
637
638COptions* COptions::Get()
639{
640        return m_theOptions;
641}
642
643void COptions::Import(pugi::xml_node element)
644{
645        LoadOptions(GetNameOptionMap(), element);
646        if (!m_save_timer.IsRunning())
647                m_save_timer.Start(15000, true);
648}
649
650void COptions::LoadOptions(std::map<std::string, unsigned int> const& nameOptionMap, pugi::xml_node settings)
651{
652        if (!settings) {
653                settings = CreateSettingsXmlElement();
654                if (!settings) {
655                        return;
656                }
657        }
658
659        for (auto setting = settings.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
660                LoadOptionFromElement(setting, nameOptionMap, false);
661        }
662}
663
664void COptions::LoadOptionFromElement(pugi::xml_node option, std::map<std::string, unsigned int> const& nameOptionMap, bool allowDefault)
665{
666        const char* name = option.attribute("name").value();
667        if (!name)
668                return;
669
670        auto const iter = nameOptionMap.find(name);
671        if (iter != nameOptionMap.end()) {
672                if (!allowDefault && options[iter->second].flags == default_only)
673                        return;
674                wxString value = GetTextElement(option);
675                if (options[iter->second].flags == default_priority) {
676                        if (allowDefault) {
677                                scoped_lock l(m_sync_);
678                                m_optionsCache[iter->second].from_default = true;
679                        }
680                        else {
681                                scoped_lock l(m_sync_);
682                                if (m_optionsCache[iter->second].from_default)
683                                        return;
684                        }
685                }
686
687                if (options[iter->second].type == number) {
688                        long numValue = 0;
689                        value.ToLong(&numValue);
690                        numValue = Validate(iter->second, numValue);
691                        scoped_lock l(m_sync_);
692                        m_optionsCache[iter->second] = numValue;
693                }
694                else {
695                        value = Validate(iter->second, value);
696                        scoped_lock l(m_sync_);
697                        m_optionsCache[iter->second] = value;
698                }
699        }
700}
701
702void COptions::LoadGlobalDefaultOptions(std::map<std::string, unsigned int> const& nameOptionMap)
703{
704        CLocalPath const defaultsDir = wxGetApp().GetDefaultsDir();
705        if (defaultsDir.empty())
706                return;
707
708        CXmlFile file(defaultsDir.GetPath() + _T("fzdefaults.xml"));
709        if (!file.Load())
710                return;
711
712        auto element = file.GetElement();
713        if (!element)
714                return;
715
716        element = element.child("Settings");
717        if (!element)
718                return;
719
720        for (auto setting = element.child("Setting"); setting; setting = setting.next_sibling("Setting")) {
721                LoadOptionFromElement(setting, nameOptionMap, true);
722        }
723}
724
725void COptions::OnTimer(wxTimerEvent&)
726{
727        Save();
728}
729
730void COptions::Save()
731{
732        if (GetOptionVal(OPTION_DEFAULT_KIOSKMODE) == 2)
733                return;
734
735        if (!m_pXmlFile)
736                return;
737
738        CInterProcessMutex mutex(MUTEX_OPTIONS);
739        m_pXmlFile->Save(true);
740}
741
742void COptions::SaveIfNeeded()
743{
744        if (!m_save_timer.IsRunning())
745                return;
746
747        m_save_timer.Stop();
748        Save();
749}
750
751namespace {
752#ifndef __WXMSW__
753wxString TryDirectory( wxString path, wxString const& suffix, bool check_exists )
754{
755        if( !path.empty() && path[0] == '/' ) {
756                if( path[path.size() - 1] != '/' ) {
757                        path += '/';
758                }
759
760                path += suffix;
761
762                if( check_exists ) {
763                        if( !wxFileName::DirExists(path) ) {
764                                path.clear();
765                        }
766                }
767        }
768        else {
769                path.clear();
770        }
771        return path;
772}
773#endif
774
775wxString GetEnv(wxString const& env)
776{
777        wxString ret;
778        if( !wxGetEnv(env, &ret) ) {
779                ret.clear();
780        }
781        return ret;
782}
783}
784
785CLocalPath COptions::GetUnadjustedSettingsDir()
786{
787        wxFileName fn;
788#ifdef __WXMSW__
789        wxChar buffer[MAX_PATH * 2 + 1];
790
791        if (SUCCEEDED(SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, buffer))) {
792                fn = wxFileName(buffer, _T(""));
793                fn.AppendDir(_T("FileZilla"));
794        }
795        else {
796                // Fall back to directory where the executable is
797                if (GetModuleFileName(0, buffer, MAX_PATH * 2))
798                        fn = buffer;
799        }
800#else
801        wxString cfg = TryDirectory(GetEnv(_T("XDG_CONFIG_HOME")), _T("filezilla/"), true);
802        if( cfg.empty() ) {
803                cfg = TryDirectory(wxGetHomeDir(), _T(".config/filezilla/"), true);
804        }
805        if( cfg.empty() ) {
806                cfg = TryDirectory(wxGetHomeDir(), _T(".filezilla/"), true);
807        }
808        if( cfg.empty() ) {
809                cfg = TryDirectory(GetEnv(_T("XDG_CONFIG_HOME")), _T("filezilla/"), false);
810        }
811        if( cfg.empty() ) {
812                cfg = TryDirectory(wxGetHomeDir(), _T(".config/filezilla/"), false);
813        }
814        if( cfg.empty() ) {
815                cfg = TryDirectory(wxGetHomeDir(), _T(".filezilla/"), false);
816        }
817        fn = wxFileName(cfg, _T(""));
818#endif
819        return CLocalPath(fn.GetPath());
820}
821
822CLocalPath COptions::InitSettingsDir()
823{
824        CLocalPath p;
825
826        wxString dir(GetOption(OPTION_DEFAULT_SETTINGSDIR));
827        if (!dir.empty()) {
828                wxStringTokenizer tokenizer(dir, _T("/\\"), wxTOKEN_RET_EMPTY_ALL);
829                dir = _T("");
830                while (tokenizer.HasMoreTokens()) {
831                        wxString token = tokenizer.GetNextToken();
832                        if (!token.empty() && token[0] == '$') {
833                                if (token.size() > 1 && token[1] == '$')
834                                        token = token.Mid(1);
835                                else {
836                                        token = GetEnv(token.Mid(1));
837                                }
838                        }
839                        dir += token;
840                        const wxChar delimiter = tokenizer.GetLastDelimiter();
841                        if (delimiter)
842                                dir += delimiter;
843                }
844
845                p.SetPath(wxGetApp().GetDefaultsDir().GetPath());
846                p.ChangePath(dir);
847        }
848        else {
849                p = GetUnadjustedSettingsDir();
850        }
851
852        if (!p.empty() && !p.Exists())
853                wxFileName::Mkdir( p.GetPath(), 0700, wxPATH_MKDIR_FULL );
854
855        SetOption(OPTION_DEFAULT_SETTINGSDIR, p.GetPath());
856
857        return p;
858}
859
860void COptions::SetDefaultValues()
861{
862        scoped_lock l(m_sync_);
863        for (int i = 0; i < OPTIONS_NUM; ++i) {
864                m_optionsCache[i] = options[i].defaultValue;
865                m_optionsCache[i].from_default = false;
866        }
867}
Note: See TracBrowser for help on using the repository browser.