Skip to content

Package: MailHandler$TailNameFormatter

MailHandler$TailNameFormatter

nameinstructionbranchcomplexitylinemethod
MailHandler.TailNameFormatter(String)
M: 13 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
equals(Object)
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
format(LogRecord)
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getTail(Handler)
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
hashCode()
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
of(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
toString()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved.
3: * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package org.eclipse.angus.mail.util.logging;
19:
20: import jakarta.activation.DataHandler;
21: import jakarta.activation.DataSource;
22: import jakarta.activation.FileTypeMap;
23: import jakarta.activation.MimetypesFileTypeMap;
24: import jakarta.mail.Address;
25: import jakarta.mail.Authenticator;
26: import jakarta.mail.BodyPart;
27: import jakarta.mail.Message;
28: import jakarta.mail.MessageContext;
29: import jakarta.mail.MessagingException;
30: import jakarta.mail.Part;
31: import jakarta.mail.PasswordAuthentication;
32: import jakarta.mail.SendFailedException;
33: import jakarta.mail.Service;
34: import jakarta.mail.Session;
35: import jakarta.mail.Transport;
36: import jakarta.mail.internet.AddressException;
37: import jakarta.mail.internet.ContentType;
38: import jakarta.mail.internet.InternetAddress;
39: import jakarta.mail.internet.MimeBodyPart;
40: import jakarta.mail.internet.MimeMessage;
41: import jakarta.mail.internet.MimeMultipart;
42: import jakarta.mail.internet.MimePart;
43: import jakarta.mail.internet.MimeUtility;
44: import jakarta.mail.util.ByteArrayDataSource;
45: import jakarta.mail.util.StreamProvider.EncoderTypes;
46:
47: import java.io.ByteArrayInputStream;
48: import java.io.ByteArrayOutputStream;
49: import java.io.IOException;
50: import java.io.OutputStreamWriter;
51: import java.io.PrintWriter;
52: import java.io.Reader;
53: import java.io.StringReader;
54: import java.io.StringWriter;
55: import java.io.UnsupportedEncodingException;
56: import java.io.Writer;
57: import java.lang.reflect.InvocationTargetException;
58: import java.net.InetAddress;
59: import java.net.URLConnection;
60: import java.net.UnknownHostException;
61: import java.nio.charset.Charset;
62: import java.security.PrivilegedAction;
63: import java.util.Arrays;
64: import java.util.Comparator;
65: import java.util.HashMap;
66: import java.util.Locale;
67: import java.util.Map;
68: import java.util.Objects;
69: import java.util.Properties;
70: import java.util.ResourceBundle;
71: import java.util.logging.ErrorManager;
72: import java.util.logging.Filter;
73: import java.util.logging.Formatter;
74: import java.util.logging.Handler;
75: import java.util.logging.Level;
76: import java.util.logging.LogRecord;
77: import java.util.logging.SimpleFormatter;
78:
79: import static org.eclipse.angus.mail.util.logging.LogManagerProperties.fromLogManager;
80:
81: /**
82: * <code>Handler</code> that formats log records as an email message.
83: *
84: * <p>
85: * This <code>Handler</code> will store a fixed number of log records used to
86: * generate a single email message. When the internal buffer reaches capacity,
87: * all log records are formatted and placed in an email which is sent to an
88: * email server. The code to manually setup this handler can be as simple as
89: * the following:
90: *
91: * <pre>
92: * Properties props = new Properties();
93: * props.put("mail.smtp.host", "my-mail-server");
94: * props.put("mail.to", "me@example.com");
95: * props.put("verify", "local");
96: * MailHandler h = new MailHandler(props);
97: * h.setLevel(Level.WARNING);
98: * </pre>
99: *
100: * <p>
101: * <b><a id="configuration">Configuration:</a></b>
102: * The LogManager should define at least one or more recipient addresses and a
103: * mail host for outgoing email. The code to setup this handler via the
104: * logging properties can be as simple as the following:
105: *
106: * <pre>
107: * #Default MailHandler settings.
108: * org.eclipse.angus.mail.util.logging.MailHandler.mail.smtp.host = my-mail-server
109: * org.eclipse.angus.mail.util.logging.MailHandler.mail.to = me@example.com
110: * org.eclipse.angus.mail.util.logging.MailHandler.level = WARNING
111: * org.eclipse.angus.mail.util.logging.MailHandler.verify = local
112: * </pre>
113: *
114: * For a custom handler, e.g. <code>com.foo.MyHandler</code>, the properties
115: * would be:
116: *
117: * <pre>
118: * #Subclass com.foo.MyHandler settings.
119: * com.foo.MyHandler.mail.smtp.host = my-mail-server
120: * com.foo.MyHandler.mail.to = me@example.com
121: * com.foo.MyHandler.level = WARNING
122: * com.foo.MyHandler.verify = local
123: * </pre>
124: *
125: * All mail <a id="top-level-properties">properties</a> documented in the
126: * <code>Jakarta Mail API</code> cascade to the LogManager by prefixing a key
127: * using the fully qualified class name of this <code>MailHandler</code> or the
128: * fully qualified derived class name dot mail property. If the prefixed
129: * property is not found, then the mail property itself is searched in the
130: * LogManager. By default each <code>MailHandler</code> is initialized using the
131: * following LogManager configuration properties where
132: * <code><handler-name></code> refers to the fully qualified class name of
133: * the handler. If properties are not defined, or contain invalid values, then
134: * the specified default values are used.
135: *
136: * <ul>
137: * <li><handler-name>.attachment.filters a comma
138: * separated list of <code>Filter</code> class names used to create each
139: * attachment. The literal <code>null</code> is reserved for attachments that
140: * do not require filtering. (defaults to the
141: * {@linkplain java.util.logging.Handler#getFilter() body} filter)
142: *
143: * <li><handler-name>.attachment.formatters a comma
144: * separated list of <code>Formatter</code> class names used to create each
145: * attachment. (default is no attachments)
146: *
147: * <li><handler-name>.attachment.names a comma separated
148: * list of names or <code>Formatter</code> class names of each attachment. All
149: * control characters are removed from the attachment names.
150: * (default is {@linkplain java.util.logging.Formatter#toString() toString}
151: * of the attachment formatter)
152: *
153: * <li><handler-name>.authenticator name of an
154: * {@linkplain jakarta.mail.Authenticator} class used to provide login credentials
155: * to the email server or string literal that is the password used with the
156: * {@linkplain Authenticator#getDefaultUserName() default} user name.
157: * (default is <code>null</code>)
158: *
159: * <li><handler-name>.capacity the max number of
160: * <code>LogRecord</code> objects include in each email message.
161: * (defaults to <code>1000</code>)
162: *
163: * <li><handler-name>.comparator name of a
164: * {@linkplain java.util.Comparator} class used to sort the published
165: * <code>LogRecord</code> objects prior to all formatting.
166: * (defaults to <code>null</code> meaning records are unsorted).
167: *
168: * <li><handler-name>.comparator.reverse a boolean
169: * <code>true</code> to reverse the order of the specified comparator or
170: * <code>false</code> to retain the original order.
171: * (defaults to <code>false</code>)
172: *
173: * <li><handler-name>.enabled a boolean
174: * <code>true</code> to allow this handler to accept records or
175: * <code>false</code> to turn off this handler.
176: * (defaults to <code>true</code>)
177: *
178: * <li><handler-name>.encoding the name of the Java
179: * {@linkplain java.nio.charset.Charset#name() character set} to use for the
180: * email message. (defaults to <code>null</code>, the
181: * {@linkplain jakarta.mail.internet.MimeUtility#getDefaultJavaCharset() default}
182: * platform encoding).
183: *
184: * <li><handler-name>.errorManager name of an
185: * <code>ErrorManager</code> class used to handle any configuration or mail
186: * transport problems. (defaults to <code>java.util.logging.ErrorManager</code>)
187: *
188: * <li><handler-name>.filter name of a <code>Filter</code>
189: * class used for the body of the message. (defaults to <code>null</code>,
190: * allow all records)
191: *
192: * <li><handler-name>.formatter name of a
193: * <code>Formatter</code> class used to format the body of this message.
194: * (defaults to <code>java.util.logging.SimpleFormatter</code>)
195: *
196: * <li><handler-name>.level specifies the default level
197: * for this <code>Handler</code> (defaults to <code>Level.WARNING</code>).
198: *
199: * <li><handler-name>.mail.bcc a comma separated list of
200: * addresses which will be blind carbon copied. Typically, this is set to the
201: * recipients that may need to be privately notified of a log message or
202: * notified that a log message was sent to a third party such as a support team.
203: * The empty string can be used to specify no blind carbon copied address.
204: * (defaults to <code>null</code>, none)
205: *
206: * <li><handler-name>.mail.cc a comma separated list of
207: * addresses which will be carbon copied. Typically, this is set to the
208: * recipients that may need to be notified of a log message but, are not
209: * required to provide direct support. The empty string can be used to specify
210: * no carbon copied address. (defaults to <code>null</code>, none)
211: *
212: * <li><handler-name>.mail.from a comma separated list of
213: * addresses which will be from addresses. Typically, this is set to the email
214: * address identifying the user running the application. The empty string can
215: * be used to override the default behavior and specify no from address.
216: * (defaults to the {@linkplain jakarta.mail.Message#setFrom() local address})
217: *
218: * <li><handler-name>.mail.host the host name or IP
219: * address of the email server. (defaults to <code>null</code>, use
220: * {@linkplain Transport#protocolConnect default}
221: * <code>Java Mail</code> behavior)
222: *
223: * <li><handler-name>.mail.reply.to a comma separated
224: * list of addresses which will be reply-to addresses. Typically, this is set
225: * to the recipients that provide support for the application itself. The empty
226: * string can be used to specify no reply-to address.
227: * (defaults to <code>null</code>, none)
228: *
229: * <li><handler-name>.mail.to a comma separated list of
230: * addresses which will be send-to addresses. Typically, this is set to the
231: * recipients that provide support for the application, system, and/or
232: * supporting infrastructure. The empty string can be used to specify no
233: * send-to address which overrides the default behavior. (defaults to
234: * {@linkplain jakarta.mail.internet.InternetAddress#getLocalAddress
235: * local address}.)
236: *
237: * <li><handler-name>.mail.sender a single address
238: * identifying sender of the email; never equal to the from address. Typically,
239: * this is set to the email address identifying the application itself. The
240: * empty string can be used to specify no sender address.
241: * (defaults to <code>null</code>, none)
242: *
243: * <li><handler-name>.mailEntries specifies the mail session properties
244: * for this <code>Handler</code>. The format for the value is described in
245: * {@linkplain #setMailEntries(java.lang.String) setMailEntries} method.
246: * This property eagerly loads the assigned mail properties where as the
247: * <a href="#top-level-properties">top level mail properties</a> are lazily
248: * loaded. Prefer using this property when <a href="#verify">verification</a>
249: * is off or when verification does not force the provider to load required
250: * mail properties. (defaults to <code>null</code>).
251: *
252: * <li><handler-name>.subject the name of a
253: * <code>Formatter</code> class or string literal used to create the subject
254: * line. The empty string can be used to specify no subject. All control
255: * characters are removed from the subject line. (defaults to {@linkplain
256: * CollectorFormatter CollectorFormatter}.)
257: *
258: * <li><handler-name>.pushFilter the name of a
259: * <code>Filter</code> class used to trigger an early push.
260: * (defaults to <code>null</code>, no early push)
261: *
262: * <li><handler-name>.pushLevel the level which will
263: * trigger an early push. (defaults to <code>Level.OFF</code>, only push when
264: * full)
265: *
266: * <li><handler-name>.verify <a id="verify">used</a> to
267: * verify the <code>Handler</code> configuration prior to a push.
268: * <ul>
269: * <li>If the value is not set, equal to an empty string, or equal to the
270: * literal <code>null</code> then no settings are verified prior to a push.
271: * <li>If set to a value of <code>limited</code> then the
272: * <code>Handler</code> will verify minimal local machine settings.
273: * <li>If set to a value of <code>local</code> the <code>Handler</code>
274: * will verify all of settings of the local machine.
275: * <li>If set to a value of <code>resolve</code>, the <code>Handler</code>
276: * will verify all local settings and try to resolve the remote host name
277: * with the domain name server.
278: * <li>If set to a value of <code>login</code>, the <code>Handler</code>
279: * will verify all local settings and try to establish a connection with
280: * the email server.
281: * <li>If set to a value of <code>remote</code>, the <code>Handler</code>
282: * will verify all local settings, try to establish a connection with the
283: * email server, and try to verify the envelope of the email message.
284: * </ul>
285: * If this <code>Handler</code> is only implicitly closed by the
286: * <code>LogManager</code>, then verification should be turned on.
287: * (defaults to <code>null</code>, no verify).
288: * </ul>
289: *
290: * <p>
291: * <b>Normalization:</b>
292: * The error manager, filters, and formatters when loaded from the LogManager
293: * are converted into canonical form inside the MailHandler. The pool of
294: * interned values is limited to each MailHandler object such that no two
295: * MailHandler objects created by the LogManager will be created sharing
296: * identical error managers, filters, or formatters. If a filter or formatter
297: * should <b>not</b> be interned then it is recommended to retain the identity
298: * equals and identity hashCode methods as the implementation. For a filter or
299: * formatter to be interned the class must implement the
300: * {@linkplain java.lang.Object#equals(java.lang.Object) equals}
301: * and {@linkplain java.lang.Object#hashCode() hashCode} methods.
302: * The recommended code to use for stateless filters and formatters is:
303: * <pre>
304: * public boolean equals(Object obj) {
305: * return obj == null ? false : obj.getClass() == getClass();
306: * }
307: *
308: * public int hashCode() {
309: * return 31 * getClass().hashCode();
310: * }
311: * </pre>
312: *
313: * <p>
314: * <b>Sorting:</b>
315: * All <code>LogRecord</code> objects are ordered prior to formatting if this
316: * <code>Handler</code> has a non null comparator. Developers might be
317: * interested in sorting the formatted email by thread id, time, and sequence
318: * properties of a <code>LogRecord</code>. Where as system administrators might
319: * be interested in sorting the formatted email by thrown, level, time, and
320: * sequence properties of a <code>LogRecord</code>. If comparator for this
321: * handler is <code>null</code> then the order is unspecified.
322: *
323: * <p>
324: * <b>Formatting:</b>
325: * The main message body is formatted using the <code>Formatter</code> returned
326: * by <code>getFormatter()</code>. Only records that pass the filter returned
327: * by <code>getFilter()</code> will be included in the message body. The
328: * subject <code>Formatter</code> will see all <code>LogRecord</code> objects
329: * that were published regardless of the current <code>Filter</code>. The MIME
330: * type of the message body can be
331: * {@linkplain FileTypeMap#setDefaultFileTypeMap overridden}
332: * by adding a MIME {@linkplain MimetypesFileTypeMap entry} using the simple
333: * class name of the body formatter as the file extension. The MIME type of the
334: * attachments can be overridden by changing the attachment file name extension
335: * or by editing the default MIME entry for a specific file name extension.
336: *
337: * <p>
338: * <b>Attachments:</b>
339: * This <code>Handler</code> allows multiple attachments per each email message.
340: * The presence of an attachment formatter will change the content type of the
341: * email message to a multi-part message. The attachment order maps directly to
342: * the array index order in this <code>Handler</code> with zero index being the
343: * first attachment. The number of attachment formatters controls the number of
344: * attachments per email and the content type of each attachment. The
345: * attachment filters determine if a <code>LogRecord</code> will be included in
346: * an attachment. If an attachment filter is <code>null</code> then all records
347: * are included for that attachment. Attachments without content will be
348: * omitted from email message. The attachment name formatters create the file
349: * name for an attachment. Custom attachment name formatters can be used to
350: * generate an attachment name based on the contents of the attachment.
351: *
352: * <p>
353: * <b>Push Level and Push Filter:</b>
354: * The push method, push level, and optional push filter can be used to
355: * conditionally trigger a push at or prior to full capacity. When a push
356: * occurs, the current buffer is formatted into an email and is sent to the
357: * email server. If the push method, push level, or push filter trigger a push
358: * then the outgoing email is flagged as high importance with urgent priority.
359: *
360: * <p>
361: * <b>Buffering:</b>
362: * Log records that are published are stored in an internal buffer. When this
363: * buffer reaches capacity the existing records are formatted and sent in an
364: * email. Any published records can be sent before reaching capacity by
365: * explictly calling the <code>flush</code>, <code>push</code>, or
366: * <code>close</code> methods. If a circular buffer is required then this
367: * handler can be wrapped with a {@linkplain java.util.logging.MemoryHandler}
368: * typically with an equivalent capacity, level, and push level.
369: *
370: * <p>
371: * <b>Error Handling:</b>
372: * If the transport of an email message fails, the email is converted to
373: * a {@linkplain jakarta.mail.internet.MimeMessage#writeTo raw}
374: * {@linkplain java.io.ByteArrayOutputStream#toString(java.lang.String) string}
375: * and is then passed as the <code>msg</code> parameter to
376: * {@linkplain Handler#reportError reportError} along with the exception
377: * describing the cause of the failure. This allows custom error managers to
378: * store, {@linkplain jakarta.mail.internet.MimeMessage#MimeMessage(
379: *jakarta.mail.Session, java.io.InputStream) reconstruct}, and resend the
380: * original MimeMessage. The message parameter string is <b>not</b> a raw email
381: * if it starts with value returned from <code>Level.SEVERE.getName()</code>.
382: * Custom error managers can use the following test to determine if the
383: * <code>msg</code> parameter from this handler is a raw email:
384: *
385: * <pre>
386: * public void error(String msg, Exception ex, int code) {
387: * if (msg == null || msg.length() == 0 || msg.startsWith(Level.SEVERE.getName())) {
388: * super.error(msg, ex, code);
389: * } else {
390: * //The 'msg' parameter is a raw email.
391: * }
392: * }
393: * </pre>
394: *
395: * @author Jason Mehrens
396: * @since JavaMail 1.4.3
397: */
398: public class MailHandler extends Handler {
399: /**
400: * Use the emptyFilterArray method.
401: */
402: private static final Filter[] EMPTY_FILTERS = new Filter[0];
403: /**
404: * Use the emptyFormatterArray method.
405: */
406: private static final Formatter[] EMPTY_FORMATTERS = new Formatter[0];
407: /**
408: * Min byte size for header data. Used for initial arrays sizing.
409: */
410: private static final int MIN_HEADER_SIZE = 1024;
411: /**
412: * Cache the off value.
413: */
414: private static final int offValue = Level.OFF.intValue();
415: /**
416: * The action to set the context class loader for use with the Jakarta Mail API.
417: * Load and pin this before it is loaded in the close method. The field is
418: * declared as java.security.PrivilegedAction so
419: * WebappClassLoader.clearReferencesStaticFinal() method will ignore this
420: * field.
421: */
422: private static final PrivilegedAction<Object> MAILHANDLER_LOADER
423: = new GetAndSetContext(MailHandler.class);
424: /**
425: * A thread local mutex used to prevent logging loops. This code has to be
426: * prepared to deal with unexpected null values since the
427: * WebappClassLoader.clearReferencesThreadLocals() and
428: * InnocuousThread.eraseThreadLocals() can remove thread local values.
429: * The MUTEX has 5 states:
430: * 1. A null value meaning default state of not publishing.
431: * 2. MUTEX_PUBLISH on first entry of a push or publish.
432: * 3. The index of the first filter to accept a log record.
433: * 4. MUTEX_REPORT when cycle of records is detected.
434: * 5. MUTEXT_LINKAGE when a linkage error is reported.
435: */
436: private static final ThreadLocal<Integer> MUTEX = new ThreadLocal<>();
437: /**
438: * The marker object used to report a publishing state.
439: * This must be less than the body filter index (-1).
440: */
441: private static final Integer MUTEX_PUBLISH = -2;
442: /**
443: * The used for the error reporting state.
444: * This must be less than the PUBLISH state.
445: */
446: private static final Integer MUTEX_REPORT = -4;
447: /**
448: * The used for linkage error reporting.
449: * This must be less than the REPORT state.
450: */
451: private static final Integer MUTEX_LINKAGE = -8;
452: /**
453: * Used to turn off security checks.
454: */
455: private volatile boolean sealed;
456: /**
457: * Determines if we are inside of a push.
458: * Makes the handler properties read-only during a push.
459: */
460: private boolean isWriting;
461: /**
462: * Holds all of the email server properties.
463: */
464: private Properties mailProps;
465: /**
466: * Holds the authenticator required to login to the email server.
467: */
468: private Authenticator auth;
469: /**
470: * Holds the session object used to generate emails.
471: * Sessions can be shared by multiple threads.
472: * See JDK-6228391 and K 6278.
473: */
474: private Session session;
475: /**
476: * A mapping of log record to matching filter index. Negative one is used
477: * to track the body filter. Zero and greater is used to track the
478: * attachment parts. All indexes less than or equal to the matched value
479: * have already seen the given log record.
480: */
481: private int[] matched;
482: /**
483: * Holds all of the log records that will be used to create the email.
484: */
485: private LogRecord[] data;
486: /**
487: * The number of log records in the buffer.
488: */
489: private int size;
490: /**
491: * The maximum number of log records to format per email.
492: * Used to roughly bound the size of an email.
493: * Every time the capacity is reached, the handler will push.
494: * The capacity will be negative if this handler is closed.
495: * Negative values are used to ensure all records are pushed.
496: */
497: private int capacity;
498: /**
499: * The level recorded at the time the handler was disabled.
500: * Null means enabled and non-null is disabled.
501: */
502: private Level disabledLevel;
503: /**
504: * Used to order all log records prior to formatting. The main email body
505: * and all attachments use the order determined by this comparator. If no
506: * comparator is present the log records will be in no specified order.
507: */
508: private Comparator<? super LogRecord> comparator;
509: /**
510: * Holds the formatter used to create the subject line of the email.
511: * A subject formatter is not required for the email message.
512: * All published records pass through the subject formatter.
513: */
514: private Formatter subjectFormatter;
515: /**
516: * Holds the push level for this handler.
517: * This is only required if an email must be sent prior to shutdown
518: * or before the buffer is full.
519: */
520: private Level pushLevel;
521: /**
522: * Holds the push filter for trigger conditions requiring an early push.
523: * Only gets called if the given log record is greater than or equal
524: * to the push level and the push level is not Level.OFF.
525: */
526: private Filter pushFilter;
527: /**
528: * Holds the entry and body filter for this handler.
529: * There is no way to un-seal the super handler.
530: */
531: private volatile Filter filter;
532: /**
533: * Holds the level for this handler.
534: * There is no way to un-seal the super handler.
535: */
536: private volatile Level logLevel = Level.ALL;
537: /**
538: * Holds the filters for each attachment. Filters are optional for
539: * each attachment. This is declared volatile because this is treated as
540: * copy-on-write. The VO_VOLATILE_REFERENCE_TO_ARRAY warning is a false
541: * positive.
542: */
543: @SuppressWarnings("VolatileArrayField")
544: private volatile Filter[] attachmentFilters;
545: /**
546: * Holds the encoding name for this handler.
547: * There is no way to un-seal the super handler.
548: */
549: private String encoding;
550: /**
551: * Holds the entry and body filter for this handler.
552: * There is no way to un-seal the super handler.
553: */
554: private Formatter formatter;
555: /**
556: * Holds the formatters that create the content for each attachment.
557: * Each formatter maps directly to an attachment. The formatters
558: * getHead, format, and getTail methods are only called if one or more
559: * log records pass through the attachment filters.
560: */
561: private Formatter[] attachmentFormatters;
562: /**
563: * Holds the formatters that create the file name for each attachment.
564: * Each formatter must produce a non null and non empty name.
565: * The final file name will be the concatenation of one getHead call, plus
566: * all of the format calls, plus one getTail call.
567: */
568: private Formatter[] attachmentNames;
569: /**
570: * Used to override the content type for the body and set the content type
571: * for each attachment.
572: */
573: private FileTypeMap contentTypes;
574: /**
575: * Holds the error manager for this handler.
576: * There is no way to un-seal the super handler.
577: */
578: private volatile ErrorManager errorManager = defaultErrorManager();
579:
580: /**
581: * Creates a <code>MailHandler</code> that is configured by the
582: * <code>LogManager</code> configuration properties.
583: *
584: * @throws SecurityException if a security manager exists and the
585: * caller does not have <code>LoggingPermission("control")</code>.
586: */
587: public MailHandler() {
588: init((Properties) null);
589: }
590:
591: /**
592: * Creates a <code>MailHandler</code> that is configured by the
593: * <code>LogManager</code> configuration properties but overrides the
594: * <code>LogManager</code> capacity with the given capacity.
595: *
596: * @param capacity of the internal buffer.
597: * @throws SecurityException if a security manager exists and the
598: * caller does not have <code>LoggingPermission("control")</code>.
599: */
600: public MailHandler(final int capacity) {
601: init((Properties) null);
602: setCapacity0(capacity);
603: }
604:
605: /**
606: * Creates a mail handler with the given mail properties.
607: * The key/value pairs are defined in the <code>Java Mail API</code>
608: * documentation. This <code>Handler</code> will also search the
609: * <code>LogManager</code> for defaults if needed.
610: *
611: * @param props a properties object or null. A null value will supply the
612: * <code>mailEntries</code> from the <code>LogManager</code>.
613: * @throws SecurityException if a security manager exists and the
614: * caller does not have <code>LoggingPermission("control")</code>.
615: */
616: public MailHandler(Properties props) {
617: init(props); //Must pass null or original object
618: }
619:
620: /**
621: * Check if this <code>Handler</code> would actually log a given
622: * <code>LogRecord</code> into its internal buffer.
623: * <p>
624: * This method checks if the <code>LogRecord</code> has an appropriate level
625: * and whether it satisfies any <code>Filter</code> including any
626: * attachment filters.
627: * However it does <b>not</b> check whether the <code>LogRecord</code> would
628: * result in a "push" of the buffer contents.
629: *
630: * @param record a <code>LogRecord</code> or null.
631: * @return true if the <code>LogRecord</code> would be logged.
632: */
633: @Override
634: public boolean isLoggable(final LogRecord record) {
635: if (record == null) { //JDK-8233979
636: return false;
637: }
638:
639: int levelValue = getLevel().intValue();
640: if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
641: return false;
642: }
643:
644: Filter body = getFilter();
645: if (body == null || body.isLoggable(record)) {
646: setMatchedPart(-1);
647: return true;
648: }
649:
650: return isAttachmentLoggable(record);
651: }
652:
653: /**
654: * Stores a <code>LogRecord</code> in the internal buffer.
655: * <p>
656: * The <code>isLoggable</code> method is called to check if the given log
657: * record is loggable. If the given record is loggable, it is copied into
658: * an internal buffer. Then the record's level property is compared with
659: * the push level. If the given level of the <code>LogRecord</code>
660: * is greater than or equal to the push level then the push filter is
661: * called. If no push filter exists, the push filter returns true,
662: * or the capacity of the internal buffer has been reached then all buffered
663: * records are formatted into one email and sent to the server.
664: *
665: * @param record description of the log event or null.
666: */
667: @Override
668: public void publish(final LogRecord record) {
669: /**
670: * It is possible for the handler to be closed after the
671: * call to isLoggable. In that case, the current thread
672: * will push to ensure that all published records are sent.
673: * See close().
674: */
675:
676: if (tryMutex()) {
677: try {
678: if (isLoggable(record)) {
679: if (record != null) {
680: record.getSourceMethodName(); //Infer caller.
681: publish0(record);
682: } else { //Override of isLoggable is broken.
683: reportNullError(ErrorManager.WRITE_FAILURE);
684: }
685: }
686: } catch (final LinkageError JDK8152515) {
687: reportLinkageError(JDK8152515, ErrorManager.WRITE_FAILURE);
688: } finally {
689: releaseMutex();
690: }
691: } else {
692: reportUnPublishedError(record);
693: }
694: }
695:
696: /**
697: * Performs the publish after the record has been filtered.
698: *
699: * @param record the record which must not be null.
700: * @since JavaMail 1.4.5
701: */
702: private void publish0(final LogRecord record) {
703: Message msg;
704: boolean priority;
705: synchronized (this) {
706: if (size == data.length && size < capacity) {
707: grow();
708: }
709:
710: if (size < data.length) {
711: //assert data.length == matched.length;
712: matched[size] = getMatchedPart();
713: data[size] = record;
714: ++size; //Be nice to client compiler.
715: priority = isPushable(record);
716: if (priority || size >= capacity) {
717: msg = writeLogRecords(ErrorManager.WRITE_FAILURE);
718: } else {
719: msg = null;
720: }
721: } else {
722: priority = false;
723: msg = null;
724: }
725: }
726:
727: if (msg != null) {
728: send(msg, priority, ErrorManager.WRITE_FAILURE);
729: }
730: }
731:
732: /**
733: * Report to the error manager that a logging loop was detected and
734: * we are going to break the cycle of messages. It is possible that
735: * a custom error manager could continue the cycle in which case
736: * we will stop trying to report errors.
737: *
738: * @param record the record or null.
739: * @since JavaMail 1.4.6
740: */
741: private void reportUnPublishedError(LogRecord record) {
742: final Integer idx = MUTEX.get();
743: if (idx == null || idx > MUTEX_REPORT) {
744: MUTEX.set(MUTEX_REPORT);
745: try {
746: final String msg;
747: if (record != null) {
748: final Formatter f = createSimpleFormatter();
749: msg = "Log record " + record.getSequenceNumber()
750: + " was not published. "
751: + head(f) + format(f, record) + tail(f, "");
752: } else {
753: msg = null;
754: }
755: Exception e = new IllegalStateException(
756: "Recursive publish detected by thread "
757: + Thread.currentThread());
758: reportError(msg, e, ErrorManager.WRITE_FAILURE);
759: } finally {
760: if (idx != null) {
761: MUTEX.set(idx);
762: } else {
763: MUTEX.remove();
764: }
765: }
766: }
767: }
768:
769: /**
770: * Used to detect reentrance by the current thread to the publish method.
771: * This mutex is thread local scope and will not block other threads.
772: * The state is advanced on if the current thread is in a reset state.
773: *
774: * @return true if the mutex was acquired.
775: * @since JavaMail 1.4.6
776: */
777: private boolean tryMutex() {
778: if (MUTEX.get() == null) {
779: MUTEX.set(MUTEX_PUBLISH);
780: return true;
781: } else {
782: return false;
783: }
784: }
785:
786: /**
787: * Releases the mutex held by the current thread.
788: * This mutex is thread local scope and will not block other threads.
789: *
790: * @since JavaMail 1.4.6
791: */
792: private void releaseMutex() {
793: MUTEX.remove();
794: }
795:
796: /**
797: * This is used to get the filter index from when {@code isLoggable} and
798: * {@code isAttachmentLoggable} was invoked by {@code publish} method.
799: *
800: * @return the filter index or MUTEX_PUBLISH if unknown.
801: * @throws NullPointerException if tryMutex was not called.
802: * @since JavaMail 1.5.5
803: */
804: private int getMatchedPart() {
805: //assert Thread.holdsLock(this);
806: Integer idx = MUTEX.get();
807: if (idx == null || idx >= readOnlyAttachmentFilters().length) {
808: idx = MUTEX_PUBLISH;
809: }
810: return idx;
811: }
812:
813: /**
814: * This is used to record the filter index when {@code isLoggable} and
815: * {@code isAttachmentLoggable} was invoked by {@code publish} method.
816: *
817: * @param index the filter index.
818: * @since JavaMail 1.5.5
819: */
820: private void setMatchedPart(int index) {
821: if (MUTEX_PUBLISH.equals(MUTEX.get())) {
822: MUTEX.set(index);
823: }
824: }
825:
826: /**
827: * Clear previous matches when the filters are modified and there are
828: * existing log records that were matched.
829: *
830: * @param index the lowest filter index to clear.
831: * @since JavaMail 1.5.5
832: */
833: private void clearMatches(int index) {
834: assert Thread.holdsLock(this);
835: for (int r = 0; r < size; ++r) {
836: if (matched[r] >= index) {
837: matched[r] = MUTEX_PUBLISH;
838: }
839: }
840: }
841:
842: /**
843: * A callback method for when this object is about to be placed into
844: * commission. This contract is defined by the
845: * {@code org.glassfish.hk2.api.PostConstruct} interface. If this class is
846: * loaded via a lifecycle managed environment other than HK2 then it is
847: * recommended that this method is called either directly or through
848: * extending this class to signal that this object is ready for use.
849: *
850: * @since JavaMail 1.5.3
851: */
852: //@javax.annotation.PostConstruct
853: public void postConstruct() {
854: }
855:
856: /**
857: * A callback method for when this object is about to be decommissioned.
858: * This contract is defined by the {@code org.glassfish.hk2.api.PreDestory}
859: * interface. If this class is loaded via a lifecycle managed environment
860: * other than HK2 then it is recommended that this method is called either
861: * directly or through extending this class to signal that this object will
862: * be destroyed.
863: *
864: * @since JavaMail 1.5.3
865: */
866: //@javax.annotation.PreDestroy
867: public void preDestroy() {
868: /**
869: * Close can require permissions so just trigger a push.
870: */
871: push(false, ErrorManager.CLOSE_FAILURE);
872: }
873:
874: /**
875: * Pushes any buffered records to the email server as high importance with
876: * urgent priority. The internal buffer is then cleared. Does nothing if
877: * called from inside a push.
878: *
879: * @see #flush()
880: */
881: public void push() {
882: push(true, ErrorManager.FLUSH_FAILURE);
883: }
884:
885: /**
886: * Pushes any buffered records to the email server as normal priority.
887: * The internal buffer is then cleared. Does nothing if called from inside
888: * a push.
889: *
890: * @see #push()
891: */
892: @Override
893: public void flush() {
894: push(false, ErrorManager.FLUSH_FAILURE);
895: }
896:
897: /**
898: * Prevents any other records from being published.
899: * Pushes any buffered records to the email server as normal priority.
900: * The internal buffer is then cleared. Once this handler is closed it
901: * will remain closed.
902: * <p>
903: * If this <code>Handler</code> is only implicitly closed by the
904: * <code>LogManager</code>, then <a href="#verify">verification</a> should
905: * be turned on and or <code>mailEntries</code> should be declared to define
906: * the mail properties.
907: *
908: * @throws SecurityException if a security manager exists and the
909: * caller does not have <code>LoggingPermission("control")</code>.
910: * @see #flush()
911: */
912: @Override
913: public void close() {
914: try {
915: checkAccess(); //Ensure setLevel works before clearing the buffer.
916: Message msg = null;
917: synchronized (this) {
918: try {
919: msg = writeLogRecords(ErrorManager.CLOSE_FAILURE);
920: } finally { //Change level after formatting.
921: this.logLevel = Level.OFF;
922: this.disabledLevel = null; //free reference
923: /**
924: * The sign bit of the capacity is set to ensure that
925: * records that have passed isLoggable, but have yet to be
926: * added to the internal buffer, are immediately pushed as
927: * an email.
928: */
929: if (this.capacity > 0) {
930: this.capacity = -this.capacity;
931: }
932:
933: //Ensure not inside a push.
934: if (size == 0 && data.length != 1) {
935: this.data = new LogRecord[1];
936: this.matched = new int[this.data.length];
937: }
938: }
939: }
940:
941: if (msg != null) {
942: send(msg, false, ErrorManager.CLOSE_FAILURE);
943: }
944: } catch (final LinkageError JDK8152515) {
945: reportLinkageError(JDK8152515, ErrorManager.CLOSE_FAILURE);
946: }
947: }
948:
949: /**
950: * Gets the enabled status of this handler.
951: *
952: * @return true if this handler is accepting log records.
953: * @see #setEnabled(boolean)
954: * @see #setLevel(java.util.logging.Level)
955: * @since Angus Mail 2.0.3
956: */
957: public boolean isEnabled() {
958: return this.logLevel.intValue() != offValue; //Volatile read
959: }
960:
961: /**
962: * Used to enable or disable this handler.
963: *
964: * Pushes any buffered records to the email server as normal priority.
965: * The internal buffer is then cleared.
966: *
967: * @param enabled true to enable and false to disable.
968: * @throws SecurityException if a security manager exists and if the caller
969: * does not have <code>LoggingPermission("control")</code>.
970: * @see #flush()
971: * @see #isEnabled()
972: * @since Angus Mail 2.0.3
973: */
974: public void setEnabled(final boolean enabled) {
975: checkAccess();
976: setEnabled0(enabled);
977: }
978:
979: /**
980: * Used to enable or disable this handler.
981: *
982: * Pushes any buffered records to the email server as normal priority.
983: * The internal buffer is then cleared.
984: *
985: * @param enabled true to enable and false to disable.
986: * @since Angus Mail 2.0.3
987: */
988: private synchronized void setEnabled0(final boolean enabled) {
989: if (this.capacity > 0) { //handler is open
990: this.push(false, ErrorManager.FLUSH_FAILURE);
991: if (enabled) {
992: if (disabledLevel != null) { //was disabled
993: this.logLevel = this.disabledLevel;
994: this.disabledLevel = null;
995: }
996: } else {
997: if (disabledLevel == null) {
998: this.disabledLevel = this.logLevel;
999: this.logLevel = Level.OFF;
1000: }
1001: }
1002: }
1003: }
1004:
1005: /**
1006: * Set the log level specifying which message levels will be
1007: * logged by this <code>Handler</code>. Message levels lower than this
1008: * value will be discarded.
1009: *
1010: * @param newLevel the new value for the log level
1011: * @throws NullPointerException if <code>newLevel</code> is
1012: * <code>null</code>.
1013: * @throws SecurityException if a security manager exists and
1014: * the caller does not have
1015: * <code>LoggingPermission("control")</code>.
1016: */
1017: @Override
1018: public void setLevel(final Level newLevel) {
1019: Objects.requireNonNull(newLevel);
1020: checkAccess();
1021:
1022: //Don't allow a closed handler to be opened (half way).
1023: synchronized (this) { //Wait for writeLogRecords.
1024: if (this.capacity > 0) {
1025: //if disabled then track the new level to be used when enabled.
1026: if (this.disabledLevel != null) {
1027: this.disabledLevel = newLevel;
1028: } else {
1029: this.logLevel = newLevel;
1030: }
1031: }
1032: }
1033: }
1034:
1035: /**
1036: * Get the log level specifying which messages will be logged by this
1037: * <code>Handler</code>. Message levels lower than this level will be
1038: * discarded.
1039: *
1040: * @return the level of messages being logged.
1041: */
1042: @Override
1043: public Level getLevel() {
1044: return logLevel; //Volatile access.
1045: }
1046:
1047: /**
1048: * Retrieves the ErrorManager for this Handler.
1049: *
1050: * @return the ErrorManager for this Handler
1051: * @throws SecurityException if a security manager exists and if the caller
1052: * does not have <code>LoggingPermission("control")</code>.
1053: */
1054: @Override
1055: public ErrorManager getErrorManager() {
1056: checkAccess();
1057: return this.errorManager; //Volatile access.
1058: }
1059:
1060: /**
1061: * Define an ErrorManager for this Handler.
1062: * <p>
1063: * The ErrorManager's "error" method will be invoked if any errors occur
1064: * while using this Handler.
1065: *
1066: * @param em the new ErrorManager
1067: * @throws SecurityException if a security manager exists and if the
1068: * caller does not have <code>LoggingPermission("control")</code>.
1069: * @throws NullPointerException if the given error manager is null.
1070: */
1071: @Override
1072: public void setErrorManager(final ErrorManager em) {
1073: checkAccess();
1074: setErrorManager0(em);
1075: }
1076:
1077: /**
1078: * Sets the error manager on this handler and the super handler. In secure
1079: * environments the super call may not be allowed which is not a failure
1080: * condition as it is an attempt to free the unused handler error manager.
1081: *
1082: * @param em a non null error manager.
1083: * @throws NullPointerException if the given error manager is null.
1084: * @since JavaMail 1.5.6
1085: */
1086: private void setErrorManager0(final ErrorManager em) {
1087: Objects.requireNonNull(em);
1088: try {
1089: synchronized (this) { //Wait for writeLogRecords.
1090: this.errorManager = em;
1091: super.setErrorManager(em); //Try to free super error manager.
1092: }
1093: } catch (RuntimeException | LinkageError ignore) {
1094: }
1095: }
1096:
1097: /**
1098: * Get the current <code>Filter</code> for this <code>Handler</code>.
1099: *
1100: * @return a <code>Filter</code> object (may be null)
1101: */
1102: @Override
1103: public Filter getFilter() {
1104: return this.filter; //Volatile access.
1105: }
1106:
1107: /**
1108: * Set a <code>Filter</code> to control output on this <code>Handler</code>.
1109: * <P>
1110: * For each call of <code>publish</code> the <code>Handler</code> will call
1111: * this <code>Filter</code> (if it is non-null) to check if the
1112: * <code>LogRecord</code> should be published or discarded.
1113: *
1114: * @param newFilter a <code>Filter</code> object (may be null)
1115: * @throws SecurityException if a security manager exists and if the caller
1116: * does not have <code>LoggingPermission("control")</code>.
1117: */
1118: @Override
1119: public void setFilter(final Filter newFilter) {
1120: checkAccess();
1121: synchronized (this) { //Wait for writeLogRecords.
1122: if (newFilter != filter) {
1123: clearMatches(-1);
1124: }
1125: this.filter = newFilter; //Volatile access.
1126: }
1127: }
1128:
1129: /**
1130: * Return the character encoding for this <code>Handler</code>.
1131: *
1132: * @return The encoding name. May be null, which indicates the default
1133: * encoding should be used.
1134: */
1135: @Override
1136: public synchronized String getEncoding() {
1137: return this.encoding;
1138: }
1139:
1140: /**
1141: * Set the character encoding used by this <code>Handler</code>.
1142: * <p>
1143: * The encoding should be set before any <code>LogRecords</code> are written
1144: * to the <code>Handler</code>.
1145: *
1146: * @param encoding The name of a supported character encoding. May be
1147: * null, to indicate the default platform encoding.
1148: * @throws SecurityException if a security manager exists and if the caller
1149: * does not have <code>LoggingPermission("control")</code>.
1150: * @throws UnsupportedEncodingException if the named encoding is not
1151: * supported.
1152: */
1153: @Override
1154: public void setEncoding(String encoding) throws UnsupportedEncodingException {
1155: checkAccess();
1156: setEncoding0(encoding);
1157: }
1158:
1159: /**
1160: * Set the character encoding used by this handler. This method does not
1161: * check permissions of the caller.
1162: *
1163: * @param e any encoding name or null for the default.
1164: * @throws UnsupportedEncodingException if the given encoding is not supported.
1165: */
1166: private void setEncoding0(String e) throws UnsupportedEncodingException {
1167: if (e != null) {
1168: try {
1169: if (!java.nio.charset.Charset.isSupported(e)) {
1170: throw new UnsupportedEncodingException(e);
1171: }
1172: } catch (java.nio.charset.IllegalCharsetNameException icne) {
1173: throw new UnsupportedEncodingException(e);
1174: }
1175: }
1176:
1177: synchronized (this) { //Wait for writeLogRecords.
1178: this.encoding = e;
1179: }
1180: }
1181:
1182: /**
1183: * Return the <code>Formatter</code> for this <code>Handler</code>.
1184: *
1185: * @return the <code>Formatter</code> (may be null).
1186: */
1187: @Override
1188: public synchronized Formatter getFormatter() {
1189: return this.formatter;
1190: }
1191:
1192: /**
1193: * Set a <code>Formatter</code>. This <code>Formatter</code> will be used
1194: * to format <code>LogRecords</code> for this <code>Handler</code>.
1195: * <p>
1196: * Some <code>Handlers</code> may not use <code>Formatters</code>, in which
1197: * case the <code>Formatter</code> will be remembered, but not used.
1198: *
1199: * @param newFormatter the <code>Formatter</code> to use (may not be null)
1200: * @throws SecurityException if a security manager exists and if the caller
1201: * does not have <code>LoggingPermission("control")</code>.
1202: * @throws NullPointerException if the given formatter is null.
1203: */
1204: @Override
1205: public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
1206: checkAccess();
1207: this.formatter = Objects.requireNonNull(newFormatter);
1208: }
1209:
1210: /**
1211: * Gets the push level. The default is <code>Level.OFF</code> meaning that
1212: * this <code>Handler</code> will only push when the internal buffer is full.
1213: *
1214: * @return a non-null push level.
1215: */
1216: public final synchronized Level getPushLevel() {
1217: return this.pushLevel;
1218: }
1219:
1220: /**
1221: * Sets the push level. This level is used to trigger a push so that
1222: * all pending records are formatted and sent to the email server. When
1223: * the push level triggers a send, the resulting email is flagged as
1224: * high importance with urgent priority.
1225: *
1226: * @param level any level object or null meaning off.
1227: * @throws SecurityException if a security manager exists and the
1228: * caller does not have <code>LoggingPermission("control")</code>.
1229: * @throws IllegalStateException if called from inside a push.
1230: */
1231: public final synchronized void setPushLevel(Level level) {
1232: checkAccess();
1233: if (level == null) {
1234: level = Level.OFF;
1235: }
1236:
1237: if (isWriting) {
1238: throw new IllegalStateException();
1239: }
1240: this.pushLevel = level;
1241: }
1242:
1243: /**
1244: * Gets the push filter. The default is <code>null</code>.
1245: *
1246: * @return the push filter or <code>null</code>.
1247: */
1248: public final synchronized Filter getPushFilter() {
1249: return this.pushFilter;
1250: }
1251:
1252: /**
1253: * Sets the push filter. This filter is only called if the given
1254: * <code>LogRecord</code> level was greater than the push level. If this
1255: * filter returns <code>true</code>, all pending records are formatted and
1256: * sent to the email server. When the push filter triggers a send, the
1257: * resulting email is flagged as high importance with urgent priority.
1258: *
1259: * @param filter push filter or <code>null</code>
1260: * @throws SecurityException if a security manager exists and the
1261: * caller does not have <code>LoggingPermission("control")</code>.
1262: * @throws IllegalStateException if called from inside a push.
1263: */
1264: public final synchronized void setPushFilter(final Filter filter) {
1265: checkAccess();
1266: if (isWriting) {
1267: throw new IllegalStateException();
1268: }
1269: this.pushFilter = filter;
1270: }
1271:
1272: /**
1273: * Gets the comparator used to order all <code>LogRecord</code> objects
1274: * prior to formatting. If <code>null</code> then the order is unspecified.
1275: *
1276: * @return the <code>LogRecord</code> comparator.
1277: */
1278: public final synchronized Comparator<? super LogRecord> getComparator() {
1279: return this.comparator;
1280: }
1281:
1282: /**
1283: * Sets the comparator used to order all <code>LogRecord</code> objects
1284: * prior to formatting. If <code>null</code> then the order is unspecified.
1285: *
1286: * @param c the <code>LogRecord</code> comparator.
1287: * @throws SecurityException if a security manager exists and the
1288: * caller does not have <code>LoggingPermission("control")</code>.
1289: * @throws IllegalStateException if called from inside a push.
1290: */
1291: public final synchronized void setComparator(Comparator<? super LogRecord> c) {
1292: checkAccess();
1293: if (isWriting) {
1294: throw new IllegalStateException();
1295: }
1296: this.comparator = c;
1297: }
1298:
1299: /**
1300: * Gets the number of log records the internal buffer can hold. When
1301: * capacity is reached, <code>Handler</code> will format all
1302: * <code>LogRecord</code> objects into one email message.
1303: *
1304: * @return the capacity.
1305: */
1306: public final synchronized int getCapacity() {
1307: assert capacity != Integer.MIN_VALUE && capacity != 0 : capacity;
1308: return Math.abs(capacity);
1309: }
1310:
1311: /**
1312: * Sets the capacity for this handler.
1313: *
1314: * Pushes any buffered records to the email server as normal priority.
1315: * The internal buffer is then cleared.
1316: *
1317: * @param newCapacity the max number of records.
1318: * @throws SecurityException if a security manager exists and the caller
1319: * does not have <code>LoggingPermission("control")</code>.
1320: * @throws IllegalArgumentException is the new capacity is less than one.
1321: * @throws IllegalStateException if called from inside a push.
1322: * @see #flush()
1323: * @since Angus Mail 2.0.3
1324: */
1325: public final synchronized void setCapacity(int newCapacity) {
1326: setCapacity0(newCapacity);
1327: }
1328:
1329: /**
1330: * Gets the <code>Authenticator</code> used to login to the email server.
1331: *
1332: * @return an <code>Authenticator</code> or <code>null</code> if none is
1333: * required.
1334: * @throws SecurityException if a security manager exists and the
1335: * caller does not have <code>LoggingPermission("control")</code>.
1336: */
1337: public final synchronized Authenticator getAuthenticator() {
1338: checkAccess();
1339: return this.auth;
1340: }
1341:
1342: /**
1343: * Sets the <code>Authenticator</code> used to login to the email server.
1344: *
1345: * @param auth an <code>Authenticator</code> object or null if none is
1346: * required.
1347: * @throws SecurityException if a security manager exists and the
1348: * caller does not have <code>LoggingPermission("control")</code>.
1349: * @throws IllegalStateException if called from inside a push.
1350: */
1351: public final void setAuthenticator(final Authenticator auth) {
1352: this.setAuthenticator0(auth);
1353: }
1354:
1355: /**
1356: * Sets the <code>Authenticator</code> used to login to the email server.
1357: *
1358: * @param password a password, empty array can be used to only supply a
1359: * user name set by <code>mail.user</code> property, or null if no
1360: * credentials are required.
1361: * @throws SecurityException if a security manager exists and the
1362: * caller does not have <code>LoggingPermission("control")</code>.
1363: * @throws IllegalStateException if called from inside a push.
1364: * @see String#toCharArray()
1365: * @since JavaMail 1.4.6
1366: */
1367: public final void setAuthenticator(final char... password) {
1368: if (password == null) {
1369: setAuthenticator0((Authenticator) null);
1370: } else {
1371: setAuthenticator0(DefaultAuthenticator.of(new String(password)));
1372: }
1373: }
1374:
1375: /**
1376: * Sets the <code>Authenticator</code> class name or password used to login
1377: * to the email server.
1378: *
1379: * @param auth the class name of the authenticator, literal password, or
1380: * empty string can be used to only supply a user name set by
1381: * <code>mail.user</code> property. A null value can be used if no
1382: * credentials are required.
1383: * @throws SecurityException if a security manager exists and the caller
1384: * does not have <code>LoggingPermission("control")</code>.
1385: * @throws IllegalStateException if called from inside a push.
1386: * @see #getAuthenticator()
1387: * @see #setAuthenticator(char...)
1388: * @since Angus Mail 2.0.3
1389: */
1390: public final synchronized void setAuthentication(final String auth) {
1391: setAuthenticator0(newAuthenticator(auth));
1392: }
1393:
1394: /**
1395: * A private hook to handle possible future overrides. See public method.
1396: *
1397: * @param auth see public method.
1398: * @throws SecurityException if a security manager exists and the
1399: * caller does not have <code>LoggingPermission("control")</code>.
1400: * @throws IllegalStateException if called from inside a push.
1401: */
1402: private void setAuthenticator0(final Authenticator auth) {
1403: checkAccess();
1404:
1405: Session settings;
1406: synchronized (this) {
1407: if (isWriting) {
1408: throw new IllegalStateException();
1409: }
1410: this.auth = auth;
1411: settings = updateSession();
1412: }
1413: verifySettings(settings);
1414: }
1415:
1416: /**
1417: * Sets the mail properties used for the session. The key/value pairs
1418: * are defined in the <code>Java Mail API</code> documentation. This
1419: * <code>Handler</code> will also search the <code>LogManager</code> for
1420: * defaults if needed. A key named <code>verify</code> can be declared to
1421: * trigger <a href="#verify">verification</a>.
1422: *
1423: * @param props properties object or null. A null value will supply the
1424: * <code>mailEntries</code> from the <code>LogManager</code>. An empty
1425: * properties will clear all existing mail properties assigned to this
1426: * handler.
1427: * @throws SecurityException if a security manager exists and the
1428: * caller does not have <code>LoggingPermission("control")</code>.
1429: * @throws IllegalStateException if called from inside a push.
1430: */
1431: public final void setMailProperties(Properties props) {
1432: if (props == null) {
1433: final String p = getClass().getName();
1434: props = parseProperties(
1435: fromLogManager(p.concat(".mailEntries")));
1436: setMailProperties0(props != null ? props : new Properties());
1437: } else {
1438: setMailProperties0(copyOf(props));
1439: }
1440: }
1441:
1442: /**
1443: * Copies a properties object. Checks that given properties clone
1444: * returns the a Properties object and that it is not null.
1445: *
1446: * @param props a properties object
1447: * @return a copy of the properties object.
1448: * @throws ClassCastException if clone doesn't return a Properties object.
1449: * @throws NullPointerExeption if props is null or if the copy was null.
1450: * @since Angus Mail 2.0.3
1451: */
1452: private Properties copyOf(Properties props) {
1453: Properties copy = (Properties) props.clone(); //Allow subclass
1454: return Objects.requireNonNull(copy); //Broken subclass
1455: }
1456:
1457: /**
1458: * A private hook to handle overrides when the public method is declared
1459: * non final. See public method for details.
1460: *
1461: * @param props a safe properties object.
1462: * @return true if verification key was present.
1463: * @throws NullPointerException if props is null.
1464: */
1465: private boolean setMailProperties0(Properties props) {
1466: Objects.requireNonNull(props);
1467: checkAccess();
1468: Session settings;
1469: synchronized (this) {
1470: if (isWriting) {
1471: throw new IllegalStateException();
1472: }
1473: this.mailProps = props;
1474: settings = updateSession();
1475: }
1476: return verifySettings(settings);
1477: }
1478:
1479: /**
1480: * Gets a copy of the mail properties used for the session.
1481: *
1482: * @return a non null properties object.
1483: * @throws SecurityException if a security manager exists and the
1484: * caller does not have <code>LoggingPermission("control")</code>.
1485: */
1486: public final Properties getMailProperties() {
1487: checkAccess();
1488: final Properties props;
1489: synchronized (this) {
1490: props = this.mailProps;
1491: }
1492:
1493: //Null check to force an error sooner rather than later.
1494: return Objects.requireNonNull((Properties) props.clone());
1495: }
1496:
1497: /**
1498: * Parses the given properties lines then clears and sets all of the mail
1499: * properties used for the session. Any parsing errors are reported to the
1500: * error manager. This method provides bean style properties support.
1501: * <p>
1502: * The given string should be treated as lines of a properties file. The
1503: * character {@code '='} or {@code ':'} are used to separate an entry also
1504: * known as a key/value pair. The line terminator characters {@code \r} or
1505: * {@code \n} or {@code \r\n} are used to separate each entry. The
1506: * characters {@code '#!'} together can be used to signal the end of an
1507: * entry when escape characters are not supported.
1508: * <p>
1509: * The example from the <a href="#configuration">configuration</a>
1510: * section would be formatted as the following string:
1511: * <pre>
1512: * mail.smtp.host:my-mail-server#!mail.to:me@example.com#!verify:local
1513: * </pre>
1514: * <p>
1515: * The key/value pairs are defined in the <code>Java Mail API</code>
1516: * documentation. This <code>Handler</code> will also search the
1517: * <code>LogManager</code> for defaults if needed. A key named
1518: * <code>verify</code> can be declared to trigger
1519: * <a href="#verify">verification</a>.
1520: *
1521: * @param entries one or more key/value pairs. A null value will supply the
1522: * <code>mailEntries</code> from the <code>LogManager</code>. An empty
1523: * string or the literal null are all treated as empty properties and will
1524: * clear all existing mail properties assigned to this handler.
1525: * @throws SecurityException if a security manager exists and the caller
1526: * does not have <code>LoggingPermission("control")</code>.
1527: * @throws IllegalStateException if called from inside a push.
1528: * @see #getMailProperties()
1529: * @see java.io.StringReader
1530: * @see java.util.Properties#load(Reader)
1531: * @since Angus Mail 2.0.3
1532: */
1533: public final void setMailEntries(String entries) {
1534: if (entries == null) {
1535: final String p = getClass().getName();
1536: entries = fromLogManager(p.concat(".mailEntries"));
1537: }
1538: final Properties props = parseProperties(entries);
1539: setMailProperties0(props != null ? props : new Properties());
1540: }
1541:
1542: /**
1543: * Formats the current mail properties as properties lines. Any formatting
1544: * errors are reported to the error manager. The returned string should be
1545: * treated as lines of a properties file. The value of this string is
1546: * reconstructed from the properties object and therefore may be different
1547: * from what was originally set. This method provides bean style properties
1548: * support.
1549: *
1550: * @return string representation of the mail properties.
1551: * @throws SecurityException if a security manager exists and the caller
1552: * does not have <code>LoggingPermission("control")</code>.
1553: * @throws IllegalStateException if called from inside a push.
1554: * @see #getMailProperties()
1555: * @see java.io.StringWriter
1556: * @see java.util.Properties#store(java.io.Writer, java.lang.String)
1557: * @since Angus Mail 2.0.3
1558: */
1559: public final String getMailEntries() {
1560: checkAccess();
1561: final Properties props;
1562: synchronized (this) {
1563: props = this.mailProps;
1564: }
1565:
1566: final StringWriter sw = new StringWriter();
1567: try {
1568: //Dynamic cast used so byte code verifier doesn't load StringWriter
1569: props.store(Writer.class.cast(sw), (String) null);
1570: } catch (IOException | RuntimeException ex) {
1571: reportError(props.toString(), ex, ErrorManager.GENERIC_FAILURE);
1572: //partially constructed values are allowed to be returned
1573: }
1574:
1575: //Properties.store will always write a date comment
1576: //which is removed by this code.
1577: String entries = sw.toString();
1578: if (entries.startsWith("#")) {
1579: String sep = System.lineSeparator();
1580: int end = entries.indexOf(sep);
1581: if (end > 0) {
1582: entries = entries.substring(end + sep.length(), entries.length());
1583: }
1584: }
1585: return entries;
1586: }
1587:
1588: /**
1589: * Gets the attachment filters. If the attachment filter does not
1590: * allow any <code>LogRecord</code> to be formatted, the attachment may
1591: * be omitted from the email.
1592: *
1593: * @return a non null array of attachment filters.
1594: */
1595: public final Filter[] getAttachmentFilters() {
1596: return readOnlyAttachmentFilters().clone();
1597: }
1598:
1599: /**
1600: * Sets the attachment filters.
1601: *
1602: * @param filters array of filters. A <code>null</code> array is treated
1603: * the same as an empty array and will remove all attachments. A
1604: * <code>null</code> index value means that all records are allowed for the
1605: * attachment at that index.
1606: * @throws SecurityException if a security manager exists and the
1607: * caller does not have <code>LoggingPermission("control")</code>.
1608: * @throws IllegalStateException if called from inside a push.
1609: */
1610: public final void setAttachmentFilters(Filter... filters) {
1611: checkAccess();
1612: if (filters == null || filters.length == 0) {
1613: filters = emptyFilterArray();
1614: } else {
1615: filters = Arrays.copyOf(filters, filters.length, Filter[].class);
1616: }
1617:
1618: synchronized (this) {
1619: if (isWriting) {
1620: throw new IllegalStateException();
1621: }
1622:
1623: if (size != 0) {
1624: final int len = Math.min(filters.length, attachmentFilters.length);
1625: int i = 0;
1626: for (; i < len; ++i) {
1627: if (filters[i] != attachmentFilters[i]) {
1628: break;
1629: }
1630: }
1631: clearMatches(i);
1632: }
1633: this.attachmentFilters = filters;
1634: this.alignAttachmentFormatters(filters.length);
1635: this.alignAttachmentNames(filters.length);
1636: }
1637: }
1638:
1639: /**
1640: * Gets the attachment formatters. This <code>Handler</code> is using
1641: * attachments only if the returned array length is non zero.
1642: *
1643: * @return a non <code>null</code> array of formatters.
1644: */
1645: public final Formatter[] getAttachmentFormatters() {
1646: Formatter[] formatters;
1647: synchronized (this) {
1648: formatters = this.attachmentFormatters;
1649: }
1650: return formatters.clone();
1651: }
1652:
1653: /**
1654: * Sets the attachment <code>Formatter</code> object for this handler.
1655: * The number of formatters determines the number of attachments per
1656: * email. This method should be the first attachment method called.
1657: * To remove all attachments, call this method with empty array.
1658: *
1659: * @param formatters an array of formatters. A null array is treated as an
1660: * empty array. Any null indexes is replaced with a
1661: * {@linkplain java.util.logging.SimpleFormatter SimpleFormatter}.
1662: * @throws SecurityException if a security manager exists and the
1663: * caller does not have <code>LoggingPermission("control")</code>.
1664: * @throws IllegalStateException if called from inside a push.
1665: */
1666: public final void setAttachmentFormatters(Formatter... formatters) {
1667: checkAccess();
1668: if (formatters == null || formatters.length == 0) { //Null check and length check.
1669: formatters = emptyFormatterArray();
1670: } else {
1671: formatters = Arrays.copyOf(formatters,
1672: formatters.length, Formatter[].class);
1673: for (int i = 0; i < formatters.length; ++i) {
1674: if (formatters[i] == null) {
1675: formatters[i] = createSimpleFormatter();
1676: }
1677: }
1678: }
1679:
1680: synchronized (this) {
1681: if (isWriting) {
1682: throw new IllegalStateException();
1683: }
1684:
1685: this.attachmentFormatters = formatters;
1686: this.alignAttachmentFilters(formatters.length);
1687: this.alignAttachmentNames(formatters.length);
1688: }
1689: }
1690:
1691: /**
1692: * Gets the attachment name formatters.
1693: * If the attachment names were set using explicit names then
1694: * the names can be returned by calling <code>toString</code> on each
1695: * attachment name formatter.
1696: *
1697: * @return non <code>null</code> array of attachment name formatters.
1698: */
1699: public final Formatter[] getAttachmentNames() {
1700: final Formatter[] formatters;
1701: synchronized (this) {
1702: formatters = this.attachmentNames;
1703: }
1704: return formatters.clone();
1705: }
1706:
1707: /**
1708: * Sets the attachment file name for each attachment. All control
1709: * characters are removed from the attachment names.
1710: * This method will create a set of custom formatters.
1711: *
1712: * @param names an array of names. A null array is treated as an empty
1713: * array. Any null or empty indexes are replaced with the string
1714: * representation of the attachment formatter.
1715: * @throws SecurityException if a security manager exists and the
1716: * caller does not have <code>LoggingPermission("control")</code>.
1717: * @throws IllegalStateException if called from inside a push.
1718: * @see Character#isISOControl(char)
1719: * @see Character#isISOControl(int)
1720: */
1721:
1722: public final void setAttachmentNames(final String... names) {
1723: checkAccess();
1724:
1725: final Formatter[] formatters;
1726: if (names == null || names.length == 0) {
1727: formatters = emptyFormatterArray();
1728: } else {
1729: formatters = new Formatter[names.length];
1730: }
1731:
1732: synchronized (this) {
1733: if (isWriting) {
1734: throw new IllegalStateException();
1735: }
1736:
1737: this.alignAttachmentFormatters(formatters.length);
1738: this.alignAttachmentFilters(formatters.length);
1739: for (int i = 0; i < formatters.length; ++i) {
1740: //names is non-null if formatters length is not zero
1741: String name = names[i];
1742: if (isEmpty(name)) {
1743: name = toString(this.attachmentFormatters[i]);
1744: }
1745: formatters[i] = TailNameFormatter.of(name);
1746: }
1747: this.attachmentNames = formatters;
1748: }
1749: }
1750:
1751: /**
1752: * Sets the attachment file name formatters. The format method of each
1753: * attachment formatter will see only the <code>LogRecord</code> objects
1754: * that passed its attachment filter during formatting. The format method
1755: * will typically return an empty string. Instead of being used to format
1756: * records, it is used to gather information about the contents of an
1757: * attachment. The <code>getTail</code> method should be used to construct
1758: * the attachment file name and reset any formatter collected state. All
1759: * control characters will be removed from the output of the formatter. The
1760: * <code>toString</code> method of the given formatter should be overridden
1761: * to provide a useful attachment file name, if possible.
1762: *
1763: * @param formatters and array of attachment name formatters.
1764: * @throws SecurityException if a security manager exists and the
1765: * caller does not have <code>LoggingPermission("control")</code>.
1766: * @throws IllegalStateException if called from inside a push.
1767: * @see Character#isISOControl(char)
1768: * @see Character#isISOControl(int)
1769: */
1770: public final void setAttachmentNames(Formatter... formatters) {
1771: setAttachmentNameFormatters(formatters);
1772: }
1773:
1774: /**
1775: * Sets the attachment file name formatters. The format method of each
1776: * attachment formatter will see only the <code>LogRecord</code> objects
1777: * that passed its attachment filter during formatting. The format method
1778: * will typically return an empty string. Instead of being used to format
1779: * records, it is used to gather information about the contents of an
1780: * attachment. The <code>getTail</code> method should be used to construct
1781: * the attachment file name and reset any formatter collected state. All
1782: * control characters will be removed from the output of the formatter. The
1783: * <code>toString</code> method of the given formatter should be overridden
1784: * to provide a useful attachment file name, if possible.
1785: *
1786: * @param formatters and array of attachment name formatters.
1787: * @throws SecurityException if a security manager exists and the
1788: * caller does not have <code>LoggingPermission("control")</code>.
1789: * @throws IllegalStateException if called from inside a push.
1790: * @see Character#isISOControl(char)
1791: * @see Character#isISOControl(int)
1792: * @since Angus Mail 2.0.3
1793: */
1794: public final void setAttachmentNameFormatters(Formatter... formatters) {
1795: checkAccess();
1796:
1797: if (formatters == null || formatters.length == 0) {
1798: formatters = emptyFormatterArray();
1799: } else {
1800: formatters = Arrays.copyOf(formatters, formatters.length,
1801: Formatter[].class);
1802: }
1803:
1804: synchronized (this) {
1805: if (isWriting) {
1806: throw new IllegalStateException();
1807: }
1808:
1809: this.alignAttachmentFormatters(formatters.length);
1810: this.alignAttachmentFilters(formatters.length);
1811: for (int i = 0; i < formatters.length; ++i) {
1812: if (formatters[i] == null) {
1813: formatters[i] = TailNameFormatter.of(toString(this.attachmentFormatters[i]));
1814: }
1815: }
1816: this.attachmentNames = formatters;
1817: }
1818: }
1819:
1820: /**
1821: * Gets the formatter used to create the subject line.
1822: * If the subject was created using a literal string then
1823: * the <code>toString</code> method can be used to get the subject line.
1824: *
1825: * @return the formatter.
1826: */
1827: public final synchronized Formatter getSubject() {
1828: return getSubjectFormatter();
1829: }
1830:
1831: /**
1832: * Gets the formatter used to create the subject line.
1833: * If the subject was created using a literal string then
1834: * the <code>toString</code> method can be used to get the subject line.
1835: *
1836: * @return the formatter.
1837: * @since Angus Mail 2.0.3
1838: */
1839: public final synchronized Formatter getSubjectFormatter() {
1840: return this.subjectFormatter;
1841: }
1842:
1843: /**
1844: * Sets a literal string for the email subject. All control characters are
1845: * removed from the subject line of the email
1846: *
1847: * @param subject a non <code>null</code> string.
1848: * @throws SecurityException if a security manager exists and the
1849: * caller does not have <code>LoggingPermission("control")</code>.
1850: * @throws NullPointerException if <code>subject</code> is
1851: * <code>null</code>.
1852: * @throws IllegalStateException if called from inside a push.
1853: * @see Character#isISOControl(char)
1854: * @see Character#isISOControl(int)
1855: */
1856: public synchronized final void setSubject(final String subject) {
1857: if (subject != null) {
1858: this.setSubjectFormatter(TailNameFormatter.of(subject));
1859: } else {
1860: checkAccess();
1861: initSubject((String) null);
1862: }
1863: }
1864:
1865: /**
1866: * Sets the subject formatter for email. The format method of the subject
1867: * formatter will see all <code>LogRecord</code> objects that were published
1868: * to this <code>Handler</code> during formatting and will typically return
1869: * an empty string. This formatter is used to gather information to create
1870: * a summary about what information is contained in the email. The
1871: * <code>getTail</code> method should be used to construct the subject and
1872: * reset any formatter collected state. All control characters
1873: * will be removed from the formatter output. The <code>toString</code>
1874: * method of the given formatter should be overridden to provide a useful
1875: * subject, if possible.
1876: *
1877: * @param format the subject formatter or null for default formatter.
1878: * @throws SecurityException if a security manager exists and the
1879: * caller does not have <code>LoggingPermission("control")</code>.
1880: * @throws IllegalStateException if called from inside a push.
1881: * @see Character#isISOControl(char)
1882: * @see Character#isISOControl(int)
1883: */
1884: public final void setSubject(final Formatter format) {
1885: setSubjectFormatter(format);
1886: }
1887:
1888: /**
1889: * Sets the subject formatter for email. The format method of the subject
1890: * formatter will see all <code>LogRecord</code> objects that were published
1891: * to this <code>Handler</code> during formatting and will typically return
1892: * an empty string. This formatter is used to gather information to create
1893: * a summary about what information is contained in the email. The
1894: * <code>getTail</code> method should be used to construct the subject and
1895: * reset any formatter collected state. All control characters
1896: * will be removed from the formatter output. The <code>toString</code>
1897: * method of the given formatter should be overridden to provide a useful
1898: * subject, if possible.
1899: *
1900: * @param format the subject formatter or null for default formatter.
1901: * @throws SecurityException if a security manager exists and the
1902: * caller does not have <code>LoggingPermission("control")</code>.
1903: * @throws IllegalStateException if called from inside a push.
1904: * @see Character#isISOControl(char)
1905: * @see Character#isISOControl(int)
1906: * @since Angus Mail 2.0.3
1907: */
1908: public synchronized final void setSubjectFormatter(final Formatter format) {
1909: checkAccess();
1910: if (format != null) {
1911: if (isWriting) {
1912: throw new IllegalStateException();
1913: }
1914: this.subjectFormatter = format;
1915: } else {
1916: initSubject((String) null);
1917: }
1918: }
1919:
1920: /**
1921: * Protected convenience method to report an error to this Handler's
1922: * ErrorManager. This method will prefix all non null error messages with
1923: * <code>Level.SEVERE.getName()</code>. This allows the receiving error
1924: * manager to determine if the <code>msg</code> parameter is a simple error
1925: * message or a raw email message.
1926: *
1927: * @param msg a descriptive string (may be null)
1928: * @param ex an exception (may be null)
1929: * @param code an error code defined in ErrorManager
1930: */
1931: @Override
1932: protected void reportError(String msg, Exception ex, int code) {
1933: try {
1934: if (msg != null) {
1935: errorManager.error(Level.SEVERE.getName()
1936: .concat(": ").concat(msg), ex, code);
1937: } else {
1938: errorManager.error((String) null, ex, code);
1939: }
1940: } catch (RuntimeException | LinkageError GLASSFISH_21258) {
1941: reportLinkageError(GLASSFISH_21258, code);
1942: }
1943: }
1944:
1945: /**
1946: * Checks logging permissions if this handler has been sealed.
1947: * @throws SecurityException if a security manager exists and the caller
1948: * does not have {@code LoggingPermission("control")}.
1949: */
1950: private void checkAccess() {
1951: if (sealed) {
1952: LogManagerProperties.checkLogManagerAccess();
1953: }
1954: }
1955:
1956: /**
1957: * Determines the mimeType of a formatter from the getHead call.
1958: * This could be made protected, or a new class could be created to do
1959: * this type of conversion. Currently, this is only used for the body
1960: * since the attachments are computed by filename.
1961: * Package-private for unit testing.
1962: *
1963: * @param chunk any char sequence or null.
1964: * @return return the mime type or null for text/plain.
1965: */
1966: final String contentTypeOf(CharSequence chunk) {
1967: if (!isEmpty(chunk)) {
1968: final int MAX_CHARS = 25;
1969: if (chunk.length() > MAX_CHARS) {
1970: chunk = chunk.subSequence(0, MAX_CHARS);
1971: }
1972: try {
1973: final String charset = getEncodingName();
1974: final byte[] b = chunk.toString().getBytes(charset);
1975: final ByteArrayInputStream in = new ByteArrayInputStream(b);
1976: assert in.markSupported() : in.getClass().getName();
1977: return URLConnection.guessContentTypeFromStream(in);
1978: } catch (final IOException IOE) {
1979: reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE);
1980: }
1981: }
1982: return null; //text/plain
1983: }
1984:
1985: /**
1986: * Determines the mimeType of a formatter by the class name. This method
1987: * avoids calling getHead and getTail of content formatters during verify
1988: * because they might trigger side effects or excessive work. The name
1989: * formatters and subject are usually safe to call.
1990: * Package-private for unit testing.
1991: *
1992: * @param f the formatter or null.
1993: * @return return the mime type or null, meaning text/plain.
1994: * @since JavaMail 1.5.6
1995: */
1996: final String contentTypeOf(final Formatter f) {
1997: assert Thread.holdsLock(this);
1998: if (f != null) {
1999: String type = getContentType(f.getClass().getName());
2000: if (type != null) {
2001: return type;
2002: }
2003:
2004: for (Class<?> k = f.getClass(); k != Formatter.class;
2005: k = k.getSuperclass()) {
2006: String name;
2007: try {
2008: name = k.getSimpleName();
2009: } catch (final InternalError JDK8057919) {
2010: name = k.getName();
2011: }
2012: name = name.toLowerCase(Locale.ENGLISH);
2013: for (int idx = name.indexOf('$') + 1;
2014: (idx = name.indexOf("ml", idx)) > -1; idx += 2) {
2015: if (idx > 0) {
2016: if (name.charAt(idx - 1) == 'x') {
2017: return "application/xml";
2018: }
2019: if (idx > 1 && name.charAt(idx - 2) == 'h'
2020: && name.charAt(idx - 1) == 't') {
2021: return "text/html";
2022: }
2023: }
2024: }
2025: }
2026: }
2027: return null;
2028: }
2029:
2030: /**
2031: * Determines if the given throwable is a no content exception. It is
2032: * assumed Transport.sendMessage will call Message.writeTo so we need to
2033: * ignore any exceptions that could be layered on top of that call chain to
2034: * infer that sendMessage is failing because of writeTo. Package-private
2035: * for unit testing.
2036: *
2037: * @param msg the message without content.
2038: * @param t the throwable chain to test.
2039: * @return true if the throwable is a missing content exception.
2040: * @throws NullPointerException if any of the arguments are null.
2041: * @since JavaMail 1.4.5
2042: */
2043: @SuppressWarnings({"UseSpecificCatch", "ThrowableResultIgnored"})
2044: final boolean isMissingContent(Message msg, Throwable t) {
2045: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
2046: try {
2047: msg.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE));
2048: } catch (final RuntimeException RE) {
2049: throw RE; //Avoid catch all.
2050: } catch (final Exception noContent) {
2051: final String txt = noContent.getMessage();
2052: if (!isEmpty(txt)) {
2053: int limit = 0;
2054: while (t != null) {
2055: if (noContent.getClass() == t.getClass()
2056: && txt.equals(t.getMessage())) {
2057: return true;
2058: }
2059:
2060: //Not all Jakarta Mail implementations support JDK 1.4
2061: //exception chaining.
2062: final Throwable cause = t.getCause();
2063: if (cause == null && t instanceof MessagingException) {
2064: t = ((MessagingException) t).getNextException();
2065: } else {
2066: t = cause;
2067: }
2068:
2069: //Deal with excessive cause chains and cyclic throwables.
2070: if (++limit == (1 << 16)) {
2071: break; //Give up.
2072: }
2073: }
2074: }
2075: } finally {
2076: getAndSetContextClassLoader(ccl);
2077: }
2078: return false;
2079: }
2080:
2081: /**
2082: * Converts a mime message to a raw string or formats the reason
2083: * why message can't be changed to raw string and reports it.
2084: *
2085: * @param msg the mime message.
2086: * @param ex the original exception.
2087: * @param code the ErrorManager code.
2088: * @since JavaMail 1.4.5
2089: */
2090: @SuppressWarnings("UseSpecificCatch")
2091: private void reportError(Message msg, Exception ex, int code) {
2092: try {
2093: try { //Use direct call so we do not prefix raw email.
2094: errorManager.error(toRawString(msg), ex, code);
2095: } catch (final Exception e) {
2096: reportError(toMsgString(e), ex, code);
2097: }
2098: } catch (final LinkageError GLASSFISH_21258) {
2099: reportLinkageError(GLASSFISH_21258, code);
2100: }
2101: }
2102:
2103: /**
2104: * Reports the given linkage error or runtime exception.
2105: *
2106: * The current LogManager code will stop closing all remaining handlers if
2107: * an error is thrown during resetLogger. This is a workaround for
2108: * GLASSFISH-21258 and JDK-8152515.
2109: *
2110: * @param le the linkage error or a RuntimeException.
2111: * @param code the ErrorManager code.
2112: * @throws NullPointerException if error is null.
2113: * @since JavaMail 1.5.3
2114: */
2115: private void reportLinkageError(final Throwable le, final int code) {
2116: if (le == null) {
2117: throw new NullPointerException(String.valueOf(code));
2118: }
2119:
2120: final Integer idx = MUTEX.get();
2121: if (idx == null || idx > MUTEX_LINKAGE) {
2122: MUTEX.set(MUTEX_LINKAGE);
2123: try {
2124: Thread.currentThread().getUncaughtExceptionHandler()
2125: .uncaughtException(Thread.currentThread(), le);
2126: } catch (RuntimeException | LinkageError ignore) {
2127: } finally {
2128: if (idx != null) {
2129: MUTEX.set(idx);
2130: } else {
2131: MUTEX.remove();
2132: }
2133: }
2134: }
2135: }
2136:
2137: /**
2138: * Determines the mimeType from the given file name.
2139: * Used to override the body content type and used for all attachments.
2140: *
2141: * @param name the file name or class name.
2142: * @return the mime type or null for text/plain.
2143: */
2144: private String getContentType(final String name) {
2145: assert Thread.holdsLock(this);
2146: if (contentTypes == null) {
2147: return null;
2148: }
2149:
2150: final String type = contentTypes.getContentType(name);
2151: if ("application/octet-stream".equalsIgnoreCase(type)) {
2152: return null; //Formatters return strings, default to text/plain.
2153: }
2154: return type;
2155: }
2156:
2157: /**
2158: * Gets the encoding set for this handler, mime encoding, or file encoding.
2159: *
2160: * @return the java charset name, never null.
2161: * @since JavaMail 1.4.5
2162: */
2163: private String getEncodingName() {
2164: String charset = getEncoding();
2165: if (charset == null) {
2166: charset = MimeUtility.getDefaultJavaCharset();
2167: }
2168: return charset;
2169: }
2170:
2171: /**
2172: * Set the content for a part using the encoding assigned to the handler.
2173: *
2174: * @param part the part to assign.
2175: * @param buf the formatted data.
2176: * @param type the mime type or null, meaning text/plain.
2177: * @throws MessagingException if there is a problem.
2178: */
2179: private void setContent(MimePart part, CharSequence buf, String type) throws MessagingException {
2180: final String charset = getEncodingName();
2181: if (type != null && !"text/plain".equalsIgnoreCase(type)) {
2182: type = contentWithEncoding(type, charset);
2183: try {
2184: DataSource source = new ByteArrayDataSource(buf.toString(), type);
2185: part.setDataHandler(new DataHandler(source));
2186: } catch (final IOException IOE) {
2187: reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE);
2188: part.setText(buf.toString(), charset);
2189: }
2190: } else {
2191: part.setText(buf.toString(), MimeUtility.mimeCharset(charset));
2192: }
2193: }
2194:
2195: /**
2196: * Replaces the charset parameter with the current encoding.
2197: *
2198: * @param type the content type.
2199: * @param encoding the java charset name.
2200: * @return the type with a specified encoding.
2201: */
2202: private String contentWithEncoding(String type, String encoding) {
2203: assert encoding != null;
2204: try {
2205: final ContentType ct = new ContentType(type);
2206: ct.setParameter("charset", MimeUtility.mimeCharset(encoding));
2207: encoding = ct.toString(); //See jakarta.mail.internet.ContentType.
2208: if (!isEmpty(encoding)) { //Support pre K5687.
2209: type = encoding;
2210: }
2211: } catch (final MessagingException ME) {
2212: reportError(type, ME, ErrorManager.FORMAT_FAILURE);
2213: }
2214: return type;
2215: }
2216:
2217: /**
2218: * Sets the capacity for this handler.
2219: *
2220: * @param newCapacity the max number of records.
2221: * @throws SecurityException if a security manager exists and the
2222: * caller does not have <code>LoggingPermission("control")</code>.
2223: * @throws IllegalStateException if called from inside a push.
2224: */
2225: private synchronized void setCapacity0(int newCapacity) {
2226: checkAccess();
2227: if (newCapacity <= 0) {
2228: newCapacity = 1000;
2229: }
2230:
2231: if (isWriting) {
2232: throw new IllegalStateException();
2233: }
2234:
2235: if (this.capacity < 0) { //If closed, remain closed.
2236: this.capacity = -newCapacity;
2237: } else {
2238: this.push(false, ErrorManager.FLUSH_FAILURE);
2239: this.capacity = newCapacity;
2240: if (this.data != null && this.data.length > newCapacity) {
2241: this.data = Arrays.copyOf(data, newCapacity, LogRecord[].class);
2242: this.matched = Arrays.copyOf(matched, newCapacity);
2243: }
2244: }
2245: }
2246:
2247: /**
2248: * Gets the attachment filters using a happens-before relationship between
2249: * this method and setAttachmentFilters. The attachment filters are treated
2250: * as copy-on-write, so the returned array must never be modified or
2251: * published outside this class.
2252: *
2253: * @return a read only array of filters.
2254: */
2255: private Filter[] readOnlyAttachmentFilters() {
2256: return this.attachmentFilters;
2257: }
2258:
2259: /**
2260: * Factory for empty formatter arrays.
2261: *
2262: * @return an empty array.
2263: */
2264: private static Formatter[] emptyFormatterArray() {
2265: return EMPTY_FORMATTERS;
2266: }
2267:
2268: /**
2269: * Factory for empty filter arrays.
2270: *
2271: * @return an empty array.
2272: */
2273: private static Filter[] emptyFilterArray() {
2274: return EMPTY_FILTERS;
2275: }
2276:
2277: /**
2278: * Expand or shrink the attachment name formatters with the attachment
2279: * formatters.
2280: *
2281: * @return true if size was changed.
2282: */
2283: private boolean alignAttachmentNames(int expect) {
2284: assert Thread.holdsLock(this);
2285: boolean fixed = false;
2286: final int current = this.attachmentNames.length;
2287: if (current != expect) {
2288: this.attachmentNames = Arrays.copyOf(attachmentNames, expect,
2289: Formatter[].class);
2290: fixed = current != 0;
2291: }
2292:
2293: //Copy of zero length array is cheap, warm up copyOf.
2294: if (expect == 0) {
2295: this.attachmentNames = emptyFormatterArray();
2296: assert this.attachmentNames.length == 0;
2297: } else {
2298: for (int i = 0; i < expect; ++i) {
2299: if (this.attachmentNames[i] == null) {
2300: this.attachmentNames[i] = TailNameFormatter.of(
2301: toString(this.attachmentFormatters[i]));
2302: }
2303: }
2304: }
2305: return fixed;
2306: }
2307:
2308: private boolean alignAttachmentFormatters(int expect) {
2309: assert Thread.holdsLock(this);
2310: boolean fixed = false;
2311: final int current = this.attachmentFormatters.length;
2312: if (current != expect) {
2313: this.attachmentFormatters = Arrays.copyOf(attachmentFormatters, expect,
2314: Formatter[].class);
2315: fixed = current != 0;
2316: }
2317:
2318: //Copy of zero length array is cheap, warm up copyOf.
2319: if (expect == 0) {
2320: this.attachmentFormatters = emptyFormatterArray();
2321: assert this.attachmentFormatters.length == 0;
2322: } else {
2323: for (int i = current; i < expect; ++i) {
2324: if (this.attachmentFormatters[i] == null) {
2325: this.attachmentFormatters[i] = createSimpleFormatter();
2326: }
2327: }
2328: }
2329: return fixed;
2330: }
2331:
2332: /**
2333: * Expand or shrink the attachment filters with the attachment formatters.
2334: *
2335: * @return true if the size was changed.
2336: */
2337: private boolean alignAttachmentFilters(int expect) {
2338: assert Thread.holdsLock(this);
2339:
2340: boolean fixed = false;
2341: final int current = this.attachmentFilters.length;
2342: if (current != expect) {
2343: this.attachmentFilters = Arrays.copyOf(attachmentFilters, expect,
2344: Filter[].class);
2345: clearMatches(Math.min(current, expect));
2346: fixed = current != 0;
2347:
2348: //Array elements default to null so skip filling if body filter
2349: //is null. If not null then only assign to expanded elements.
2350: final Filter body = this.filter;
2351: if (body != null) {
2352: for (int i = current; i < expect; ++i) {
2353: this.attachmentFilters[i] = body;
2354: }
2355: }
2356: }
2357:
2358: //Copy of zero length array is cheap, warm up copyOf.
2359: if (expect == 0) {
2360: this.attachmentFilters = emptyFilterArray();
2361: assert this.attachmentFilters.length == 0;
2362: }
2363: return fixed;
2364: }
2365:
2366: /**
2367: * Sets the size to zero and clears the current buffer.
2368: */
2369: private void reset() {
2370: assert Thread.holdsLock(this);
2371: if (size < data.length) {
2372: Arrays.fill(data, 0, size, null);
2373: } else {
2374: Arrays.fill(data, null);
2375: }
2376: this.size = 0;
2377: }
2378:
2379: /**
2380: * Expands the internal buffer up to the capacity.
2381: */
2382: private void grow() {
2383: assert Thread.holdsLock(this);
2384: final int len = data.length;
2385: int newCapacity = len + (len >> 1) + 1;
2386: if (newCapacity > capacity || newCapacity < len) {
2387: newCapacity = capacity;
2388: }
2389: assert len != capacity : len;
2390: this.data = Arrays.copyOf(data, newCapacity, LogRecord[].class);
2391: this.matched = Arrays.copyOf(matched, newCapacity);
2392: }
2393:
2394: /**
2395: * Configures the handler properties from the log manager. On normal return
2396: * this object will be sealed.
2397: *
2398: * @param props the given mail properties. Maybe null and are never
2399: * captured by this handler.
2400: * @throws SecurityException if a security manager exists and the
2401: * caller does not have <code>LoggingPermission("control")</code>.
2402: * @see #sealed
2403: */
2404: private synchronized void init(final Properties props) {
2405: assert this.errorManager != null;
2406: final String p = getClass().getName();
2407: this.mailProps = new Properties(); //ensure non-null on exception
2408: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
2409: try {
2410: this.contentTypes = FileTypeMap.getDefaultFileTypeMap();
2411: } finally {
2412: getAndSetContextClassLoader(ccl);
2413: }
2414:
2415: //Assign any custom error manager first so it can detect all failures.
2416: initErrorManager(fromLogManager(p.concat(".errorManager")));
2417: initCapacity(fromLogManager(p.concat(".capacity")));
2418: initLevel(fromLogManager(p.concat(".level")));
2419: initEnabled(fromLogManager(p.concat(".enabled")));
2420: initFilter(fromLogManager(p.concat(".filter")));
2421: this.auth = newAuthenticator(fromLogManager(p.concat(".authenticator")));
2422:
2423: initEncoding(fromLogManager(p.concat(".encoding")));
2424: initFormatter(fromLogManager(p.concat(".formatter")));
2425: initComparator(fromLogManager(p.concat(".comparator")));
2426: initComparatorReverse(fromLogManager(p.concat(".comparator.reverse")));
2427: initPushLevel(fromLogManager(p.concat(".pushLevel")));
2428: initPushFilter(fromLogManager(p.concat(".pushFilter")));
2429:
2430: initSubject(fromLogManager(p.concat(".subject")));
2431:
2432: initAttachmentFormaters(fromLogManager(p.concat(".attachment.formatters")));
2433: initAttachmentFilters(fromLogManager(p.concat(".attachment.filters")));
2434: initAttachmentNames(fromLogManager(p.concat(".attachment.names")));
2435:
2436: //Verification of all of the MailHandler properties starts here
2437: //That means setting new object members goes above this comment.
2438: //Entries are always parsed to report any errors.
2439: Properties entries = parseProperties(fromLogManager(p.concat(".mailEntries")));
2440: sealed = true;
2441: boolean verified;
2442: if (props != null) {
2443: //Given properties do not fallback to log manager.
2444: setMailProperties0(copyOf(props));
2445: verified = true;
2446: } else if (entries != null) {
2447: //.mailEntries should fallback to log manager when verify key not present.
2448: verified = setMailProperties0(entries);
2449: } else {
2450: checkAccess();
2451: verified = false;
2452: }
2453:
2454: if (!verified && fromLogManager(p.concat(".verify")) != null) {
2455: verifySettings(initSession());
2456: }
2457: intern(); //Show verify warnings first.
2458: }
2459:
2460: /**
2461: * Interns the error manager, formatters, and filters contained in this
2462: * handler. The comparator is not interned. This method can only be
2463: * called from init after all of formatters and filters are in a constructed
2464: * and in a consistent state.
2465: *
2466: * @since JavaMail 1.5.0
2467: */
2468: private void intern() {
2469: assert Thread.holdsLock(this);
2470: try {
2471: Object canidate;
2472: Object result;
2473: final Map<Object, Object> seen = new HashMap<>();
2474: try {
2475: intern(seen, this.errorManager);
2476: } catch (final SecurityException se) {
2477: reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
2478: }
2479:
2480: try {
2481: canidate = this.filter;
2482: result = intern(seen, canidate);
2483: if (result != canidate && result instanceof Filter) {
2484: this.filter = (Filter) result;
2485: }
2486:
2487: canidate = this.formatter;
2488: result = intern(seen, canidate);
2489: if (result != canidate && result instanceof Formatter) {
2490: this.formatter = (Formatter) result;
2491: }
2492: } catch (final SecurityException se) {
2493: reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
2494: }
2495:
2496: canidate = this.subjectFormatter;
2497: result = intern(seen, canidate);
2498: if (result != canidate && result instanceof Formatter) {
2499: this.subjectFormatter = (Formatter) result;
2500: }
2501:
2502: canidate = this.pushFilter;
2503: result = intern(seen, canidate);
2504: if (result != canidate && result instanceof Filter) {
2505: this.pushFilter = (Filter) result;
2506: }
2507:
2508: for (int i = 0; i < attachmentFormatters.length; ++i) {
2509: canidate = attachmentFormatters[i];
2510: result = intern(seen, canidate);
2511: if (result != canidate && result instanceof Formatter) {
2512: attachmentFormatters[i] = (Formatter) result;
2513: }
2514:
2515: canidate = attachmentFilters[i];
2516: result = intern(seen, canidate);
2517: if (result != canidate && result instanceof Filter) {
2518: attachmentFilters[i] = (Filter) result;
2519: }
2520:
2521: canidate = attachmentNames[i];
2522: result = intern(seen, canidate);
2523: if (result != canidate && result instanceof Formatter) {
2524: attachmentNames[i] = (Formatter) result;
2525: }
2526: }
2527: } catch (final Exception skip) {
2528: reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE);
2529: } catch (final LinkageError skip) {
2530: reportError(skip.getMessage(), new InvocationTargetException(skip),
2531: ErrorManager.OPEN_FAILURE);
2532: }
2533: }
2534:
2535: /**
2536: * If possible performs an intern of the given object into the
2537: * map. If the object can not be interned the given object is returned.
2538: *
2539: * @param m the map used to record the interned values.
2540: * @param o the object to try an intern.
2541: * @return the original object or an intern replacement.
2542: * @throws SecurityException if this operation is not allowed by the
2543: * security manager.
2544: * @throws Exception if there is an unexpected problem.
2545: * @since JavaMail 1.5.0
2546: */
2547: private Object intern(Map<Object, Object> m, Object o) throws Exception {
2548: if (o == null) {
2549: return null;
2550: }
2551:
2552: /**
2553: * The common case is that most objects will not intern. The given
2554: * object has a public no argument constructor or is an instance of a
2555: * TailNameFormatter. TailNameFormatter is safe use as a map key.
2556: * For everything else we create a clone of the given object.
2557: * This is done because of the following:
2558: * 1. Clones can be used to test that a class provides an equals method
2559: * and that the equals method works correctly.
2560: * 2. Calling equals on the given object is assumed to be cheap.
2561: * 3. The intern map can be filtered so it only contains objects that
2562: * can be interned, which reduces the memory footprint.
2563: * 4. Clones are method local garbage.
2564: * 5. Hash code is only called on the clones so bias locking is not
2565: * disabled on the objects the handler will use.
2566: */
2567: final Object key;
2568: if (o.getClass().getName().equals(TailNameFormatter.class.getName())) {
2569: key = o;
2570: } else {
2571: //This call was already made in the LogManagerProperties so this
2572: //shouldn't trigger loading of any lazy reflection code.
2573: key = o.getClass().getConstructor().newInstance();
2574: }
2575:
2576: final Object use;
2577: //Check the classloaders of each object avoiding the security manager.
2578: if (key.getClass() == o.getClass()) {
2579: Object found = m.get(key); //Transitive equals test.
2580: if (found == null) {
2581: //Ensure that equals is symmetric to prove intern is safe.
2582: final boolean right = key.equals(o);
2583: final boolean left = o.equals(key);
2584: if (right && left) {
2585: //Assume hashCode is defined at this point.
2586: found = m.put(o, o);
2587: if (found != null) {
2588: reportNonDiscriminating(key, found);
2589: found = m.remove(key);
2590: if (found != o) {
2591: reportNonDiscriminating(key, found);
2592: m.clear(); //Try to restore order.
2593: }
2594: }
2595: } else {
2596: if (right != left) {
2597: reportNonSymmetric(o, key);
2598: }
2599: }
2600: use = o;
2601: } else {
2602: //Check for a discriminating equals method.
2603: if (o.getClass() == found.getClass()) {
2604: use = found;
2605: } else {
2606: reportNonDiscriminating(o, found);
2607: use = o;
2608: }
2609: }
2610: } else {
2611: use = o;
2612: }
2613: return use;
2614: }
2615:
2616: /**
2617: * Factory method used to create a java.util.logging.SimpleFormatter.
2618: *
2619: * @return a new SimpleFormatter.
2620: * @since JavaMail 1.5.6
2621: */
2622: private static Formatter createSimpleFormatter() {
2623: //Don't force the byte code verifier to load the formatter.
2624: return Formatter.class.cast(new SimpleFormatter());
2625: }
2626:
2627: /**
2628: * Checks a char sequence value for null or empty.
2629: *
2630: * @param s the char sequence.
2631: * @return true if the given string is null or zero length.
2632: */
2633: private static boolean isEmpty(final CharSequence s) {
2634: return s == null || s.length() == 0;
2635: }
2636:
2637: /**
2638: * Checks that a string is not empty and not equal to the literal "null".
2639: *
2640: * @param name the string to check for a value.
2641: * @return true if the string has a valid value.
2642: */
2643: private static boolean hasValue(final String name) {
2644: return !isEmpty(name) && !"null".equalsIgnoreCase(name);
2645: }
2646:
2647: /**
2648: * Parses LogManager string values into objects used by this handler.
2649: *
2650: * @param list the list of attachment filter class names.
2651: * @throws SecurityException if not allowed.
2652: */
2653: private void initAttachmentFilters(final String list) {
2654: assert Thread.holdsLock(this);
2655: assert this.attachmentFormatters != null;
2656: if (!isEmpty(list)) {
2657: final String[] names = list.split(",");
2658: Filter[] a = new Filter[names.length];
2659: for (int i = 0; i < a.length; ++i) {
2660: names[i] = names[i].trim();
2661: if (!"null".equalsIgnoreCase(names[i])) {
2662: try {
2663: a[i] = LogManagerProperties.newFilter(names[i]);
2664: } catch (final SecurityException SE) {
2665: throw SE; //Avoid catch all.
2666: } catch (final Exception E) {
2667: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2668: }
2669: }
2670: }
2671:
2672: this.attachmentFilters = a;
2673: if (alignAttachmentFilters(attachmentFormatters.length)) {
2674: reportError("Attachment filters.",
2675: attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE);
2676: }
2677: } else {
2678: this.attachmentFilters = emptyFilterArray();
2679: alignAttachmentFilters(attachmentFormatters.length);
2680: }
2681: }
2682:
2683: /**
2684: * Parses LogManager string values into objects used by this handler.
2685: *
2686: * @param list the list of attachment formatter class names or literal names.
2687: * @throws SecurityException if not allowed.
2688: */
2689: private void initAttachmentFormaters(final String list) {
2690: assert Thread.holdsLock(this);
2691: if (!isEmpty(list)) {
2692: final Formatter[] a;
2693: final String[] names = list.split(",");
2694: if (names.length == 0) {
2695: a = emptyFormatterArray();
2696: } else {
2697: a = new Formatter[names.length];
2698: }
2699:
2700: for (int i = 0; i < a.length; ++i) {
2701: names[i] = names[i].trim();
2702: if (!"null".equalsIgnoreCase(names[i])) {
2703: try {
2704: a[i] = LogManagerProperties.newFormatter(names[i]);
2705: if (a[i] instanceof TailNameFormatter) {
2706: final Exception CNFE = new ClassNotFoundException(a[i].toString());
2707: reportError("Attachment formatter.", CNFE, ErrorManager.OPEN_FAILURE);
2708: a[i] = createSimpleFormatter();
2709: }
2710: } catch (final SecurityException SE) {
2711: throw SE; //Avoid catch all.
2712: } catch (final Exception E) {
2713: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2714: a[i] = createSimpleFormatter();
2715: }
2716: } else {
2717: a[i] = createSimpleFormatter();
2718: }
2719: }
2720:
2721: this.attachmentFormatters = a;
2722: } else {
2723: this.attachmentFormatters = emptyFormatterArray();
2724: }
2725: }
2726:
2727: /**
2728: * Parses LogManager string values into objects used by this handler.
2729: *
2730: * @param list of formatter class names or literals.
2731: * @throws SecurityException if not allowed.
2732: */
2733: private void initAttachmentNames(final String list) {
2734: assert Thread.holdsLock(this);
2735: assert this.attachmentFormatters != null;
2736:
2737: if (!isEmpty(list)) {
2738: final String[] names = list.split(",");
2739: final Formatter[] a = new Formatter[names.length];
2740: for (int i = 0; i < a.length; ++i) {
2741: names[i] = names[i].trim();
2742: if (!"null".equalsIgnoreCase(names[i])) {
2743: try {
2744: try {
2745: a[i] = LogManagerProperties.newFormatter(names[i]);
2746: } catch (ClassNotFoundException
2747: | ClassCastException literal) {
2748: a[i] = TailNameFormatter.of(names[i]);
2749: }
2750: } catch (final SecurityException SE) {
2751: throw SE; //Avoid catch all.
2752: } catch (final Exception E) {
2753: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2754: }
2755: } else {
2756: a[i] = TailNameFormatter.of(toString(attachmentFormatters[i]));
2757: }
2758: }
2759:
2760: this.attachmentNames = a;
2761: if (alignAttachmentNames(attachmentFormatters.length)) {
2762: reportError("Attachment names.",
2763: attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE);
2764: }
2765: } else {
2766: this.attachmentNames = emptyFormatterArray();
2767: alignAttachmentNames(attachmentFormatters.length);
2768: }
2769: }
2770:
2771: /**
2772: * Parses LogManager string values into objects used by this handler.
2773: *
2774: * @param name the authenticator class name, literal password, or empty string.
2775: * @throws SecurityException if not allowed.
2776: */
2777: private Authenticator newAuthenticator(final String name) {
2778: Authenticator a = null;
2779: if (name != null && !"null".equalsIgnoreCase(name)) {
2780: if (!name.isEmpty()) {
2781: try {
2782: a = LogManagerProperties
2783: .newObjectFrom(name, Authenticator.class);
2784: } catch (final SecurityException SE) {
2785: throw SE;
2786: } catch (final ClassNotFoundException
2787: | ClassCastException literalAuth) {
2788: a = DefaultAuthenticator.of(name);
2789: } catch (final Exception E) {
2790: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2791: } catch (final LinkageError JDK8152515) {
2792: reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE);
2793: }
2794: } else { //Authenticator is installed to provide the user name.
2795: a = DefaultAuthenticator.of(name);
2796: }
2797: }
2798: return a;
2799: }
2800:
2801: /**
2802: * Parses LogManager string values into objects used by this handler.
2803: *
2804: * @param nameOrNumber the level name or number.
2805: * @throws SecurityException if not allowed.
2806: */
2807: private void initLevel(final String nameOrNumber) {
2808: assert Thread.holdsLock(this);
2809: assert disabledLevel == null : disabledLevel;
2810: try {
2811: if (!isEmpty(nameOrNumber)) {
2812: logLevel = Level.parse(nameOrNumber);
2813: } else {
2814: logLevel = Level.WARNING;
2815: }
2816: } catch (final SecurityException SE) {
2817: throw SE; //Avoid catch all.
2818: } catch (final RuntimeException RE) {
2819: reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
2820: logLevel = Level.WARNING;
2821: }
2822: }
2823:
2824: /**
2825: * Parses the given properties lines. Any parsing errors are reported to the
2826: * error manager.
2827: *
2828: * @param entries one or more key/value pairs. An empty string, null value
2829: * or, the literal null are all treated as empty properties and will simply
2830: * clear all existing mail properties assigned to this handler.
2831: * @return the parsed properties or null if entries was null.
2832: * @since Angus Mail 2.0.3
2833: * @see #setMailEntries(java.lang.String)
2834: */
2835: private Properties parseProperties(String entries) {
2836: if (entries != null) {
2837: final Properties props = new Properties();
2838: if (!hasValue(entries)) {
2839: return props;
2840: }
2841:
2842: /**
2843: * The characters # and ! are used for comment lines in properties
2844: * format. The characters \r or \n are not allowed in WildFly form
2845: * validation however, properties comment characters are allowed.
2846: * Comment lines are useless for this handler therefore, "#!"
2847: * characters are used to represent logical lines and are assumed to
2848: * not be present together in a key or value.
2849: */
2850: try {
2851: entries = entries.replace("#!", "\r\n");
2852: //Dynamic cast used so byte code verifier doesn't load StringReader
2853: props.load(Reader.class.cast(new StringReader(entries)));
2854: } catch (IOException | RuntimeException ex) {
2855: reportError(entries, ex, ErrorManager.OPEN_FAILURE);
2856: //Allow a partial load of properties to be set
2857: }
2858: return props;
2859: }
2860: return null;
2861: }
2862:
2863: /**
2864: * Disables this handler if the property was specified in LogManager.
2865: * Assumes that initLevel was called before this method.
2866: *
2867: * @param enabled the string false will only disable this handler.
2868: * @since Angus Mail 2.0.3
2869: */
2870: private void initEnabled(final String enabled) {
2871: assert Thread.holdsLock(this);
2872: assert logLevel != null;
2873: assert capacity != 0;
2874: //By default the Handler is enabled so only need to disable it on init.
2875: if (hasValue(enabled) && !Boolean.parseBoolean(enabled)) {
2876: setEnabled0(false);
2877: }
2878: }
2879:
2880: /**
2881: * Parses LogManager string values into objects used by this handler.
2882: *
2883: * @param name the filter class name or null.
2884: * @throws SecurityException if not allowed.
2885: */
2886: private void initFilter(final String name) {
2887: assert Thread.holdsLock(this);
2888: try {
2889: if (hasValue(name)) {
2890: filter = LogManagerProperties.newFilter(name);
2891: } else {
2892: filter = null;
2893: }
2894: } catch (final SecurityException SE) {
2895: throw SE; //Avoid catch all.
2896: } catch (final Exception E) {
2897: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2898: }
2899: }
2900:
2901: /**
2902: * Parses LogManager string values into objects used by this handler.
2903: *
2904: * @param value the capacity value.
2905: * @throws SecurityException if not allowed.
2906: */
2907: private void initCapacity(final String value) {
2908: assert Thread.holdsLock(this);
2909: final int DEFAULT_CAPACITY = 1000;
2910: try {
2911: if (value != null) {
2912: this.setCapacity0(Integer.parseInt(value));
2913: } else {
2914: this.setCapacity0(DEFAULT_CAPACITY);
2915: }
2916: } catch (final SecurityException SE) {
2917: throw SE; //Avoid catch all.
2918: } catch (final RuntimeException RE) {
2919: reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
2920: }
2921:
2922: if (capacity <= 0) {
2923: capacity = DEFAULT_CAPACITY;
2924: }
2925:
2926: this.data = new LogRecord[1];
2927: this.matched = new int[this.data.length];
2928: }
2929:
2930: /**
2931: * Sets the encoding of this handler.
2932: *
2933: * @param e the encoding name or null.
2934: * @throws SecurityException if not allowed.
2935: */
2936: private void initEncoding(final String e) {
2937: assert Thread.holdsLock(this);
2938: try {
2939: setEncoding0(e);
2940: } catch (final SecurityException SE) {
2941: throw SE; //Avoid catch all.
2942: } catch (UnsupportedEncodingException | RuntimeException UEE) {
2943: reportError(UEE.getMessage(), UEE, ErrorManager.OPEN_FAILURE);
2944: }
2945: }
2946:
2947: /**
2948: * Used to get or create the default ErrorManager used before init.
2949: *
2950: * @return the super error manager or a new ErrorManager.
2951: * @since JavaMail 1.5.3
2952: */
2953: private ErrorManager defaultErrorManager() {
2954: ErrorManager em;
2955: try { //Try to share the super error manager.
2956: em = super.getErrorManager();
2957: } catch (RuntimeException | LinkageError ignore) {
2958: em = null;
2959: }
2960:
2961: //Don't assume that the super call is not null.
2962: if (em == null) {
2963: em = new ErrorManager();
2964: }
2965: return em;
2966: }
2967:
2968: /**
2969: * Creates the error manager for this handler.
2970: *
2971: * @param name the error manager class name.
2972: * @throws SecurityException if not allowed.
2973: */
2974: private void initErrorManager(final String name) {
2975: assert Thread.holdsLock(this);
2976: try {
2977: if (name != null) {
2978: setErrorManager0(LogManagerProperties.newErrorManager(name));
2979: }
2980: } catch (final SecurityException SE) {
2981: throw SE; //Avoid catch all.
2982: } catch (final Exception E) {
2983: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
2984: }
2985: }
2986:
2987: /**
2988: * Parses LogManager string values into objects used by this handler.
2989: *
2990: * @param name the formatter class name or null.
2991: * @throws SecurityException if not allowed.
2992: */
2993: private void initFormatter(final String name) {
2994: assert Thread.holdsLock(this);
2995: try {
2996: if (hasValue(name)) {
2997: final Formatter f
2998: = LogManagerProperties.newFormatter(name);
2999: assert f != null;
3000: if (f instanceof TailNameFormatter == false) {
3001: formatter = f;
3002: } else {
3003: formatter = createSimpleFormatter();
3004: }
3005: } else {
3006: formatter = createSimpleFormatter();
3007: }
3008: } catch (final SecurityException SE) {
3009: throw SE; //Avoid catch all.
3010: } catch (final Exception E) {
3011: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
3012: formatter = createSimpleFormatter();
3013: }
3014: }
3015:
3016: /**
3017: * Creates the comparator for this handler.
3018: *
3019: * @param p the handler class name used as the prefix.
3020: * @throws SecurityException if not allowed.
3021: */
3022: private void initComparator(final String name) {
3023: assert Thread.holdsLock(this);
3024: try {
3025: if (hasValue(name)) {
3026: comparator = LogManagerProperties.newComparator(name);
3027: } else {
3028: comparator = null;
3029: }
3030: } catch (final SecurityException SE) {
3031: throw SE; //Avoid catch all.
3032: } catch (final Exception E) {
3033: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
3034: }
3035: }
3036:
3037:
3038: private void initComparatorReverse(final String reverse) {
3039: if (Boolean.parseBoolean(reverse)) {
3040: if (comparator != null) {
3041: comparator = LogManagerProperties.reverseOrder(comparator);
3042: } else {
3043: IllegalArgumentException E = new IllegalArgumentException(
3044: "No comparator to reverse.");
3045: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
3046: }
3047: }
3048: }
3049:
3050: /**
3051: * Parses LogManager string values into objects used by this handler.
3052: *
3053: * @param nameOrNumber the level name, number, or null for OFF.
3054: * @throws SecurityException if not allowed.
3055: */
3056: private void initPushLevel(final String nameOrNumber) {
3057: assert Thread.holdsLock(this);
3058: try {
3059: if (!isEmpty(nameOrNumber)) {
3060: this.pushLevel = Level.parse(nameOrNumber);
3061: } else {
3062: this.pushLevel = Level.OFF;
3063: }
3064: } catch (final RuntimeException RE) {
3065: reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
3066: }
3067:
3068: if (this.pushLevel == null) {
3069: this.pushLevel = Level.OFF;
3070: }
3071: }
3072:
3073: /**
3074: * Parses LogManager string values into objects used by this handler.
3075: *
3076: * @param name the push filter class name.
3077: * @throws SecurityException if not allowed.
3078: */
3079: private void initPushFilter(final String name) {
3080: assert Thread.holdsLock(this);
3081: try {
3082: if (hasValue(name)) {
3083: this.pushFilter = LogManagerProperties.newFilter(name);
3084: } else {
3085: this.pushFilter = null;
3086: }
3087: } catch (final SecurityException SE) {
3088: throw SE; //Avoid catch all.
3089: } catch (final Exception E) {
3090: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
3091: }
3092: }
3093:
3094: /**
3095: * Creates the subject formatter used by this handler.
3096: *
3097: * @param name the formatter class name, string literal, or null.
3098: * @throws SecurityException if not allowed.
3099: */
3100: private void initSubject(String name) {
3101: assert Thread.holdsLock(this);
3102: if (isWriting) {
3103: throw new IllegalStateException();
3104: }
3105:
3106: if (name == null) { //Soft dependency on CollectorFormatter.
3107: name = "org.eclipse.angus.mail.util.logging.CollectorFormatter";
3108: }
3109:
3110: if (hasValue(name)) {
3111: try {
3112: this.subjectFormatter = LogManagerProperties.newFormatter(name);
3113: } catch (final SecurityException SE) {
3114: throw SE; //Avoid catch all.
3115: } catch (ClassNotFoundException
3116: | ClassCastException literalSubject) {
3117: this.subjectFormatter = TailNameFormatter.of(name);
3118: } catch (final Exception E) {
3119: this.subjectFormatter = TailNameFormatter.of(name);
3120: reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
3121: }
3122: } else { //User has forced empty or literal null.
3123: this.subjectFormatter = TailNameFormatter.of(name);
3124: }
3125: }
3126:
3127: /**
3128: * Check if any attachment would actually format the given
3129: * <code>LogRecord</code>. This method does not check if the handler
3130: * is level is set to OFF or if the handler is closed.
3131: *
3132: * @param record a <code>LogRecord</code>
3133: * @return true if the <code>LogRecord</code> would be formatted.
3134: */
3135: private boolean isAttachmentLoggable(final LogRecord record) {
3136: final Filter[] filters = readOnlyAttachmentFilters();
3137: for (int i = 0; i < filters.length; ++i) {
3138: final Filter f = filters[i];
3139: if (f == null || f.isLoggable(record)) {
3140: setMatchedPart(i);
3141: return true;
3142: }
3143: }
3144: return false;
3145: }
3146:
3147: /**
3148: * Check if this <code>Handler</code> would push after storing the
3149: * <code>LogRecord</code> into its internal buffer.
3150: *
3151: * @param record a <code>LogRecord</code>
3152: * @return true if the <code>LogRecord</code> triggers an email push.
3153: * @throws NullPointerException if tryMutex was not called.
3154: */
3155: private boolean isPushable(final LogRecord record) {
3156: assert Thread.holdsLock(this);
3157: final int value = getPushLevel().intValue();
3158: if (value == offValue || record.getLevel().intValue() < value) {
3159: return false;
3160: }
3161:
3162: final Filter push = getPushFilter();
3163: if (push == null) {
3164: return true;
3165: }
3166:
3167: final int match = getMatchedPart();
3168: if ((match == -1 && getFilter() == push)
3169: || (match >= 0 && attachmentFilters[match] == push)) {
3170: return true;
3171: } else {
3172: return push.isLoggable(record);
3173: }
3174: }
3175:
3176: /**
3177: * Used to perform push or flush.
3178: *
3179: * @param priority true for high priority otherwise false for normal.
3180: * @param code the error manager code.
3181: */
3182: private void push(final boolean priority, final int code) {
3183: if (tryMutex()) {
3184: try {
3185: final Message msg = writeLogRecords(code);
3186: if (msg != null) {
3187: send(msg, priority, code);
3188: }
3189: } catch (final LinkageError JDK8152515) {
3190: reportLinkageError(JDK8152515, code);
3191: } finally {
3192: releaseMutex();
3193: }
3194: } else {
3195: reportUnPublishedError(null);
3196: }
3197: }
3198:
3199: /**
3200: * Used to send the generated email or write its contents to the
3201: * error manager for this handler. This method does not hold any
3202: * locks so new records can be added to this handler during a send or
3203: * failure.
3204: *
3205: * @param msg the message or null.
3206: * @param priority true for high priority or false for normal.
3207: * @param code the ErrorManager code.
3208: * @throws NullPointerException if message is null.
3209: */
3210: private void send(Message msg, boolean priority, int code) {
3211: try {
3212: envelopeFor(msg, priority);
3213: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3214: try { //JDK-8025251
3215: Transport.send(msg); //Calls save changes.
3216: } finally {
3217: getAndSetContextClassLoader(ccl);
3218: }
3219: } catch (final Exception e) {
3220: reportError(msg, e, code);
3221: }
3222: }
3223:
3224: /**
3225: * Performs a sort on the records if needed.
3226: * Any exception thrown during a sort is considered a formatting error.
3227: */
3228: private void sort() {
3229: assert Thread.holdsLock(this);
3230: if (comparator != null) {
3231: try {
3232: if (size != 1) {
3233: Arrays.sort(data, 0, size, comparator);
3234: } else {
3235: if (comparator.compare(data[0], data[0]) != 0) {
3236: throw new IllegalArgumentException(
3237: comparator.getClass().getName());
3238: }
3239: }
3240: } catch (final RuntimeException RE) {
3241: reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
3242: }
3243: }
3244: }
3245:
3246: /**
3247: * Formats all records in the buffer and places the output in a Message.
3248: * This method under most conditions will catch, report, and continue when
3249: * exceptions occur. This method holds a lock on this handler.
3250: *
3251: * @param code the error manager code.
3252: * @return null if there are no records or is currently in a push.
3253: * Otherwise a new message is created with a formatted message and
3254: * attached session.
3255: */
3256: private Message writeLogRecords(final int code) {
3257: try {
3258: synchronized (this) {
3259: if (size > 0 && !isWriting) {
3260: isWriting = true;
3261: try {
3262: return writeLogRecords0();
3263: } finally {
3264: isWriting = false;
3265: if (size > 0) {
3266: reset();
3267: }
3268: }
3269: }
3270: }
3271: } catch (final Exception e) {
3272: reportError(e.getMessage(), e, code);
3273: }
3274: return null;
3275: }
3276:
3277: /**
3278: * Formats all records in the buffer and places the output in a Message.
3279: * This method under most conditions will catch, report, and continue when
3280: * exceptions occur.
3281: *
3282: * @return null if there are no records or is currently in a push. Otherwise
3283: * a new message is created with a formatted message and attached session.
3284: * @throws MessagingException if there is a problem.
3285: * @throws IOException if there is a problem.
3286: * @throws RuntimeException if there is an unexpected problem.
3287: * @since JavaMail 1.5.3
3288: */
3289: private Message writeLogRecords0() throws Exception {
3290: assert Thread.holdsLock(this);
3291: sort();
3292: if (session == null) {
3293: initSession();
3294: }
3295: MimeMessage msg = new MimeMessage(session);
3296:
3297: /**
3298: * Parts are lazily created when an attachment performs a getHead
3299: * call. Therefore, a null part at an index means that the head is
3300: * required.
3301: */
3302: MimeBodyPart[] parts = new MimeBodyPart[attachmentFormatters.length];
3303:
3304: /**
3305: * The buffers are lazily created when the part requires a getHead.
3306: */
3307: StringBuilder[] buffers = new StringBuilder[parts.length];
3308: StringBuilder buf = null;
3309: final MimePart body;
3310: if (parts.length == 0) {
3311: msg.setDescription(descriptionFrom(
3312: getFormatter(), getFilter(), subjectFormatter));
3313: body = msg;
3314: } else {
3315: msg.setDescription(descriptionFrom(
3316: comparator, pushLevel, pushFilter));
3317: body = createBodyPart();
3318: }
3319:
3320: appendSubject(msg, head(subjectFormatter));
3321: final Formatter bodyFormat = getFormatter();
3322: final Filter bodyFilter = getFilter();
3323:
3324: Locale lastLocale = null;
3325: for (int ix = 0; ix < size; ++ix) {
3326: boolean formatted = false;
3327: final int match = matched[ix];
3328: final LogRecord r = data[ix];
3329: data[ix] = null; //Clear while formatting.
3330:
3331: final Locale locale = localeFor(r);
3332: appendSubject(msg, format(subjectFormatter, r));
3333: Filter lmf = null; //Identity of last matched filter.
3334: if (bodyFilter == null || match == -1 || parts.length == 0
3335: || (match < -1 && bodyFilter.isLoggable(r))) {
3336: lmf = bodyFilter;
3337: if (buf == null) {
3338: buf = new StringBuilder();
3339: buf.append(head(bodyFormat));
3340: }
3341: formatted = true;
3342: buf.append(format(bodyFormat, r));
3343: if (locale != null && !locale.equals(lastLocale)) {
3344: appendContentLang(body, locale);
3345: }
3346: }
3347:
3348: for (int i = 0; i < parts.length; ++i) {
3349: //A match index less than the attachment index means that
3350: //the filter has not seen this record.
3351: final Filter af = attachmentFilters[i];
3352: if (af == null || lmf == af || match == i
3353: || (match < i && af.isLoggable(r))) {
3354: if (lmf == null && af != null) {
3355: lmf = af;
3356: }
3357: if (parts[i] == null) {
3358: parts[i] = createBodyPart(i);
3359: buffers[i] = new StringBuilder();
3360: buffers[i].append(head(attachmentFormatters[i]));
3361: appendFileName(parts[i], head(attachmentNames[i]));
3362: }
3363: formatted = true;
3364: appendFileName(parts[i], format(attachmentNames[i], r));
3365: buffers[i].append(format(attachmentFormatters[i], r));
3366: if (locale != null && !locale.equals(lastLocale)) {
3367: appendContentLang(parts[i], locale);
3368: }
3369: }
3370: }
3371:
3372: if (formatted) {
3373: if (body != msg && locale != null
3374: && !locale.equals(lastLocale)) {
3375: appendContentLang(msg, locale);
3376: }
3377: } else { //Belongs to no mime part.
3378: reportFilterError(r);
3379: }
3380: lastLocale = locale;
3381: }
3382: this.size = 0;
3383:
3384: for (int i = parts.length - 1; i >= 0; --i) {
3385: if (parts[i] != null) {
3386: appendFileName(parts[i], tail(attachmentNames[i], "err"));
3387: buffers[i].append(tail(attachmentFormatters[i], ""));
3388:
3389: if (buffers[i].length() > 0) {
3390: String name = parts[i].getFileName();
3391: if (isEmpty(name)) { //Exceptional case.
3392: name = toString(attachmentFormatters[i]);
3393: parts[i].setFileName(name);
3394: }
3395: setContent(parts[i], buffers[i], getContentType(name));
3396: } else {
3397: setIncompleteCopy(msg);
3398: parts[i] = null; //Skip this part.
3399: }
3400: buffers[i] = null;
3401: }
3402: }
3403:
3404: if (buf != null) {
3405: buf.append(tail(bodyFormat, ""));
3406: //This body part is always added, even if the buffer is empty,
3407: //so the body is never considered an incomplete-copy.
3408: } else {
3409: buf = new StringBuilder(0);
3410: }
3411:
3412: appendSubject(msg, tail(subjectFormatter, ""));
3413:
3414: String contentType = contentTypeOf(buf);
3415: String altType = contentTypeOf(bodyFormat);
3416: setContent(body, buf, altType == null ? contentType : altType);
3417: if (body != msg) {
3418: final MimeMultipart multipart = createMultipart();
3419: //assert body instanceof BodyPart : body;
3420: multipart.addBodyPart((BodyPart) body);
3421:
3422: for (int i = 0; i < parts.length; ++i) {
3423: if (parts[i] != null) {
3424: multipart.addBodyPart(parts[i]);
3425: }
3426: }
3427: msg.setContent(multipart);
3428: }
3429:
3430: return msg;
3431: }
3432:
3433: /**
3434: * Checks all of the settings if the caller requests a verify and a verify
3435: * was not performed yet and no verify is in progress. A verify is
3436: * performed on create because this handler may be at the end of a handler
3437: * chain and therefore may not see any log records until LogManager.reset()
3438: * is called and at that time all of the settings have been cleared.
3439: *
3440: * @param session the current session or null.
3441: * @return true if verification key was present.
3442: * @since JavaMail 1.4.4
3443: */
3444: private boolean verifySettings(final Session session) {
3445: try {
3446: if (session == null) {
3447: return false;
3448: }
3449:
3450: final Properties props = session.getProperties();
3451: final Object check = props.put("verify", "");
3452: if (check == null) {
3453: return false;
3454: }
3455:
3456: if (check instanceof String) {
3457: String value = (String) check;
3458: //Perform the verify if needed.
3459: if (hasValue(value)) {
3460: verifySettings0(session, value);
3461: }
3462: return true;
3463: } else { //Pass some invalid string.
3464: verifySettings0(session, check.getClass().toString());
3465: }
3466: } catch (final LinkageError JDK8152515) {
3467: reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE);
3468: }
3469: return false;
3470: }
3471:
3472: /**
3473: * Checks all of the settings using the given setting.
3474: * This triggers the LogManagerProperties to copy all of the mail
3475: * settings without explictly knowing them. Once all of the properties
3476: * are copied this handler can handle LogManager.reset clearing all of the
3477: * properties. It is expected that this method is, at most, only called
3478: * once per session.
3479: *
3480: * @param session the current session.
3481: * @param verify the type of verify to perform.
3482: * @since JavaMail 1.4.4
3483: */
3484: private void verifySettings0(Session session, String verify) {
3485: assert verify != null : (String) null;
3486: if (!"local".equals(verify) && !"remote".equals(verify)
3487: && !"limited".equals(verify) && !"resolve".equals(verify)
3488: && !"login".equals(verify)) {
3489: reportError("Verify must be 'limited', local', "
3490: + "'resolve', 'login', or 'remote'.",
3491: new IllegalArgumentException(verify),
3492: ErrorManager.OPEN_FAILURE);
3493: return;
3494: }
3495:
3496: final MimeMessage abort = new MimeMessage(session);
3497: final String msg;
3498: if (!"limited".equals(verify)) {
3499: msg = "Local address is "
3500: + InternetAddress.getLocalAddress(session) + '.';
3501:
3502: try { //Verify subclass or declared mime charset.
3503: Charset.forName(getEncodingName());
3504: } catch (final RuntimeException RE) {
3505: UnsupportedEncodingException UEE =
3506: new UnsupportedEncodingException(RE.toString());
3507: UEE.initCause(RE);
3508: reportError(msg, UEE, ErrorManager.FORMAT_FAILURE);
3509: }
3510: } else {
3511: msg = "Skipping local address check.";
3512: }
3513:
3514: //Perform all of the copy actions first.
3515: String[] atn;
3516: synchronized (this) { //Create the subject.
3517: appendSubject(abort, head(subjectFormatter));
3518: appendSubject(abort, tail(subjectFormatter, ""));
3519: atn = new String[attachmentNames.length];
3520: for (int i = 0; i < atn.length; ++i) {
3521: atn[i] = head(attachmentNames[i]);
3522: if (atn[i].length() == 0) {
3523: atn[i] = tail(attachmentNames[i], "");
3524: } else {
3525: atn[i] = atn[i].concat(tail(attachmentNames[i], ""));
3526: }
3527: }
3528: }
3529:
3530: setIncompleteCopy(abort); //Original body part is never added.
3531: envelopeFor(abort, true);
3532: saveChangesNoContent(abort, msg);
3533: try {
3534: //Ensure transport provider is installed.
3535: Address[] all = abort.getAllRecipients();
3536: if (all == null) { //Don't pass null to sendMessage.
3537: all = new InternetAddress[0];
3538: }
3539: Transport t;
3540: try {
3541: final Address[] any = all.length != 0 ? all : abort.getFrom();
3542: if (any != null && any.length != 0) {
3543: t = session.getTransport(any[0]);
3544: session.getProperty("mail.transport.protocol"); //Force copy
3545: } else {
3546: MessagingException me = new MessagingException(
3547: "No recipient or from address.");
3548: reportError(msg, me, ErrorManager.OPEN_FAILURE);
3549: throw me;
3550: }
3551: } catch (final MessagingException protocol) {
3552: //Switching the CCL emulates the current send behavior.
3553: Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3554: try {
3555: t = session.getTransport();
3556: } catch (final MessagingException fail) {
3557: throw attach(protocol, fail);
3558: } finally {
3559: getAndSetContextClassLoader(ccl);
3560: }
3561: }
3562:
3563: String local = null;
3564: if ("remote".equals(verify) || "login".equals(verify)) {
3565: MessagingException closed = null;
3566: t.connect();
3567: try {
3568: try {
3569: //Capture localhost while connection is open.
3570: local = getLocalHost(t);
3571:
3572: //A message without content will fail at message writeTo
3573: //when sendMessage is called. This allows the handler
3574: //to capture all mail properties set in the LogManager.
3575: if ("remote".equals(verify)) {
3576: t.sendMessage(abort, all);
3577: }
3578: } finally {
3579: try {
3580: t.close();
3581: } catch (final MessagingException ME) {
3582: closed = ME;
3583: }
3584: }
3585: //Close the transport before reporting errors.
3586: if ("remote".equals(verify)) {
3587: reportUnexpectedSend(abort, verify, null);
3588: } else {
3589: final String protocol = t.getURLName().getProtocol();
3590: verifyProperties(session, protocol);
3591: }
3592: } catch (final SendFailedException sfe) {
3593: Address[] recip = sfe.getInvalidAddresses();
3594: if (recip != null && recip.length != 0) {
3595: setErrorContent(abort, verify, sfe);
3596: reportError(abort, sfe, ErrorManager.OPEN_FAILURE);
3597: }
3598:
3599: recip = sfe.getValidSentAddresses();
3600: if (recip != null && recip.length != 0) {
3601: reportUnexpectedSend(abort, verify, sfe);
3602: }
3603: } catch (final MessagingException ME) {
3604: if (!isMissingContent(abort, ME)) {
3605: setErrorContent(abort, verify, ME);
3606: reportError(abort, ME, ErrorManager.OPEN_FAILURE);
3607: }
3608: }
3609:
3610: if (closed != null) {
3611: setErrorContent(abort, verify, closed);
3612: reportError(abort, closed, ErrorManager.CLOSE_FAILURE);
3613: }
3614: } else {
3615: //Force a property copy, JDK-7092981.
3616: final String protocol = t.getURLName().getProtocol();
3617: verifyProperties(session, protocol);
3618: String mailHost = session.getProperty("mail."
3619: + protocol + ".host");
3620: if (isEmpty(mailHost)) {
3621: mailHost = session.getProperty("mail.host");
3622: } else {
3623: session.getProperty("mail.host");
3624: }
3625:
3626: local = session.getProperty("mail." + protocol + ".localhost");
3627: if (isEmpty(local)) {
3628: local = session.getProperty("mail."
3629: + protocol + ".localaddress");
3630: } else {
3631: session.getProperty("mail." + protocol + ".localaddress");
3632: }
3633:
3634: if ("resolve".equals(verify)) {
3635: try { //Resolve the remote host name.
3636: String transportHost = t.getURLName().getHost();
3637: if (!isEmpty(transportHost)) {
3638: verifyHost(transportHost);
3639: if (!transportHost.equalsIgnoreCase(mailHost)) {
3640: verifyHost(mailHost);
3641: }
3642: } else {
3643: verifyHost(mailHost);
3644: }
3645: } catch (final RuntimeException | IOException IOE) {
3646: MessagingException ME =
3647: new MessagingException(msg, IOE);
3648: setErrorContent(abort, verify, ME);
3649: reportError(abort, ME, ErrorManager.OPEN_FAILURE);
3650: }
3651: }
3652: }
3653:
3654: if (!"limited".equals(verify)) {
3655: try { //Verify host name and hit the host name cache.
3656: if (!"remote".equals(verify) && !"login".equals(verify)) {
3657: local = getLocalHost(t);
3658: }
3659: verifyHost(local);
3660: } catch (final RuntimeException | IOException IOE) {
3661: MessagingException ME = new MessagingException(msg, IOE);
3662: setErrorContent(abort, verify, ME);
3663: reportError(abort, ME, ErrorManager.OPEN_FAILURE);
3664: }
3665:
3666: try { //Verify that the DataHandler can be loaded.
3667: Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3668: try {
3669: //Always load the multipart classes.
3670: MimeMultipart multipart = new MimeMultipart();
3671: MimeBodyPart[] ambp = new MimeBodyPart[atn.length];
3672: final MimeBodyPart body;
3673: final String bodyContentType;
3674: synchronized (this) {
3675: bodyContentType = contentTypeOf(getFormatter());
3676: body = createBodyPart();
3677: for (int i = 0; i < atn.length; ++i) {
3678: ambp[i] = createBodyPart(i);
3679: ambp[i].setFileName(atn[i]);
3680: //Convert names to mime type under lock.
3681: atn[i] = getContentType(atn[i]);
3682: }
3683: }
3684:
3685: body.setDescription(verify);
3686: setContent(body, "", bodyContentType);
3687: multipart.addBodyPart(body);
3688: for (int i = 0; i < ambp.length; ++i) {
3689: ambp[i].setDescription(verify);
3690: setContent(ambp[i], "", atn[i]);
3691: }
3692:
3693: abort.setContent(multipart);
3694: abort.saveChanges();
3695: abort.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE));
3696: } finally {
3697: getAndSetContextClassLoader(ccl);
3698: }
3699: } catch (final IOException IOE) {
3700: MessagingException ME = new MessagingException(msg, IOE);
3701: setErrorContent(abort, verify, ME);
3702: reportError(abort, ME, ErrorManager.FORMAT_FAILURE);
3703: }
3704: }
3705:
3706: //Verify all recipients.
3707: if (all.length != 0) {
3708: verifyAddresses(all);
3709: } else {
3710: throw new MessagingException("No recipient addresses.");
3711: }
3712:
3713: //Verify from and sender addresses.
3714: Address[] from = abort.getFrom();
3715: Address sender = abort.getSender();
3716: if (sender instanceof InternetAddress) {
3717: ((InternetAddress) sender).validate();
3718: }
3719:
3720: //If from address is declared then check sender.
3721: if (abort.getHeader("From", ",") != null && from.length != 0) {
3722: verifyAddresses(from);
3723: for (int i = 0; i < from.length; ++i) {
3724: if (from[i].equals(sender)) {
3725: MessagingException ME = new MessagingException(
3726: "Sender address '" + sender
3727: + "' equals from address.");
3728: throw new MessagingException(msg, ME);
3729: }
3730: }
3731: } else {
3732: if (sender == null) {
3733: MessagingException ME = new MessagingException(
3734: "No from or sender address.");
3735: throw new MessagingException(msg, ME);
3736: }
3737: }
3738:
3739: //Verify reply-to addresses.
3740: verifyAddresses(abort.getReplyTo());
3741: } catch (final Exception ME) {
3742: setErrorContent(abort, verify, ME);
3743: reportError(abort, ME, ErrorManager.OPEN_FAILURE);
3744: }
3745: }
3746:
3747: /**
3748: * Handles all exceptions thrown when save changes is called on a message
3749: * that doesn't have any content.
3750: *
3751: * @param abort the message requiring save changes.
3752: * @param msg the error description.
3753: * @since JavaMail 1.6.0
3754: */
3755: private void saveChangesNoContent(final Message abort, final String msg) {
3756: if (abort != null) {
3757: try {
3758: try {
3759: abort.saveChanges();
3760: } catch (final NullPointerException xferEncoding) {
3761: //Workaround GNU JavaMail bug in MimeUtility.getEncoding
3762: //when the mime message has no content.
3763: try {
3764: String cte = "Content-Transfer-Encoding";
3765: if (abort.getHeader(cte) == null) {
3766: abort.setHeader(cte, EncoderTypes.BASE_64.getEncoder());
3767: abort.saveChanges();
3768: } else {
3769: throw xferEncoding;
3770: }
3771: } catch (RuntimeException | MessagingException e) {
3772: if (e != xferEncoding) {
3773: e.addSuppressed(xferEncoding);
3774: }
3775: throw e;
3776: }
3777: }
3778: } catch (RuntimeException | MessagingException ME) {
3779: reportError(msg, ME, ErrorManager.FORMAT_FAILURE);
3780: }
3781: }
3782: }
3783:
3784: /**
3785: * Cache common session properties into the LogManagerProperties. This is
3786: * a workaround for JDK-7092981.
3787: *
3788: * @param session the session.
3789: * @param protocol the mail protocol.
3790: * @throws NullPointerException if session is null.
3791: * @since JavaMail 1.6.0
3792: */
3793: private static void verifyProperties(Session session, String protocol) {
3794: session.getProperty("mail.from");
3795: session.getProperty("mail." + protocol + ".from");
3796: session.getProperty("mail.dsn.ret");
3797: session.getProperty("mail." + protocol + ".dsn.ret");
3798: session.getProperty("mail.dsn.notify");
3799: session.getProperty("mail." + protocol + ".dsn.notify");
3800: session.getProperty("mail." + protocol + ".port");
3801: session.getProperty("mail.user");
3802: session.getProperty("mail." + protocol + ".user");
3803: session.getProperty("mail." + protocol + ".localport");
3804: }
3805:
3806: /**
3807: * Perform a lookup of the host address or FQDN.
3808: *
3809: * @param host the host or null.
3810: * @return the address.
3811: * @throws IOException if the host name is not valid.
3812: * @throws SecurityException if security manager is present and doesn't
3813: * allow access to check connect permission.
3814: * @since JavaMail 1.5.0
3815: */
3816: private static InetAddress verifyHost(String host) throws IOException {
3817: InetAddress a;
3818: if (isEmpty(host)) {
3819: a = InetAddress.getLocalHost();
3820: } else {
3821: a = InetAddress.getByName(host);
3822: }
3823: if (a.getCanonicalHostName().length() == 0) {
3824: throw new UnknownHostException();
3825: }
3826: return a;
3827: }
3828:
3829: /**
3830: * Calls validate for every address given.
3831: * If the addresses given are null, empty or not an InternetAddress then
3832: * the check is skipped.
3833: *
3834: * @param all any address array, null or empty.
3835: * @throws AddressException if there is a problem.
3836: * @since JavaMail 1.4.5
3837: */
3838: private static void verifyAddresses(Address[] all) throws AddressException {
3839: if (all != null) {
3840: for (int i = 0; i < all.length; ++i) {
3841: final Address a = all[i];
3842: if (a instanceof InternetAddress) {
3843: ((InternetAddress) a).validate();
3844: }
3845: }
3846: }
3847: }
3848:
3849: /**
3850: * Reports that an empty content message was sent and should not have been.
3851: *
3852: * @param msg the MimeMessage.
3853: * @param verify the verify enum.
3854: * @param cause the exception that caused the problem or null.
3855: * @since JavaMail 1.4.5
3856: */
3857: private void reportUnexpectedSend(MimeMessage msg, String verify, Exception cause) {
3858: final MessagingException write = new MessagingException(
3859: "An empty message was sent.", cause);
3860: setErrorContent(msg, verify, write);
3861: reportError(msg, write, ErrorManager.OPEN_FAILURE);
3862: }
3863:
3864: /**
3865: * Creates and sets the message content from the given Throwable.
3866: * When verify fails, this method fixes the 'abort' message so that any
3867: * created envelope data can be used in the error manager.
3868: *
3869: * @param msg the message with or without content.
3870: * @param verify the verify enum.
3871: * @param t the throwable or null.
3872: * @since JavaMail 1.4.5
3873: */
3874: private void setErrorContent(MimeMessage msg, String verify, Throwable t) {
3875: try { //Add content so toRawString doesn't fail.
3876: final MimeBodyPart body;
3877: final String subjectType;
3878: final String msgDesc;
3879: synchronized (this) {
3880: body = createBodyPart();
3881: msgDesc = descriptionFrom(comparator, pushLevel, pushFilter);
3882: subjectType = getClassId(subjectFormatter);
3883: }
3884:
3885: body.setDescription("Formatted using "
3886: + (t == null ? Throwable.class.getName()
3887: : t.getClass().getName()) + ", filtered with "
3888: + verify + ", and named by "
3889: + subjectType + '.');
3890: setContent(body, toMsgString(t), "text/plain");
3891: final MimeMultipart multipart = new MimeMultipart();
3892: multipart.addBodyPart(body);
3893: msg.setContent(multipart);
3894: msg.setDescription(msgDesc);
3895: setAcceptLang(msg);
3896: msg.saveChanges();
3897: } catch (MessagingException | RuntimeException ME) {
3898: reportError("Unable to create body.", ME, ErrorManager.OPEN_FAILURE);
3899: }
3900: }
3901:
3902: /**
3903: * Used to update the cached session object based on changes in
3904: * mail properties or authenticator.
3905: *
3906: * @return the current session or null if no verify is required.
3907: */
3908: private Session updateSession() {
3909: assert Thread.holdsLock(this);
3910: final Session settings;
3911: if (mailProps.getProperty("verify") != null) {
3912: settings = initSession();
3913: assert settings == session : session;
3914: } else {
3915: session = null; //Remove old session.
3916: settings = null;
3917: }
3918: return settings;
3919: }
3920:
3921: /**
3922: * Creates a session using a proxy properties object.
3923: *
3924: * @return the session that was created and assigned.
3925: */
3926: private Session initSession() {
3927: assert Thread.holdsLock(this);
3928: final String p = getClass().getName();
3929: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3930: try {
3931: LogManagerProperties proxy = new LogManagerProperties(mailProps, p);
3932: session = Session.getInstance(proxy, auth);
3933: } finally {
3934: getAndSetContextClassLoader(ccl);
3935: }
3936: return session;
3937: }
3938:
3939: /**
3940: * Creates all of the envelope information for a message.
3941: * This method is safe to call outside of a lock because the message
3942: * provides the safe snapshot of the mail properties.
3943: *
3944: * @param msg the Message to write the envelope information.
3945: * @param priority true for high priority.
3946: */
3947: private void envelopeFor(Message msg, boolean priority) {
3948: setAcceptLang(msg);
3949: setFrom(msg);
3950: if (!setRecipient(msg, "mail.to", Message.RecipientType.TO)) {
3951: setDefaultRecipient(msg, Message.RecipientType.TO);
3952: }
3953: setRecipient(msg, "mail.cc", Message.RecipientType.CC);
3954: setRecipient(msg, "mail.bcc", Message.RecipientType.BCC);
3955: setReplyTo(msg);
3956: setSender(msg);
3957: setMailer(msg);
3958: setAutoSubmitted(msg);
3959: if (priority) {
3960: setPriority(msg);
3961: }
3962:
3963: try {
3964: msg.setSentDate(new java.util.Date());
3965: } catch (final MessagingException ME) {
3966: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
3967: }
3968: }
3969:
3970: private MimeMultipart createMultipart() throws MessagingException {
3971: assert Thread.holdsLock(this);
3972: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3973: try {
3974: return new MimeMultipart();
3975: } finally {
3976: getAndSetContextClassLoader(ccl);
3977: }
3978: }
3979:
3980: /**
3981: * Factory to create the in-line body part.
3982: *
3983: * @return a body part with default headers set.
3984: * @throws MessagingException if there is a problem.
3985: */
3986: private MimeBodyPart createBodyPart() throws MessagingException {
3987: assert Thread.holdsLock(this);
3988: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
3989: try {
3990: final MimeBodyPart part = new MimeBodyPart();
3991: part.setDisposition(Part.INLINE);
3992: part.setDescription(descriptionFrom(getFormatter(),
3993: getFilter(), subjectFormatter));
3994: setAcceptLang(part);
3995: return part;
3996: } finally {
3997: getAndSetContextClassLoader(ccl);
3998: }
3999: }
4000:
4001: /**
4002: * Factory to create the attachment body part.
4003: *
4004: * @param index the attachment index.
4005: * @return a body part with default headers set.
4006: * @throws MessagingException if there is a problem.
4007: * @throws IndexOutOfBoundsException if the given index is not an valid
4008: * attachment index.
4009: */
4010: private MimeBodyPart createBodyPart(int index) throws MessagingException {
4011: assert Thread.holdsLock(this);
4012: final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
4013: try {
4014: final MimeBodyPart part = new MimeBodyPart();
4015: part.setDisposition(Part.ATTACHMENT);
4016: part.setDescription(descriptionFrom(
4017: attachmentFormatters[index],
4018: attachmentFilters[index],
4019: attachmentNames[index]));
4020: setAcceptLang(part);
4021: return part;
4022: } finally {
4023: getAndSetContextClassLoader(ccl);
4024: }
4025: }
4026:
4027: /**
4028: * Gets the description for the MimeMessage itself.
4029: * The push level and filter are included because they play a role in
4030: * formatting of a message when triggered or not triggered.
4031: *
4032: * @param c the comparator.
4033: * @param l the pushLevel.
4034: * @param f the pushFilter
4035: * @return the description.
4036: * @throws NullPointerException if level is null.
4037: * @since JavaMail 1.4.5
4038: */
4039: private String descriptionFrom(Comparator<?> c, Level l, Filter f) {
4040: return "Sorted using " + (c == null ? "no comparator"
4041: : c.getClass().getName()) + ", pushed when " + l.getName()
4042: + ", and " + (f == null ? "no push filter"
4043: : f.getClass().getName()) + '.';
4044: }
4045:
4046: /**
4047: * Creates a description for a body part.
4048: *
4049: * @param f the content formatter.
4050: * @param filter the content filter.
4051: * @param name the naming formatter.
4052: * @return the description for the body part.
4053: */
4054: private String descriptionFrom(Formatter f, Filter filter, Formatter name) {
4055: return "Formatted using " + getClassId(f)
4056: + ", filtered with " + (filter == null ? "no filter"
4057: : filter.getClass().getName()) + ", and named by "
4058: + getClassId(name) + '.';
4059: }
4060:
4061: /**
4062: * Gets a class name represents the behavior of the formatter.
4063: * The class name may not be assignable to a Formatter.
4064: *
4065: * @param f the formatter.
4066: * @return a class name that represents the given formatter.
4067: * @throws NullPointerException if the parameter is null.
4068: * @since JavaMail 1.4.5
4069: */
4070: private String getClassId(final Formatter f) {
4071: if (f instanceof TailNameFormatter) {
4072: return String.class.getName(); //Literal string.
4073: } else {
4074: return f.getClass().getName();
4075: }
4076: }
4077:
4078: /**
4079: * Ensure that a formatter creates a valid string for a part name.
4080: *
4081: * @param f the formatter.
4082: * @return the to string value or the class name.
4083: */
4084: private String toString(final Formatter f) {
4085: //Should never be null but, guard against formatter bugs.
4086: final String name = f.toString();
4087: if (!isEmpty(name)) {
4088: return name;
4089: } else {
4090: return getClassId(f);
4091: }
4092: }
4093:
4094: /**
4095: * Constructs a file name from a formatter. This method is called often
4096: * but, rarely does any work.
4097: *
4098: * @param part to append to.
4099: * @param chunk non null string to append.
4100: */
4101: private void appendFileName(final Part part, final String chunk) {
4102: if (chunk != null) {
4103: if (chunk.length() > 0) {
4104: appendFileName0(part, chunk);
4105: }
4106: } else {
4107: reportNullError(ErrorManager.FORMAT_FAILURE);
4108: }
4109: }
4110:
4111: /**
4112: * It is assumed that file names are short and that in most cases
4113: * getTail will be the only method that will produce a result.
4114: *
4115: * @param part to append to.
4116: * @param chunk non null string to append.
4117: */
4118: private void appendFileName0(final Part part, String chunk) {
4119: try {
4120: //Remove all control character groups.
4121: chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", "");
4122: final String old = part.getFileName();
4123: part.setFileName(old != null ? old.concat(chunk) : chunk);
4124: } catch (final MessagingException ME) {
4125: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4126: }
4127: }
4128:
4129: /**
4130: * Constructs a subject line from a formatter.
4131: *
4132: * @param msg to append to.
4133: * @param chunk non null string to append.
4134: */
4135: private void appendSubject(final Message msg, final String chunk) {
4136: if (chunk != null) {
4137: if (chunk.length() > 0) {
4138: appendSubject0(msg, chunk);
4139: }
4140: } else {
4141: reportNullError(ErrorManager.FORMAT_FAILURE);
4142: }
4143: }
4144:
4145: /**
4146: * It is assumed that subject lines are short and that in most cases
4147: * getTail will be the only method that will produce a result.
4148: *
4149: * @param msg to append to.
4150: * @param chunk non null string to append.
4151: */
4152: private void appendSubject0(final Message msg, String chunk) {
4153: try {
4154: //Remove all control character groups.
4155: chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", "");
4156: final String charset = getEncodingName();
4157: final String old = msg.getSubject();
4158: assert msg instanceof MimeMessage : msg;
4159: ((MimeMessage) msg).setSubject(old != null ? old.concat(chunk)
4160: : chunk, MimeUtility.mimeCharset(charset));
4161: } catch (final MessagingException ME) {
4162: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4163: }
4164: }
4165:
4166: /**
4167: * Gets the locale for the given log record from the resource bundle.
4168: * If the resource bundle is using the root locale then the default locale
4169: * is returned.
4170: *
4171: * @param r the log record.
4172: * @return null if not localized otherwise, the locale of the record.
4173: * @since JavaMail 1.4.5
4174: */
4175: private Locale localeFor(final LogRecord r) {
4176: Locale l;
4177: final ResourceBundle rb = r.getResourceBundle();
4178: if (rb != null) {
4179: l = rb.getLocale();
4180: if (l == null || isEmpty(l.getLanguage())) {
4181: //The language of the fallback bundle (root) is unknown.
4182: //1. Use default locale. Should only be wrong if the app is
4183: // used with a langauge that was unintended. (unlikely)
4184: //2. Mark it as not localized (force null, info loss).
4185: //3. Use the bundle name (encoded) as an experimental language.
4186: l = Locale.getDefault();
4187: }
4188: } else {
4189: l = null;
4190: }
4191: return l;
4192: }
4193:
4194: /**
4195: * Appends the content language to the given mime part.
4196: * The language tag is only appended if the given language has not been
4197: * specified. This method is only used when we have LogRecords that are
4198: * localized with an assigned resource bundle.
4199: *
4200: * @param p the mime part.
4201: * @param l the locale to append.
4202: * @throws NullPointerException if any argument is null.
4203: * @since JavaMail 1.4.5
4204: */
4205: private void appendContentLang(final MimePart p, final Locale l) {
4206: try {
4207: String lang = LogManagerProperties.toLanguageTag(l);
4208: if (lang.length() != 0) {
4209: String header = p.getHeader("Content-Language", null);
4210: if (isEmpty(header)) {
4211: p.setHeader("Content-Language", lang);
4212: } else if (!header.equalsIgnoreCase(lang)) {
4213: lang = ",".concat(lang);
4214: int idx = 0;
4215: while ((idx = header.indexOf(lang, idx)) > -1) {
4216: idx += lang.length();
4217: if (idx == header.length()
4218: || header.charAt(idx) == ',') {
4219: break;
4220: }
4221: }
4222:
4223: if (idx < 0) {
4224: int len = header.lastIndexOf("\r\n\t");
4225: if (len < 0) { //If not folded.
4226: len = (18 + 2) + header.length();
4227: } else {
4228: len = (header.length() - len) + 8;
4229: }
4230:
4231: //Perform folding of header if needed.
4232: if ((len + lang.length()) > 76) {
4233: header = header.concat("\r\n\t".concat(lang));
4234: } else {
4235: header = header.concat(lang);
4236: }
4237: p.setHeader("Content-Language", header);
4238: }
4239: }
4240: }
4241: } catch (final MessagingException ME) {
4242: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4243: }
4244: }
4245:
4246: /**
4247: * Sets the accept language to the default locale of the JVM.
4248: * If the locale is the root locale the header is not added.
4249: *
4250: * @param p the part to set.
4251: * @since JavaMail 1.4.5
4252: */
4253: private void setAcceptLang(final Part p) {
4254: try {
4255: final String lang = LogManagerProperties
4256: .toLanguageTag(Locale.getDefault());
4257: if (lang.length() != 0) {
4258: p.setHeader("Accept-Language", lang);
4259: }
4260: } catch (final MessagingException ME) {
4261: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4262: }
4263: }
4264:
4265: /**
4266: * Used when a log record was loggable prior to being inserted
4267: * into the buffer but at the time of formatting was no longer loggable.
4268: * Filters were changed after publish but prior to a push or a bug in the
4269: * body filter or one of the attachment filters.
4270: *
4271: * @param record that was not formatted.
4272: * @since JavaMail 1.4.5
4273: */
4274: private void reportFilterError(final LogRecord record) {
4275: assert Thread.holdsLock(this);
4276: final Formatter f = createSimpleFormatter();
4277: final String msg = "Log record " + record.getSequenceNumber()
4278: + " was filtered from all message parts. "
4279: + head(f) + format(f, record) + tail(f, "");
4280: final String txt = getFilter() + ", "
4281: + Arrays.asList(readOnlyAttachmentFilters());
4282: reportError(msg, new IllegalArgumentException(txt),
4283: ErrorManager.FORMAT_FAILURE);
4284: }
4285:
4286: /**
4287: * Reports symmetric contract violations an equals implementation.
4288: *
4289: * @param o the test object must be non null.
4290: * @param found the possible intern, must be non null.
4291: * @throws NullPointerException if any argument is null.
4292: * @since JavaMail 1.5.0
4293: */
4294: private void reportNonSymmetric(final Object o, final Object found) {
4295: reportError("Non symmetric equals implementation."
4296: , new IllegalArgumentException(o.getClass().getName()
4297: + " is not equal to " + found.getClass().getName())
4298: , ErrorManager.OPEN_FAILURE);
4299: }
4300:
4301: /**
4302: * Reports equals implementations that do not discriminate between objects
4303: * of different types or subclass types.
4304: *
4305: * @param o the test object must be non null.
4306: * @param found the possible intern, must be non null.
4307: * @throws NullPointerException if any argument is null.
4308: * @since JavaMail 1.5.0
4309: */
4310: private void reportNonDiscriminating(final Object o, final Object found) {
4311: reportError("Non discriminating equals implementation."
4312: , new IllegalArgumentException(o.getClass().getName()
4313: + " should not be equal to " + found.getClass().getName())
4314: , ErrorManager.OPEN_FAILURE);
4315: }
4316:
4317: /**
4318: * Used to outline the bytes to report a null pointer exception.
4319: * See BUD ID 6533165.
4320: *
4321: * @param code the ErrorManager code.
4322: */
4323: private void reportNullError(final int code) {
4324: reportError("null", new NullPointerException(), code);
4325: }
4326:
4327: /**
4328: * Creates the head or reports a formatting error.
4329: *
4330: * @param f the formatter.
4331: * @return the head string or an empty string.
4332: */
4333: private String head(final Formatter f) {
4334: try {
4335: return f.getHead(this);
4336: } catch (final RuntimeException RE) {
4337: reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
4338: return "";
4339: }
4340: }
4341:
4342: /**
4343: * Creates the formatted log record or reports a formatting error.
4344: *
4345: * @param f the formatter.
4346: * @param r the log record.
4347: * @return the formatted string or an empty string.
4348: */
4349: private String format(final Formatter f, final LogRecord r) {
4350: try {
4351: return f.format(r);
4352: } catch (final RuntimeException RE) {
4353: reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
4354: return "";
4355: }
4356: }
4357:
4358: /**
4359: * Creates the tail or reports a formatting error.
4360: *
4361: * @param f the formatter.
4362: * @param def the default string to use when there is an error.
4363: * @return the tail string or the given default string.
4364: */
4365: private String tail(final Formatter f, final String def) {
4366: try {
4367: return f.getTail(this);
4368: } catch (final RuntimeException RE) {
4369: reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
4370: return def;
4371: }
4372: }
4373:
4374: /**
4375: * Sets the x-mailer header.
4376: *
4377: * @param msg the target message.
4378: */
4379: private void setMailer(final Message msg) {
4380: try {
4381: final Class<?> mail = MailHandler.class;
4382: final Class<?> k = getClass();
4383: String value;
4384: if (k == mail) {
4385: value = mail.getName();
4386: } else {
4387: try {
4388: value = MimeUtility.encodeText(k.getName());
4389: } catch (final UnsupportedEncodingException E) {
4390: reportError(E.getMessage(), E, ErrorManager.FORMAT_FAILURE);
4391: value = k.getName().replaceAll("[^\\x00-\\x7F]", "\uu001A");
4392: }
4393: value = MimeUtility.fold(10, mail.getName() + " using the "
4394: + value + " extension.");
4395: }
4396: msg.setHeader("X-Mailer", value);
4397: } catch (final MessagingException ME) {
4398: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4399: }
4400: }
4401:
4402: /**
4403: * Sets the priority and importance headers.
4404: *
4405: * @param msg the target message.
4406: */
4407: private void setPriority(final Message msg) {
4408: try {
4409: msg.setHeader("Importance", "High");
4410: msg.setHeader("Priority", "urgent");
4411: msg.setHeader("X-Priority", "2"); //High
4412: } catch (final MessagingException ME) {
4413: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4414: }
4415: }
4416:
4417: /**
4418: * Used to signal that body parts are missing from a message. Also used
4419: * when LogRecords were passed to an attachment formatter but the formatter
4420: * produced no output, which is allowed. Used during a verify because all
4421: * parts are omitted, none of the content formatters are used. This is
4422: * not used when a filter prevents LogRecords from being formatted.
4423: * This header is defined in RFC 2156 and RFC 4021.
4424: *
4425: * @param msg the message.
4426: * @since JavaMail 1.4.5
4427: */
4428: private void setIncompleteCopy(final Message msg) {
4429: try {
4430: msg.setHeader("Incomplete-Copy", "");
4431: } catch (final MessagingException ME) {
4432: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4433: }
4434: }
4435:
4436: /**
4437: * Signals that this message was generated by automatic process.
4438: * This header is defined in RFC 3834 section 5.
4439: *
4440: * @param msg the message.
4441: * @since JavaMail 1.4.6
4442: */
4443: private void setAutoSubmitted(final Message msg) {
4444: if (allowRestrictedHeaders()) {
4445: try { //RFC 3834 (5.2)
4446: msg.setHeader("auto-submitted", "auto-generated");
4447: } catch (final MessagingException ME) {
4448: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4449: }
4450: }
4451: }
4452:
4453: /**
4454: * Sets from address header.
4455: *
4456: * @param msg the target message.
4457: */
4458: private void setFrom(final Message msg) {
4459: final String from = getSession(msg).getProperty("mail.from");
4460: if (from != null) {
4461: try {
4462: final Address[] address = InternetAddress.parse(from, false);
4463: if (address.length > 0) {
4464: if (address.length == 1) {
4465: msg.setFrom(address[0]);
4466: } else { //Greater than 1 address.
4467: msg.addFrom(address);
4468: }
4469: }
4470: //Can't place an else statement here because the 'from' is
4471: //not null which causes the local address computation
4472: //to fail. Assume the user wants to omit the from address
4473: //header.
4474: } catch (final MessagingException ME) {
4475: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4476: setDefaultFrom(msg);
4477: }
4478: } else {
4479: setDefaultFrom(msg);
4480: }
4481: }
4482:
4483: /**
4484: * Sets the from header to the local address.
4485: *
4486: * @param msg the target message.
4487: */
4488: private void setDefaultFrom(final Message msg) {
4489: try {
4490: msg.setFrom();
4491: } catch (final MessagingException ME) {
4492: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4493: }
4494: }
4495:
4496: /**
4497: * Computes the default to-address if none was specified. This can
4498: * fail if the local address can't be computed.
4499: *
4500: * @param msg the message
4501: * @param type the recipient type.
4502: * @since JavaMail 1.5.0
4503: */
4504: private void setDefaultRecipient(final Message msg,
4505: final Message.RecipientType type) {
4506: try {
4507: Address a = InternetAddress.getLocalAddress(getSession(msg));
4508: if (a != null) {
4509: msg.setRecipient(type, a);
4510: } else {
4511: final MimeMessage m = new MimeMessage(getSession(msg));
4512: m.setFrom(); //Should throw an exception with a cause.
4513: Address[] from = m.getFrom();
4514: if (from.length > 0) {
4515: msg.setRecipients(type, from);
4516: } else {
4517: throw new MessagingException("No local address.");
4518: }
4519: }
4520: } catch (MessagingException | RuntimeException ME) {
4521: reportError("Unable to compute a default recipient.",
4522: ME, ErrorManager.FORMAT_FAILURE);
4523: }
4524: }
4525:
4526: /**
4527: * Sets reply-to address header.
4528: *
4529: * @param msg the target message.
4530: */
4531: private void setReplyTo(final Message msg) {
4532: final String reply = getSession(msg).getProperty("mail.reply.to");
4533: if (!isEmpty(reply)) {
4534: try {
4535: final Address[] address = InternetAddress.parse(reply, false);
4536: if (address.length > 0) {
4537: msg.setReplyTo(address);
4538: }
4539: } catch (final MessagingException ME) {
4540: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4541: }
4542: }
4543: }
4544:
4545: /**
4546: * Sets sender address header.
4547: *
4548: * @param msg the target message.
4549: */
4550: private void setSender(final Message msg) {
4551: assert msg instanceof MimeMessage : msg;
4552: final String sender = getSession(msg).getProperty("mail.sender");
4553: if (!isEmpty(sender)) {
4554: try {
4555: final InternetAddress[] address =
4556: InternetAddress.parse(sender, false);
4557: if (address.length > 0) {
4558: ((MimeMessage) msg).setSender(address[0]);
4559: if (address.length > 1) {
4560: reportError("Ignoring other senders.",
4561: tooManyAddresses(address, 1),
4562: ErrorManager.FORMAT_FAILURE);
4563: }
4564: }
4565: } catch (final MessagingException ME) {
4566: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4567: }
4568: }
4569: }
4570:
4571: /**
4572: * A common factory used to create the too many addresses exception.
4573: *
4574: * @param address the addresses, never null.
4575: * @param offset the starting address to display.
4576: * @return the too many addresses exception.
4577: */
4578: private AddressException tooManyAddresses(Address[] address, int offset) {
4579: Object l = Arrays.asList(address).subList(offset, address.length);
4580: return new AddressException(l.toString());
4581: }
4582:
4583: /**
4584: * Sets the recipient for the given message.
4585: *
4586: * @param msg the message.
4587: * @param key the key to search in the session.
4588: * @param type the recipient type.
4589: * @return true if the key was contained in the session.
4590: */
4591: private boolean setRecipient(final Message msg,
4592: final String key, final Message.RecipientType type) {
4593: boolean containsKey;
4594: final String value = getSession(msg).getProperty(key);
4595: containsKey = value != null;
4596: if (!isEmpty(value)) {
4597: try {
4598: final Address[] address = InternetAddress.parse(value, false);
4599: if (address.length > 0) {
4600: msg.setRecipients(type, address);
4601: }
4602: } catch (final MessagingException ME) {
4603: reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
4604: }
4605: }
4606: return containsKey;
4607: }
4608:
4609: /**
4610: * Converts an email message to a raw string. This raw string
4611: * is passed to the error manager to allow custom error managers
4612: * to recreate the original MimeMessage object.
4613: *
4614: * @param msg a Message object.
4615: * @return the raw string or null if msg was null.
4616: * @throws MessagingException if there was a problem with the message.
4617: * @throws IOException if there was a problem.
4618: */
4619: private String toRawString(final Message msg) throws MessagingException, IOException {
4620: if (msg != null) {
4621: Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
4622: try { //JDK-8025251
4623: int nbytes = Math.max(msg.getSize() + MIN_HEADER_SIZE, MIN_HEADER_SIZE);
4624: ByteArrayOutputStream out = new ByteArrayOutputStream(nbytes);
4625: msg.writeTo(out); //Headers can be UTF-8 or US-ASCII.
4626: return out.toString("UTF-8");
4627: } finally {
4628: getAndSetContextClassLoader(ccl);
4629: }
4630: } else { //Must match this.reportError behavior, see push method.
4631: return null; //Null is the safe choice.
4632: }
4633: }
4634:
4635: /**
4636: * Converts a throwable to a message string.
4637: *
4638: * @param t any throwable or null.
4639: * @return the throwable with a stack trace or the literal null.
4640: */
4641: private String toMsgString(final Throwable t) {
4642: if (t == null) {
4643: return "null";
4644: }
4645:
4646: final String charset = getEncodingName();
4647: try {
4648: final ByteArrayOutputStream out =
4649: new ByteArrayOutputStream(MIN_HEADER_SIZE);
4650:
4651: //Create an output stream writer so streams are not double buffered.
4652: try (OutputStreamWriter ows = new OutputStreamWriter(out, charset);
4653: PrintWriter pw = new PrintWriter(ows)) {
4654: pw.println(t.getMessage());
4655: t.printStackTrace(pw);
4656: pw.flush();
4657: } //Close OSW before generating string. JDK-6995537
4658: return out.toString(charset);
4659: } catch (final Exception badMimeCharset) {
4660: return t.toString() + ' ' + badMimeCharset.toString();
4661: }
4662: }
4663:
4664: /**
4665: * Replaces the current context class loader with our class loader.
4666: *
4667: * @param ccl null for boot class loader, a class loader, a class used to
4668: * get the class loader, or a source object to get the class loader.
4669: * @return null for the boot class loader, a class loader, or a marker
4670: * object to signal that no modification was required.
4671: * @since JavaMail 1.5.3
4672: */
4673: private Object getAndSetContextClassLoader(final Object ccl) {
4674: if (ccl != GetAndSetContext.NOT_MODIFIED) {
4675: try {
4676: final PrivilegedAction<?> pa;
4677: if (ccl instanceof PrivilegedAction) {
4678: pa = (PrivilegedAction<?>) ccl;
4679: } else {
4680: pa = new GetAndSetContext(ccl);
4681: }
4682: return LogManagerProperties.runOrDoPrivileged(pa);
4683: } catch (final SecurityException ignore) {
4684: }
4685: }
4686: return GetAndSetContext.NOT_MODIFIED;
4687: }
4688:
4689: /**
4690: * A factory used to create a common attachment mismatch type.
4691: *
4692: * @param msg the exception message.
4693: * @return a RuntimeException to represent the type of error.
4694: */
4695: private static RuntimeException attachmentMismatch(final String msg) {
4696: return new IndexOutOfBoundsException(msg);
4697: }
4698:
4699: /**
4700: * Try to attach a suppressed exception to a MessagingException in any order
4701: * that is possible.
4702: *
4703: * @param required the exception expected to see as a reported failure.
4704: * @param optional the suppressed exception.
4705: * @return either the required or the optional exception.
4706: */
4707: private static MessagingException attach(
4708: MessagingException required, Exception optional) {
4709: if (optional != null && !required.setNextException(optional)) {
4710: if (optional instanceof MessagingException) {
4711: final MessagingException head = (MessagingException) optional;
4712: if (head.setNextException(required)) {
4713: return head;
4714: }
4715: }
4716:
4717: if (optional != required) {
4718: required.addSuppressed(optional);
4719: }
4720: }
4721: return required;
4722: }
4723:
4724: /**
4725: * Gets the local host from the given service object.
4726: *
4727: * @param s the service to check.
4728: * @return the local host or null.
4729: * @since JavaMail 1.5.3
4730: */
4731: private String getLocalHost(final Service s) {
4732: try {
4733: return LogManagerProperties.getLocalHost(s);
4734: } catch (SecurityException | NoSuchMethodException
4735: | LinkageError ignore) {
4736: } catch (final Exception ex) {
4737: reportError(s.toString(), ex, ErrorManager.OPEN_FAILURE);
4738: }
4739: return null;
4740: }
4741:
4742: /**
4743: * Google App Engine doesn't support Message.getSession.
4744: *
4745: * @param msg the message.
4746: * @return the session from the given message.
4747: * @throws NullPointerException if the given message is null.
4748: * @since JavaMail 1.5.3
4749: */
4750: private Session getSession(final Message msg) {
4751: Objects.requireNonNull(msg);
4752: return new MessageContext(msg).getSession();
4753: }
4754:
4755: /**
4756: * Determines if restricted headers are allowed in the current environment.
4757: *
4758: * @return true if restricted headers are allowed.
4759: * @since JavaMail 1.5.3
4760: */
4761: private boolean allowRestrictedHeaders() {
4762: //GAE will prevent delivery of email with forbidden headers.
4763: //Assume the environment is GAE if access to the LogManager is
4764: //forbidden.
4765: return LogManagerProperties.hasLogManager();
4766: }
4767:
4768: /**
4769: * Used for storing a password from the LogManager or literal string.
4770: *
4771: * @since JavaMail 1.4.6
4772: */
4773: private static final class DefaultAuthenticator extends Authenticator {
4774:
4775: /**
4776: * Creates an Authenticator for the given password. This method is used
4777: * so class verification of assignments in MailHandler doesn't require
4778: * loading this class which otherwise can occur when using the
4779: * constructor. Default access to avoid generating extra class files.
4780: *
4781: * @param pass the password.
4782: * @return an Authenticator for the password.
4783: * @since JavaMail 1.5.6
4784: */
4785: static Authenticator of(final String pass) {
4786: return new DefaultAuthenticator(pass);
4787: }
4788:
4789: /**
4790: * The password to use.
4791: */
4792: private final String pass;
4793:
4794: /**
4795: * Use the factory method instead of this constructor.
4796: *
4797: * @param pass the password.
4798: */
4799: private DefaultAuthenticator(final String pass) {
4800: assert pass != null;
4801: this.pass = pass;
4802: }
4803:
4804: @Override
4805: protected final PasswordAuthentication getPasswordAuthentication() {
4806: return new PasswordAuthentication(getDefaultUserName(), pass);
4807: }
4808: }
4809:
4810: /**
4811: * Performs a get and set of the context class loader with privileges
4812: * enabled.
4813: *
4814: * @since JavaMail 1.4.6
4815: */
4816: private static final class GetAndSetContext implements PrivilegedAction<Object> {
4817: /**
4818: * A marker object used to signal that the class loader was not
4819: * modified.
4820: */
4821: public static final Object NOT_MODIFIED = GetAndSetContext.class;
4822: /**
4823: * The source containing the class loader.
4824: */
4825: private final Object source;
4826:
4827: /**
4828: * Create the action.
4829: *
4830: * @param source null for boot class loader, a class loader, a class
4831: * used to get the class loader, or a source object to get the class
4832: * loader. Default access to avoid generating extra class files.
4833: */
4834: GetAndSetContext(final Object source) {
4835: this.source = source;
4836: }
4837:
4838: /**
4839: * Gets the class loader from the source and sets the CCL only if
4840: * the source and CCL are not the same.
4841: *
4842: * @return the replaced context class loader which can be null or
4843: * NOT_MODIFIED to indicate that nothing was modified.
4844: */
4845: @SuppressWarnings("override") //JDK-6954234
4846: public final Object run() {
4847: final Thread current = Thread.currentThread();
4848: final ClassLoader ccl = current.getContextClassLoader();
4849: final ClassLoader loader;
4850: if (source == null) {
4851: loader = null; //boot class loader
4852: } else if (source instanceof ClassLoader) {
4853: loader = (ClassLoader) source;
4854: } else if (source instanceof Class) {
4855: loader = ((Class<?>) source).getClassLoader();
4856: } else if (source instanceof Thread) {
4857: loader = ((Thread) source).getContextClassLoader();
4858: } else {
4859: assert !(source instanceof Class) : source;
4860: loader = source.getClass().getClassLoader();
4861: }
4862:
4863: if (ccl != loader) {
4864: current.setContextClassLoader(loader);
4865: return ccl;
4866: } else {
4867: return NOT_MODIFIED;
4868: }
4869: }
4870: }
4871:
4872: /**
4873: * Used for naming attachment file names and the main subject line.
4874: */
4875: private static final class TailNameFormatter extends Formatter {
4876:
4877: /**
4878: * Creates or gets a formatter from the given name. This method is used
4879: * so class verification of assignments in MailHandler doesn't require
4880: * loading this class which otherwise can occur when using the
4881: * constructor. Default access to avoid generating extra class files.
4882: *
4883: * @param name any not null string.
4884: * @return a formatter for that string.
4885: * @since JavaMail 1.5.6
4886: */
4887: static Formatter of(final String name) {
4888: return new TailNameFormatter(name);
4889: }
4890:
4891: /**
4892: * The value used as the output.
4893: */
4894: private final String name;
4895:
4896: /**
4897: * Use the factory method instead of this constructor.
4898: *
4899: * @param name any not null string.
4900: */
4901: private TailNameFormatter(final String name) {
4902:• assert name != null;
4903: this.name = name;
4904: }
4905:
4906: @Override
4907: public final String format(LogRecord record) {
4908: return "";
4909: }
4910:
4911: @Override
4912: public final String getTail(Handler h) {
4913: return name;
4914: }
4915:
4916: /**
4917: * Equals method.
4918: *
4919: * @param o the other object.
4920: * @return true if equal
4921: * @since JavaMail 1.4.4
4922: */
4923: @Override
4924: public final boolean equals(Object o) {
4925:• if (o instanceof TailNameFormatter) {
4926: return name.equals(((TailNameFormatter) o).name);
4927: }
4928: return false;
4929: }
4930:
4931: /**
4932: * Hash code method.
4933: *
4934: * @return the hash code.
4935: * @since JavaMail 1.4.4
4936: */
4937: @Override
4938: public final int hashCode() {
4939: return getClass().hashCode() + name.hashCode();
4940: }
4941:
4942: @Override
4943: public final String toString() {
4944: return name;
4945: }
4946: }
4947: }