33 #include <kstandarddirs.h>
34 #include <dcopclient.h>
36 #include <tdemessagebox.h>
37 #include <tdeprocess.h>
38 #include <tdelocale.h>
39 #include <tdeaboutdata.h>
40 #include <tdefileitem.h>
41 #include <tdeio/netaccess.h>
42 #include <tdetempfile.h>
43 #include <tdeemailsettings.h>
46 #include <libkpimidentities/identitymanager.h>
47 #include <libkpimidentities/identity.h>
48 #include <libemailfunctions/email.h>
49 #include <libkcal/person.h>
51 #include <kmime_header_parsing.h>
57 #include "preferences.h"
61 namespace HeaderParsing
63 bool parseAddress(
const char* & scursor,
const char *
const send,
64 KMime::Types::Address & result,
bool isCRLF=
false );
65 bool parseAddressList(
const char* & scursor,
const char *
const send,
66 TQValueList<KMime::Types::Address> & result,
bool isCRLF=
false );
71 TQString getHostName();
76 KAMailData(
const KAEvent& e,
const TQString& fr,
const TQString& bc,
bool allownotify)
77 : event(e), from(fr), bcc(bc), allowNotify(allownotify) { }
85 TQString KAMail::i18n_NeedFromEmailAddress()
86 {
return i18n(
"A 'From' email address must be configured in order to execute email alarms."); }
88 TQString KAMail::i18n_sent_mail()
89 {
return i18n(
"KMail folder name: this should be translated the same as in kmail",
"sent-mail"); }
91 KPIM::IdentityManager* KAMail::mIdentityManager = 0;
92 KPIM::IdentityManager* KAMail::identityManager()
94 if (!mIdentityManager)
95 mIdentityManager =
new KPIM::IdentityManager(
true);
96 return mIdentityManager;
105 bool KAMail::send(
const KAEvent& event, TQStringList& errmsgs,
bool allowNotify)
109 KPIM::Identity identity;
110 if (!event.emailFromId())
111 from = Preferences::emailAddress();
114 identity = mIdentityManager->identityForUoid(event.emailFromId());
115 if (identity.isNull())
117 kdError(5950) <<
"KAMail::send(): identity" <<
event.emailFromId() <<
"not found" << endl;
118 errmsgs = errors(i18n(
"Invalid 'From' email address.\nKMail identity '%1' not found.").arg(event.emailFromId()));
121 from = identity.fullEmailAddr();
124 kdError(5950) <<
"KAMail::send(): identity" << identity.identityName() <<
"uoid" << identity.uoid() <<
": no email address" << endl;
125 errmsgs = errors(i18n(
"Invalid 'From' email address.\nEmail identity '%1' has no email address").arg(identity.identityName()));
131 switch (Preferences::emailFrom())
133 case Preferences::MAIL_FROM_KMAIL:
134 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."));
136 case Preferences::MAIL_FROM_CONTROL_CENTRE:
137 errmsgs = errors(i18n(
"No 'From' email address is configured.\nPlease set it in the Trinity Control Center or in the KAlarm Preferences dialog."));
139 case Preferences::MAIL_FROM_ADDR:
141 errmsgs = errors(i18n(
"No 'From' email address is configured.\nPlease set it in the KAlarm Preferences dialog."));
146 KAMailData data(event, from,
147 (event.emailBcc() ? Preferences::emailBccAddress() : TQString()),
149 kdDebug(5950) <<
"KAlarmApp::sendEmail(): To: " <<
event.emailAddresses(
", ")
150 <<
"\nSubject: " <<
event.emailSubject() << endl;
152 if (Preferences::emailClient() == Preferences::SENDMAIL)
155 TQString textComplete;
156 TQString command = TDEStandardDirs::findExe(TQString::fromLatin1(
"sendmail"),
157 TQString::fromLatin1(
"/sbin:/usr/sbin:/usr/lib"));
158 if (!command.isNull())
160 command += TQString::fromLatin1(
" -f ");
161 command += KPIM::getEmailAddress(from);
162 command += TQString::fromLatin1(
" -oi -t ");
163 textComplete = initHeaders(data,
false);
167 command = TDEStandardDirs::findExe(TQString::fromLatin1(
"mail"));
168 if (command.isNull())
170 errmsgs = errors(i18n(
"%1 not found").arg(TQString::fromLatin1(
"sendmail")));
174 command += TQString::fromLatin1(
" -s ");
175 command += KShellProcess::quote(event.emailSubject());
177 if (!data.bcc.isEmpty())
179 command += TQString::fromLatin1(
" -b ");
180 command += KShellProcess::quote(data.bcc);
184 command +=
event.emailAddresses(
" ");
189 err = appendBodyAttachments(textComplete, event);
192 errmsgs = errors(err);
197 FILE* fd = popen(command.local8Bit(),
"w");
200 kdError(5950) <<
"KAMail::send(): Unable to open a pipe to " << command << endl;
204 fwrite(textComplete.local8Bit(), textComplete.length(), 1, fd);
207 if (Preferences::emailCopyToKMail())
210 err = addToKMailFolder(data,
"sent-mail",
true);
212 errmsgs = errors(err,
false);
221 err = sendKMail(data);
224 errmsgs = errors(err);
236 TQString KAMail::sendKMail(
const KAMailData& data)
238 TQString err = KAlarm::runKMail(
true);
243 bool useSend =
false;
244 TQCString sendFunction =
"sendMessage(TQString,TQString,TQString,TQString,TQString,TQString,KURL::List)";
245 QCStringList funcs = kapp->dcopClient()->remoteFunctions(
"kmail",
"MailTransportServiceIface");
246 for (QCStringList::Iterator it=funcs.begin(); it != funcs.end() && !useSend; ++it)
248 TQCString func = DCOPClient::normalizeFunctionSignature(*it);
249 if (func.left(5) ==
"bool ")
252 func.replace(TQRegExp(
" [0-9A-Za-z_:]+"),
"");
253 useSend = (func == sendFunction);
257 TQByteArray callData;
258 TQDataStream arg(callData, IO_WriteOnly);
259 kdDebug(5950) <<
"KAMail::sendKMail(): using " << (useSend ?
"sendMessage()" :
"dcopAddMessage()") << endl;
265 arg << data.event.emailAddresses(
", ");
268 arg << data.event.emailSubject();
269 arg << data.event.message();
270 arg << KURL::List(data.event.emailAttachments());
271 if (!callKMail(callData,
"MailTransportServiceIface", sendFunction,
"bool"))
272 return i18n(
"Error calling KMail");
278 err = addToKMailFolder(data,
"outbox",
false);
282 if (data.allowNotify)
283 notifyQueued(data.event);
292 TQString KAMail::addToKMailFolder(
const KAMailData& data,
const char* folder,
bool checkKmailRunning)
295 if (checkKmailRunning)
296 err = KAlarm::runKMail(
true);
299 TQString message = initHeaders(data,
true);
300 err = appendBodyAttachments(message, data.event);
306 tmpFile.setAutoDelete(
true);
307 TQTextStream* stream = tmpFile.textStream();
310 kdError(5950) <<
"KAMail::addToKMailFolder(" << folder <<
"): Unable to open a temporary mail file" << endl;
315 if (tmpFile.status())
317 kdError(5950) <<
"KAMail::addToKMailFolder(" << folder <<
"): Error " << tmpFile.status() <<
" writing to temporary mail file" << endl;
322 TQByteArray callData;
323 TQDataStream arg(callData, IO_WriteOnly);
324 arg << TQString::fromLatin1(folder) << tmpFile.name();
325 if (callKMail(callData,
"KMailIface",
"dcopAddMessage(TQString,TQString)",
"int"))
327 err = i18n(
"Error calling KMail");
329 kdError(5950) <<
"KAMail::addToKMailFolder(" << folder <<
"): " << err << endl;
336 bool KAMail::callKMail(
const TQByteArray& callData,
const TQCString& iface,
const TQCString&
function,
const TQCString& funcType)
339 TQByteArray replyData;
340 if (!kapp->dcopClient()->call(
"kmail", iface,
function, callData, replyType, replyData)
341 || replyType != funcType)
343 TQCString funcname =
function;
344 funcname.replace(TQRegExp(
"(.+$"),
"()");
345 kdError(5950) <<
"KAMail::callKMail(): kmail " << funcname <<
" call failed\n";;
348 TQDataStream replyStream(replyData, IO_ReadOnly);
349 TQCString funcname =
function;
350 funcname.replace(TQRegExp(
"(.+$"),
"()");
351 if (replyType ==
"int")
354 replyStream >> result;
357 kdError(5950) <<
"KAMail::callKMail(): kmail " << funcname <<
" call returned error code = " << result << endl;
361 else if (replyType ==
"bool")
364 replyStream >> result;
367 kdError(5950) <<
"KAMail::callKMail(): kmail " << funcname <<
" call returned error\n";
377 TQString KAMail::initHeaders(
const KAMailData& data,
bool dateId)
383 gettimeofday(&tod, 0);
384 time_t timenow = tod.tv_sec;
386 strftime(buff,
sizeof(buff),
"Date: %a, %d %b %Y %H:%M:%S %z", localtime(&timenow));
387 TQString from = data.from;
388 from.replace(TQRegExp(
"^.*<"), TQString()).replace(TQRegExp(
">.*$"), TQString());
389 message = TQString::fromLatin1(buff);
390 message += TQString::fromLatin1(
"\nMessage-Id: <%1.%2.%3>\n").arg(timenow).arg(tod.tv_usec).arg(from);
392 message += TQString::fromLatin1(
"From: ") + data.from;
393 message += TQString::fromLatin1(
"\nTo: ") + data.event.emailAddresses(
", ");
394 if (!data.bcc.isEmpty())
395 message += TQString::fromLatin1(
"\nBcc: ") + data.bcc;
396 message += TQString::fromLatin1(
"\nSubject: ") + data.event.emailSubject();
397 message += TQString::fromLatin1(
"\nX-Mailer: %1/" KALARM_VERSION).arg(kapp->aboutData()->programName());
406 TQString KAMail::appendBodyAttachments(TQString& message,
const KAEvent& event)
408 static const char* textMimeTypes[] = {
409 "application/x-sh",
"application/x-csh",
"application/x-shellscript",
410 "application/x-nawk",
"application/x-gawk",
"application/x-awk",
411 "application/x-perl",
"application/x-desktop",
414 TQStringList attachments =
event.emailAttachments();
415 if (!attachments.count())
419 message +=
event.message();
428 boundary.sprintf(
"------------_%lu_-%lx=", 2*timenow, timenow);
429 message += TQString::fromLatin1(
"\nMIME-Version: 1.0");
430 message += TQString::fromLatin1(
"\nContent-Type: multipart/mixed;\n boundary=\"%1\"\n").arg(boundary.data());
432 if (!event.message().isEmpty())
435 message += TQString::fromLatin1(
"\n--%1\nContent-Type: text/plain\nContent-Transfer-Encoding: 8bit\n\n").arg(boundary.data());
436 message +=
event.message();
440 TQString attachError = i18n(
"Error attaching file:\n%1");
441 for (TQStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at)
443 TQString attachment = (*at).local8Bit();
444 KURL url(attachment);
447 if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) {
448 kdError(5950) <<
"KAMail::appendBodyAttachments(): not found: " << attachment << endl;
449 return i18n(
"Attachment not found:\n%1").arg(attachment);
451 KFileItem fi(uds, url);
452 if (fi.isDir() || !fi.isReadable()) {
453 kdError(5950) <<
"KAMail::appendBodyAttachments(): not file/not readable: " << attachment << endl;
454 return attachError.arg(attachment);
458 TQString mimeType = fi.mimetype();
459 bool text = mimeType.startsWith(
"text/");
462 for (
int i = 0; !text && textMimeTypes[i]; ++i)
463 text = (mimeType == textMimeTypes[i]);
466 message += TQString::fromLatin1(
"\n--%1").arg(boundary.data());
467 message += TQString::fromLatin1(
"\nContent-Type: %2; name=\"%3\"").arg(mimeType).arg(fi.text());
468 message += TQString::fromLatin1(
"\nContent-Transfer-Encoding: %1").arg(TQString::fromLatin1(text ?
"8bit" :
"BASE64"));
469 message += TQString::fromLatin1(
"\nContent-Disposition: attachment; filename=\"%4\"\n\n").arg(fi.text());
473 if (!TDEIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow())) {
474 kdError(5950) <<
"KAMail::appendBodyAttachments(): load failure: " << attachment << endl;
475 return attachError.arg(attachment);
477 TQFile file(tmpFile);
478 if (!file.open(IO_ReadOnly) ) {
479 kdDebug(5950) <<
"KAMail::appendBodyAttachments() tmp load error: " << attachment << endl;
480 return attachError.arg(attachment);
482 TQIODevice::Offset size = file.size();
483 char* contents =
new char [size + 1];
484 TQ_LONG bytes = file.readBlock(contents, size);
487 bool atterror =
false;
488 if (bytes == -1 || (TQIODevice::Offset)bytes < size) {
489 kdDebug(5950) <<
"KAMail::appendBodyAttachments() read error: " << attachment << endl;
500 TQIODevice::Offset base64Size;
501 char* base64 = base64Encode(contents, size, base64Size);
502 if (base64Size == (TQIODevice::Offset)-1) {
503 kdDebug(5950) <<
"KAMail::appendBodyAttachments() base64 buffer overflow: " << attachment << endl;
507 message += TQString::fromLatin1(base64, base64Size);
512 return attachError.arg(attachment);
514 message += TQString::fromLatin1(
"\n--%1--\n.\n").arg(boundary.data());
523 void KAMail::notifyQueued(
const KAEvent& event)
525 KMime::Types::Address addr;
526 TQString localhost = TQString::fromLatin1(
"localhost");
527 TQString hostname = getHostName();
528 const EmailAddressList& addresses =
event.emailAddresses();
529 for (TQValueList<KCal::Person>::ConstIterator it = addresses.begin(); it != addresses.end(); ++it)
531 TQCString email = (*it).email().local8Bit();
532 const char* em = email;
534 && HeaderParsing::parseAddress(em, em + email.length(), addr))
536 TQString domain = addr.mailboxList.first().addrSpec.domain;
537 if (!domain.isEmpty() && domain != localhost && domain != hostname)
539 TQString text = (Preferences::emailClient() == Preferences::KMAIL)
540 ? i18n(
"An email has been queued to be sent by KMail")
541 : i18n(
"An email has been queued to be sent");
542 KMessageBox::information(0, text, TQString(), Preferences::EMAIL_QUEUED_NOTIFY);
552 bool KAMail::identitiesExist()
555 return mIdentityManager->begin() != mIdentityManager->end();
561 uint KAMail::identityUoid(
const TQString& identityUoidOrName)
564 uint
id = identityUoidOrName.toUInt(&ok);
565 if (!ok || identityManager()->identityForUoid(
id).isNull())
568 for (KPIM::IdentityManager::ConstIterator it = mIdentityManager->begin();
569 it != mIdentityManager->end(); ++it)
571 if ((*it).identityName() == identityUoidOrName)
584 TQString KAMail::controlCentreAddress()
587 return e.getSetting(KEMailSettings::EmailAddress);
595 TQString KAMail::convertAddresses(
const TQString& items, EmailAddressList& list)
598 TQCString addrs = items.local8Bit();
599 const char* ad =
static_cast<const char*
>(addrs);
602 TQValueList<KMime::Types::Address> maybeAddressList;
603 if (!HeaderParsing::parseAddressList(ad, ad + addrs.length(), maybeAddressList))
604 return TQString::fromLocal8Bit(ad);
607 for (TQValueList<KMime::Types::Address>::ConstIterator it = maybeAddressList.begin();
608 it != maybeAddressList.end(); ++it)
610 TQString bad = convertAddress(*it, list);
623 TQString KAMail::convertAddress(
const TQString& item, EmailAddressList& list)
625 TQCString addr = item.local8Bit();
626 const char* ad =
static_cast<const char*
>(addr);
627 KMime::Types::Address maybeAddress;
628 if (!HeaderParsing::parseAddress(ad, ad + addr.length(), maybeAddress))
630 return convertAddress(maybeAddress, list);
638 TQString KAMail::convertAddress(KMime::Types::Address addr, EmailAddressList& list)
640 if (!addr.displayName.isEmpty())
642 kdDebug(5950) <<
"mailbox groups not allowed! Name: \"" << addr.displayName <<
"\"" << endl;
643 return addr.displayName;
645 const TQValueList<KMime::Types::Mailbox>& mblist = addr.mailboxList;
646 for (TQValueList<KMime::Types::Mailbox>::ConstIterator mb = mblist.begin();
647 mb != mblist.end(); ++mb)
649 TQString addrPart = (*mb).addrSpec.localPart;
650 if (!(*mb).addrSpec.domain.isEmpty())
652 addrPart += TQChar(
'@');
653 addrPart += (*mb).addrSpec.domain;
690 int KAMail::checkAddress(TQString& address)
692 address = address.stripWhiteSpace();
694 if (address.find(
',') >= 0 || address.find(
';') >= 0)
696 int n = address.length();
701 if (address[end] ==
'>')
704 if ((start = address.find(
'<')) < 0)
709 int i = address.find(
'@', start);
712 if (i == start || i == end)
739 TQString KAMail::convertAttachments(
const TQString& items, TQStringList& list)
743 int length = items.length();
744 for (
int next = 0; next < length; )
747 int i = items.find(
',', next);
750 int sc = items.find(
';', next);
755 TQString item = items.mid(next, i - next).stripWhiteSpace();
756 switch (checkAttachment(item))
758 case 1: list += item;
break;
761 default:
return item;
774 TQString KAMail::convertAttachments(
const TQString& items, KURL::List& list)
778 TQCString addrs = items.local8Bit();
779 int length = items.length();
780 for (
int next = 0; next < length; )
783 int i = items.find(
',', next);
786 int sc = items.find(
';', next);
791 TQString item = items.mid(next, i - next);
792 switch (checkAttachment(item, &url))
794 case 1: list += url;
break;
797 default:
return item;
812 int KAMail::checkAttachment(TQString& attachment, KURL* url)
814 attachment = attachment.stripWhiteSpace();
815 if (attachment.isEmpty())
822 KURL u = KURL::fromPathOrURL(attachment);
826 return checkAttachment(u) ? 1 : -1;
832 bool KAMail::checkAttachment(
const KURL& url)
835 if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
837 KFileItem fi(uds, url);
838 if (fi.isDir() || !fi.isReadable())
850 char* KAMail::base64Encode(
const char* in, TQIODevice::Offset size, TQIODevice::Offset& outSize)
852 const int MAX_LINELEN = 72;
853 static unsigned char dtable[65] =
854 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
855 "abcdefghijklmnopqrstuvwxyz"
858 char* out =
new char [2*size + 5];
859 outSize = (TQIODevice::Offset)-1;
860 TQIODevice::Offset outIndex = 0;
862 for (TQIODevice::Offset inIndex = 0; inIndex < size; )
864 unsigned char igroup[3];
866 for (n = 0; n < 3; ++n)
869 igroup[n] = (
unsigned char)in[inIndex++];
872 igroup[n] = igroup[2] = 0;
879 unsigned char ogroup[4];
880 ogroup[0] = dtable[igroup[0] >> 2];
881 ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
882 ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
883 ogroup[3] = dtable[igroup[2] & 0x3F];
891 if (outIndex >= size*2)
896 for (
int i = 0; i < 4; ++i)
898 if (lineLength >= MAX_LINELEN)
900 out[outIndex++] =
'\r';
901 out[outIndex++] =
'\n';
904 out[outIndex++] = ogroup[i];
910 if (outIndex + 2 < size*2)
912 out[outIndex++] =
'\r';
913 out[outIndex++] =
'\n';
922 TQStringList KAMail::errors(
const TQString& err,
bool sendfail)
924 TQString error1 = sendfail ? i18n(
"Failed to send email")
925 : i18n(
"Error copying sent email to KMail %1 folder").arg(i18n_sent_mail());
927 return TQStringList(error1);
928 TQStringList errs(TQString::fromLatin1(
"%1:").arg(error1));
936 TQString KAMail::getMailBody(TQ_UINT32 serialNumber)
940 TQByteArray replyData;
942 TQDataStream arg(data, IO_WriteOnly);
946 if (kapp->dcopClient()->call(
"kmail",
"KMailIface",
"getDecodedBodyPart(TQ_UINT32,int)", data, replyType, replyData)
947 && replyType ==
"TQString")
949 TQDataStream reply_stream(replyData, IO_ReadOnly);
950 reply_stream >> body;
953 kdDebug(5950) <<
"KAMail::getMailBody(): kmail getDecodedBodyPart() call failed\n";
962 TQString getHostName()
965 if (gethostname(hname,
sizeof(hname)))
967 return TQString::fromLocal8Bit(hname);
978 namespace HeaderParsing
981 using namespace KMime;
982 using namespace KMime::Types;
983 using namespace KMime::HeaderParsing;
989 bool parseUserName(
const char* & scursor,
const char *
const send,
990 TQString & result,
bool isCRLF ) {
992 TQString maybeLocalPart;
995 if ( scursor != send ) {
997 eatCFWS( scursor, send, isCRLF );
999 char ch = *scursor++;
1009 if ( parseAtom( scursor, send, result,
false ) ) {
1010 if (getpwnam(result.local8Bit()))
1024 bool parseAddress(
const char* & scursor,
const char *
const send,
1025 Address & result,
bool isCRLF ) {
1028 eatCFWS( scursor, send, isCRLF );
1029 if ( scursor == send )
return false;
1032 Mailbox maybeMailbox;
1033 const char * oldscursor = scursor;
1034 if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
1036 result.displayName = TQString();
1037 result.mailboxList.append( maybeMailbox );
1040 scursor = oldscursor;
1044 TQString maybeUserName;
1045 if ( parseUserName( scursor, send, maybeUserName, isCRLF ) ) {
1047 maybeMailbox.displayName = TQString();
1048 maybeMailbox.addrSpec.localPart = maybeUserName;
1049 maybeMailbox.addrSpec.domain = TQString();
1050 result.displayName = TQString();
1051 result.mailboxList.append( maybeMailbox );
1054 scursor = oldscursor;
1056 Address maybeAddress;
1059 if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) )
1061 scursor = oldscursor;
1065 result = maybeAddress;
1073 bool parseAddressList(
const char* & scursor,
const char *
const send,
1074 TQValueList<Address> & result,
bool isCRLF ) {
1075 while ( scursor != send ) {
1076 eatCFWS( scursor, send, isCRLF );
1078 if ( scursor == send )
return true;
1080 if ( *scursor ==
',' || *scursor ==
';' ) { scursor++;
continue; }
1083 Address maybeAddress;
1084 if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) )
return false;
1085 result.append( maybeAddress );
1087 eatCFWS( scursor, send, isCRLF );
1089 if ( scursor == send )
return true;
1091 if ( *scursor ==
',' || *scursor ==
';' ) scursor++;
represents calendar alarms and events
KAEvent corresponds to a KCal::Event instance.
the KAlarm application object