• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.14
  • kdepim
  • kalarm
kamail.cpp
Go to the documentation of this file.
1 /*
2  * kamail.cpp - email functions
3  * Program: kalarm
4  * Copyright © 2002-2016 by David Jarvie <djarvie@kde.org>
5  *
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.
10  *
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.
15  *
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.
19  */
20 
21 #include "kalarm.h" //krazy:exclude=includes (kalarm.h must be first)
22 #include "kamail.h"
23 
24 #include "functions.h"
25 #include "kalarmapp.h"
26 #include "mainwindow.h"
27 #include "messagebox.h"
28 #include "preferences.h"
29 
30 #include <kalarmcal/identities.h>
31 
32 #include <kpimidentities/identitymanager.h>
33 #include <kpimidentities/identity.h>
34 #include <kpimutils/email.h>
35 #include <mailtransport/transportmanager.h>
36 #include <mailtransport/transport.h>
37 #include <mailtransport/messagequeuejob.h>
38 #ifdef USE_AKONADI
39 #include <kcalcore/person.h>
40 #else
41 #include <kcal/person.h>
42 #endif
43 #include <kmime/kmime_header_parsing.h>
44 #include <kmime/kmime_headers.h>
45 #include <kmime/kmime_message.h>
46 
47 #include <kstandarddirs.h>
48 #include <klocale.h>
49 #include <kaboutdata.h>
50 #include <kfileitem.h>
51 #include <kio/netaccess.h>
52 #include <ktemporaryfile.h>
53 #include <kemailsettings.h>
54 #include <kcodecs.h>
55 #include <kcharsets.h>
56 #include <kascii.h>
57 #include <kdebug.h>
58 
59 #include <QFile>
60 #include <QHostInfo>
61 #include <QList>
62 #include <QByteArray>
63 #include <QTextCodec>
64 #include <QtDBus/QtDBus>
65 
66 #include <pwd.h>
67 
68 #ifdef KMAIL_SUPPORTED
69 #include "kmailinterface.h"
70 
71 static const QLatin1String KMAIL_DBUS_SERVICE("org.kde.kmail");
72 //static const QLatin1String KMAIL_DBUS_PATH("/KMail");
73 #endif
74 
75 namespace HeaderParsing
76 {
77 bool parseAddress( const char* & scursor, const char * const send,
78  KMime::Types::Address & result, bool isCRLF=false );
79 }
80 
81 static void initHeaders(KMime::Message&, KAMail::JobData&);
82 static KMime::Types::Mailbox::List parseAddresses(const QString& text, QString& invalidItem);
83 static QString extractEmailAndNormalize(const QString& emailAddress);
84 static QStringList extractEmailsAndNormalize(const QString& emailAddresses);
85 static QByteArray autoDetectCharset(const QString& text);
86 static const QTextCodec* codecForName(const QByteArray& str);
87 
88 QString KAMail::i18n_NeedFromEmailAddress()
89 { return i18nc("@info/plain", "A 'From' email address must be configured in order to execute email alarms."); }
90 
91 QString KAMail::i18n_sent_mail()
92 { return i18nc("@info/plain KMail folder name: this should be translated the same as in kmail", "sent-mail"); }
93 
94 KAMail* KAMail::mInstance = 0; // used only to enable signals/slots to work
95 QQueue<MailTransport::MessageQueueJob*> KAMail::mJobs;
96 QQueue<KAMail::JobData> KAMail::mJobData;
97 
98 KAMail* KAMail::instance()
99 {
100  if (!mInstance)
101  mInstance = new KAMail();
102  return mInstance;
103 }
104 
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.
110 */
111 int KAMail::send(JobData& jobdata, QStringList& errmsgs)
112 {
113  QString err;
114  KPIMIdentities::Identity identity;
115  jobdata.from = Preferences::emailAddress();
116  if (jobdata.event.emailFromId()
117  && Preferences::emailFrom() == Preferences::MAIL_FROM_KMAIL)
118  {
119  identity = Identities::identityManager()->identityForUoid(jobdata.event.emailFromId());
120  if (identity.isNull())
121  {
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()));
124  return -1;
125  }
126  if (identity.primaryEmailAddress().isEmpty())
127  {
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()));
130  return -1;
131  }
132  jobdata.from = identity.fullEmailAddr();
133  }
134  if (jobdata.from.isEmpty())
135  {
136  switch (Preferences::emailFrom())
137  {
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>"));
141  break;
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>"));
145  break;
146  case Preferences::MAIL_FROM_ADDR:
147  default:
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>"));
150  break;
151  }
152  return -1;
153  }
154  jobdata.bcc = (jobdata.event.emailBcc() ? Preferences::emailBccAddress() : QString());
155  kDebug() << "To:" << jobdata.event.emailAddresses(QLatin1String(","))
156  << endl << "Subject:" << jobdata.event.emailSubject();
157 
158  MailTransport::TransportManager* manager = MailTransport::TransportManager::self();
159  MailTransport::Transport* transport = 0;
160  if (Preferences::emailClient() == Preferences::sendmail)
161  {
162  kDebug() << "Sending via sendmail";
163  if (KStandardDirs::findExe(QLatin1String("akonadi_mailfilter_agent")).isEmpty())
164  {
165  kError() << "Sendmail requires akonadi_mailfilter_agent to be installed";
166  errmsgs = errors(i18nc("@info", "Sendmail option requires <filename>%1</filename> to be installed", QLatin1String("akonadi_mailfilter_agent")));
167  return -1;
168  }
169  const QList<MailTransport::Transport*> transports = manager->transports();
170  for (int i = 0, count = transports.count(); i < count; ++i)
171  {
172  if (transports[i]->type() == MailTransport::Transport::EnumType::Sendmail)
173  {
174  // Use the first sendmail transport found
175  transport = transports[i];
176  break;
177  }
178  }
179  if (!transport)
180  {
181  QString command = KStandardDirs::findExe(QLatin1String("sendmail"),
182  QLatin1String("/sbin:/usr/sbin:/usr/lib"));
183  transport = manager->createTransport();
184  transport->setName(QLatin1String("sendmail"));
185  transport->setType(MailTransport::Transport::EnumType::Sendmail);
186  transport->setHost(command);
187  transport->setRequiresAuthentication(false);
188  transport->setStorePassword(false);
189  manager->addTransport(transport);
190  transport->writeConfig();
191  kDebug() << "Creating sendmail transport, id=" << transport->id();
192  }
193  }
194  else
195  {
196  kDebug() << "Sending via KDE";
197  const int transportId = identity.transport().isEmpty() ? -1 : identity.transport().toInt();
198  transport = manager->transportById(transportId, true);
199  if (!transport)
200  {
201  kError() << "No mail transport found for identity" << identity.identityName() << "uoid" << identity.uoid();
202  errmsgs = errors(i18nc("@info", "No mail transport configured for email identity <resource>%1</resource>", identity.identityName()));
203  return -1;
204  }
205  }
206  kDebug() << "Using transport" << transport->name() << ", id=" << transport->id();
207 
208  KMime::Message::Ptr message = KMime::Message::Ptr(new KMime::Message);
209  initHeaders(*message, jobdata);
210  err = appendBodyAttachments(*message, jobdata);
211  if (!err.isNull())
212  {
213  kError() << "Error compiling message:" << err;
214  errmsgs = errors(err);
215  return -1;
216  }
217 
218  MailTransport::MessageQueueJob* mailjob = new MailTransport::MessageQueueJob(kapp);
219  mailjob->setMessage(message);
220  mailjob->transportAttribute().setTransportId(transport->id());
221  // MessageQueueJob email addresses must be pure, i.e. without display name. Note
222  // that display names are included in the actual headers set up by initHeaders().
223  mailjob->addressAttribute().setFrom(extractEmailAndNormalize(jobdata.from));
224  mailjob->addressAttribute().setTo(extractEmailsAndNormalize(jobdata.event.emailAddresses(QLatin1String(","))));
225  if (!jobdata.bcc.isEmpty())
226  mailjob->addressAttribute().setBcc(extractEmailsAndNormalize(jobdata.bcc));
227  MailTransport::SentBehaviourAttribute::SentBehaviour sentAction =
228  (Preferences::emailClient() == Preferences::kmail || Preferences::emailCopyToKMail())
229  ? MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection : MailTransport::SentBehaviourAttribute::Delete;
230  mailjob->sentBehaviourAttribute().setSentBehaviour(sentAction);
231  mJobs.enqueue(mailjob);
232  mJobData.enqueue(jobdata);
233  if (mJobs.count() == 1)
234  {
235  // There are no jobs already active or queued, so send now
236  connect(mailjob, SIGNAL(result(KJob*)), instance(), SLOT(slotEmailSent(KJob*)));
237  mailjob->start();
238  }
239  return 0;
240 }
241 
242 /******************************************************************************
243 * Called when sending an email is complete.
244 */
245 void KAMail::slotEmailSent(KJob* job)
246 {
247  bool copyerr = false;
248  QStringList errmsgs;
249  if (job->error())
250  {
251  kError() << "Failed:" << job->errorString();
252  errmsgs = errors(job->errorString(), SEND_ERROR);
253  }
254  JobData jobdata;
255  if (mJobs.isEmpty() || mJobData.isEmpty() || job != mJobs.head())
256  {
257  // The queue has been corrupted, so we can't locate the job's data
258  kError() << "Wrong job at head of queue: wiping queue";
259  mJobs.clear();
260  mJobData.clear();
261  if (!errmsgs.isEmpty())
262  theApp()->emailSent(jobdata, errmsgs);
263  errmsgs.clear();
264  errmsgs += i18nc("@info", "Emails may not have been sent");
265  errmsgs += i18nc("@info", "Program error");
266  theApp()->emailSent(jobdata, errmsgs);
267  return;
268  }
269  mJobs.dequeue();
270  jobdata = mJobData.dequeue();
271  if (jobdata.allowNotify)
272  notifyQueued(jobdata.event);
273  theApp()->emailSent(jobdata, errmsgs, copyerr);
274  if (!mJobs.isEmpty())
275  {
276  // Send the next queued email
277  connect(mJobs.head(), SIGNAL(result(KJob*)), instance(), SLOT(slotEmailSent(KJob*)));
278  mJobs.head()->start();
279  }
280 }
281 
282 /******************************************************************************
283 * Create the headers part of the email.
284 */
285 void initHeaders(KMime::Message& message, KAMail::JobData& data)
286 {
287  KMime::Headers::Date* date = new KMime::Headers::Date;
288  date->setDateTime(KDateTime::currentDateTime(Preferences::timeZone()));
289  message.setHeader(date);
290 
291  KMime::Headers::From* from = new KMime::Headers::From;
292  from->fromUnicodeString(data.from, autoDetectCharset(data.from));
293  message.setHeader(from);
294 
295  KMime::Headers::To* to = new KMime::Headers::To;
296 #ifdef USE_AKONADI
297  KCalCore::Person::List toList = data.event.emailAddressees();
298 #else
299  QList<KCal::Person> toList = data.event.emailAddressees();
300 #endif
301  for (int i = 0, count = toList.count(); i < count; ++i)
302 #ifdef USE_AKONADI
303  to->addAddress(toList[i]->email().toLatin1(), toList[i]->name());
304 #else
305  to->addAddress(toList[i].email().toLatin1(), toList[i].name());
306 #endif
307  message.setHeader(to);
308 
309  if (!data.bcc.isEmpty())
310  {
311  KMime::Headers::Bcc* bcc = new KMime::Headers::Bcc;
312  bcc->fromUnicodeString(data.bcc, autoDetectCharset(data.bcc));
313  message.setHeader(bcc);
314  }
315 
316  KMime::Headers::Subject* subject = new KMime::Headers::Subject;
317  QString str = data.event.emailSubject();
318  subject->fromUnicodeString(str, autoDetectCharset(str));
319  message.setHeader(subject);
320 
321  KMime::Headers::UserAgent* agent = new KMime::Headers::UserAgent;
322  agent->fromUnicodeString(KGlobal::mainComponent().aboutData()->programName() + QLatin1String("/" KALARM_VERSION), "us-ascii");
323  message.setHeader(agent);
324 
325  KMime::Headers::MessageID* id = new KMime::Headers::MessageID;
326  id->generate(data.from.mid(data.from.indexOf(QLatin1Char('@')) + 1).toLatin1());
327  message.setHeader(id);
328 }
329 
330 /******************************************************************************
331 * Append the body and attachments to the email text.
332 * Reply = reason for error
333 * = empty string if successful.
334 */
335 QString KAMail::appendBodyAttachments(KMime::Message& message, JobData& data)
336 {
337  QStringList attachments = data.event.emailAttachments();
338  if (!attachments.count())
339  {
340  // There are no attachments, so simply append the message body
341  message.contentType()->setMimeType("text/plain");
342  message.contentType()->setCharset("utf-8");
343  message.fromUnicodeString(data.event.message());
344  QList<KMime::Headers::contentEncoding> encodings = KMime::encodingsForData(message.body());
345  encodings.removeAll(KMime::Headers::CE8Bit); // not handled by KMime
346  message.contentTransferEncoding()->setEncoding(encodings[0]);
347  message.assemble();
348  }
349  else
350  {
351  // There are attachments, so the message must be in MIME format
352  message.contentType()->setMimeType("multipart/mixed");
353  message.contentType()->setBoundary(KMime::multiPartBoundary());
354 
355  if (!data.event.message().isEmpty())
356  {
357  // There is a message body
358  KMime::Content* content = new KMime::Content();
359  content->contentType()->setMimeType("text/plain");
360  content->contentType()->setCharset("utf-8");
361  content->fromUnicodeString(data.event.message());
362  QList<KMime::Headers::contentEncoding> encodings = KMime::encodingsForData(content->body());
363  encodings.removeAll(KMime::Headers::CE8Bit); // not handled by KMime
364  content->contentTransferEncoding()->setEncoding(encodings[0]);
365  content->assemble();
366  message.addContent(content);
367  }
368 
369  // Append each attachment in turn
370  for (QStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at)
371  {
372  QString attachment = QString::fromLatin1((*at).toLocal8Bit());
373  KUrl url(attachment);
374  QString attachError = i18nc("@info", "Error attaching file: <filename>%1</filename>", attachment);
375  url.cleanPath();
376  KIO::UDSEntry uds;
377  if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) {
378  kError() << "Not found:" << attachment;
379  return i18nc("@info", "Attachment not found: <filename>%1</filename>", attachment);
380  }
381  KFileItem fi(uds, url);
382  if (fi.isDir() || !fi.isReadable()) {
383  kError() << "Not file/not readable:" << attachment;
384  return attachError;
385  }
386 
387  // Read the file contents
388  QString tmpFile;
389  if (!KIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow())) {
390  kError() << "Load failure:" << attachment;
391  return attachError;
392  }
393  QFile file(tmpFile);
394  if (!file.open(QIODevice::ReadOnly)) {
395  kDebug() << "tmp load error:" << attachment;
396  return attachError;
397  }
398  qint64 size = file.size();
399  QByteArray contents = file.readAll();
400  file.close();
401  bool atterror = false;
402  if (contents.size() < size) {
403  kDebug() << "Read error:" << attachment;
404  atterror = true;
405  }
406 
407  QByteArray coded = KCodecs::base64Encode(contents, true);
408  KMime::Content* content = new KMime::Content();
409  content->setBody(coded + "\n\n");
410 
411  // Set the content type
412  KMimeType::Ptr type = KMimeType::findByUrl(url);
413  KMime::Headers::ContentType* ctype = new KMime::Headers::ContentType(content);
414  ctype->fromUnicodeString(type->name(), autoDetectCharset(type->name()));
415  ctype->setName(attachment, "local");
416  content->setHeader(ctype);
417 
418  // Set the encoding
419  KMime::Headers::ContentTransferEncoding* cte = new KMime::Headers::ContentTransferEncoding(content);
420  cte->setEncoding(KMime::Headers::CEbase64);
421  cte->setDecoded(false);
422  content->setHeader(cte);
423  content->assemble();
424  message.addContent(content);
425  if (atterror)
426  return attachError;
427  }
428  message.assemble();
429  }
430  return QString();
431 }
432 
433 /******************************************************************************
434 * If any of the destination email addresses are non-local, display a
435 * notification message saying that an email has been queued for sending.
436 */
437 void KAMail::notifyQueued(const KAEvent& event)
438 {
439  KMime::Types::Address addr;
440  QString localhost = QLatin1String("localhost");
441  QString hostname = QHostInfo::localHostName();
442 #ifdef USE_AKONADI
443  KCalCore::Person::List addresses = event.emailAddressees();
444 #else
445  QList<KCal::Person> addresses = event.emailAddressees();
446 #endif
447  for (int i = 0, end = addresses.count(); i < end; ++i)
448  {
449 #ifdef USE_AKONADI
450  QByteArray email = addresses[i]->email().toLocal8Bit();
451 #else
452  QByteArray email = addresses[i].email().toLocal8Bit();
453 #endif
454  const char* em = email;
455  if (!email.isEmpty()
456  && HeaderParsing::parseAddress(em, em + email.length(), addr))
457  {
458  QString domain = addr.mailboxList.first().addrSpec().domain;
459  if (!domain.isEmpty() && domain != localhost && domain != hostname)
460  {
461  KAMessageBox::information(MainWindow::mainMainWindow(), i18nc("@info", "An email has been queued to be sent"), QString(), Preferences::EMAIL_QUEUED_NOTIFY);
462  return;
463  }
464  }
465  }
466 }
467 
468 /******************************************************************************
469 * Fetch the user's email address configured in the KDE System Settings.
470 */
471 QString KAMail::controlCentreAddress()
472 {
473  KEMailSettings e;
474  return e.getSetting(KEMailSettings::EmailAddress);
475 }
476 
477 /******************************************************************************
478 * Parse a list of email addresses, optionally containing display names,
479 * entered by the user.
480 * Reply = the invalid item if error, else empty string.
481 */
482 #ifdef USE_AKONADI
483 QString KAMail::convertAddresses(const QString& items, KCalCore::Person::List& list)
484 #else
485 QString KAMail::convertAddresses(const QString& items, QList<KCal::Person>& list)
486 #endif
487 {
488  list.clear();
489  QString invalidItem;
490  const KMime::Types::Mailbox::List mailboxes = parseAddresses(items, invalidItem);
491  if (!invalidItem.isEmpty())
492  return invalidItem;
493  for (int i = 0, count = mailboxes.count(); i < count; ++i)
494  {
495 #ifdef USE_AKONADI
496  KCalCore::Person::Ptr person(new KCalCore::Person(mailboxes[i].name(), mailboxes[i].addrSpec().asString()));
497  list += person;
498 #else
499  list += KCal::Person(mailboxes[i].name(), mailboxes[i].addrSpec().asString());
500 #endif
501  }
502  return QString();
503 }
504 
505 /******************************************************************************
506 * Check the validity of an email address.
507 * Because internal email addresses don't have to abide by the usual internet
508 * email address rules, only some basic checks are made.
509 * Reply = 1 if alright, 0 if empty, -1 if error.
510 */
511 int KAMail::checkAddress(QString& address)
512 {
513  address = address.trimmed();
514  // Check that there are no list separator characters present
515  if (address.indexOf(QLatin1Char(',')) >= 0 || address.indexOf(QLatin1Char(';')) >= 0)
516  return -1;
517  int n = address.length();
518  if (!n)
519  return 0;
520  int start = 0;
521  int end = n - 1;
522  if (address[end] == QLatin1Char('>'))
523  {
524  // The email address is in <...>
525  if ((start = address.indexOf(QLatin1Char('<'))) < 0)
526  return -1;
527  ++start;
528  --end;
529  }
530  int i = address.indexOf(QLatin1Char('@'), start);
531  if (i >= 0)
532  {
533  if (i == start || i == end) // check @ isn't the first or last character
534 // || address.indexOf(QLatin1Char('@'), i + 1) >= 0) // check for multiple @ characters
535  return -1;
536  }
537 /* else
538  {
539  // Allow the @ character to be missing if it's a local user
540  if (!getpwnam(address.mid(start, end - start + 1).toLocal8Bit()))
541  return false;
542  }
543  for (int i = start; i <= end; ++i)
544  {
545  char ch = address[i].toLatin1();
546  if (ch == '.' || ch == '@' || ch == '-' || ch == '_'
547  || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
548  || (ch >= '0' && ch <= '9'))
549  continue;
550  return false;
551  }*/
552  return 1;
553 }
554 
555 /******************************************************************************
556 * Convert a comma or semicolon delimited list of attachments into a
557 * QStringList. The items are checked for validity.
558 * Reply = the invalid item if error, else empty string.
559 */
560 QString KAMail::convertAttachments(const QString& items, QStringList& list)
561 {
562  KUrl url;
563  list.clear();
564  int length = items.length();
565  for (int next = 0; next < length; )
566  {
567  // Find the first delimiter character (, or ;)
568  int i = items.indexOf(QLatin1Char(','), next);
569  if (i < 0)
570  i = items.length();
571  int sc = items.indexOf(QLatin1Char(';'), next);
572  if (sc < 0)
573  sc = items.length();
574  if (sc < i)
575  i = sc;
576  QString item = items.mid(next, i - next).trimmed();
577  switch (checkAttachment(item))
578  {
579  case 1: list += item; break;
580  case 0: break; // empty attachment name
581  case -1:
582  default: return item; // error
583  }
584  next = i + 1;
585  }
586  return QString();
587 }
588 
589 /******************************************************************************
590 * Check for the existence of the attachment file.
591 * If non-null, '*url' receives the KUrl of the attachment.
592 * Reply = 1 if attachment exists
593 * = 0 if null name
594 * = -1 if doesn't exist.
595 */
596 int KAMail::checkAttachment(QString& attachment, KUrl* url)
597 {
598  attachment = attachment.trimmed();
599  if (attachment.isEmpty())
600  {
601  if (url)
602  *url = KUrl();
603  return 0;
604  }
605  // Check that the file exists
606  KUrl u(attachment);
607  u.cleanPath();
608  if (url)
609  *url = u;
610  return checkAttachment(u) ? 1 : -1;
611 }
612 
613 /******************************************************************************
614 * Check for the existence of the attachment file.
615 */
616 bool KAMail::checkAttachment(const KUrl& url)
617 {
618  KIO::UDSEntry uds;
619  if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
620  return false; // doesn't exist
621  KFileItem fi(uds, url);
622  if (fi.isDir() || !fi.isReadable())
623  return false;
624  return true;
625 }
626 
627 /******************************************************************************
628 * Set the appropriate error messages for a given error string.
629 */
630 QStringList KAMail::errors(const QString& err, ErrType prefix)
631 {
632  QString error1;
633  switch (prefix)
634  {
635  case SEND_FAIL: error1 = i18nc("@info", "Failed to send email"); break;
636  case SEND_ERROR: error1 = i18nc("@info", "Error sending email"); break;
637  }
638  if (err.isEmpty())
639  return QStringList(error1);
640  QStringList errs(QString::fromLatin1("%1:").arg(error1));
641  errs += err;
642  return errs;
643 }
644 
645 #ifdef KMAIL_SUPPORTED
646 /******************************************************************************
647 * Get the body of an email from KMail, given its serial number.
648 */
649 QString KAMail::getMailBody(quint32 serialNumber)
650 {
651  QList<QVariant> args;
652  args << serialNumber << (int)0;
653 #ifdef __GNUC__
654 #warning Set correct DBus interface/object for kmail
655 #endif
656  QDBusInterface iface(KMAIL_DBUS_SERVICE, QString(), QLatin1String("KMailIface"));
657  QDBusReply<QString> reply = iface.callWithArgumentList(QDBus::Block, QLatin1String("getDecodedBodyPart"), args);
658  if (!reply.isValid())
659  {
660  kError() << "D-Bus call failed:" << reply.error().message();
661  return QString();
662  }
663  return reply.value();
664 }
665 #endif
666 
667 /******************************************************************************
668 * Extract the pure addresses from given email addresses.
669 */
670 QString extractEmailAndNormalize(const QString& emailAddress)
671 {
672  return KPIMUtils::extractEmailAddress(KPIMUtils::normalizeAddressesAndEncodeIdn(emailAddress));
673 }
674 
675 QStringList extractEmailsAndNormalize(const QString& emailAddresses)
676 {
677  const QStringList splitEmails(KPIMUtils::splitAddressList(emailAddresses));
678  QStringList normalizedEmail;
679  Q_FOREACH(const QString& email, splitEmails)
680  {
681  normalizedEmail << KPIMUtils::extractEmailAddress(KPIMUtils::normalizeAddressesAndEncodeIdn(email));
682  }
683  return normalizedEmail;
684 }
685 
686 //-----------------------------------------------------------------------------
687 // Based on KMail KMMsgBase::autoDetectCharset().
688 QByteArray autoDetectCharset(const QString& text)
689 {
690  static QList<QByteArray> charsets;
691  if (charsets.isEmpty())
692  charsets << "us-ascii" << "iso-8859-1" << "locale" << "utf-8";
693 
694  for (int i = 0, count = charsets.count(); i < count; ++i)
695  {
696  QByteArray encoding = charsets[i];
697  if (encoding == "locale")
698  {
699  encoding = QTextCodec::codecForName(KGlobal::locale()->encoding())->name();
700  kAsciiToLower(encoding.data());
701  }
702  if (text.isEmpty())
703  return encoding;
704  if (encoding == "us-ascii")
705  {
706  if (KMime::isUsAscii(text))
707  return encoding;
708  }
709  else
710  {
711  const QTextCodec *codec = codecForName(encoding);
712  if (!codec)
713  kDebug() <<"Auto-Charset: Something is wrong and I cannot get a codec. [" << encoding <<"]";
714  else
715  {
716  if (codec->canEncode(text))
717  return encoding;
718  }
719  }
720  }
721  return 0;
722 }
723 
724 //-----------------------------------------------------------------------------
725 // Based on KMail KMMsgBase::codecForName().
726 const QTextCodec* codecForName(const QByteArray& str)
727 {
728  if (str.isEmpty())
729  return 0;
730  QByteArray codec = str;
731  kAsciiToLower(codec.data());
732  return KGlobal::charsets()->codecForName(QLatin1String(codec));
733 }
734 
735 /******************************************************************************
736 * Parse a string containing multiple addresses, separated by comma or semicolon,
737 * while retaining Unicode name parts.
738 * Note that this only needs to parse strings input into KAlarm, so it only
739 * needs to accept the common syntax for email addresses, not obsolete syntax.
740 */
741 KMime::Types::Mailbox::List parseAddresses(const QString& text, QString& invalidItem)
742 {
743  KMime::Types::Mailbox::List list;
744  int state = 0;
745  int start = 0; // start of this item
746  int endName = 0; // character after end of name
747  int startAddr = 0; // start of address
748  int endAddr = 0; // character after end of address
749  char lastch = '\0';
750  bool ended = false; // found the end of the item
751  for (int i = 0, count = text.length(); i <= count; ++i)
752  {
753  if (i == count)
754  ended = true;
755  else
756  {
757  char ch = text[i].toLatin1();
758  switch (state)
759  {
760  case 0: // looking for start of item
761  if (ch == ' ' || ch == '\t')
762  continue;
763  start = i;
764  state = (ch == '"') ? 10 : 1;
765  break;
766  case 1: // looking for start of address, or end of item
767  switch (ch)
768  {
769  case '<':
770  startAddr = i + 1;
771  state = 2;
772  break;
773  case ',':
774  case ';':
775  ended = true;
776  break;
777  case ' ':
778  break;
779  default:
780  endName = i + 1;
781  break;
782  }
783  break;
784  case 2: // looking for '>' at end of address
785  if (ch == '>')
786  {
787  endAddr = i;
788  state = 3;
789  }
790  break;
791  case 3: // looking for item separator
792  if (ch == ',' || ch == ';')
793  ended = true;
794  else if (ch != ' ')
795  {
796  invalidItem = text.mid(start);
797  return KMime::Types::Mailbox::List();
798  }
799  break;
800  case 10: // looking for closing quote
801  if (ch == '"' && lastch != '\\')
802  {
803  ++start; // remove opening quote from name
804  endName = i;
805  state = 11;
806  }
807  lastch = ch;
808  break;
809  case 11: // looking for '<'
810  if (ch == '<')
811  {
812  startAddr = i + 1;
813  state = 2;
814  }
815  break;
816  }
817  }
818  if (ended)
819  {
820  // Found the end of the item - add it to the list
821  if (!startAddr)
822  {
823  startAddr = start;
824  endAddr = endName;
825  endName = 0;
826  }
827  QString addr = text.mid(startAddr, endAddr - startAddr);
828  KMime::Types::Mailbox mbox;
829  mbox.fromUnicodeString(addr);
830  if (mbox.address().isEmpty())
831  {
832  invalidItem = text.mid(start, endAddr - start);
833  return KMime::Types::Mailbox::List();
834  }
835  if (endName)
836  {
837  int len = endName - start;
838  QString name = text.mid(start, endName - start);
839  if (name[0] == QLatin1Char('"') && name[len - 1] == QLatin1Char('"'))
840  name = name.mid(1, len - 2);
841  mbox.setName(name);
842  }
843  list.append(mbox);
844 
845  endName = startAddr = endAddr = 0;
846  start = i + 1;
847  state = 0;
848  ended = false;
849  }
850  }
851  return list;
852 }
853 
854 /*=============================================================================
855 = HeaderParsing : modified and additional functions.
856 = The following functions are modified from, or additional to, those in
857 = libkmime kmime_header_parsing.cpp.
858 =============================================================================*/
859 
860 namespace HeaderParsing
861 {
862 
863 using namespace KMime;
864 using namespace KMime::Types;
865 using namespace KMime::HeaderParsing;
866 
867 /******************************************************************************
868 * New function.
869 * Allow a local user name to be specified as an email address.
870 */
871 bool parseUserName( const char* & scursor, const char * const send,
872  QString & result, bool isCRLF ) {
873 
874  QString maybeLocalPart;
875  QString tmp;
876 
877  if ( scursor != send ) {
878  // first, eat any whitespace
879  eatCFWS( scursor, send, isCRLF );
880 
881  char ch = *scursor++;
882  switch ( ch ) {
883  case '.': // dot
884  case '@':
885  case '"': // quoted-string
886  return false;
887 
888  default: // atom
889  scursor--; // re-set scursor to point to ch again
890  tmp.clear();
891  if ( parseAtom( scursor, send, result, false /* no 8bit */ ) ) {
892  if (getpwnam(result.toLocal8Bit()))
893  return true;
894  }
895  return false; // parseAtom can only fail if the first char is non-atext.
896  }
897  }
898  return false;
899 }
900 
901 /******************************************************************************
902 * Modified function.
903 * Allow a local user name to be specified as an email address, and reinstate
904 * the original scursor on error return.
905 */
906 bool parseAddress( const char* & scursor, const char * const send,
907  Address & result, bool isCRLF ) {
908  // address := mailbox / group
909 
910  eatCFWS( scursor, send, isCRLF );
911  if ( scursor == send )
912  return false;
913 
914  // first try if it's a single mailbox:
915  Mailbox maybeMailbox;
916  const char * oldscursor = scursor;
917  if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
918  // yes, it is:
919  result.displayName.clear();
920  result.mailboxList.append( maybeMailbox );
921  return true;
922  }
923  scursor = oldscursor;
924 
925  // KAlarm: Allow a local user name to be specified
926  // no, it's not a single mailbox. Try if it's a local user name:
927  QString maybeUserName;
928  if ( parseUserName( scursor, send, maybeUserName, isCRLF ) ) {
929  // yes, it is:
930  maybeMailbox.setName( QString() );
931  AddrSpec addrSpec;
932  addrSpec.localPart = maybeUserName;
933  addrSpec.domain.clear();
934  maybeMailbox.setAddress( addrSpec );
935  result.displayName.clear();
936  result.mailboxList.append( maybeMailbox );
937  return true;
938  }
939  scursor = oldscursor;
940 
941  Address maybeAddress;
942 
943  // no, it's not a single mailbox. Try if it's a group:
944  if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) )
945  {
946  scursor = oldscursor; // KAlarm: reinstate original scursor on error return
947  return false;
948  }
949 
950  result = maybeAddress;
951  return true;
952 }
953 
954 } // namespace HeaderParsing
955 
956 // vim: et sw=4:
QList::clear
void clear()
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QString::append
QString & append(QChar ch)
KAMail::controlCentreAddress
static QString controlCentreAddress()
Definition: kamail.cpp:471
HeaderParsing::parseUserName
bool parseUserName(const char *&scursor, const char *const send, QString &result, bool isCRLF)
Definition: kamail.cpp:871
QQueue< MailTransport::MessageQueueJob * >
QTextCodec::name
virtual QByteArray name() const =0
initHeaders
static void initHeaders(KMime::Message &, KAMail::JobData &)
Definition: kamail.cpp:285
extractEmailsAndNormalize
static QStringList extractEmailsAndNormalize(const QString &emailAddresses)
Definition: kamail.cpp:675
KAlarmApp::emailSent
void emailSent(KAMail::JobData &, const QStringList &errmsgs, bool copyerr=false)
Definition: kalarmapp.cpp:2053
QByteArray
QQueue::enqueue
void enqueue(const T &t)
QDBusReply
text
virtual QByteArray text(quint32 serialNumber) const =0
date
time_t date() const
QQueue::dequeue
T dequeue()
KAMail::checkAddress
static int checkAddress(QString &address)
Definition: kamail.cpp:511
QString::size
int size() const
QByteArray::isEmpty
bool isEmpty() const
QQueue::head
T & head()
KALARM_VERSION
#define KALARM_VERSION
Definition: kalarm.h:31
QDBusError::message
QString message() const
KAMail::JobData::event
KAEvent event
Definition: kamail.h:61
KAMail::JobData::from
QString from
Definition: kamail.h:63
QDBusReply::isValid
bool isValid() const
KAMail::i18n_sent_mail
static QString i18n_sent_mail()
Definition: kamail.cpp:91
QByteArray::length
int length() const
from
QString from() const
Preferences::MAIL_FROM_ADDR
Definition: preferences.h:40
QFile
QString::isNull
bool isNull() const
QString::clear
void clear()
kalarmapp.h
the KAlarm application object
KAMail::convertAddresses
static QString convertAddresses(const QString &addresses, QList< KCal::Person > &)
Definition: kamail.cpp:485
QList::count
int count(const T &value) const
KAMail::checkAttachment
static int checkAttachment(QString &attachment, KUrl *=0)
Definition: kamail.cpp:596
Preferences::MAIL_FROM_KMAIL
Definition: preferences.h:40
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:199
QDBusReply::value
Type value() const
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
QList::removeAll
int removeAll(const T &value)
QString::trimmed
QString trimmed() const
KAMail
Definition: kamail.h:50
mainwindow.h
main application window
to
QString to() const
subject
QString subject() const
QList::Iterator
typedef Iterator
KAMail::i18n_NeedFromEmailAddress
static QString i18n_NeedFromEmailAddress()
Definition: kamail.cpp:88
KAMail::convertAttachments
static QString convertAttachments(const QString &attachments, QStringList &list)
Definition: kamail.cpp:560
messagebox.h
QString
QList
QTextCodec
Preferences::EMAIL_QUEUED_NOTIFY
static const QLatin1String EMAIL_QUEUED_NOTIFY
Definition: preferences.h:89
theApp
KAlarmApp * theApp()
Definition: kalarmapp.h:263
Preferences::emailCopyToKMail
static bool emailCopyToKMail()
Definition: preferences.h:69
QStringList
QDBusInterface
autoDetectCharset
static QByteArray autoDetectCharset(const QString &text)
Definition: kamail.cpp:688
QList::end
iterator end()
QString::toLocal8Bit
QByteArray toLocal8Bit() const
QLatin1Char
preferences.h
KAMail::JobData::bcc
QString bcc
Definition: kamail.h:63
kamail.h
QString::toLatin1
QByteArray toLatin1() const
QString::mid
QString mid(int position, int n) const
Preferences::emailBccAddress
static QString emailBccAddress()
Definition: preferences.cpp:335
QLatin1String
functions.h
miscellaneous functions
parseAddresses
static KMime::Types::Mailbox::List parseAddresses(const QString &text, QString &invalidItem)
Definition: kamail.cpp:741
Preferences::emailAddress
static QString emailAddress()
Definition: preferences.cpp:304
kalarm.h
QTextCodec::codecForName
QTextCodec * codecForName(const QByteArray &name)
QString::length
int length() const
QTextCodec::canEncode
bool canEncode(QChar ch) const
QByteArray::data
char * data()
Preferences::MAIL_FROM_SYS_SETTINGS
Definition: preferences.h:40
QHostInfo::localHostName
QString localHostName()
extractEmailAndNormalize
static QString extractEmailAndNormalize(const QString &emailAddress)
Definition: kamail.cpp:670
QString::fromLatin1
QString fromLatin1(const char *str, int size)
KAMail::JobData
Definition: kamail.h:56
KAMail::send
static int send(JobData &, QStringList &errmsgs)
Definition: kamail.cpp:111
serialNumber
quint32 serialNumber() const
QByteArray::size
int size() const
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:286
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QDBusReply::error
const QDBusError & error()
KJob
QList::begin
iterator begin()
codecForName
static const QTextCodec * codecForName(const QByteArray &str)
Definition: kamail.cpp:726
HeaderParsing::parseAddress
bool parseAddress(const char *&scursor, const char *const send, KMime::Types::Address &result, bool isCRLF=false)
Preferences::emailFrom
static MailFrom emailFrom()
Definition: preferences.cpp:291
KAMessageBox::information
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Options(Notify|WindowModal))
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:34:51 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer
  • pimprint

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal