|
|
// kmgroupware.cpp |
|
|
// Author: Karl-Heinz Zimmer <khz@klaralvdalens-datakonsult.se> |
|
|
|
|
|
#include <kdeversion.h> |
|
|
#include <klocale.h> |
|
|
#include <kdebug.h> |
|
|
#include <kmessagebox.h> |
|
|
#include <klibloader.h> |
|
|
#include <kpopupmenu.h> |
|
|
#include <krfcdate.h> |
|
|
#include <kiconloader.h> |
|
|
#include <kapplication.h> |
|
|
#include <dcopclient.h> |
|
|
|
|
|
#include "kmgroupware.h" |
|
|
#include "kfileio.h" |
|
|
#include "kmkernel.h" |
|
|
#include "kmmainwin.h" |
|
|
#include "kmmainwidget.h" |
|
|
#include "kmmsgpart.h" |
|
|
#include "kmfolder.h" |
|
|
#include "kmfolderdir.h" |
|
|
#include "kmfoldermgr.h" |
|
|
#include "kmfoldertree.h" |
|
|
#include "kmheaders.h" |
|
|
#include "kmreaderwin.h" |
|
|
#include "kmcomposewin.h" |
|
|
#include "kmmimeparttree.h" |
|
|
#include "kmidentity.h" |
|
|
#include "identitymanager.h" |
|
|
#include "kmgroupwarewizard.h" |
|
|
#include "kmacctmgr.h" |
|
|
#include "kmgroupwarefuncs.h" |
|
|
#include "kmcommands.h" |
|
|
#include "kmfolderindex.h" |
|
|
|
|
|
#include <ktnef/ktnefparser.h> |
|
|
#include <ktnef/ktnefattach.h> |
|
|
#include <ktnef/ktnefproperty.h> |
|
|
#include <ktnef/ktnefmessage.h> |
|
|
#include <ktnef/ktnefdefs.h> |
|
|
|
|
|
#include <mimelib/enum.h> |
|
|
#include <mimelib/headers.h> |
|
|
#include <mimelib/bodypart.h> |
|
|
#include <mimelib/string.h> |
|
|
#include <mimelib/text.h> |
|
|
|
|
|
#include <assert.h> |
|
|
|
|
|
#include <qdatetime.h> |
|
|
#include <qstring.h> |
|
|
#include <qsplitter.h> |
|
|
#include <qregexp.h> |
|
|
#include <qbuffer.h> |
|
|
#include <qfile.h> |
|
|
#include <qinputdialog.h> |
|
|
|
|
|
// groupware folder icons: |
|
|
QPixmap* KMGroupware::pixContacts = 0; |
|
|
QPixmap* KMGroupware::pixCalendar = 0; |
|
|
QPixmap* KMGroupware::pixNotes = 0; |
|
|
QPixmap* KMGroupware::pixTasks = 0; |
|
|
|
|
|
// global status flag: |
|
|
static bool ignore_GroupwareDataChangeSlots = false; |
|
|
|
|
|
// Global tables of foldernames is different languages |
|
|
// For now: 0->English, 1->German |
|
|
static QMap<KFolderTreeItem::Type,QString> folderNames[2]; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
KMGroupware::KMGroupware( QObject* parent, const char* name ) : |
|
|
QObject(parent, name), |
|
|
mMainWin(0), |
|
|
mUseGroupware(true), |
|
|
mGroupwareIsHidingMimePartTree(false), |
|
|
mFolderLanguage(0), |
|
|
mKOrgPart(0), |
|
|
mFolderParent(0), |
|
|
mFolderType(KMFolderTypeUnknown), |
|
|
mContactsLocked(false), |
|
|
mContacts(0), |
|
|
mCalendar(0), |
|
|
mNotes(0), |
|
|
mTasks(0), |
|
|
mWizardRunning(false) |
|
|
{ |
|
|
} |
|
|
|
|
|
void KMGroupware::readConfigStartup() |
|
|
{ |
|
|
readConfigInternal(); |
|
|
|
|
|
if( !checkFolders() ) { |
|
|
mWizardRunning = true; |
|
|
KMGroupwareWizard wiz(0, "groupware wizard", TRUE ); |
|
|
int rc = wiz.exec(); |
|
|
mWizardRunning = false; |
|
|
|
|
|
if( rc == QDialog::Accepted ) { |
|
|
mUseGroupware = wiz.groupwareEnabled(); |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
options.writeEntry( "Enabled", isEnabled() ); |
|
|
if( isEnabled() ) { |
|
|
mFolderParent = wiz.folder()->child(); |
|
|
mFolderType = wiz.folder()->folderType(); |
|
|
mFolderLanguage = wiz.language(); |
|
|
options.writeEntry( "FolderLanguage", mFolderLanguage ); |
|
|
options.writeEntry( "GroupwareFolder", wiz.folder()->idString() ); |
|
|
} else |
|
|
return; |
|
|
|
|
|
readConfigInternal(); |
|
|
} else { |
|
|
// Just disable everything if the user cancels/closes the dialog |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
options.writeEntry( "Enabled", false ); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
initFolders(); |
|
|
} |
|
|
|
|
|
void KMGroupware::readConfig() |
|
|
{ |
|
|
if (mWizardRunning) |
|
|
// This was called by the configuration dialog. But the wizard takes care of all config stuff, so don't do it now |
|
|
return; |
|
|
|
|
|
readConfigInternal(); |
|
|
if( !checkFolders() ) { |
|
|
assert( mFolderParent ); |
|
|
if( KMessageBox::questionYesNo( 0, i18n("KMail will now create the required groupware folders" |
|
|
" as subfolders of %1. If you dont want this, press \"No\"," |
|
|
" and the groupware functions will be disabled").arg(mFolderParent->name()), |
|
|
i18n("Groupware Folders") ) == KMessageBox::No ) { |
|
|
mUseGroupware = false; |
|
|
} |
|
|
} |
|
|
initFolders(); |
|
|
} |
|
|
|
|
|
void KMGroupware::readConfigInternal() |
|
|
{ |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
bool enabled = options.readBoolEntry( "Enabled", true ); |
|
|
setEnabled( enabled ); |
|
|
|
|
|
mFolderLanguage = options.readNumEntry( "FolderLanguage", 0 ); |
|
|
if( mFolderLanguage > 1 ) mFolderLanguage = 0; // Just for safety |
|
|
|
|
|
QString parentName( options.readEntry("GroupwareFolder") ); |
|
|
KMFolder* folderParent = kernel->folderMgr()->findIdString( parentName ); |
|
|
if( folderParent == 0 ) |
|
|
folderParent = kernel->imapFolderMgr()->findIdString( parentName ); |
|
|
|
|
|
if( folderParent == 0 ) { |
|
|
// Maybe nothing was configured? |
|
|
mFolderParent = &(kernel->folderMgr()->dir()); |
|
|
mFolderType = KMFolderTypeMaildir; |
|
|
} else { |
|
|
mFolderParent = folderParent->createChildFolder(); |
|
|
mFolderType = folderParent->folderType(); |
|
|
} |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
KMGroupware::~KMGroupware() |
|
|
{ |
|
|
delete (KParts::ReadOnlyPart*)mKOrgPart; |
|
|
} |
|
|
|
|
|
void KMGroupware::setEnabled( bool b ) |
|
|
{ |
|
|
if( b == mUseGroupware ) |
|
|
return; |
|
|
|
|
|
mUseGroupware = b; |
|
|
|
|
|
// Set the menus on the main window |
|
|
setupActions(); |
|
|
|
|
|
if( mUseGroupware ) { |
|
|
// FIXME (Bo): Create this (see FIXME just below) |
|
|
// internalCreateKOrgPart(); |
|
|
|
|
|
// ?? Not even this makes the widgets show the real content :-( |
|
|
slotCalendarFolderChanged(); |
|
|
slotNotesFolderChanged(); |
|
|
slotTasksFolderChanged(); |
|
|
} else { |
|
|
slotGroupwareHide(); |
|
|
|
|
|
// FIXME (Bo): If the part is deleted here, kmail crashes if groupware |
|
|
// mode is later enabled again. Workaround is simply not deleting it |
|
|
// delete (KParts::ReadOnlyPart*)mKOrgPart; |
|
|
// mKOrgPart = 0; |
|
|
|
|
|
if( mContacts ) mContacts->setType("<EFBFBD>h"); |
|
|
mContacts = mCalendar = mNotes = mTasks = 0; |
|
|
if( mReader ) mReader->setUseGroupware( false ); |
|
|
if( mMainWin && mMainWin->mainKMWidget()->folderTree() ) |
|
|
mMainWin->mainKMWidget()->folderTree()->reload(); |
|
|
} |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Returns true if folder is a groupware folder. If groupware mode isn't enabled |
|
|
// this always returns false |
|
|
bool KMGroupware::isGroupwareFolder( KMFolder* folder ) const |
|
|
{ |
|
|
return mUseGroupware && folder && ( folder == mCalendar || folder == mContacts || |
|
|
folder == mNotes || folder == mTasks ); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
KFolderTreeItem::Type KMGroupware::folderType( KMFolder* folder ) const |
|
|
{ |
|
|
if( mUseGroupware ) { |
|
|
if( folder == mCalendar ) |
|
|
return KFolderTreeItem::Calendar; |
|
|
else if( folder == mContacts ) |
|
|
return KFolderTreeItem::Contacts; |
|
|
else if( folder == mNotes ) |
|
|
return KFolderTreeItem::Notes; |
|
|
else if( folder == mTasks ) |
|
|
return KFolderTreeItem::Tasks; |
|
|
} |
|
|
|
|
|
return KFolderTreeItem::Other; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
QString KMGroupware::folderName( KFolderTreeItem::Type type, int language ) const |
|
|
{ |
|
|
static bool folderNamesSet = false; |
|
|
if( !folderNamesSet ) { |
|
|
folderNamesSet = true; |
|
|
/* |
|
|
Logical (enum) English German |
|
|
------------------------------------------------------ |
|
|
Contacts Contacts Kontakte |
|
|
Calendar Calendar Kalender |
|
|
Notes Notes Notizen |
|
|
Tasks Tasks Aufgaben |
|
|
Inbox inbox Posteingang |
|
|
Outbox outbox Postausgang |
|
|
*/ |
|
|
/* NOTE: If you add something here, you also need to update |
|
|
GroupwarePage in configuredialog.cpp */ |
|
|
// English |
|
|
folderNames[0][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendar"); |
|
|
folderNames[0][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts"); |
|
|
folderNames[0][KFolderTreeItem::Notes] = QString::fromLatin1("Notes"); |
|
|
folderNames[0][KFolderTreeItem::Tasks] = QString::fromLatin1("Tasks"); |
|
|
//folderNames[0][KFolderTreeItem::Journal] = QString::fromLatin1("Journals"); |
|
|
|
|
|
// German |
|
|
folderNames[1][KFolderTreeItem::Calendar] = QString::fromLatin1("Kalender"); |
|
|
folderNames[1][KFolderTreeItem::Contacts] = QString::fromLatin1("Kontakte"); |
|
|
folderNames[1][KFolderTreeItem::Notes] = QString::fromLatin1("Notizen"); |
|
|
folderNames[1][KFolderTreeItem::Tasks] = QString::fromLatin1("Aufgaben"); |
|
|
} |
|
|
|
|
|
if( language == -1 ) return folderNames[mFolderLanguage][type]; |
|
|
else return folderNames[language][type]; |
|
|
} |
|
|
|
|
|
bool KMGroupware::checkFolders() const |
|
|
{ |
|
|
if( !mUseGroupware ) return true; |
|
|
if( !mFolderParent ) return false; |
|
|
|
|
|
KMFolderNode* node; |
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Contacts ) ); |
|
|
if( !node || node->isDir() ) return false; |
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Calendar ) ); |
|
|
if( !node || node->isDir() ) return false; |
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Notes ) ); |
|
|
if( !node || node->isDir() ) return false; |
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Tasks ) ); |
|
|
if( !node || node->isDir() ) return false; |
|
|
return true; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::initFolders() |
|
|
{ |
|
|
if( mUseGroupware && mFolderParent ){ |
|
|
KMFolderType type = mFolderType; |
|
|
if( type == KMFolderTypeUnknown ) |
|
|
type = KMFolderTypeMaildir; |
|
|
KMFolderNode* node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Contacts ) ); |
|
|
if( node && !node->isDir() ) mContacts = static_cast<KMFolder*>(node); |
|
|
if( !mContacts ) mContacts = mFolderParent->createFolder( folderName( KFolderTreeItem::Contacts ), false, type ); |
|
|
if (mContacts->canAccess() != 0) { |
|
|
KMessageBox::sorry(0, i18n("You do not have read/write permission to your Contacts folder.") ); |
|
|
exit(1); |
|
|
} |
|
|
mContacts->setType("GCo"); |
|
|
mContacts->setSystemFolder(TRUE); |
|
|
mContacts->open(); |
|
|
connect( mContacts, SIGNAL( changed() ), |
|
|
this, SLOT( slotContactsFolderChanged() ) ); |
|
|
connect( mContacts, SIGNAL( msgAdded(int) ), |
|
|
this, SLOT( slotContactsFolderChanged() ) ); |
|
|
connect( mContacts, SIGNAL( msgRemoved(int, QString) ), |
|
|
this, SLOT( slotContactsFolderChanged() ) ); |
|
|
|
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Calendar ) ); |
|
|
if( node && !node->isDir() ) mCalendar = static_cast<KMFolder*>(node); |
|
|
if( !mCalendar ) mCalendar = mFolderParent->createFolder( folderName( KFolderTreeItem::Calendar ), false, type ); |
|
|
if (mCalendar->canAccess() != 0) { |
|
|
KMessageBox::sorry(0, i18n("You do not have read/write permission to your Calendar folder.") ); |
|
|
exit(1); |
|
|
} |
|
|
mCalendar->setType("GCa"); |
|
|
mCalendar->setSystemFolder(TRUE); |
|
|
mCalendar->open(); |
|
|
connect( mCalendar, SIGNAL( changed() ), |
|
|
this, SLOT( slotCalendarFolderChanged() ) ); |
|
|
connect( mCalendar, SIGNAL( msgAdded(int) ), |
|
|
this, SLOT( slotCalendarFolderChanged() ) ); |
|
|
// connect( mCalendar, SIGNAL( msgRemoved(int, QString) ), |
|
|
// this, SLOT( slotCalendarFolderChanged() ) ); |
|
|
|
|
|
|
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Notes ) ); |
|
|
if( node && !node->isDir() ) mNotes = static_cast<KMFolder*>(node); |
|
|
if( !mNotes ) mNotes = mFolderParent->createFolder( folderName( KFolderTreeItem::Notes ), false, type ); |
|
|
|
|
|
if (mNotes->canAccess() != 0) { |
|
|
KMessageBox::sorry(0, i18n("You do not have read/write permission to your Notes folder.") ); |
|
|
exit(1); |
|
|
} |
|
|
mNotes->setType("GNo"); |
|
|
mNotes->setSystemFolder(TRUE); |
|
|
mNotes->open(); |
|
|
connect( mNotes, SIGNAL( changed() ), |
|
|
this, SLOT( slotNotesFolderChanged() ) ); |
|
|
connect( mNotes, SIGNAL( msgAdded(int) ), |
|
|
this, SLOT( slotNotesFolderChanged() ) ); |
|
|
connect( mNotes, SIGNAL( msgRemoved(int, QString) ), |
|
|
this, SLOT( slotNotesFolderChanged() ) ); |
|
|
|
|
|
node = mFolderParent->hasNamedFolder( folderName( KFolderTreeItem::Tasks ) ); |
|
|
if( node && !node->isDir() ) mTasks = static_cast<KMFolder*>(node); |
|
|
if( !mTasks ) mTasks = mFolderParent->createFolder( folderName( KFolderTreeItem::Tasks ), false, type ); |
|
|
|
|
|
if (mTasks->canAccess() != 0) { |
|
|
KMessageBox::sorry(0, i18n("You do not have read/write permission to your Tasks folder.") ); |
|
|
exit(1); |
|
|
} |
|
|
mTasks->setType("GTa"); |
|
|
mTasks->setSystemFolder(TRUE); |
|
|
mTasks->open(); |
|
|
connect( mTasks, SIGNAL( changed() ), |
|
|
this, SLOT( slotTasksFolderChanged() ) ); |
|
|
connect( mTasks, SIGNAL( msgAdded(int) ), |
|
|
this, SLOT( slotTasksFolderChanged() ) ); |
|
|
connect( mTasks, SIGNAL( msgRemoved(int, QString) ), |
|
|
this, SLOT( slotTasksFolderChanged() ) ); |
|
|
|
|
|
if( mMainWin && mMainWin->mainKMWidget()->folderTree() ) { |
|
|
mMainWin->mainKMWidget()->folderTree()->reload(); |
|
|
} |
|
|
} else { |
|
|
cleanup(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool KMGroupware::vPartFoundAndDecoded( KMMessage* msg, |
|
|
int& aUpdateCounter, |
|
|
QString* s, |
|
|
QCString* sc ) |
|
|
{ |
|
|
assert( msg ); |
|
|
assert( s != 0 || sc != 0 ); |
|
|
// kdDebug() << "KMGroupware::vPartFoundAndDecoded( " << msg->subject() << endl; |
|
|
// kdDebug() << msg->type() << ", " << msg->subtype() << ")" << endl; |
|
|
|
|
|
if( ( DwMime::kTypeText == msg->type() && ( DwMime::kSubtypeVCal == msg->subtype() || |
|
|
DwMime::kSubtypeXVCard == msg->subtype() ) ) || |
|
|
( DwMime::kTypeApplication == msg->type() && |
|
|
DwMime::kSubtypeOctetStream == msg->subtype() ) ) |
|
|
{ |
|
|
if( sc ) |
|
|
*sc = msg->bodyDecoded(); |
|
|
if( s ) |
|
|
*s = QString::fromUtf8( sc ? *sc : msg->bodyDecoded() ); |
|
|
return true; |
|
|
} else if( DwMime::kTypeMultipart == msg->type() && |
|
|
DwMime::kSubtypeMixed == msg->subtype() ) |
|
|
{ |
|
|
// kdDebug(5006) << "KMGroupware looking for TNEF data" << endl; |
|
|
DwBodyPart* dwPart = msg->findDwBodyPart( DwMime::kTypeApplication, |
|
|
DwMime::kSubtypeMsTNEF ); |
|
|
if( !dwPart ) |
|
|
dwPart = msg->findDwBodyPart( DwMime::kTypeApplication, |
|
|
DwMime::kSubtypeOctetStream ); |
|
|
if( dwPart ){ |
|
|
// kdDebug(5006) << "KMGroupware analyzing TNEF data" << endl; |
|
|
KMMessagePart msgPart; |
|
|
KMMessage::bodyPart(dwPart, &msgPart); |
|
|
return KMGroupware::msTNEFToVPart( msgPart.bodyDecodedBinary(), aUpdateCounter, s, sc ); |
|
|
} |
|
|
}else if( DwMime::kTypeMultipart == msg->type() && |
|
|
DwMime::kSubtypeMixed == msg->subtype() ){ |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotCalendarFolderChanged() |
|
|
{ |
|
|
QStringList lEvents; |
|
|
QString s; |
|
|
int iDummy; |
|
|
if( mCalendar ) |
|
|
for( int i=0; i<mCalendar->count(); ++i ){ |
|
|
bool unget = !mCalendar->isMessage(i); |
|
|
if( KMGroupware::vPartFoundAndDecoded( mCalendar->getMsg( i ), iDummy, &s, 0 ) ) |
|
|
lEvents << s; |
|
|
if( unget ) mCalendar->unGetMsg(i); |
|
|
} |
|
|
|
|
|
ignore_GroupwareDataChangeSlots = true; |
|
|
emit signalRefreshEvents( lEvents ); |
|
|
ignore_GroupwareDataChangeSlots = false; |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotContactsFolderChanged() |
|
|
{ |
|
|
//pending(khz): remove this methode if it is no longer needed |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotTasksFolderChanged() |
|
|
{ |
|
|
QStringList lTasks; |
|
|
QString s; |
|
|
int iDummy; |
|
|
if( mTasks ) |
|
|
for( int i=0; i<mTasks->count(); ++i ){ |
|
|
bool unget = !mTasks->isMessage(i); |
|
|
if( vPartFoundAndDecoded( mTasks->getMsg( i ), iDummy, &s, 0 ) ) |
|
|
lTasks << s; |
|
|
if( unget ) mTasks->unGetMsg(i); |
|
|
} |
|
|
|
|
|
ignore_GroupwareDataChangeSlots = true; |
|
|
emit signalRefreshTasks( lTasks ); |
|
|
ignore_GroupwareDataChangeSlots = false; |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotNotesFolderChanged() |
|
|
{ |
|
|
QStringList lNotes; |
|
|
KMMessage* m; |
|
|
if( mNotes ) |
|
|
for( int i=0; i<mNotes->count(); ++i ){ |
|
|
bool unget = !mNotes->isMessage(i); |
|
|
m = mNotes->getMsg( i ); |
|
|
if( m ){ |
|
|
lNotes << QString::fromUtf8( m->asDwString().data(), m->asDwString().length() ); |
|
|
m->touch(); |
|
|
} |
|
|
if( unget ) mNotes->unGetMsg(i); |
|
|
} |
|
|
|
|
|
ignore_GroupwareDataChangeSlots = true; |
|
|
emit signalRefreshNotes( lNotes ); |
|
|
ignore_GroupwareDataChangeSlots = false; |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotInvalidateIMAPFolders() |
|
|
{ |
|
|
QString str = i18n("Are you sure you want to refresh the IMAP cache?\n" |
|
|
"This will remove all changes you have done locally to your folders"); |
|
|
QString s1 = i18n("Refresh IMAP Cache"); |
|
|
QString s2 = i18n("&Refresh"); |
|
|
if( KMessageBox::warningContinueCancel(mMainWin, str, s1, s2 ) == KMessageBox::Continue) |
|
|
kernel->acctMgr()->invalidateIMAPFolders(); |
|
|
} |
|
|
|
|
|
void KMGroupware::cleanup() |
|
|
{ |
|
|
if( mContacts ) { |
|
|
mContacts->disconnect( this ); |
|
|
mContacts->close(TRUE); |
|
|
} |
|
|
if( mCalendar ) { |
|
|
mCalendar->disconnect( this ); |
|
|
mCalendar->close(TRUE); |
|
|
} |
|
|
if( mNotes ) { |
|
|
mNotes->disconnect( this ); |
|
|
mNotes->close(TRUE); |
|
|
} |
|
|
if( mTasks ) { |
|
|
mTasks->disconnect( this ); |
|
|
mTasks->close(TRUE); |
|
|
} |
|
|
//if( mFolderParent ) mFolderParent->close( TRUE ); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::setFolderPixmap(const KMFolder& folder, KMFolderTreeItem& fti) const |
|
|
{ |
|
|
if( !mUseGroupware ) |
|
|
return false; |
|
|
|
|
|
// Make sure they are set |
|
|
loadPixmaps(); |
|
|
|
|
|
if( folder.label() == folderName( KFolderTreeItem::Contacts ) ) |
|
|
fti.setPixmap( 0, *pixContacts ); |
|
|
else if( folder.label() == folderName( KFolderTreeItem::Calendar ) ) |
|
|
fti.setPixmap( 0, *pixCalendar ); |
|
|
else if( folder.label() == folderName( KFolderTreeItem::Notes ) ) |
|
|
fti.setPixmap( 0, *pixNotes ); |
|
|
else if( folder.label() == folderName( KFolderTreeItem::Tasks ) ) |
|
|
fti.setPixmap( 0, *pixTasks ); |
|
|
else |
|
|
return false; |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::setupKMReaderWin(KMReaderWin* reader) |
|
|
{ |
|
|
mReader = reader; |
|
|
mReader->setUseGroupware( mUseGroupware ); |
|
|
|
|
|
// connect KMReaderWin's signals to our slots |
|
|
connect( mReader, SIGNAL( signalGroupwareShow( bool ) ), |
|
|
this, SLOT( slotGroupwareShow( bool ) ) ); |
|
|
|
|
|
// HACK (Bo): Don't receive events while showing groupware widgets |
|
|
mReader->installEventFilter( this ); |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::setHeaders( KMHeaders* headers ) |
|
|
{ |
|
|
mHeaders = headers; |
|
|
|
|
|
// HACK (Bo): Don't receive events while showing groupware widgets |
|
|
mHeaders->installEventFilter( this ); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::setMimePartTree(KMMimePartTree* mimePartTree) |
|
|
{ |
|
|
mMimePartTree = mimePartTree; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::createKOrgPart(QWidget* parent) |
|
|
{ |
|
|
if( mKOrgPart ) |
|
|
// The part is already set up. |
|
|
return; |
|
|
|
|
|
// We must at least remember this. |
|
|
mKOrgPartParent = parent; |
|
|
|
|
|
if( !mUseGroupware ) |
|
|
// Groupware mode is disabled, so hold setting it up until user enables it |
|
|
return; |
|
|
|
|
|
// Ok, we can proceed with actually making the part |
|
|
internalCreateKOrgPart(); |
|
|
} |
|
|
|
|
|
void KMGroupware::internalCreateKOrgPart() |
|
|
{ |
|
|
// create a KOrganizer KPart and embedd it into the messageParent |
|
|
KLibFactory *factory = KLibLoader::self()->factory( "libkorganizer" ); |
|
|
if( !factory ) { |
|
|
KMessageBox::error(mMainWin, "No libkorganizer found !"); |
|
|
mUseGroupware = false; |
|
|
mReader->setUseGroupware( false ); |
|
|
return; |
|
|
} |
|
|
|
|
|
// Create the part |
|
|
QStringList partArgs; |
|
|
const KMIdentity& identity = kernel->identityManager()->defaultIdentity(); |
|
|
partArgs << QString( "name=%1" ).arg( identity.fullName() ); |
|
|
partArgs << QString( "email=%1" ).arg( identity.emailAddr() ); |
|
|
partArgs << "storage=imap"; |
|
|
mKOrgPart = (KParts::ReadOnlyPart *)factory->create( (QWidget*)mKOrgPartParent, |
|
|
"korganizerpart", |
|
|
"KParts::ReadOnlyPart", partArgs ); |
|
|
|
|
|
// initially hide the KOrganizer part |
|
|
mKOrgPart->widget()->hide(); |
|
|
|
|
|
// connect our signals to KOrgPart's slots |
|
|
connect( this, SIGNAL( signalSetKroupwareCommunicationEnabled( QObject* ) ), |
|
|
mKOrgPart, SLOT( slotSetKroupwareCommunicationEnabled( QObject* ) ) ); |
|
|
|
|
|
connect( this, SIGNAL( signalShowCalendarView() ), mKOrgPart, SLOT( slotShowNextXView() ) ); |
|
|
connect( this, SIGNAL( signalShowNotesView() ), mKOrgPart, SLOT( slotShowNotesView() ) ); |
|
|
connect( this, SIGNAL( signalShowTodoView() ), mKOrgPart, SLOT( slotShowTodoView() ) ); |
|
|
// exception: Contacts are handled by KAddressbook - so call it via KMMainWin |
|
|
connect( this, SIGNAL( signalShowContactsView() ), mMainWin, SLOT( slotAddrBook() ) ); |
|
|
|
|
|
connect( this, SIGNAL( signalCalendarUpdateView( const QDateTime&, const QDateTime& ) ), |
|
|
mKOrgPart, SLOT( slotUpdateView( const QDateTime&, const QDateTime& ) ) ); |
|
|
connect( this, SIGNAL( signalRefreshEvents( const QStringList& ) ), |
|
|
mKOrgPart, SLOT( slotRefreshEvents( const QStringList& ) ) ); |
|
|
connect( this, SIGNAL( signalRefreshNotes( const QStringList& ) ), |
|
|
mKOrgPart, SLOT( slotRefreshNotes( const QStringList& ) ) ); |
|
|
connect( this, SIGNAL( signalRefreshTasks( const QStringList& ) ), |
|
|
mKOrgPart, SLOT( slotRefreshTasks( const QStringList& ) ) ); |
|
|
|
|
|
connect( this, SIGNAL( signalEventRequest( const QCString&, const QString&, bool&, |
|
|
QString&, QString&, bool& ) ), |
|
|
mKOrgPart, SLOT(slotEventRequest( const QCString&, const QString&, bool&, |
|
|
QString&, QString&, bool& ) ) ); |
|
|
connect( this, SIGNAL( signalResourceRequest( const QValueList<QPair<QDateTime,QDateTime> >&, |
|
|
const QCString&, const QString&, bool&, |
|
|
QString&, bool&, bool&, |
|
|
QDateTime&, QDateTime& ) ), |
|
|
mKOrgPart, |
|
|
SLOT( slotResourceRequest( const QValueList<QPair<QDateTime, QDateTime> >&, |
|
|
const QCString&, const QString&, bool&, |
|
|
QString&, bool&, bool&, QDateTime&, QDateTime& ) ) ); |
|
|
connect( this, SIGNAL( signalAcceptedEvent( bool, const QCString&, const QString&, |
|
|
bool&, QString&, bool& ) ), |
|
|
mKOrgPart, SLOT(slotAcceptedEvent( bool, const QCString&, const QString&, |
|
|
bool&, QString&, bool& ) ) ); |
|
|
connect( this, SIGNAL( signalRejectedEvent( const QCString&, const QString&, bool&, |
|
|
QString&, bool& ) ), |
|
|
mKOrgPart, SLOT(slotRejectedEvent( const QCString&, const QString&, bool&, |
|
|
QString&, bool& ) ) ); |
|
|
connect( this, SIGNAL( signalIncidenceAnswer( const QString&, QString& ) ), |
|
|
mKOrgPart, SLOT( slotIncidenceAnswer( const QString&, QString& ) ) ); |
|
|
connect( this, SIGNAL( signalEventDeleted( const QString& ) ), |
|
|
mKOrgPart, SLOT( slotEventDeleted( const QString& ) ) ); |
|
|
|
|
|
connect( this, SIGNAL( signalTaskDeleted( const QString& ) ), |
|
|
mKOrgPart, SLOT( slotTaskDeleted( const QString& ) ) ); |
|
|
|
|
|
connect( this, SIGNAL( signalNoteDeleted( const QString& ) ), |
|
|
mKOrgPart, SLOT( slotNoteDeleted( const QString& ) ) ); |
|
|
|
|
|
connect( mKOrgPart,SIGNAL( signalKOrganizerShow( bool ) ), |
|
|
this, SLOT( slotGroupwareShow( bool ) ) ); |
|
|
|
|
|
emit signalSetKroupwareCommunicationEnabled( this ); |
|
|
|
|
|
// initialize Groupware using data stored in our folders |
|
|
// ignore_GroupwareDataChangeSlots = true; |
|
|
// Calendar |
|
|
slotCalendarFolderChanged(); |
|
|
// Notes |
|
|
slotNotesFolderChanged(); |
|
|
// Tasks |
|
|
slotTasksFolderChanged(); |
|
|
// ignore_GroupwareDataChangeSlots = false; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::reparent(QSplitter* panner) |
|
|
{ |
|
|
mPanner = panner; |
|
|
if( mKOrgPart ) |
|
|
mKOrgPart->widget()->reparent( mPanner, 0, QPoint( 0, 0 ) ); |
|
|
else |
|
|
// Since the creation of the part is possibly deferred, we should remember this |
|
|
mKOrgPartParent = panner; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::moveToLast() |
|
|
{ |
|
|
if( mKOrgPart ) |
|
|
mPanner->moveToLast( mKOrgPart->widget() ); |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::setupActions() |
|
|
{ |
|
|
static bool actionsSetup = false; |
|
|
|
|
|
if( !actionsSetup && mMainWin ) { |
|
|
actionsSetup = true; |
|
|
|
|
|
// file menu: some entries |
|
|
new KAction( i18n("Merge Calendar"), 0, mKOrgPart, SLOT(slotFileMerge()), |
|
|
mMainWin->actionCollection(), "file_korganizermerge_calendar" ); |
|
|
new KAction( i18n("Archive Old Entries"), 0, mKOrgPart, SLOT(slotFileArchive()), |
|
|
mMainWin->actionCollection(), "file_korganizerarchive_old_entries" ); |
|
|
new KAction( i18n("Export iCal"), 0, mKOrgPart, SLOT(slotExportICalendar()), |
|
|
mMainWin->actionCollection(), "file_korganizerexport_ical" ); |
|
|
new KAction( i18n("Export vCal"), 0, mKOrgPart, SLOT(slotExportVCalendar()), |
|
|
mMainWin->actionCollection(), "file_korganizerexport_vcal" ); |
|
|
new KAction( i18n("delete completed To-Dos","Purge Completed To-Dos"), 0, mKOrgPart, |
|
|
SLOT(slotPurgeCompleted()), mMainWin->actionCollection(), |
|
|
"file_korganizerpurge_completed" ); |
|
|
new KAction( i18n("refresh local imap cache", "Refresh Local IMAP Cache"), 0, |
|
|
this, SLOT(slotInvalidateIMAPFolders()), mMainWin->actionCollection(), |
|
|
"invalidate_imap_cache" ); |
|
|
// view menu: some entries |
|
|
new KAction( i18n("What's Next"), "whatsnext", 0, mKOrgPart, SLOT(slotShowWhatsNextView()), |
|
|
mMainWin->actionCollection(), "view_korganizerwhats_next" ); |
|
|
new KAction( i18n("List"), "list", 0, mKOrgPart, SLOT(slotShowListView()), |
|
|
mMainWin->actionCollection(), "view_korganizerlist" ); |
|
|
new KAction( i18n("Day"), "1day", 0, mKOrgPart, SLOT(slotShowDayView()), |
|
|
mMainWin->actionCollection(), "view_korganizerday" ); |
|
|
new KAction( i18n("Work Week"), "5days", 0, mKOrgPart, SLOT(slotShowWorkWeekView()), |
|
|
mMainWin->actionCollection(), "view_korganizerwork_week" ); |
|
|
new KAction( i18n("Week"), "7days", 0, mKOrgPart, SLOT(slotShowWeekView()), |
|
|
mMainWin->actionCollection(), "view_korganizerweek" ); |
|
|
new KAction( i18n("Next 3 Days"), 0, mKOrgPart, SLOT(slotShowNextXView()), |
|
|
mMainWin->actionCollection(), "view_korganizernext_three_days" ); |
|
|
new KAction( i18n("Month"), "month", 0, mKOrgPart, SLOT(slotShowMonthView()), |
|
|
mMainWin->actionCollection(), "view_korganizermonth" ); |
|
|
new KAction( i18n("To-Do List"), "todo", 0, mKOrgPart, SLOT(slotShowTodoView()), |
|
|
mMainWin->actionCollection(), "view_korganizertodo_list" ); |
|
|
new KAction( i18n("Notes"), "notes", 0, mKOrgPart, SLOT(slotShowNotesView()), |
|
|
mMainWin->actionCollection(), "view_korganizernotes" ); |
|
|
new KAction( i18n("Journal"), 0, mKOrgPart, SLOT(slotShowJournalView()), |
|
|
mMainWin->actionCollection(), "view_korganizerjournal" ); |
|
|
new KAction( i18n("Update"), 0, mKOrgPart, SLOT(slotUpdate()), |
|
|
mMainWin->actionCollection(), "view_korganizerupdate" ); |
|
|
// FIXME (Bo): IMHO this doesn't make sense |
|
|
// new KAction( i18n("Hide Organizer"), 0, this, SLOT(slotGroupwareHide()), |
|
|
// mMainWin->actionCollection(), "view_korganizerhide_groupware" ); |
|
|
// go menu: some entries |
|
|
new KAction( i18n("Go Backward in Calendar"), "1leftarrow", 0, mKOrgPart, |
|
|
SLOT(slotGoPrevious()), mMainWin->actionCollection(), |
|
|
"go_korganizerbackward" ); |
|
|
new KAction( i18n("Go Forward in Calendar"), "1rightarrow", 0, mKOrgPart, |
|
|
SLOT(slotGoNext()), mMainWin->actionCollection(), "go_korganizerforward" ); |
|
|
new KAction( i18n("Go to Today"), "today", 0, mKOrgPart, SLOT(slotGoToday()), |
|
|
mMainWin->actionCollection(), "go_korganizertoday" ); |
|
|
// actions menu: complete menu |
|
|
new KAction( i18n("New Event"), "appointment", 0, mKOrgPart, SLOT(slotAppointment_new()), |
|
|
mMainWin->actionCollection(), "korganizeractions_new_event" ); |
|
|
new KAction( i18n("New To-Do"), "newtodo", 0, mKOrgPart, SLOT(slotNewTodo()), |
|
|
mMainWin->actionCollection(), "korganizeractions_new_todo" ); |
|
|
new KAction( i18n("New Sub-To-Do"), 0, mKOrgPart, SLOT(slotSubTodo()), |
|
|
mMainWin->actionCollection(), "korganizeractions_new_subtodo" ); |
|
|
new KAction( i18n("New Note"), 0, mKOrgPart, SLOT(slotNewNote()), |
|
|
mMainWin->actionCollection(), "korganizeractions_new_note" ); |
|
|
new KAction( i18n("Delete"), 0, mKOrgPart, SLOT(slotDeleteIncidence()), |
|
|
mMainWin->actionCollection(), "korganizeractions_new_delete" ); |
|
|
new KAction( i18n("Edit"), 0, mKOrgPart, SLOT(slotEditIncidence()), |
|
|
mMainWin->actionCollection(), "korganizeractions_edit" ); |
|
|
new KAction( i18n("Make Sub-To-Do Independent"), 0, mKOrgPart, SLOT(slotTodo_unsub()), |
|
|
mMainWin->actionCollection(), "korganizeractions_make_subtodo_independent" ); |
|
|
new KAction( i18n("Organizer Print Preview"), 0, mKOrgPart, SLOT(slotPrintPreview()), |
|
|
mMainWin->actionCollection(), "korganizeractions_printpreview" ); |
|
|
new KAction( i18n( "Organizer Print" ), 0, mKOrgPart, SLOT( slotPrint() ), |
|
|
mMainWin->actionCollection(), "korganizeractions_print" ); |
|
|
// schedule menu: complete menu |
|
|
new KAction( i18n("Publish Free Busy Information"), 0, mKOrgPart, |
|
|
SLOT(slotPublishFreeBusy()), mMainWin->actionCollection(), |
|
|
"korganizerschedule_publish_free_busy_information" ); |
|
|
// settings menu: some entries |
|
|
new KAction( i18n("Configure KOrganizer"), "korganizer", 0, mKOrgPart, |
|
|
SLOT(slotConfigure()), mMainWin->actionCollection(), |
|
|
"settings_korganizerKOrganizer" ); |
|
|
new KAction( i18n("Configure Date && Time..."), 0, |
|
|
mKOrgPart, SLOT(slotConfigureDateTime()), |
|
|
mMainWin->actionCollection(), "settings_korganizerdatetime" ); |
|
|
new KAction( i18n("Edit Filters"), 0, mKOrgPart, SLOT(slotEditFilters()), |
|
|
mMainWin->actionCollection(), "settings_korganizeredit_filters" ); |
|
|
new KAction( i18n("Edit Categories"), 0, mKOrgPart, SLOT(slotShowCategoryEditDialog()), |
|
|
mMainWin->actionCollection(), "settings_korganizeredit_categories" ); |
|
|
} |
|
|
|
|
|
emit signalMenusChanged(); |
|
|
} |
|
|
|
|
|
// find message matching a given UID and return it in msg* or trash it |
|
|
KMMessage *KMGroupware::findMessageByUID( const QString& uid, KMFolder* folder, |
|
|
bool takeMessage ) |
|
|
{ |
|
|
assert( folder ); |
|
|
|
|
|
KMMessage* msg = 0; |
|
|
KMMessage* m; |
|
|
for( int i=0; i<folder->count() && !msg; ++i ){ |
|
|
bool unget = !folder->isMessage(i); |
|
|
m = folder->getMsg( i ); |
|
|
if( m ){ |
|
|
int iDummy; |
|
|
QCString vCalOld; |
|
|
if( vPartFoundAndDecoded( m, iDummy, 0, &vCalOld ) ){ |
|
|
QString uidOld( "UID" ); |
|
|
vPartMicroParser( vCalOld, uidOld ); |
|
|
if( uidOld == uid ){ |
|
|
if( takeMessage ) { |
|
|
msg = folder->take(i); |
|
|
msg->removeHeaderField("X-UID"); |
|
|
} else |
|
|
msg = m; |
|
|
} |
|
|
} |
|
|
} |
|
|
if( !msg && unget ) |
|
|
folder->unGetMsg(i); |
|
|
} |
|
|
|
|
|
return msg; |
|
|
} |
|
|
|
|
|
void KMGroupware::deleteMsg( KMMessage *msg ) |
|
|
{ |
|
|
assert( msg ); |
|
|
QPtrList<KMMsgBase> mList; |
|
|
mList.append(msg); |
|
|
( new KMDeleteMsgCommand( msg->parent(), mList ) )->start(); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Special Contacts methods called by KMKernel's DCOP functions |
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::requestAddresses( QString fname ) |
|
|
{ |
|
|
QFile file( fname ); |
|
|
if( file.open( IO_WriteOnly ) ) { |
|
|
QTextStream ts( &file ); |
|
|
ts.setEncoding( QTextStream::UnicodeUTF8 ); |
|
|
if( mContacts ){ |
|
|
int iDummy; |
|
|
QString s; |
|
|
for( int i=0; i<mContacts->count(); ++i ){ |
|
|
bool unget = !mContacts->isMessage(i); |
|
|
if( KMGroupware::vPartFoundAndDecoded( mContacts->getMsg( i ), iDummy, &s, 0 ) ){ |
|
|
ts << s; |
|
|
s.replace('\n', "\\n"); |
|
|
s.truncate(65); |
|
|
} |
|
|
if( unget ) mContacts->unGetMsg(i); |
|
|
} |
|
|
}else{ |
|
|
kdDebug() << "+++KMGroupware::requestAddresses(): Contacts folder does not exist" << endl; |
|
|
} |
|
|
file.close(); |
|
|
}else{ |
|
|
kdDebug() << "+++KMGroupware::requestAddresses(): could not open file" << endl; |
|
|
} |
|
|
} |
|
|
|
|
|
//-------------- |
|
|
bool KMGroupware::storeAddresses( QString fname, QStringList delUIDs ) |
|
|
{ |
|
|
if( mContacts ){ |
|
|
QFile file( fname ); |
|
|
QStringList vCards; |
|
|
if( file.open( IO_ReadOnly ) ) { |
|
|
QTextStream ts( &file ); |
|
|
ts.setEncoding( QTextStream::UnicodeUTF8 ); |
|
|
QString currentVCard; |
|
|
while( !ts.eof() ) { |
|
|
QString line; |
|
|
line = ts.readLine(); |
|
|
if( line.isEmpty() ) { |
|
|
// New vCard |
|
|
vCards << currentVCard; |
|
|
currentVCard = ""; |
|
|
} else { |
|
|
// Continue current vCard |
|
|
currentVCard += line + "\r\n"; |
|
|
} |
|
|
} |
|
|
file.close(); |
|
|
}else{ |
|
|
kdDebug() << "+++KMGroupware::storeAddresses(): could not open file" << endl; |
|
|
return false; |
|
|
} |
|
|
|
|
|
for( QStringList::iterator it = delUIDs.begin(); it != delUIDs.end(); ++it ) { |
|
|
KMMessage* msg = findMessageByUID( *it, mContacts ); |
|
|
if( msg ) |
|
|
deleteMsg( msg ); |
|
|
else |
|
|
kdDebug() << "vCard not found, cannot remove: " << *it << endl; |
|
|
} |
|
|
|
|
|
for( QStringList::iterator it2 = vCards.begin(); it2 != vCards.end(); ++it2 ) { |
|
|
QCString vCard( (*it2).utf8() ); |
|
|
QString uid( "UID" ); |
|
|
QString name( "NAME" ); |
|
|
vPartMicroParser( vCard, uid, name ); |
|
|
KMMessage* msg = findMessageByUID( uid, mContacts, false ); |
|
|
if( !msg ) { |
|
|
// process a new event: |
|
|
msg = new KMMessage(); // makes a "Content-Type=text/plain" message |
|
|
msg->initHeader(); |
|
|
msg->setType( DwMime::kTypeText ); |
|
|
msg->setSubtype( DwMime::kSubtypeXVCard ); |
|
|
msg->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" ); |
|
|
msg->setSubject( "Contact" ); |
|
|
msg->setTo( name ); |
|
|
|
|
|
// add missing headers/content: |
|
|
msg->setBodyEncoded( vCard ); |
|
|
|
|
|
// mark the message as read and store it in our Contacts folder |
|
|
msg->touch(); |
|
|
mContacts->addMsg( msg ); |
|
|
} else { |
|
|
// Figure out if the contact have been changed |
|
|
int iDummy; |
|
|
QString s; |
|
|
if( vPartFoundAndDecoded( msg, iDummy, &s, 0 ) && s.utf8() != vCard ) { |
|
|
msg->setBodyEncoded( vCard ); |
|
|
msg->setTo( name ); |
|
|
} |
|
|
} |
|
|
} |
|
|
}else{ |
|
|
kdDebug() << "+++KMGroupware::storeAddresses(): Contacts folder does not exist" << endl; |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
//-------------- |
|
|
bool KMGroupware::lockContactsFolder() |
|
|
{ |
|
|
if( mContactsLocked ) |
|
|
return false; |
|
|
mContactsLocked = true; |
|
|
return true; |
|
|
} |
|
|
//-------------- |
|
|
bool KMGroupware::unlockContactsFolder() |
|
|
{ |
|
|
if( !mContactsLocked ) |
|
|
return false; |
|
|
mContactsLocked = false; |
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::slotNewOrUpdatedNote( const QString& id, const QString& geometry, const QColor& color, |
|
|
const QString& text) |
|
|
{ |
|
|
if( ignore_GroupwareDataChangeSlots ) return; |
|
|
blockSignals(true); |
|
|
KMFolder* folder = mNotes; |
|
|
KMMessage* msgNew = 0; |
|
|
|
|
|
for( int i=0; i<folder->count(); ++i ){ |
|
|
bool unget = !folder->isMessage(i); |
|
|
KMMessage* m = folder->getMsg( i ); |
|
|
if( m && (id == m->headerField("X-KOrg-Note-Id")) ) { |
|
|
msgNew = folder->take(i); |
|
|
break; |
|
|
} |
|
|
if( unget ) folder->unGetMsg(i); |
|
|
} |
|
|
|
|
|
if( !msgNew ) msgNew = new KMMessage(); |
|
|
|
|
|
msgNew->setFrom( kernel->identityManager()->defaultIdentity().fullEmailAddr() ); |
|
|
msgNew->setTo( kernel->identityManager()->defaultIdentity().fullEmailAddr() ); |
|
|
msgNew->setHeaderField("Content-Type", |
|
|
"text/plain; charset=\"utf-8\""); |
|
|
msgNew->setHeaderField("X-KOrg-Note-Id", id); |
|
|
if( !geometry.isEmpty() ) msgNew->setHeaderField("X-KOrg-Note-Geometry", geometry); |
|
|
if( color.isValid() ) msgNew->setHeaderField("X-KOrg-Note-Color", color.name() ); |
|
|
msgNew->setBodyEncoded(text.utf8()); |
|
|
folder->addMsg(msgNew); |
|
|
blockSignals(false); |
|
|
} |
|
|
|
|
|
void KMGroupware::slotDeleteNote( const QString& _id ) |
|
|
{ |
|
|
QString id = _id.stripWhiteSpace(); |
|
|
//if( ignore_GroupwareDataChangeSlots ) return; |
|
|
//blockSignals(true); |
|
|
mNotes->open(); |
|
|
for( int i=0; i<mNotes->count(); ++i ) { |
|
|
bool unget = !mNotes->isMessage(i); |
|
|
KMMessage* msg = mNotes->getMsg( i ); |
|
|
if( msg && id == msg->headerField("X-KOrg-Note-Id").stripWhiteSpace() ) { |
|
|
deleteMsg( msg ); |
|
|
break; |
|
|
} else if( unget ) |
|
|
mNotes->unGetMsg(i); |
|
|
} |
|
|
mNotes->close(); |
|
|
//blockSignals(false); |
|
|
} |
|
|
|
|
|
void internal_directlySendMessage(KMMessage* msg) |
|
|
{ |
|
|
// important: We create a composer, but don't want to show it, |
|
|
// so we can *not* call mMainWin->slotCompose(). |
|
|
KMComposeWin win( msg ); |
|
|
win.mNeverSign = true; |
|
|
win.mNeverEncrypt = true; |
|
|
win.slotSendNow(); |
|
|
//mMainWin->slotCompose( msgNew, 0 ); |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::slotNewOrUpdatedIncident( const QString& type, |
|
|
const QString& vCalNew, |
|
|
const QString& uid, |
|
|
const QStringList& recipients, |
|
|
const QString& subject ) |
|
|
{ |
|
|
if( ignore_GroupwareDataChangeSlots || vCalNew.isEmpty() ) return; |
|
|
|
|
|
KMFolder* folder = 0; |
|
|
|
|
|
if( type == "Contact" ) { |
|
|
folder = mCalendar; |
|
|
} else if( type == "Calendar" ) { |
|
|
folder = mCalendar; |
|
|
} else if( type == "Note" ) { |
|
|
folder = mNotes; |
|
|
} else if( type == "Task" ) { |
|
|
folder = mTasks; |
|
|
} else { |
|
|
assert(0); |
|
|
} |
|
|
|
|
|
const QString deleteMe( "DELETE ME:" ); |
|
|
const bool bDeleteMe = uid.startsWith( deleteMe ); |
|
|
const QString uidNew( bDeleteMe ? uid.mid(deleteMe.length()) : uid ); |
|
|
|
|
|
QStringList lEvents, lNotes, lTasks; |
|
|
|
|
|
// Calendar |
|
|
KMMessage* msgNew = findMessageByUID( uidNew, folder ); |
|
|
bool bWasOld = (msgNew != 0); |
|
|
|
|
|
// NOTE: We send *no* message to recipients |
|
|
// when user deletes an event from her Calendar. |
|
|
if( bWasOld || !bDeleteMe ){ |
|
|
QString subjectHeader; |
|
|
if( !bWasOld ){ |
|
|
// process a new event: |
|
|
msgNew = new KMMessage(); // makes a "Content-Type=text/plain" message |
|
|
msgNew->initHeader(); |
|
|
msgNew->setType( DwMime::kTypeText ); |
|
|
msgNew->setSubtype( DwMime::kSubtypeVCal ); |
|
|
msgNew->setHeaderField("Content-Type", |
|
|
"text/calendar; method=REQUEST; charset=\"utf-8\""); |
|
|
} |
|
|
if( bDeleteMe ) |
|
|
subjectHeader = i18n("DELETED: "); |
|
|
else if( bWasOld ) |
|
|
subjectHeader = i18n("UPDATE: "); |
|
|
subjectHeader.append( subject ); |
|
|
msgNew->setSubject( subjectHeader ); |
|
|
if( bWasOld && recipients.count() ){ |
|
|
msgNew->setType( DwMime::kTypeText ); |
|
|
msgNew->setSubtype( DwMime::kSubtypeVCal ); |
|
|
msgNew->setHeaderField("Content-Type", |
|
|
"text/calendar; method=REPLY; charset=\"utf-8\""); |
|
|
} |
|
|
// add missing headers/content: |
|
|
if( recipients.count() ) |
|
|
msgNew->setTo( recipients.join(",") ); |
|
|
else |
|
|
msgNew->setTo( msgNew->from() ); |
|
|
msgNew->setBodyEncoded( vCalNew.utf8() ); |
|
|
|
|
|
// send the message to the recipients, but only if there are any |
|
|
if( recipients.count() ){ |
|
|
KMMessage* msgSend = new KMMessage( *msgNew ); |
|
|
internal_directlySendMessage( msgSend ); |
|
|
} |
|
|
|
|
|
if( bDeleteMe ){ |
|
|
// we have informed any recipients and put the entry into the paper bin now |
|
|
if( bWasOld ){ |
|
|
deleteMsg( msgNew ); |
|
|
} |
|
|
}else{ |
|
|
// mark the message as read and store it in our Calendar folder |
|
|
msgNew->touch(); |
|
|
folder->addMsg( msgNew ); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
KMGroupware::VCalType KMGroupware::getVCalType( const QString &vCal ) |
|
|
{ |
|
|
// This is ugly: We can't even use vPartMicroParser() here, because |
|
|
// we are actually looking for the _second_ BEGIN: line. |
|
|
// PENDING(kalle) We might need to look for even more things here, |
|
|
// like journals. |
|
|
if( vCal.find( QRegExp( "BEGIN:\\s*VEVENT" ) ) != -1 ) |
|
|
return vCalEvent; |
|
|
else if( vCal.find( QRegExp( "BEGIN:\\s*VTODO" ) ) != -1 ) |
|
|
return vCalTodo; |
|
|
return vCalUnknown; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::processVCalRequest( const QCString& receiver, |
|
|
const QString& vCalIn, |
|
|
QString& choice ) |
|
|
{ |
|
|
ignore_GroupwareDataChangeSlots = true; |
|
|
bool inOK = false, outOK = false; |
|
|
QString outVCal; |
|
|
|
|
|
VCalType type = getVCalType( vCalIn ); |
|
|
if( type == vCalUnknown ) { |
|
|
kdDebug() << "processVCalReply called with something that is not a vCal\n"; |
|
|
return; |
|
|
} |
|
|
|
|
|
// If we are in legacy mode, and there is more than one receiver, we |
|
|
// need to ask the user which address to use |
|
|
KMMessage* msgOld = mMainWin->mainKMWidget()->headers()->currentMsg(); |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
QString fromAddress; // this variable is only used in legacy mode |
|
|
if( options.readBoolEntry( "LegacyMangleFromToHeaders", false ) ) { |
|
|
QStringList toAddresses = KMMessage::splitEmailAddrList( msgOld->to() ); |
|
|
if( toAddresses.count() <= 1 ) |
|
|
// only one address: no problem, we can spare the user the dialog |
|
|
// and just take the from address |
|
|
fromAddress = msgOld->to(); |
|
|
else { |
|
|
// We have more than one To: address and are in legacy mode. Next |
|
|
// try is to search the identities for one of the email addresses |
|
|
// in the toAddresses list. |
|
|
for( QStringList::Iterator sit = toAddresses.begin(); |
|
|
sit != toAddresses.end(); ++sit ) { |
|
|
if( KMMessage::getEmailAddr( *sit ) == |
|
|
kernel->identityManager()->defaultIdentity().emailAddr().local8Bit() ) { |
|
|
// our default identity was contained in the To: list, |
|
|
// copy that from To: to From: |
|
|
fromAddress = *sit; |
|
|
break; // We are done |
|
|
} |
|
|
} |
|
|
|
|
|
// If we still haven't found anything, we have to ask the user |
|
|
// what to do. |
|
|
if( fromAddress.isEmpty() ) { |
|
|
bool bOk; |
|
|
fromAddress = QInputDialog::getItem( i18n( "Select Address" ), |
|
|
i18n( "In order to let Outlook(tm) recognize you as the receiver, you need to indicate which one of the following addresses is your email address" ), |
|
|
toAddresses, 0, false, &bOk, |
|
|
mMainWin ); |
|
|
if( !bOk ) |
|
|
// If the user didn't select anything, just take the |
|
|
// first one so that we have something at all. |
|
|
fromAddress = toAddresses.first(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// step 1: call Organizer |
|
|
if("accept" == choice ){ |
|
|
emit signalAcceptedEvent( false, receiver, vCalIn, inOK, outVCal, outOK ); |
|
|
}else if("accept conditionally" == choice ){ |
|
|
emit signalAcceptedEvent( true, receiver, vCalIn, inOK, outVCal, outOK ); |
|
|
}else if("decline" == choice ){ |
|
|
emit signalRejectedEvent( receiver, vCalIn, inOK, outVCal, outOK ); |
|
|
}else if("check" == choice ){ |
|
|
emit signalShowCalendarView(); |
|
|
slotGroupwareShow( true ); |
|
|
// try to find out the start and end time |
|
|
QString sDtStart( "DTSTART" ); |
|
|
QString sDtEnd( "DTEND" ); |
|
|
vPartMicroParser( vCalIn.utf8(), sDtStart, sDtEnd ); |
|
|
if( !sDtStart.isEmpty() && !sDtEnd.isEmpty() ) { |
|
|
sDtStart = ISOToLocalQDateTime( sDtStart ); |
|
|
sDtEnd = ISOToLocalQDateTime( sDtEnd ); |
|
|
QDateTime start = QDateTime::fromString( sDtStart.left(sDtStart.find('@')), Qt::ISODate ); |
|
|
QDateTime end = QDateTime::fromString( sDtEnd.left( sDtEnd.find( '@')), Qt::ISODate ); |
|
|
emit signalCalendarUpdateView( start, end ); |
|
|
} |
|
|
emit signalEventRequest( receiver, vCalIn, inOK, choice, outVCal, outOK ); |
|
|
} |
|
|
// step 2: process vCal returned by Organizer |
|
|
if( outOK && mMainWin && mCalendar ){ |
|
|
// mMainWin->slotNewBodyReplyToMsg( outVCal ); |
|
|
KMMessage* msgNew = 0; |
|
|
if( msgOld ){ |
|
|
msgNew = msgOld->createReply( false, false, outVCal, false, true, TRUE ); |
|
|
|
|
|
// This is really, really, really ugly, but Outlook will only |
|
|
// understand the reply if the From: header is the same as the |
|
|
// To: header of the invitation message. |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
if( options.readBoolEntry( "LegacyMangleFromToHeaders", false ) ) |
|
|
msgNew->setFrom( fromAddress ); |
|
|
|
|
|
msgNew->setType( DwMime::kTypeText ); |
|
|
msgNew->setSubtype( DwMime::kSubtypeVCal ); |
|
|
msgNew->setHeaderField("Content-Type", |
|
|
"text/calendar; method=REPLY; charset=\"utf-8\""); |
|
|
internal_directlySendMessage( msgNew ); |
|
|
} |
|
|
if( "accept" == choice || "accept conditionally" == choice ) { |
|
|
if( type == vCalTodo ) |
|
|
// This is a task |
|
|
mMainWin->mainKMWidget()->slotMoveMsgToFolder( mTasks ); |
|
|
else |
|
|
// This is an appointment |
|
|
mMainWin->mainKMWidget()->slotMoveMsgToFolder( mCalendar ); |
|
|
} else if("decline" == choice ) |
|
|
mMainWin->mainKMWidget()->slotTrashMsg(); |
|
|
} |
|
|
slotGroupwareHide(); |
|
|
ignore_GroupwareDataChangeSlots = false; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
void KMGroupware::processVCalReply( const QCString& /*receiver*/, |
|
|
const QString& vCalIn, |
|
|
QString& choice ) |
|
|
{ |
|
|
VCalType type = getVCalType( vCalIn ); |
|
|
if( type == vCalUnknown ) { |
|
|
kdDebug() << "processVCalReply called with something that is not a vCal\n"; |
|
|
return; |
|
|
} |
|
|
|
|
|
if("enter" == choice ){ |
|
|
// step 1: call Organizer |
|
|
QString vCalOut; |
|
|
ignore_GroupwareDataChangeSlots = true; |
|
|
// emit signal... |
|
|
emit signalIncidenceAnswer( vCalIn, vCalOut ); |
|
|
|
|
|
// note: If we do not get a vCalOut back, we just store the vCalIn into mCalendar |
|
|
|
|
|
QString uid( "UID" ); |
|
|
vPartMicroParser( vCalOut.isEmpty() ? vCalIn.utf8() : vCalOut.utf8(), uid ); |
|
|
KMMessage* msgNew = findMessageByUID( uid, mCalendar ); |
|
|
if( !msgNew ){ |
|
|
// process a new event: |
|
|
msgNew = new KMMessage(); // makes a "Content-Type=text/plain" message |
|
|
msgNew->initHeader(); |
|
|
msgNew->setType( DwMime::kTypeText ); |
|
|
msgNew->setSubtype( DwMime::kSubtypeVCal ); |
|
|
msgNew->setHeaderField("Content-Type", |
|
|
"text/calendar; method=REPLY; charset=\"utf-8\""); |
|
|
if( type == vCalEvent ) |
|
|
msgNew->setSubject( "Meeting" ); |
|
|
else if( type == vCalTodo ) |
|
|
msgNew->setSubject( "Task" ); |
|
|
} |
|
|
// add missing headers/content: |
|
|
msgNew->setTo( msgNew->from() ); |
|
|
msgNew->setBodyEncoded( vCalOut.isEmpty() ? vCalIn.utf8() : vCalOut.utf8() ); |
|
|
|
|
|
// mark the message as read and store it in a folder |
|
|
msgNew->touch(); |
|
|
if( type == vCalEvent ) |
|
|
mCalendar->addMsg( msgNew ); |
|
|
else if( type == vCalTodo ) |
|
|
mTasks->addMsg( msgNew ); |
|
|
|
|
|
// step 2: inform user that Organizer was updated |
|
|
KMessageBox::information(mMainWin, (type == vCalEvent ? |
|
|
i18n("The answer was registered in your calendar.") : |
|
|
i18n("The answer was registered in your task list.")), |
|
|
QString::null, "groupwareBox"); |
|
|
ignore_GroupwareDataChangeSlots = false; |
|
|
} else if( "cancel" == choice ) { |
|
|
QString uid( "UID" ); |
|
|
QString descr("DESCRIPTION"); |
|
|
QString summary("SUMMARY"); |
|
|
|
|
|
vPartMicroParser( vCalIn.utf8(), uid, descr, summary ); |
|
|
if( type == vCalEvent ) { |
|
|
emit signalEventDeleted( uid ); |
|
|
KMessageBox::information( mMainWin, i18n("The event %1 was deleted from your calendar") |
|
|
.arg( descr) ); |
|
|
} else if( type == vCalTodo ) { |
|
|
emit signalTaskDeleted( uid ); |
|
|
KMessageBox::information( mMainWin, i18n("The task was deleted from your tasks") |
|
|
.arg( summary ) ); |
|
|
} |
|
|
} else { |
|
|
// Don't know what to do, so better not delete the mail |
|
|
return; |
|
|
} |
|
|
|
|
|
// An answer was saved, so trash the message |
|
|
mMainWin->mainKMWidget()->slotTrashMsg(); |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::isContactsFolder( KMFolder* folder ) const |
|
|
{ |
|
|
return mContacts && folder == mContacts; |
|
|
}; |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::folderSelected( KMFolder* folder ) |
|
|
{ |
|
|
bool bFound = mUseGroupware; |
|
|
if( mUseGroupware ){ |
|
|
if( folder == mCalendar ){ |
|
|
slotGroupwareShow( true ); |
|
|
emit signalShowCalendarView(); |
|
|
} |
|
|
else if( folder == mContacts ){ |
|
|
// note: We do *not* show the KOrganizer plugin here! |
|
|
// Contacts are done via KAddressbook. |
|
|
emit signalShowContactsView(); |
|
|
} |
|
|
else if( folder == mNotes ){ |
|
|
slotGroupwareShow( true ); |
|
|
emit signalShowNotesView(); |
|
|
} |
|
|
else if( folder == mTasks ){ |
|
|
slotGroupwareShow( true ); |
|
|
emit signalShowTodoView(); |
|
|
} |
|
|
else{ |
|
|
slotGroupwareHide(); |
|
|
bFound = false; |
|
|
} |
|
|
} |
|
|
return bFound; |
|
|
} |
|
|
|
|
|
|
|
|
/* View->Groupware menu */ |
|
|
void KMGroupware::slotGroupwareHide() |
|
|
{ |
|
|
if( mKOrgPart ){ |
|
|
mKOrgPart->widget()->hide(); |
|
|
mHeaders->show(); |
|
|
mReader->show(); |
|
|
if( mGroupwareIsHidingMimePartTree ){ |
|
|
mGroupwareIsHidingMimePartTree = false; |
|
|
mMimePartTree->show(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* additional groupware slots */ |
|
|
void KMGroupware::slotGroupwareShow(bool visible) |
|
|
{ |
|
|
if( mKOrgPart ){ |
|
|
if( visible ){ |
|
|
mHeaders->hide(); |
|
|
mReader->hide(); |
|
|
if( !mMimePartTree->isHidden() ){ |
|
|
mMimePartTree->hide(); |
|
|
mGroupwareIsHidingMimePartTree = true; |
|
|
} |
|
|
mKOrgPart->widget()->show(); |
|
|
} |
|
|
else |
|
|
slotGroupwareHide(); |
|
|
} |
|
|
} |
|
|
|
|
|
bool KMGroupware::eventFilter( QObject *o, QEvent *e ) const { |
|
|
if( o ) { |
|
|
if( o == mReader || o == mHeaders ) |
|
|
// When a groupware widget is shown, these two must not get any events |
|
|
return mKOrgPart->widget()->isShown(); |
|
|
|
|
|
if( o == mMainWin ) |
|
|
// Only filter keypresses from main win |
|
|
return mKOrgPart->widget()->isShown() && e->type() == QEvent::KeyPress; |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::vPartToHTML( int aUpdateCounter, const QCString& vCal, QString fname, |
|
|
bool useGroupware, QString& prefix, QString& postfix ) |
|
|
{ |
|
|
VCalType type = getVCalType( vCal ); |
|
|
if( type == vCalUnknown ) { |
|
|
kdDebug() << "Unkown incidence!\n"; |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Read the vCal |
|
|
QString sLocation( "LOCATION" ); |
|
|
QString sDtStart( "DTSTART" ); |
|
|
QString sDtEnd( "DTEND" ); |
|
|
QString sDescr( "DESCRIPTION" ); |
|
|
QString sMethod( "METHOD"); |
|
|
QString sAttendee( "ATTENDEE" ); |
|
|
QString sSummary( "SUMMARY" ); |
|
|
vPartMicroParser( vCal, sLocation, sDtStart, sDtEnd, sDescr, sMethod, sAttendee, sSummary ); |
|
|
string2HTML( sLocation ); |
|
|
while( sDescr.endsWith("\\n") ) |
|
|
sDescr.truncate( sDescr.length()-2 ); |
|
|
string2HTML( sDescr ); |
|
|
sDtStart = ISOToLocalQDateTime( sDtStart ); |
|
|
sDtEnd = ISOToLocalQDateTime( sDtEnd ); |
|
|
sDtStart = sDtStart.right( sDtStart.length() - sDtStart.find( '@' ) - 1 ) ; |
|
|
sDtEnd = sDtEnd.right( sDtEnd.length() - sDtEnd.find( '@' ) - 1 ); |
|
|
sMethod = sMethod.lower(); |
|
|
sAttendee = sAttendee.upper(); |
|
|
|
|
|
QString typeString; |
|
|
if( type == vCalEvent ) |
|
|
typeString = i18n("calendar"); |
|
|
else |
|
|
typeString = i18n("tasks"); |
|
|
|
|
|
if( sMethod == "request" ) { |
|
|
if( type == vCalEvent ) { |
|
|
if( aUpdateCounter == 0 ) |
|
|
prefix = i18n("You have been invited to a meeting"); |
|
|
else |
|
|
prefix = i18n("This is an update of a previous invitation."); |
|
|
prefix += "<br>"; |
|
|
if( !sLocation.isEmpty() ) |
|
|
prefix.append( i18n( "The meeting will take place in %1 from %2 to %3" ) |
|
|
.arg( sLocation ).arg( sDtStart ).arg( sDtEnd ) ); |
|
|
else |
|
|
prefix.append( i18n( "The meeting will take place from %1 to %2" ) |
|
|
.arg( sDtStart ) |
|
|
.arg( sDtEnd ) ); |
|
|
} else { |
|
|
prefix = i18n( "You have been assigned a task:<br>%1" ).arg( sSummary ); |
|
|
} |
|
|
} else if( sMethod == "reply" ){ |
|
|
if( 0 < sAttendee.contains("PARTSTAT=ACCEPTED") ) { |
|
|
if( type == vCalEvent ) |
|
|
prefix = i18n("Sender <b>accepts</b> the invitation to meet in %1<br>from %2 to %3.") |
|
|
.arg( sLocation ).arg( sDtStart ).arg( sDtEnd ); |
|
|
else if( type == vCalTodo ) |
|
|
prefix = i18n( "Sender <b>accepts</b> the task <b>%1</b>." ).arg(sSummary ); |
|
|
} else if( 0 < sAttendee.contains("PARTSTAT=TENTATIVE") ) { |
|
|
if( type == vCalEvent ) |
|
|
prefix = i18n("Sender <b>tentatively accepts</b> the invitation to meet in %1<br>from %2 to %3.") |
|
|
.arg( sLocation ).arg( sDtStart ).arg( sDtEnd ); |
|
|
else if( type == vCalTodo ) |
|
|
prefix = i18n( "Sender <b>tentatively accepts</b> the task <b>%1</b>." ). |
|
|
arg(sSummary ); |
|
|
} else if( 0 < sAttendee.contains("PARTSTAT=DECLINED") ) { |
|
|
if( type == vCalEvent ) |
|
|
prefix = i18n("Sender <b>declines</b> the invitation to meet in %1<br>from %2 to %3.") |
|
|
.arg( sLocation ).arg( sDtStart ).arg( sDtEnd ); |
|
|
else if( vCalTodo ) |
|
|
prefix = i18n( "Sender <b>declines</b> the task %1." ).arg( sSummary ); |
|
|
} else { |
|
|
if( type == vCalEvent ) { |
|
|
prefix = i18n("This is an unknown reply to the event in %1 from %2 to %3") |
|
|
.arg( sLocation ).arg( sDtStart ).arg( sDtEnd ); |
|
|
} else if( type == vCalTodo ) { |
|
|
prefix = i18n("This is an unknown reply to the task %1").arg(sSummary); |
|
|
} |
|
|
} |
|
|
} else if( sMethod == "cancel" ) { |
|
|
if( type == vCalEvent ) { |
|
|
prefix = i18n("The event %1 was cancelled").arg(sSummary); |
|
|
} else if( type == vCalTodo ) { |
|
|
prefix = i18n("The task %1 was cancelled").arg(sSummary); |
|
|
} |
|
|
} |
|
|
|
|
|
// show the 'buttons' (only if in groupware mode) |
|
|
if( useGroupware ) { |
|
|
prefix.append( "<br> <br> <br><table border=\"0\" cellspacing=\"0\"><tr><td> </td><td>" ); |
|
|
if( sMethod == "request" || sMethod == "update" ) { |
|
|
// Accept |
|
|
prefix.append( QString("<a href=\"kmail:groupware_vCal_request_accept#%1\"><b>") |
|
|
.arg(fname) ); |
|
|
prefix.append( i18n("[Accept]") ); |
|
|
prefix.append( QString("</b></a></td><td> </td><td>") ); |
|
|
// Accept conditionally |
|
|
prefix.append( QString("<a href=\"kmail:groupware_vCal_request_accept conditionally#%1\"><b>") |
|
|
.arg( fname ) ); |
|
|
prefix.append( i18n("[Accept cond.]") ); |
|
|
prefix.append( QString("</b></a></td><td> </td><td>") ); |
|
|
// Decline |
|
|
prefix.append( QString("<a href=\"kmail:groupware_vCal_request_decline#%1\"><b>") |
|
|
.arg( fname ) ); |
|
|
prefix.append( i18n("[Decline]") ); |
|
|
prefix.append( QString("</b></a></td><td> </td><td>" ) ); |
|
|
if( type == vCalEvent ) { |
|
|
// Check my calendar... |
|
|
prefix.append(QString("<a href=\"kmail:groupware_vCal_request_check#%1\"><b>") |
|
|
.arg(fname)); |
|
|
prefix.append(i18n("[Check my calendar...]")); |
|
|
prefix.append(QString("</b></a>")); |
|
|
} |
|
|
} else if( sMethod == "reply" ) { |
|
|
// Enter this into my calendar |
|
|
prefix.append(QString("<a href=\"kmail:groupware_vCal_reply_enter#%1\"><b>") |
|
|
.arg(fname)); |
|
|
if( type == vCalEvent ) |
|
|
prefix.append(i18n("[Enter this into my calendar]")); |
|
|
else |
|
|
prefix.append(i18n("[Enter this into my tasks]")); |
|
|
prefix.append(QString("</b></a>")); |
|
|
} else if( sMethod == "cancel" ) { |
|
|
// Cancel event from my calendar |
|
|
prefix.append( QString("<a href=\"kmail:groupware_vCal_cancel_enter#%1\"><b>") |
|
|
.arg( fname ) ); |
|
|
prefix.append( i18n("[Remove this from my calendar]")); |
|
|
prefix.append(QString("</b></a>")); |
|
|
} |
|
|
prefix.append( "</td></tr></table>" ); |
|
|
} |
|
|
|
|
|
if( sMethod == "request" || sMethod == "cancel" ) { |
|
|
sDescr.prepend( "<br> <br> <br><u>" + i18n("Description:") |
|
|
+ "</u><br><table border=\"0\"><tr><td> </td><td>" ); |
|
|
sDescr.append( "</td></tr></table>" ); |
|
|
prefix.append( sDescr ); |
|
|
} |
|
|
prefix.append(" <br> <br><u>"); |
|
|
prefix.append(i18n("Original message:")); |
|
|
prefix.append("</u><br><table border=\"0\"><tr><td> </td><td>"); |
|
|
// postfix: |
|
|
postfix = "</td></tr></table>"; |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
QString attendeeLine( const QString& name, const QString& mail, bool bIsRSVP, |
|
|
bool bIsReply, bool bAccepted, bool bAcceptedCond, bool bDeclined ) |
|
|
{ |
|
|
QString line( "ATTENDEE;" ); |
|
|
if( !name.isEmpty() ){ |
|
|
line.append( "CN=\"" ); |
|
|
line.append( name ); |
|
|
line.append( "\";" ); |
|
|
} |
|
|
line.append( "RSVP=" ); |
|
|
line.append( bIsRSVP ? "TRUE" : "FALSE" ); |
|
|
line.append( ";" ); |
|
|
if( bIsReply ){ |
|
|
line.append( "PARTSTAT=" ); |
|
|
if( bAccepted ) |
|
|
line.append( "ACCEPTED" ); |
|
|
else if( bAcceptedCond ) |
|
|
line.append( "TENTATIVE" ); |
|
|
else if( bDeclined ) |
|
|
line.append( "DECLINED" ); |
|
|
else |
|
|
line.append( "TENTATIVE" ); // use this as fallback ? (khz, 2002/10/16) |
|
|
line.append( ':' ); |
|
|
}else{ |
|
|
line.append( "PARTSTAT=NEEDS-ACTION;" ); |
|
|
line.append( "ROLE=REQ-PARTICIPANT;" ); |
|
|
} |
|
|
line.append( "MAILTO:" ); |
|
|
line.append( mail ); |
|
|
return line; |
|
|
} |
|
|
|
|
|
QString stringProp( KTNEFMessage* tnefMsg, const QString& prefix, const QString& title, |
|
|
const Q_UINT32& key, const QString& fallback ) |
|
|
{ |
|
|
QString res; |
|
|
QString value( tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16, |
|
|
fallback ) ); |
|
|
if( !value.isEmpty() ){ |
|
|
res = prefix; |
|
|
if( !title.isEmpty() ){ |
|
|
res.append( title ); |
|
|
res.append( ':' ); |
|
|
} |
|
|
res.append( value ); |
|
|
} |
|
|
return res; |
|
|
} |
|
|
|
|
|
QString sNamedProp( KTNEFMessage* tnefMsg, const QString& prefix, const QString& title, |
|
|
const QString& name, const QString& fallback ) |
|
|
{ |
|
|
QString res; |
|
|
QString value( tnefMsg->findNamedProp( name, fallback ) ); |
|
|
if( !value.isEmpty() ){ |
|
|
res = prefix; |
|
|
if( !title.isEmpty() ){ |
|
|
res.append( title ); |
|
|
res.append( ':' ); |
|
|
} |
|
|
res.append( value ); |
|
|
} |
|
|
return res; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::msTNEFToVPart( const QByteArray& tnef, |
|
|
int& aUpdateCounter, |
|
|
QString* aVPart, |
|
|
QCString* aVPartc ) |
|
|
{ |
|
|
// Note: vPart is not erased but |
|
|
// keeps it's initial data if it cannot be decoded |
|
|
bool bOk = false; |
|
|
QString vPart; |
|
|
|
|
|
KTNEFParser parser; |
|
|
QBuffer buf( tnef ); |
|
|
if( parser.openDevice( &buf ) ) |
|
|
{ |
|
|
KTNEFMessage* tnefMsg = parser.message(); |
|
|
//QMap<int,KTNEFProperty*> props = parser.message()->properties(); |
|
|
|
|
|
// everything depends from property PR_MESSAGE_CLASS |
|
|
// (this is added by KTNEFParser): |
|
|
QString msgClass = tnefMsg->findProp(0x001A, "", true).upper(); |
|
|
if( !msgClass.isEmpty() ){ |
|
|
// Match the old class names that might be used by Outlook for |
|
|
// compatibility with Microsoft Mail for Windows for Workgroups 3.1. |
|
|
bool bCompatClassAppointment = false; |
|
|
bool bCompatMethodRequest = false; |
|
|
bool bCompatMethodCancled = false; |
|
|
bool bCompatMethodAccepted = false; |
|
|
bool bCompatMethodAcceptedCond = false; |
|
|
bool bCompatMethodDeclined = false; |
|
|
if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ){ |
|
|
bCompatClassAppointment = true; |
|
|
if( msgClass.endsWith( ".MTGREQ" ) ) |
|
|
bCompatMethodRequest = true; |
|
|
if( msgClass.endsWith( ".MTGCNCL" ) ) |
|
|
bCompatMethodCancled = true; |
|
|
if( msgClass.endsWith( ".MTGRESPP" ) ) |
|
|
bCompatMethodAccepted = true; |
|
|
if( msgClass.endsWith( ".MTGRESPA" ) ) |
|
|
bCompatMethodAcceptedCond = true; |
|
|
if( msgClass.endsWith( ".MTGRESPN" ) ) |
|
|
bCompatMethodDeclined = true; |
|
|
} |
|
|
bool bCompatClassNote = (msgClass == "IPM.MICROSOFT MAIL.NOTE"); |
|
|
|
|
|
if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ){ |
|
|
|
|
|
// retrieve the update counter |
|
|
aUpdateCounter = tnefMsg->findNamedProp("0x8201", "0").toInt(); |
|
|
// compose a vCal |
|
|
bool bIsReply = false; |
|
|
vPart = "BEGIN:VCALENDAR\n"; |
|
|
vPart += "PRODID:-//Microsoft Corporation//Outlook "; |
|
|
vPart += tnefMsg->findNamedProp("0x8554", "9.0"); |
|
|
vPart += "MIMEDIR/EN\n"; |
|
|
vPart += "VERSION:2.0\n"; |
|
|
|
|
|
vPart += "METHOD:"; |
|
|
if( bCompatMethodRequest ) |
|
|
vPart += "REQUEST"; |
|
|
else if( bCompatMethodCancled ) |
|
|
vPart += "CANCEL"; |
|
|
else if( bCompatMethodAccepted || |
|
|
bCompatMethodAcceptedCond || |
|
|
bCompatMethodDeclined ){ |
|
|
bIsReply = true; |
|
|
vPart += "REPLY"; |
|
|
} |
|
|
else{ |
|
|
// pending(khz): verify whether "0x0c17" is the right tag ??? |
|
|
// |
|
|
// at the moment we think there are REQUESTS and UPDATES |
|
|
// |
|
|
// but WHAT ABOUT REPLIES ??? |
|
|
// |
|
|
// |
|
|
if( tnefMsg->findProp(0x0c17, "") == "1" ) |
|
|
bIsReply = true; |
|
|
vPart += "REQUEST"; |
|
|
} |
|
|
vPart += '\n'; |
|
|
|
|
|
QString sSenderSearchKeyEmail( tnefMsg->findProp(0x0C1D, "") ); |
|
|
if( !sSenderSearchKeyEmail.isEmpty() ){ |
|
|
int colon = sSenderSearchKeyEmail.find(':'); |
|
|
if( -1 < colon ) // may be e.g. "SMTP:KHZ@KDE.ORG" |
|
|
sSenderSearchKeyEmail.remove(0, colon+1); |
|
|
} |
|
|
|
|
|
vPart += "BEGIN:VEVENT\n"; |
|
|
QString s( tnefMsg->findProp(0x0e04, "") ); |
|
|
QStringList attendees( QStringList::split(';', s) ); |
|
|
if( attendees.count() ){ |
|
|
for ( QStringList::Iterator it = attendees.begin(); it != attendees.end(); ++it ) { |
|
|
// skip all entries that have no '@' since these are no mail addresses |
|
|
if( -1 < (*it).find('@') ){ |
|
|
s = (*it).stripWhiteSpace(); |
|
|
vPart += attendeeLine( QString(), |
|
|
s, |
|
|
true, |
|
|
bIsReply, |
|
|
bCompatMethodAccepted, |
|
|
bCompatMethodAcceptedCond, |
|
|
bCompatMethodDeclined ); |
|
|
vPart += '\n'; |
|
|
} |
|
|
} |
|
|
}else{ |
|
|
// Oops, no attendees? |
|
|
// This must be old style, let us use the PR_SENDER_SEARCH_KEY. |
|
|
s = sSenderSearchKeyEmail; |
|
|
if( !s.isEmpty() ){ |
|
|
vPart += attendeeLine( QString(), |
|
|
s, |
|
|
true, |
|
|
bIsReply, |
|
|
bCompatMethodAccepted, |
|
|
bCompatMethodAcceptedCond, |
|
|
bCompatMethodDeclined ); |
|
|
vPart += '\n'; |
|
|
} |
|
|
} |
|
|
s = tnefMsg->findProp(0x0c1f, ""); // look for organizer property |
|
|
if( s.isEmpty() && !bIsReply ) |
|
|
s = sSenderSearchKeyEmail; |
|
|
if( !s.isEmpty() ){ |
|
|
vPart += "ORGANIZER;MAILTO:"; |
|
|
vPart += s; |
|
|
vPart += '\n'; |
|
|
} |
|
|
s = tnefMsg->findNamedProp("0x8516", "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), ""); |
|
|
vPart += "DTSTART:"; |
|
|
vPart += s; |
|
|
vPart += "Z\n"; |
|
|
s = tnefMsg->findNamedProp("0x8517", "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), ""); |
|
|
vPart += "DTEND:"; |
|
|
vPart += s; |
|
|
vPart += "Z\n"; |
|
|
vPart += "LOCATION:"; |
|
|
vPart += tnefMsg->findNamedProp("0x8208", ""); |
|
|
vPart += '\n'; |
|
|
|
|
|
// is it OK to set this to OPAQUE always ?? |
|
|
vPart += "TRANSP:OPAQUE\n"; |
|
|
|
|
|
vPart += "SEQUENCE:0\n"; |
|
|
|
|
|
// is "0x0023" OK - or should we look for "0x0003" ?? |
|
|
vPart += "UID:"; |
|
|
vPart += tnefMsg->findNamedProp("0x0023", ""); |
|
|
vPart += '\n'; |
|
|
|
|
|
vPart += "DTSTAMP:"; |
|
|
// pending(khz): is this value in local timezone ?? must it be adjusted ?? |
|
|
// most likely this is a bug in the server or in Outlook - we ignore it for now. |
|
|
vPart += tnefMsg->findNamedProp("0x8202", "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), ""); |
|
|
vPart += '\n'; |
|
|
|
|
|
vPart += "CATEGORIES:"; |
|
|
vPart += tnefMsg->findNamedProp("Keywords", ""); |
|
|
vPart += '\n'; |
|
|
vPart += "DESCRIPTION:"; |
|
|
vPart += tnefMsg->findProp(0x1000, ""); |
|
|
vPart += '\n'; |
|
|
vPart += "SUMMARY:"; |
|
|
vPart += tnefMsg->findProp(0x0070, ""); |
|
|
vPart += '\n'; |
|
|
|
|
|
vPart += "PRIORITY:"; |
|
|
s = tnefMsg->findProp(0x0026, ""); |
|
|
if( "1" == s ) |
|
|
vPart += "URGENT"; |
|
|
else if( ("0" == s) || ("-1" == s) ) |
|
|
vPart += "NORMAL"; |
|
|
vPart += '\n'; |
|
|
|
|
|
// is reminder flag set ? |
|
|
if( "TRUE" == tnefMsg->findProp(0x8503, "").upper() ){ |
|
|
vPart += "CLASS:PUBLIC\n"; |
|
|
vPart += "BEGIN:VALARM\n"; |
|
|
QDateTime highNoonTime( |
|
|
pureISOToLocalQDateTime( tnefMsg->findProp(0x8502, "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), "") ) ); |
|
|
QDateTime wakeMeUpTime( |
|
|
pureISOToLocalQDateTime( tnefMsg->findProp(0x8560, "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), "") ) ); |
|
|
vPart += "TRIGGER:PT"; |
|
|
if( highNoonTime.isValid() && wakeMeUpTime.isValid() ) |
|
|
vPart += QString::number( wakeMeUpTime.secsTo( highNoonTime ) / 60 ); |
|
|
else |
|
|
vPart += "15"; // default: wake them up 15 minutes before the appointment |
|
|
vPart += "M\n"; |
|
|
|
|
|
// sorry: the different action types are not known (yet) |
|
|
// so we allways set 'DISPLAY' (no sounds, no images...) |
|
|
vPart += "ACTION:DISPLAY\n"; |
|
|
vPart += "DESCRIPTION:"; |
|
|
vPart += i18n("Reminder"); |
|
|
vPart += '\n'; |
|
|
vPart += "END:VALARM\n"; |
|
|
} |
|
|
vPart += "END:VEVENT\n"; |
|
|
vPart += "END:CALENDAR\n"; |
|
|
bOk = true; |
|
|
// we finished composing a vCal |
|
|
|
|
|
}else if( bCompatClassNote || "IPM.CONTACT" == msgClass ){ |
|
|
|
|
|
vPart = stringProp(tnefMsg, "\n","UID", attMSGID, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","FN", MAPI_TAG_PR_DISPLAY_NAME, "" ); |
|
|
vPart += sNamedProp(tnefMsg, "\n","EMAIL", MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS, "" ); |
|
|
vPart += sNamedProp(tnefMsg, "\n","EMAIL", MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS, "" ); |
|
|
vPart += sNamedProp(tnefMsg, "\n","EMAIL", MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS, "" ); |
|
|
vPart += sNamedProp(tnefMsg, "\n","X-KADDRESSBOOK-X-IMAddress", MAPI_TAG_CONTACT_IMADDRESS, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-SpousesName", MAPI_TAG_PR_SPOUSE_NAME, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-ManagersName", MAPI_TAG_PR_MANAGER_NAME, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-AssistantsName", MAPI_TAG_PR_ASSISTANT, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-Department", MAPI_TAG_PR_DEPARTMENT_NAME, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-Office", MAPI_TAG_PR_OFFICE_LOCATION, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","X-KADDRESSBOOK-X-Profession", MAPI_TAG_PR_PROFESSION, "" ); |
|
|
QString s( tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY, "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), "") ); |
|
|
if( !s.isEmpty() ){ |
|
|
vPart += "\nX-KADDRESSBOOK-X-Anniversary:"; |
|
|
vPart += s; |
|
|
} |
|
|
vPart += sNamedProp(tnefMsg, "\n","URL", MAPI_TAG_CONTACT_WEBPAGE, "" ); |
|
|
// collect parts of Name entry |
|
|
s = stringProp(tnefMsg, "","", MAPI_TAG_PR_SURNAME, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_GIVEN_NAME, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_MIDDLE_NAME, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_DISPLAY_NAME_PREFIX, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_GENERATION, "" ); |
|
|
if( s != ";;;;" ){ |
|
|
vPart += "\nN:"; |
|
|
vPart += s; |
|
|
} |
|
|
vPart += stringProp(tnefMsg, "\n","NICKNAME", MAPI_TAG_PR_NICKNAME, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","ROLE", MAPI_TAG_PR_TITLE, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","ORG", MAPI_TAG_PR_COMPANY_NAME, "" ); |
|
|
/* |
|
|
the MAPI property ID of this (multiline) )field is unknown: |
|
|
vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" ); |
|
|
*/ |
|
|
|
|
|
s = stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_PO_BOX, "" ); |
|
|
s += ";"; |
|
|
//s += stringProp(tnefMsg, "","", don't know, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_STREET, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_CITY, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_HOME_ADDRESS_COUNTRY, "" ); |
|
|
// note: If no HOME address properties were found |
|
|
// we use the POSTAL address as home address. |
|
|
if( s == ";;;;;;" ){ |
|
|
s = stringProp(tnefMsg, "","", MAPI_TAG_PR_PO_BOX, "" ); |
|
|
s += ";"; |
|
|
//s += stringProp(tnefMsg, "","", don't know, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_STREET_ADDRESS, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_LOCALITY, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_STATE_OR_PROVINCE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_POSTAL_CODE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_STATE_OR_PROVINCE, "" ); |
|
|
} |
|
|
if( s != ";;;;;;" ){ |
|
|
vPart += "\nADR;TYPE=home:"; |
|
|
vPart += s; |
|
|
} |
|
|
s = sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX, "" ); |
|
|
s += ";"; |
|
|
//s += sNamedProp(tnefMsg, "","", don't know, "" ); |
|
|
s += ";"; |
|
|
s += sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET, "" ); |
|
|
s += ";"; |
|
|
s += sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSCITY, "" ); |
|
|
s += ";"; |
|
|
s += sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE, "" ); |
|
|
s += ";"; |
|
|
s += sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE, "" ); |
|
|
s += ";"; |
|
|
s += sNamedProp(tnefMsg, "","", MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY, "" ); |
|
|
if( s != ";;;;;;" ){ |
|
|
vPart += "\nADR;TYPE=work:"; |
|
|
vPart += s; |
|
|
} |
|
|
s = stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX, "" ); |
|
|
s += ";"; |
|
|
//s += stringProp(tnefMsg, "","", don't know, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_STREET, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_CITY, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE, "" ); |
|
|
s += ";"; |
|
|
s += stringProp(tnefMsg, "","", MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY, "" ); |
|
|
if( s != ";;;;;;" ){ |
|
|
vPart += "\nADR;TYPE=dom:"; |
|
|
vPart += s; |
|
|
} |
|
|
// problem: the 'other' address was stored by KOrganizer in |
|
|
// a line looking like the following one: |
|
|
// vPart += "\nADR;TYPE=dom;TYPE=intl;TYPE=parcel;TYPE=postal;TYPE=work;TYPE=home:other_pobox;;other_str1\nother_str2;other_loc;other_region;other_pocode;other_country |
|
|
|
|
|
vPart += stringProp(tnefMsg, "\n","TEL;TYPE=home", MAPI_TAG_PR_HOME_TELEPHONE_NUMBER, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","TEL;TYPE=work", MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","TEL;TYPE=cell", MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","TEL;TYPE=home;TYPE=fax", MAPI_TAG_PR_HOME_FAX_NUMBER, "" ); |
|
|
vPart += stringProp(tnefMsg, "\n","TEL;TYPE=work;TYPE=fax", MAPI_TAG_PR_BUSINESS_FAX_NUMBER, "" ); |
|
|
s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY, "") |
|
|
.replace(QChar('-'), "") |
|
|
.replace(QChar(':'), ""); |
|
|
if( !s.isEmpty() ){ |
|
|
vPart += "\nBDAY:"; |
|
|
vPart += s; |
|
|
} |
|
|
// add the vPart's header and footer |
|
|
if( !vPart.isEmpty() ){ |
|
|
vPart.prepend("BEGIN:VCARD" |
|
|
"\nVERSION:3.0" ); |
|
|
vPart.append( "\nCLASS:PRIVATE" |
|
|
"\nEND:VCARD" ); |
|
|
bOk = true; |
|
|
} |
|
|
}else if( "IPM.NOTE" == msgClass ){ |
|
|
|
|
|
} // else if ... and so on ... |
|
|
} |
|
|
} |
|
|
if( aVPart ) |
|
|
*aVPart = vPart; |
|
|
if( aVPartc ) |
|
|
*aVPartc = vPart.utf8(); |
|
|
return bOk; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::msTNEFToHTML( KMReaderWin* reader, |
|
|
QString& vPart, QString fname, |
|
|
bool useGroupware, |
|
|
QString& prefix, QString& postfix ) |
|
|
{ |
|
|
QByteArray tnef( kFileToBytes( fname, false ) ); |
|
|
if( tnef.count() ) { |
|
|
int updateCounter; |
|
|
QCString vPartc; |
|
|
if( msTNEFToVPart( tnef, updateCounter, &vPart, &vPartc ) ){ |
|
|
QByteArray theBody( vPartc ); |
|
|
QString fname2( KMReaderWin::byteArrayToTempFile( reader, |
|
|
"groupware", |
|
|
"vPart_decoded.raw", |
|
|
theBody ) ); |
|
|
if( !fname2.isEmpty() ) |
|
|
return vPartToHTML( updateCounter, vPartc, fname2, |
|
|
useGroupware, prefix, postfix ); |
|
|
} |
|
|
}else{ |
|
|
KMessageBox::error(0, i18n("Unable to open file %1").arg(fname)); |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
bool KMGroupware::foundGroupwareLink( const QString aUrl, QString& gwType, QString& gwAction, |
|
|
QString& gwAction2, QString& gwData ) |
|
|
{ |
|
|
static QString gwPrefix("groupware_"); |
|
|
gwType = ""; |
|
|
gwAction = ""; |
|
|
gwAction2 = ""; |
|
|
gwData = ""; |
|
|
|
|
|
int i1 = aUrl.find( gwPrefix ); |
|
|
if( -1 < i1 ) { |
|
|
i1 += gwPrefix.length(); |
|
|
|
|
|
int i2 = aUrl.find("_", i1); |
|
|
if( i1 <= i2 ) |
|
|
{ |
|
|
// retrieve gwType |
|
|
gwType = aUrl.mid( i1, i2-i1 ); |
|
|
i1 = i2+1; |
|
|
i2 = aUrl.find("_", i1); |
|
|
if( i1 <= i2 ) |
|
|
{ |
|
|
// retrieve gwAction |
|
|
gwAction = aUrl.mid( i1, i2-i1 ); |
|
|
i1 = i2+1; |
|
|
i2 = aUrl.find("#", i1); |
|
|
if( i1 <= i2 ) |
|
|
{ |
|
|
// retrieve gwAction2 |
|
|
gwAction2 = aUrl.mid( i1, i2-i1 ); |
|
|
i2 += 1; |
|
|
// retrieve gwData |
|
|
gwData = aUrl.mid( i2 ); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return !gwType.isEmpty(); |
|
|
} |
|
|
|
|
|
|
|
|
bool KMGroupware::handleLink( const KURL &aUrl, KMMessage* msg ) |
|
|
{ |
|
|
QString gwType, gwAction, gwAction2, gwData; |
|
|
|
|
|
if( !aUrl.hasRef() || !foundGroupwareLink( aUrl.path()+"#"+aUrl.ref(), gwType, |
|
|
gwAction, gwAction2, gwData ) ) |
|
|
// No groupware link to handle here |
|
|
return false; |
|
|
|
|
|
if( gwType != "vCal" || gwData.isEmpty() |
|
|
|| ( "request" != gwAction && "reply" != gwAction && "cancel" != gwAction ) ) |
|
|
// Then we can't handle it. But it is a groupware link, so we return true |
|
|
return true; |
|
|
|
|
|
// Read the vCal |
|
|
QFile file( gwData ); |
|
|
if( !file.open( IO_ReadOnly ) ) { |
|
|
kdDebug() << "Could not open file " << gwData << endl; |
|
|
return true; |
|
|
} |
|
|
QTextStream ts( &file ); |
|
|
QString vCal = ts.read(); |
|
|
file.close(); |
|
|
|
|
|
// Find the receiver if we can |
|
|
QCString receiver; |
|
|
if( msg ) |
|
|
receiver = KMMessage::getEmailAddr( msg->to() ); |
|
|
|
|
|
if( "request" == gwAction ) |
|
|
processVCalRequest( receiver, vCal, gwAction2 ); |
|
|
else if( "reply" == gwAction ) |
|
|
processVCalReply( receiver, vCal, gwAction2 ); |
|
|
else if( "cancel" == gwAction ) |
|
|
/* Note, we pass gwAction here, not gwAction2 */ |
|
|
processVCalReply( receiver, vCal, gwAction ); |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
/*! |
|
|
This method handles incoming resource requests. It sends them off to |
|
|
KOrganizer for answering, records the result and sends an answer |
|
|
back. |
|
|
*/ |
|
|
bool KMGroupware::incomingResourceMessage( KMAccount* acct, KMMessage* msg ) |
|
|
{ |
|
|
if( !mUseGroupware) |
|
|
return false; |
|
|
|
|
|
int updateCounter; |
|
|
QString vCalIn; |
|
|
if( vPartFoundAndDecoded( msg, updateCounter, &vCalIn, 0 ) ) |
|
|
return false; |
|
|
|
|
|
bool vCalInOK, vCalOutOK, isFree; |
|
|
QString vCalOut; |
|
|
QDateTime start, end; |
|
|
emit( signalResourceRequest( acct->intervals(), KMMessage::getEmailAddr( msg->to() ), |
|
|
vCalIn, vCalInOK, vCalOut, vCalOutOK, isFree, start, end ) ); |
|
|
if( !vCalInOK || !vCalOutOK ) |
|
|
return false; // parsing or generation error somewhere |
|
|
|
|
|
// Check whether we are supposed to answer automatically at all |
|
|
KConfigGroup options( KMKernel::config(), "Groupware" ); |
|
|
if( isFree && options.readBoolEntry( "AutoAccept", false ) ) |
|
|
return false; |
|
|
if( !isFree && options.readBoolEntry( "AutoDeclConflict", false ) ) |
|
|
return false; |
|
|
|
|
|
// Everything went fine so far, now attach the answer |
|
|
KMMessage* msgNew = 0; |
|
|
if( msg ){ |
|
|
msgNew = msg->createReply( false, false, vCalOut, false, true, TRUE ); |
|
|
msgNew->setType( DwMime::kTypeText ); |
|
|
msgNew->setSubtype( DwMime::kSubtypeVCal ); |
|
|
msgNew->setHeaderField("Content-Type", "text/calendar; method=REPLY; charset=\"utf-8\""); |
|
|
internal_directlySendMessage( msgNew ); |
|
|
} |
|
|
|
|
|
// And also record in the account. |
|
|
acct->addInterval( qMakePair( start, end ) ); |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
/*! |
|
|
This method checks whether the folder is one of Calendar, Notes, and |
|
|
Tasks and informs KOrganizer accordingly about the deleted object. |
|
|
*/ |
|
|
void KMGroupware::msgRemoved( KMFolder* folder, KMMessage* msg ) |
|
|
{ |
|
|
assert( msg ); |
|
|
assert( msg->isMessage() ); |
|
|
|
|
|
int iDummy; |
|
|
QCString vCal; |
|
|
|
|
|
// Let's try for a note |
|
|
QString noteId = msg->headerField( "X-KOrg-Note-Id" ); |
|
|
if( !noteId.isEmpty() ) { |
|
|
kdDebug() << "%%% Deleting note with id: " << noteId << endl; |
|
|
emit signalNoteDeleted( noteId ); |
|
|
} if( vPartFoundAndDecoded( msg, iDummy, 0, &vCal ) ) { |
|
|
QString uid( "UID" ); |
|
|
vPartMicroParser( vCal, uid ); |
|
|
if( !uid.isEmpty() ){ |
|
|
// We have found something with an UID, now tell KOrganizer if |
|
|
// this was a relevant folder. |
|
|
if( folder == mCalendar ) |
|
|
emit signalEventDeleted( uid ); |
|
|
else if( folder == mTasks ) |
|
|
emit signalTaskDeleted( uid ); |
|
|
} |
|
|
} else |
|
|
kdDebug() << "%%% Unknown groupware deletion\n"; |
|
|
} |
|
|
|
|
|
|
|
|
void KMGroupware::loadPixmaps() const |
|
|
{ |
|
|
static bool pixmapsLoaded = false; |
|
|
|
|
|
if( mUseGroupware && !pixmapsLoaded ) { |
|
|
pixmapsLoaded = true; |
|
|
pixContacts = new QPixmap( UserIcon("kmgroupware_folder_contacts")); |
|
|
pixCalendar = new QPixmap( UserIcon("kmgroupware_folder_calendar")); |
|
|
pixNotes = new QPixmap( UserIcon("kmgroupware_folder_notes")); |
|
|
pixTasks = new QPixmap( UserIcon("kmgroupware_folder_tasks")); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#include "kmgroupware.moc"
|
|
|
|