/*
    This file is part of KMail.

    Copyright (c) 2003 Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
    Copyright (c) 2003 - 2004 Bo Thorsen <bo@sonofthor.dk>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "kmailicalifaceimpl.h"
#include "kmfoldertree.h"
#include "kmfolderdir.h"
#include "kmgroupware.h"
#include "kmfoldermgr.h"
#include "kmcommands.h"
#include "kmfolderindex.h"
#include "kmmsgdict.h"
#include "kmmsgpart.h"
#include "kmfolderimap.h"
#include "globalsettings.h"
#include "kmacctmgr.h"
#include "acljobs.h"
#include "kmfoldercachedimap.h"

#include <mimelib/enum.h>
#include <mimelib/utility.h>
#include <mimelib/body.h>
#include <mimelib/mimepp.h>

#include <kdebug.h>
#include <kiconloader.h>
#include <dcopclient.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kurl.h>
#include <qmap.h>
#include <ktempfile.h>
#include <qfile.h>
#include <qdom.h>
#include <qtextcodec.h>
#include "kmfoldercachedimap.h"

// Local helper methods
static void vPartMicroParser( const QString& str, QString& s );
static void reloadFolderTree();

// The index in this array is the KMail::FolderContentsType enum
static const struct {
  const char* contentsTypeStr; // the string used in the DCOP interface
  const char* mimetype;
  KFolderTreeItem::Type treeItemType;
  const char* annotation;
  const char* translatedName;
} s_folderContentsType[] = {
  { "Mail", "application/x-vnd.kolab.mail", KFolderTreeItem::Other, "mail", I18N_NOOP( "Mail" ) },
  { "Calendar", "application/x-vnd.kolab.event", KFolderTreeItem::Calendar, "event", I18N_NOOP( "Calendar" ) },
  { "Contact", "application/x-vnd.kolab.contact", KFolderTreeItem::Contacts, "contact", I18N_NOOP( "Contacts" ) },
  { "Note", "application/x-vnd.kolab.note", KFolderTreeItem::Notes, "note", I18N_NOOP( "Notes" ) },
  { "Task", "application/x-vnd.kolab.task", KFolderTreeItem::Tasks, "task", I18N_NOOP( "Tasks" ) },
  { "Journal", "application/x-vnd.kolab.journal", KFolderTreeItem::Journals, "journal", I18N_NOOP( "Journal" ) }
};

static QString folderContentsType( KMail::FolderContentsType type )
{
  return s_folderContentsType[type].contentsTypeStr;
}

static QString folderKolabMimeType( KMail::FolderContentsType type )
{
  return s_folderContentsType[type].mimetype;
}

static KMail::FolderContentsType folderContentsType( const QString& type )
{
  for ( uint i = 0 ; i < sizeof s_folderContentsType / sizeof *s_folderContentsType; ++i )
    if ( type == s_folderContentsType[i].contentsTypeStr )
      return static_cast<KMail::FolderContentsType>( i );
  return KMail::ContentsTypeMail;
}

static QString localizedDefaultFolderName( KMail::FolderContentsType type )
{
  return i18n( s_folderContentsType[type].translatedName );
}

const char* KMailICalIfaceImpl::annotationForContentsType( KMail::FolderContentsType type )
{
  return s_folderContentsType[type].annotation;
}

/*
  This interface has three parts to it - libkcal interface;
  kmail interface; and helper functions.

  The libkcal interface and the kmail interface have the same three
  methods: add, delete and refresh. The only difference is that the
  libkcal interface is used from the IMAP resource in libkcal and
  the kmail interface is used from the groupware object in kmail.
*/

KMailICalIfaceImpl::KMailICalIfaceImpl()
  : DCOPObject( "KMailICalIface" ), QObject( 0, "KMailICalIfaceImpl" ),
    mContacts( 0 ), mCalendar( 0 ), mNotes( 0 ), mTasks( 0 ), mJournals( 0 ),
    mFolderLanguage( 0 ), mFolderParentDir( 0 ), mFolderType( KMFolderTypeUnknown ),
    mUseResourceIMAP( false ), mResourceQuiet( false ), mHideFolders( true )
{
  // Listen to config changes
  connect( kmkernel, SIGNAL( configChanged() ), this, SLOT( readConfig() ) );
  connect( kmkernel, SIGNAL( folderRemoved( KMFolder* ) ),
           this, SLOT( slotFolderRemoved( KMFolder* ) ) );

  mExtraFolders.setAutoDelete( true );
}

// Receive an iCal or vCard from the resource
bool KMailICalIfaceImpl::addIncidence( const QString& type,
                                       const QString& folder,
                                       const QString& uid,
                                       const QString& ical )
{
  kdDebug(5006) << "KMailICalIfaceImpl::addIncidence( " << type << ", "
                << uid << ", " << ical << " )" << endl;

  if( !mUseResourceIMAP )
    return false;

  // Find the folder
  KMFolder* f = folderFromType( type, folder );
  if( !f ) {
    kdError(5006) << "addIncidence(" << type << "," << folder << ") : Not an IMAP resource folder" << endl;
    return false;
  }
  if ( storageFormat( f ) != StorageIcalVcard ) {
    kdError(5006) << "addIncidence(" << type << "," << folder << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return false;
  }

  bool rc = false;
  bool quiet = mResourceQuiet;
  mResourceQuiet = true;
  // Make a new message for the incidence
  KMMessage* msg = new KMMessage();
  msg->initHeader();
  msg->setType( DwMime::kTypeText );
  if( f == mContacts ) {
    msg->setSubtype( DwMime::kSubtypeXVCard );
    msg->setHeaderField( "Content-Type", "Text/X-VCard; charset=\"utf-8\"" );
    msg->setSubject( "vCard " + uid );
  } else {
    msg->setSubtype( DwMime::kSubtypeVCal );
    msg->setHeaderField("Content-Type",
                        "text/calendar; method=REQUEST; charset=\"utf-8\"");
    msg->setSubject( "iCal " + uid );
  }
  msg->setBodyEncoded( ical.utf8() );

  // Mark the message as read and store it in the folder
  msg->touch();
  msg->setComplete( true );
  f->addMsg( msg );

  rc = true;

  addFolderChange( f, Contents );

  mResourceQuiet = quiet;
  return rc;
}

// Helper function to find an attachment of a given mimetype
// Can't use KMMessage::findDwBodyPart since it only works with known mimetypes.
static DwBodyPart* findBodyPartByMimeType( const KMMessage& msg, const char* sType, const char* sSubtype, bool startsWith = false )
{
  // quickly searching for our message part: since Kolab parts are
  // top-level parts we do *not* have to travel into embedded multiparts
  DwBodyPart* part = msg.getFirstDwBodyPart();
  while( part ){
    //  kdDebug() << part->Headers().ContentType().TypeStr().c_str() << " "
    //          << part->Headers().ContentType().SubtypeStr().c_str() << endl;
    if ( part->hasHeaders() ) {
      DwMediaType& contentType = part->Headers().ContentType();
      if ( startsWith ) {
        if ( contentType.TypeStr() == sType
             && QString( contentType.SubtypeStr().c_str() ).startsWith( sSubtype ) )
          return part;
      }
      else
        if ( contentType.TypeStr() == sType
             && contentType.SubtypeStr() == sSubtype )
          return part;
    }
    part = part->Next();
  }
  return 0;
}

// Helper function to find an attachment with a given filename
static DwBodyPart* findBodyPart( const KMMessage& msg, const QString& attachmentName )
{
  // quickly searching for our message part: since Kolab parts are
  // top-level parts we do *not* have to travel into embedded multiparts
  for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
    //kdDebug(5006) << "findBodyPart:  - " << part->Headers().ContentDisposition().Filename().c_str() << endl;
    if ( part->hasHeaders()
         && attachmentName == part->Headers().ContentDisposition().Filename().c_str() )
      return part;
  }
  return 0;
}

#if 0
static void debugBodyParts( const char* foo, const KMMessage& msg )
{
  kdDebug(5006) << "--debugBodyParts " << foo << "--" << endl;
  for ( DwBodyPart* part = msg.getFirstDwBodyPart(); part; part = part->Next() ) {
    if ( part->hasHeaders() ) {
      kdDebug(5006) << " bodypart: " << part << endl;
      kdDebug(5006) << "        " << part->Headers().AsString().c_str() << endl;
    }
    else
      kdDebug(5006) << " part " << part << " has no headers" << endl;
  }
}
#else
inline static void debugBodyParts( const char*, const KMMessage& ) {}
#endif


