1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail;
20
21 import davmail.ui.tray.DavGatewayTray;
22 import org.apache.log4j.ConsoleAppender;
23 import org.apache.log4j.FileAppender;
24 import org.apache.log4j.Level;
25 import org.apache.log4j.Logger;
26 import org.apache.log4j.PatternLayout;
27 import org.apache.log4j.RollingFileAppender;
28
29 import java.io.BufferedReader;
30 import java.io.BufferedWriter;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.io.OutputStreamWriter;
38 import java.nio.charset.StandardCharsets;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.nio.file.attribute.FileAttribute;
43 import java.nio.file.attribute.PosixFilePermissions;
44 import java.util.ArrayList;
45 import java.util.Enumeration;
46 import java.util.Iterator;
47 import java.util.Map;
48 import java.util.Properties;
49 import java.util.TreeSet;
50
51 import static org.apache.http.util.TextUtils.isEmpty;
52
53
54
55
56
57
58 public final class Settings {
59
60 private static final Logger LOGGER = Logger.getLogger(Settings.class);
61
62 public static final String OUTLOOK_URL = "https://outlook.office365.com";
63 public static final String O365_URL = OUTLOOK_URL + "/EWS/Exchange.asmx";
64
65 public static final String GRAPH_URL = "https://graph.microsoft.com";
66
67 public static final String O365_LOGIN_URL = "https://login.microsoftonline.com";
68
69
70 public static final String O365_EWS = "O365EWS";
71 public static final String O365_GRAPH = "O365Graph";
72 public static final String EXCHANGE_EWS = "ExchangeEWS";
73 public static final String EXCHANGE_WEBDAV = "ExchangeWebDav";
74
75
76 public static final String O365_INTERACTIVE = "O365Interactive";
77 public static final String O365_MANUAL = "O365Manual";
78 public static final String O365_DEVICECODE = "O365DeviceCode";
79 public static final String O365_TRANSPARENT = "O365Transparent";
80
81
82 public static final String O365_MODERN = "O365Modern";
83 public static final String O365 = "O365";
84 public static final String WEBDAV = "WebDav";
85 public static final String EWS = "EWS";
86 public static final String AUTO = "Auto";
87
88 public static final String EDGE_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 Edg/90.0.818.49";
89
90 private Settings() {
91 }
92
93 private static final Properties SETTINGS_PROPERTIES = new Properties() {
94 @Override
95 public synchronized Enumeration<Object> keys() {
96 Enumeration<Object> keysEnumeration = super.keys();
97 TreeSet<String> sortedKeySet = new TreeSet<>();
98 while (keysEnumeration.hasMoreElements()) {
99 sortedKeySet.add((String) keysEnumeration.nextElement());
100 }
101 final Iterator<String> sortedKeysIterator = sortedKeySet.iterator();
102 return new Enumeration<Object>() {
103
104 public boolean hasMoreElements() {
105 return sortedKeysIterator.hasNext();
106 }
107
108 public Object nextElement() {
109 return sortedKeysIterator.next();
110 }
111 };
112 }
113
114 };
115 private static String configFilePath;
116 private static boolean isFirstStart;
117
118
119
120
121
122
123 public static synchronized void setConfigFilePath(String path) {
124 configFilePath = path;
125 }
126
127
128
129
130
131
132 public static synchronized boolean isFirstStart() {
133 return isFirstStart;
134 }
135
136
137
138
139
140
141
142 public static synchronized void load(InputStream inputStream) throws IOException {
143 SETTINGS_PROPERTIES.load(inputStream);
144 updateLoggingConfig();
145 }
146
147
148
149
150 public static synchronized void load() {
151 try {
152 if (configFilePath == null) {
153
154 configFilePath = System.getProperty("user.home") + "/.davmail.properties";
155 if (!new File(configFilePath).exists()) {
156
157 configFilePath = getXDGConfigFilePath();
158 }
159 }
160 File configFile = new File(configFilePath);
161 if (configFile.exists()) {
162 try (FileInputStream fileInputStream = new FileInputStream(configFile)) {
163 load(fileInputStream);
164 }
165 } else {
166 isFirstStart = true;
167
168
169 setDefaultSettings();
170 save();
171 }
172 } catch (IOException e) {
173 DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_LOAD_SETTINGS"), e);
174 }
175 updateLoggingConfig();
176 }
177
178
179
180
181
182
183 private static String getXDGConfigFilePath() {
184 String defaultConfigPath = System.getProperty("user.home")+".davmail.properties";
185 String xdgConfigDirectory = System.getenv("XDG_CONFIG_HOME");
186 if (xdgConfigDirectory == null) {
187 xdgConfigDirectory = System.getProperty("user.home")+"/.config";
188 }
189 File xdgConfigDirectoryFile = new File(xdgConfigDirectory);
190 if (!xdgConfigDirectoryFile.exists()) {
191 if (!xdgConfigDirectoryFile.mkdirs()) {
192 return defaultConfigPath;
193 }
194 }
195 File xdgConfigDavMailDirectoryFile = new File(xdgConfigDirectory+"/davmail");
196 if (!xdgConfigDavMailDirectoryFile.exists()) {
197 if (!xdgConfigDavMailDirectoryFile.mkdirs()) {
198 return defaultConfigPath;
199 }
200 }
201 return xdgConfigDirectory+"/davmail/davmail.properties";
202 }
203
204
205
206
207
208 public static void setDefaultSettings() {
209 SETTINGS_PROPERTIES.put("davmail.mode", O365_EWS);
210 SETTINGS_PROPERTIES.put("davmail.authentication", O365_INTERACTIVE);
211 SETTINGS_PROPERTIES.put("davmail.url", getO365Url());
212 SETTINGS_PROPERTIES.put("davmail.popPort", "1110");
213 SETTINGS_PROPERTIES.put("davmail.imapPort", "1143");
214 SETTINGS_PROPERTIES.put("davmail.smtpPort", "1025");
215 SETTINGS_PROPERTIES.put("davmail.caldavPort", "1080");
216 SETTINGS_PROPERTIES.put("davmail.ldapPort", "1389");
217 SETTINGS_PROPERTIES.put("davmail.clientSoTimeout", "");
218 SETTINGS_PROPERTIES.put("davmail.keepDelay", "30");
219 SETTINGS_PROPERTIES.put("davmail.sentKeepDelay", "0");
220 SETTINGS_PROPERTIES.put("davmail.caldavPastDelay", "0");
221 SETTINGS_PROPERTIES.put("davmail.caldavAutoSchedule", Boolean.TRUE.toString());
222 SETTINGS_PROPERTIES.put("davmail.imapIdleDelay", "");
223 SETTINGS_PROPERTIES.put("davmail.folderSizeLimit", "");
224 SETTINGS_PROPERTIES.put("davmail.enableKeepAlive", Boolean.FALSE.toString());
225 SETTINGS_PROPERTIES.put("davmail.allowRemote", Boolean.FALSE.toString());
226 SETTINGS_PROPERTIES.put("davmail.bindAddress", "");
227 SETTINGS_PROPERTIES.put("davmail.useSystemProxies", Boolean.FALSE.toString());
228 SETTINGS_PROPERTIES.put("davmail.enableProxy", Boolean.FALSE.toString());
229 SETTINGS_PROPERTIES.put("davmail.enableKerberos", "false");
230 SETTINGS_PROPERTIES.put("davmail.disableUpdateCheck", "false");
231 SETTINGS_PROPERTIES.put("davmail.proxyHost", "");
232 SETTINGS_PROPERTIES.put("davmail.proxyPort", "");
233 SETTINGS_PROPERTIES.put("davmail.proxyUser", "");
234 SETTINGS_PROPERTIES.put("davmail.proxyPassword", "");
235 SETTINGS_PROPERTIES.put("davmail.noProxyFor", "");
236 SETTINGS_PROPERTIES.put("davmail.server", Boolean.FALSE.toString());
237 SETTINGS_PROPERTIES.put("davmail.server.certificate.hash", "");
238 SETTINGS_PROPERTIES.put("davmail.caldavAlarmSound", "");
239 SETTINGS_PROPERTIES.put("davmail.carddavReadPhoto", Boolean.TRUE.toString());
240 SETTINGS_PROPERTIES.put("davmail.forceActiveSyncUpdate", Boolean.FALSE.toString());
241 if (isLinux()) {
242 SETTINGS_PROPERTIES.put("davmail.enableTray", Boolean.FALSE.toString());
243 } else {
244 SETTINGS_PROPERTIES.put("davmail.enableTray", Boolean.TRUE.toString());
245 }
246 SETTINGS_PROPERTIES.put("davmail.showStartupBanner", Boolean.TRUE.toString());
247 SETTINGS_PROPERTIES.put("davmail.disableGuiNotifications", Boolean.FALSE.toString());
248 SETTINGS_PROPERTIES.put("davmail.disableTrayActivitySwitch", Boolean.FALSE.toString());
249 SETTINGS_PROPERTIES.put("davmail.imapAutoExpunge", Boolean.TRUE.toString());
250 SETTINGS_PROPERTIES.put("davmail.imapAlwaysApproxMsgSize", Boolean.FALSE.toString());
251 SETTINGS_PROPERTIES.put("davmail.popMarkReadOnRetr", Boolean.FALSE.toString());
252 SETTINGS_PROPERTIES.put("davmail.smtpSaveInSent", Boolean.TRUE.toString());
253 SETTINGS_PROPERTIES.put("davmail.ssl.keystoreType", "");
254 SETTINGS_PROPERTIES.put("davmail.ssl.keystoreFile", "");
255 SETTINGS_PROPERTIES.put("davmail.ssl.keystorePass", "");
256 SETTINGS_PROPERTIES.put("davmail.ssl.keyPass", "");
257 if (isWindows()) {
258
259 SETTINGS_PROPERTIES.put("davmail.ssl.clientKeystoreType", "MSCAPI");
260 } else {
261 SETTINGS_PROPERTIES.put("davmail.ssl.clientKeystoreType", "");
262 }
263 SETTINGS_PROPERTIES.put("davmail.ssl.clientKeystoreFile", "");
264 SETTINGS_PROPERTIES.put("davmail.ssl.clientKeystorePass", "");
265 SETTINGS_PROPERTIES.put("davmail.ssl.pkcs11Library", "");
266 SETTINGS_PROPERTIES.put("davmail.ssl.pkcs11Config", "");
267 SETTINGS_PROPERTIES.put("davmail.ssl.nosecurepop", Boolean.FALSE.toString());
268 SETTINGS_PROPERTIES.put("davmail.ssl.nosecureimap", Boolean.FALSE.toString());
269 SETTINGS_PROPERTIES.put("davmail.ssl.nosecuresmtp", Boolean.FALSE.toString());
270 SETTINGS_PROPERTIES.put("davmail.ssl.nosecurecaldav", Boolean.FALSE.toString());
271 SETTINGS_PROPERTIES.put("davmail.ssl.nosecureldap", Boolean.FALSE.toString());
272
273
274 SETTINGS_PROPERTIES.put("log4j.rootLogger", Level.WARN.toString());
275 SETTINGS_PROPERTIES.put("log4j.logger.davmail", Level.DEBUG.toString());
276 SETTINGS_PROPERTIES.put("log4j.logger.httpclient.wire", Level.WARN.toString());
277 SETTINGS_PROPERTIES.put("log4j.logger.httpclient", Level.WARN.toString());
278 String logFilePath = "";
279 if (isFlatpak()) {
280 logFilePath = System.getenv("XDG_DATA_HOME")+"/davmail.log";
281 }
282 SETTINGS_PROPERTIES.put("davmail.logFilePath", logFilePath);
283 }
284
285
286
287
288
289
290 public static String getLogFilePath() {
291 String logFilePath = Settings.getProperty("davmail.logFilePath");
292
293 if ((logFilePath == null || logFilePath.isEmpty())) {
294 if (Settings.getBooleanProperty("davmail.server")) {
295 logFilePath = "davmail.log";
296 } else if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) {
297
298 logFilePath = System.getProperty("user.home") + "/Library/Logs/DavMail/davmail.log";
299 } else {
300
301 logFilePath = System.getProperty("user.home") + "/davmail.log";
302 }
303 } else {
304 File logFile = new File(logFilePath);
305 if (logFile.isDirectory()) {
306 logFilePath += "/davmail.log";
307 }
308 }
309 return logFilePath;
310 }
311
312
313
314
315
316
317 public static String getLogFileDirectory() {
318 String logFilePath = getLogFilePath();
319 if (logFilePath == null || logFilePath.isEmpty()) {
320 return ".";
321 }
322 int lastSlashIndex = logFilePath.lastIndexOf('/');
323 if (lastSlashIndex == -1) {
324 lastSlashIndex = logFilePath.lastIndexOf('\\');
325 }
326 if (lastSlashIndex >= 0) {
327 return logFilePath.substring(0, lastSlashIndex);
328 } else {
329 return ".";
330 }
331 }
332
333
334
335
336 public static void updateLoggingConfig() {
337 String logFilePath = getLogFilePath();
338
339 try {
340 if (!isDocker()) {
341 if (logFilePath != null && !logFilePath.isEmpty()) {
342 File logFile = new File(logFilePath);
343
344 File logFileDir = logFile.getParentFile();
345 if (logFileDir != null && !logFileDir.exists() && (!logFileDir.mkdirs())) {
346 DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_CREATE_LOG_FILE_DIR"));
347 throw new IOException();
348
349 }
350 } else {
351 logFilePath = "davmail.log";
352 }
353
354 synchronized (Logger.getRootLogger()) {
355
356 FileAppender fileAppender = (FileAppender) Logger.getRootLogger().getAppender("FileAppender");
357 if (fileAppender == null) {
358 String logFileSize = Settings.getProperty("davmail.logFileSize");
359 if (logFileSize == null || logFileSize.isEmpty()) {
360 logFileSize = "1MB";
361 }
362
363 if ("0".equals(logFileSize)) {
364 fileAppender = new FileAppender();
365 } else {
366 fileAppender = new RollingFileAppender();
367 ((RollingFileAppender) fileAppender).setMaxBackupIndex(2);
368 ((RollingFileAppender) fileAppender).setMaxFileSize(logFileSize);
369 }
370 fileAppender.setName("FileAppender");
371 fileAppender.setEncoding("UTF-8");
372 fileAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p [%t] %c %x - %m%n"));
373 }
374 fileAppender.setFile(logFilePath, true, false, 8192);
375 Logger.getRootLogger().addAppender(fileAppender);
376 }
377 }
378
379
380 ConsoleAppender consoleAppender = (ConsoleAppender) Logger.getRootLogger().getAppender("ConsoleAppender");
381 if (consoleAppender != null) {
382 consoleAppender.setThreshold(Level.ALL);
383 }
384
385 } catch (IOException e) {
386 DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_SET_LOG_FILE_PATH"));
387 }
388
389
390 Settings.setLoggingLevel("rootLogger", Settings.getLoggingLevel("rootLogger"));
391 Settings.setLoggingLevel("davmail", Settings.getLoggingLevel("davmail"));
392
393 Settings.setLoggingLevel("org.apache.http.wire", Settings.getLoggingLevel("httpclient.wire"));
394 Settings.setLoggingLevel("org.apache.http.conn.ssl", Settings.getLoggingLevel("httpclient.wire"));
395 Settings.setLoggingLevel("org.apache.http", Settings.getLoggingLevel("httpclient"));
396 }
397
398
399
400
401 public static synchronized void save() {
402
403 if (configFilePath != null) {
404
405 Properties properties = new Properties();
406 properties.putAll(SETTINGS_PROPERTIES);
407
408 ArrayList<String> lines = new ArrayList<>();
409
410
411 Path path = Paths.get(configFilePath);
412 if (!Files.exists(path) && isUnix()) {
413 FileAttribute<?> permissions = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------"));
414 try {
415 Files.createFile(path, permissions);
416 } catch (IOException e) {
417 LOGGER.error(e.getMessage());
418 }
419 }
420
421 readLines(lines, properties);
422
423 try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.ISO_8859_1))) {
424 for (String value : lines) {
425 writer.write(value);
426 writer.newLine();
427 }
428
429
430 Enumeration<?> propertyEnumeration = properties.propertyNames();
431 while (propertyEnumeration.hasMoreElements()) {
432 String propertyName = (String) propertyEnumeration.nextElement();
433 writer.write(propertyName + "=" + escapeValue(properties.getProperty(propertyName)));
434 writer.newLine();
435 }
436 } catch (IOException e) {
437 DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_STORE_SETTINGS"), e);
438 }
439 }
440 updateLoggingConfig();
441 }
442
443 private static void readLines(ArrayList<String> lines, Properties properties) {
444 try {
445 File configFile = new File(configFilePath);
446 if (configFile.exists()) {
447 try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(configFile.toPath()), StandardCharsets.ISO_8859_1))) {
448 String line;
449 while ((line = reader.readLine()) != null) {
450 lines.add(convertLine(line, properties));
451 }
452 }
453 }
454 } catch (IOException e) {
455 DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_LOAD_SETTINGS"), e);
456 }
457 }
458
459
460
461
462
463
464
465
466
467 private static String convertLine(String line, Properties properties) {
468 int hashIndex = line.indexOf('#');
469 int equalsIndex = line.indexOf('=');
470
471
472 if (equalsIndex >= 0 && (hashIndex < 0 || hashIndex >= equalsIndex)) {
473 String key = line.substring(0, equalsIndex);
474 String value = properties.getProperty(key);
475 if (value != null) {
476
477 line = key + "=" + escapeValue(value);
478
479 properties.remove(key);
480 }
481 }
482 return line;
483 }
484
485
486
487
488
489
490
491 private static String escapeValue(String value) {
492 StringBuilder buffer = new StringBuilder();
493 for (char c : value.toCharArray()) {
494 if (c == '\\') {
495 buffer.append('\\');
496 }
497 buffer.append(c);
498 }
499 return buffer.toString();
500 }
501
502
503
504
505
506
507
508
509 public static synchronized String getProperty(String property) {
510 String value = SETTINGS_PROPERTIES.getProperty(property);
511
512 if (value != null && value.isEmpty()) {
513 value = null;
514 }
515 return value;
516 }
517
518
519
520
521
522
523
524
525 public static synchronized String getProperty(String property, String defaultValue) {
526 String value = getProperty(property);
527 if (value == null) {
528 value = defaultValue;
529 }
530 return value;
531 }
532
533
534
535
536
537
538
539
540 public static synchronized char[] getCharArrayProperty(String property) {
541 String propertyValue = Settings.getProperty(property);
542 char[] value = null;
543 if (propertyValue != null) {
544 value = propertyValue.toCharArray();
545 }
546 return value;
547 }
548
549
550
551
552
553
554
555 public static synchronized void setProperty(String property, String value) {
556 if (value != null) {
557 SETTINGS_PROPERTIES.setProperty(property, value);
558 } else {
559 SETTINGS_PROPERTIES.setProperty(property, "");
560 }
561 }
562
563
564
565
566
567
568
569 public static synchronized int getIntProperty(String property) {
570 return getIntProperty(property, 0);
571 }
572
573
574
575
576
577
578
579
580 public static synchronized int getIntProperty(String property, int defaultValue) {
581 int value = defaultValue;
582 try {
583 String propertyValue = SETTINGS_PROPERTIES.getProperty(property);
584 if (propertyValue != null && !propertyValue.isEmpty()) {
585 value = Integer.parseInt(propertyValue);
586 }
587 } catch (NumberFormatException e) {
588 DavGatewayTray.error(new BundleMessage("LOG_INVALID_SETTING_VALUE", property), e);
589 }
590 return value;
591 }
592
593
594
595
596
597
598
599 public static synchronized boolean getBooleanProperty(String property) {
600 String propertyValue = SETTINGS_PROPERTIES.getProperty(property);
601 return Boolean.parseBoolean(propertyValue);
602 }
603
604
605
606
607
608
609
610
611 public static synchronized boolean getBooleanProperty(String property, boolean defaultValue) {
612 boolean value = defaultValue;
613 String propertyValue = SETTINGS_PROPERTIES.getProperty(property);
614 if (propertyValue != null && !propertyValue.isEmpty()) {
615 value = Boolean.parseBoolean(propertyValue);
616 }
617 return value;
618 }
619
620 public static synchronized String loadRefreshToken(String username) {
621 String tokenFilePath = Settings.getProperty("davmail.oauth.tokenFilePath");
622 if (isEmpty(tokenFilePath)) {
623 return Settings.getProperty("davmail.oauth." + username.toLowerCase() + ".refreshToken");
624 } else {
625 return loadTokenFromFile(tokenFilePath, username.toLowerCase());
626 }
627 }
628
629
630 public static synchronized void storeRefreshToken(String username, String refreshToken) {
631 String tokenFilePath = Settings.getProperty("davmail.oauth.tokenFilePath");
632 if (isEmpty(tokenFilePath)) {
633 Settings.setProperty("davmail.oauth." + username.toLowerCase() + ".refreshToken", refreshToken);
634 Settings.save();
635 } else {
636 saveTokenToFile(tokenFilePath, username.toLowerCase(), refreshToken);
637 }
638 }
639
640
641
642
643
644
645
646
647 private static void saveTokenToFile(String tokenFilePath, String username, String refreshToken) {
648 try {
649 checkCreateTokenFilePath(tokenFilePath);
650 Properties properties = new Properties();
651 try (FileInputStream fis = new FileInputStream(tokenFilePath)) {
652 properties.load(fis);
653 }
654 properties.setProperty(username, refreshToken);
655 try (FileOutputStream fos = new FileOutputStream(tokenFilePath)) {
656 properties.store(fos, "Oauth tokens");
657 }
658 } catch (IOException e) {
659 Logger.getLogger(Settings.class).warn(e + " " + e.getMessage());
660 }
661 }
662
663
664
665
666
667
668
669
670 private static String loadTokenFromFile(String tokenFilePath, String username) {
671 try {
672 checkCreateTokenFilePath(tokenFilePath);
673 Properties properties = new Properties();
674 try (FileInputStream fis = new FileInputStream(tokenFilePath)) {
675 properties.load(fis);
676 }
677 return properties.getProperty(username);
678 } catch (IOException e) {
679 Logger.getLogger(Settings.class).warn(e + " " + e.getMessage());
680 }
681 return null;
682 }
683
684 private static void checkCreateTokenFilePath(String tokenFilePath) throws IOException {
685 File file = new File(tokenFilePath);
686 File parentFile = file.getParentFile();
687 if (parentFile != null && (parentFile.mkdirs())) {
688 LOGGER.info("Created token file directory " + parentFile.getAbsolutePath());
689
690 }
691 if (file.createNewFile()) {
692 LOGGER.info("Created token file " + tokenFilePath);
693 }
694 }
695
696
697
698
699
700
701
702 private static String getLoggingPrefix(String category) {
703 String prefix;
704 if ("rootLogger".equals(category)) {
705 prefix = "log4j.";
706 } else {
707 prefix = "log4j.logger.";
708 }
709 return prefix;
710 }
711
712
713
714
715
716
717
718 public static synchronized Level getLoggingLevel(String category) {
719 String prefix = getLoggingPrefix(category);
720 String currentValue = SETTINGS_PROPERTIES.getProperty(prefix + category);
721
722 if (currentValue != null && !currentValue.isEmpty()) {
723 return Level.toLevel(currentValue);
724 } else if ("rootLogger".equals(category)) {
725 return Logger.getRootLogger().getLevel();
726 } else {
727 return Logger.getLogger(category).getLevel();
728 }
729 }
730
731
732
733
734
735
736
737 public static synchronized Properties getSubProperties(String scope) {
738 final String keyStart;
739 if (scope == null || scope.isEmpty()) {
740 keyStart = "";
741 } else if (scope.endsWith(".")) {
742 keyStart = scope;
743 } else {
744 keyStart = scope + '.';
745 }
746 Properties result = new Properties();
747 for (Map.Entry<Object, Object> entry : SETTINGS_PROPERTIES.entrySet()) {
748 String key = (String) entry.getKey();
749 if (key.startsWith(keyStart)) {
750 String value = (String) entry.getValue();
751 result.setProperty(key.substring(keyStart.length()), value);
752 }
753 }
754 return result;
755 }
756
757
758
759
760
761
762
763 public static synchronized void setLoggingLevel(String category, Level level) {
764 if (level != null) {
765 String prefix = getLoggingPrefix(category);
766 SETTINGS_PROPERTIES.setProperty(prefix + category, level.toString());
767 if ("rootLogger".equals(category)) {
768 Logger.getRootLogger().setLevel(level);
769 } else {
770 Logger.getLogger(category).setLevel(level);
771 }
772 }
773 }
774
775
776
777
778
779
780
781 public static synchronized void saveProperty(String property, String value) {
782 Settings.load();
783 Settings.setProperty(property, value);
784 Settings.save();
785 }
786
787
788
789
790
791
792 public static boolean isWindows() {
793 return System.getProperty("os.name").toLowerCase().startsWith("windows");
794 }
795
796
797
798
799
800
801 public static boolean isOSX() {
802 return System.getProperty("os.name").toLowerCase().startsWith("mac os x");
803 }
804
805
806
807
808
809
810 public static boolean isLinux() {
811 return System.getProperty("os.name").toLowerCase().startsWith("linux");
812 }
813
814 public static boolean isUnix() {
815 return isLinux() ||
816 System.getProperty("os.name").toLowerCase().startsWith("freebsd");
817 }
818
819 public static String getUserAgent() {
820 return getProperty("davmail.userAgent", Settings.EDGE_USER_AGENT);
821 }
822
823 public static String getOutlookUrl() {
824 String tld = getProperty("davmail.tld");
825 String outlookUrl = getProperty("davmail.outlookUrl");
826 if (outlookUrl != null) {
827 return outlookUrl;
828 } else if (tld == null) {
829 return OUTLOOK_URL;
830 } else {
831 return "https://outlook.office365." + tld;
832 }
833 }
834
835 public static String getO365Url() {
836 String tld = getProperty("davmail.tld");
837 String outlookUrl = getProperty("davmail.outlookUrl");
838 if (outlookUrl != null) {
839 return outlookUrl + "/EWS/Exchange.asmx";
840 } else if (tld == null) {
841 return O365_URL;
842 } else {
843 return "https://outlook.office365." + tld + "/EWS/Exchange.asmx";
844 }
845 }
846
847
848
849
850
851
852 public static String getGraphUrl() {
853 String tld = getProperty("davmail.tld");
854 String graphUrl = getProperty("davmail.graphUrl");
855 if (graphUrl != null) {
856 return graphUrl;
857 } else if (tld == null) {
858 return GRAPH_URL;
859 } else {
860 return "https://graph.microsoft." + tld;
861 }
862 }
863
864 public static String getO365LoginUrl() {
865 String tld = getProperty("davmail.tld");
866 String loginUrl = getProperty("davmail.loginUrl");
867 if (loginUrl != null) {
868 return loginUrl;
869 } else if (tld == null) {
870 return O365_LOGIN_URL;
871 } else {
872 return "https://login.microsoftonline." + tld;
873 }
874 }
875
876 public static String getOauthScope() {
877 String defaultOauthScope = "openid profile offline_access Mail.ReadWrite Calendars.ReadWrite MailboxSettings.Read Mail.ReadWrite.Shared Contacts.ReadWrite Tasks.ReadWrite Mail.Send People.Read";
878 if (Settings.getProperty("davmail.oauth.redirectUri","").startsWith("urn:")) {
879 defaultOauthScope = "openid profile offline_access";
880 }
881 return Settings.getProperty("davmail.oauth.scope", defaultOauthScope);
882 }
883
884 public static boolean isGraphEnabled() {
885 return Settings.O365_GRAPH.equals(Settings.getProperty("davmail.mode")) || Settings.getBooleanProperty("davmail.enableGraph");
886 }
887
888 public static boolean isSWTAvailable() {
889 boolean isSWTAvailable = false;
890 ClassLoader classloader = Settings.class.getClassLoader();
891 try {
892
893 classloader.loadClass("org.eclipse.swt.SWT");
894 isSWTAvailable = true;
895 } catch (Throwable e) {
896
897 if (!isOSX()) {
898 LOGGER.info(new BundleMessage("LOG_SWT_NOT_AVAILABLE"));
899 }
900 }
901 return isSWTAvailable;
902 }
903
904 public static boolean isJFXAvailable() {
905 boolean isJFXAvailable = false;
906 try {
907 Class.forName("javafx.application.Platform");
908 isJFXAvailable = true;
909 } catch (ClassNotFoundException | NullPointerException e) {
910 LOGGER.info("JavaFX (OpenJFX) not available");
911 }
912 return isJFXAvailable;
913 }
914
915 public static boolean isDocker() {
916 boolean isDocker = new File("/.dockerenv").exists();
917 boolean isPodman = new File("/run/.containerenv").exists();
918 if (isDocker) {
919 LOGGER.info("Running in docker or podman");
920 }
921 return isDocker || isPodman;
922 }
923
924 public static boolean isFlatpak() {
925 boolean isFlatpak = "flatpak".equals(System.getenv("container"));
926 if (isFlatpak) {
927 LOGGER.info("Running in Flatpak");
928 }
929 return isFlatpak;
930 }
931
932
933
934
935
936 public static synchronized String getConfigFilePath() {
937 if (isFlatpak()) {
938 return System.getenv("XDG_CONFIG_HOME")+"/davmail.properties";
939 } else {
940
941 return System.getenv("DAVMAIL_PROPERTIES");
942 }
943 }
944 }