1 |
/*
|
1 |
/*
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
3 |
*
|
3 |
*
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
5 |
*
|
5 |
*
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
9 |
* language governing permissions and limitations under the License.
|
9 |
* language governing permissions and limitations under the License.
|
10 |
*
|
10 |
*
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
12 |
*/
|
12 |
*/
|
13 |
|
13 |
|
14 |
package org.openconcerto.utils;
|
14 |
package org.openconcerto.utils;
|
15 |
|
15 |
|
16 |
import org.openconcerto.utils.DesktopEnvironment.Gnome;
|
16 |
import org.openconcerto.utils.DesktopEnvironment.Gnome;
|
17 |
import org.openconcerto.utils.DesktopEnvironment.KDE;
|
17 |
import org.openconcerto.utils.DesktopEnvironment.KDE;
|
18 |
import org.openconcerto.utils.DesktopEnvironment.Mac;
|
18 |
import org.openconcerto.utils.DesktopEnvironment.Mac;
|
19 |
import org.openconcerto.utils.DesktopEnvironment.Windows;
|
19 |
import org.openconcerto.utils.DesktopEnvironment.Windows;
|
- |
|
20 |
import org.openconcerto.utils.DesktopEnvironment.XFCE;
|
- |
|
21 |
import org.openconcerto.utils.OSFamily.Unix;
|
20 |
import org.openconcerto.utils.io.PercentEncoder;
|
22 |
import org.openconcerto.utils.io.PercentEncoder;
|
21 |
|
23 |
|
22 |
import java.io.BufferedOutputStream;
|
24 |
import java.io.BufferedOutputStream;
|
23 |
import java.io.BufferedWriter;
|
25 |
import java.io.BufferedWriter;
|
24 |
import java.io.File;
|
26 |
import java.io.File;
|
25 |
import java.io.IOException;
|
27 |
import java.io.IOException;
|
26 |
import java.io.OutputStreamWriter;
|
28 |
import java.io.OutputStreamWriter;
|
27 |
import java.io.PrintStream;
|
29 |
import java.io.PrintStream;
|
28 |
import java.io.Writer;
|
30 |
import java.io.Writer;
|
29 |
import java.net.URI;
|
31 |
import java.net.URI;
|
30 |
import java.net.URISyntaxException;
|
32 |
import java.net.URISyntaxException;
|
31 |
import java.util.ArrayList;
|
33 |
import java.util.ArrayList;
|
32 |
import java.util.Arrays;
|
34 |
import java.util.Arrays;
|
33 |
import java.util.List;
|
35 |
import java.util.List;
|
34 |
import java.util.regex.Matcher;
|
36 |
import java.util.regex.Matcher;
|
35 |
import java.util.regex.Pattern;
|
37 |
import java.util.regex.Pattern;
|
36 |
|
38 |
|
37 |
public abstract class EmailClient {
|
39 |
public abstract class EmailClient {
|
38 |
|
40 |
|
39 |
public static enum EmailClientType {
|
41 |
public static enum EmailClientType {
|
40 |
Thunderbird, AppleMail, Outlook, XDG
|
42 |
Thunderbird, AppleMail, Outlook, XDG
|
41 |
}
|
43 |
}
|
42 |
|
44 |
|
43 |
private static EmailClient PREFERRED = null;
|
45 |
private static EmailClient PREFERRED = null;
|
44 |
|
46 |
|
45 |
/**
|
47 |
/**
|
46 |
* Find the preferred email client.
|
48 |
* Find the preferred email client.
|
47 |
*
|
49 |
*
|
48 |
* @return the preferred email client, never <code>null</code>.
|
50 |
* @return the preferred email client, never <code>null</code>.
|
49 |
* @throws IOException if an error occurs.
|
51 |
* @throws IOException if an error occurs.
|
50 |
*/
|
52 |
*/
|
51 |
public static final EmailClient getPreferred() throws IOException {
|
53 |
public static final EmailClient getPreferred() throws IOException {
|
52 |
if (PREFERRED == null) {
|
54 |
if (PREFERRED == null) {
|
53 |
PREFERRED = findPreferred();
|
55 |
PREFERRED = findPreferred();
|
54 |
// should at least return MailTo
|
56 |
// should at least return MailTo
|
55 |
assert PREFERRED != null;
|
57 |
assert PREFERRED != null;
|
56 |
}
|
58 |
}
|
57 |
return PREFERRED;
|
59 |
return PREFERRED;
|
58 |
}
|
60 |
}
|
59 |
|
61 |
|
60 |
/**
|
62 |
/**
|
61 |
* Clear the preferred client.
|
63 |
* Clear the preferred client.
|
62 |
*/
|
64 |
*/
|
63 |
public static final void resetPreferred() {
|
65 |
public static final void resetPreferred() {
|
64 |
PREFERRED = null;
|
66 |
PREFERRED = null;
|
65 |
}
|
67 |
}
|
66 |
|
68 |
|
67 |
// XP used tabs, but not 7
|
69 |
// XP used tabs, but not 7
|
68 |
// MULTILINE since there's several lines in addition to the wanted one
|
70 |
// MULTILINE since there's several lines in addition to the wanted one
|
69 |
private static final Pattern registryPattern = Pattern.compile("\\s+REG_SZ\\s+(.*)$", Pattern.MULTILINE);
|
71 |
private static final Pattern registryPattern = Pattern.compile("\\s+REG_SZ\\s+(.*)$", Pattern.MULTILINE);
|
70 |
private static final Pattern cmdLinePattern = Pattern.compile("(\"(.*?)\")|([^\\s\"]+)\\b");
|
72 |
private static final Pattern cmdLinePattern = Pattern.compile("(\"(.*?)\")|([^\\s\"]+)\\b");
|
71 |
// any whitespace except space and tab
|
73 |
// any whitespace except space and tab
|
72 |
private static final Pattern wsPattern = Pattern.compile("[\\s&&[^ \t]]");
|
74 |
private static final Pattern wsPattern = Pattern.compile("[\\s&&[^ \t]]");
|
73 |
private static final Pattern dictPattern;
|
75 |
private static final Pattern dictPattern;
|
74 |
private static final String AppleMailBundleID = "com.apple.mail";
|
76 |
private static final String AppleMailBundleID = "com.apple.mail";
|
75 |
private static final String ThunderbirdBundleID = "org.mozilla.thunderbird";
|
77 |
private static final String ThunderbirdBundleID = "org.mozilla.thunderbird";
|
76 |
static {
|
78 |
static {
|
77 |
final String rolePattern = "(?:LSHandlerRoleAll\\s*=\\s*\"([\\w\\.]+)\";\\s*)?";
|
79 |
final String rolePattern = "(?:LSHandlerRoleAll\\s*=\\s*\"([\\w\\.]+)\";\\s*)?";
|
78 |
dictPattern = Pattern.compile("\\{\\s*" + rolePattern + "LSHandlerURLScheme = mailto;\\s*" + rolePattern + "\\}");
|
80 |
dictPattern = Pattern.compile("\\{\\s*" + rolePattern + "LSHandlerURLScheme = mailto;\\s*" + rolePattern + "\\}");
|
79 |
}
|
81 |
}
|
80 |
|
82 |
|
81 |
private final static String createEncodedParam(final String name, final String value) {
|
83 |
private final static String createEncodedParam(final String name, final String value) {
|
82 |
return name + "=" + PercentEncoder.encode(value, StringUtils.UTF8);
|
84 |
return name + "=" + PercentEncoder.encode(value, StringUtils.UTF8);
|
83 |
}
|
85 |
}
|
84 |
|
86 |
|
85 |
private final static String createASParam(final String name, final String value) {
|
87 |
private final static String createASParam(final String name, final String value) {
|
86 |
return name + ":" + StringUtils.doubleQuote(value);
|
88 |
return name + ":" + StringUtils.doubleQuote(value);
|
87 |
}
|
89 |
}
|
88 |
|
90 |
|
89 |
private final static String createVBParam(final String name, final String value) {
|
91 |
private final static String createVBParam(final String name, final String value) {
|
90 |
final String switchName = "/" + name + ":";
|
92 |
final String switchName = "/" + name + ":";
|
91 |
if (value == null || value.length() == 0)
|
93 |
if (value == null || value.length() == 0)
|
92 |
return switchName;
|
94 |
return switchName;
|
93 |
// we need to encode the value since when invoking cscript.exe we cannot pass "
|
95 |
// we need to encode the value since when invoking cscript.exe we cannot pass "
|
94 |
// since all arguments are re-parsed
|
96 |
// since all arguments are re-parsed
|
95 |
final String encoded = PercentEncoder.encodeUTF16(value);
|
97 |
final String encoded = PercentEncoder.encodeUTF16(value);
|
96 |
assert encoded.indexOf('"') < 0 : "Encoded contains a double quote, this will confuse cscript";
|
98 |
assert encoded.indexOf('"') < 0 : "Encoded contains a double quote, this will confuse cscript";
|
97 |
return switchName + '"' + encoded + '"';
|
99 |
return switchName + '"' + encoded + '"';
|
98 |
}
|
100 |
}
|
99 |
|
101 |
|
100 |
/**
|
102 |
/**
|
101 |
* Create a mailto URI.
|
103 |
* Create a mailto URI.
|
102 |
*
|
104 |
*
|
103 |
* @param to the recipient, can be <code>null</code>.
|
105 |
* @param to the recipient, can be <code>null</code>.
|
104 |
* @param subject the subject, can be <code>null</code>.
|
106 |
* @param subject the subject, can be <code>null</code>.
|
105 |
* @param body the body of the email, can be <code>null</code>.
|
107 |
* @param body the body of the email, can be <code>null</code>.
|
106 |
* @param attachments files to attach, for security reason this parameter is ignored by at least
|
108 |
* @param attachments files to attach, for security reason this parameter is ignored by at least
|
107 |
* Outlook 2007, Apple Mail and Thunderbird.
|
109 |
* Outlook 2007, Apple Mail and Thunderbird.
|
108 |
* @return the mailto URI.
|
110 |
* @return the mailto URI.
|
109 |
* @throws IOException if an encoding error happens.
|
111 |
* @throws IOException if an encoding error happens.
|
110 |
* @see <a href="http://tools.ietf.org/html/rfc2368">RFC 2368</a>
|
112 |
* @see <a href="http://tools.ietf.org/html/rfc2368">RFC 2368</a>
|
111 |
* @see <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=67254">Don't allow attachment
|
113 |
* @see <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=67254">Don't allow attachment
|
112 |
* of local file from non-local link</a>
|
114 |
* of local file from non-local link</a>
|
113 |
*/
|
115 |
*/
|
114 |
public final static URI getMailToURI(final String to, final String subject, final String body, final File... attachments) throws IOException {
|
116 |
public final static URI getMailToURI(final String to, final String subject, final String body, final File... attachments) throws IOException {
|
115 |
// mailto:p.dupond@example.com?subject=Sujet%20du%20courrier&cc=pierre@example.org&bcc=jacques@example.net&body=Bonjour
|
117 |
// mailto:p.dupond@example.com?subject=Sujet%20du%20courrier&cc=pierre@example.org&bcc=jacques@example.net&body=Bonjour
|
116 |
|
118 |
|
117 |
// Outlook doesn't support the to header as mandated by 2. of the RFC
|
119 |
// Outlook doesn't support the to header as mandated by 2. of the RFC
|
118 |
final String encodedTo = to == null ? "" : PercentEncoder.encode(to, StringUtils.UTF8);
|
120 |
final String encodedTo = to == null ? "" : PercentEncoder.encode(to, StringUtils.UTF8);
|
119 |
final List<String> l = new ArrayList<String>(4);
|
121 |
final List<String> l = new ArrayList<String>(4);
|
120 |
if (subject != null)
|
122 |
if (subject != null)
|
121 |
l.add(createEncodedParam("subject", subject));
|
123 |
l.add(createEncodedParam("subject", subject));
|
122 |
if (body != null)
|
124 |
if (body != null)
|
123 |
l.add(createEncodedParam("body", body));
|
125 |
l.add(createEncodedParam("body", body));
|
124 |
for (final File attachment : attachments)
|
126 |
for (final File attachment : attachments)
|
125 |
l.add(createEncodedParam("attachment", attachment.getAbsolutePath()));
|
127 |
l.add(createEncodedParam("attachment", attachment.getAbsolutePath()));
|
126 |
final String query = CollectionUtils.join(l, "&");
|
128 |
final String query = CollectionUtils.join(l, "&");
|
127 |
try {
|
129 |
try {
|
128 |
return new URI("mailto:" + encodedTo + "?" + query);
|
130 |
return new URI("mailto:" + encodedTo + "?" + query);
|
129 |
} catch (URISyntaxException e) {
|
131 |
} catch (URISyntaxException e) {
|
130 |
throw new IOException("Couldn't create mailto URI", e);
|
132 |
throw new IOException("Couldn't create mailto URI", e);
|
131 |
}
|
133 |
}
|
132 |
}
|
134 |
}
|
133 |
|
135 |
|
134 |
// see http://kb.mozillazine.org/Command_line_arguments_(Thunderbird)
|
136 |
// see http://kb.mozillazine.org/Command_line_arguments_(Thunderbird)
|
135 |
// The escape mechanism isn't specified, it turns out we can pass percent encoded strings
|
137 |
// The escape mechanism isn't specified, it turns out we can pass percent encoded strings
|
136 |
private final static String getTBParam(final String to, final String subject, final String body, final File... attachments) {
|
138 |
private final static String getTBParam(final String to, final String subject, final String body, final File... attachments) {
|
137 |
/**
|
139 |
/**
|
138 |
* <pre>
|
140 |
* <pre>
|
139 |
"to='john@example.com,kathy@example.com',cc='britney@example.com',subject='dinner',body='How about dinner tonight?',attachment='file:///C:/cygwin/Cygwin.bat,file:///C:/cygwin/Cygwin.ico'";
|
141 |
"to='john@example.com,kathy@example.com',cc='britney@example.com',subject='dinner',body='How about dinner tonight?',attachment='file:///C:/cygwin/Cygwin.bat,file:///C:/cygwin/Cygwin.ico'";
|
140 |
* </pre>
|
142 |
* </pre>
|
141 |
*/
|
143 |
*/
|
142 |
|
144 |
|
143 |
final List<String> l = new ArrayList<String>(4);
|
145 |
final List<String> l = new ArrayList<String>(4);
|
144 |
if (to != null)
|
146 |
if (to != null)
|
145 |
l.add(createEncodedParam("to", to));
|
147 |
l.add(createEncodedParam("to", to));
|
146 |
if (subject != null)
|
148 |
if (subject != null)
|
147 |
l.add(createEncodedParam("subject", subject));
|
149 |
l.add(createEncodedParam("subject", subject));
|
148 |
if (body != null)
|
150 |
if (body != null)
|
149 |
l.add(createEncodedParam("body", body));
|
151 |
l.add(createEncodedParam("body", body));
|
150 |
final List<String> urls = new ArrayList<String>(attachments.length);
|
152 |
final List<String> urls = new ArrayList<String>(attachments.length);
|
151 |
for (final File attachment : attachments) {
|
153 |
for (final File attachment : attachments) {
|
152 |
// Thunderbird doesn't parse java URI file:/C:/
|
154 |
// Thunderbird doesn't parse java URI file:/C:/
|
153 |
final String rawPath = attachment.toURI().getRawPath();
|
155 |
final String rawPath = attachment.toURI().getRawPath();
|
154 |
// handle UNC paths
|
156 |
// handle UNC paths
|
155 |
final String tbURL = (rawPath.startsWith("//") ? "file:///" : "file://") + rawPath;
|
157 |
final String tbURL = (rawPath.startsWith("//") ? "file:///" : "file://") + rawPath;
|
156 |
urls.add(tbURL);
|
158 |
urls.add(tbURL);
|
157 |
}
|
159 |
}
|
158 |
l.add(createEncodedParam("attachment", CollectionUtils.join(urls, ",")));
|
160 |
l.add(createEncodedParam("attachment", CollectionUtils.join(urls, ",")));
|
159 |
|
161 |
|
160 |
return DesktopEnvironment.getDE().quoteParamForExec(CollectionUtils.join(l, ","));
|
162 |
return DesktopEnvironment.getDE().quoteParamForExec(CollectionUtils.join(l, ","));
|
161 |
}
|
163 |
}
|
162 |
|
164 |
|
163 |
private final static String getAppleMailParam(final String subject, final String body) {
|
165 |
private final static String getAppleMailParam(final String subject, final String body) {
|
164 |
final List<String> l = new ArrayList<String>(3);
|
166 |
final List<String> l = new ArrayList<String>(3);
|
165 |
l.add("visible:true");
|
167 |
l.add("visible:true");
|
166 |
if (subject != null)
|
168 |
if (subject != null)
|
167 |
l.add(createASParam("subject", subject));
|
169 |
l.add(createASParam("subject", subject));
|
168 |
if (body != null)
|
170 |
if (body != null)
|
169 |
l.add(createASParam("content", body));
|
171 |
l.add(createASParam("content", body));
|
170 |
|
172 |
|
171 |
return CollectionUtils.join(l, ", ");
|
173 |
return CollectionUtils.join(l, ", ");
|
172 |
}
|
174 |
}
|
173 |
|
175 |
|
174 |
// @param cmdLine "C:\Program Files\Mozilla Thunderbird\thunderbird.exe" -osint -compose "%1"
|
176 |
// @param cmdLine "C:\Program Files\Mozilla Thunderbird\thunderbird.exe" -osint -compose "%1"
|
175 |
// @param toReplace "%1"
|
177 |
// @param toReplace "%1"
|
176 |
private static String[] tbCommand(final String cmdLine, final String toReplace, final String to, final String subject, final String body, final File... attachments) {
|
178 |
private static String[] tbCommand(final String cmdLine, final String toReplace, final String to, final String subject, final String body, final File... attachments) {
|
177 |
final String composeArg = getTBParam(to, subject, body, attachments);
|
179 |
final String composeArg = getTBParam(to, subject, body, attachments);
|
178 |
|
180 |
|
179 |
final List<String> arguments = new ArrayList<String>();
|
181 |
final List<String> arguments = new ArrayList<String>();
|
180 |
final Matcher cmdMatcher = cmdLinePattern.matcher(cmdLine);
|
182 |
final Matcher cmdMatcher = cmdLinePattern.matcher(cmdLine);
|
181 |
while (cmdMatcher.find()) {
|
183 |
while (cmdMatcher.find()) {
|
182 |
final String quoted = cmdMatcher.group(2);
|
184 |
final String quoted = cmdMatcher.group(2);
|
183 |
final String unquoted = cmdMatcher.group(3);
|
185 |
final String unquoted = cmdMatcher.group(3);
|
184 |
assert quoted == null ^ unquoted == null : "Both quoted and unquoted, or neither quoted nor quoted: " + quoted + " and " + unquoted;
|
186 |
assert quoted == null ^ unquoted == null : "Both quoted and unquoted, or neither quoted nor quoted: " + quoted + " and " + unquoted;
|
185 |
final String arg = quoted != null ? quoted : unquoted;
|
187 |
final String arg = quoted != null ? quoted : unquoted;
|
186 |
|
188 |
|
187 |
final boolean replace = arg.equals(toReplace);
|
189 |
final boolean replace = arg.equals(toReplace);
|
188 |
// e.g. on Linux
|
190 |
// e.g. on Linux
|
189 |
if (replace && !arguments.contains("-compose"))
|
191 |
if (replace && !arguments.contains("-compose"))
|
190 |
arguments.add("-compose");
|
192 |
arguments.add("-compose");
|
191 |
arguments.add(replace ? composeArg : arg);
|
193 |
arguments.add(replace ? composeArg : arg);
|
192 |
}
|
194 |
}
|
193 |
|
195 |
|
194 |
return arguments.toArray(new String[arguments.size()]);
|
196 |
return arguments.toArray(new String[arguments.size()]);
|
195 |
}
|
197 |
}
|
196 |
|
198 |
|
197 |
/**
|
199 |
/**
|
198 |
* Open a composing window in the default email client.
|
200 |
* Open a composing window in the default email client.
|
199 |
*
|
201 |
*
|
200 |
* @param to the recipient, can be <code>null</code>.
|
202 |
* @param to the recipient, can be <code>null</code>.
|
201 |
* @param subject the subject, can be <code>null</code>.
|
203 |
* @param subject the subject, can be <code>null</code>.
|
202 |
* @param body the body of the email, can be <code>null</code>.
|
204 |
* @param body the body of the email, can be <code>null</code>.
|
203 |
* @param attachments files to attach, ATTN can be ignored if mailto: is used
|
205 |
* @param attachments files to attach, ATTN can be ignored if mailto: is used
|
204 |
* {@link #getMailToURI(String, String, String, File...)}.
|
206 |
* {@link #getMailToURI(String, String, String, File...)}.
|
205 |
* @throws IOException if a program cannot be executed.
|
207 |
* @throws IOException if a program cannot be executed.
|
206 |
* @throws InterruptedException if the thread is interrupted while waiting for a native program.
|
208 |
* @throws InterruptedException if the thread is interrupted while waiting for a native program.
|
207 |
*/
|
209 |
*/
|
208 |
public void compose(final String to, String subject, final String body, final File... attachments) throws IOException, InterruptedException {
|
210 |
public void compose(final String to, String subject, final String body, final File... attachments) throws IOException, InterruptedException {
|
209 |
// check now as letting the native commands do is a lot less reliable
|
211 |
// check now as letting the native commands do is a lot less reliable
|
210 |
for (File attachment : attachments) {
|
212 |
for (File attachment : attachments) {
|
211 |
if (!attachment.exists())
|
213 |
if (!attachment.exists())
|
212 |
throw new IOException("Attachment doesn't exist: '" + attachment.getAbsolutePath() + "'");
|
214 |
throw new IOException("Attachment doesn't exist: '" + attachment.getAbsolutePath() + "'");
|
213 |
}
|
215 |
}
|
214 |
|
216 |
|
215 |
// a subject should only be one line (Thunderbird strips newlines anyway and Outlook sends a
|
217 |
// a subject should only be one line (Thunderbird strips newlines anyway and Outlook sends a
|
216 |
// malformed email)
|
218 |
// malformed email)
|
217 |
subject = wsPattern.matcher(subject).replaceAll(" ");
|
219 |
subject = wsPattern.matcher(subject).replaceAll(" ");
|
218 |
final boolean handled;
|
220 |
final boolean handled;
|
219 |
// was only trying native if necessary, but mailto url has length limitations and can have
|
221 |
// was only trying native if necessary, but mailto url has length limitations and can have
|
220 |
// encoding issues
|
222 |
// encoding issues
|
221 |
handled = composeNative(to, subject, body, attachments);
|
223 |
handled = composeNative(to, subject, body, attachments);
|
222 |
|
224 |
|
223 |
if (!handled) {
|
225 |
if (!handled) {
|
224 |
final URI mailto = getMailToURI(to, subject, body, attachments);
|
226 |
final URI mailto = getMailToURI(to, subject, body, attachments);
|
225 |
java.awt.Desktop.getDesktop().mail(mailto);
|
227 |
java.awt.Desktop.getDesktop().mail(mailto);
|
226 |
}
|
228 |
}
|
227 |
}
|
229 |
}
|
228 |
|
230 |
|
229 |
static private String cmdSubstitution(String... args) throws IOException {
|
231 |
static private String cmdSubstitution(String... args) throws IOException {
|
230 |
return DesktopEnvironment.cmdSubstitution(Runtime.getRuntime().exec(args));
|
232 |
return DesktopEnvironment.cmdSubstitution(Runtime.getRuntime().exec(args));
|
231 |
}
|
233 |
}
|
232 |
|
234 |
|
233 |
private static EmailClient findPreferred() throws IOException {
|
235 |
private static EmailClient findPreferred() throws IOException {
|
234 |
final DesktopEnvironment de = DesktopEnvironment.getDE();
|
236 |
final DesktopEnvironment de = DesktopEnvironment.getDE();
|
235 |
if (de instanceof Windows) {
|
237 |
if (de instanceof Windows) {
|
236 |
// Tested on XP and 7
|
238 |
// Tested on XP and 7
|
237 |
// <SANS NOM> REG_SZ "C:\Program Files\Mozilla
|
239 |
// <SANS NOM> REG_SZ "C:\Program Files\Mozilla
|
238 |
// Thunderbird\thunderbird.exe" -osint -compose "%1"
|
240 |
// Thunderbird\thunderbird.exe" -osint -compose "%1"
|
239 |
final String out = cmdSubstitution("reg", "query", "HKEY_CLASSES_ROOT\\mailto\\shell\\open\\command");
|
241 |
final String out = cmdSubstitution("reg", "query", "HKEY_CLASSES_ROOT\\mailto\\shell\\open\\command");
|
240 |
|
242 |
|
241 |
final Matcher registryMatcher = registryPattern.matcher(out);
|
243 |
final Matcher registryMatcher = registryPattern.matcher(out);
|
242 |
if (registryMatcher.find()) {
|
244 |
if (registryMatcher.find()) {
|
243 |
final String cmdLine = registryMatcher.group(1);
|
245 |
final String cmdLine = registryMatcher.group(1);
|
244 |
if (cmdLine.contains("thunderbird")) {
|
246 |
if (cmdLine.contains("thunderbird")) {
|
245 |
return new ThunderbirdCommandLine(cmdLine, "%1");
|
247 |
return new ThunderbirdCommandLine(cmdLine, "%1");
|
246 |
} else if (cmdLine.toLowerCase().contains("outlook")) {
|
248 |
} else if (cmdLine.toLowerCase().contains("outlook")) {
|
247 |
return Outlook;
|
249 |
return Outlook;
|
248 |
}
|
250 |
}
|
249 |
}
|
251 |
}
|
250 |
} else if (de instanceof Mac) {
|
252 |
} else if (de instanceof Mac) {
|
251 |
// (
|
253 |
// (
|
252 |
// {
|
254 |
// {
|
253 |
// LSHandlerRoleAll = "com.apple.mail";
|
255 |
// LSHandlerRoleAll = "com.apple.mail";
|
254 |
// LSHandlerURLScheme = mailto;
|
256 |
// LSHandlerURLScheme = mailto;
|
255 |
// }
|
257 |
// }
|
256 |
// )
|
258 |
// )
|
257 |
final String bundleID;
|
259 |
final String bundleID;
|
258 |
final String dict = cmdSubstitution("defaults", "read", "com.apple.LaunchServices", "LSHandlers");
|
260 |
final String dict = cmdSubstitution("defaults", "read", "com.apple.LaunchServices", "LSHandlers");
|
259 |
final Matcher dictMatcher = dictPattern.matcher(dict);
|
261 |
final Matcher dictMatcher = dictPattern.matcher(dict);
|
260 |
if (dictMatcher.find()) {
|
262 |
if (dictMatcher.find()) {
|
261 |
// LSHandlerRoleAll can be before or after LSHandlerURLScheme
|
263 |
// LSHandlerRoleAll can be before or after LSHandlerURLScheme
|
262 |
final String before = dictMatcher.group(1);
|
264 |
final String before = dictMatcher.group(1);
|
263 |
final String after = dictMatcher.group(2);
|
265 |
final String after = dictMatcher.group(2);
|
264 |
assert before == null ^ after == null : "Both before and after, or neither before nor after: " + before + " and " + after;
|
266 |
assert before == null ^ after == null : "Both before and after, or neither before nor after: " + before + " and " + after;
|
265 |
bundleID = before != null ? before : after;
|
267 |
bundleID = before != null ? before : after;
|
266 |
} else
|
268 |
} else
|
267 |
// the default
|
269 |
// the default
|
268 |
bundleID = AppleMailBundleID;
|
270 |
bundleID = AppleMailBundleID;
|
269 |
|
271 |
|
270 |
if (bundleID.equals(AppleMailBundleID)) {
|
272 |
if (bundleID.equals(AppleMailBundleID)) {
|
271 |
return AppleMail;
|
273 |
return AppleMail;
|
272 |
} else if (bundleID.equals(ThunderbirdBundleID)) {
|
274 |
} else if (bundleID.equals(ThunderbirdBundleID)) {
|
273 |
// doesn't work if Thunderbird is already open:
|
275 |
// doesn't work if Thunderbird is already open:
|
274 |
// https://bugzilla.mozilla.org/show_bug.cgi?id=424155
|
276 |
// https://bugzilla.mozilla.org/show_bug.cgi?id=424155
|
275 |
// https://bugzilla.mozilla.org/show_bug.cgi?id=472891
|
277 |
// https://bugzilla.mozilla.org/show_bug.cgi?id=472891
|
276 |
// MAYBE find out if launched and let handled=false
|
278 |
// MAYBE find out if launched and let handled=false
|
277 |
|
279 |
|
278 |
final File appDir = ((Mac) de).getAppDir(bundleID);
|
280 |
final File appDir = ((Mac) de).getAppDir(bundleID);
|
279 |
final File exe = new File(appDir, "Contents/MacOS/thunderbird-bin");
|
281 |
final File exe = new File(appDir, "Contents/MacOS/thunderbird-bin");
|
280 |
|
282 |
|
281 |
return new ThunderbirdPath(exe);
|
283 |
return new ThunderbirdPath(exe);
|
282 |
}
|
284 |
}
|
283 |
} else if (de instanceof Gnome) {
|
285 |
} else if (de instanceof Gnome) {
|
284 |
if (de.getVersion().startsWith("2.")) {
|
286 |
if (de.getVersion().startsWith("2.")) {
|
285 |
// evolution %s
|
287 |
// evolution %s
|
286 |
final String cmdLine = cmdSubstitution("gconftool", "-g", "/desktop/gnome/url-handlers/mailto/command");
|
288 |
final String cmdLine = cmdSubstitution("gconftool", "-g", "/desktop/gnome/url-handlers/mailto/command");
|
287 |
if (cmdLine.contains("thunderbird")) {
|
289 |
if (cmdLine.contains("thunderbird")) {
|
288 |
return new ThunderbirdCommandLine(cmdLine, "%s");
|
290 |
return new ThunderbirdCommandLine(cmdLine, "%s");
|
289 |
}
|
291 |
}
|
290 |
}
|
292 |
}
|
291 |
return XDG;
|
293 |
return XDG;
|
292 |
} else if (de instanceof KDE) {
|
294 |
} else if (de instanceof KDE) {
|
293 |
// TODO look for EmailClient=/usr/bin/thunderbird in
|
295 |
// TODO look for EmailClient=/usr/bin/thunderbird in
|
294 |
// ~/.kde/share/config/emaildefaults or /etc/kde (ou /usr/share/config qui est un
|
296 |
// ~/.kde/share/config/emaildefaults or /etc/kde (ou /usr/share/config qui est un
|
295 |
// lien symbolique vers /etc/kde)
|
297 |
// lien symbolique vers /etc/kde)
|
296 |
return XDG;
|
298 |
return XDG;
|
- |
|
299 |
} else if (de instanceof XFCE) {
|
- |
|
300 |
// .config/xfce4/helpers.rc contains "MailReader=desktopName"
|
- |
|
301 |
// A custom one can be created in .local/share/xfce4/helpers/custom-MailReader.desktop
|
- |
|
302 |
return XDG;
|
- |
|
303 |
} else if (OSFamily.getInstance() instanceof Unix) {
|
- |
|
304 |
return XDG;
|
297 |
}
|
305 |
}
|
298 |
|
306 |
|
299 |
return MailTo;
|
307 |
return MailTo;
|
300 |
}
|
308 |
}
|
301 |
|
309 |
|
302 |
public static final EmailClient MailTo = new EmailClient(null) {
|
310 |
public static final EmailClient MailTo = new EmailClient(null) {
|
303 |
@Override
|
311 |
@Override
|
304 |
public boolean composeNative(String to, String subject, String body, File... attachments) {
|
312 |
public boolean composeNative(String to, String subject, String body, File... attachments) {
|
305 |
return false;
|
313 |
return false;
|
306 |
}
|
314 |
}
|
307 |
};
|
315 |
};
|
308 |
|
316 |
|
309 |
public static final EmailClient XDG = new EmailClient(EmailClientType.XDG) {
|
317 |
public static final EmailClient XDG = new EmailClient(EmailClientType.XDG) {
|
310 |
@Override
|
318 |
@Override
|
311 |
public boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
319 |
public boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
312 |
final ProcessBuilder pb = new ProcessBuilder("xdg-email");
|
320 |
final ProcessBuilder pb = new ProcessBuilder("xdg-email");
|
313 |
if (subject != null) {
|
321 |
if (subject != null) {
|
314 |
pb.command().add("--subject");
|
322 |
pb.command().add("--subject");
|
315 |
pb.command().add(subject);
|
323 |
pb.command().add(subject);
|
316 |
}
|
324 |
}
|
317 |
if (body != null) {
|
325 |
if (body != null) {
|
318 |
pb.command().add("--body");
|
326 |
pb.command().add("--body");
|
319 |
pb.command().add(body);
|
327 |
pb.command().add(body);
|
320 |
}
|
328 |
}
|
321 |
for (File attachment : attachments) {
|
329 |
for (File attachment : attachments) {
|
322 |
pb.command().add("--attach");
|
330 |
pb.command().add("--attach");
|
323 |
pb.command().add(attachment.getAbsolutePath());
|
331 |
pb.command().add(attachment.getAbsolutePath());
|
324 |
}
|
332 |
}
|
325 |
pb.command().add(to);
|
333 |
pb.command().add(to);
|
326 |
pb.inheritIO();
|
334 |
pb.inheritIO();
|
327 |
final Process process = pb.start();
|
335 |
final Process process = pb.start();
|
328 |
process.getOutputStream().close();
|
336 |
process.getOutputStream().close();
|
329 |
final int returnCode = process.waitFor();
|
337 |
final int returnCode = process.waitFor();
|
330 |
if (returnCode != 0)
|
338 |
if (returnCode != 0)
|
331 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
339 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
332 |
return true;
|
340 |
return true;
|
333 |
}
|
341 |
}
|
334 |
};
|
342 |
};
|
335 |
|
343 |
|
336 |
public static final EmailClient Outlook = new EmailClient(EmailClientType.Outlook) {
|
344 |
public static final EmailClient Outlook = new EmailClient(EmailClientType.Outlook) {
|
337 |
@Override
|
345 |
@Override
|
338 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
346 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
339 |
final DesktopEnvironment de = DesktopEnvironment.getDE();
|
347 |
final DesktopEnvironment de = DesktopEnvironment.getDE();
|
340 |
final File vbs = FileUtils.getFile(EmailClient.class.getResource("OutlookEmail.vbs"));
|
348 |
final File vbs = FileUtils.getFile(EmailClient.class.getResource("OutlookEmail.vbs"));
|
341 |
final List<String> l = new ArrayList<String>(6);
|
349 |
final List<String> l = new ArrayList<String>(6);
|
342 |
l.add("cscript");
|
350 |
l.add("cscript");
|
343 |
l.add(de.quoteParamForExec(vbs.getAbsolutePath()));
|
351 |
l.add(de.quoteParamForExec(vbs.getAbsolutePath()));
|
344 |
if (to != null)
|
352 |
if (to != null)
|
345 |
l.add(createVBParam("to", to));
|
353 |
l.add(createVBParam("to", to));
|
346 |
if (subject != null)
|
354 |
if (subject != null)
|
347 |
l.add(createVBParam("subject", subject));
|
355 |
l.add(createVBParam("subject", subject));
|
348 |
// at least set a parameter otherwise the usage get displayed
|
356 |
// at least set a parameter otherwise the usage get displayed
|
349 |
l.add(createVBParam("unicodeStdIn", "1"));
|
357 |
l.add(createVBParam("unicodeStdIn", "1"));
|
350 |
for (File attachment : attachments) {
|
358 |
for (File attachment : attachments) {
|
351 |
l.add(de.quoteParamForExec(attachment.getAbsolutePath()));
|
359 |
l.add(de.quoteParamForExec(attachment.getAbsolutePath()));
|
352 |
}
|
360 |
}
|
353 |
|
361 |
|
354 |
final Process process = new ProcessBuilder(l).start();
|
362 |
final Process process = new ProcessBuilder(l).start();
|
355 |
// VBScript only knows ASCII and UTF-16
|
363 |
// VBScript only knows ASCII and UTF-16
|
356 |
final Writer writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StringUtils.UTF16));
|
364 |
final Writer writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StringUtils.UTF16));
|
357 |
writer.write(body);
|
365 |
writer.write(body);
|
358 |
writer.close();
|
366 |
writer.close();
|
359 |
final int returnCode = process.waitFor();
|
367 |
final int returnCode = process.waitFor();
|
360 |
if (returnCode != 0)
|
368 |
if (returnCode != 0)
|
361 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
369 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
362 |
return true;
|
370 |
return true;
|
363 |
}
|
371 |
}
|
364 |
};
|
372 |
};
|
365 |
|
373 |
|
366 |
public static final EmailClient AppleMail = new EmailClient(EmailClientType.AppleMail) {
|
374 |
public static final EmailClient AppleMail = new EmailClient(EmailClientType.AppleMail) {
|
367 |
@Override
|
375 |
@Override
|
368 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
376 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException {
|
369 |
final Process process = Runtime.getRuntime().exec(new String[] { "osascript" });
|
377 |
final Process process = Runtime.getRuntime().exec(new String[] { "osascript" });
|
370 |
final PrintStream w = new PrintStream(new BufferedOutputStream(process.getOutputStream()));
|
378 |
final PrintStream w = new PrintStream(new BufferedOutputStream(process.getOutputStream()));
|
371 |
// use ID to handle application renaming (always a slight delay after a rename for
|
379 |
// use ID to handle application renaming (always a slight delay after a rename for
|
372 |
// this to work, though)
|
380 |
// this to work, though)
|
373 |
w.println("tell application id \"" + AppleMailBundleID + "\"");
|
381 |
w.println("tell application id \"" + AppleMailBundleID + "\"");
|
374 |
w.println(" set theMessage to make new outgoing message with properties {" + getAppleMailParam(subject, body) + "}");
|
382 |
w.println(" set theMessage to make new outgoing message with properties {" + getAppleMailParam(subject, body) + "}");
|
375 |
if (to != null)
|
383 |
if (to != null)
|
376 |
w.println(" tell theMessage to make new to recipient with properties {address:" + StringUtils.doubleQuote(to) + "}");
|
384 |
w.println(" tell theMessage to make new to recipient with properties {address:" + StringUtils.doubleQuote(to) + "}");
|
377 |
for (File attachment : attachments) {
|
385 |
for (File attachment : attachments) {
|
378 |
w.println(" tell content of theMessage to make new attachment with properties {file name:" + StringUtils.doubleQuote(attachment.getAbsolutePath()) + "} at after last paragraph");
|
386 |
w.println(" tell content of theMessage to make new attachment with properties {file name:" + StringUtils.doubleQuote(attachment.getAbsolutePath()) + "} at after last paragraph");
|
379 |
}
|
387 |
}
|
380 |
w.println("end tell");
|
388 |
w.println("end tell");
|
381 |
w.close();
|
389 |
w.close();
|
382 |
if (w.checkError())
|
390 |
if (w.checkError())
|
383 |
throw new IOException();
|
391 |
throw new IOException();
|
384 |
|
392 |
|
385 |
final int returnCode = process.waitFor();
|
393 |
final int returnCode = process.waitFor();
|
386 |
if (returnCode != 0)
|
394 |
if (returnCode != 0)
|
387 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
395 |
throw new IllegalStateException("Non zero return code: " + returnCode);
|
388 |
return true;
|
396 |
return true;
|
389 |
}
|
397 |
}
|
390 |
};
|
398 |
};
|
391 |
|
399 |
|
392 |
public static abstract class Thunderbird extends EmailClient {
|
400 |
public static abstract class Thunderbird extends EmailClient {
|
393 |
|
401 |
|
394 |
public static Thunderbird createFromExe(final File exe) {
|
402 |
public static Thunderbird createFromExe(final File exe) {
|
395 |
if (exe == null)
|
403 |
if (exe == null)
|
396 |
throw new NullPointerException();
|
404 |
throw new NullPointerException();
|
397 |
if (!exe.isFile())
|
405 |
if (!exe.isFile())
|
398 |
return null;
|
406 |
return null;
|
399 |
return new ThunderbirdPath(exe);
|
407 |
return new ThunderbirdPath(exe);
|
400 |
}
|
408 |
}
|
401 |
|
409 |
|
402 |
public static Thunderbird createFromCommandLine(final String cmdLine, final String toReplace) {
|
410 |
public static Thunderbird createFromCommandLine(final String cmdLine, final String toReplace) {
|
403 |
return new ThunderbirdCommandLine(cmdLine, toReplace);
|
411 |
return new ThunderbirdCommandLine(cmdLine, toReplace);
|
404 |
}
|
412 |
}
|
405 |
|
413 |
|
406 |
protected Thunderbird() {
|
414 |
protected Thunderbird() {
|
407 |
super(EmailClientType.Thunderbird);
|
415 |
super(EmailClientType.Thunderbird);
|
408 |
}
|
416 |
}
|
409 |
|
417 |
|
410 |
@Override
|
418 |
@Override
|
411 |
public String toString() {
|
419 |
public String toString() {
|
412 |
return this.getClass().getSimpleName();
|
420 |
return this.getClass().getSimpleName();
|
413 |
}
|
421 |
}
|
414 |
}
|
422 |
}
|
415 |
|
423 |
|
416 |
private static final class ThunderbirdCommandLine extends Thunderbird {
|
424 |
private static final class ThunderbirdCommandLine extends Thunderbird {
|
417 |
|
425 |
|
418 |
private final String cmdLine;
|
426 |
private final String cmdLine;
|
419 |
private final String toReplace;
|
427 |
private final String toReplace;
|
420 |
|
428 |
|
421 |
private ThunderbirdCommandLine(final String cmdLine, final String toReplace) {
|
429 |
private ThunderbirdCommandLine(final String cmdLine, final String toReplace) {
|
422 |
this.cmdLine = cmdLine;
|
430 |
this.cmdLine = cmdLine;
|
423 |
this.toReplace = toReplace;
|
431 |
this.toReplace = toReplace;
|
424 |
}
|
432 |
}
|
425 |
|
433 |
|
426 |
@Override
|
434 |
@Override
|
427 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException {
|
435 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException {
|
428 |
Runtime.getRuntime().exec(tbCommand(this.cmdLine, this.toReplace, to, subject, body, attachments));
|
436 |
Runtime.getRuntime().exec(tbCommand(this.cmdLine, this.toReplace, to, subject, body, attachments));
|
429 |
// don't wait for Thunderbird to quit if it wasn't launched
|
437 |
// don't wait for Thunderbird to quit if it wasn't launched
|
430 |
// (BTW return code of 1 means the program was already launched)
|
438 |
// (BTW return code of 1 means the program was already launched)
|
431 |
return true;
|
439 |
return true;
|
432 |
}
|
440 |
}
|
433 |
|
441 |
|
434 |
@Override
|
442 |
@Override
|
435 |
public String toString() {
|
443 |
public String toString() {
|
436 |
return super.toString() + " " + this.cmdLine;
|
444 |
return super.toString() + " " + this.cmdLine;
|
437 |
}
|
445 |
}
|
438 |
}
|
446 |
}
|
439 |
|
447 |
|
440 |
private static final class ThunderbirdPath extends Thunderbird {
|
448 |
private static final class ThunderbirdPath extends Thunderbird {
|
441 |
|
449 |
|
442 |
private final File exe;
|
450 |
private final File exe;
|
443 |
|
451 |
|
444 |
private ThunderbirdPath(File exe) {
|
452 |
private ThunderbirdPath(File exe) {
|
445 |
this.exe = exe;
|
453 |
this.exe = exe;
|
446 |
}
|
454 |
}
|
447 |
|
455 |
|
448 |
@Override
|
456 |
@Override
|
449 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException {
|
457 |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException {
|
450 |
final String composeArg = getTBParam(to, subject, body, attachments);
|
458 |
final String composeArg = getTBParam(to, subject, body, attachments);
|
451 |
Runtime.getRuntime().exec(new String[] { this.exe.getPath(), "-compose", composeArg });
|
459 |
Runtime.getRuntime().exec(new String[] { this.exe.getPath(), "-compose", composeArg });
|
452 |
return true;
|
460 |
return true;
|
453 |
}
|
461 |
}
|
454 |
|
462 |
|
455 |
@Override
|
463 |
@Override
|
456 |
public String toString() {
|
464 |
public String toString() {
|
457 |
return super.toString() + " " + this.exe;
|
465 |
return super.toString() + " " + this.exe;
|
458 |
}
|
466 |
}
|
459 |
}
|
467 |
}
|
460 |
|
468 |
|
461 |
private final EmailClientType type;
|
469 |
private final EmailClientType type;
|
462 |
|
470 |
|
463 |
public EmailClient(EmailClientType type) {
|
471 |
public EmailClient(EmailClientType type) {
|
464 |
this.type = type;
|
472 |
this.type = type;
|
465 |
}
|
473 |
}
|
466 |
|
474 |
|
467 |
public final EmailClientType getType() {
|
475 |
public final EmailClientType getType() {
|
468 |
return this.type;
|
476 |
return this.type;
|
469 |
}
|
477 |
}
|
470 |
|
478 |
|
471 |
protected abstract boolean composeNative(final String to, final String subject, final String body, final File... attachments) throws IOException, InterruptedException;
|
479 |
protected abstract boolean composeNative(final String to, final String subject, final String body, final File... attachments) throws IOException, InterruptedException;
|
472 |
|
480 |
|
473 |
@Override
|
481 |
@Override
|
474 |
public String toString() {
|
482 |
public String toString() {
|
475 |
final EmailClientType t = this.getType();
|
483 |
final EmailClientType t = this.getType();
|
476 |
return t == null ? "mailto" : t.toString();
|
484 |
return t == null ? "mailto" : t.toString();
|
477 |
}
|
485 |
}
|
478 |
|
486 |
|
479 |
public final static void main(String[] args) throws Exception {
|
487 |
public final static void main(String[] args) throws Exception {
|
480 |
if (args.length == 1 && "--help".equals(args[0])) {
|
488 |
if (args.length == 1 && "--help".equals(args[0])) {
|
481 |
System.out.println("Usage: java [-Dparam=value] " + EmailClient.class.getName() + " [EmailClientType args]");
|
489 |
System.out.println("Usage: java [-Dparam=value] " + EmailClient.class.getName() + " [EmailClientType args]");
|
482 |
System.out.println("\tEmailClientType: mailto or " + Arrays.asList(EmailClientType.values()));
|
490 |
System.out.println("\tEmailClientType: mailto or " + Arrays.asList(EmailClientType.values()));
|
483 |
System.out.println("\tparam: to, subject, body, files (seprated by ',' double it to escape)");
|
491 |
System.out.println("\tparam: to, subject, body, files (seprated by ',' double it to escape)");
|
484 |
return;
|
492 |
return;
|
485 |
}
|
493 |
}
|
486 |
|
494 |
|
487 |
final EmailClient client = createFromString(args);
|
495 |
final EmailClient client = createFromString(args);
|
488 |
System.out.println("Using " + (args.length == 0 ? "preferred" : "passed") + " client : " + client);
|
496 |
System.out.println("Using " + (args.length == 0 ? "preferred" : "passed") + " client : " + client);
|
489 |
final String to = System.getProperty("to", "Pierre Dupond <p.dupond@example.com>, p.dupont@server.com");
|
497 |
final String to = System.getProperty("to", "Pierre Dupond <p.dupond@example.com>, p.dupont@server.com");
|
490 |
// ',to=' to test escaping of Thunderbird (passing subject='foo'bar' works)
|
498 |
// ',to=' to test escaping of Thunderbird (passing subject='foo'bar' works)
|
491 |
final String subject = System.getProperty("subject", "Sujé € du courrier ',to='&;\\<> \"autre'\n2nd line");
|
499 |
final String subject = System.getProperty("subject", "Sujé € du courrier ',to='&;\\<> \"autre'\n2nd line");
|
492 |
final String body = System.getProperty("body", "Bonjour,\n\tsingle ' double \" backslash(arrière) \\ slash /");
|
500 |
final String body = System.getProperty("body", "Bonjour,\n\tsingle ' double \" backslash(arrière) \\ slash /");
|
493 |
final String filesPath = System.getProperty("files");
|
501 |
final String filesPath = System.getProperty("files");
|
494 |
final String[] paths = filesPath == null || filesPath.length() == 0 ? new String[0] : filesPath.split("(?<!,),(?!,)");
|
502 |
final String[] paths = filesPath == null || filesPath.length() == 0 ? new String[0] : filesPath.split("(?<!,),(?!,)");
|
495 |
final File[] f = new File[paths.length];
|
503 |
final File[] f = new File[paths.length];
|
496 |
for (int i = 0; i < f.length; i++) {
|
504 |
for (int i = 0; i < f.length; i++) {
|
497 |
f[i] = new File(paths[i].replace(",,", ","));
|
505 |
f[i] = new File(paths[i].replace(",,", ","));
|
498 |
}
|
506 |
}
|
499 |
client.compose(to, subject, body, f);
|
507 |
client.compose(to, subject, body, f);
|
500 |
}
|
508 |
}
|
501 |
|
509 |
|
502 |
private static final EmailClient createFromString(final String... args) throws IOException {
|
510 |
private static final EmailClient createFromString(final String... args) throws IOException {
|
503 |
// switch doesn't support null
|
511 |
// switch doesn't support null
|
504 |
if (args.length == 0)
|
512 |
if (args.length == 0)
|
505 |
return getPreferred();
|
513 |
return getPreferred();
|
506 |
else if ("mailto".equals(args[0]))
|
514 |
else if ("mailto".equals(args[0]))
|
507 |
return MailTo;
|
515 |
return MailTo;
|
508 |
|
516 |
|
509 |
final EmailClientType t = EmailClientType.valueOf(args[0]);
|
517 |
final EmailClientType t = EmailClientType.valueOf(args[0]);
|
510 |
switch (t) {
|
518 |
switch (t) {
|
511 |
case XDG:
|
519 |
case XDG:
|
512 |
return XDG;
|
520 |
return XDG;
|
513 |
case Outlook:
|
521 |
case Outlook:
|
514 |
return Outlook;
|
522 |
return Outlook;
|
515 |
case AppleMail:
|
523 |
case AppleMail:
|
516 |
return AppleMail;
|
524 |
return AppleMail;
|
517 |
case Thunderbird:
|
525 |
case Thunderbird:
|
518 |
EmailClient res = null;
|
526 |
EmailClient res = null;
|
519 |
if (args.length == 2) {
|
527 |
if (args.length == 2) {
|
520 |
final File exe = new File(args[1]);
|
528 |
final File exe = new File(args[1]);
|
521 |
res = Thunderbird.createFromExe(exe);
|
529 |
res = Thunderbird.createFromExe(exe);
|
522 |
if (res == null)
|
530 |
if (res == null)
|
523 |
throw new IOException("Invalid exe : " + exe);
|
531 |
throw new IOException("Invalid exe : " + exe);
|
524 |
} else if (args.length == 3) {
|
532 |
} else if (args.length == 3) {
|
525 |
res = Thunderbird.createFromCommandLine(args[1], args[2]);
|
533 |
res = Thunderbird.createFromCommandLine(args[1], args[2]);
|
526 |
} else {
|
534 |
} else {
|
527 |
throw new IllegalArgumentException(t + " needs 1 or 2 arguments");
|
535 |
throw new IllegalArgumentException(t + " needs 1 or 2 arguments");
|
528 |
}
|
536 |
}
|
529 |
return res;
|
537 |
return res;
|
530 |
default:
|
538 |
default:
|
531 |
throw new IllegalStateException("Unknown type " + t);
|
539 |
throw new IllegalStateException("Unknown type " + t);
|
532 |
}
|
540 |
}
|
533 |
}
|
541 |
}
|
534 |
}
|
542 |
}
|