/* * kamail.cpp - email functions * Program: kalarm * Copyright © 2002-2005,2008 by David Jarvie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kalarm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alarmevent.h" #include "functions.h" #include "kalarmapp.h" #include "mainwindow.h" #include "preferences.h" #include "kamail.h" namespace HeaderParsing { bool parseAddress( const char* & scursor, const char * const send, KMime::Types::Address & result, bool isCRLF=false ); bool parseAddressList( const char* & scursor, const char * const send, TQValueList & result, bool isCRLF=false ); } namespace { TQString getHostName(); } struct KAMailData { KAMailData(const KAEvent& e, const TQString& fr, const TQString& bc, bool allownotify) : event(e), from(fr), bcc(bc), allowNotify(allownotify) { } const KAEvent& event; TQString from; TQString bcc; bool allowNotify; }; TQString KAMail::i18n_NeedFromEmailAddress() { return i18n("A 'From' email address must be configured in order to execute email alarms."); } TQString KAMail::i18n_sent_mail() { return i18n("KMail folder name: this should be translated the same as in kmail", "sent-mail"); } KPIM::IdentityManager* KAMail::mIdentityManager = 0; KPIM::IdentityManager* KAMail::identityManager() { if (!mIdentityManager) mIdentityManager = new KPIM::IdentityManager(true); // create a read-only kmail identity manager return mIdentityManager; } /****************************************************************************** * Send the email message specified in an event. * Reply = true if the message was sent - 'errmsgs' may contain copy error messages. * = false if the message was not sent - 'errmsgs' contains the error messages. */ bool KAMail::send(const KAEvent& event, TQStringList& errmsgs, bool allowNotify) { TQString err; TQString from; KPIM::Identity identity; if (!event.emailFromId()) from = Preferences::emailAddress(); else { identity = mIdentityManager->identityForUoid(event.emailFromId()); if (identity.isNull()) { kdError(5950) << "KAMail::send(): identity" << event.emailFromId() << "not found" << endl; errmsgs = errors(i18n("Invalid 'From' email address.\nKMail identity '%1' not found.").arg(event.emailFromId())); return false; } from = identity.fullEmailAddr(); if (from.isEmpty()) { kdError(5950) << "KAMail::send(): identity" << identity.identityName() << "uoid" << identity.uoid() << ": no email address" << endl; errmsgs = errors(i18n("Invalid 'From' email address.\nEmail identity '%1' has no email address").arg(identity.identityName())); return false; } } if (from.isEmpty()) { switch (Preferences::emailFrom()) { case Preferences::MAIL_FROM_KMAIL: errmsgs = errors(i18n("No 'From' email address is configured (no default KMail identity found)\nPlease set it in KMail or in the KAlarm Preferences dialog.")); break; case Preferences::MAIL_FROM_CONTROL_CENTRE: errmsgs = errors(i18n("No 'From' email address is configured.\nPlease set it in the TDE Control Center or in the KAlarm Preferences dialog.")); break; case Preferences::MAIL_FROM_ADDR: default: errmsgs = errors(i18n("No 'From' email address is configured.\nPlease set it in the KAlarm Preferences dialog.")); break; } return false; } KAMailData data(event, from, (event.emailBcc() ? Preferences::emailBccAddress() : TQString()), allowNotify); kdDebug(5950) << "KAlarmApp::sendEmail(): To: " << event.emailAddresses(", ") << "\nSubject: " << event.emailSubject() << endl; if (Preferences::emailClient() == Preferences::SENDMAIL) { // Use sendmail to send the message TQString textComplete; TQString command = TDEStandardDirs::findExe(TQString::fromLatin1("sendmail"), TQString::fromLatin1("/sbin:/usr/sbin:/usr/lib")); if (!command.isNull()) { command += TQString::fromLatin1(" -f "); command += KPIM::getEmailAddress(from); command += TQString::fromLatin1(" -oi -t "); textComplete = initHeaders(data, false); } else { command = TDEStandardDirs::findExe(TQString::fromLatin1("mail")); if (command.isNull()) { errmsgs = errors(i18n("%1 not found").arg(TQString::fromLatin1("sendmail"))); // give up return false; } command += TQString::fromLatin1(" -s "); command += KShellProcess::quote(event.emailSubject()); if (!data.bcc.isEmpty()) { command += TQString::fromLatin1(" -b "); command += KShellProcess::quote(data.bcc); } command += ' '; command += event.emailAddresses(" "); // locally provided, okay } // Add the body and attachments to the message. // (Sendmail requires attachments to have already been included in the message.) err = appendBodyAttachments(textComplete, event); if (!err.isNull()) { errmsgs = errors(err); return false; } // Execute the send command FILE* fd = popen(command.local8Bit(), "w"); if (!fd) { kdError(5950) << "KAMail::send(): Unable to open a pipe to " << command << endl; errmsgs = errors(); return false; } fwrite(textComplete.local8Bit(), textComplete.length(), 1, fd); pclose(fd); if (Preferences::emailCopyToKMail()) { // Create a copy of the sent email in KMail's 'Sent-mail' folder err = addToKMailFolder(data, "sent-mail", true); if (!err.isNull()) errmsgs = errors(err, false); // not a fatal error - continue } if (allowNotify) notifyQueued(event); } else { // Use KMail to send the message err = sendKMail(data); if (!err.isNull()) { errmsgs = errors(err); return false; } } return true; } /****************************************************************************** * Send the email message via KMail. * Reply = reason for failure (which may be the empty string) * = null string if success. */ TQString KAMail::sendKMail(const KAMailData& data) { TQString err = KAlarm::runKMail(true); if (!err.isNull()) return err; // KMail is now running. Determine which DCOP call to use. bool useSend = false; TQCString sendFunction = "sendMessage(TQString,TQString,TQString,TQString,TQString,TQString,KURL::List)"; QCStringList funcs = kapp->dcopClient()->remoteFunctions("kmail", "MailTransportServiceIface"); for (QCStringList::Iterator it=funcs.begin(); it != funcs.end() && !useSend; ++it) { TQCString func = DCOPClient::normalizeFunctionSignature(*it); if (func.left(5) == "bool ") { func = func.mid(5); func.replace(TQRegExp(" [0-9A-Za-z_:]+"), ""); useSend = (func == sendFunction); } } TQByteArray callData; TQDataStream arg(callData, IO_WriteOnly); kdDebug(5950) << "KAMail::sendKMail(): using " << (useSend ? "sendMessage()" : "dcopAddMessage()") << endl; if (useSend) { // This version of KMail has the sendMessage() function, // which transmits the message immediately. arg << data.from; arg << data.event.emailAddresses(", "); arg << ""; // CC: arg << data.bcc; arg << data.event.emailSubject(); arg << data.event.message(); arg << KURL::List(data.event.emailAttachments()); if (!callKMail(callData, "MailTransportServiceIface", sendFunction, "bool")) return i18n("Error calling KMail"); } else { // KMail is an older version, so use dcopAddMessage() // to add the message to the outbox for later transmission. err = addToKMailFolder(data, "outbox", false); if (!err.isNull()) return err; } if (data.allowNotify) notifyQueued(data.event); return TQString(); } /****************************************************************************** * Add the message to a KMail folder. * Reply = reason for failure (which may be the empty string) * = null string if success. */ TQString KAMail::addToKMailFolder(const KAMailData& data, const char* folder, bool checkKmailRunning) { TQString err; if (checkKmailRunning) err = KAlarm::runKMail(true); if (err.isNull()) { TQString message = initHeaders(data, true); err = appendBodyAttachments(message, data.event); if (!err.isNull()) return err; // Write to a temporary file for feeding to KMail KTempFile tmpFile; tmpFile.setAutoDelete(true); // delete file when it is destructed TQTextStream* stream = tmpFile.textStream(); if (!stream) { kdError(5950) << "KAMail::addToKMailFolder(" << folder << "): Unable to open a temporary mail file" << endl; return TQString(""); } *stream << message; tmpFile.close(); if (tmpFile.status()) { kdError(5950) << "KAMail::addToKMailFolder(" << folder << "): Error " << tmpFile.status() << " writing to temporary mail file" << endl; return TQString(""); } // Notify KMail of the message in the temporary file TQByteArray callData; TQDataStream arg(callData, IO_WriteOnly); arg << TQString::fromLatin1(folder) << tmpFile.name(); if (callKMail(callData, "KMailIface", "dcopAddMessage(TQString,TQString)", "int")) return TQString(); err = i18n("Error calling KMail"); } kdError(5950) << "KAMail::addToKMailFolder(" << folder << "): " << err << endl; return err; } /****************************************************************************** * Call KMail via DCOP. The DCOP function must return an 'int'. */ bool KAMail::callKMail(const TQByteArray& callData, const TQCString& iface, const TQCString& function, const TQCString& funcType) { TQCString replyType; TQByteArray replyData; if (!kapp->dcopClient()->call("kmail", iface, function, callData, replyType, replyData) || replyType != funcType) { TQCString funcname = function; funcname.replace(TQRegExp("(.+$"), "()"); kdError(5950) << "KAMail::callKMail(): kmail " << funcname << " call failed\n";; return false; } TQDataStream replyStream(replyData, IO_ReadOnly); TQCString funcname = function; funcname.replace(TQRegExp("(.+$"), "()"); if (replyType == "int") { int result; replyStream >> result; if (result <= 0) { kdError(5950) << "KAMail::callKMail(): kmail " << funcname << " call returned error code = " << result << endl; return false; } } else if (replyType == "bool") { bool result; replyStream >> result; if (!result) { kdError(5950) << "KAMail::callKMail(): kmail " << funcname << " call returned error\n"; return false; } } return true; } /****************************************************************************** * Create the headers part of the email. */ TQString KAMail::initHeaders(const KAMailData& data, bool dateId) { TQString message; if (dateId) { struct timeval tod; gettimeofday(&tod, 0); time_t timenow = tod.tv_sec; char buff[64]; strftime(buff, sizeof(buff), "Date: %a, %d %b %Y %H:%M:%S %z", localtime(&timenow)); TQString from = data.from; from.replace(TQRegExp("^.*<"), TQString()).replace(TQRegExp(">.*$"), TQString()); message = TQString::fromLatin1(buff); message += TQString::fromLatin1("\nMessage-Id: <%1.%2.%3>\n").arg(timenow).arg(tod.tv_usec).arg(from); } message += TQString::fromLatin1("From: ") + data.from; message += TQString::fromLatin1("\nTo: ") + data.event.emailAddresses(", "); if (!data.bcc.isEmpty()) message += TQString::fromLatin1("\nBcc: ") + data.bcc; message += TQString::fromLatin1("\nSubject: ") + data.event.emailSubject(); message += TQString::fromLatin1("\nX-Mailer: %1/" KALARM_VERSION).arg(kapp->aboutData()->programName()); return message; } /****************************************************************************** * Append the body and attachments to the email text. * Reply = reason for error * = 0 if successful. */ TQString KAMail::appendBodyAttachments(TQString& message, const KAEvent& event) { static const char* textMimeTypes[] = { "application/x-sh", "application/x-csh", "application/x-shellscript", "application/x-nawk", "application/x-gawk", "application/x-awk", "application/x-perl", "application/x-desktop", 0 }; TQStringList attachments = event.emailAttachments(); if (!attachments.count()) { // There are no attachments, so simply append the message body message += "\n\n"; message += event.message(); } else { // There are attachments, so the message must be in MIME format // Create a boundary string time_t timenow; time(&timenow); TQCString boundary; boundary.sprintf("------------_%lu_-%lx=", 2*timenow, timenow); message += TQString::fromLatin1("\nMIME-Version: 1.0"); message += TQString::fromLatin1("\nContent-Type: multipart/mixed;\n boundary=\"%1\"\n").arg(boundary.data()); if (!event.message().isEmpty()) { // There is a message body message += TQString::fromLatin1("\n--%1\nContent-Type: text/plain\nContent-Transfer-Encoding: 8bit\n\n").arg(boundary.data()); message += event.message(); } // Append each attachment in turn TQString attachError = i18n("Error attaching file:\n%1"); for (TQStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at) { TQString attachment = (*at).local8Bit(); KURL url(attachment); url.cleanPath(); TDEIO::UDSEntry uds; if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) { kdError(5950) << "KAMail::appendBodyAttachments(): not found: " << attachment << endl; return i18n("Attachment not found:\n%1").arg(attachment); } KFileItem fi(uds, url); if (fi.isDir() || !fi.isReadable()) { kdError(5950) << "KAMail::appendBodyAttachments(): not file/not readable: " << attachment << endl; return attachError.arg(attachment); } // Check if the attachment is a text file TQString mimeType = fi.mimetype(); bool text = mimeType.startsWith("text/"); if (!text) { for (int i = 0; !text && textMimeTypes[i]; ++i) text = (mimeType == textMimeTypes[i]); } message += TQString::fromLatin1("\n--%1").arg(boundary.data()); message += TQString::fromLatin1("\nContent-Type: %2; name=\"%3\"").arg(mimeType).arg(fi.text()); message += TQString::fromLatin1("\nContent-Transfer-Encoding: %1").arg(TQString::fromLatin1(text ? "8bit" : "BASE64")); message += TQString::fromLatin1("\nContent-Disposition: attachment; filename=\"%4\"\n\n").arg(fi.text()); // Read the file contents TQString tmpFile; if (!TDEIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow())) { kdError(5950) << "KAMail::appendBodyAttachments(): load failure: " << attachment << endl; return attachError.arg(attachment); } TQFile file(tmpFile); if (!file.open(IO_ReadOnly) ) { kdDebug(5950) << "KAMail::appendBodyAttachments() tmp load error: " << attachment << endl; return attachError.arg(attachment); } TQIODevice::Offset size = file.size(); char* contents = new char [size + 1]; TQ_LONG bytes = file.readBlock(contents, size); file.close(); contents[size] = 0; bool atterror = false; if (bytes == -1 || (TQIODevice::Offset)bytes < size) { kdDebug(5950) << "KAMail::appendBodyAttachments() read error: " << attachment << endl; atterror = true; } else if (text) { // Text attachment doesn't need conversion message += contents; } else { // Convert the attachment to BASE64 encoding TQIODevice::Offset base64Size; char* base64 = base64Encode(contents, size, base64Size); if (base64Size == (TQIODevice::Offset)-1) { kdDebug(5950) << "KAMail::appendBodyAttachments() base64 buffer overflow: " << attachment << endl; atterror = true; } else message += TQString::fromLatin1(base64, base64Size); delete[] base64; } delete[] contents; if (atterror) return attachError.arg(attachment); } message += TQString::fromLatin1("\n--%1--\n.\n").arg(boundary.data()); } return TQString(); } /****************************************************************************** * If any of the destination email addresses are non-local, display a * notification message saying that an email has been queued for sending. */ void KAMail::notifyQueued(const KAEvent& event) { KMime::Types::Address addr; TQString localhost = TQString::fromLatin1("localhost"); TQString hostname = getHostName(); const EmailAddressList& addresses = event.emailAddresses(); for (TQValueList::ConstIterator it = addresses.begin(); it != addresses.end(); ++it) { TQCString email = (*it).email().local8Bit(); const char* em = email; if (!email.isEmpty() && HeaderParsing::parseAddress(em, em + email.length(), addr)) { TQString domain = addr.mailboxList.first().addrSpec.domain; if (!domain.isEmpty() && domain != localhost && domain != hostname) { TQString text = (Preferences::emailClient() == Preferences::KMAIL) ? i18n("An email has been queued to be sent by KMail") : i18n("An email has been queued to be sent"); KMessageBox::information(0, text, TQString(), Preferences::EMAIL_QUEUED_NOTIFY); return; } } } } /****************************************************************************** * Return whether any KMail identities exist. */ bool KAMail::identitiesExist() { identityManager(); // create identity manager if not already done return mIdentityManager->begin() != mIdentityManager->end(); } /****************************************************************************** * Fetch the uoid of an email identity name or uoid string. */ uint KAMail::identityUoid(const TQString& identityUoidOrName) { bool ok; uint id = identityUoidOrName.toUInt(&ok); if (!ok || identityManager()->identityForUoid(id).isNull()) { identityManager(); // fetch it if not already done for (KPIM::IdentityManager::ConstIterator it = mIdentityManager->begin(); it != mIdentityManager->end(); ++it) { if ((*it).identityName() == identityUoidOrName) { id = (*it).uoid(); break; } } } return id; } /****************************************************************************** * Fetch the user's email address configured in the TDE Control Centre. */ TQString KAMail::controlCentreAddress() { KEMailSettings e; return e.getSetting(KEMailSettings::EmailAddress); } /****************************************************************************** * Parse a list of email addresses, optionally containing display names, * entered by the user. * Reply = the invalid item if error, else empty string. */ TQString KAMail::convertAddresses(const TQString& items, EmailAddressList& list) { list.clear(); TQCString addrs = items.local8Bit(); const char* ad = static_cast(addrs); // parse an address-list TQValueList maybeAddressList; if (!HeaderParsing::parseAddressList(ad, ad + addrs.length(), maybeAddressList)) return TQString::fromLocal8Bit(ad); // return the address in error // extract the mailboxes and complain if there are groups for (TQValueList::ConstIterator it = maybeAddressList.begin(); it != maybeAddressList.end(); ++it) { TQString bad = convertAddress(*it, list); if (!bad.isEmpty()) return bad; } return TQString(); } #if 0 /****************************************************************************** * Parse an email address, optionally containing display name, entered by the * user, and append it to the specified list. * Reply = the invalid item if error, else empty string. */ TQString KAMail::convertAddress(const TQString& item, EmailAddressList& list) { TQCString addr = item.local8Bit(); const char* ad = static_cast(addr); KMime::Types::Address maybeAddress; if (!HeaderParsing::parseAddress(ad, ad + addr.length(), maybeAddress)) return item; // error return convertAddress(maybeAddress, list); } #endif /****************************************************************************** * Convert a single KMime::Types address to a KCal::Person instance and append * it to the specified list. */ TQString KAMail::convertAddress(KMime::Types::Address addr, EmailAddressList& list) { if (!addr.displayName.isEmpty()) { kdDebug(5950) << "mailbox groups not allowed! Name: \"" << addr.displayName << "\"" << endl; return addr.displayName; } const TQValueList& mblist = addr.mailboxList; for (TQValueList::ConstIterator mb = mblist.begin(); mb != mblist.end(); ++mb) { TQString addrPart = (*mb).addrSpec.localPart; if (!(*mb).addrSpec.domain.isEmpty()) { addrPart += TQChar('@'); addrPart += (*mb).addrSpec.domain; } list += KCal::Person((*mb).displayName, addrPart); } return TQString(); } /* TQString KAMail::convertAddresses(const TQString& items, TQStringList& list) { EmailAddressList addrs; TQString item = convertAddresses(items, addrs); if (!item.isEmpty()) return item; for (EmailAddressList::Iterator ad = addrs.begin(); ad != addrs.end(); ++ad) { item = (*ad).fullName().local8Bit(); switch (checkAddress(item)) { case 1: // OK list += item; break; case 0: // null address break; case -1: // invalid address return item; } } return TQString(); }*/ /****************************************************************************** * Check the validity of an email address. * Because internal email addresses don't have to abide by the usual internet * email address rules, only some basic checks are made. * Reply = 1 if alright, 0 if empty, -1 if error. */ int KAMail::checkAddress(TQString& address) { address = address.stripWhiteSpace(); // Check that there are no list separator characters present if (address.find(',') >= 0 || address.find(';') >= 0) return -1; int n = address.length(); if (!n) return 0; int start = 0; int end = n - 1; if (address[end] == '>') { // The email address is in <...> if ((start = address.find('<')) < 0) return -1; ++start; --end; } int i = address.find('@', start); if (i >= 0) { if (i == start || i == end) // check @ isn't the first or last character // || address.find('@', i + 1) >= 0) // check for multiple @ characters return -1; } /* else { // Allow the @ character to be missing if it's a local user if (!getpwnam(address.mid(start, end - start + 1).local8Bit())) return false; } for (int i = start; i <= end; ++i) { char ch = address[i].latin1(); if (ch == '.' || ch == '@' || ch == '-' || ch == '_' || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) continue; return false; }*/ return 1; } /****************************************************************************** * Convert a comma or semicolon delimited list of attachments into a * TQStringList. The items are checked for validity. * Reply = the invalid item if error, else empty string. */ TQString KAMail::convertAttachments(const TQString& items, TQStringList& list) { KURL url; list.clear(); int length = items.length(); for (int next = 0; next < length; ) { // Find the first delimiter character (, or ;) int i = items.find(',', next); if (i < 0) i = items.length(); int sc = items.find(';', next); if (sc < 0) sc = items.length(); if (sc < i) i = sc; TQString item = items.mid(next, i - next).stripWhiteSpace(); switch (checkAttachment(item)) { case 1: list += item; break; case 0: break; // empty attachment name case -1: default: return item; // error } next = i + 1; } return TQString(); } #if 0 /****************************************************************************** * Convert a comma or semicolon delimited list of attachments into a * KURL::List. The items are checked for validity. * Reply = the invalid item if error, else empty string. */ TQString KAMail::convertAttachments(const TQString& items, KURL::List& list) { KURL url; list.clear(); TQCString addrs = items.local8Bit(); int length = items.length(); for (int next = 0; next < length; ) { // Find the first delimiter character (, or ;) int i = items.find(',', next); if (i < 0) i = items.length(); int sc = items.find(';', next); if (sc < 0) sc = items.length(); if (sc < i) i = sc; TQString item = items.mid(next, i - next); switch (checkAttachment(item, &url)) { case 1: list += url; break; case 0: break; // empty attachment name case -1: default: return item; // error } next = i + 1; } return TQString(); } #endif /****************************************************************************** * Check for the existence of the attachment file. * If non-null, '*url' receives the KURL of the attachment. * Reply = 1 if attachment exists * = 0 if null name * = -1 if doesn't exist. */ int KAMail::checkAttachment(TQString& attachment, KURL* url) { attachment = attachment.stripWhiteSpace(); if (attachment.isEmpty()) { if (url) *url = KURL(); return 0; } // Check that the file exists KURL u = KURL::fromPathOrURL(attachment); u.cleanPath(); if (url) *url = u; return checkAttachment(u) ? 1 : -1; } /****************************************************************************** * Check for the existence of the attachment file. */ bool KAMail::checkAttachment(const KURL& url) { TDEIO::UDSEntry uds; if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) return false; // doesn't exist KFileItem fi(uds, url); if (fi.isDir() || !fi.isReadable()) return false; return true; } /****************************************************************************** * Convert a block of memory to Base64 encoding. * 'outSize' is set to the number of bytes used in the returned block, or to * -1 if overflow. * Reply = BASE64 buffer, which the caller must delete[] afterwards. */ char* KAMail::base64Encode(const char* in, TQIODevice::Offset size, TQIODevice::Offset& outSize) { const int MAX_LINELEN = 72; static unsigned char dtable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; char* out = new char [2*size + 5]; outSize = (TQIODevice::Offset)-1; TQIODevice::Offset outIndex = 0; int lineLength = 0; for (TQIODevice::Offset inIndex = 0; inIndex < size; ) { unsigned char igroup[3]; int n; for (n = 0; n < 3; ++n) { if (inIndex < size) igroup[n] = (unsigned char)in[inIndex++]; else { igroup[n] = igroup[2] = 0; break; } } if (n > 0) { unsigned char ogroup[4]; ogroup[0] = dtable[igroup[0] >> 2]; ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; ogroup[3] = dtable[igroup[2] & 0x3F]; if (n < 3) { ogroup[3] = '='; if (n < 2) ogroup[2] = '='; } if (outIndex >= size*2) { delete[] out; return 0; } for (int i = 0; i < 4; ++i) { if (lineLength >= MAX_LINELEN) { out[outIndex++] = '\r'; out[outIndex++] = '\n'; lineLength = 0; } out[outIndex++] = ogroup[i]; ++lineLength; } } } if (outIndex + 2 < size*2) { out[outIndex++] = '\r'; out[outIndex++] = '\n'; } outSize = outIndex; return out; } /****************************************************************************** * Set the appropriate error messages for a given error string. */ TQStringList KAMail::errors(const TQString& err, bool sendfail) { TQString error1 = sendfail ? i18n("Failed to send email") : i18n("Error copying sent email to KMail %1 folder").arg(i18n_sent_mail()); if (err.isEmpty()) return TQStringList(error1); TQStringList errs(TQString::fromLatin1("%1:").arg(error1)); errs += err; return errs; } /****************************************************************************** * Get the body of an email, given its serial number. */ TQString KAMail::getMailBody(TQ_UINT32 serialNumber) { // Get the body of the email from KMail TQCString replyType; TQByteArray replyData; TQByteArray data; TQDataStream arg(data, IO_WriteOnly); arg << serialNumber; arg << (int)0; TQString body; if (kapp->dcopClient()->call("kmail", "KMailIface", "getDecodedBodyPart(TQ_UINT32,int)", data, replyType, replyData) && replyType == TQSTRING_OBJECT_NAME_STRING) { TQDataStream reply_stream(replyData, IO_ReadOnly); reply_stream >> body; } else kdDebug(5950) << "KAMail::getMailBody(): kmail getDecodedBodyPart() call failed\n"; return body; } namespace { /****************************************************************************** * Get the local system's host name. */ TQString getHostName() { char hname[256]; if (gethostname(hname, sizeof(hname))) return TQString(); return TQString::fromLocal8Bit(hname); } } /*============================================================================= = HeaderParsing : modified and additional functions. = The following functions are modified from, or additional to, those in = libtdenetwork kmime_header_parsing.cpp. =============================================================================*/ namespace HeaderParsing { using namespace KMime; using namespace KMime::Types; using namespace KMime::HeaderParsing; /****************************************************************************** * New function. * Allow a local user name to be specified as an email address. */ bool parseUserName( const char* & scursor, const char * const send, TQString & result, bool isCRLF ) { TQString maybeLocalPart; TQString tmp; if ( scursor != send ) { // first, eat any whitespace eatCFWS( scursor, send, isCRLF ); char ch = *scursor++; switch ( ch ) { case '.': // dot case '@': case '"': // quoted-string return false; default: // atom scursor--; // re-set scursor to point to ch again tmp = TQString(); if ( parseAtom( scursor, send, result, false /* no 8bit */ ) ) { if (getpwnam(result.local8Bit())) return true; } return false; // parseAtom can only fail if the first char is non-atext. } } return false; } /****************************************************************************** * Modified function. * Allow a local user name to be specified as an email address, and reinstate * the original scursor on error return. */ bool parseAddress( const char* & scursor, const char * const send, Address & result, bool isCRLF ) { // address := mailbox / group eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // first try if it's a single mailbox: Mailbox maybeMailbox; const char * oldscursor = scursor; if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) { // yes, it is: result.displayName = TQString(); result.mailboxList.append( maybeMailbox ); return true; } scursor = oldscursor; // KAlarm: Allow a local user name to be specified // no, it's not a single mailbox. Try if it's a local user name: TQString maybeUserName; if ( parseUserName( scursor, send, maybeUserName, isCRLF ) ) { // yes, it is: maybeMailbox.displayName = TQString(); maybeMailbox.addrSpec.localPart = maybeUserName; maybeMailbox.addrSpec.domain = TQString(); result.displayName = TQString(); result.mailboxList.append( maybeMailbox ); return true; } scursor = oldscursor; Address maybeAddress; // no, it's not a single mailbox. Try if it's a group: if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) ) { scursor = oldscursor; // KAlarm: reinstate original scursor on error return return false; } result = maybeAddress; return true; } /****************************************************************************** * Modified function. * Allow either ',' or ';' to be used as an email address separator. */ bool parseAddressList( const char* & scursor, const char * const send, TQValueList
& result, bool isCRLF ) { while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // end of header: this is OK. if ( scursor == send ) return true; // empty entry: ignore: if ( *scursor == ',' || *scursor == ';' ) { scursor++; continue; } // KAlarm: allow ';' as address separator // parse one entry Address maybeAddress; if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) ) return false; result.append( maybeAddress ); eatCFWS( scursor, send, isCRLF ); // end of header: this is OK. if ( scursor == send ) return true; // comma separating entries: eat it. if ( *scursor == ',' || *scursor == ';' ) scursor++; // KAlarm: allow ';' as address separator } return true; } } // namespace HeaderParsing