Skip to content

Commit f8f55e1

Browse files
committed
[refactor] Cleanup multipart boundary handling
1 parent dbbb36e commit f8f55e1

File tree

2 files changed

+112
-77
lines changed

2 files changed

+112
-77
lines changed

extensions/modules/mail/src/main/java/org/exist/xquery/modules/mail/SendEmailFunction.java

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public class SendEmailFunction extends BasicFunction {
7575

7676
static final String ERROR_MSG_NON_MIME_CLIENT = "Error your mail client is not MIME Compatible";
7777

78+
private static final Random RANDOM = new Random();
79+
7880
public final static FunctionSignature deprecated = new FunctionSignature(
7981
new QName("send-email", MailModule.NAMESPACE_URI, MailModule.PREFIX),
8082
"Sends an email through the SMTP Server.",
@@ -482,38 +484,42 @@ static void writeMessage(final PrintWriter out, final Mail aMail, final boolean
482484

483485

484486
boolean multipartAlternative = false;
485-
String multipartBoundary = null;
487+
int multipartInstanceCount = 0;
488+
final Deque<String> multipartBoundary = new ArrayDeque<>();
486489

487490
if (aMail.attachmentIterator().hasNext()) {
488491
// we have an attachment as well as text and/or html, so we need a multipart/mixed message
489-
multipartBoundary = multipartBoundary(1);
492+
multipartBoundary.addFirst(multipartBoundary(++multipartInstanceCount));
490493
} else if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML())) {
491494
// we have text and html, so we need a multipart/alternative message and no attachment
492495
multipartAlternative = true;
493-
multipartBoundary = multipartBoundary(1) + "_alt";
496+
multipartBoundary.addFirst(multipartBoundary(++multipartInstanceCount));
494497
}
495498
// else {
496499
// // we have either text or html and no attachment this message is not multipart
497500
// }
498501

499502
//content type
500-
if (multipartBoundary != null) {
503+
if (!multipartBoundary.isEmpty()) {
501504
//multipart message
502505

503-
out.print("Content-Type: " + (multipartAlternative ? "multipart/alternative" : "multipart/mixed") + "; boundary=" + parameterValue(multipartBoundary) + eol);
506+
out.print("Content-Type: " + (multipartAlternative ? "multipart/alternative" : "multipart/mixed") + "; boundary=" + parameterValue(multipartBoundary.peekFirst()) + eol);
504507

505508
//Mime warning
506509
out.print(eol);
507510
out.print(ERROR_MSG_NON_MIME_CLIENT + eol);
508511
out.print(eol);
509512

510-
out.print("--" + multipartBoundary + eol);
513+
out.print("--" + multipartBoundary.peekFirst() + eol);
511514
}
512515

513516
if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML()) && aMail.attachmentIterator().hasNext()) {
514-
out.print("Content-Type: multipart/alternative; boundary=" + parameterValue(multipartBoundary(1) + "_alt") + eol);
517+
// we are a multipart inside a multipart
518+
multipartBoundary.addFirst(multipartBoundary(++multipartInstanceCount));
519+
520+
out.print("Content-Type: multipart/alternative; boundary=" + parameterValue(multipartBoundary.peekFirst()) + eol);
515521
out.print(eol);
516-
out.print("--" + multipartBoundary(1) + "_alt" + eol);
522+
out.print("--" + multipartBoundary.peekFirst() + eol);
517523
}
518524