// Add (or overwrite, resp.) an attachment in an existing mail,
// attachments must be local files, they are identified by their names.
// If lookupByName if false the attachment to replace is looked up by mimetype.
// return value: wrong if attachment could not be added/updated
bool KMailICalIfaceImpl::updateAttachment( KMMessage& msg,
                                           const QString& attachmentURL,
                                           const QString& attachmentName,
                                           const QString& attachmentMimetype,
                                           bool lookupByName )
{
  kdDebug(5006) << "KMailICalIfaceImpl::updateAttachment( " << attachmentURL << " )" << endl;

  bool bOK = false;

  KURL url( attachmentURL );
  if ( url.isValid() && url.isLocalFile() ) {
    const QString fileName( url.path() );
    QFile file( fileName );
    if( file.open( IO_ReadOnly ) ) {
      QByteArray rawData = file.readAll();
      file.close();

      // create the new message part with data read from temp file
      KMMessagePart msgPart;
      msgPart.setName( attachmentName );

      const int iSlash = attachmentMimetype.find('/');
      const QCString sType    = attachmentMimetype.left( iSlash   ).latin1();
      const QCString sSubtype = attachmentMimetype.mid(  iSlash+1 ).latin1();
      msgPart.setTypeStr( sType );
      msgPart.setSubtypeStr( sSubtype );
      QCString ctd("attachment;\n  filename=\"");
      ctd.append( attachmentName.latin1() );
      ctd.append("\"");
      msgPart.setContentDisposition( ctd );
      QValueList<int> dummy;
      msgPart.setBodyAndGuessCte( rawData, dummy );
      msgPart.setPartSpecifier( fileName );

      DwBodyPart* newPart = msg.createDWBodyPart( &msgPart );
      // This whole method is a bit special. We mix code for writing and code for reading.
      // E.g. we need to parse the content-disposition again for ContentDisposition().Filename()
      // to work later on.
      newPart->Headers().ContentDisposition().Parse();

      DwBodyPart* part = lookupByName ? findBodyPart( msg, attachmentName )
                         : findBodyPartByMimeType( msg, sType, sSubtype );
      if ( part ) {
        // Make sure the replacing body part is pointing
        // to the same next part as the original body part.
        newPart->SetNext( part->Next() );
        // call DwBodyPart::operator =
        // which calls DwEntity::operator =
        *part = *newPart;
        delete newPart;
        msg.setNeedsAssembly();
        kdDebug(5006) << "Attachment " << attachmentName << " updated." << endl;
      } else {
        msg.addDwBodyPart( newPart );
        kdDebug(5006) << "Attachment " << attachmentName << " added." << endl;
      }
      bOK = true;
    }else{
      kdDebug(5006) << "Attachment " << attachmentURL << " can not be read." << endl;
    }
  }else{
    kdDebug(5006) << "Attachment " << attachmentURL << " not a local file." << endl;
  }

  return bOK;
}

// Look for the attachment with the right mimetype
bool KMailICalIfaceImpl::kolabXMLFoundAndDecoded( const KMMessage& msg, const QString& mimetype, QString& s )
{
  const int iSlash = mimetype.find('/');
  const QCString sType    = mimetype.left( iSlash   ).latin1();
  const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
  DwBodyPart* part = findBodyPartByMimeType( msg, sType, sSubtype, true /* starts with sSubtype, to accept application/x-vnd.kolab.contact.distlist */ );
  if ( part ) {
    KMMessagePart msgPart;
    KMMessage::bodyPart(part, &msgPart);
    s = msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) );
    return true;
  }
  return false;
}

// Delete an attachment in an existing mail.
// return value: wrong if attachment could not be deleted
//
// This code could be optimized: for now we just replace
// the attachment by an empty dummy attachment since Mimelib
// does not provide an option for deleting attachments yet.
bool KMailICalIfaceImpl::deleteAttachment( KMMessage& msg,
                                           const QString& attachmentName )
{
  kdDebug(5006) << "KMailICalIfaceImpl::deleteAttachment( " << attachmentName << " )" << endl;

  bool bOK = false;

  // quickly searching for our message part: since Kolab parts are
  // top-level parts we do *not* have to travel into embedded multiparts
  DwBodyPart* part = findBodyPart( msg, attachmentName );
  if ( part ) {
    msg.getTopLevelPart()->Body().RemoveBodyPart( part );
    delete part;
    msg.setNeedsAssembly();
    kdDebug(5006) << "Attachment deleted." << endl;
    bOK = true;
  }

  if( !bOK ){
    kdDebug(5006) << "Attachment " << attachmentName << " not found." << endl;
  }

  return bOK;
}


// Store a new entry that was received from the resource
Q_UINT32 KMailICalIfaceImpl::addIncidenceKolab( KMFolder& folder,
                                                const QString& subject,
                                                const QString& plainTextBody,
                                                const QMap<QCString, QString>& customHeaders,
                                                const QStringList& attachmentURLs,
                                                const QStringList& attachmentNames,
                                                const QStringList& attachmentMimetypes )
{
  kdDebug(5006) << "KMailICalIfaceImpl::addIncidenceKolab( " << attachmentNames << " )" << endl;

  Q_UINT32 sernum = 0;
  bool bAttachOK = true;

  // Make a new message for the incidence
  KMMessage* msg = new KMMessage();
  msg->initHeader();
  msg->setType( DwMime::kTypeMultipart );
  msg->setSubtype( DwMime::kSubtypeMixed );
  msg->headers().ContentType().CreateBoundary( 0 );
  msg->headers().ContentType().Assemble();
  msg->setSubject( subject );
  msg->setAutomaticFields( true );

  QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
  const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.end();
  for ( ; ith != ithEnd ; ++ith ) {
    msg->setHeaderField( ith.key(), ith.data() );
  }

  // add a first body part to be displayed by all mailer
  // than can NOT display Kolab data: no matter if these
  // mailers are MIME compliant or not
  KMMessagePart firstPart;
  firstPart.setType(    DwMime::kTypeText     );
  firstPart.setSubtype( DwMime::kSubtypePlain );
  firstPart.setBodyFromUnicode( plainTextBody );
  msg->addBodyPart( &firstPart );

  Q_ASSERT( attachmentMimetypes.count() == attachmentURLs.count() );
  Q_ASSERT( attachmentNames.count() == attachmentURLs.count() );
  // Add all attachments by reading them from their temp. files
  QStringList::ConstIterator itmime = attachmentMimetypes.begin();
  QStringList::ConstIterator iturl = attachmentURLs.begin();
  for( QStringList::ConstIterator itname = attachmentNames.begin();
       itname != attachmentNames.end()
       && itmime != attachmentMimetypes.end()
       && iturl != attachmentURLs.end();
       ++itname, ++iturl, ++itmime ){
    bool bymimetype = (*itmime).startsWith( "application/x-vnd.kolab." );
    if( !updateAttachment( *msg, *iturl, *itname, *itmime, !bymimetype ) ){
      kdWarning(5006) << "Attachment error, can not add Incidence." << endl;
      bAttachOK = false;
      break;
    }
  }

  if( bAttachOK ){
    // Mark the message as read and store it in the folder
    msg->cleanupHeader();
    //debugBodyParts( "after cleanup", *msg );
    msg->touch();
    msg->setComplete( true );
    if ( folder.addMsg( msg ) == 0 )
      // Message stored
      sernum = msg->getMsgSerNum();
    kdDebug(5006) << "addIncidenceKolab(): Message done and saved. Sernum: "
                  << sernum << endl;

    //debugBodyParts( "after addMsg", *msg );

  } else
    kdError(5006) << "addIncidenceKolab(): Message *NOT* saved!\n";

  return sernum;
}

// The resource orders a deletion
bool KMailICalIfaceImpl::deleteIncidence( const QString& type,
                                          const QString& folder,
                                          const QString& uid )
{
  if( !mUseResourceIMAP )
    return false;

  kdDebug(5006) << "KMailICalIfaceImpl::deleteIncidence( " << type << ", "
            << uid << " )" << endl;

  // Find the folder
  KMFolder* f = folderFromType( type, folder );

  if( !f ) {
    kdError(5006) << "deleteIncidence(" << type << "," << folder << ") : Not an IMAP resource folder" << endl;
    return false;
  }
  if ( storageFormat( f ) != StorageIcalVcard ) {
    kdError(5006) << "deleteIncidence(" << type << "," << folder << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return false;
  }

  bool rc = false;
  bool quiet = mResourceQuiet;
  mResourceQuiet = true;

  KMMessage* msg = findMessageByUID( uid, f );
  if( msg ) {
    // Message found - delete it and return happy
    deleteMsg( msg );
    rc = true;
  } else {
    kdDebug(5006) << "Message not found, cannot remove id " << uid << endl;
  }

  mResourceQuiet = quiet;
  return rc;
}

