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

kalarm

kamail.cpp

Go to the documentation of this file.
00001 /*
00002  *  kamail.cpp  -  email functions
00003  *  Program:  kalarm
00004  *  Copyright © 2002-2008 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"   //krazy:exclude=includes (kalarm.h must be first)
00022 #include "kamail.h"
00023 
00024 #include "functions.h"
00025 #include "kalarmapp.h"
00026 #include "mainwindow.h"
00027 #include "preferences.h"
00028 
00029 #include <stdlib.h>
00030 #include <time.h>
00031 #include <sys/stat.h>
00032 #include <sys/time.h>
00033 #include <pwd.h>
00034 
00035 #include <QFile>
00036 #include <QList>
00037 #include <QRegExp>
00038 #include <QByteArray>
00039 #include <QTextStream>
00040 #include <QtDBus/QtDBus>
00041 
00042 #include <kstandarddirs.h>
00043 #include <kmessagebox.h>
00044 #include <kshell.h>
00045 #include <klocale.h>
00046 #include <kaboutdata.h>
00047 #include <kfileitem.h>
00048 #include <kio/netaccess.h>
00049 #include <ktemporaryfile.h>
00050 #include <kemailsettings.h>
00051 #include <kdebug.h>
00052 #include <k3resolver.h>
00053 
00054 #include <kpimidentities/identitymanager.h>
00055 #include <kpimidentities/identity.h>
00056 #include <kpimutils/email.h>
00057 #include <mailtransport/transportmanager.h>
00058 #include <mailtransport/transport.h>
00059 #include <mailtransport/transportjob.h>
00060 #include <kcal/person.h>
00061 
00062 #include <kmime/kmime_header_parsing.h>
00063 
00064 #ifdef KMAIL_SUPPORTED
00065 #include "kmailinterface.h"
00066 
00067 static const char* KMAIL_DBUS_SERVICE = "org.kde.kmail";
00068 static const char* KMAIL_DBUS_PATH    = "/KMail";
00069 #endif
00070 
00071 
00072 namespace HeaderParsing
00073 {
00074 bool parseAddress( const char* & scursor, const char * const send,
00075                    KMime::Types::Address & result, bool isCRLF=false );
00076 bool parseAddressList( const char* & scursor, const char * const send,
00077                        QList<KMime::Types::Address> & result, bool isCRLF=false );
00078 }
00079 
00080 
00081 static QString initHeaders(const KAMail::JobData&, bool dateId);
00082 
00083 QString KAMail::i18n_NeedFromEmailAddress()
00084 { return i18nc("@info/plain", "A 'From' email address must be configured in order to execute email alarms."); }
00085 
00086 QString KAMail::i18n_sent_mail()
00087 { return i18nc("@info/plain KMail folder name: this should be translated the same as in kmail", "sent-mail"); }
00088 
00089 KAMail*                              KAMail::mInstance = 0;   // used only to enable signals/slots to work
00090 QQueue<MailTransport::TransportJob*> KAMail::mJobs;
00091 QQueue<KAMail::JobData>              KAMail::mJobData;
00092 KPIMIdentities::IdentityManager*     KAMail::mIdentityManager = 0;
00093 
00094 KAMail* KAMail::instance()
00095 {
00096     if (!mInstance)
00097         mInstance = new KAMail();
00098     return mInstance;
00099 }
00100 
00101 KPIMIdentities::IdentityManager* KAMail::identityManager()
00102 {
00103     if (!mIdentityManager)
00104         mIdentityManager = new KPIMIdentities::IdentityManager(true);   // create a read-only kmail identity manager
00105     return mIdentityManager;
00106 }
00107 
00108 
00109 /******************************************************************************
00110 * Send the email message specified in an event.
00111 * Reply = 1 if the message was sent - 'errmsgs' may contain copy error messages.
00112 *       = 0 if the message is queued for sending.
00113 *       = -1 if the message was not sent - 'errmsgs' contains the error messages.
00114 */
00115 int KAMail::send(JobData& jobdata, QStringList& errmsgs)
00116 {
00117     QString err;
00118     KPIMIdentities::Identity identity;
00119     if (!jobdata.event.emailFromId())
00120         jobdata.from = Preferences::emailAddress();
00121     else
00122     {
00123         identity = mIdentityManager->identityForUoid(jobdata.event.emailFromId());
00124         if (identity.isNull())
00125         {
00126             kError() << "Identity" << jobdata.event.emailFromId() << "not found";
00127             errmsgs = errors(i18nc("@info", "Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> not found", jobdata.event.emailFromId()));
00128             return -1;
00129         }
00130         jobdata.from = identity.fullEmailAddr();
00131         if (jobdata.from.isEmpty())
00132         {
00133             kError() << "Identity" << identity.identityName() << "uoid" << identity.uoid() << ": no email address";
00134             errmsgs = errors(i18nc("@info", "Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> has no email address", identity.identityName()));
00135             return -1;
00136         }
00137     }
00138     if (jobdata.from.isEmpty())
00139     {
00140         switch (Preferences::emailFrom())
00141         {
00142 #ifdef KMAIL_SUPPORTED
00143             case Preferences::MAIL_FROM_KMAIL:
00144                 errmsgs = errors(i18nc("@info", "<para>No 'From' email address is configured (no default email identity found)</para>"
00145                                                 "<para>Please set it in <application>KMail</application> or in the <application>KAlarm</application> Preferences dialog.</para>"));
00146                 break;
00147 #endif
00148             case Preferences::MAIL_FROM_SYS_SETTINGS:
00149                 errmsgs = errors(i18nc("@info", "<para>No 'From' email address is configured.</para>"
00150                                                 "<para>Please set it in the KDE System Settings or in the <application>KAlarm</application> Preferences dialog.</para>"));
00151                 break;
00152             case Preferences::MAIL_FROM_ADDR:
00153             default:
00154                 errmsgs = errors(i18nc("@info", "<para>No 'From' email address is configured.</para>"
00155                                                 "<para>Please set it in the <application>KAlarm</application> Preferences dialog.</para>"));
00156                 break;
00157         }
00158         return -1;
00159     }
00160     jobdata.bcc  = (jobdata.event.emailBcc() ? Preferences::emailBccAddress() : QString());
00161     kDebug() << "To:" << jobdata.event.emailAddresses(",")
00162                   << endl << "Subject:" << jobdata.event.emailSubject();
00163 
00164     if (Preferences::emailClient() == Preferences::sendmail)
00165     {
00166         // Use sendmail to send the message
00167         QString textComplete;
00168         QString command = KStandardDirs::findExe(QLatin1String("sendmail"),
00169                                                  QLatin1String("/sbin:/usr/sbin:/usr/lib"));
00170         if (!command.isNull())
00171         {
00172             command += QLatin1String(" -f ");
00173             command += KPIMUtils::extractEmailAddress(jobdata.from);
00174             command += QLatin1String(" -oi -t ");
00175             textComplete = initHeaders(jobdata, false);
00176         }
00177         else
00178         {
00179             command = KStandardDirs::findExe(QLatin1String("mail"));
00180             if (command.isNull())
00181             {
00182                 errmsgs = errors(i18nc("@info", "<command>%1</command> not found", QLatin1String("sendmail"))); // give up
00183                 return -1;
00184             }
00185 
00186             command += QLatin1String(" -s ");
00187             command += KShell::quoteArg(jobdata.event.emailSubject());
00188 
00189             if (!jobdata.bcc.isEmpty())
00190             {
00191                 command += QLatin1String(" -b ");
00192                 command += KShell::quoteArg(jobdata.bcc);
00193             }
00194 
00195             command += ' ';
00196             command += jobdata.event.emailAddresses(" "); // locally provided, okay
00197         }
00198 
00199         // Add the body and attachments to the message.
00200         // (Sendmail requires attachments to have already been included in the message.)
00201         err = appendBodyAttachments(textComplete, jobdata.event);
00202         if (!err.isNull())
00203         {
00204             errmsgs = errors(err);
00205             return -1;
00206         }
00207 
00208         // Execute the send command
00209         FILE* fd = popen(command.toLocal8Bit(), "w");
00210         if (!fd)
00211         {
00212             kError() << "Unable to open a pipe to" << command;
00213             errmsgs = errors();
00214             return -1;
00215         }
00216         fwrite(textComplete.toLocal8Bit(), textComplete.length(), 1, fd);
00217         pclose(fd);
00218 
00219 #ifdef KMAIL_SUPPORTED
00220         if (Preferences::emailCopyToKMail())
00221         {
00222             // Create a copy of the sent email in KMail's 'Sent-mail' folder
00223             err = addToKMailFolder(jobdata, "sent-mail", true);
00224             if (!err.isNull())
00225                 errmsgs = errors(err, COPY_ERROR);    // not a fatal error - continue
00226         }
00227 #endif
00228 
00229         if (jobdata.allowNotify)
00230             notifyQueued(jobdata.event);
00231         return 1;
00232     }
00233     else
00234     {
00235         // Use KDE to send the message
00236         MailTransport::TransportManager* manager = MailTransport::TransportManager::self();
00237         MailTransport::Transport* transport = manager->transportByName(identity.transport(), true);
00238         if (!transport)
00239         {
00240             kError() << "No mail transport found for identity" << identity.identityName() << "uoid" << identity.uoid();
00241             errmsgs = errors(i18nc("@info", "No mail transport configured for email identity <resource>%1</resource>", identity.identityName()));
00242             return -1;
00243         }
00244         int transportId = transport->id();
00245         MailTransport::TransportJob* mailjob = manager->createTransportJob(transportId);
00246         if (!mailjob)
00247         {
00248             kError() << "Failed to create mail transport job for identity" << identity.identityName() << "uoid" << identity.uoid();
00249             errmsgs = errors(i18nc("@info", "Unable to create mail transport job"));
00250             return -1;
00251         }
00252         QString message = initHeaders(jobdata, true);
00253         message += jobdata.event.message();
00254         err = appendBodyAttachments(message, jobdata.event);
00255         if (!err.isNull())
00256         {
00257             errmsgs = errors(err);
00258             return -1;
00259         }
00260         mailjob->setSender(jobdata.from);
00261         mailjob->setTo(QStringList(jobdata.event.emailAddresses()));
00262         if (!jobdata.bcc.isEmpty())
00263             mailjob->setBcc(QStringList(jobdata.bcc));
00264         mailjob->setData(message.toLocal8Bit());
00265         mJobs.enqueue(mailjob);
00266         mJobData.enqueue(jobdata);
00267         if (mJobs.count() == 1)
00268         {
00269             // There are no jobs already active or queued, so send now
00270             connect(mailjob, SIGNAL(result(KJob*)), instance(), SLOT(slotEmailSent(KJob*)));
00271             mailjob->start();
00272         }
00273         return 0;
00274     }
00275 }
00276 
00277 /******************************************************************************
00278 * Called when sending an email is complete.
00279 */
00280 void KAMail::slotEmailSent(KJob* job)
00281 {
00282     QStringList errmsgs;
00283     JobData jobdata;
00284     if (job->error())
00285     {
00286         kError() << "Failed:" << job->error();
00287         errmsgs = errors(job->errorString(), SEND_ERROR);
00288     }
00289     if (mJobs.isEmpty()  ||  mJobData.isEmpty()  ||  job != mJobs.head())
00290     {
00291         // The queue has been corrupted, so we can't locate the job's data
00292         kError() << "Wrong job at head of queue: wiping queue";
00293         mJobs.clear();
00294         mJobData.clear();
00295         if (!errmsgs.isEmpty())
00296             theApp()->emailSent(jobdata, errmsgs);
00297         errmsgs.clear();
00298         errmsgs += i18nc("@info", "Emails may not have been sent");
00299         errmsgs += i18nc("@info", "Program error");
00300         theApp()->emailSent(jobdata, errmsgs);
00301         return;
00302     }
00303     mJobs.dequeue();
00304     jobdata = mJobData.dequeue();
00305     if (jobdata.allowNotify)
00306         notifyQueued(jobdata.event);
00307     theApp()->emailSent(jobdata, errmsgs);
00308     if (!mJobs.isEmpty())
00309     {
00310         // Send the next queued email
00311         connect(mJobs.head(), SIGNAL(result(KJob*)), instance(), SLOT(slotEmailSent(KJob*)));
00312         mJobs.head()->start();
00313     }
00314 }
00315 
00316 #ifdef KMAIL_SUPPORTED
00317 /******************************************************************************
00318 * Add the message to a KMail folder.
00319 * Reply = reason for failure (which may be the empty string)
00320 *       = null string if success.
00321 */
00322 QString KAMail::addToKMailFolder(const JobData& data, const char* folder, bool checkKmailRunning)
00323 {
00324     QString err;
00325     if (checkKmailRunning)
00326         err = KAlarm::runKMail(true);
00327     if (err.isNull())
00328     {
00329         QString message = initHeaders(data, true);
00330         err = appendBodyAttachments(message, data.event);
00331         if (!err.isNull())
00332             return err;
00333 
00334         // Write to a temporary file for feeding to KMail
00335         KTemporaryFile tmpFile;
00336         if (!tmpFile.open())
00337         {
00338             kError() << folder << ": Unable to open a temporary mail file";
00339             return QString("");
00340         }
00341         QTextStream stream(&tmpFile);
00342         stream << message;
00343         stream.flush();
00344         if (tmpFile.error() != QFile::NoError)
00345         {
00346             kError() << folder << ": Error" << tmpFile.errorString() << " writing to temporary mail file";
00347             return QString("");
00348         }
00349 
00350         // Notify KMail of the message in the temporary file
00351         org::kde::kmail::kmail kmail(KMAIL_DBUS_SERVICE, KMAIL_DBUS_PATH, QDBusConnection::sessionBus());
00352         QDBusReply<int> reply = kmail.dbusAddMessage(QString::fromLatin1(folder), tmpFile.fileName(), QString());
00353         if (!reply.isValid())
00354             kError() << "D-Bus call failed:" << reply.error().message();
00355         else if (reply.value() <= 0)
00356             kError() << "D-Bus call returned error code =" << reply.value();
00357         else
00358             return QString();    // success
00359         err = i18nc("@info", "Error calling <application>KMail</application>");
00360     }
00361     kError() << folder << ":" << err;
00362     return err;
00363 }
00364 #endif // KMAIL_SUPPORTED
00365 
00366 /******************************************************************************
00367 * Create the headers part of the email.
00368 */
00369 QString initHeaders(const KAMail::JobData& data, bool dateId)
00370 {
00371     QString message;
00372     if (dateId)
00373     {
00374         KDateTime now = KDateTime::currentUtcDateTime();
00375         QString from = data.from;
00376         from.remove(QRegExp("^.*<")).remove(QRegExp(">.*$"));
00377         message = QLatin1String("Date: ");
00378         message += now.toTimeSpec(Preferences::timeZone()).toString(KDateTime::RFCDateDay);
00379         message += QString::fromLatin1("\nMessage-Id: <%1.%2.%3>\n").arg(now.toTime_t()).arg(now.time().msec()).arg(from);
00380     }
00381     message += QLatin1String("From: ") + data.from;
00382     message += QLatin1String("\nTo: ") + data.event.emailAddresses(", ");
00383     if (!data.bcc.isEmpty())
00384         message += QLatin1String("\nBcc: ") + data.bcc;
00385     message += QLatin1String("\nSubject: ") + data.event.emailSubject();
00386     message += QString::fromLatin1("\nX-Mailer: %1/" KALARM_VERSION).arg(KGlobal::mainComponent().aboutData()->programName());
00387     return message;
00388 }
00389 
00390 /******************************************************************************
00391 * Append the body and attachments to the email text.
00392 * Reply = reason for error
00393 *       = 0 if successful.
00394 */
00395 QString KAMail::appendBodyAttachments(QString& message, const KAEvent& event)
00396 {
00397     static const char* textMimeTypes[] = {
00398         "application/x-sh", "application/x-csh", "application/x-shellscript",
00399         "application/x-nawk", "application/x-gawk", "application/x-awk",
00400         "application/x-perl", "application/x-desktop",
00401         0
00402     };
00403     QStringList attachments = event.emailAttachments();
00404     if (!attachments.count())
00405     {
00406         // There are no attachments, so simply append the message body
00407         message += "\n\n";
00408         message += event.message();
00409     }
00410     else
00411     {
00412         // There are attachments, so the message must be in MIME format
00413         // Create a boundary string
00414         time_t timenow;
00415         time(&timenow);
00416         QString boundary;
00417         boundary.sprintf("------------_%lu_-%lx=", 2*timenow, timenow);
00418         message += QLatin1String("\nMIME-Version: 1.0");
00419         message += QString::fromLatin1("\nContent-Type: multipart/mixed;\n  boundary=\"%1\"\n").arg(boundary);
00420 
00421         if (!event.message().isEmpty())
00422         {
00423             // There is a message body
00424             message += QString::fromLatin1("\n--%1\nContent-Type: text/plain\nContent-Transfer-Encoding: 8bit\n\n").arg(boundary);
00425             message += event.message();
00426         }
00427 
00428         // Append each attachment in turn
00429         for (QStringList::Iterator at = attachments.begin();  at != attachments.end();  ++at)
00430         {
00431             QString attachment = (*at).toLocal8Bit();
00432             KUrl url(attachment);
00433             QString attachError = i18nc("@info", "Error attaching file: <filename>%1</filename>", attachment);
00434             url.cleanPath();
00435             KIO::UDSEntry uds;
00436             if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) {
00437                 kError() << "Not found:" << attachment;
00438                 return i18nc("@info", "Attachment not found: <filename>%1</filename>", attachment);
00439             }
00440             KFileItem fi(uds, url);
00441             if (fi.isDir()  ||  !fi.isReadable()) {
00442                 kError() << "Not file/not readable:" << attachment;
00443                 return attachError;
00444             }
00445 
00446             // Check if the attachment is a text file
00447             QString mimeType = fi.mimetype();
00448             bool text = mimeType.startsWith("text/");
00449             if (!text)
00450             {
00451                 for (int i = 0;  !text && textMimeTypes[i];  ++i)
00452                     text = (mimeType == textMimeTypes[i]);
00453             }
00454 
00455             message += QString::fromLatin1("\n--%1").arg(boundary);
00456             message += QString::fromLatin1("\nContent-Type: %2; name=\"%3\"").arg(mimeType).arg(fi.text());
00457             message += QString::fromLatin1("\nContent-Transfer-Encoding: %1").arg(QLatin1String(text ? "8bit" : "BASE64"));
00458             message += QString::fromLatin1("\nContent-Disposition: attachment; filename=\"%4\"\n\n").arg(fi.text());
00459 
00460             // Read the file contents
00461             QString tmpFile;
00462             if (!KIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow())) {
00463                 kError() << "Load failure:" << attachment;
00464                 return attachError;
00465             }
00466             QFile file(tmpFile);
00467             if (!file.open(QIODevice::ReadOnly)) {
00468                 kDebug() << "tmp load error:" << attachment;
00469                 return attachError;
00470             }
00471             qint64 size = file.size();
00472             QByteArray contents = file.readAll();
00473             file.close();
00474             bool atterror = false;
00475             if (contents.size() < size) {
00476                 kDebug() << "Read error:" << attachment;
00477                 atterror = true;
00478             }
00479             else if (text)
00480             {
00481                 // Text attachment doesn't need conversion
00482                 message += QString::fromLatin1(contents);
00483             }
00484             else
00485             {
00486                 // Convert the attachment to BASE64 encoding
00487                 message += QString::fromLatin1(contents.toBase64());
00488             }
00489             if (atterror)
00490                 return attachError;
00491         }
00492         message += QString::fromLatin1("\n--%1--\n.\n").arg(boundary);
00493     }
00494     return QString();
00495 }
00496 
00497 /******************************************************************************
00498 * If any of the destination email addresses are non-local, display a
00499 * notification message saying that an email has been queued for sending.
00500 */
00501 void KAMail::notifyQueued(const KAEvent& event)
00502 {
00503     KMime::Types::Address addr;
00504     QString localhost = QLatin1String("localhost");
00505     QString hostname  = KNetwork::KResolver::localHostName();
00506     const EmailAddressList& addresses = event.emailAddresses();
00507     for (int i = 0, end = addresses.count();  i < end;  ++i)
00508     {
00509         QByteArray email = addresses[i].email().toLocal8Bit();
00510         const char* em = email;
00511         if (!email.isEmpty()
00512         &&  HeaderParsing::parseAddress(em, em + email.length(), addr))
00513         {
00514             QString domain = addr.mailboxList.first().addrSpec().domain;
00515             if (!domain.isEmpty()  &&  domain != localhost  &&  domain != hostname)
00516             {
00517                 KMessageBox::information(0, i18nc("@info", "An email has been queued to be sent"), QString(), Preferences::EMAIL_QUEUED_NOTIFY);
00518                 return;
00519             }
00520         }
00521     }
00522 }
00523 
00524 /******************************************************************************
00525 *  Return whether any email identities exist.
00526 */
00527 bool KAMail::identitiesExist()
00528 {
00529     identityManager();    // create identity manager if not already done
00530     return mIdentityManager->begin() != mIdentityManager->end();
00531 }
00532 
00533 /******************************************************************************
00534 *  Fetch the uoid of an email identity name or uoid string.
00535 */
00536 uint KAMail::identityUoid(const QString& identityUoidOrName)
00537 {
00538     bool ok;
00539     uint id = identityUoidOrName.toUInt(&ok);
00540     if (!ok  ||  identityManager()->identityForUoid(id).isNull())
00541     {
00542         identityManager();   // fetch it if not already done
00543         for (KPIMIdentities::IdentityManager::ConstIterator it = mIdentityManager->begin();
00544              it != mIdentityManager->end();  ++it)
00545         {
00546             if ((*it).identityName() == identityUoidOrName)
00547             {
00548                 id = (*it).uoid();
00549                 break;
00550             }
00551         }
00552     }
00553     return id;
00554 }
00555 
00556 /******************************************************************************
00557 *  Fetch the user's email address configured in the KDE System Settings.
00558 */
00559 QString KAMail::controlCentreAddress()
00560 {
00561     KEMailSettings e;
00562     return e.getSetting(KEMailSettings::EmailAddress);
00563 }
00564 
00565 /******************************************************************************
00566 *  Parse a list of email addresses, optionally containing display names,
00567 *  entered by the user.
00568 *  Reply = the invalid item if error, else empty string.
00569 */
00570 QString KAMail::convertAddresses(const QString& items, EmailAddressList& list)
00571 {
00572     list.clear();
00573     QByteArray addrs = items.toLocal8Bit();
00574     const char* ad = static_cast<const char*>(addrs);
00575 
00576     // parse an address-list
00577     QList<KMime::Types::Address> maybeAddressList;
00578     if (!HeaderParsing::parseAddressList(ad, ad + addrs.length(), maybeAddressList))
00579         return QString::fromLocal8Bit(ad);    // return the address in error
00580 
00581     // extract the mailboxes and complain if there are groups
00582     for (int i = 0, end = maybeAddressList.count();  i < end;  ++i)
00583     {
00584         QString bad = convertAddress(maybeAddressList[i], list);
00585         if (!bad.isEmpty())
00586             return bad;
00587     }
00588     return QString();
00589 }
00590 
00591 #if 0
00592 /******************************************************************************
00593 *  Parse an email address, optionally containing display name, entered by the
00594 *  user, and append it to the specified list.
00595 *  Reply = the invalid item if error, else empty string.
00596 */
00597 QString KAMail::convertAddress(const QString& item, EmailAddressList& list)
00598 {
00599     QByteArray addr = item.toLocal8Bit();
00600     const char* ad = static_cast<const char*>(addr);
00601     KMime::Types::Address maybeAddress;
00602     if (!HeaderParsing::parseAddress(ad, ad + addr.length(), maybeAddress))
00603         return item;     // error
00604     return convertAddress(maybeAddress, list);
00605 }
00606 #endif
00607 
00608 /******************************************************************************
00609 *  Convert a single KMime::Types address to a KCal::Person instance and append
00610 *  it to the specified list.
00611 */
00612 QString KAMail::convertAddress(KMime::Types::Address addr, EmailAddressList& list)
00613 {
00614     if (!addr.displayName.isEmpty())
00615     {
00616         kDebug() << "Mailbox groups not allowed! Name:" << addr.displayName;
00617         return addr.displayName;
00618     }
00619     const QList<KMime::Types::Mailbox>& mblist = addr.mailboxList;
00620     for (int i = 0, end = mblist.count();  i < end;  ++i)
00621     {
00622         QString addrPart = mblist[i].addrSpec().localPart;
00623         if (!mblist[i].addrSpec().domain.isEmpty())
00624         {
00625             addrPart += QLatin1Char('@');
00626             addrPart += mblist[i].addrSpec().domain;
00627         }
00628         list += KCal::Person(mblist[i].name(), addrPart);
00629     }
00630     return QString();
00631 }
00632 
00633 /*
00634 QString KAMail::convertAddresses(const QString& items, QStringList& list)
00635 {
00636     EmailAddressList addrs;
00637     QString item = convertAddresses(items, addrs);
00638     if (!item.isEmpty())
00639         return item;
00640     for (EmailAddressList::Iterator ad = addrs.begin();  ad != addrs.end();  ++ad)
00641     {
00642         item = (*ad).fullName().toLocal8Bit();
00643         switch (checkAddress(item))
00644         {
00645             case 1:      // OK
00646                 list += item;
00647                 break;
00648             case 0:      // null address
00649                 break;
00650             case -1:     // invalid address
00651                 return item;
00652         }
00653     }
00654     return QString();
00655 }*/
00656 
00657 /******************************************************************************
00658 *  Check the validity of an email address.
00659 *  Because internal email addresses don't have to abide by the usual internet
00660 *  email address rules, only some basic checks are made.
00661 *  Reply = 1 if alright, 0 if empty, -1 if error.
00662 */
00663 int KAMail::checkAddress(QString& address)
00664 {
00665     address = address.trimmed();
00666     // Check that there are no list separator characters present
00667     if (address.indexOf(QLatin1Char(',')) >= 0  ||  address.indexOf(QLatin1Char(';')) >= 0)
00668         return -1;
00669     int n = address.length();
00670     if (!n)
00671         return 0;
00672     int start = 0;
00673     int end   = n - 1;
00674     if (address[end] == QLatin1Char('>'))
00675     {
00676         // The email address is in <...>
00677         if ((start = address.indexOf(QLatin1Char('<'))) < 0)
00678             return -1;
00679         ++start;
00680         --end;
00681     }
00682     int i = address.indexOf(QLatin1Char('@'), start);
00683     if (i >= 0)
00684     {
00685         if (i == start  ||  i == end)          // check @ isn't the first or last character
00686 //      ||  address.indexOf(QLatin1Char('@'), i + 1) >= 0)    // check for multiple @ characters
00687             return -1;
00688     }
00689 /*  else
00690     {
00691         // Allow the @ character to be missing if it's a local user
00692         if (!getpwnam(address.mid(start, end - start + 1).toLocal8Bit()))
00693             return false;
00694     }
00695     for (int i = start;  i <= end;  ++i)
00696     {
00697         char ch = address[i].toLatin1();
00698         if (ch == '.'  ||  ch == '@'  ||  ch == '-'  ||  ch == '_'
00699         ||  (ch >= 'A' && ch <= 'Z')  ||  (ch >= 'a' && ch <= 'z')
00700         ||  (ch >= '0' && ch <= '9'))
00701             continue;
00702         return false;
00703     }*/
00704     return 1;
00705 }
00706 
00707 /******************************************************************************
00708 *  Convert a comma or semicolon delimited list of attachments into a
00709 *  QStringList. The items are checked for validity.
00710 *  Reply = the invalid item if error, else empty string.
00711 */
00712 QString KAMail::convertAttachments(const QString& items, QStringList& list)
00713 {
00714     KUrl url;
00715     list.clear();
00716     int length = items.length();
00717     for (int next = 0;  next < length;  )
00718     {
00719         // Find the first delimiter character (, or ;)
00720         int i = items.indexOf(QLatin1Char(','), next);
00721         if (i < 0)
00722             i = items.length();
00723         int sc = items.indexOf(QLatin1Char(';'), next);
00724         if (sc < 0)
00725             sc = items.length();
00726         if (sc < i)
00727             i = sc;
00728         QString item = items.mid(next, i - next).trimmed();
00729         switch (checkAttachment(item))
00730         {
00731             case 1:   list += item;  break;
00732             case 0:   break;          // empty attachment name
00733             case -1:
00734             default:  return item;    // error
00735         }
00736         next = i + 1;
00737     }
00738     return QString();
00739 }
00740 
00741 #if 0
00742 /******************************************************************************
00743 *  Convert a comma or semicolon delimited list of attachments into a
00744 *  KUrl::List. The items are checked for validity.
00745 *  Reply = the invalid item if error, else empty string.
00746 */
00747 QString KAMail::convertAttachments(const QString& items, KUrl::List& list)
00748 {
00749     KUrl url;
00750     list.clear();
00751     QByteArray addrs = items.toLocal8Bit();
00752     int length = items.length();
00753     for (int next = 0;  next < length;  )
00754     {
00755         // Find the first delimiter character (, or ;)
00756         int i = items.indexOf(QLatin1Char(','), next);
00757         if (i < 0)
00758             i = items.length();
00759         int sc = items.indexOf(QLatin1Char(';'), next);
00760         if (sc < 0)
00761             sc = items.length();
00762         if (sc < i)
00763             i = sc;
00764         QString item = items.mid(next, i - next);
00765         switch (checkAttachment(item, &url))
00766         {
00767             case 1:   list += url;  break;
00768             case 0:   break;          // empty attachment name
00769             case -1:
00770             default:  return item;    // error
00771         }
00772         next = i + 1;
00773     }
00774     return QString();
00775 }
00776 #endif
00777 
00778 /******************************************************************************
00779 *  Check for the existence of the attachment file.
00780 *  If non-null, '*url' receives the KUrl of the attachment.
00781 *  Reply = 1 if attachment exists
00782 *        = 0 if null name
00783 *        = -1 if doesn't exist.
00784 */
00785 int KAMail::checkAttachment(QString& attachment, KUrl* url)
00786 {
00787     attachment = attachment.trimmed();
00788     if (attachment.isEmpty())
00789     {
00790         if (url)
00791             *url = KUrl();
00792         return 0;
00793     }
00794     // Check that the file exists
00795     KUrl u(attachment);
00796     u.cleanPath();
00797     if (url)
00798         *url = u;
00799     return checkAttachment(u) ? 1 : -1;
00800 }
00801 
00802 /******************************************************************************
00803 *  Check for the existence of the attachment file.
00804 */
00805 bool KAMail::checkAttachment(const KUrl& url)
00806 {
00807     KIO::UDSEntry uds;
00808     if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
00809         return false;       // doesn't exist
00810     KFileItem fi(uds, url);
00811     if (fi.isDir()  ||  !fi.isReadable())
00812         return false;
00813     return true;
00814 }
00815 
00816 /******************************************************************************
00817 * Set the appropriate error messages for a given error string.
00818 */
00819 QStringList KAMail::errors(const QString& err, ErrType prefix)
00820 {
00821     QString error1;
00822     switch (prefix)
00823     {
00824         case SEND_FAIL:  error1 = i18nc("@info", "Failed to send email");  break;
00825         case SEND_ERROR:  error1 = i18nc("@info", "Error sending email");  break;
00826         case COPY_ERROR:  error1 = i18nc("@info", "Error copying sent email to <application>KMail</application> <resource>%1</resource> folder", i18n_sent_mail());  break;
00827     }
00828     if (err.isEmpty())
00829         return QStringList(error1);
00830     QStringList errs(QString::fromLatin1("%1:").arg(error1));
00831     errs += err;
00832     return errs;
00833 }
00834 
00835 #ifdef KMAIL_SUPPORTED
00836 /******************************************************************************
00837 *  Get the body of an email from KMail, given its serial number.
00838 */
00839 QString KAMail::getMailBody(quint32 serialNumber)
00840 {
00841     QList<QVariant> args;
00842     args << serialNumber << (int)0;
00843 #ifdef __GNUC__
00844 #warning Set correct DBus interface/object for kmail
00845 #endif
00846     QDBusInterface iface(KMAIL_DBUS_SERVICE, QString(), QLatin1String("KMailIface"));
00847     QDBusReply<QString> reply = iface.callWithArgumentList(QDBus::Block, QLatin1String("getDecodedBodyPart"), args);
00848     if (!reply.isValid())
00849     {
00850         kError() << "D-Bus call failed:" << reply.error().message();
00851         return QString();
00852     }
00853     return reply.value();
00854 }
00855 #endif
00856 
00857 /*=============================================================================
00858 =  HeaderParsing :  modified and additional functions.
00859 =  The following functions are modified from, or additional to, those in
00860 =  libkdenetwork kmime_header_parsing.cpp.
00861 =============================================================================*/
00862 
00863 namespace HeaderParsing
00864 {
00865 
00866 using namespace KMime;
00867 using namespace KMime::Types;
00868 using namespace KMime::HeaderParsing;
00869 
00870 /******************************************************************************
00871 *  New function.
00872 *  Allow a local user name to be specified as an email address.
00873 */
00874 bool parseUserName( const char* & scursor, const char * const send,
00875                     QString & result, bool isCRLF ) {
00876 
00877   QString maybeLocalPart;
00878   QString tmp;
00879 
00880   if ( scursor != send ) {
00881     // first, eat any whitespace
00882     eatCFWS( scursor, send, isCRLF );
00883 
00884     char ch = *scursor++;
00885     switch ( ch ) {
00886     case '.': // dot
00887     case '@':
00888     case '"': // quoted-string
00889       return false;
00890 
00891     default: // atom
00892       scursor--; // re-set scursor to point to ch again
00893       tmp.clear();
00894       if ( parseAtom( scursor, send, result, false /* no 8bit */ ) ) {
00895         if (getpwnam(result.toLocal8Bit()))
00896           return true;
00897       }
00898       return false; // parseAtom can only fail if the first char is non-atext.
00899     }
00900   }
00901   return false;
00902 }
00903 
00904 /******************************************************************************
00905 *  Modified function.
00906 *  Allow a local user name to be specified as an email address, and reinstate
00907 *  the original scursor on error return.
00908 */
00909 bool parseAddress( const char* & scursor, const char * const send,
00910            Address & result, bool isCRLF ) {
00911   // address       := mailbox / group
00912 
00913   eatCFWS( scursor, send, isCRLF );
00914   if ( scursor == send ) return false;
00915 
00916   // first try if it's a single mailbox:
00917   Mailbox maybeMailbox;
00918   const char * oldscursor = scursor;
00919   if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
00920     // yes, it is:
00921     result.displayName.clear();
00922     result.mailboxList.append( maybeMailbox );
00923     return true;
00924   }
00925   scursor = oldscursor;
00926 
00927   // KAlarm: Allow a local user name to be specified
00928   // no, it's not a single mailbox. Try if it's a local user name:
00929   QString maybeUserName;
00930   if ( parseUserName( scursor, send, maybeUserName, isCRLF ) ) {
00931     // yes, it is:
00932     maybeMailbox.setName( QString() );
00933     AddrSpec addrSpec;
00934     addrSpec.localPart = maybeUserName;
00935     addrSpec.domain.clear();
00936     maybeMailbox.setAddress( addrSpec );
00937     result.displayName.clear();
00938     result.mailboxList.append( maybeMailbox );
00939     return true;
00940   }
00941   scursor = oldscursor;
00942 
00943   Address maybeAddress;
00944 
00945   // no, it's not a single mailbox. Try if it's a group:
00946   if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) )
00947   {
00948     scursor = oldscursor;   // KAlarm: reinstate original scursor on error return
00949     return false;
00950   }
00951 
00952   result = maybeAddress;
00953   return true;
00954 }
00955 
00956 /******************************************************************************
00957 *  Modified function.
00958 *  Allow either ',' or ';' to be used as an email address separator.
00959 */
00960 bool parseAddressList( const char* & scursor, const char * const send,
00961                QList<Address> & result, bool isCRLF ) {
00962   while ( scursor != send ) {
00963     eatCFWS( scursor, send, isCRLF );
00964     // end of header: this is OK.
00965     if ( scursor == send ) return true;
00966     // empty entry: ignore:
00967     if ( *scursor == ',' || *scursor == ';' ) { scursor++; continue; }   // KAlarm: allow ';' as address separator
00968 
00969     // parse one entry
00970     Address maybeAddress;
00971     if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) ) return false;
00972     result.append( maybeAddress );
00973 
00974     eatCFWS( scursor, send, isCRLF );
00975     // end of header: this is OK.
00976     if ( scursor == send ) return true;
00977     // comma separating entries: eat it.
00978     if ( *scursor == ',' || *scursor == ';' ) scursor++;   // KAlarm: allow ';' as address separator
00979   }
00980   return true;
00981 }
00982 
00983 } // namespace HeaderParsing

kalarm

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