ccb31e072b804175ff8c3dee64b88403ac59de40
[idea/community.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / libraryEditor / JavadocQuarantineStatusCleaner.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.roots.ui.configuration.libraryEditor;
3
4 import com.intellij.ide.JavaUiBundle;
5 import com.intellij.notification.Notification;
6 import com.intellij.notification.NotificationGroup;
7 import com.intellij.notification.NotificationType;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.application.ModalityState;
10 import com.intellij.openapi.diagnostic.Logger;
11 import com.intellij.openapi.progress.ProgressIndicator;
12 import com.intellij.openapi.progress.Task;
13 import com.intellij.openapi.ui.MessageDialogBuilder;
14 import com.intellij.openapi.ui.Messages;
15 import com.intellij.openapi.util.SystemInfo;
16 import com.intellij.openapi.util.text.StringUtil;
17 import com.intellij.openapi.vfs.VirtualFile;
18 import com.sun.jna.platform.mac.XAttrUtil;
19 import org.jetbrains.annotations.NotNull;
20
21 import java.io.IOException;
22 import java.net.URI;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.nio.file.Paths;
26 import java.util.List;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29
30 /**
31  * Files downloaded from Internet are marked as 'quarantined' by OS X.
32  * For such files, opening URLs of type file://path#fragment via
33  * <a href="https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html">
34  *   Launch Services API
35  * </a>
36  * (used internally by {@link java.awt.Desktop#browse(URI)}) won't work as expected (fragment will be ignored on file opening).
37  * This class allows to clear quarantine status from folder containing Javadoc, if confirmed by user.
38  *
39  * Implementation note: UserDefinedFileAttributeView is not supported on macOS (https://bugs.openjdk.java.net/browse/JDK-8030048),
40  * so the class resorts to JNA.
41  */
42 public final class JavadocQuarantineStatusCleaner {
43   private static final Logger LOG = Logger.getInstance(JavadocQuarantineStatusCleaner.class);
44
45   private static final String QUARANTINE_ATTRIBUTE = "com.apple.quarantine";
46
47   public static void cleanIfNeeded(VirtualFile @NotNull ... docFolders) {
48     if (docFolders.length > 0 && SystemInfo.isMac) {
49       ApplicationManager.getApplication().executeOnPooledThread(() -> {
50         List<String> quarantined = Stream.of(docFolders)
51           .filter(f -> f.isInLocalFileSystem() && f.isDirectory() && XAttrUtil.getXAttr(f.getPath(), QUARANTINE_ATTRIBUTE) != null)
52           .map(VirtualFile::getPath)
53           .collect(Collectors.toList());
54         if (!quarantined.isEmpty()) {
55           ApplicationManager.getApplication().invokeLater(() -> {
56             String title = JavaUiBundle.message("quarantine.cleaner");
57             String message = JavaUiBundle.message("quarantine.dialog.message", StringUtil.join(quarantined, "\n"));
58             if (MessageDialogBuilder.yesNo(title, message).show() == Messages.YES) {
59               cleanQuarantineStatusInBackground(quarantined);
60             }
61           }, ModalityState.any());
62         }
63       });
64     }
65   }
66
67   private static void cleanQuarantineStatusInBackground(List<String> paths) {
68     new Task.Backgroundable(null, JavaUiBundle.message("quarantine.clean.progress"), true) {
69       @Override
70       public void run(@NotNull ProgressIndicator indicator) {
71         for (String path : paths) {
72           indicator.checkCanceled();
73           indicator.setText2(path);
74           try (Stream<Path> s = Files.walk(Paths.get(path))) {
75             s.forEach(p -> {
76               indicator.checkCanceled();
77               XAttrUtil.removeXAttr(p.toFile().getAbsolutePath(), QUARANTINE_ATTRIBUTE);
78             });
79           }
80           catch (IOException e) {
81             throw new RuntimeException(e);
82           }
83         }
84       }
85
86       @Override
87       public void onThrowable(@NotNull Throwable error) {
88         LOG.warn(error);
89         String title = JavaUiBundle.message("quarantine.cleaner");
90         String message = JavaUiBundle.message("quarantine.error.message", error.getMessage());
91         new Notification(NotificationGroup.createIdWithTitle("Quarantine Cleaner", title), title, message, NotificationType.WARNING)
92           .notify(null);
93       }
94     }.queue();
95   }
96 }