bool KMailICalIfaceImpl::deleteIncidenceKolab( const QString& resource,
                                               Q_UINT32 sernum )
{
  // Find the message from the serial number and delete it.
  if( !mUseResourceIMAP )
    return false;

  kdDebug(5006) << "KMailICalIfaceImpl::deleteIncidenceKolab( "
                << resource << ", " << sernum << ")\n";

  // Find the folder
  KMFolder* f = findResourceFolder( resource );
  if( !f ) {
    kdError(5006) << "deleteIncidenceKolab(" << resource << ") : Not an IMAP resource folder" << endl;
    return false;
  }
  if ( storageFormat( f ) != StorageXML ) {
    kdError(5006) << "deleteIncidenceKolab(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return false;
  }

  bool rc = false;

  KMMessage* msg = findMessageBySerNum( sernum, f );
  if( msg ) {
    // Message found - delete it and return happy
    deleteMsg( msg );
    rc = true;
  } else {
    kdDebug(5006) << "Message not found, cannot remove serNum " << sernum << endl;
  }
  return rc;
}

// The resource asks for a full list of incidences
QStringList KMailICalIfaceImpl::incidences( const QString& type,
                                            const QString& folder )
{
  if( !mUseResourceIMAP )
    return QStringList();

  kdDebug(5006) << "KMailICalIfaceImpl::incidences( " << type << ", "
                << folder << " )" << endl;
  QStringList ilist;

  KMFolder* f = folderFromType( type, folder );
  if( !f ) {
    kdError(5006) << "incidences(" << type << "," << folder << ") : Not an IMAP resource folder" << endl;
    return ilist;
  }
  if ( storageFormat( f ) != StorageIcalVcard ) {
    kdError(5006) << "incidences(" << type << "," << folder << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return ilist;
  }

  f->open();
  QString s;
  for( int i=0; i<f->count(); ++i ) {
    bool unget = !f->isMessage(i);
    if( vPartFoundAndDecoded( f->getMsg( i ), s ) )
      ilist << s;
    if( unget ) f->unGetMsg(i);
  }

  return ilist;
}