519525
//text email
@@ -525,19 +531,13 @@ static void writeMessage(final PrintWriter out, final Mail aMail, final boolean
525531
out.print(eol);
526532
out.print(aMail.getText() + eol);
527533

528-
if (multipartBoundary != null) {
534+
if (!multipartBoundary.isEmpty()) {
529535
if (nonEmpty(aMail.getXHTML()) || aMail.attachmentIterator().hasNext()) {
530-
if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML()) && aMail.attachmentIterator().hasNext()) {
531-
out.print("--" + multipartBoundary(1) + "_alt" + eol);
532-
} else {
533-
out.print("--" + multipartBoundary + eol);
534-
}
536+
out.print("--" + multipartBoundary.peekFirst() + eol);
535537
} else {
536-
if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML()) && aMail.attachmentIterator().hasNext()) {
537-
out.print("--" + multipartBoundary(1) + "_alt--" + eol);
538-
} else {
539-
out.print("--" + multipartBoundary + "--" + eol);
540-
}
538+
// End multipart message
539+
out.print("--" + multipartBoundary.peekFirst() + "--" + eol);
540+
multipartBoundary.removeFirst();
541541
}
542542
}
543543
}
@@ -552,20 +552,20 @@ static void writeMessage(final PrintWriter out, final Mail aMail, final boolean
552552
out.print("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" + eol);
553553
out.print(aMail.getXHTML() + eol);
554554

555-
if (multipartBoundary != null) {
555+
if (!multipartBoundary.isEmpty()) {
556556
if (aMail.attachmentIterator().hasNext()) {
557557
if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML()) && aMail.attachmentIterator().hasNext()) {
558-
out.print("--" + multipartBoundary(1) + "_alt--" + eol);
559-
out.print("--" + multipartBoundary + eol);
560-
} else {
561-
out.print("--" + multipartBoundary + eol);
558+
// End multipart message
559+
out.print("--" + multipartBoundary.peekFirst() + "--" + eol);
560+
multipartBoundary.removeFirst();
562561
}
562+
563+
out.print("--" + multipartBoundary.peekFirst() + eol);
564+
563565
} else {
564-
if (nonEmpty(aMail.getText()) && nonEmpty(aMail.getXHTML()) && aMail.attachmentIterator().hasNext()) {
565-
out.print("--" + multipartBoundary(1) + "_alt--" + eol);
566-
} else {
567-
out.print("--" + multipartBoundary + "--" + eol);
568-
}
566+
// End multipart message
567+
out.print("--" + multipartBoundary.peekFirst() + "--" + eol);
568+
multipartBoundary.removeFirst();
569569
}
570570
}
571571
}
@@ -591,12 +591,13 @@ static void writeMessage(final PrintWriter out, final Mail aMail, final boolean
591591
}
592592

593593
if (itAttachment.hasNext()) {
594-
out.print("--" + multipartBoundary + eol);
594+
out.print("--" + multipartBoundary.peekFirst() + eol);
595595
}
596596
}
597597

598-
//Emd multipart message
599-
out.print("--" + multipartBoundary + "--" + eol);
598+
// End multipart message
599+
out.print("--" + multipartBoundary.peekFirst() + "--" + eol);
600+
multipartBoundary.removeFirst();
600601
}
601602

602603
//end the message, <cr><lf>.<cr><lf>
@@ -1382,9 +1383,29 @@ private static boolean isNonToken(final String str) {
13821383
*
13831384
* @return the multi-part boundary string.
13841385
*/
1385-
static String multipartBoundary(final int multipartInstance) {
1386-
// Get the version of eXist-db
1387-
final String version = Version.getVersion();
1388-
return "eXist-db.multipart." + version + "_multipart_" + multipartInstance;
1386+
private static String multipartBoundary(final int multipartInstance) {
1387+
return multipartBoundaryPrefix(multipartInstance) + "_" + nextRandomPositiveInteger() + "." + System.currentTimeMillis();
1388+
}
1389+
1390+
/**
1391+
* Produce the prefix of a multi-part boundary string.
1392+
*
1393+
* Access is package-private for unit testing purposes.
1394+
*
1395+
* @param multipartInstance the number of this multipart instance.
1396+
*
1397+
* @return the multi-part boundary string prefix.
1398+
*/
1399+
static String multipartBoundaryPrefix(final int multipartInstance) {
1400+
return "----=_mail.mime.boundary_" + multipartInstance;
1401+
}
1402+
1403+
/**
1404+
* Generates a positive random integer.
1405+
*
1406+
* @return the integer
1407+
*/
1408+
private static int nextRandomPositiveInteger() {
1409+
return RANDOM.nextInt() & Integer.MAX_VALUE;
13891410
}
13901411
}

0 commit comments

Comments
 (0)