oblac / jodd-mail

Simple Java Mail library.
https://mail.jodd.org
BSD 2-Clause "Simplified" License
29 stars 6 forks source link

[Bug] Receiving mails with `MailServer.Builder.storeAttachmentsIn​(folder)` set results in Exception on Windows #19

Closed ShadowCreator250 closed 2 weeks ago

ShadowCreator250 commented 1 month ago

The MailServer.Builder.storeAttachmentsIn​() method isn't really well documented for users. The way I understand it, is that you can pass in a folder where attachments will be saved. I build a ImapServer with this setting to receive mails. Running it results in the stack trace seen below.

Digging in the code a bit, I found that the file name comes from the messageId. The messageId my ImapServer sends with the email seems to be something like <6d0455f09ad249c897c0aa28a7ee3579@domain>.

It seems to me like the files can't be saved bc their file names contain < & >, which are illegal under Windows 10.

I think the easiest solution would be to hash the messageId for the file name. That way it just produces numbers & letters which every OS should accept. More complex solutions could be:

Stack trace (personal details replaced):

jodd.mail.MailException: Message parsing failed; <--- java.io.FileNotFoundException: tmp\email-attachements\<6d0455f09ad249c897c0aa28a7ee3579@domain>-1 (Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch)
    at jodd.mail.ReceivedEmail.<init>(ReceivedEmail.java:121) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmails._fetch(ReceivedEmails.java:63) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmails.fetch(ReceivedEmails.java:36) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceiverBuilder$ReceiverRunner.fetch(ReceiverBuilder.java:167) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceiverBuilder.get(ReceiverBuilder.java:119) ~[jodd-mail-7.0.1.jar:7.0.1]
    at app.email.service.EmailReceiveService.getUnseenEmails(EmailReceiveService.java:26) ~[classes/:na]
    at app.DemoApplication.lambda$1(DemoApplication.java:33) ~[classes/:na]
    at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:790) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.11.jar:6.1.11]
    at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.11.jar:6.1.11]
    at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.11.jar:6.1.11]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:789) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.3.2.jar:3.3.2]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
    at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:342) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.2.jar:3.3.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.2.jar:3.3.2]
    at app.DemoApplication.main(DemoApplication.java:23) ~[classes/:na]
Caused by: jodd.mail.MailException: tmp\email-attachements\<6d0455f09ad249c897c0aa28a7ee3579@domain>-1 (Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch); <--- java.io.FileNotFoundException: tmp\email-attachements\<6d0455f09ad249c897c0aa28a7ee3579@domain>-1 (Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch)
    at jodd.mail.EmailAttachmentBuilder.buildFileDataSource(EmailAttachmentBuilder.java:239) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.addAttachment(ReceivedEmail.java:448) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.processPart(ReceivedEmail.java:192) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.processMultipart(ReceivedEmail.java:212) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.processPart(ReceivedEmail.java:190) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.parseMessage(ReceivedEmail.java:171) ~[jodd-mail-7.0.1.jar:7.0.1]
    at jodd.mail.ReceivedEmail.<init>(ReceivedEmail.java:119) ~[jodd-mail-7.0.1.jar:7.0.1]
    ... 26 common frames omitted
Caused by: java.io.FileNotFoundException: tmp\email-attachements\<6d0455f09ad249c897c0aa28a7ee3579@domain>-1 (Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch)
    at java.base/java.io.FileOutputStream.open0(Native Method) ~[na:na]
    at java.base/java.io.FileOutputStream.open(FileOutputStream.java:289) ~[na:na]
    at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:230) ~[na:na]
    at jodd.io.FileUtil.writeStream(FileUtil.java:863) ~[jodd-util-6.0.1.jar:6.0.1]
    at jodd.mail.EmailAttachmentBuilder.buildFileDataSource(EmailAttachmentBuilder.java:233) ~[jodd-mail-7.0.1.jar:7.0.1]
    ... 32 common frames omitted
igr commented 1 month ago

Thanx, on it! Give me a couple of days, please

igr commented 2 weeks ago

I have added simple messageId sanitisation. Message id itself is unique, so there should be no issue to perform a simple sanitisation on it