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