int KMailICalIfaceImpl::incidencesKolabCount( const QString& mimetype,
                                              const QString& resource )
{
  if( !mUseResourceIMAP )
    return 0;

  KMFolder* f = findResourceFolder( resource );
  if( !f ) {
    kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
    return 0;
  }
  if ( storageFormat( f ) != StorageXML ) {
    kdError(5006) << "incidencesKolab(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return 0;
  }

  f->open();
  int n = f->count();
  f->close();
  kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolabCount( " << mimetype << ", "
                << resource << " ) returned " << n << endl;
  return n;
}

QMap<Q_UINT32, QString> KMailICalIfaceImpl::incidencesKolab( const QString& mimetype,
                                                             const QString& resource,
                                                             int startIndex,
                                                             int nbMessages )
{
  /// Get the mimetype attachments from this folder. Returns a
  /// QMap with serialNumber/attachment pairs.
  /// (serial numbers of the mail are provided for easier later update)

  QMap<Q_UINT32, QString> aMap;
  if( !mUseResourceIMAP )
    return aMap;

  KMFolder* f = findResourceFolder( resource );
  if( !f ) {
    kdError(5006) << "incidencesKolab(" << resource << ") : Not an IMAP resource folder" << endl;
    return aMap;
  }
  if ( storageFormat( f ) != StorageXML ) {
    kdError(5006) << "incidencesKolab(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return aMap;
  }

  f->open();

  int stopIndex = nbMessages == -1 ? f->count() :
                  QMIN( f->count(), startIndex + nbMessages );
  kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolab( " << mimetype << ", "
                << resource << " ) from " << startIndex << " to " << stopIndex << endl;

  for(int i = startIndex; i < stopIndex; ++i) {
    KMMessage* msg = f->storage()->readTemporaryMsg(i);
    if ( msg ) {
      const int iSlash = mimetype.find('/');
      const QCString sType    = mimetype.left( iSlash   ).latin1();
      const QCString sSubtype = mimetype.mid(  iSlash+1 ).latin1();
      if ( sType.isEmpty() || sSubtype.isEmpty() ) {
        kdError(5006) << mimetype << " not an type/subtype combination" << endl;
      } else {
        DwBodyPart* dwPart = findBodyPartByMimeType( *msg, sType, sSubtype );
        if ( dwPart ) {
          KMMessagePart msgPart;
          KMMessage::bodyPart(dwPart, &msgPart);
          aMap.insert(msg->getMsgSerNum(), msgPart.bodyToUnicode( QTextCodec::codecForName( "utf8" ) ));
        } else {
          // This is *not* an error: it may be that not all of the messages
          // have a message part that is matching the wanted MIME type
        }
      }
      delete msg;
    }
  }
  return aMap;
}

QStringList KMailICalIfaceImpl::subresources( const QString& type )
{
  QStringList lst;

  // Add the default one
  KMFolder* f = folderFromType( type, QString::null );
  if ( f && storageFormat( f ) == StorageIcalVcard )
    lst << f->location();

  // Add the extra folders
  KMail::FolderContentsType t = folderContentsType( type );
  QDictIterator<ExtraFolder> it( mExtraFolders );
  for ( ; it.current(); ++it ) {
    f = it.current()->folder;
    if ( f && f->storage()->contentsType() == t
         && storageFormat( f ) == StorageIcalVcard )
      lst << f->location();
  }

  return lst;
}

QValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKolab( const QString& contentsType )
{
  QValueList<SubResource> subResources;

  // Add the default one
  KMFolder* f = folderFromType( contentsType, QString::null );
  if ( f && storageFormat( f ) == StorageXML ) {
    subResources.append( SubResource( f->location(),  f->prettyURL(),
                                     !f->isReadOnly(), folderIsAlarmRelevant( f ) ) );
    kdDebug(5006) << "Adding(1) folder " << f->location() << "    " <<
      ( f->isReadOnly() ? "readonly" : "" ) << endl;
  }

  // get the extra ones
  const KMail::FolderContentsType t = folderContentsType( contentsType );
  QDictIterator<ExtraFolder> it( mExtraFolders );
  for ( ; it.current(); ++it ){
    f = it.current()->folder;
    if ( f && f->storage()->contentsType() == t
         && storageFormat( f ) == StorageXML ) {
      subResources.append( SubResource( f->location(), f->prettyURL(),
                                        !f->isReadOnly(),
                                        folderIsAlarmRelevant( f ) ) );
      kdDebug(5006) << "Adding(2) folder " << f->location() << "     " <<
              ( f->isReadOnly() ? "readonly" : "" ) << endl;
    }
  }

  if ( subResources.isEmpty() )
    kdDebug(5006) << "subresourcesKolab: No folder found for " << contentsType << endl;
  return subResources;
}

bool KMailICalIfaceImpl::isWritableFolder( const QString& type,
                                           const QString& resource )
{
  KMFolder* f = folderFromType( type, resource );
  if ( !f )
    // Definitely not writable
    return false;

  return !f->isReadOnly();
}

bool KMailICalIfaceImpl::update( const QString& type, const QString& folder,
                                 const QStringList& entries )
{
  if( !mUseResourceIMAP )
    return false;

  if( entries.count() % 2 == 1 )
    // Something is wrong - an odd amount of strings should not happen
    return false;

  QStringList::ConstIterator it = entries.begin();
  while( true ) {
    // Read them in pairs and call the single update method
    QString uid, entry;
    if( it == entries.end() )
      break;
    uid = *it;
    ++it;
    if( it == entries.end() )
      break;
    entry = *it;
    ++it;

    if( !update( type, folder, uid, entry ) )
      // Some error happened
      return false;
  }

  return true;
}

bool KMailICalIfaceImpl::update( const QString& type, const QString& folder,
                                 const QString& uid, const QString& entry )
{
  if( !mUseResourceIMAP )
    return false;

  kdDebug(5006) << "Update( " << type << ", " << folder << ", " << uid << ")\n";

  // Find the folder and the incidence in it
  KMFolder* f = folderFromType( type, folder );
  if( !f ) {
    kdError(5006) << "update(" << type << "," << folder << ") : Not an IMAP resource folder" << endl;
    return false;
  }
  if ( storageFormat( f ) != StorageIcalVcard ) {
    kdError(5006) << "update(" << type << "," << folder << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return false;
  }

  bool rc = true;
  bool quiet = mResourceQuiet;
  mResourceQuiet = true;
  KMMessage* msg = findMessageByUID( uid, f );
  if( msg ) {
    // Message found - update it
    deleteMsg( msg );
    addIncidence( type, folder, uid, entry );
    rc = true;
  } else {
    kdDebug(5006) << type << " not found, cannot update uid " << uid << endl;
    // Since it doesn't seem to be there, save it instead
    addIncidence( type, folder, uid, entry );
  }
  mResourceQuiet = quiet;
  return rc;
}

Q_UINT32 KMailICalIfaceImpl::update( const QString& resource,
                                     Q_UINT32 sernum,
                                     const QString& subject,
                                     const QString& plainTextBody,
                                     const QMap<QCString, QString>& customHeaders,
                                     const QStringList& attachmentURLs,
                                     const QStringList& attachmentMimetypes,
                                     const QStringList& attachmentNames,
                                     const QStringList& deletedAttachments )
{
  Q_UINT32 rc = 0;

  // This finds the message with serial number "sernum", sets the
  // xml attachments to hold the contents of "xml", and updates all
  // attachments.
  // The mail can have additional attachments, and these are not
  // touched!  They belong to other clients - like Outlook
  // So we delete all the attachments listed in the
  // "deletedAttachments" arg, and then update/add all the attachments
  // given by the urllist attachments.

  // If the mail does not already exist, id will not be a valid serial
  // number, and the mail is just added instead. In this case
  // the deletedAttachments can be forgotten.
  if( !mUseResourceIMAP )
    return rc;

  Q_ASSERT( !resource.isEmpty() );

  kdDebug(5006) << "KMailICalIfaceImpl::update( " << resource << ", " << sernum << " )\n";
  kdDebug(5006) << attachmentURLs << "\n";
  kdDebug(5006) << attachmentMimetypes << "\n";
  kdDebug(5006) << attachmentNames << "\n";
  kdDebug(5006) << "deleted attachments:" << deletedAttachments << "\n";

  // Find the folder
  KMFolder* f = findResourceFolder( resource );
  if( !f ) {
    kdError(5006) << "update(" << resource << ") : Not an IMAP resource folder" << endl;
    return rc;
  }
  if ( storageFormat( f ) != StorageXML ) {
    kdError(5006) << "update(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return rc;
  }

  f->open();

  kdDebug(5006) << "Updating in folder " << f << " " << f->location() << endl;
  KMMessage* msg = 0;
  if ( sernum != 0 )
    msg = findMessageBySerNum( sernum, f );
  if ( msg ) {
    // Message found - make a copy and update it:
    KMMessage* newMsg = new KMMessage( *msg );
    newMsg->setSubject( subject );
    QMap<QCString, QString>::ConstIterator ith = customHeaders.begin();
    const QMap<QCString, QString>::ConstIterator ithEnd = customHeaders.begin();
    for ( ; ith != ithEnd ; ++ith )
      newMsg->setHeaderField( ith.key(), ith.data() );
    newMsg->setParent( 0 ); // workaround strange line in KMMsgBase::assign. newMsg is not in any folder yet.
    // Note that plainTextBody isn't used in this branch. We assume it's still valid from when the mail was created.

    // Delete some attachments according to list
    for( QStringList::ConstIterator it = deletedAttachments.begin();
         it != deletedAttachments.end();
         ++it ){
      if( !deleteAttachment( *newMsg, *it ) ){
        // Note: It is _not_ an error if an attachment was already deleted.
      }
    }

    // Add all attachments by reading them from their temp. files
    QStringList::ConstIterator iturl = attachmentURLs.begin();
    QStringList::ConstIterator itmime = attachmentMimetypes.begin();
    QStringList::ConstIterator itname = attachmentNames.begin();
    for( ;
         iturl != attachmentURLs.end()
         && itmime != attachmentMimetypes.end()
         && itname != attachmentNames.end();
         ++iturl, ++itname, ++itmime ){
      bool bymimetype = (*itname).startsWith( "application/x-vnd.kolab." );
      if( !updateAttachment( *newMsg, *iturl, *itname, *itmime, bymimetype ) ){
        kdDebug(5006) << "Attachment error, can not update attachment " << *iturl << endl;
        break;
      }
    }

    //debugBodyParts( "in update, before cleanup", *newMsg );

    // This is necessary for the headers to be readable later on
    newMsg->cleanupHeader();

    //debugBodyParts( "in update, after cleanup", *newMsg );

    deleteMsg( msg );
    newMsg->setComplete( true );
    if ( f->addMsg( newMsg ) == 0 ) {
      // Message stored
      rc = newMsg->getMsgSerNum();
      kdDebug(5006) << "forget about " << sernum << ", it's " << rc << " now" << endl;
    }

  } else {
    // Message not found - store it newly
    rc = addIncidenceKolab( *f, subject, plainTextBody, customHeaders,
                            attachmentURLs,
                            attachmentNames,
                            attachmentMimetypes );
  }

  f->close();
  addFolderChange( f, Contents );
  return rc;
}

KURL KMailICalIfaceImpl::getAttachment( const QString& resource,
                                        Q_UINT32 sernum,
                                        const QString& filename )
{
  // This finds the attachment with the filename, saves it to a
  // temp file and returns a URL to it. It's up to the resource
  // to delete the tmp file later.
  if( !mUseResourceIMAP )
    return KURL();

  kdDebug(5006) << "KMailICalIfaceImpl::getAttachment( "
                << resource << ", " << sernum << ", " << filename << " )\n";

  // Find the folder
  KMFolder* f = findResourceFolder( resource );
  if( !f ) {
    kdError(5006) << "getAttachment(" << resource << ") : Not an IMAP resource folder" << endl;
    return KURL();
  }
  if ( storageFormat( f ) != StorageXML ) {
    kdError(5006) << "getAttachment(" << resource << ") : Folder has wrong storage format " << storageFormat( f ) << endl;
    return KURL();
  }

  KURL url;

  bool bOK = false;
  bool quiet = mResourceQuiet;
  mResourceQuiet = true;

  KMMessage* msg = findMessageBySerNum( sernum, f );
  if( msg ) {
    // Message found - look for the attachment:

    DwBodyPart* part = findBodyPart( *msg, filename );
    if ( part ) {
      // Save the contents of the attachment.
      KMMessagePart aPart;
      msg->bodyPart( part, &aPart );
      QByteArray rawData( aPart.bodyDecodedBinary() );

      KTempFile file;
      file.file()->writeBlock( rawData.data(), rawData.size() );

      url.setPath( file.name() );

      bOK = true;
    }

    if( !bOK ){
      kdDebug(5006) << "Attachment " << filename << " not found." << endl;
    }
  }else{
    kdDebug(5006) << "Message not found." << endl;
  }

  mResourceQuiet = quiet;
  return url;
}

void KMailICalIfaceImpl::slotFolderRemoved( KMFolder* folder )
{
  // pretend the folder just changed back to the mail type, which
  // does the right thing, namely remove resource
  folderContentsTypeChanged( folder, KMail::ContentsTypeMail );
  KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
  configGroup.deleteEntry( folder->idString() + "-storageFormat" );
  configGroup.deleteEntry( folder->idString() + "-changes" );
}

// KMail added a file to one of the groupware folders
void KMailICalIfaceImpl::slotIncidenceAdded( KMFolder* folder,
                                             Q_UINT32 sernum )
{
  if( mResourceQuiet || !mUseResourceIMAP )
    return;

  QString type = folderContentsType( folder->storage()->contentsType() );
  if( !type.isEmpty() ) {
    // Get the index of the mail
    int i = 0;
    KMFolder* aFolder = 0;
    kmkernel->msgDict()->getLocation( sernum, &aFolder, &i );
    assert( folder == aFolder );

    bool unget = !folder->isMessage( i );
    QString s;
    bool ok = false;
    KMMessage* msg = folder->getMsg( i );
    StorageFormat format = storageFormat( folder );
    switch( format ) {
    case StorageIcalVcard:
      // Read the iCal or vCard
      ok = vPartFoundAndDecoded( msg, s );
      break;
    case StorageXML:
      // Read the XML from the attachment with the given mimetype
      ok = kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s );
      break;
    }
    if ( ok ) {
      kdDebug(5006) << "Emitting DCOP signal incidenceAdded( " << type
                      << ", " << folder->location() << ", " << s << " )" << endl;
      incidenceAdded( type, folder->location(), s );
      incidenceAdded( type, folder->location(), sernum, format, s );
    }
    if( unget ) folder->unGetMsg(i);
  } else
    kdError(5006) << "Not an IMAP resource folder" << endl;
}

// KMail deleted a file
void KMailICalIfaceImpl::slotIncidenceDeleted( KMFolder* folder,
                                               Q_UINT32 sernum )
{
  if( mResourceQuiet || !mUseResourceIMAP )
    return;

  QString type = folderContentsType( folder->storage()->contentsType() );
  kdDebug(5006) << folder << " " << type << " " << sernum << endl;
  if( !type.isEmpty() ) {
    // Get the index of the mail
    int i = 0;
    KMFolder* aFolder = 0;
    kmkernel->msgDict()->getLocation( sernum, &aFolder, &i );
    assert( folder == aFolder );

    // Read the iCal or vCard
    bool unget = !folder->isMessage( i );
    QString s;
    bool ok = false;
    KMMessage* msg = folder->getMsg( i );
    QString uid( "UID" );
    switch( storageFormat( folder ) ) {
    case StorageIcalVcard:
        if( vPartFoundAndDecoded( msg, s ) ) {
            vPartMicroParser( s, uid );
            ok = true;
        }
        break;
    case StorageXML:
        if ( kolabXMLFoundAndDecoded( *msg, folderKolabMimeType( folder->storage()->contentsType() ), s ) ) {
            QDomDocument doc;
            if ( doc.setContent( s ) ) {
                QDomElement top = doc.documentElement();
                for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "uid" ) {
                        uid = e.text();
                        ok = true;
                        break;
                    }
                }
            }
        }
        break;
    }
    if ( ok ) {
        kdDebug(5006) << "Emitting DCOP signal incidenceDeleted( "
                      << type << ", " << folder->location() << ", " << uid
                      << " )" << endl;
        incidenceDeleted( type, folder->location(), uid );
    }
    if( unget ) folder->unGetMsg(i);
  } else
    kdError(5006) << "Not a groupware folder" << endl;
}

