WrenSecurity/wrenidm

Email body encoding is hard coded to ISO-8859-1

karelmaxa opened this issue · 5 comments

Current implementation of EmailClient disallow specification of content type charset. Therefore, encoding is always set to ISO-8859-1 and some characters are not encoded properly (charset is resolved here).

Email client should allow content type with charset as it is valid header (See RFC 2616).

I had a look into what is happening. The underlying issue is that javax.mail is checking target encoding by capturing output generated via javax.activation's DataHandler and under the hood it is javax.mail's own text_plain handler that is being picked and used. However text_plain uses ISO charset as default charset so the encoding test is being done on broken stream.

Thread [main] (Suspended)	
	owns: OutputStreamWriter  (id=40)	
	owns: MimeMessage  (id=19)	
	AsciiOutputStream.check(int) line: 1681	
	AsciiOutputStream.write(int) line: 1647	
	text_plain$NoCloseOutputStream(FilterOutputStream).write(int) line: 77	
	text_plain$NoCloseOutputStream(FilterOutputStream).write(byte[], int, int) line: 125	
	StreamEncoder.writeBytes() line: 221 [local variables unavailable]	
	StreamEncoder.implClose() line: 316 [local variables unavailable]	
	StreamEncoder.close() line: 149 [local variables unavailable]	
	OutputStreamWriter.close() line: 233	
	text_plain.writeTo(Object, String, OutputStream) line: 138	
	ObjectDataContentHandler.writeTo(Object, String, OutputStream) line: 889	
	DataHandler.writeTo(OutputStream) line: 317	
	MimeUtility.getEncoding(DataHandler) line: 316	
	MimeBodyPart.updateHeaders(MimePart) line: 1551	
	MimeMessage.updateHeaders() line: 2247	
	MimeMessage.saveChanges() line: 2207	
	MailTest.main(String[]) line: 24	

Previously as javax.activation was part of JRE it was not able to resolve handlers from javax.mail as they were on bundle classpath. So the current issue is that either javax.activation has access to javax.mail handlers via bundle wiring or we have thread context class-loader set.

And finally - I am not sure that it is safe to simply call MimeMessage#setContent like we do in EmailClient. Depending on javax.mail to correctly guess charset seems unstable. So it might be better to rewrite message construction logic as well (EmailClient)... or at least make sure we allow charset in content type.

Btw. we are using Activation from ServiceMix and Mail from Sun maven groups. I think we should switch to Jakarta artifacts (hope they have correct OSGi headers). Also those artifacts should be defined in Wren:IDM and not transitively resolved from some "random" (wrensec-commons/json-resource) library.

Related to wrensec-commons#28 .

Bundle manifest information for Activation and Mail variants:

javax.activation:activation:1.1.1
/mnt/cache/mvn/javax/activation/activation/1.1.1$ bnd print -Cci activation-1.1.1.jar
[IMPEXP]
[CAPABILITIES]
[COMPONENTS]
org.apache.servicemix.specs:org.apache.servicemix.specs.activation-api-1.1:2.9.0
/mnt/cache/mvn/org/apache/servicemix/specs/org.apache.servicemix.specs.activation-api-1.1/2.9.0$ bnd print -Cci org.apache.servicemix.specs.activation-api-1.1-2.9.0.jar
[IMPEXP]
Import-Package
  org.osgi.framework                     {version=[1.5,2)}
Export-Package
  javax.activation                       {version=1.1}
[CAPABILITIES]
[COMPONENTS]
com.sun.activation:jakarta.activation:1.2.2
/mnt/cache/mvn/com/sun/activation/jakarta.activation/1.2.2$ bnd print -Cci jakarta.activation-1.2.2.jar
[IMPEXP]
Import-Package
  com.sun.activation.registries          {version=[1.2,2)}
  javax.activation                       {version=[1.2,2)}
Export-Package
  com.sun.activation.registries          {version=1.2.2, imported-as=[1.2,2)}
  com.sun.activation.viewers             {version=1.2.2}
  javax.activation                       {version=1.2, imported-as=[1.2,2)}
[CAPABILITIES]
Require-Capability
  osgi.ee                                {filter:=(&(osgi.ee=JavaSE)(version=9.0))}
[COMPONENTS]
com.sun.activation:jakarta.activation::1.2.1
/mnt/cache/mvn/com/sun/activation/jakarta.activation/1.2.1$ bnd print -Cci jakarta.activation-1.2.1.jar
[IMPEXP]
Import-Package
  com.sun.activation.registries          {version=1.2}
  com.sun.activation.viewers             {version=1.2}
  javax.activation                       {version=1.2}
Export-Package
  com.sun.activation.registries          {version=1.2.1, imported-as=1.2}
  com.sun.activation.viewers             {version=1.2.1, imported-as=1.2}
  javax.activation                       {version=1.2, imported-as=1.2}
[CAPABILITIES]
[COMPONENTS]
com.sun.mail:javax.mail:1.6.2
/mnt/cache/mvn/com/sun/mail/javax.mail/1.6.2$ bnd print -Cci javax.mail-1.6.2.jar
[IMPEXP]
Import-Package
  com.sun.mail.iap                       {version=[1.6,2)}
  com.sun.mail.imap                      {version=[1.6,2)}
  com.sun.mail.util                      {version=[1.6,2)}
  javax.activation                       
  javax.crypto                           
  javax.crypto.spec                      
  javax.mail                             {version=[1.6,2)}
  javax.mail.event                       {version=[1.6,2)}
  javax.mail.internet                    {version=[1.6,2)}
  javax.mail.search                      {version=[1.6,2)}
  javax.mail.util                        {version=[1.6,2)}
  javax.net                              
  javax.net.ssl                          
  javax.security.auth.callback           
  javax.security.auth.x500               
  javax.security.sasl                    {resolution:=optional}
  javax.xml.transform                    
  javax.xml.transform.stream             
  sun.security.util                      {resolution:=optional}
