View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.exchange.auth;
21  
22  import davmail.BundleMessage;
23  import davmail.Settings;
24  import davmail.ui.tray.SwtGatewayTray;
25  import org.apache.log4j.Logger;
26  import org.eclipse.swt.SWT;
27  import org.eclipse.swt.browser.Browser;
28  import org.eclipse.swt.browser.LocationEvent;
29  import org.eclipse.swt.browser.LocationListener;
30  import org.eclipse.swt.layout.FillLayout;
31  import org.eclipse.swt.widgets.Display;
32  import org.eclipse.swt.widgets.Shell;
33  
34  import java.util.concurrent.TimeUnit;
35  import java.util.concurrent.locks.Condition;
36  import java.util.concurrent.locks.ReentrantLock;
37  
38  /**
39   * Alternative embedded browser implementation based on SWT instead of OpenJFX
40   */
41  public class O365InteractiveAuthenticatorSWT {
42  
43      private static final Logger LOGGER = Logger.getLogger(O365InteractiveAuthenticatorSWT.class);
44  
45      private O365InteractiveAuthenticator authenticator;
46  
47      Shell shell;
48      Browser browser;
49      private final ReentrantLock lock = new ReentrantLock();
50      private final Condition ready = lock.newCondition();
51      private boolean isReady = false;
52      private Error error;
53  
54      public O365InteractiveAuthenticatorSWT() {
55  
56      }
57  
58      public void setO365InteractiveAuthenticator(O365InteractiveAuthenticator authenticator) {
59          this.authenticator = authenticator;
60      }
61  
62      public void authenticate(final String initUrl, final String redirectUri) {
63          // initialize SWT
64          SwtGatewayTray.initDisplay();
65  
66          // allow SSO on windows
67          System.setProperty("org.eclipse.swt.browser.Edge.allowSingleSignOnUsingOSPrimaryAccount",
68                  Settings.getProperty("davmail.oauth.allowSingleSignOnUsingOSPrimaryAccount", "true"));
69  
70          Display.getDefault().asyncExec(() -> {
71              try {
72                  shell = new Shell(Display.getDefault());
73                  shell.setText(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
74                  shell.setSize(600, 600);
75  
76                  shell.setLayout(new FillLayout());
77  
78                  shell.setImage(SwtGatewayTray.loadSwtImage("tray.png", 32));
79  
80                  shell.addListener(SWT.Close, event -> {
81                      if (!authenticator.isAuthenticated && authenticator.errorCode == null) {
82                          authenticator.errorCode = "user closed authentication window";
83                      }
84                      dispose();
85                  });
86  
87                  browser = new Browser(shell, SWT.NONE);
88  
89                  browser.setUrl(initUrl);
90                  browser.addTitleListener(titleEvent -> shell.setText("DavMail: " + titleEvent.title));
91                  browser.addLocationListener(new LocationListener() {
92                      @Override
93                      public void changing(LocationEvent locationEvent) {
94                          LOGGER.debug("Navigate to " + locationEvent.toString());
95                          String location = locationEvent.location;
96  
97                          if (location.startsWith(redirectUri)) {
98  
99                              LOGGER.debug("Location starts with redirectUri, check code");
100                             authenticator.handleCode(location);
101 
102                             shell.close();
103                             dispose();
104                         }
105 
106                     }
107 
108                     @Override
109                     public void changed(LocationEvent locationEvent) {
110                         LOGGER.debug("Page changed: " + locationEvent.toString());
111                     }
112                 });
113 
114                 lock.lock();
115                 try {
116                     isReady = true;
117                     ready.signalAll();
118                 } finally {
119                     lock.unlock();
120                 }
121 
122                 shell.open();
123                 shell.setActive();
124             } catch (Throwable e) {
125                 LOGGER.error("Error in SWT thread", e);
126                 lock.lock();
127                 try {
128                     error = new Error(e);
129                     ready.signalAll();
130                 } finally {
131                     lock.unlock();
132                 }
133             }
134 
135         });
136 
137         lock.lock();
138         try {
139             while (!isReady) {
140                 ready.await();
141                 if (error != null) {
142                     throw error;
143                 }
144             }
145         } catch (InterruptedException e) {
146             Thread.currentThread().interrupt();
147         } finally {
148             lock.unlock();
149         }
150     }
151 
152     protected void dispose() {
153         Display.getDefault().asyncExec(() -> {
154             if (browser != null) {
155                 browser.dispose();
156             }
157             if (shell != null) {
158                 shell.dispose();
159             }
160         });
161     }
162 }