// KMail orders a refresh
void KMailICalIfaceImpl::slotRefresh( const QString& type )
{
  if( mUseResourceIMAP ) {
    signalRefresh( type, QString::null /* PENDING(bo) folder->location() */ );
    kdDebug(5006) << "Emitting DCOP signal signalRefresh( " << type << " )" << endl;
  }
}

// This is among other things called when an expunge of a folder happens
void KMailICalIfaceImpl::slotRefreshFolder( KMFolder* folder)
{
  // TODO: The resources would of course be better off, if only this
  // folder would need refreshing. Currently it just orders a reload of
  // the type of the folder
  if( mUseResourceIMAP && folder ) {
    if( folder == mCalendar || folder == mContacts
        || folder == mNotes || folder == mTasks
        || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
      // Refresh the folder of this type
      KMail::FolderContentsType ct = folder->storage()->contentsType();
      slotRefresh( s_folderContentsType[ct].contentsTypeStr );
    }
  }
}

/****************************
 * The folder and message stuff code
 */

KMFolder* KMailICalIfaceImpl::folderFromType( const QString& type,
                                              const QString& folder )
{
  if( mUseResourceIMAP ) {
    KMFolder* f = 0;
    if ( !folder.isEmpty() ) {
      f = extraFolder( type, folder );
      if ( f )
        return f;
    }

    if( type == "Calendar" ) f = mCalendar;
    else if( type == "Contact" ) f = mContacts;
    else if( type == "Note" ) f = mNotes;
    else if( type == "Task" || type == "Todo" ) f = mTasks;
    else if( type == "Journal" ) f = mJournals;

    if ( f && ( folder.isEmpty() || folder == f->location() ) )
      return f;

    kdError(5006) << "No folder ( " << type << ", " << folder << " )\n";
  }

  return 0;
}


// Returns true if folder is a resource folder. If the resource isn't enabled
// this always returns false
bool KMailICalIfaceImpl::isResourceFolder( KMFolder* folder ) const
{
  return mUseResourceIMAP && folder &&
    ( isStandardResourceFolder( folder ) || mExtraFolders.find( folder->location() )!=0 );
}

bool KMailICalIfaceImpl::isStandardResourceFolder( KMFolder* folder ) const
{
  return ( folder == mCalendar || folder == mTasks || folder == mJournals ||
           folder == mNotes || folder == mContacts );
}

bool KMailICalIfaceImpl::hideResourceImapFolder( KMFolder* folder ) const
{
  return mHideFolders && isResourceFolder( folder );
}

KFolderTreeItem::Type KMailICalIfaceImpl::folderType( KMFolder* folder ) const
{
  if( mUseResourceIMAP && folder ) {
    if( folder == mCalendar || folder == mContacts
        || folder == mNotes || folder == mTasks
        || folder == mJournals || mExtraFolders.find( folder->location() ) ) {
      KMail::FolderContentsType ct = folder->storage()->contentsType();
      return s_folderContentsType[ct].treeItemType;
    }
  }

  return KFolderTreeItem::Other;
}

