1 /*
2  * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include "stdinc.h"
20 
21 #include "SettingsManager.h"
22 
23 #include "SimpleXML.h"
24 #include "Util.h"
25 #include "File.h"
26 #include "version.h"
27 #include "AdcHub.h"
28 #include "CID.h"
29 #ifdef WITH_DHT
30 #include "dht/DHT.h"
31 #endif
32 #include "SearchManager.h"
33 #include "StringTokenizer.h"
34 
35 namespace dcpp {
36 
37 StringList SettingsManager::connectionSpeeds;
38 
39 const string SettingsManager::settingTags[] =
40 {
41     // Strings
42     "Nick", "UploadSpeed", "Description", "DownloadDirectory", "EMail",
43     "ExternalIp", "HublistServers", "HttpProxy",
44     "LogDirectory", "LogFormatPostDownload","LogFormatPostFinishedDownload",
45     "LogFormatPostUpload", "LogFormatMainChat", "LogFormatPrivateChat",
46     "TempDownloadDirectory", "BindAddress", "SocksServer",
47     "SocksUser", "SocksPassword", "ConfigVersion", "DefaultAwayMessage",
48     "TimeStampsFormat", "CID", "LogFileMainChat", "LogFilePrivateChat",
49     "LogFileStatus", "LogFileUpload", "LogFileDownload", "LogFileFinishedDownload",
50     "LogFileSystem",
51     "LogFormatSystem", "LogFormatStatus", "LogFileSpy", "LogFormatSpy", "TLSPrivateKeyFile",
52     "TLSCertificateFile", "TLSTrustedCertificatesPath",
53     "Language", "SkipListShare", "InternetIp", "BindIfaceName",
54     "DHTKey", "DynDNSServer", "MimeHandler",
55     "SENTRY",
56     // Ints
57     "IncomingConnections", "InPort", "Slots", "AutoFollow",
58     "ShareHidden", "FilterMessages", "AutoSearch",
59     "AutoSearchTime", "ReportFoundAlternates", "TimeStamps",
60     "IgnoreHubPms", "IgnoreBotPms",
61     "ListDuplicates", "BufferSize", "DownloadSlots", "MaxDownloadSpeed",
62     "LogMainChat", "LogPrivateChat", "LogDownloads","LogFileFinishedDownload",
63     "LogUploads", "MinUploadSpeed", "AutoAway",
64     "SocksPort", "SocksResolve", "KeepLists", "AutoKick",
65     "CompressTransfers", "SFVCheck",
66     "MaxCompression", "NoAwayMsgToBots", "SkipZeroByte", "AdlsBreakOnFirst",
67     "HubUserCommands", "AutoSearchAutoMatch","LogSystem",
68     "LogFilelistTransfers",
69     "SendUnknownCommands", "MaxHashSpeed",
70     "GetUserCountry", "LogStatusMessages", "SearchPassiveAlways",
71     "AddFinishedInstantly", "DontDLAlreadyShared",
72     "UDPPort", "ShowLastLinesLog", "AdcDebug",
73     "SearchHistory", "SetMinislotSize",
74     "MaxFilelistSize", "HighestPrioSize", "HighPrioSize", "NormalPrioSize",
75     "LowPrioSize", "LowestPrio", "AutoDropSpeed", "AutoDropInterval",
76     "AutoDropElapsed", "AutoDropInactivity", "AutoDropMinSources",
77     "AutoDropFilesize", "AutoDropAll", "AutoDropFilelists",
78     "AutoDropDisconnect", "OutgoingConnections", "NoIpOverride", "NoUseTempDir",
79     "ShareTempFiles", "SearchOnlyFreeSlots", "LastSearchType",
80     "SocketInBuffer", "SocketOutBuffer",
81     "AutoRefreshTime", "HashingStartDelay", "UseTLS", "AutoSearchLimit",
82     "AutoKickNoFavs", "PromptPassword",
83     "DontDlAlreadyQueued", "MaxCommandLength", "AllowUntrustedHubs",
84     "AllowUntrustedClients", "TLSPort", "FastHash",
85     "SegmentedDL", "FollowLinks", "SendBloom",
86     "Coral", "SearchFilterShared", "FinishedDLOnlyFull",
87     "SearchMerge", "HashBufferSize", "HashBufferPopulate",
88     "HashBufferNoReserve", "HashBufferPrivate",
89     "UseDHT", "DHTPort",
90     "ReconnectDelay", "AutoDetectIncomingConnection",
91     "BandwidthLimitStart", "BandwidthLimitEnd", "EnableThrottle","TimeDependentThrottle",
92     "MaxDownloadSpeedAlternate", "MaxUploadSpeedAlternate",
93     "MaxDownloadSpeedMain", "MaxUploadSpeedMain",
94     "SlotsAlternateLimiting", "SlotsPrimaryLimiting", "KeepFinishedFiles",
95     "ShowFreeSlotsDesc", "UseIP", "OverLapChunks", "CaseSensitiveFilelist",
96     "IpFilter", "TextColor", "UseLua", "AllowNatt", "IpTOSValue", "SegmentSize",
97     "BindIface", "MinimumSearchInterval", "EnableDynDNS", "AllowUploadOverMultiHubs",
98     "UseADLOnlyOnOwnList", "AllowSimUploads", "CheckTargetsPathsOnStart", "NmdcDebug",
99     "ShareSkipZeroByte", "RequireTLS", "LogSpy", "AppUnitBase",
100     "SENTRY",
101     // Int64
102     "TotalUpload", "TotalDownload",
103     "SENTRY",
104     // Floats
105     "SENTRY"
106 };
107 
SettingsManager()108 SettingsManager::SettingsManager()
109 {
110 
111     connectionSpeeds.push_back("0.005");
112     connectionSpeeds.push_back("0.01");
113     connectionSpeeds.push_back("0.02");
114     connectionSpeeds.push_back("0.05");
115     connectionSpeeds.push_back("0.1");
116     connectionSpeeds.push_back("0.2");
117     connectionSpeeds.push_back("0.5");
118     connectionSpeeds.push_back("1");
119     connectionSpeeds.push_back("2");
120     connectionSpeeds.push_back("5");
121     connectionSpeeds.push_back("10");
122     connectionSpeeds.push_back("20");
123     connectionSpeeds.push_back("50");
124     connectionSpeeds.push_back("100");
125     connectionSpeeds.push_back("1000");
126 
127     for(int i=0; i<SETTINGS_LAST; i++)
128         isSet[i] = false;
129 
130     for(int i=0; i<INT_LAST-INT_FIRST; i++) {
131         intDefaults[i] = 0;
132         intSettings[i] = 0;
133     }
134     for(int i=0; i<INT64_LAST-INT64_FIRST; i++) {
135         int64Defaults[i] = 0;
136         int64Settings[i] = 0;
137     }
138     for(int i=0; i<FLOAT_LAST-FLOAT_FIRST; i++) {
139         floatDefaults[i] = 0;
140         floatSettings[i] = 0;
141     }
142 
143     setDefault(DOWNLOAD_DIRECTORY, Util::getPath(Util::PATH_DOWNLOADS));
144     setDefault(TEMP_DOWNLOAD_DIRECTORY, Util::getPath(Util::PATH_DOWNLOADS) + "Incomplete" PATH_SEPARATOR_STR);
145     setDefault(SLOTS, 5);
146     setDefault(TCP_PORT, 3000);
147     setDefault(UDP_PORT, 3000);
148     setDefault(TLS_PORT, 3001);
149     setDefault(INCOMING_CONNECTIONS, INCOMING_DIRECT);
150     setDefault(OUTGOING_CONNECTIONS, OUTGOING_DIRECT);
151     setDefault(AUTO_FOLLOW, true);
152     setDefault(SHARE_HIDDEN, false);
153     setDefault(FILTER_MESSAGES, true);
154     setDefault(AUTO_SEARCH, true);
155     setDefault(AUTO_SEARCH_TIME, 2);
156     setDefault(REPORT_ALTERNATES, true);
157     setDefault(TIME_STAMPS, true);
158     setDefault(IGNORE_HUB_PMS, false);
159     setDefault(IGNORE_BOT_PMS, false);
160     setDefault(LIST_DUPES, true);
161     setDefault(BUFFER_SIZE, 64);
162     setDefault(HUBLIST_SERVERS,
163                "http://dchublist.com/hublist.xml.bz2;"
164                "http://dchublist.ru/hublist.xml.bz2"
165                );
166     setDefault(DOWNLOAD_SLOTS, 3);
167     setDefault(SKIPLIST_SHARE, "*.~*|*.*~");
168     setDefault(MAX_DOWNLOAD_SPEED, 0);
169     setDefault(LOG_DIRECTORY, Util::getPath(Util::PATH_USER_LOCAL) + "Logs" PATH_SEPARATOR_STR);
170     setDefault(LOG_UPLOADS, false);
171     setDefault(LOG_DOWNLOADS, false);
172     setDefault(LOG_FINISHED_DOWNLOADS, false);
173     setDefault(LOG_PRIVATE_CHAT, false);
174     setDefault(LOG_MAIN_CHAT, false);
175     setDefault(LOG_SPY, false);
176     setDefault(UPLOAD_SPEED, connectionSpeeds[11]);
177     setDefault(MIN_UPLOAD_SPEED, 0);
178     setDefault(LOG_FORMAT_POST_DOWNLOAD, "[%Y-%m-%d %H:%M:%S] %[target] downloaded from %[userNI] (%[userCID]), %[fileSI] (%[fileSIchunk]), %[speed], %[time], %[fileTR]");
179     setDefault(LOG_FORMAT_POST_FINISHED_DOWNLOAD, "%Y-%m-%d %H:%M: %[target] " + string(_("downloaded from")) + " %[userNI] (%[userCID]), %[fileSI] (%[fileSIsession]), %[speed], %[time], %[fileTR]");
180     setDefault(LOG_FORMAT_POST_UPLOAD,   "[%Y-%m-%d %H:%M:%S] %[source] uploaded to %[userNI] (%[userCID]), %[fileSI] (%[fileSIchunk]), %[speed], %[time], %[fileTR]");
181     setDefault(LOG_FORMAT_MAIN_CHAT,     "[%Y-%m-%d %H:%M:%S] %[message]");
182     setDefault(LOG_FORMAT_PRIVATE_CHAT,  "[%Y-%m-%d %H:%M:%S] %[message]");
183     setDefault(LOG_FORMAT_STATUS,        "[%Y-%m-%d %H:%M:%S] %[message]");
184     setDefault(LOG_FORMAT_SYSTEM,        "[%Y-%m-%d %H:%M:%S] %[message]");
185     setDefault(LOG_FORMAT_SPY,        "[%Y-%m-%d %H:%M:%S] %[message] (%[count])");
186     setDefault(LOG_FILE_MAIN_CHAT,    "CHAT/%B - %Y/%[hubNI] (%[hubURL]).log");
187     setDefault(LOG_FILE_STATUS,       "STATUS/%B - %Y/%[hubNI] (%[hubURL]).log");
188     setDefault(LOG_FILE_PRIVATE_CHAT, "PM/%B - %Y/%[userNI] (%[userCID]).log");
189     setDefault(LOG_FILE_UPLOAD,       "Uploads.log");
190     setDefault(LOG_FILE_DOWNLOAD,     "Downloads.log");
191     setDefault(LOG_FILE_FINISHED_DOWNLOAD, "Finished_downloads.log");
192     setDefault(LOG_FILE_SYSTEM,       "System.log");
193     setDefault(LOG_FILE_SPY,       "Spy.log");
194     setDefault(AUTO_AWAY, false);
195     setDefault(BIND_ADDRESS, "0.0.0.0");
196     setDefault(SOCKS_PORT, 1080);
197     setDefault(SOCKS_RESOLVE, 1);
198     setDefault(CONFIG_VERSION, "0.181");        // 0.181 is the last version missing configversion
199     setDefault(KEEP_LISTS, false);
200     setDefault(AUTO_KICK, false);
201     setDefault(COMPRESS_TRANSFERS, true);
202     setDefault(SFV_CHECK, true);
203     setDefault(DEFAULT_AWAY_MESSAGE, "I'm away. State your business and I might answer later if you're lucky.");
204     setDefault(TIME_STAMPS_FORMAT, "%H:%M");
205     setDefault(MAX_COMPRESSION, 6);
206     setDefault(NO_AWAYMSG_TO_BOTS, true);
207     setDefault(SKIP_ZERO_BYTE, false);
208     setDefault(ADLS_BREAK_ON_FIRST, false);
209     setDefault(HUB_USER_COMMANDS, true);
210     setDefault(AUTO_SEARCH_AUTO_MATCH, false);
211     setDefault(LOG_FILELIST_TRANSFERS, false);
212     setDefault(LOG_SYSTEM, false);
213     setDefault(SEND_UNKNOWN_COMMANDS, true);
214     setDefault(MAX_HASH_SPEED, 0);
215     setDefault(GET_USER_COUNTRY, true);
216     setDefault(LOG_STATUS_MESSAGES, false);
217     setDefault(ADD_FINISHED_INSTANTLY, false);
218     setDefault(DONT_DL_ALREADY_SHARED, false);
219     setDefault(SHOW_LAST_LINES_LOG, 0);
220     setDefault(ADC_DEBUG, false);
221     setDefault(SEARCH_HISTORY, 10);
222     setDefault(SET_MINISLOT_SIZE, 64);
223     setDefault(MAX_FILELIST_SIZE, 4*1024);
224     setDefault(PRIO_HIGHEST_SIZE, 64);
225     setDefault(PRIO_HIGH_SIZE, 0);
226     setDefault(PRIO_NORMAL_SIZE, 0);
227     setDefault(PRIO_LOW_SIZE, 0);
228     setDefault(PRIO_LOWEST, false);
229     setDefault(AUTODROP_SPEED, 1024);
230     setDefault(AUTODROP_INTERVAL, 10);
231     setDefault(AUTODROP_ELAPSED, 15);
232     setDefault(AUTODROP_INACTIVITY, 10);
233     setDefault(AUTODROP_MINSOURCES, 2);
234     setDefault(AUTODROP_FILESIZE, 0);
235     setDefault(AUTODROP_ALL, true);
236     setDefault(AUTODROP_FILELISTS, true);
237     setDefault(AUTODROP_DISCONNECT, true);
238     setDefault(NO_IP_OVERRIDE, false);
239     setDefault(NO_USE_TEMP_DIR, false);
240     setDefault(SHARE_TEMP_FILES, false);
241     setDefault(SEARCH_ONLY_FREE_SLOTS, false);
242     setDefault(SEARCH_FILTER_SHARED, true);
243     setDefault(LAST_SEARCH_TYPE, 0);
244     setDefault(SOCKET_IN_BUFFER, 64*1024);
245     setDefault(SOCKET_OUT_BUFFER, 64*1024);
246     setDefault(TLS_TRUSTED_CERTIFICATES_PATH, Util::getPath(Util::PATH_USER_CONFIG) + "Certificates" PATH_SEPARATOR_STR);
247     setDefault(TLS_PRIVATE_KEY_FILE, Util::getPath(Util::PATH_USER_CONFIG) + "Certificates" PATH_SEPARATOR_STR "client.key");
248     setDefault(TLS_CERTIFICATE_FILE, Util::getPath(Util::PATH_USER_CONFIG) + "Certificates" PATH_SEPARATOR_STR "client.crt");
249     setDefault(AUTO_REFRESH_TIME, 60);  // minutes
250     setDefault(HASHING_START_DELAY, 60); // seconds
251     setDefault(USE_TLS, true);
252     setDefault(AUTO_SEARCH_LIMIT, 5);
253     setDefault(AUTO_KICK_NO_FAVS, false);
254     setDefault(PROMPT_PASSWORD, false);
255     setDefault(DONT_DL_ALREADY_QUEUED, false);
256     setDefault(MAX_COMMAND_LENGTH, 16*1024*1024);
257     setDefault(ALLOW_UNTRUSTED_HUBS, true);
258     setDefault(ALLOW_UNTRUSTED_CLIENTS, true);
259     setDefault(FAST_HASH, true);
260     setDefault(SEGMENTED_DL, true);
261     setDefault(FOLLOW_LINKS, false);
262     setDefault(SEND_BLOOM, true);
263     setDefault(CORAL, true);
264     setDefault(FINISHED_DL_ONLY_FULL, true);
265     setDefault(SEARCH_MERGE, true);
266     setDefault(HASH_BUFFER_SIZE_MB, 8);
267     setDefault(HASH_BUFFER_POPULATE, true);
268     setDefault(HASH_BUFFER_NORESERVE, true);
269     setDefault(HASH_BUFFER_PRIVATE, true);
270     setDefault(RECONNECT_DELAY, 15);
271     setDefault(DHT_PORT, 6250);
272     setDefault(USE_DHT, false);
273     setDefault(SEARCH_PASSIVE, false);
274     setDefault(AUTO_DETECT_CONNECTION, false);
275     setDefault(MAX_UPLOAD_SPEED_MAIN, 0);
276     setDefault(MAX_DOWNLOAD_SPEED_MAIN, 0);
277     setDefault(TIME_DEPENDENT_THROTTLE, false);
278     setDefault(THROTTLE_ENABLE, false);
279     setDefault(MAX_DOWNLOAD_SPEED_ALTERNATE, 0);
280     setDefault(MAX_UPLOAD_SPEED_ALTERNATE, 0);
281     setDefault(BANDWIDTH_LIMIT_START, 1);
282     setDefault(BANDWIDTH_LIMIT_END, 1);
283     setDefault(SLOTS_ALTERNATE_LIMITING, 1);
284     setDefault(SLOTS_PRIMARY, 3);
285     setDefault(KEEP_FINISHED_FILES, false);
286     setDefault(USE_IP, true);
287     setDefault(SHOW_FREE_SLOTS_DESC, false);
288     setDefault(OVERLAP_CHUNKS, true);
289     setDefault(CASESENSITIVE_FILELIST, false);
290     setDefault(IPFILTER,false);
291     setDefault(USE_LUA,false);
292     setDefault(ALLOW_NATT, true);
293     setDefault(IP_TOS_VALUE, -1);
294     setDefault(SEGMENT_SIZE, 0);
295     setDefault(BIND_IFACE, false);
296     setDefault(BIND_IFACE_NAME, "");
297     setDefault(MINIMUM_SEARCH_INTERVAL, 60);
298     setDefault(DYNDNS_SERVER, "http://checkip.dyndns.org/index.html");
299     setDefault(DYNDNS_ENABLE, false);
300     setDefault(ALLOW_UPLOAD_MULTI_HUB, true);
301     setDefault(USE_ADL_ONLY_OWN_LIST, false);
302     setDefault(ALLOW_SIM_UPLOADS, true);
303     setDefault(CHECK_TARGETS_PATHS_ON_START, false);
304     setDefault(SHARE_SKIP_ZERO_BYTE, false);
305     setDefault(APP_UNIT_BASE, 0);
306     setSearchTypeDefaults();
307 }
308 
load(string const & aFileName)309 void SettingsManager::load(string const& aFileName)
310 {
311     try {
312         SimpleXML xml;
313 
314         xml.fromXML(File(aFileName, File::READ, File::OPEN).read());
315 
316         xml.resetCurrentChild();
317 
318         xml.stepIn();
319 
320         if(xml.findChild("Settings"))
321         {
322             xml.stepIn();
323 
324             int i;
325 
326             for(i=STR_FIRST; i<STR_LAST; i++)
327             {
328                 const string& attr = settingTags[i];
329                 dcassert(attr.find("SENTRY") == string::npos);
330 
331                 if(xml.findChild(attr))
332                     set(StrSetting(i), xml.getChildData());
333                 xml.resetCurrentChild();
334             }
335             for(i=INT_FIRST; i<INT_LAST; i++)
336             {
337                 const string& attr = settingTags[i];
338                 dcassert(attr.find("SENTRY") == string::npos);
339 
340                 if(xml.findChild(attr))
341                     set(IntSetting(i), Util::toInt(xml.getChildData()));
342                 xml.resetCurrentChild();
343             }
344             for(i=FLOAT_FIRST; i<FLOAT_LAST; i++)
345             {
346                 const string& attr = settingTags[i];
347                 dcassert(attr.find("SENTRY") == string::npos);
348 
349                 if(xml.findChild(attr))
350                     set(FloatSetting(i), Util::toInt(xml.getChildData()) / 1000.);
351                 xml.resetCurrentChild();
352             }
353             for(i=INT64_FIRST; i<INT64_LAST; i++)
354             {
355                 const string& attr = settingTags[i];
356                 dcassert(attr.find("SENTRY") == string::npos);
357 
358                 if(xml.findChild(attr))
359                     set(Int64Setting(i), Util::toInt64(xml.getChildData()));
360                 xml.resetCurrentChild();
361             }
362 
363             xml.stepOut();
364         }
365 
366         xml.resetCurrentChild();
367         if(xml.findChild("SearchTypes")) {
368             try {
369                 searchTypes.clear();
370                 xml.stepIn();
371                 while(xml.findChild("SearchType")) {
372                     const string& extensions = xml.getChildData();
373                     if(extensions.empty()) {
374                         continue;
375                     }
376                     const string& name = xml.getChildAttrib("Id");
377                     if(name.empty()) {
378                         continue;
379                     }
380                     searchTypes[name] = StringTokenizer<string>(extensions, ';').getTokens();
381                 }
382                 xml.stepOut();
383             } catch(const SimpleXMLException&) {
384                 setSearchTypeDefaults();
385             }
386         }
387 
388         if(SETTING(PRIVATE_ID).length() != 39 || CID(SETTING(PRIVATE_ID)).isZero()) {
389             set(PRIVATE_ID, CID::generate().toBase32());
390         }
391 
392         double v = Util::toDouble(SETTING(CONFIG_VERSION));
393         // if(v < 0.x) { // Fix old settings here }
394 
395         if(v <= 0.674) {
396 
397             // Formats changed, might as well remove these...
398             unset(LOG_FORMAT_POST_DOWNLOAD);
399             unset(LOG_FORMAT_POST_UPLOAD);
400             unset(LOG_FORMAT_MAIN_CHAT);
401             unset(LOG_FORMAT_PRIVATE_CHAT);
402             unset(LOG_FORMAT_STATUS);
403             unset(LOG_FORMAT_SYSTEM);
404             unset(LOG_FILE_MAIN_CHAT);
405             unset(LOG_FILE_STATUS);
406             unset(LOG_FILE_PRIVATE_CHAT);
407             unset(LOG_FILE_UPLOAD);
408             unset(LOG_FILE_DOWNLOAD);
409             unset(LOG_FILE_SYSTEM);
410         }
411 
412         if(SETTING(SET_MINISLOT_SIZE) < 64)
413             set(SET_MINISLOT_SIZE, 64);
414         if(SETTING(AUTODROP_INTERVAL) < 1)
415             set(AUTODROP_INTERVAL, 1);
416         if(SETTING(AUTODROP_ELAPSED) < 1)
417             set(AUTODROP_ELAPSED, 1);
418         if(SETTING(AUTO_SEARCH_LIMIT) > 5)
419             set(AUTO_SEARCH_LIMIT, 5);
420         else if(SETTING(AUTO_SEARCH_LIMIT) < 1)
421             set(AUTO_SEARCH_LIMIT, 1);
422 
423 #ifdef _DEBUG
424         set(PRIVATE_ID, CID::generate().toBase32());
425 #endif
426         setDefault(UDP_PORT, SETTING(TCP_PORT));
427 
428         File::ensureDirectory(SETTING(TLS_TRUSTED_CERTIFICATES_PATH));
429 
430         fire(SettingsManagerListener::Load(), xml);
431 
432         xml.stepOut();
433 
434     } catch(const Exception&) {
435         if(CID(SETTING(PRIVATE_ID)).isZero())
436             set(PRIVATE_ID, CID::generate().toBase32());
437     }
438     if (SETTING(DHT_KEY).length() != 39 || CID(SETTING(DHT_KEY)).isZero())
439         set(DHT_KEY, CID::generate().toBase32());
440 }
441 
save(string const & aFileName)442 void SettingsManager::save(string const& aFileName) {
443 
444     SimpleXML xml;
445     xml.addTag("DCPlusPlus");
446     xml.stepIn();
447     xml.addTag("Settings");
448     xml.stepIn();
449 
450     int i;
451     string type("type"), curType("string");
452 
453     for(i=STR_FIRST; i<STR_LAST; i++)
454     {
455         if(i == CONFIG_VERSION) {
456             xml.addTag(settingTags[i], VERSIONSTRING);
457             xml.addChildAttrib(type, curType);
458         } else if(isSet[i]) {
459             xml.addTag(settingTags[i], get(StrSetting(i), false));
460             xml.addChildAttrib(type, curType);
461         }
462     }
463 
464     curType = "int";
465     for(i=INT_FIRST; i<INT_LAST; i++)
466     {
467         if(isSet[i]) {
468             xml.addTag(settingTags[i], get(IntSetting(i), false));
469             xml.addChildAttrib(type, curType);
470         }
471     }
472     for(i=FLOAT_FIRST; i<FLOAT_LAST; i++)
473     {
474         if(isSet[i]) {
475             xml.addTag(settingTags[i], static_cast<int>(get(FloatSetting(i), false) * 1000.));
476             xml.addChildAttrib(type, curType);
477         }
478     }
479     curType = "int64";
480     for(i=INT64_FIRST; i<INT64_LAST; i++)
481     {
482         if(isSet[i])
483         {
484             xml.addTag(settingTags[i], get(Int64Setting(i), false));
485             xml.addChildAttrib(type, curType);
486         }
487     }
488     xml.stepOut();
489 
490     xml.addTag("SearchTypes");
491     xml.stepIn();
492     for(SearchTypesIterC i = searchTypes.begin(); i != searchTypes.end(); ++i) {
493         xml.addTag("SearchType", Util::toString(";", i->second));
494         xml.addChildAttrib("Id", i->first);
495     }
496     xml.stepOut();
497 
498     fire(SettingsManagerListener::Save(), xml);
499 
500     try {
501         File out(aFileName + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
502         BufferedOutputStream<false> f(&out);
503         f.write(SimpleXML::utf8Header);
504         xml.toXML(&f);
505         f.flush();
506         out.close();
507         File::deleteFile(aFileName);
508         File::renameFile(aFileName + ".tmp", aFileName);
509     } catch(const FileException&) {
510         // ...
511     }
512 }
513 
validateSearchTypeName(const string & name) const514 void SettingsManager::validateSearchTypeName(const string& name) const {
515     if(name.empty() || (name.size() == 1 && name[0] >= '1' && name[0] <= '6')) {
516         throw SearchTypeException(_("Invalid search type name"));
517     }
518     for(int type = SearchManager::TYPE_ANY; type != SearchManager::TYPE_LAST; ++type) {
519         if(SearchManager::getTypeStr(type) == name) {
520             throw SearchTypeException(_("This search type already exists"));
521         }
522     }
523 }
524 
setSearchTypeDefaults()525 void SettingsManager::setSearchTypeDefaults() {
526     searchTypes.clear();
527 
528     // for conveniency, the default search exts will be the same as the ones defined by SEGA.
529     const vector<StringList>& searchExts = AdcHub::getSearchExts();
530     for(size_t i = 0, n = searchExts.size(); i < n; ++i)
531         searchTypes[string(1, '1' + i)] = searchExts[i];
532 
533     fire(SettingsManagerListener::SearchTypesChanged());
534 }
535 
addSearchType(const string & name,const StringList & extensions,bool validated)536 void SettingsManager::addSearchType(const string& name, const StringList& extensions, bool validated) {
537     if(!validated) {
538         validateSearchTypeName(name);
539     }
540 
541     if(searchTypes.find(name) != searchTypes.end()) {
542         throw SearchTypeException(_("This search type already exists"));
543     }
544 
545     searchTypes[name] = extensions;
546     fire(SettingsManagerListener::SearchTypesChanged());
547 }
548 
delSearchType(const string & name)549 void SettingsManager::delSearchType(const string& name) {
550     validateSearchTypeName(name);
551     searchTypes.erase(name);
552     fire(SettingsManagerListener::SearchTypesChanged());
553 }
554 
renameSearchType(const string & oldName,const string & newName)555 void SettingsManager::renameSearchType(const string& oldName, const string& newName) {
556     validateSearchTypeName(newName);
557     StringList exts = getSearchType(oldName)->second;
558     addSearchType(newName, exts, true);
559     searchTypes.erase(oldName);
560 }
561 
modSearchType(const string & name,const StringList & extensions)562 void SettingsManager::modSearchType(const string& name, const StringList& extensions) {
563     getSearchType(name)->second = extensions;
564     fire(SettingsManagerListener::SearchTypesChanged());
565 }
566 
getExtensions(const string & name)567 const StringList& SettingsManager::getExtensions(const string& name) {
568     return getSearchType(name)->second;
569 }
570 
getSearchType(const string & name)571 SettingsManager::SearchTypesIter SettingsManager::getSearchType(const string& name) {
572     SearchTypesIter ret = searchTypes.find(name);
573     if(ret == searchTypes.end()) {
574         throw SearchTypeException(_("No such search type"));
575     }
576     return ret;
577 }
578 
getType(const char * name,int & n,int & type) const579 bool SettingsManager::getType(const char* name, int& n, int& type) const {
580     for(n = 0; n < INT64_LAST; n++) {
581         if (strcmp(settingTags[n].c_str(), name) == 0) {
582             if (n < STR_LAST) {
583                 type = TYPE_STRING;
584                 return true;
585             } else if (n < INT_LAST) {
586                 type= TYPE_INT;
587                 return true;
588             } else {
589                 type = TYPE_INT64;
590                 return true;
591             }
592         }
593     }
594     return false;
595 }
596 
parseCoreCmd(const std::string & cmd)597 const std::string SettingsManager::parseCoreCmd(const std::string& cmd) {
598     StringTokenizer<string> sl(cmd, ' ');
599     if (sl.getTokens().size() == 1) {
600         string ret;
601         bool b = parseCoreCmd(ret, sl.getTokens().at(0), Util::emptyString);
602         return (!b ? _("Error: setting not found!") : _("Core setting ") + sl.getTokens().at(0) + ": " + ret);
603     } else if (sl.getTokens().size() >= 2) {
604         string tmp = cmd.substr(sl.getTokens().at(0).size()+1);
605         string ret;
606         bool b = parseCoreCmd(ret, sl.getTokens().at(0), tmp);
607         return (!b ? _("Error: setting not found!") : _("Change core setting ") + sl.getTokens().at(0) + _(" to ") + tmp);
608     }
609     return Util::emptyString;
610 }
611 
parseCoreCmd(string & ret,const std::string & key,const string & value)612 bool SettingsManager::parseCoreCmd(string& ret, const std::string& key, const string& value) {
613     if (key.empty()) {
614         return false;
615     }
616 
617     int n,type;
618     getType(key.c_str(),n,type);
619     if (type == SettingsManager::TYPE_INT) {
620         if (!value.empty()) {
621             int i = atoi(value.c_str());
622             set((SettingsManager::IntSetting)n,i);
623         } else if (value.empty())
624             ret = Util::toString(get((SettingsManager::IntSetting)n,false));
625     }
626     else if (type == SettingsManager::TYPE_STRING) {
627         if (!value.empty())
628             set((SettingsManager::StrSetting)n, value);
629         else if (value.empty())
630             ret = get((SettingsManager::StrSetting)n,false);
631     } else
632         return false;
633     return true;
634 }
635 
636 } // namespace dcpp
637