Skip to content

Package: MailHandler

MailHandler

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