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

kalarm

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

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