Decode disposition notification

This commit is contained in:
M66B 2021-12-24 16:24:03 +01:00
parent 79985f494b
commit 9d84dca6d7
1 changed files with 95 additions and 43 deletions

View File

@ -1094,6 +1094,15 @@ public class MessageHelper {
this.imessage = message;
}
boolean isReport() {
try {
return imessage.isMimeType("multipart/report");
} catch (Throwable ex) {
Log.w(ex);
return false;
}
}
boolean getSeen() throws MessagingException {
return imessage.isSet(Flags.Flag.SEEN);
}
@ -1336,25 +1345,17 @@ public class MessageHelper {
}
boolean subject_threading = prefs.getBoolean("subject_threading", false);
if (subject_threading) {
boolean dsn = false;
try {
dsn = imessage.isMimeType("multipart/report");
} catch (Throwable ex) {
Log.w(ex);
}
if (!dsn) {
String sender = getSortKey(getFrom());
String subject = getSubject();
long since = new Date().getTime() - MAX_SUBJECT_AGE * 3600 * 1000L;
if (!TextUtils.isEmpty(sender) && !TextUtils.isEmpty(subject)) {
List<EntityMessage> subjects = db.message().getMessagesBySubject(account, sender, subject, since);
for (EntityMessage message : subjects)
if (!thread.equals(message.thread)) {
Log.w("Updating subject thread from " + message.thread + " to " + thread);
db.message().updateMessageThread(message.account, message.thread, thread, since);
}
}
if (subject_threading && !isReport()) {
String sender = getSortKey(getFrom());
String subject = getSubject();
long since = new Date().getTime() - MAX_SUBJECT_AGE * 3600 * 1000L;
if (!TextUtils.isEmpty(sender) && !TextUtils.isEmpty(subject)) {
List<EntityMessage> subjects = db.message().getMessagesBySubject(account, sender, subject, since);
for (EntityMessage message : subjects)
if (!thread.equals(message.thread)) {
Log.w("Updating subject thread from " + message.thread + " to " + thread);
db.message().updateMessageThread(message.account, message.thread, thread, since);
}
}
}
@ -2311,9 +2312,9 @@ public class MessageHelper {
return "text/html".equalsIgnoreCase(contentType.getBaseType());
}
boolean isDSN() {
return ("message/delivery-status".equalsIgnoreCase(contentType.getBaseType()) ||
"message/disposition-notification".equalsIgnoreCase(contentType.getBaseType()));
boolean isReport() {
String ct = contentType.getBaseType();
return Report.isDeliveryStatus(ct) || Report.isDispositionNotification(ct);
}
}
@ -2581,11 +2582,13 @@ public class MessageHelper {
}
}
}
} else if (h.isDSN()) {
DeliveryReport report = new DeliveryReport(result);
} else if (h.isReport()) {
Report report = new Report(h.contentType.getBaseType(), result);
result = report.html;
if (report.isNonDelivery() && report.diag != null)
warnings.add(report.diag);
if (!report.isDelivered() && report.diagnostic != null)
warnings.add(report.diagnostic);
if (!report.isDisplayed() && report.disposition != null)
warnings.add(report.disposition);
} else
Log.w("Unexpected content type=" + h.contentType);
@ -3714,13 +3717,18 @@ public class MessageHelper {
}
}
static class DeliveryReport {
static class Report {
String type;
String reporter;
String action;
String recipient;
String status;
String diag;
String diagnostic;
String disposition;
String html;
DeliveryReport(String content) {
Report(String type, String content) {
this.type = type;
StringBuilder report = new StringBuilder();
report.append("<hr><div style=\"font-family: monospace; font-size: small;\">");
content = content.replaceAll("(\\r?\\n)+", "\n");
@ -3740,17 +3748,38 @@ public class MessageHelper {
.append(TextUtils.htmlEncode(value))
.append("<br>");
// https://datatracker.ietf.org/doc/html/rfc3464#section-2.3
switch (name) {
case "Action":
this.action = value;
break;
case "Status":
this.status = value;
break;
case "Diagnostic-Code":
this.diag = value;
break;
if (isDeliveryStatus(type)) {
// https://datatracker.ietf.org/doc/html/rfc3464#section-2.3
switch (name) {
case "Reporting-MTA":
this.reporter = value;
break;
case "Action":
this.action = value;
break;
case "Final-Recipient":
this.recipient = value;
break;
case "Status":
this.status = value;
break;
case "Diagnostic-Code":
this.diagnostic = value;
break;
}
} else if (isDispositionNotification(type)) {
//https://datatracker.ietf.org/doc/html/rfc3798#section-3.2.6
switch (name) {
case "Reporting-UA":
this.reporter = value;
break;
case "Original-Recipient":
this.recipient = value;
break;
case "Disposition":
this.disposition = value;
break;
}
}
}
} catch (Throwable ex) {
@ -3761,12 +3790,35 @@ public class MessageHelper {
this.html = report.toString();
}
boolean isDelivery() {
boolean isDelivered() {
return ("delivered".equals(action) || "relayed".equals(action) || "expanded".equals(action));
}
boolean isNonDelivery() {
return ("failed".equals(action) || "delayed".equals(action));
boolean isDisplayed() {
return isType("displayed");
}
boolean isDeleted() {
return isType("deleted");
}
private boolean isType(String t) {
// manual-action/MDN-sent-manually; displayed
if (disposition == null)
return false;
int semi = disposition.lastIndexOf(';');
if (semi < 0)
return false;
String type = disposition.substring(semi + 1).trim();
return t.equals(type);
}
static boolean isDeliveryStatus(String type) {
return "message/delivery-status".equalsIgnoreCase(type);
}
static boolean isDispositionNotification(String type) {
return "message/disposition-notification".equalsIgnoreCase(type);
}
}
}