// Global tables of foldernames is different languages
// For now: 0->English, 1->German, 2->French, 3->Dutch
static QMap<KFolderTreeItem::Type,QString> folderNames[4];
QString KMailICalIfaceImpl::folderName( KFolderTreeItem::Type type, int language ) const
{
  // With the XML storage, folders are always (internally) named in English
  if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
    language = 0;

  static bool folderNamesSet = false;
  if( !folderNamesSet ) {
    folderNamesSet = true;
    /* 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::Tasks] = QString::fromLatin1("Tasks");
    folderNames[0][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
    folderNames[0][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
    folderNames[0][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");

    // German
    folderNames[1][KFolderTreeItem::Calendar] = QString::fromLatin1("Kalender");
    folderNames[1][KFolderTreeItem::Tasks] = QString::fromLatin1("Aufgaben");
    folderNames[1][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
    folderNames[1][KFolderTreeItem::Contacts] = QString::fromLatin1("Kontakte");
    folderNames[1][KFolderTreeItem::Notes] = QString::fromLatin1("Notizen");

    // French
    folderNames[2][KFolderTreeItem::Calendar] = QString::fromLatin1("Calendrier");
    folderNames[2][KFolderTreeItem::Tasks] = QString::fromLatin1("Tāches");
    folderNames[2][KFolderTreeItem::Journals] = QString::fromLatin1("Journal");
    folderNames[2][KFolderTreeItem::Contacts] = QString::fromLatin1("Contacts");
    folderNames[2][KFolderTreeItem::Notes] = QString::fromLatin1("Notes");

    // Dutch
    folderNames[3][KFolderTreeItem::Calendar] = QString::fromLatin1("Agenda");
    folderNames[3][KFolderTreeItem::Tasks] = QString::fromLatin1("Taken");
    folderNames[3][KFolderTreeItem::Journals] = QString::fromLatin1("Logboek");
    folderNames[3][KFolderTreeItem::Contacts] = QString::fromLatin1("Contactpersonen");
    folderNames[3][KFolderTreeItem::Notes] = QString::fromLatin1("Notities");
  }

  if( language < 0 || language > 3 ) {
    return folderNames[mFolderLanguage][type];
  }
  else {
    return folderNames[language][type];
  }
}


// Find message matching a given UID
KMMessage *KMailICalIfaceImpl::findMessageByUID( const QString& uid, KMFolder* folder )
{
  if( !folder ) return 0;

  for( int i=0; i<folder->count(); ++i ) {
    bool unget = !folder->isMessage(i);
    KMMessage* msg = folder->getMsg( i );
    if( msg ) {
      QString vCal;
      if( vPartFoundAndDecoded( msg, vCal ) ) {
        QString msgUid( "UID" );
        vPartMicroParser( vCal, msgUid );
        if( msgUid == uid )
          return msg;
      }
    }
    if( unget ) folder->unGetMsg(i);
  }

  return 0;
}

// Find message matching a given serial number
KMMessage *KMailICalIfaceImpl::findMessageBySerNum( Q_UINT32 serNum, KMFolder* folder )
{
  if( !folder ) return 0;

  KMMessage *message = 0;
  KMFolder* aFolder = 0;
  int index;
  kmkernel->msgDict()->getLocation( serNum, &aFolder, &index );
  if( aFolder && aFolder != folder ){
    kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) found it in folder " << aFolder->location() << ", expected " << folder->location() << endl;
  }else{
    if( aFolder )
      message = aFolder->getMsg( index );
    if (!message)
      kdWarning(5006) << "findMessageBySerNum( " << serNum << " ) invalid serial number\n" << endl;
  }
  return message;
}

void KMailICalIfaceImpl::deleteMsg( KMMessage *msg )
{
  if( !msg ) return;
  // Commands are now delayed; can't use that anymore, we need immediate deletion
  //( new KMDeleteMsgCommand( msg->parent(), msg ) )->start();
  KMFolder *srcFolder = msg->parent();
  int idx = srcFolder->find(msg);
  assert(idx != -1);
  srcFolder->removeMsg(idx);
  delete msg;
  addFolderChange( srcFolder, Contents );
}

void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder,
                                                    KMail::FolderContentsType contentsType )
{
  if ( !mUseResourceIMAP )
    return;
  kdDebug(5006) << "folderContentsTypeChanged( " << folder->name()
                << ", " << contentsType << ")\n";

  // The builtins can't change type
  if ( isStandardResourceFolder( folder ) )
    return;

  // Check if already know that 'extra folder'
  const QString location = folder->location();
  ExtraFolder* ef = mExtraFolders.find( location );
  if ( ef && ef->folder ) {
    // Notify that the old folder resource is no longer available
    subresourceDeleted(folderContentsType( folder->storage()->contentsType() ), location );

    if ( contentsType == 0 ) {
      // Delete the old entry, stop listening and stop here
      mExtraFolders.remove( location );
      folder->disconnect( this );
      return;
    }
    // So the type changed to another groupware type, ok.
  } else {
    if ( ef && !ef->folder ) // deleted folder, clean up
      mExtraFolders.remove( location );
    if ( contentsType == 0 )
        return;

    kdDebug(5006) << "registering " << location << " as extra folder" << endl;
    // Make a new entry for the list
    ef = new ExtraFolder( folder );
    mExtraFolders.insert( location, ef );

    StorageFormat format= GlobalSettings::self()->theIMAPResourceStorageFormat()
      == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
    FolderInfo info( format, NoChange );
    mFolderInfoMap.insert( folder, info );

    // Adjust the folder names of all foo.default folders.
    // German users will get Kalender as the name of all default Calendar folders,
    // including their own, so that the default calendar folder of their Japanese
    // coworker appears as /user/hirohito/Kalender, although Hirohito sees his folder
    // in Japanese. On the server the folders are always in English.
    if ( folder->folderType() == KMFolderTypeCachedImap ) {
      QString annotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
      kdDebug(5006) << "folderContentsTypeChanged: " << folder->name() << " has annotation " << annotation << endl;
      if ( annotation == QString( s_folderContentsType[contentsType].annotation ) + ".default" )
        folder->setLabel( localizedDefaultFolderName( contentsType ) );
    }

    connectFolder( folder );
  }

  subresourceAdded( folderContentsType( contentsType ), location, folder->prettyURL(),
                    !folder->isReadOnly(), folderIsAlarmRelevant( folder ) );

  /* FIXME merge once we are back in HEAD. IMAP Resource still uses the other one. */
  subresourceAdded( folderContentsType( contentsType ), location );
}

KMFolder* KMailICalIfaceImpl::extraFolder( const QString& type,
                                           const QString& folder )
{
  // If an extra folder exists that matches the type and folder location,
  // use that
  int t = folderContentsType( type );
  if ( t < 1 || t > 5 )
    return 0;

  ExtraFolder* ef = mExtraFolders.find( folder );
  if ( ef && ef->folder && ef->folder->storage()->contentsType() == t )
    return ef->folder;

  return 0;
}

KMailICalIfaceImpl::StorageFormat KMailICalIfaceImpl::storageFormat( KMFolder* folder ) const
{
  FolderInfoMap::ConstIterator it = mFolderInfoMap.find( folder );
  if ( it != mFolderInfoMap.end() )
    return (*it).mStorageFormat;
  return GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
}

void KMailICalIfaceImpl::setStorageFormat( KMFolder* folder, StorageFormat format )
{
  FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
  if ( it != mFolderInfoMap.end() ) {
    (*it).mStorageFormat = format;
  } else {
    FolderInfo info( format, NoChange );
    mFolderInfoMap.insert( folder, info );
  }
  KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
  configGroup.writeEntry( folder->idString() + "-storageFormat",
                          format == StorageXML ? "xml" : "icalvcard" );
}

void KMailICalIfaceImpl::addFolderChange( KMFolder* folder, FolderChanges changes )
{
  FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
  if ( it != mFolderInfoMap.end() ) {
    (*it).mChanges = static_cast<FolderChanges>( (*it).mChanges | changes );
  } else { // Otherwise, well, it's a folder we don't care about.
    kdDebug(5006) << "addFolderChange: nothing known about folder " << folder->location() << endl;
  }
  KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
  configGroup.writeEntry( folder->idString() + "-changes", (*it).mChanges );
}

void KMailICalIfaceImpl::folderSynced( KMFolder* folder, const KURL& folderURL )
{
  FolderInfoMap::Iterator it = mFolderInfoMap.find( folder );
  if ( it != mFolderInfoMap.end() && (*it).mChanges ) {
    handleFolderSynced( folder, folderURL, (*it).mChanges );
    (*it).mChanges = NoChange;
  }
}

void KMailICalIfaceImpl::handleFolderSynced( KMFolder* folder,
                                             const KURL& folderURL,
                                             int _changes )
{
  // This is done here instead of in the resource, because
  // there could be 0, 1, or N kolab resources at this point.
  // We can hack the N case, but not the 0 case.
  // So the idea of a DCOP signal for this wouldn't work.
  if ( ( _changes & KMailICalIface::Contents ) ||
       ( _changes & KMailICalIface::ACL ) ) {
    if ( storageFormat( folder ) == StorageXML && folder->storage()->contentsType() == KMail::ContentsTypeCalendar )
      triggerKolabFreeBusy( folderURL );
  }
}

void KMailICalIfaceImpl::folderDeletedOnServer( const KURL& folderURL )
{
  triggerKolabFreeBusy( folderURL );
}

void KMailICalIfaceImpl::triggerKolabFreeBusy( const KURL& folderURL )
{
  /* Steffen said: you must issue an authenticated HTTP GET request to
     https://kolabserver/freebusy/trigger/user@domain/Folder/NestedFolder.pfb
     (replace .pfb with .xpfb for extended fb lists). */
  KURL httpURL( folderURL );
  // Keep username ("user@domain"), pass, and host from the imap url
  httpURL.setProtocol( "https" );
  httpURL.setPort( 0 ); // remove imap port

  // IMAP path is either /INBOX/<path> or /user/someone/<path>
  QString path = folderURL.path( -1 );
  Q_ASSERT( path.startsWith( "/" ) );
  int secondSlash = path.find( '/', 1 );
  if ( secondSlash == -1 ) {
    kdWarning() << "KCal::ResourceKolab::fromKMailFolderSynced path is too short: " << path << endl;
    return;
  }
  if ( path.startsWith( "/INBOX/", false ) ) {
    // If INBOX, replace it with the username (which is user@domain)
    path = path.mid( secondSlash );
    path.prepend( folderURL.user() );
  } else {
    // If user, just remove it. So we keep the IMAP-returned username.
    // This assumes it's a known user on the same domain.
    path = path.mid( secondSlash );
  }

  httpURL.setPath( "/freebusy/trigger/" + path + ".pfb" );
  httpURL.setQuery( QString::null );
  // Ensure that we encode everything with UTF8
  httpURL = KURL( httpURL.url(0,106), 106 );
  kdDebug() << "Triggering PFB update for " << folderURL << " : getting " << httpURL << endl;
  // "Fire and forget". No need for error handling, nor for explicit deletion.
  // Maybe we should try to prevent launching it if it's already running (for this URL) though.
  /*KIO::Job* job =*/ KIO::get( httpURL, false, false /*no progress info*/ );
}

void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder )
{
  if ( isResourceFolder( folder ) ) {
    const QString location = folder->location();
    const QString contentsTypeStr = folderContentsType( folder->storage()->contentsType() );

    // Emit deleted+added.
    // It's necessary to reload all incidences to make them readonly (or readwrite).

    subresourceDeleted( contentsTypeStr, location );

    subresourceAdded( contentsTypeStr, location, folder->prettyURL(),
                      !folder->isReadOnly(), folderIsAlarmRelevant( folder ) );

    /* FIXME merge once we are back in HEAD. IMAP Resource still uses the other one. */
    subresourceAdded( contentsTypeStr, location );
  }
}

// Must only be connected to a signal from KMFolder!
void KMailICalIfaceImpl::slotFolderRenamed()
{
  const KMFolder* folder = static_cast<const KMFolder *>( sender() );
  slotFolderPropertiesChanged( const_cast<KMFolder*>( folder ) );
}

void KMailICalIfaceImpl::slotFolderLocationChanged( const QString &oldLocation,
                                                    const QString &newLocation )
{
  KMFolder *folder = findResourceFolder( oldLocation );

  ExtraFolder* ef = mExtraFolders.find( oldLocation );
  if ( ef ) {
    // reuse the ExtraFolder entry, but adjust the key
    mExtraFolders.setAutoDelete( false );
    mExtraFolders.remove( oldLocation );
    mExtraFolders.setAutoDelete( true );
    mExtraFolders.insert( newLocation, ef );
  }
  if (  folder )
    subresourceDeleted( folderContentsType(  folder->storage()->contentsType() ), oldLocation );
}

KMFolder* KMailICalIfaceImpl::findResourceFolder( const QString& resource )
{
  // Try the standard folders
  if( mCalendar && mCalendar->location() == resource )
    return mCalendar;
  if ( mContacts && mContacts->location() == resource )
    return mContacts;
  if ( mNotes && mNotes->location() == resource )
    return mNotes;
  if ( mTasks && mTasks->location() == resource )
    return mTasks;
  if ( mJournals && mJournals->location() == resource )
    return mJournals;

  // No luck. Try the extrafolders
  ExtraFolder* ef = mExtraFolders.find( resource );
  if ( ef )
    return ef->folder;

  // No luck at all
  return 0;
}

/****************************
 * The config stuff
 */

