File indexing completed on 2025-02-16 04:50:26
0001 /* 0002 0003 SPDX-FileCopyrightText: 2012 Christian Mollekopf <chrigi_1@fastmail.fm> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "imip.h" 0009 #include "pimkolab_debug.h" 0010 0011 #include <KCalUtils/IncidenceFormatter> 0012 #include <KEmailAddress> 0013 #include <KMime/Message> 0014 0015 /* 0016 * The code in here is copy paste work from kdepim/calendarsupport. 0017 * 0018 * We need to refactor the code there and move the relevant parts to kdepimlibs to make it reusable. 0019 * 0020 * 0021 */ 0022 0023 // From MailClient::send 0024 KMime::Message::Ptr createMessage(const QString &from, 0025 const QString &_to, 0026 const QString &cc, 0027 const QString &subject, 0028 const QString &body, 0029 bool hidden, 0030 bool bccMe, 0031 const QString &attachment /*, const QString &mailTransport */) 0032 { 0033 Q_UNUSED(hidden) 0034 0035 const bool outlookConformInvitation = false; 0036 QString userAgent = QStringLiteral("libkolab"); 0037 0038 // We must have a recipients list for most MUAs. Thus, if the 'to' list 0039 // is empty simply use the 'from' address as the recipient. 0040 QString to = _to; 0041 if (to.isEmpty()) { 0042 to = from; 0043 } 0044 qCDebug(PIMKOLAB_LOG) << "\nFrom:" << from << "\nTo:" << to << "\nCC:" << cc << "\nSubject:" << subject << "\nBody: \n" 0045 << body << "\nAttachment:\n" 0046 << attachment 0047 /*<< "\nmailTransport: " << mailTransport*/; 0048 0049 // Now build the message we like to send. The message KMime::Message::Ptr instance 0050 // will be the root message that has 2 additional message. The body itself and 0051 // the attached cal.ics calendar file. 0052 KMime::Message::Ptr message = KMime::Message::Ptr(new KMime::Message); 0053 message->contentTransferEncoding()->clear(); // 7Bit, decoded. 0054 0055 // Set the headers 0056 message->userAgent()->fromUnicodeString(userAgent, "utf-8"); 0057 message->from()->fromUnicodeString(from, "utf-8"); 0058 message->to()->fromUnicodeString(to, "utf-8"); 0059 message->cc()->fromUnicodeString(cc, "utf-8"); 0060 if (bccMe) { 0061 message->bcc()->fromUnicodeString(from, "utf-8"); // from==me, right? 0062 } 0063 message->date()->setDateTime(QDateTime::currentDateTime()); 0064 message->subject()->fromUnicodeString(subject, "utf-8"); 0065 0066 if (outlookConformInvitation) { 0067 message->contentType()->setMimeType("text/calendar"); 0068 message->contentType()->setCharset("utf-8"); 0069 message->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); 0070 message->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); 0071 0072 if (!attachment.isEmpty()) { 0073 auto disposition = new KMime::Headers::ContentDisposition(); 0074 disposition->setDisposition(KMime::Headers::CDinline); 0075 message->setHeader(disposition); 0076 message->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); 0077 message->setBody(KMime::CRLFtoLF(attachment.toUtf8())); 0078 } 0079 } else { 0080 // We need to set following 4 lines by hand else KMime::Content::addContent 0081 // will create a new Content instance for us to attach the main message 0082 // what we don't need cause we already have the main message instance where 0083 // 2 additional messages are attached. 0084 KMime::Headers::ContentType *ct = message->contentType(); 0085 ct->setMimeType("multipart/mixed"); 0086 ct->setBoundary(KMime::multiPartBoundary()); 0087 0088 // Set the first multipart, the body message. 0089 auto bodyMessage = new KMime::Content; 0090 auto bodyDisposition = new KMime::Headers::ContentDisposition(); 0091 bodyDisposition->setDisposition(KMime::Headers::CDinline); 0092 bodyMessage->contentType()->setMimeType("text/plain"); 0093 bodyMessage->contentType()->setCharset("utf-8"); 0094 bodyMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); 0095 bodyMessage->setBody(KMime::CRLFtoLF(body.toUtf8())); 0096 message->appendContent(bodyMessage); 0097 0098 // Set the sedcond multipart, the attachment. 0099 if (!attachment.isEmpty()) { 0100 auto attachMessage = new KMime::Content; 0101 auto attachDisposition = new KMime::Headers::ContentDisposition(); 0102 attachDisposition->setDisposition(KMime::Headers::CDattachment); 0103 attachMessage->contentType()->setMimeType("text/calendar"); 0104 attachMessage->contentType()->setCharset("utf-8"); 0105 attachMessage->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); 0106 attachMessage->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); 0107 attachMessage->setHeader(attachDisposition); 0108 attachMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); 0109 attachMessage->setBody(KMime::CRLFtoLF(attachment.toUtf8())); 0110 message->appendContent(attachMessage); 0111 } 0112 } 0113 0114 // Job done, attach the both multiparts and assemble the message. 0115 message->assemble(); 0116 return message; 0117 } 0118 0119 // From MailClient::mailAttendees 0120 QByteArray mailAttendees(const KCalendarCore::IncidenceBase::Ptr &incidence, 0121 // const KPIMIdentities::Identity &identity, 0122 bool bccMe, 0123 const QString &attachment 0124 /*const QString &mailTransport */) 0125 { 0126 KCalendarCore::Attendee::List attendees = incidence->attendees(); 0127 if (attendees.isEmpty()) { 0128 qCWarning(PIMKOLAB_LOG) << "There are no attendees to e-mail"; 0129 return {}; 0130 } 0131 0132 const QString from = incidence->organizer().fullName(); 0133 const QString organizerEmail = incidence->organizer().email(); 0134 0135 QStringList toList; 0136 QStringList ccList; 0137 const int numberOfAttendees(attendees.count()); 0138 for (int i = 0; i < numberOfAttendees; ++i) { 0139 KCalendarCore::Attendee a = attendees.at(i); 0140 0141 const QString email = a.email(); 0142 if (email.isEmpty()) { 0143 continue; 0144 } 0145 0146 // In case we (as one of our identities) are the organizer we are sending 0147 // this mail. We could also have added ourselves as an attendee, in which 0148 // case we don't want to send ourselves a notification mail. 0149 if (organizerEmail == email) { 0150 continue; 0151 } 0152 0153 // Build a nice address for this attendee including the CN. 0154 QString tname, temail; 0155 const QString username = KEmailAddress::quoteNameIfNecessary(a.name()); 0156 // ignore the return value from extractEmailAddressAndName() because 0157 // it will always be false since tusername does not contain "@domain". 0158 KEmailAddress::extractEmailAddressAndName(username, temail /*byref*/, tname /*byref*/); 0159 tname += QLatin1StringView(" <") + email + QLatin1Char('>'); 0160 0161 // Optional Participants and Non-Participants are copied on the email 0162 if (a.role() == KCalendarCore::Attendee::OptParticipant || a.role() == KCalendarCore::Attendee::NonParticipant) { 0163 ccList << tname; 0164 } else { 0165 toList << tname; 0166 } 0167 } 0168 if (toList.isEmpty() && ccList.isEmpty()) { 0169 // Not really to be called a groupware meeting, eh 0170 qCWarning(PIMKOLAB_LOG) << "There are really no attendees to e-mail"; 0171 return {}; 0172 } 0173 QString to; 0174 if (!toList.isEmpty()) { 0175 to = toList.join(QLatin1StringView(", ")); 0176 } 0177 QString cc; 0178 if (!ccList.isEmpty()) { 0179 cc = ccList.join(QLatin1StringView(", ")); 0180 } 0181 0182 QString subject; 0183 if (incidence->type() != KCalendarCore::Incidence::TypeFreeBusy) { 0184 KCalendarCore::Incidence::Ptr inc = incidence.staticCast<KCalendarCore::Incidence>(); 0185 subject = inc->summary(); 0186 } else { 0187 subject = QStringLiteral("Free Busy Object"); 0188 } 0189 0190 const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); 0191 0192 return createMessage(/* identity, */ from, to, cc, subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); 0193 } 0194 0195 QByteArray mailOrganizer(const KCalendarCore::IncidenceBase::Ptr &incidence, 0196 // const KPIMIdentities::Identity &identity, 0197 const QString &from, 0198 bool bccMe, 0199 const QString &attachment, 0200 const QString &sub /*, const QString &mailTransport*/) 0201 { 0202 const QString to = incidence->organizer().fullName(); 0203 QString subject = sub; 0204 0205 if (incidence->type() != KCalendarCore::Incidence::TypeFreeBusy) { 0206 KCalendarCore::Incidence::Ptr inc = incidence.staticCast<KCalendarCore::Incidence>(); 0207 if (subject.isEmpty()) { 0208 subject = inc->summary(); 0209 } 0210 } else { 0211 subject = QStringLiteral("Free Busy Message"); 0212 } 0213 0214 QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); 0215 0216 return createMessage(/*identity, */ from, to, QString(), subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); 0217 }