File indexing completed on 2024-04-21 05:18:13
0001 /* -*- c++ -*- 0002 kmime_mdn.cpp 0003 0004 KMime, the KDE Internet mail/usenet news message library. 0005 SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 /** 0010 @file 0011 This file is part of the API for handling @ref MIME data and 0012 provides functions for supporting Message Disposition Notifications (MDNs), 0013 also known as email return receipts. 0014 0015 @brief 0016 Provides support for Message Disposition Notifications. 0017 0018 @authors Marc Mutz \<mutz@kde.org\> 0019 */ 0020 0021 #include "kmime_mdn.h" 0022 #include "kmime_version.h" 0023 #include "kmime_util.h" 0024 #include "kmime_codecs_p.h" 0025 #include "kmime_debug.h" 0026 0027 #include <KLocalizedString> 0028 0029 #include <QByteArray> 0030 0031 #ifdef Q_OS_WIN // gethostname 0032 # include <winsock2.h> 0033 #else 0034 # include <unistd.h> 0035 #endif 0036 #include <KLazyLocalizedString> 0037 0038 namespace KMime 0039 { 0040 0041 namespace MDN 0042 { 0043 0044 static const struct { 0045 DispositionType dispositionType; 0046 const char *string; 0047 const KLazyLocalizedString description; 0048 } dispositionTypes[] = {{Displayed, 0049 "displayed", 0050 kli18n("The message sent on ${date} to ${to} with subject " 0051 "\"${subject}\" has been displayed. This is no guarantee that " 0052 "the message has been read or understood.")}, 0053 {Deleted, 0054 "deleted", 0055 kli18n("The message sent on ${date} to ${to} with subject " 0056 "\"${subject}\" has been deleted unseen. This is no guarantee " 0057 "that the message will not be \"undeleted\" and nonetheless " 0058 "read later on.")}, 0059 {Dispatched, 0060 "dispatched", 0061 kli18n("The message sent on ${date} to ${to} with subject " 0062 "\"${subject}\" has been dispatched. This is no guarantee " 0063 "that the message will not be read later on.")}, 0064 {Processed, 0065 "processed", 0066 kli18n("The message sent on ${date} to ${to} with subject " 0067 "\"${subject}\" has been processed by some automatic means.")}, 0068 {Denied, 0069 "denied", 0070 kli18n("The message sent on ${date} to ${to} with subject " 0071 "\"${subject}\" has been acted upon. The sender does not wish " 0072 "to disclose more details to you than that.")}, 0073 {Failed, 0074 "failed", 0075 kli18n("Generation of a Message Disposition Notification for the " 0076 "message sent on ${date} to ${to} with subject \"${subject}\" " 0077 "failed. Reason is given in the Failure: header field below.")}}; 0078 0079 static const int numDispositionTypes = 0080 sizeof dispositionTypes / sizeof *dispositionTypes; 0081 0082 static const char *stringFor(DispositionType d) 0083 { 0084 for (int i = 0 ; i < numDispositionTypes ; ++i) { 0085 if (dispositionTypes[i].dispositionType == d) { 0086 return dispositionTypes[i].string; 0087 } 0088 } 0089 return nullptr; 0090 } 0091 0092 // 0093 // disposition-modifier 0094 // 0095 static const struct { 0096 DispositionModifier dispositionModifier; 0097 const char *string; 0098 } dispositionModifiers[] = { 0099 { Error, "error" }, 0100 { Warning, "warning" }, 0101 { Superseded, "superseded" }, 0102 { Expired, "expired" }, 0103 { MailboxTerminated, "mailbox-terminated" } 0104 }; 0105 0106 static const int numDispositionModifiers = 0107 sizeof dispositionModifiers / sizeof *dispositionModifiers; 0108 0109 static const char *stringFor(DispositionModifier m) 0110 { 0111 for (int i = 0 ; i < numDispositionModifiers ; ++i) { 0112 if (dispositionModifiers[i].dispositionModifier == m) { 0113 return dispositionModifiers[i].string; 0114 } 0115 } 0116 return nullptr; 0117 } 0118 0119 // 0120 // action-mode (part of disposition-mode) 0121 // 0122 0123 static const struct { 0124 ActionMode actionMode; 0125 const char *string; 0126 } actionModes[] = { 0127 { ManualAction, "manual-action" }, 0128 { AutomaticAction, "automatic-action" } 0129 }; 0130 0131 static const int numActionModes = 0132 sizeof actionModes / sizeof *actionModes; 0133 0134 static const char *stringFor(ActionMode a) 0135 { 0136 for (int i = 0 ; i < numActionModes ; ++i) { 0137 if (actionModes[i].actionMode == a) { 0138 return actionModes[i].string; 0139 } 0140 } 0141 return nullptr; 0142 } 0143 0144 // 0145 // sending-mode (part of disposition-mode) 0146 // 0147 0148 static const struct { 0149 SendingMode sendingMode; 0150 const char *string; 0151 } sendingModes[] = { 0152 { SentManually, "MDN-sent-manually" }, 0153 { SentAutomatically, "MDN-sent-automatically" } 0154 }; 0155 0156 static const int numSendingModes = 0157 sizeof sendingModes / sizeof *sendingModes; 0158 0159 static const char *stringFor(SendingMode s) 0160 { 0161 for (int i = 0 ; i < numSendingModes ; ++i) { 0162 if (sendingModes[i].sendingMode == s) { 0163 return sendingModes[i].string; 0164 } 0165 } 0166 return nullptr; 0167 } 0168 0169 static QByteArray dispositionField(DispositionType d, ActionMode a, 0170 SendingMode s, 0171 const QList<DispositionModifier> &m) { 0172 0173 // mandatory parts: Disposition: foo/baz; bar 0174 QByteArray result = "Disposition: "; 0175 result += stringFor(a); 0176 result += '/'; 0177 result += stringFor(s); 0178 result += "; "; 0179 result += stringFor(d); 0180 0181 // optional parts: Disposition: foo/baz; bar/mod1,mod2,mod3 0182 bool first = true; 0183 for (QList<DispositionModifier>::const_iterator mt = m.begin(); 0184 mt != m.end(); ++mt) { 0185 if (first) { 0186 result += '/'; 0187 first = false; 0188 } else { 0189 result += ','; 0190 } 0191 result += stringFor(*mt); 0192 } 0193 return result + '\n'; 0194 } 0195 0196 static QByteArray finalRecipient(const QString &recipient) 0197 { 0198 if (recipient.isEmpty()) { 0199 return {}; 0200 } else { 0201 return "Final-Recipient: rfc822; " 0202 + encodeRFC2047String(recipient, "utf-8") + '\n'; 0203 } 0204 } 0205 0206 static QByteArray orginalRecipient(const QByteArray &recipient) 0207 { 0208 if (recipient.isEmpty()) { 0209 return {}; 0210 } else { 0211 return "Original-Recipient: " + recipient + '\n'; 0212 } 0213 } 0214 0215 static QByteArray originalMessageID(const QByteArray &msgid) 0216 { 0217 if (msgid.isEmpty()) { 0218 return {}; 0219 } else { 0220 return "Original-Message-ID: " + msgid + '\n'; 0221 } 0222 } 0223 0224 static QByteArray reportingUAField() 0225 { 0226 char hostName[256]; 0227 if (gethostname(hostName, 255)) { 0228 hostName[0] = '\0'; // gethostname failed: pretend empty string 0229 } else { 0230 hostName[255] = '\0'; // gethostname may have returned 255 chars (man page) 0231 } 0232 return QByteArray("Reporting-UA: ") + QByteArray(hostName) + 0233 QByteArray("; KMime " KMIME_VERSION_STRING "\n"); 0234 } 0235 0236 QByteArray dispositionNotificationBodyContent( 0237 const QString &r, const QByteArray &o, const QByteArray &omid, 0238 DispositionType d, ActionMode a, SendingMode s, 0239 const QList<DispositionModifier> &m, const QString &special) { 0240 // in Perl: chomp(special) 0241 QString spec; 0242 if (special.endsWith(QLatin1Char('\n'))) { 0243 spec = special.left(special.length() - 1); 0244 } else { 0245 spec = special; 0246 } 0247 0248 // std headers: 0249 QByteArray result = reportingUAField(); 0250 result += orginalRecipient(o); 0251 result += finalRecipient(r); 0252 result += originalMessageID(omid); 0253 result += dispositionField(d, a, s, m); 0254 0255 // headers that are only present for certain disposition {types,modifiers}: 0256 if (d == Failed) { 0257 result += "Failure: " + encodeRFC2047String(spec, "utf-8") + '\n'; 0258 } else if (m.contains(Error)) { 0259 result += "Error: " + encodeRFC2047String(spec, "utf-8") + '\n'; 0260 } else if (m.contains(Warning)) { 0261 result += "Warning: " + encodeRFC2047String(spec, "utf-8") + '\n'; 0262 } 0263 0264 return result; 0265 } 0266 0267 QString descriptionFor(DispositionType d, const QList<DispositionModifier> &) { 0268 for (int i = 0 ; i < numDispositionTypes ; ++i) { 0269 if (dispositionTypes[i].dispositionType == d) { 0270 return dispositionTypes[i].description.toString(); 0271 } 0272 } 0273 qCWarning(KMIME_LOG) << "KMime::MDN::descriptionFor(): No such disposition type:" 0274 << static_cast<int>(d); 0275 return {}; 0276 } 0277 0278 } // namespace MDN 0279 } // namespace KMime