void KMailICalIfaceImpl::readConfig()
{
  bool enabled = GlobalSettings::self()->theIMAPResourceEnabled();

  if( !enabled ) {
    if( mUseResourceIMAP == true ) {
      // Shutting down
      mUseResourceIMAP = false;
      cleanup();
      reloadFolderTree();
    }
    return;
  }
  mUseResourceIMAP = enabled;

  // Read remaining options
  const bool hideFolders = GlobalSettings::self()->hideGroupwareFolders();
  QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();

  // Find the folder parent
  KMFolderDir* folderParentDir;
  KMFolderType folderType;
  KMFolder* folderParent = kmkernel->findFolderById( parentName );
  if( folderParent == 0 ) {
    // Parent folder not found. It was probably deleted. The user will have to
    // configure things again.
    kdDebug(5006) << "Groupware folder " << parentName << " not found. Groupware functionality disabled" << endl;
    // Or maybe the inbox simply wasn't created on the first startup
    KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
    Q_ASSERT( account );
    if ( account ) {
      // just in case we were connected already
      disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
               this, SLOT( slotCheckDone() ) );
      connect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
               this, SLOT( slotCheckDone() ) );
    }
    mUseResourceIMAP = false;
    // We can't really call cleanup(), if those folders were completely deleted.
    mCalendar = 0;
    mTasks    = 0;
    mJournals = 0;
    mContacts = 0;
    mNotes    = 0;
    return;
  } else {
    folderParentDir = folderParent->createChildFolder();
    folderType = folderParent->folderType();
  }

  // Make sure the folder parent has the subdirs
  // Globally there are 3 cases: nothing found, some stuff found by type/name heuristics, or everything found OK
  bool noneFound = true;
  bool mustFix = false; // true when at least one was found by heuristics
  QValueVector<StandardFolderSearchResult> results( KMail::ContentsTypeLast + 1 );
  for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
    if ( i != KMail::ContentsTypeMail ) {
      results[i] = findStandardResourceFolder( folderParentDir, static_cast<KMail::FolderContentsType>(i) );
      if ( results[i].found == StandardFolderSearchResult::FoundAndStandard )
        noneFound = false;
      else if ( results[i].found == StandardFolderSearchResult::FoundByType ||
                results[i].found == StandardFolderSearchResult::FoundByName ) {
        mustFix = true;
        noneFound = false;
      } else // NotFound
        mustFix = true;
    }
  }

  kdDebug() << k_funcinfo << "noneFound=" << noneFound << " mustFix=" << mustFix << endl;

  // Check if something changed
  if( mUseResourceIMAP && !noneFound && !mustFix && mFolderParentDir == folderParentDir
      && mFolderType == folderType ) {
    // Nothing changed
    if ( hideFolders != mHideFolders ) {
      // Well, the folder hiding has changed
      mHideFolders = hideFolders;
      reloadFolderTree();
    }
    return;
  }

  if( noneFound || mustFix ) {
    QString msg;
    QString parentFolderName = folderParent != 0 ? folderParent->name() : folderParentDir->name();
    if ( noneFound ) {
      // No subfolder was found, so ask if we can make them
      msg = i18n("KMail will now create the required groupware folders"
                 " as subfolders of %1; if you do not want this, press \"No\","
                 " and the IMAP resource will be disabled").arg(parentFolderName);
    } else {
      // Some subfolders were found, be more precise
      QString operations = "<ul>";
      for ( int i = 0; i < KMail::ContentsTypeLast+1; ++i ) {
        if ( i != KMail::ContentsTypeMail ) {
          QString typeName = i18n( s_folderContentsType[i].translatedName );
          if ( results[i].found == StandardFolderSearchResult::NotFound )
            operations += "<li>" + i18n( "%1: no folder found, will create it" ).arg( typeName ) + "</li>";
          else if ( results[i].found == StandardFolderSearchResult::FoundByType || results[i].found == StandardFolderSearchResult::FoundByName )
            operations += "<li>" + i18n( "%1: found folder %2, will set it as main groupware folder" ).
                          arg( typeName ).arg( results[i].folder->label() ) + "</li>";
        }
      }
      operations += "</ul>";

      msg = i18n("<qt>KMail found the following groupware folders in %1 and needs to perform the following operations: %2"
                 "<br>If you do not want this, press \"No\","
                 " and the IMAP resource will be disabled").arg(parentFolderName, operations);

    }

    if( KMessageBox::questionYesNo( 0, msg,
                                    i18n("Standard Groupware Folders") ) == KMessageBox::No ) {

      GlobalSettings::self()->setTheIMAPResourceEnabled( false );
      mUseResourceIMAP = false;
      mFolderParentDir = 0;
      reloadFolderTree();
      return;
    }
  }

  // Make the new settings work
  mUseResourceIMAP = true;
  mFolderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
  if( mFolderLanguage > 3 ) mFolderLanguage = 0;
  mFolderParentDir = folderParentDir;
  mFolderType = folderType;
  mHideFolders = hideFolders;

  // Close the previous folders
  cleanup();

  // Set the new folders
  mCalendar = initFolder( "GCa", KMail::ContentsTypeCalendar );
  mTasks    = initFolder( "GTa", KMail::ContentsTypeTask );
  mJournals = initFolder( "GTa", KMail::ContentsTypeJournal );
  mContacts = initFolder( "GCo", KMail::ContentsTypeContact );
  mNotes    = initFolder( "GNo", KMail::ContentsTypeNote );

  // Store final annotation (with .default) so that we won't ask again on next startup
  if ( mCalendar->folderType() == KMFolderTypeCachedImap )
    static_cast<KMFolderCachedImap *>( mCalendar->storage() )->updateAnnotationFolderType();
  if ( mTasks->folderType() == KMFolderTypeCachedImap )
    static_cast<KMFolderCachedImap *>( mTasks->storage() )->updateAnnotationFolderType();
  if ( mJournals->folderType() == KMFolderTypeCachedImap )
    static_cast<KMFolderCachedImap *>( mJournals->storage() )->updateAnnotationFolderType();
  if ( mContacts->folderType() == KMFolderTypeCachedImap )
    static_cast<KMFolderCachedImap *>( mContacts->storage() )->updateAnnotationFolderType();
  if ( mNotes->folderType() == KMFolderTypeCachedImap )
    static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();

  kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
  kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;

  // Find all extra folders
  QStringList folderNames;
  QValueList<QGuardedPtr<KMFolder> > folderList;
  kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
  for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
      it != folderList.end(); ++it)
  {
    FolderStorage* storage = (*it)->storage();
    if ( storage->contentsType() != 0 ) {
      folderContentsTypeChanged( *it, storage->contentsType() );
    }
  }

  // If we just created them, they might have been registered as extra folders temporarily.
  // -> undo that.
  mExtraFolders.remove( mCalendar->location() );
  mExtraFolders.remove( mTasks->location() );
  mExtraFolders.remove( mJournals->location() );
  mExtraFolders.remove( mContacts->location() );
  mExtraFolders.remove( mNotes->location() );


  subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(),
                    mCalendar->label(), true, true );
  subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location() );
  subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(),
                    mTasks->label(), true, true );
  subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location() );
  subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(),
                    mJournals->label(), true, false );
  subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location() );
  subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location(),
                    mContacts->label(), true, false );
  subresourceAdded( folderContentsType( KMail::ContentsTypeContact ), mContacts->location() );
  subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(),
                    mNotes->label(), true, false );
  subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location() );

  reloadFolderTree();
}

void KMailICalIfaceImpl::slotCheckDone()
{
  QString parentName = GlobalSettings::self()->theIMAPResourceFolderParent();
  KMFolder* folderParent = kmkernel->findFolderById( parentName );
  kdDebug(5006) << k_funcinfo << " folderParent=" << folderParent << endl;
  if ( folderParent )  // cool it exists now
  {
    KMAccount* account = kmkernel->acctMgr()->find( GlobalSettings::self()->theIMAPResourceAccount() );
    if ( account )
      disconnect( account, SIGNAL( finishedCheck( bool, CheckStatus ) ),
                  this, SLOT( slotCheckDone() ) );
    readConfig();
  }
}