Export-Package
  com.sun.mail.handlers                  {version=1.6.2}
  com.sun.mail.iap                       {version=1.6.2, imported-as=[1.6,2)}
  com.sun.mail.imap                      {version=1.6.2, imported-as=[1.6,2)}
  com.sun.mail.imap.protocol             {version=1.6.2}
  com.sun.mail.pop3                      {version=1.6.2}
  com.sun.mail.smtp                      {version=1.6.2}
  com.sun.mail.util                      {version=1.6.2, imported-as=[1.6,2)}
  com.sun.mail.util.logging              {version=1.6.2}
  javax.mail                             {version=1.6, imported-as=[1.6,2)}
  javax.mail.event                       {version=1.6, imported-as=[1.6,2)}
  javax.mail.internet                    {version=1.6, imported-as=[1.6,2)}
  javax.mail.search                      {version=1.6, imported-as=[1.6,2)}
  javax.mail.util                        {version=1.6, imported-as=[1.6,2)}
[CAPABILITIES]
Require-Capability
  osgi.ee                                {filter:=(&(osgi.ee=JavaSE)(version=1.7))}
[COMPONENTS]
com.sun.mail:jakarta.mail:1.6.5
/mnt/cache/mvn/com/sun/mail/jakarta.mail/1.6.5$ bnd print -Cci jakarta.mail-1.6.5.jar
[IMPEXP]
Import-Package
  com.sun.mail.iap                       {version=[1.6,2)}
  com.sun.mail.imap                      {version=[1.6,2)}
  com.sun.mail.util                      {version=[1.6,2)}
  javax.activation                       
  javax.crypto                           
  javax.crypto.spec                      
  javax.mail                             {version=[1.6,2)}
  javax.mail.event                       {version=[1.6,2)}
  javax.mail.internet                    {version=[1.6,2)}
  javax.mail.search                      {version=[1.6,2)}
  javax.mail.util                        {version=[1.6,2)}
  javax.net                              
  javax.net.ssl                          
  javax.security.auth.callback           
  javax.security.auth.x500               
  javax.security.sasl                    {resolution:=optional}
  javax.xml.transform                    
  javax.xml.transform.stream             
  sun.security.util                      {resolution:=optional}
Export-Package
  com.sun.mail.handlers                  {version=1.6.5}
  com.sun.mail.iap                       {version=1.6.5, imported-as=[1.6,2)}
  com.sun.mail.imap                      {version=1.6.5, imported-as=[1.6,2)}
  com.sun.mail.imap.protocol             {version=1.6.5}
  com.sun.mail.pop3                      {version=1.6.5}
  com.sun.mail.smtp                      {version=1.6.5}
  com.sun.mail.util                      {version=1.6.5, imported-as=[1.6,2)}
  com.sun.mail.util.logging              {version=1.6.5}
  javax.mail                             {version=1.6, imported-as=[1.6,2)}
  javax.mail.event                       {version=1.6, imported-as=[1.6,2)}
  javax.mail.internet                    {version=1.6, imported-as=[1.6,2)}
  javax.mail.search                      {version=1.6, imported-as=[1.6,2)}
  javax.mail.util                        {version=1.6, imported-as=[1.6,2)}
[CAPABILITIES]
Require-Capability
  osgi.ee                                {filter:=(&(osgi.ee=JavaSE)(version=1.7))}
[COMPONENTS]

Few observations regarding Activation:

  • ServiceMix Activation variant was historically required as the original activation artifact doesn't have bundle metadata
  • Jakarta's legacy variant has bundle metadata so we don't need ServiceMix and can use that one
  • We can't use Activation 1.2.2 as it requires JRE 9+ (we are still maintaining JRE 8 compatibility)

Few observations regarding Mail:

  • We can switch to Jakarta's legacy variant

Our next major version should consider switiching to full Jakarta artifacts. However that would require major refactor in commons (changing package names).

Ehm... the issue is probably custom activator in ServiceMix bundle that eagerly registers command map from all bundles - https://github.com/apache/servicemix-specs/blob/master/activation-api-1.2.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java#L56 . We should get rid of this dependency.

Daemon Thread [FelixStartLevel] (Suspended (breakpoint at line 252 in MailcapCommandMap))	
	owns: OsgiMailcapCommandMap  (id=550)	
	owns: Activator  (id=551)	
	OsgiMailcapCommandMap(MailcapCommandMap).addMailcap(String) line: 252	
	OsgiMailcapCommandMap.addMailcap(String, Bundle) line: 40	
	Activator.rebuildCommandMap() line: 99	
	Activator.register(Bundle) line: 79	
	Activator(Activator).start(BundleContext) line: 70	
	Activator.start(BundleContext) line: 46	
	SecureAction.startActivator(BundleActivator, BundleContext) line: 698	
	Felix.activateBundle(BundleImpl, boolean) line: 2402	
	Felix.startBundle(BundleImpl, int) line: 2308	
	Felix.setActiveStartLevel(int, FrameworkListener[]) line: 1539	
	FrameworkStartLevelImpl.run() line: 308	
	Thread.run() line: 834	

Fixed by @krystofNovotny and released as 6.0.2.