2 * kamail.cpp - email functions
4 * Copyright © 2002-2011 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h" //krazy:exclude=includes (kalarm.h must be first)
24 #include "functions.h"
25 #include "identities.h"
26 #include "kalarmapp.h"
27 #include "mainwindow.h"
28 #include "messagebox.h"
29 #include "preferences.h"
31 #include <kpimidentities/identitymanager.h>
32 #include <kpimidentities/identity.h>
33 #include <kpimutils/email.h>
34 #include <mailtransport/transportmanager.h>
35 #include <mailtransport/transport.h>
36 #include <mailtransport/messagequeuejob.h>
38 #include <kcalcore/person.h>
40 #include <kcal/person.h>
42 #include <kmime/kmime_header_parsing.h>
43 #include <kmime/kmime_headers.h>
44 #include <kmime/kmime_message.h>
46 #include <kstandarddirs.h>
48 #include <kaboutdata.h>
49 #include <kfileitem.h>
50 #include <kio/netaccess.h>
51 #include <ktemporaryfile.h>
52 #include <kemailsettings.h>
54 #include <kcharsets.h>
63 #include <QTextStream>
65 #include <QtDBus/QtDBus>
69 #ifdef KMAIL_SUPPORTED
70 #include "kmailinterface.h"
72 static const char* KMAIL_DBUS_SERVICE
= "org.kde.kmail";
73 //static const char* KMAIL_DBUS_PATH = "/KMail";
76 namespace HeaderParsing
78 bool parseAddress( const char* & scursor
, const char * const send
,
79 KMime::Types::Address
& result
, bool isCRLF
=false );
82 static void initHeaders(KMime::Message
&, KAMail::JobData
&);
83 static KMime::Types::Mailbox::List
parseAddresses(const QString
& text
, QString
& invalidItem
);
84 static QByteArray
autoDetectCharset(const QString
& text
);
85 static const QTextCodec
* codecForName(const QByteArray
& str
);
87 QString
KAMail::i18n_NeedFromEmailAddress()
88 { return i18nc("@info/plain", "A 'From' email address must be configured in order to execute email alarms."); }
90 QString
KAMail::i18n_sent_mail()
91 { return i18nc("@info/plain KMail folder name: this should be translated the same as in kmail", "sent-mail"); }
93 KAMail
* KAMail::mInstance
= 0; // used only to enable signals/slots to work
94 QQueue
<MailTransport::MessageQueueJob
*> KAMail::mJobs
;
95 QQueue
<KAMail::JobData
> KAMail::mJobData
;
97 KAMail
* KAMail::instance()
100 mInstance
= new KAMail();
105 /******************************************************************************
106 * Send the email message specified in an event.
107 * Reply = 1 if the message was sent - 'errmsgs' may contain copy error messages.
108 * = 0 if the message is queued for sending.
109 * = -1 if the message was not sent - 'errmsgs' contains the error messages.
111 int KAMail::send(JobData
& jobdata
, QStringList
& errmsgs
)
114 KPIMIdentities::Identity identity
;
115 if (!jobdata
.event
.emailFromId())
116 jobdata
.from
= Preferences::emailAddress();
119 identity
= Identities::identityManager()->identityForUoid(jobdata
.event
.emailFromId());
120 if (identity
.isNull())
122 kError() << "Identity" << jobdata
.event
.emailFromId() << "not found";
123 errmsgs
= errors(i18nc("@info", "Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> not found", jobdata
.event
.emailFromId()));
126 if (identity
.primaryEmailAddress().isEmpty())
128 kError() << "Identity" << identity
.identityName() << "uoid" << identity
.uoid() << ": no email address";
129 errmsgs
= errors(i18nc("@info", "Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> has no email address", identity
.identityName()));
132 jobdata
.from
= identity
.fullEmailAddr();
134 if (jobdata
.from
.isEmpty())
136 switch (Preferences::emailFrom())
138 case Preferences::MAIL_FROM_KMAIL
:
139 errmsgs
= errors(i18nc("@info", "<para>No 'From' email address is configured (no default email identity found)</para>"
140 "<para>Please set it in <application>KMail</application> or in the <application>KAlarm</application> Configuration dialog.</para>"));
142 case Preferences::MAIL_FROM_SYS_SETTINGS
:
143 errmsgs
= errors(i18nc("@info", "<para>No 'From' email address is configured.</para>"
144 "<para>Please set it in the KDE System Settings or in the <application>KAlarm</application> Configuration dialog.</para>"));
146 case Preferences::MAIL_FROM_ADDR
:
148 errmsgs
= errors(i18nc("@info", "<para>No 'From' email address is configured.</para>"
149 "<para>Please set it in the <application>KAlarm</application> Configuration dialog.</para>"));
154 jobdata
.bcc
= (jobdata
.event
.emailBcc() ? Preferences::emailBccAddress() : QString());
155 kDebug() << "To:" << jobdata
.event
.emailAddresses(",")
156 << endl
<< "Subject:" << jobdata
.event
.emailSubject();
158 MailTransport::TransportManager
* manager
= MailTransport::TransportManager::self();
159 MailTransport::Transport
* transport
= 0;
160 if (Preferences::emailClient() == Preferences::sendmail
)
162 kDebug() << "Sending via sendmail";
163 const QList
<MailTransport::Transport
*> transports
= manager
->transports();
164 for (int i
= 0, count
= transports
.count(); i
< count
; ++i
)
166 if (transports
[i
]->type() == MailTransport::Transport::EnumType::Sendmail
)
168 // Use the first sendmail transport found
169 transport
= transports
[i
];
175 QString command
= KStandardDirs::findExe(QLatin1String("sendmail"),
176 QLatin1String("/sbin:/usr/sbin:/usr/lib"));
177 transport
= manager
->createTransport();
178 transport
->setName(QLatin1String("sendmail"));
179 transport
->setType(MailTransport::Transport::EnumType::Sendmail
);
180 transport
->setHost(command
);
181 transport
->setRequiresAuthentication(false);
182 transport
->setStorePassword(false);
183 manager
->addTransport(transport
);
184 transport
->writeConfig();
185 kDebug() << "Creating sendmail transport, id=" << transport
->id();
190 kDebug() << "Sending via KDE";
191 const int transportId
= identity
.transport().isEmpty() ? -1 : identity
.transport().toInt();
192 transport
= manager
->transportById( transportId
, true );
195 kError() << "No mail transport found for identity" << identity
.identityName() << "uoid" << identity
.uoid();
196 errmsgs
= errors(i18nc("@info", "No mail transport configured for email identity <resource>%1</resource>", identity
.identityName()));
200 kDebug() << "Using transport" << transport
->name() << ", id=" << transport
->id();
202 KMime::Message::Ptr message
= KMime::Message::Ptr(new KMime::Message
);
203 initHeaders(*message
, jobdata
);
204 err
= appendBodyAttachments(*message
, jobdata
);
207 kError() << "Error compiling message:" << err
;
208 errmsgs
= errors(err
);
212 MailTransport::MessageQueueJob
* mailjob
= new MailTransport::MessageQueueJob(kapp
);
213 mailjob
->setMessage(message
);
214 mailjob
->transportAttribute().setTransportId(transport
->id());
215 mailjob
->addressAttribute().setFrom(jobdata
.from
);
216 mailjob
->addressAttribute().setTo(static_cast<QStringList
>(jobdata
.event
.emailAddresses()));
217 if (!jobdata
.bcc
.isEmpty())
218 mailjob
->addressAttribute().setBcc(QStringList(KPIMUtils::extractEmailAddress(jobdata
.bcc
)));
219 MailTransport::SentBehaviourAttribute::SentBehaviour sentAction
=
220 (Preferences::emailClient() == Preferences::kmail
|| Preferences::emailCopyToKMail())
221 ? MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection
: MailTransport::SentBehaviourAttribute::Delete
;
222 mailjob
->sentBehaviourAttribute().setSentBehaviour(sentAction
);
223 mJobs
.enqueue(mailjob
);
224 mJobData
.enqueue(jobdata
);
225 if (mJobs
.count() == 1)
227 // There are no jobs already active or queued, so send now
228 connect(mailjob
, SIGNAL(result(KJob
*)), instance(), SLOT(slotEmailSent(KJob
*)));
234 /******************************************************************************
235 * Called when sending an email is complete.
237 void KAMail::slotEmailSent(KJob
* job
)
240 bool copyerr
= false;
244 kError() << "Failed:" << job
->errorString();
245 errmsgs
= errors(job
->errorString(), SEND_ERROR
);
249 if (mJobs
.isEmpty() || mJobData
.isEmpty() || job
!= mJobs
.head())
251 // The queue has been corrupted, so we can't locate the job's data
252 kError() << "Wrong job at head of queue: wiping queue";
255 if (!errmsgs
.isEmpty())
256 theApp()->emailSent(jobdata
, errmsgs
);
258 errmsgs
+= i18nc("@info", "Emails may not have been sent");
259 errmsgs
+= i18nc("@info", "Program error");
260 theApp()->emailSent(jobdata
, errmsgs
);
264 jobdata
= mJobData
.dequeue();
265 if (jobdata
.allowNotify
)
266 notifyQueued(jobdata
.event
);
267 theApp()->emailSent(jobdata
, errmsgs
, copyerr
);
268 if (!mJobs
.isEmpty())
270 // Send the next queued email
271 connect(mJobs
.head(), SIGNAL(result(KJob
*)), instance(), SLOT(slotEmailSent(KJob
*)));
272 mJobs
.head()->start();
276 /******************************************************************************
277 * Create the headers part of the email.
279 void initHeaders(KMime::Message
& message
, KAMail::JobData
& data
)
281 KMime::Headers::Date
* date
= new KMime::Headers::Date
;
282 date
->setDateTime(KDateTime::currentDateTime(Preferences::timeZone()));
283 message
.setHeader(date
);
285 KMime::Headers::From
* from
= new KMime::Headers::From
;
286 from
->fromUnicodeString(data
.from
, autoDetectCharset(data
.from
));
287 message
.setHeader(from
);
289 KMime::Headers::To
* to
= new KMime::Headers::To
;
290 EmailAddressList toList
= data
.event
.emailAddresses();
291 for (int i
= 0, count
= toList
.count(); i
< count
; ++i
)
293 to
->addAddress(toList
[i
]->email().toLatin1(), toList
[i
]->name());
295 to
->addAddress(toList
[i
].email().toLatin1(), toList
[i
].name());
297 message
.setHeader(to
);
299 if (!data
.bcc
.isEmpty())
301 KMime::Headers::Bcc
* bcc
= new KMime::Headers::Bcc
;
302 bcc
->fromUnicodeString(data
.bcc
, autoDetectCharset(data
.bcc
));
303 message
.setHeader(bcc
);
306 KMime::Headers::Subject
* subject
= new KMime::Headers::Subject
;
307 QString str
= data
.event
.emailSubject();
308 subject
->fromUnicodeString(str
, autoDetectCharset(str
));
309 message
.setHeader(subject
);
311 KMime::Headers::UserAgent
* agent
= new KMime::Headers::UserAgent
;
312 agent
->fromUnicodeString(KGlobal::mainComponent().aboutData()->programName() + "/" KALARM_VERSION
, "us-ascii");
313 message
.setHeader(agent
);
315 KMime::Headers::MessageID
* id
= new KMime::Headers::MessageID
;
316 id
->generate(data
.from
.mid(data
.from
.indexOf('@') + 1).toLatin1());
317 message
.setHeader(id
);
320 /******************************************************************************
321 * Append the body and attachments to the email text.
322 * Reply = reason for error
323 * = empty string if successful.
325 QString
KAMail::appendBodyAttachments(KMime::Message
& message
, JobData
& data
)
327 QStringList attachments
= data
.event
.emailAttachments();
328 if (!attachments
.count())
330 // There are no attachments, so simply append the message body
331 message
.contentType()->setMimeType("text/plain");
332 message
.contentType()->setCharset("utf-8");
333 message
.fromUnicodeString(data
.event
.message());
334 QList
<KMime::Headers::contentEncoding
> encodings
= KMime::encodingsForData(message
.body());
335 encodings
.removeAll(KMime::Headers::CE8Bit
); // not handled by KMime
336 message
.contentTransferEncoding()->setEncoding(encodings
[0]);
341 // There are attachments, so the message must be in MIME format
342 message
.contentType()->setMimeType("multipart/mixed");
343 message
.contentType()->setBoundary(KMime::multiPartBoundary());
345 if (!data
.event
.message().isEmpty())
347 // There is a message body
348 KMime::Content
* content
= new KMime::Content();
349 content
->contentType()->setMimeType("text/plain");
350 content
->contentType()->setCharset("utf-8");
351 content
->fromUnicodeString(data
.event
.message());
352 QList
<KMime::Headers::contentEncoding
> encodings
= KMime::encodingsForData(content
->body());
353 encodings
.removeAll(KMime::Headers::CE8Bit
); // not handled by KMime
354 content
->contentTransferEncoding()->setEncoding(encodings
[0]);
356 message
.addContent(content
);
359 // Append each attachment in turn
360 for (QStringList::Iterator at
= attachments
.begin(); at
!= attachments
.end(); ++at
)
362 QString attachment
= (*at
).toLocal8Bit();
363 KUrl
url(attachment
);
364 QString attachError
= i18nc("@info", "Error attaching file: <filename>%1</filename>", attachment
);
367 if (!KIO::NetAccess::stat(url
, uds
, MainWindow::mainMainWindow())) {
368 kError() << "Not found:" << attachment
;
369 return i18nc("@info", "Attachment not found: <filename>%1</filename>", attachment
);
371 KFileItem
fi(uds
, url
);
372 if (fi
.isDir() || !fi
.isReadable()) {
373 kError() << "Not file/not readable:" << attachment
;
377 // Read the file contents
379 if (!KIO::NetAccess::download(url
, tmpFile
, MainWindow::mainMainWindow())) {
380 kError() << "Load failure:" << attachment
;
384 if (!file
.open(QIODevice::ReadOnly
)) {
385 kDebug() << "tmp load error:" << attachment
;
388 qint64 size
= file
.size();
389 QByteArray contents
= file
.readAll();
391 bool atterror
= false;
392 if (contents
.size() < size
) {
393 kDebug() << "Read error:" << attachment
;
397 QByteArray coded
= KCodecs::base64Encode(contents
, true);
398 KMime::Content
* content
= new KMime::Content();
399 content
= new KMime::Content();
400 content
->setBody(coded
+ "\n\n");
402 // Set the content type
403 KMimeType::Ptr type
= KMimeType::findByUrl(url
);
404 KMime::Headers::ContentType
* ctype
= new KMime::Headers::ContentType(content
);
405 ctype
->fromUnicodeString(type
->name(), autoDetectCharset(type
->name()));
406 ctype
->setName(attachment
, "local");
407 content
->setHeader(ctype
);
410 KMime::Headers::ContentTransferEncoding
* cte
= new KMime::Headers::ContentTransferEncoding(content
);
411 cte
->setEncoding(KMime::Headers::CEbase64
);
412 cte
->setDecoded(false);
413 content
->setHeader(cte
);
415 message
.addContent(content
);
424 /******************************************************************************
425 * If any of the destination email addresses are non-local, display a
426 * notification message saying that an email has been queued for sending.
428 void KAMail::notifyQueued(const KAEvent
& event
)
430 KMime::Types::Address addr
;
431 QString localhost
= QLatin1String("localhost");
432 QString hostname
= QHostInfo::localHostName();
433 const EmailAddressList
& addresses
= event
.emailAddresses();
434 for (int i
= 0, end
= addresses
.count(); i
< end
; ++i
)
437 QByteArray email
= addresses
[i
]->email().toLocal8Bit();
439 QByteArray email
= addresses
[i
].email().toLocal8Bit();
441 const char* em
= email
;
443 && HeaderParsing::parseAddress(em
, em
+ email
.length(), addr
))
445 QString domain
= addr
.mailboxList
.first().addrSpec().domain
;
446 if (!domain
.isEmpty() && domain
!= localhost
&& domain
!= hostname
)
448 KAMessageBox::information(MainWindow::mainMainWindow(), i18nc("@info", "An email has been queued to be sent"), QString(), Preferences::EMAIL_QUEUED_NOTIFY
);
455 /******************************************************************************
456 * Fetch the user's email address configured in the KDE System Settings.
458 QString
KAMail::controlCentreAddress()
461 return e
.getSetting(KEMailSettings::EmailAddress
);
464 /******************************************************************************
465 * Parse a list of email addresses, optionally containing display names,
466 * entered by the user.
467 * Reply = the invalid item if error, else empty string.
469 QString
KAMail::convertAddresses(const QString
& items
, EmailAddressList
& list
)
473 const KMime::Types::Mailbox::List mailboxes
= parseAddresses(items
, invalidItem
);
474 if (!invalidItem
.isEmpty())
476 for (int i
= 0, count
= mailboxes
.count(); i
< count
; ++i
)
479 KCalCore::Person::Ptr
person(new KCalCore::Person(mailboxes
[i
].name(), mailboxes
[i
].addrSpec().asString()));
482 list
+= KCal::Person(mailboxes
[i
].name(), mailboxes
[i
].addrSpec().asString());
488 /******************************************************************************
489 * Check the validity of an email address.
490 * Because internal email addresses don't have to abide by the usual internet
491 * email address rules, only some basic checks are made.
492 * Reply = 1 if alright, 0 if empty, -1 if error.
494 int KAMail::checkAddress(QString
& address
)
496 address
= address
.trimmed();
497 // Check that there are no list separator characters present
498 if (address
.indexOf(QLatin1Char(',')) >= 0 || address
.indexOf(QLatin1Char(';')) >= 0)
500 int n
= address
.length();
505 if (address
[end
] == QLatin1Char('>'))
507 // The email address is in <...>
508 if ((start
= address
.indexOf(QLatin1Char('<'))) < 0)
513 int i
= address
.indexOf(QLatin1Char('@'), start
);
516 if (i
== start
|| i
== end
) // check @ isn't the first or last character
517 // || address.indexOf(QLatin1Char('@'), i + 1) >= 0) // check for multiple @ characters
522 // Allow the @ character to be missing if it's a local user
523 if (!getpwnam(address.mid(start, end - start + 1).toLocal8Bit()))
526 for (int i = start; i <= end; ++i)
528 char ch = address[i].toLatin1();
529 if (ch == '.' || ch == '@' || ch == '-' || ch == '_'
530 || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
531 || (ch >= '0' && ch <= '9'))
538 /******************************************************************************
539 * Convert a comma or semicolon delimited list of attachments into a
540 * QStringList. The items are checked for validity.
541 * Reply = the invalid item if error, else empty string.
543 QString
KAMail::convertAttachments(const QString
& items
, QStringList
& list
)
547 int length
= items
.length();
548 for (int next
= 0; next
< length
; )
550 // Find the first delimiter character (, or ;)
551 int i
= items
.indexOf(QLatin1Char(','), next
);
554 int sc
= items
.indexOf(QLatin1Char(';'), next
);
559 QString item
= items
.mid(next
, i
- next
).trimmed();
560 switch (checkAttachment(item
))
562 case 1: list
+= item
; break;
563 case 0: break; // empty attachment name
565 default: return item
; // error
572 /******************************************************************************
573 * Check for the existence of the attachment file.
574 * If non-null, '*url' receives the KUrl of the attachment.
575 * Reply = 1 if attachment exists
577 * = -1 if doesn't exist.
579 int KAMail::checkAttachment(QString
& attachment
, KUrl
* url
)
581 attachment
= attachment
.trimmed();
582 if (attachment
.isEmpty())
588 // Check that the file exists
593 return checkAttachment(u
) ? 1 : -1;
596 /******************************************************************************
597 * Check for the existence of the attachment file.
599 bool KAMail::checkAttachment(const KUrl
& url
)
602 if (!KIO::NetAccess::stat(url
, uds
, MainWindow::mainMainWindow()))
603 return false; // doesn't exist
604 KFileItem
fi(uds
, url
);
605 if (fi
.isDir() || !fi
.isReadable())
610 /******************************************************************************
611 * Set the appropriate error messages for a given error string.
613 QStringList
KAMail::errors(const QString
& err
, ErrType prefix
)
618 case SEND_FAIL
: error1
= i18nc("@info", "Failed to send email"); break;
619 case SEND_ERROR
: error1
= i18nc("@info", "Error sending email"); break;
622 return QStringList(error1
);
623 QStringList
errs(QString::fromLatin1("%1:").arg(error1
));
628 #ifdef KMAIL_SUPPORTED
629 /******************************************************************************
630 * Get the body of an email from KMail, given its serial number.
632 QString
KAMail::getMailBody(quint32 serialNumber
)
634 QList
<QVariant
> args
;
635 args
<< serialNumber
<< (int)0;
637 #warning Set correct DBus interface/object for kmail
639 QDBusInterface
iface(KMAIL_DBUS_SERVICE
, QString(), QLatin1String("KMailIface"));
640 QDBusReply
<QString
> reply
= iface
.callWithArgumentList(QDBus::Block
, QLatin1String("getDecodedBodyPart"), args
);
641 if (!reply
.isValid())
643 kError() << "D-Bus call failed:" << reply
.error().message();
646 return reply
.value();
650 //-----------------------------------------------------------------------------
651 // Based on KMail KMMsgBase::autoDetectCharset().
652 QByteArray
autoDetectCharset(const QString
& text
)
654 static QList
<QByteArray
> charsets
;
655 if (charsets
.isEmpty())
656 charsets
<< "us-ascii" << "iso-8859-1" << "locale" << "utf-8";
658 for (int i
= 0, count
= charsets
.count(); i
< count
; ++i
)
660 QByteArray encoding
= charsets
[i
];
661 if (encoding
== "locale")
663 encoding
= QTextCodec::codecForName(KGlobal::locale()->encoding())->name();
664 kAsciiToLower(encoding
.data());
668 if (encoding
== "us-ascii")
670 if (KMime::isUsAscii(text
))
675 const QTextCodec
*codec
= codecForName(encoding
);
677 kDebug() <<"Auto-Charset: Something is wrong and I can not get a codec. [" << encoding
<<"]";
680 if (codec
->canEncode(text
))
688 //-----------------------------------------------------------------------------
689 // Based on KMail KMMsgBase::codecForName().
690 const QTextCodec
* codecForName(const QByteArray
& str
)
694 QByteArray codec
= str
;
695 kAsciiToLower(codec
.data());
696 return KGlobal::charsets()->codecForName(codec
);
699 /******************************************************************************
700 * Parse a string containing multiple addresses, separated by comma or semicolon,
701 * while retaining Unicode name parts.
702 * Note that this only needs to parse strings input into KAlarm, so it only
703 * needs to accept the common syntax for email addresses, not obsolete syntax.
705 KMime::Types::Mailbox::List
parseAddresses(const QString
& text
, QString
& invalidItem
)
707 KMime::Types::Mailbox::List list
;
709 int start
= 0; // start of this item
710 int endName
= 0; // character after end of name
711 int startAddr
= 0; // start of address
712 int endAddr
= 0; // character after end of address
714 bool ended
= false; // found the end of the item
715 for (int i
= 0, count
= text
.length(); i
<= count
; ++i
)
721 char ch
= text
[i
].toLatin1();
724 case 0: // looking for start of item
725 if (ch
== ' ' || ch
== '\t')
728 state
= (ch
== '"') ? 10 : 1;
730 case 1: // looking for start of address, or end of item
748 case 2: // looking for '>' at end of address
755 case 3: // looking for item separator
756 if (ch
== ',' || ch
== ';')
760 invalidItem
= text
.mid(start
);
761 return KMime::Types::Mailbox::List();
764 case 10: // looking for closing quote
765 if (ch
== '"' && lastch
!= '\\')
767 ++start
; // remove opening quote from name
773 case 11: // looking for '<'
784 // Found the end of the item - add it to the list
791 QString addr
= text
.mid(startAddr
, endAddr
- startAddr
);
792 KMime::Types::Mailbox mbox
;
793 mbox
.fromUnicodeString(addr
);
794 if (mbox
.address().isEmpty())
796 invalidItem
= text
.mid(start
, endAddr
- start
);
797 return KMime::Types::Mailbox::List();
801 int len
= endName
- start
;
802 QString name
= text
.mid(start
, endName
- start
);
803 if (name
[0] == '"' && name
[len
- 1] == '"')
804 name
= name
.mid(1, len
- 2);
809 endName
= startAddr
= endAddr
= 0;
818 /*=============================================================================
819 = HeaderParsing : modified and additional functions.
820 = The following functions are modified from, or additional to, those in
821 = libkmime kmime_header_parsing.cpp.
822 =============================================================================*/
824 namespace HeaderParsing
827 using namespace KMime
;
828 using namespace KMime::Types
;
829 using namespace KMime::HeaderParsing
;
831 /******************************************************************************
833 * Allow a local user name to be specified as an email address.
835 bool parseUserName( const char* & scursor
, const char * const send
,
836 QString
& result
, bool isCRLF
) {
838 QString maybeLocalPart
;
841 if ( scursor
!= send
) {
842 // first, eat any whitespace
843 eatCFWS( scursor
, send
, isCRLF
);
845 char ch
= *scursor
++;
849 case '"': // quoted-string
853 scursor
--; // re-set scursor to point to ch again
855 if ( parseAtom( scursor
, send
, result
, false /* no 8bit */ ) ) {
856 if (getpwnam(result
.toLocal8Bit()))
859 return false; // parseAtom can only fail if the first char is non-atext.
865 /******************************************************************************
867 * Allow a local user name to be specified as an email address, and reinstate
868 * the original scursor on error return.
870 bool parseAddress( const char* & scursor
, const char * const send
,
871 Address
& result
, bool isCRLF
) {
872 // address := mailbox / group
874 eatCFWS( scursor
, send
, isCRLF
);
875 if ( scursor
== send
)
878 // first try if it's a single mailbox:
879 Mailbox maybeMailbox
;
880 const char * oldscursor
= scursor
;
881 if ( parseMailbox( scursor
, send
, maybeMailbox
, isCRLF
) ) {
883 result
.displayName
.clear();
884 result
.mailboxList
.append( maybeMailbox
);
887 scursor
= oldscursor
;
889 // KAlarm: Allow a local user name to be specified
890 // no, it's not a single mailbox. Try if it's a local user name:
891 QString maybeUserName
;
892 if ( parseUserName( scursor
, send
, maybeUserName
, isCRLF
) ) {
894 maybeMailbox
.setName( QString() );
896 addrSpec
.localPart
= maybeUserName
;
897 addrSpec
.domain
.clear();
898 maybeMailbox
.setAddress( addrSpec
);
899 result
.displayName
.clear();
900 result
.mailboxList
.append( maybeMailbox
);
903 scursor
= oldscursor
;
905 Address maybeAddress
;
907 // no, it's not a single mailbox. Try if it's a group:
908 if ( !parseGroup( scursor
, send
, maybeAddress
, isCRLF
) )
910 scursor
= oldscursor
; // KAlarm: reinstate original scursor on error return
914 result
= maybeAddress
;
918 } // namespace HeaderParsing