KMFolder* KMailICalIfaceImpl::initFolder( const char* typeString,
                                          KMail::FolderContentsType contentsType )
{
  // Figure out what type of folder this is supposed to be
  KMFolderType type = mFolderType;
  if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;

  KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
  //kdDebug(5006) << "KMailICalIfaceImpl::initFolder " << folderName( itemType ) << endl;

  // Find the folder
  StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType );
  KMFolder* folder = result.folder;

  StorageFormat defaultFormat = GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML ? StorageXML : StorageIcalVcard;
  if ( !folder ) {
    // The folder isn't there yet - create it
    folder =
      mFolderParentDir->createFolder( localizedDefaultFolderName( contentsType ), false, type );
    if( mFolderType == KMFolderTypeImap )
      static_cast<KMFolderImap*>( folder->storage() )->
        createFolder( localizedDefaultFolderName( contentsType ) );

    // Groupware folder created, use the global setting for storage format
    setStorageFormat( folder, defaultFormat );
  } else {
    KConfigGroup configGroup( kmkernel->config(), "GroupwareFolderInfo" );
    QString str = configGroup.readEntry( folder->idString() + "-storageFormat", "unset" );
    FolderInfo info;
    if ( str == "unset" ) {
      info.mStorageFormat = defaultFormat;
      configGroup.writeEntry( folder->idString() + "-storageFormat",
                              info.mStorageFormat == StorageXML ? "xml" : "icalvcard" );
    } else {
      info.mStorageFormat = ( str == "xml" ) ? StorageXML : StorageIcalVcard;
    }
    info.mChanges = (FolderChanges) configGroup.readNumEntry( folder->idString() + "-changes" );
    mFolderInfoMap.insert( folder, info );

    //kdDebug(5006) << "Found existing folder type " << itemType << " : " << folder->location()  << endl;
  }

  if( folder->canAccess() != 0 ) {
    KMessageBox::sorry(0, i18n("You do not have read/write permission to your %1 folder.")
                       .arg( folderName( itemType ) ) );
    return 0;
  }
  folder->setType( typeString );
  folder->storage()->setContentsType( contentsType );
  folder->setSystemFolder( true );
  folder->storage()->writeConfig();
  folder->open();
  connectFolder( folder );
  return folder;
}

void KMailICalIfaceImpl::connectFolder( KMFolder* folder )
{
  // avoid multiple connections
  disconnect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
              this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
  disconnect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
              this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
  disconnect( folder, SIGNAL( expunged( KMFolder* ) ),
              this, SLOT( slotRefreshFolder( KMFolder* ) ) );
  disconnect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
              this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
  disconnect( folder, SIGNAL( nameChanged() ),
              this, SLOT( slotFolderRenamed() ) );
  disconnect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
              this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );

  // Setup the signals to listen for changes
  connect( folder, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
           this, SLOT( slotIncidenceAdded( KMFolder*, Q_UINT32 ) ) );
  connect( folder, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
           this, SLOT( slotIncidenceDeleted( KMFolder*, Q_UINT32 ) ) );
  connect( folder, SIGNAL( expunged( KMFolder* ) ),
           this, SLOT( slotRefreshFolder( KMFolder* ) ) );
  connect( folder->storage(), SIGNAL( readOnlyChanged( KMFolder* ) ),
           this, SLOT( slotFolderPropertiesChanged( KMFolder* ) ) );
  connect( folder, SIGNAL( nameChanged() ),
           this, SLOT( slotFolderRenamed() ) );
  connect( folder->storage(), SIGNAL( locationChanged( const QString&, const QString&) ),
           this, SLOT( slotFolderLocationChanged( const QString&, const QString&) ) );

}

static void cleanupFolder( KMFolder* folder, KMailICalIfaceImpl* _this )
{
  if( folder ) {
    folder->setType( "plain" );
    folder->setSystemFolder( false );
    folder->disconnect( _this );
    folder->close();
  }
}

void KMailICalIfaceImpl::cleanup()
{
  cleanupFolder( mContacts, this );
  cleanupFolder( mCalendar, this );
  cleanupFolder( mNotes, this );
  cleanupFolder( mTasks, this );
  cleanupFolder( mJournals, this );

  mContacts = mCalendar = mNotes = mTasks = mJournals = 0;
}

QString KMailICalIfaceImpl::folderPixmap( KFolderTreeItem::Type type ) const
{
  if( !mUseResourceIMAP )
    return QString::null;

  if( type == KFolderTreeItem::Contacts )
    return QString::fromLatin1( "kmgroupware_folder_contacts" );
  else if( type == KFolderTreeItem::Calendar )
    return QString::fromLatin1( "kmgroupware_folder_calendar" );
  else if( type == KFolderTreeItem::Notes )
    return QString::fromLatin1( "kmgroupware_folder_notes" );
  else if( type == KFolderTreeItem::Tasks )
    return QString::fromLatin1( "kmgroupware_folder_tasks" );
  else if( type == KFolderTreeItem::Journals )
    return QString::fromLatin1( "kmgroupware_folder_journals" );

  return QString::null;
}

static void reloadFolderTree()
{
  // Make the folder tree show the icons or not
  kmkernel->folderMgr()->contentsChanged();
}

// This is a very light-weight and fast 'parser' to retrieve
// a data entry from a vCal taking continuation lines
// into account
static void vPartMicroParser( const QString& str, QString& s )
{
  QString line;
  uint len = str.length();

  for( uint i=0; i<len; ++i){
    if( str[i] == '\r' || str[i] == '\n' ){
      if( str[i] == '\r' )
        ++i;
      if( i+1 < len && str[i+1] == ' ' ){
        // found a continuation line, skip it's leading blanc
        ++i;
      }else{
        // found a logical line end, process the line
        if( line.startsWith( s ) ) {
          s = line.mid( s.length() + 1 );
          return;
        }
        line = "";
      }
    } else {
      line += str[i];
    }
  }

  // Not found. Clear it
  s.truncate(0);
}

// Returns the first child folder having the given annotation
static KMFolder* findFolderByAnnotation( KMFolderDir* folderParentDir, const QString& annotation )
{
    QPtrListIterator<KMFolderNode> it( *folderParentDir );
    for ( ; it.current(); ++it ) {
      if ( !it.current()->isDir() ) {
        KMFolder* folder = static_cast<KMFolder *>( it.current() );
        if ( folder->folderType() == KMFolderTypeCachedImap ) {
          QString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
          //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
          if ( folderAnnotation == annotation )
            return folder;
        }
      }
    }
    return 0;
}

KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType )
{
  if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
  {
    // Look for a folder with an annotation like "event.default"
    KMFolder* folder = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) + ".default" );
    if ( folder )
      return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundAndStandard );

    // Fallback: look for a folder with an annotation like "event"
    folder = findFolderByAnnotation( folderParentDir, QString( s_folderContentsType[contentsType].annotation ) );
    if ( folder )
      return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundByType );

    // Fallback: look for the folder by name (we'll need to change its type)
    KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) );
    if ( node && !node->isDir() )
      return StandardFolderSearchResult( static_cast<KMFolder *>( node ), StandardFolderSearchResult::FoundByName );

    kdDebug(5006) << "findStandardResourceFolder: found no resource folder for " << s_folderContentsType[contentsType].annotation << endl;
    return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
  }
  else // icalvcard: look up standard resource folders by name
  {
    KFolderTreeItem::Type itemType = s_folderContentsType[contentsType].treeItemType;
    unsigned int folderLanguage = GlobalSettings::self()->theIMAPResourceFolderLanguage();
    if( folderLanguage > 3 ) folderLanguage = 0;
    KMFolderNode* node = folderParentDir->hasNamedFolder( folderName( itemType, folderLanguage ) );
    if ( !node || node->isDir() )
      return StandardFolderSearchResult( 0, StandardFolderSearchResult::NotFound );
    return StandardFolderSearchResult( static_cast<KMFolder*>( node ), StandardFolderSearchResult::FoundAndStandard );
  }
}


/* We treat all folders as relevant wrt alarms for which we have Administer
 * rights or for which the "Incidences relevant for everyone" annotation has
 * been set. It can be reasonably assumed that those are "ours". All local folders
 * must be ours anyhow. */
bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder )
{
  bool administerRights = true;
  bool relevantForOwner = true;
  bool relevantForEveryone = false;
  if ( folder->folderType() == KMFolderTypeImap ) {
    const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
    administerRights =
      imapFolder->userRights() <= 0 || imapFolder->userRights() & KMail::ACLJobs::Administer;
  }
  if ( folder->folderType() == KMFolderTypeCachedImap ) {
    const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
    administerRights =
      dimapFolder->userRights() <= 0 || dimapFolder->userRights() & KMail::ACLJobs::Administer;
    relevantForOwner = dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins;
    relevantForEveryone = ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders );
  }
#if 0
  kdDebug(5006) << k_funcinfo << endl;
  kdDebug(5006) << "Folder: " << folder->label() << " has administer rights: " << administerRights << endl;
  kdDebug(5006) << "and is relevant for owner: " << relevantForOwner <<  endl;
  kdDebug(5006) << "and relevant for everyone: "  << relevantForEveryone << endl;
#endif
  return ( administerRights && relevantForOwner ) || relevantForEveryone;
}

void KMailICalIfaceImpl::setResourceQuiet(bool q)
{
  mResourceQuiet = q;
}

bool KMailICalIfaceImpl::isResourceQuiet() const
{
  return mResourceQuiet;
}

#include "kmailicalifaceimpl.moc"
