|
|
|
/*
|
|
|
|
This file is part of the exchange resource.
|
|
|
|
Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
|
|
|
|
Parts are derived from the old libkpimexchange library:
|
|
|
|
Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
|
|
|
|
|
|
|
|
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., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "exchangeconvertercalendar.h"
|
|
|
|
#include <webdavhandler.h>
|
|
|
|
#include <libkcal/incidence.h>
|
|
|
|
#include <libkcal/event.h>
|
|
|
|
#include <libkcal/journal.h>
|
|
|
|
#include <libkcal/todo.h>
|
|
|
|
#include <libkcal/icalformat.h>
|
|
|
|
#include <libemailfunctions/email.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
using namespace KCal;
|
|
|
|
|
|
|
|
#define TaskNamespace1 "http://schemas.microsoft.com/mapi/id/{00062003-0000-0000-C000-000000000046}/"
|
|
|
|
#define TaskProp_Status "0x00008101"
|
|
|
|
#define TaskProp_PercentCompleted "0x00008102"
|
|
|
|
#define TaskProp_DtStart "0x00008104"
|
|
|
|
#define TaskProp_DtDue "0x00008105"
|
|
|
|
#define TaskProp_Duration "0x00008106"
|
|
|
|
#define TaskProp_CompletionDate "0x0000810f"
|
|
|
|
#define TaskProp_IsCompleted "0x0000811C"
|
|
|
|
#define TaskProp_Owner "0x0000811F"
|
|
|
|
#define TaskProp_DoesRecur "0x00008126"
|
|
|
|
|
|
|
|
#define TaskNamespace2 "http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/"
|
|
|
|
#define TaskProp_ReminderTime "0x00008502"
|
|
|
|
#define TaskProp_ReminderSet "0x00008503"
|
|
|
|
#define TaskProp_ReminderPlaySound "0x0000851E"
|
|
|
|
#define TaskProp_ReminderSoundFile "0x0000851F"
|
|
|
|
#define TaskProp_ContactNames "0x0000853A"
|
|
|
|
|
|
|
|
|
|
|
|
ExchangeConverterCalendar::ExchangeConverterCalendar()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExchangeConverterCalendar::setTimeZone( const TQString &id )
|
|
|
|
{
|
|
|
|
// kdDebug() << "Setting timezone to: " << id << endl;
|
|
|
|
mFormat.setTimeZone( id, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
#define propertyDAV( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, root, "d:" prop )
|
|
|
|
#define propertyNS( ns, prop ) \
|
|
|
|
WebdavHandler::addElementNS( doc, root, ns, prop )
|
|
|
|
#define propertyCalendar( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, root, "c:" prop )
|
|
|
|
#define propertyHTTPMail( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, root, "m:" prop )
|
|
|
|
#define propertyMailHeader( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, root, "h:" prop )
|
|
|
|
#define property( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, root, prop )
|
|
|
|
|
|
|
|
void ExchangeConverterCalendar::createRequestIncidence( TQDomDocument &doc, TQDomElement &root )
|
|
|
|
{
|
|
|
|
propertyDAV( "contentclass" );
|
|
|
|
propertyDAV( "getcontenttype" );
|
|
|
|
propertyNS( "http://schemas.microsoft.com/exchange/", "outlookmessageclass" );
|
|
|
|
propertyDAV( "getetag" );
|
|
|
|
propertyDAV( "href" );
|
|
|
|
propertyDAV( "isreadonly" );
|
|
|
|
propertyNS( "http://schemas.microsoft.com/repl/", "repl-uid" );
|
|
|
|
propertyHTTPMail( "subject" );
|
|
|
|
propertyHTTPMail( "textdescription" );
|
|
|
|
propertyHTTPMail( "date" );
|
|
|
|
propertyDAV( "comment" );
|
|
|
|
propertyNS( "urn:schemas-microsoft-com:office:office", "Keywords" );
|
|
|
|
propertyNS( "http://schemas.microsoft.com/exchange/", "sensitivity" );
|
|
|
|
propertyHTTPMail( "priority" );
|
|
|
|
propertyHTTPMail( "from" );
|
|
|
|
propertyHTTPMail( "to" );
|
|
|
|
propertyHTTPMail( "cc" );
|
|
|
|
propertyHTTPMail( "bcc" );
|
|
|
|
propertyHTTPMail( "hasattachment" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExchangeConverterCalendar::createRequestAppointment( TQDomDocument &doc, TQDomElement &root )
|
|
|
|
{
|
|
|
|
createRequestIncidence( doc, root );
|
|
|
|
TQDomAttr att_c = doc.createAttribute( "xmlns:c" );
|
|
|
|
att_c.setValue( "urn:schemas:calendar:" );
|
|
|
|
doc.documentElement().setAttributeNode( att_c );
|
|
|
|
propertyCalendar( "uid" );
|
|
|
|
propertyCalendar( "created" );
|
|
|
|
propertyCalendar( "lastmodified" );
|
|
|
|
propertyCalendar( "dtstamp" );
|
|
|
|
propertyCalendar( "sequence" );
|
|
|
|
propertyCalendar( "location" );
|
|
|
|
propertyCalendar( "busystatus" );
|
|
|
|
propertyCalendar( "transparent" );
|
|
|
|
propertyCalendar( "timezone" );
|
|
|
|
propertyCalendar( "alldayevent" );
|
|
|
|
propertyCalendar( "dtstart" );
|
|
|
|
propertyCalendar( "dtend" );
|
|
|
|
propertyCalendar( "duration" );
|
|
|
|
propertyCalendar( "rrule" );
|
|
|
|
propertyCalendar( "rdate" );
|
|
|
|
propertyCalendar( "exrule" );
|
|
|
|
propertyCalendar( "exdate" );
|
|
|
|
propertyCalendar( "recurrenceid" );
|
|
|
|
propertyCalendar( "instancetype" );
|
|
|
|
propertyCalendar( "reminderoffset" );
|
|
|
|
propertyCalendar( "resources" );
|
|
|
|
}
|
|
|
|
|
|
|
|
#define propertyTask1( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, props, "t1:" prop )
|
|
|
|
#define propertyTask2( prop ) \
|
|
|
|
WebdavHandler::addElement( doc, props, "t2:" prop )
|
|
|
|
|
|
|
|
void ExchangeConverterCalendar::createRequestTask( TQDomDocument &doc, TQDomElement &props )
|
|
|
|
{
|
|
|
|
createRequestIncidence( doc, props );
|
|
|
|
|
|
|
|
TQDomElement root = doc.documentElement();
|
|
|
|
|
|
|
|
TQDomAttr att_t1 = doc.createAttribute( "xmlns:t1" );
|
|
|
|
att_t1.setValue( TaskNamespace1 );
|
|
|
|
root.setAttributeNode( att_t1 );
|
|
|
|
|
|
|
|
TQDomAttr att_t2 = doc.createAttribute( "xmlns:t2" );
|
|
|
|
att_t2.setValue( TaskNamespace2 );
|
|
|
|
root.setAttributeNode( att_t2 );
|
|
|
|
|
|
|
|
// TODO: Insert the correct namespaces here:
|
|
|
|
// propertyTask1( TaskProp_UID );
|
|
|
|
propertyDAV( "creationdate" );
|
|
|
|
propertyDAV( "getlastmodified" );
|
|
|
|
propertyTask1( TaskProp_Owner );
|
|
|
|
propertyTask2( TaskProp_ContactNames );
|
|
|
|
propertyTask1( TaskProp_DtStart );
|
|
|
|
propertyTask1( TaskProp_DtDue );
|
|
|
|
propertyTask1( TaskProp_Duration );
|
|
|
|
propertyTask1( TaskProp_IsCompleted );
|
|
|
|
propertyTask1( TaskProp_PercentCompleted );
|
|
|
|
propertyTask1( TaskProp_CompletionDate );
|
|
|
|
propertyTask1( TaskProp_DoesRecur );
|
|
|
|
// What to do about recurrence rules?
|
|
|
|
propertyTask2( TaskProp_ReminderSet );
|
|
|
|
propertyTask2( TaskProp_ReminderTime );
|
|
|
|
propertyTask2( TaskProp_ReminderPlaySound );
|
|
|
|
propertyTask2( TaskProp_ReminderSoundFile );
|
|
|
|
propertyTask1( TaskProp_Status );
|
|
|
|
}
|
|
|
|
#undef propertyTask1
|
|
|
|
#undef propertyTask2
|
|
|
|
|
|
|
|
void ExchangeConverterCalendar::createRequestJournal( TQDomDocument &doc, TQDomElement &root )
|
|
|
|
{
|
|
|
|
createRequestIncidence( doc, root );
|
|
|
|
propertyDAV( "uid" );
|
|
|
|
propertyDAV( "creationdate" );
|
|
|
|
propertyDAV( "getlastmodified" );
|
|
|
|
}
|
|
|
|
#undef propertyDAV
|
|
|
|
#undef propertyNS
|
|
|
|
#undef propertyCalendar
|
|
|
|
#undef propertyHTTPMail
|
|
|
|
#undef propertyMailHeader
|
|
|
|
#undef property
|
|
|
|
|
|
|
|
bool ExchangeConverterCalendar::readTZ( const TQDomElement &node, Incidence */*incidence*/ )
|
|
|
|
{
|
|
|
|
TQString timezoneid;
|
|
|
|
if ( WebdavHandler::extractString( node, "timezoneid", timezoneid ) ) {
|
|
|
|
// kdDebug() << "DEBUG: timezoneid = " << timezoneid << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString timezone;
|
|
|
|
if ( WebdavHandler::extractString( node, "timezone", timezone ) ) {
|
|
|
|
// kdDebug() << "DEBUG: timezone = " << timezone << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
/* // mFormat is used for parsing recurrence rules.
|
|
|
|
TQString localTimeZoneId;
|
|
|
|
if ( mCalendar ) {
|
|
|
|
mFormat.setTimeZone( mCalendar->timeZoneId(), !mCalendar->isLocalTime() );
|
|
|
|
localTimeZoneId = mCalendar->timeZoneId();
|
|
|
|
} else {
|
|
|
|
localTimeZoneId = "UTC";
|
|
|
|
// If no mCalendar, stay in UTC
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExchangeConverterCalendar::readIncidence( const TQDomElement &node, Incidence *incidence )
|
|
|
|
{
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readIncidencd"<<endl;
|
|
|
|
TQDateTime tmpdt;
|
|
|
|
bool tmpbool;
|
|
|
|
TQString tmpstr;
|
|
|
|
long tmplng;
|
|
|
|
TQStringList tmplst;
|
|
|
|
|
|
|
|
readTZ( node, incidence );
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractString( node, "getetag", tmpstr ) )
|
|
|
|
incidence->setCustomProperty( "TDEPIM-Exchange-Resource", "fingerprint", tmpstr );
|
|
|
|
if ( WebdavHandler::extractString( node, "href", tmpstr ) )
|
|
|
|
incidence->setCustomProperty( "TDEPIM-Exchange-Resource", "href", tmpstr );
|
|
|
|
|
|
|
|
// FIXME: use repl-uid as scheduling id?
|
|
|
|
if ( WebdavHandler::extractString( node, "textdescription", tmpstr ) )
|
|
|
|
incidence->setDescription( tmpstr );
|
|
|
|
if ( WebdavHandler::extractString( node, "subject", tmpstr ) )
|
|
|
|
incidence->setSummary( tmpstr );
|
|
|
|
if ( WebdavHandler::extractStringList( node, "Keywords", tmplst ) )
|
|
|
|
incidence->setCategories( tmplst );
|
|
|
|
|
|
|
|
// Use "created" or "creationdate"?
|
|
|
|
if ( WebdavHandler::extractBool( node, "isreadonly" , tmpbool ) )
|
|
|
|
incidence->setReadOnly( tmpbool );
|
|
|
|
|
|
|
|
// FIXME: Ignore the comment for now
|
|
|
|
|
|
|
|
// Exchange sentitivity values:
|
|
|
|
// 0 None, 1 Personal, 2 Private, 3 Company Confidential
|
|
|
|
if ( WebdavHandler::extractLong( node, "sensitivity", tmplng ) ) {
|
|
|
|
switch( tmplng ) {
|
|
|
|
case 0: incidence->setSecrecy( KCal::Incidence::SecrecyPublic ); break;
|
|
|
|
case 1:
|
|
|
|
case 2: incidence->setSecrecy( KCal::Incidence::SecrecyPrivate ); break;
|
|
|
|
case 3: incidence->setSecrecy( KCal::Incidence::SecrecyConfidential ); break;
|
|
|
|
default: kdWarning() << "Unknown sensitivity: " << tmplng << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractBool( node, "hasattachment", tmpbool ) && tmpbool ) {
|
|
|
|
// FIXME: Extract attachments...
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractLong( node, "priority", tmplng ) )
|
|
|
|
incidence->setPriority( tmplng );
|
|
|
|
|
|
|
|
// FIXME: Use the urn:schemes:httpmail:date property for what?
|
|
|
|
|
|
|
|
// Organizer, required and optional Attendees:
|
|
|
|
if ( WebdavHandler::extractString( node, "from", tmpstr ) )
|
|
|
|
incidence->setOrganizer( tmpstr );
|
|
|
|
if ( WebdavHandler::extractString( node, "to", tmpstr ) ) {
|
|
|
|
TQStringList atts( KPIM::splitEmailAddrList( tmpstr ) );
|
|
|
|
for ( TQStringList::Iterator it = atts.begin(); it != atts.end(); ++it ) {
|
|
|
|
TQString name, email;
|
|
|
|
KPIM::getNameAndMail( *it, name, email );
|
|
|
|
Attendee *att = new Attendee( name, email );
|
|
|
|
att->setRole( KCal::Attendee::ReqParticipant );
|
|
|
|
// FIXME: Retrieve the other attendee properties somehow...
|
|
|
|
// urn:schemas:calendar:method
|
|
|
|
// urn:schemas:calendar:responserequested
|
|
|
|
// urn:schemas:calendar:meetingstatus
|
|
|
|
// urn:schemas:calendar:replytime
|
|
|
|
incidence->addAttendee( att );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( WebdavHandler::extractString( node, "cc", tmpstr ) ) {
|
|
|
|
TQStringList atts( KPIM::splitEmailAddrList( tmpstr ) );
|
|
|
|
for ( TQStringList::Iterator it = atts.begin(); it != atts.end(); ++it ) {
|
|
|
|
TQString name, email;
|
|
|
|
KPIM::getNameAndMail( *it, name, email );
|
|
|
|
Attendee *att = new Attendee( name, email );
|
|
|
|
att->setRole( KCal::Attendee::OptParticipant );
|
|
|
|
// FIXME: Retrieve the other attendee properties somehow...
|
|
|
|
// urn:schemas:calendar:method
|
|
|
|
// urn:schemas:calendar:responserequested
|
|
|
|
// urn:schemas:calendar:meetingstatus
|
|
|
|
// urn:schemas:calendar:replytime
|
|
|
|
incidence->addAttendee( att );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Handle recurrences
|
|
|
|
void ExchangeDownload::handleRecurrence( TQString uid )
|
|
|
|
{
|
|
|
|
// kdDebug() << "Handling recurrence info for uid=" << uid << endl;
|
|
|
|
TQString query =
|
|
|
|
"SELECT \"DAV:href\", \"urn:schemas:calendar:instancetype\"\r\n"
|
|
|
|
"FROM Scope('shallow traversal of \"\"')\r\n"
|
|
|
|
"WHERE \"urn:schemas:calendar:uid\" = '" + uid + "'\r\n"
|
|
|
|
" AND (\"urn:schemas:calendar:instancetype\" = 1)\r\n";
|
|
|
|
// " OR \"urn:schemas:calendar:instancetype\" = 3)\r\n" // FIXME: exception are not handled
|
|
|
|
|
|
|
|
// kdDebug() << "Exchange master query: " << endl << query << endl;
|
|
|
|
|
|
|
|
TDEIO::DavJob* job = TDEIO::davSearch( mAccount->calendarURL(), "DAV:", "sql",
|
|
|
|
query, false );
|
|
|
|
TDEIO::Scheduler::scheduleJob( job );
|
|
|
|
job->setWindow( mWindow );
|
|
|
|
connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
|
|
|
|
TQT_SLOT( slotMasterResult( TDEIO::Job * ) ) );
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool ExchangeConverterCalendar::readEvent( const TQDomElement &node, Event *event )
|
|
|
|
{
|
|
|
|
if ( !readIncidence( node, event ) ) return false;
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readEvent"<<endl;
|
|
|
|
|
|
|
|
TQDateTime tmpdt;
|
|
|
|
TQString tmpstr;
|
|
|
|
long tmplng;
|
|
|
|
bool tmpbool;
|
|
|
|
|
|
|
|
// The UID is absolutely required!
|
|
|
|
if ( WebdavHandler::extractString( node, "uid", tmpstr ) ) {
|
|
|
|
event->setUid( tmpstr );
|
|
|
|
} else {
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readIncidence: ERROR: No UID given"<<endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "created", tmpdt ) )
|
|
|
|
event->setCreated( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "lastmodified", tmpdt ) )
|
|
|
|
event->setLastModified( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
// FIXME: Retrieve time zone: "timezone"
|
|
|
|
// FIXME: Use the "recurrenceid" prop for the recurrenceId of the event (which is protected!)
|
|
|
|
|
|
|
|
// FIXME: Retrieve MICROSOFT-CDO-* tags first
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractLong( node, "sequence", tmplng ) )
|
|
|
|
event->setRevision( tmplng );
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractString( node, "location", tmpstr ) )
|
|
|
|
event->setLocation( tmpstr );
|
|
|
|
|
|
|
|
// FIXME: Use "organizer" here instead of the From: person?
|
|
|
|
/* if ( WebdavHandler::extractString( node, "organizer", tmpstr ) )
|
|
|
|
incidence->setOrganizer( tmpstr );*/
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "dtstart", tmpdt ) ) {
|
|
|
|
event->setDtStart( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool allDay = false;
|
|
|
|
if ( WebdavHandler::extractBool( node, "alldayevent", allDay ) )
|
|
|
|
event->setFloats( allDay );
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractLong( node, "duration", tmplng ) ) {
|
|
|
|
if (allDay)
|
|
|
|
tmplng--; // Otherwise event extends into next day
|
|
|
|
event->setDuration( tmplng );
|
|
|
|
// kdDebug() << "DURATION " << tmplng << "\n";
|
|
|
|
} else if ( WebdavHandler::extractDateTime( node, "dtend", tmpdt ) ) {
|
|
|
|
event->setDtEnd( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Here we have two different props for the same thing?!?!?
|
|
|
|
if ( WebdavHandler::extractLong( node, "transparent", tmplng ) )
|
|
|
|
event->setTransparency( tmplng>0 ? Event::Transparent : Event::Opaque );
|
|
|
|
if ( WebdavHandler::extractString( node, "busystatus", tmpstr ) ) {
|
|
|
|
if ( tmpstr == "FREE" )
|
|
|
|
event->setTransparency( KCal::Event::Transparent );
|
|
|
|
if ( tmpstr == "BUSY" )
|
|
|
|
event->setTransparency( KCal::Event::Opaque );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractLong( node, "reminderoffset", tmplng ) ) {
|
|
|
|
// Duration before event in seconds
|
|
|
|
KCal::Duration offset( -tmplng );
|
|
|
|
KCal::Alarm *alarm = event->newAlarm();
|
|
|
|
alarm->setStartOffset( offset );
|
|
|
|
alarm->setEnabled( true );
|
|
|
|
alarm->setType( KCal::Alarm::Display);
|
|
|
|
// TODO: multiple alarms;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractString( node, "rrule", tmpstr ) && !tmpstr.isEmpty() ) {
|
|
|
|
kdDebug() << "Got rrule: " << tmpstr << endl;
|
|
|
|
// Timezone should be handled automatically
|
|
|
|
// because we used mFormat.setTimeZone() earlier
|
|
|
|
// FIXME: Implement this using the format!
|
|
|
|
RecurrenceRule *rrule = event->recurrence()->defaultRRule( true );
|
|
|
|
if ( ! mFormat.fromString( rrule, tmpstr ) ) {
|
|
|
|
kdError() << "ERROR parsing rrule " << tmpstr << endl;
|
|
|
|
event->recurrence()->addRRule( rrule );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList tmplst;
|
|
|
|
if ( WebdavHandler::extractStringList( node, "exdate", tmplst ) ) {
|
|
|
|
TQStringList::Iterator it = tmplst.begin();
|
|
|
|
KCal::DateList exdates;
|
|
|
|
for ( ; it != tmplst.end(); ++it ) {
|
|
|
|
exdates.append( /*utcAsZone(*/ TQDateTime::fromString( *it, TQt::ISODate )/*,
|
|
|
|
localTimeZoneId )*/.date() );
|
|
|
|
}
|
|
|
|
event->recurrence()->setExDates( exdates );
|
|
|
|
}
|
|
|
|
// FIXME: use rdate and exrule!
|
|
|
|
/* FIXME: Recurring events, they are split up
|
|
|
|
TQDomElement instancetypeElement = prop.namedItem( "instancetype" ).toElement();
|
|
|
|
if ( instancetypeElement.isNull() ) {
|
|
|
|
kdError() << "Error: no instance type in Exchange server reply" << endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int instanceType = instancetypeElement.text().toInt();
|
|
|
|
//kdDebug() << "Instance type: " << instanceType << endl;
|
|
|
|
|
|
|
|
if ( recurrence && instanceType > 0 ) {
|
|
|
|
TQDomElement uidElement = prop.namedItem( "uid" ).toElement();
|
|
|
|
if ( uidElement.isNull() ) {
|
|
|
|
kdError() << "Error: no uid in Exchange server reply" << endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TQString uid = uidElement.text();
|
|
|
|
if ( ! m_uids.contains( uid ) ) {
|
|
|
|
m_uids[uid] = 1;
|
|
|
|
handleRecurrence(uid);
|
|
|
|
successCount++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: read the resources from the "resources" tag
|
|
|
|
|
|
|
|
// FIXME: Custom fields not yet implemented
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExchangeConverterCalendar::readTodo( const TQDomElement &node, Todo *todo )
|
|
|
|
{
|
|
|
|
if ( !readIncidence( node, todo ) ) return false;
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readTodo"<<endl;
|
|
|
|
|
|
|
|
// FIXME: Retrieve time zone: "timezone"
|
|
|
|
// FIXME: What to with TaskProp_Owner and TaskProp_ContactNames?
|
|
|
|
|
|
|
|
TQDateTime tmpdt;
|
|
|
|
float tmpfloat;
|
|
|
|
long tmplong;
|
|
|
|
bool tmpbool;
|
|
|
|
TQString tmpstr;
|
|
|
|
|
|
|
|
// The UID is absolutely required!
|
|
|
|
// FIXME: Which field shall be used as uid???
|
|
|
|
/* if ( WebdavHandler::extractString( node, "uid", tmpstr ) ) {
|
|
|
|
todo->setUid( tmpstr );
|
|
|
|
} else {
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readIncidence: ERROR: No UID given"<<endl;
|
|
|
|
return false;
|
|
|
|
}*/
|
|
|
|
// if ( WebdavHandler::extractDateTime( node, "created", tmpdt ) )
|
|
|
|
/* FIXME: creation and last modification dates:
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "creationdate", tmpdt ) )
|
|
|
|
incidence->setCreated( tmpdt );
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "getlastmodified", tmpdt ) )
|
|
|
|
incidence->setLastModified( tmpdt );*/
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractDateTime( node, TaskProp_DtStart, tmpdt ) )
|
|
|
|
todo->setDtStart( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
if ( WebdavHandler::extractDateTime( node, TaskProp_DtDue, tmpdt ) )
|
|
|
|
todo->setDtDue( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
if ( WebdavHandler::extractLong( node, TaskProp_Duration, tmplong ) )
|
|
|
|
todo->setDuration( tmplong );
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractBool( node, TaskProp_IsCompleted, tmpbool ) && tmpbool ) {
|
|
|
|
todo->setCompleted( tmpbool );
|
|
|
|
if ( tmpbool && WebdavHandler::extractDateTime( node, TaskProp_CompletionDate, tmpdt ) ) {
|
|
|
|
todo->setCompleted( WebdavHandler::utcAsZone( tmpdt, mFormat.timeZoneId() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( WebdavHandler::extractFloat( node, TaskProp_PercentCompleted, tmpfloat ) )
|
|
|
|
todo->setPercentComplete( (int)(tmpfloat*100) );
|
|
|
|
|
|
|
|
// FIXME: Recurrence, using TaskProp_DoesRecur
|
|
|
|
// What to do about recurrence rules?
|
|
|
|
|
|
|
|
// FIXME: Reminders, use TaskProp_ReminderSet, TaskProp_ReminderTime,
|
|
|
|
// TaskProp_ReminderPlaySound, TaskProp_ReminderSoundFile, TaskProp_Status
|
|
|
|
// But how do I get the offset?
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExchangeConverterCalendar::readJournal( const TQDomElement &node, Journal *journal )
|
|
|
|
{
|
|
|
|
if ( !readIncidence( node, journal ) ) return false;
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readJournal"<<endl;
|
|
|
|
TQDateTime tmpdt;
|
|
|
|
TQString tmpstr;
|
|
|
|
// The UID is absolutely required!
|
|
|
|
// FIXME: Which field shall be used as UID?
|
|
|
|
if ( WebdavHandler::extractString( node, "uid", tmpstr ) ) {
|
|
|
|
journal->setUid( tmpstr );
|
|
|
|
} else {
|
|
|
|
kdDebug()<<"ExchangeConverterCalendar::readJournal: ERROR: No UID given"<<endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* FIXME: creation and last modification times:
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "created", tmpdt ) )
|
|
|
|
incidence->setCreated( tmpdt );
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "lastmodified", tmpdt ) )
|
|
|
|
incidence->setLastModified( tmpdt );*/
|
|
|
|
|
|
|
|
if ( WebdavHandler::extractDateTime( node, "date", tmpdt ) )
|
|
|
|
journal->setDtStart( tmpdt );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List ExchangeConverterCalendar::parseWebDAV( const TQDomDocument& davdata )
|
|
|
|
{
|
|
|
|
TQDomElement prop = davdata.documentElement().namedItem( "response" )
|
|
|
|
.namedItem( "propstat" ).namedItem( "prop" ).toElement();
|
|
|
|
if ( prop.isNull() ) return Incidence::List();
|
|
|
|
|
|
|
|
TQString contentclass;
|
|
|
|
bool success = WebdavHandler::extractString( prop, "contentclass", contentclass );
|
|
|
|
if ( !success ) return Incidence::List();
|
|
|
|
|
|
|
|
Incidence *incidence = 0;
|
|
|
|
success = false;
|
|
|
|
if ( contentclass == "urn:content-classes:appointment" ) {
|
|
|
|
Event *event = new Event();
|
|
|
|
success = readEvent( prop, event );
|
|
|
|
incidence = event;
|
|
|
|
} else if ( contentclass == "urn:content-classes:task" ) {
|
|
|
|
Todo *todo = new Todo();
|
|
|
|
success = readTodo( prop, todo );
|
|
|
|
incidence = todo;
|
|
|
|
} else if ( contentclass == "urn:content-classes:journal" ||
|
|
|
|
contentclass == "urn:content-classes:message" ) {
|
|
|
|
Journal *journal = new Journal();
|
|
|
|
success = readJournal( prop, journal );
|
|
|
|
incidence = journal;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List list;
|
|
|
|
if ( success ) {
|
|
|
|
list.append( incidence );
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define domDavProperty( name, value ) \
|
|
|
|
WebdavHandler::addElement( mDoc, mElement, "d:" name, value )
|
|
|
|
#define domProperty( NS, name, value ) \
|
|
|
|
WebdavHandler::addElementNS( mDoc, mElement, NS, name, value )
|
|
|
|
#define domCalendarProperty( name, value ) \
|
|
|
|
WebdavHandler::addElement( mDoc, mElement, "c:" name, value )
|
|
|
|
#define domHTTPMailProperty( name, value ) \
|
|
|
|
WebdavHandler::addElement( mDoc, mElement, "m:" name, value )
|
|
|
|
#define domMailHeaderProperty( name, value ) \
|
|
|
|
WebdavHandler::addElement( mDoc, mElement, "h:" name, value )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExchangeConverterCalendar::createWebDAVVisitor : public IncidenceBase::Visitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
createWebDAVVisitor() : Visitor() {}
|
|
|
|
bool act( TQDomDocument doc, TQDomElement el, IncidenceBase *incidence, const TQString &timeZoneId )
|
|
|
|
{
|
|
|
|
mDoc = doc;
|
|
|
|
mElement = el;
|
|
|
|
mTimeZoneId = timeZoneId;
|
|
|
|
return incidence->accept( *this );
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
void addBoolProp( TQDomElement &el ) { el.setAttribute( "b:dt", "boolean" ); }
|
|
|
|
void addDateProp( TQDomElement &el ) { el.setAttribute( "b:dt", "dateTime.tz" ); }
|
|
|
|
void addFloatProp( TQDomElement &el ) { el.setAttribute( "b:dt", "float" ); }
|
|
|
|
void addIntProp( TQDomElement &el ) { el.setAttribute( "b:dt", "int" ); }
|
|
|
|
TQString timePropString( const TQDateTime &dt ) { return dt.toString( TQt::ISODate )+"Z"; }
|
|
|
|
|
|
|
|
bool visitIncidence( Incidence *incidence )
|
|
|
|
{
|
|
|
|
TQString tmpstr;
|
|
|
|
domDavProperty( "isreadonly", (incidence->isReadOnly())?"1":"0" );
|
|
|
|
// FIXME: scheduling ID
|
|
|
|
// domProperty( "http://schemas.microsoft.com/repl/", "repl-uid", ??? );
|
|
|
|
domHTTPMailProperty( "subject", incidence->summary() );
|
|
|
|
domHTTPMailProperty( "textdescription", incidence->description() );
|
|
|
|
// FIXME: timestampt, comments and categories
|
|
|
|
// domHTTPMailProperty( "date", ??? ); // timestamp not available in libkcal
|
|
|
|
// domDavProperty( "comment", incidence->comments() ); // libkcal has a TQStringlist, not one string
|
|
|
|
// domProperty( "urn:schemas-microsoft-com:office:office", "Keywords", ??? ); // It's a <v>entyr1</v><v>entry2</v> String list!
|
|
|
|
tmpstr = TQString();
|
|
|
|
switch ( incidence->secrecy() ) {
|
|
|
|
case KCal::Incidence::SecrecyPublic: tmpstr = "0"; break;
|
|
|
|
case KCal::Incidence::SecrecyPrivate: tmpstr = "2"; break;
|
|
|
|
case KCal::Incidence::SecrecyConfidential: tmpstr = "3"; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
if ( !tmpstr.isEmpty() )
|
|
|
|
domProperty( "http://schemas.microsoft.com/exchange/", "sensitivity", tmpstr );
|
|
|
|
|
|
|
|
domHTTPMailProperty( "priority", TQString::number(incidence->priority()) );
|
|
|
|
|
|
|
|
domMailHeaderProperty( "from", incidence->organizer().fullName() );
|
|
|
|
|
|
|
|
// Attendees:
|
|
|
|
tmpstr = TQString();
|
|
|
|
TQStringList reqattnames;
|
|
|
|
TQStringList optattnames;
|
|
|
|
Attendee::List atts = incidence->attendees();
|
|
|
|
for ( Attendee::List::Iterator it = atts.begin(); it != atts.end(); ++it ) {
|
|
|
|
switch ( (*it)->role() ) {
|
|
|
|
case KCal::Attendee::Chair:
|
|
|
|
case KCal::Attendee::ReqParticipant:
|
|
|
|
reqattnames << (*it)->fullName();
|
|
|
|
break;
|
|
|
|
case KCal::Attendee::OptParticipant:
|
|
|
|
case KCal::Attendee::NonParticipant:
|
|
|
|
optattnames << (*it)->fullName();
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
domMailHeaderProperty( "to", reqattnames.join(", ") );
|
|
|
|
|
|
|
|
domMailHeaderProperty( "cc", optattnames.join(", ") );
|
|
|
|
|
|
|
|
// FIXME: Attachments: propertyHTTPMail( "hasattachment" );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool visit( Event *event )
|
|
|
|
{
|
|
|
|
if ( !visitIncidence(event) ) return false;
|
|
|
|
|
|
|
|
TQDomAttr att_c = mDoc.createAttribute( "xmlns:c" );
|
|
|
|
att_c.setValue( "urn:schemas:calendar:" );
|
|
|
|
mDoc.documentElement().setAttributeNode( att_c );
|
|
|
|
|
|
|
|
domDavProperty( "contentclass", "urn:content-classes:appointment" );
|
|
|
|
domProperty( "http://schemas.microsoft.com/exchange/",
|
|
|
|
"outlookmessageclass", "IPM.Appointment" );
|
|
|
|
domCalendarProperty( "uid", event->uid() );
|
|
|
|
TQDomElement el = domCalendarProperty( "created", timePropString( WebdavHandler::zoneAsUtc( event->created(), mTimeZoneId ) ) );
|
|
|
|
addDateProp( el );
|
|
|
|
el = domCalendarProperty( "lastmodified", timePropString( WebdavHandler::zoneAsUtc( event->lastModified(), mTimeZoneId ) ) );
|
|
|
|
addDateProp( el );
|
|
|
|
// FIXME: domCalendarProperty( "dtstamp", ??);
|
|
|
|
// FIXME: domCalendarProperty( "sequence", event->sequence() );
|
|
|
|
domCalendarProperty( "location", event->location() );
|
|
|
|
|
|
|
|
TQString tmpstr;
|
|
|
|
switch ( event->transparency() ) {
|
|
|
|
case KCal::Event::Transparent: tmpstr = "FREE"; break;
|
|
|
|
case KCal::Event::Opaque: tmpstr = "BUSY"; break;
|
|
|
|
}
|
|
|
|
if ( !tmpstr.isEmpty() )
|
|
|
|
domCalendarProperty( "busystatus", tmpstr );
|
|
|
|
// FIXME: What do do with the "transparent" property?
|
|
|
|
// FIXME: Use the "timezone" property...
|
|
|
|
domCalendarProperty( "alldayevent", event->doesFloat()?"1":"0" );
|
|
|
|
el = domCalendarProperty( "dtstart", timePropString( WebdavHandler::zoneAsUtc( event->dtStart(), mTimeZoneId ) ) );
|
|
|
|
addDateProp( el );
|
|
|
|
if ( event->hasEndDate() ) {
|
|
|
|
el = domCalendarProperty( "dtend", timePropString( WebdavHandler::zoneAsUtc( event->dtEnd(), mTimeZoneId ) ) );
|
|
|
|
addDateProp( el );
|
|
|
|
} else {
|
|
|
|
domCalendarProperty( "duration", TQString::number( event->duration() ) );
|
|
|
|
}
|
|
|
|
// FIXME: Convert the recurrence rule to a string:
|
|
|
|
if ( event->doesRecur() ) {
|
|
|
|
// tmpstr = event->recurrence().....
|
|
|
|
// domCalendarProperty( "rrule", tmpstr );
|
|
|
|
// FIXME: Use "rdate" and "exrule"
|
|
|
|
// FIXME: Use "exdate", what's the syntax?
|
|
|
|
// FIXME: use the "instancetype" property
|
|
|
|
}
|
|
|
|
// FIXME: RecurrenceID is protected!
|
|
|
|
// domCalendarProperty( "recurrenceid", event->recurrenceId() );
|
|
|
|
// FIXME: "reminderoffset"
|
|
|
|
// FIXME: "resources" Alarm::List alarms = event->alarms();
|
|
|
|
|
|
|
|
Alarm::List::ConstIterator it;
|
|
|
|
Alarm::List alarms = event->alarms();
|
|
|
|
for( it = alarms.begin(); it != alarms.end(); ++it ) {
|
|
|
|
if ((*it)->hasStartOffset()) {
|
|
|
|
domCalendarProperty( "reminderoffset", TQString::number( (*it)->startOffset().asSeconds() * -1 ) );
|
|
|
|
} else {
|
|
|
|
kdDebug() << "ExchangeConverterCalendar::createWebDAVVisitor: Alarm type not supported\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool visit( Todo *todo )
|
|
|
|
{
|
|
|
|
if ( !visitIncidence(todo) ) return false;
|
|
|
|
|
|
|
|
TQDomAttr att_t1 = mDoc.createAttribute( "xmlns:t1" );
|
|
|
|
att_t1.setValue( TaskNamespace1 );
|
|
|
|
mDoc.documentElement().setAttributeNode( att_t1 );
|
|
|
|
|
|
|
|
TQDomAttr att_t2 = mDoc.createAttribute( "xmlns:t2" );
|
|
|
|
att_t2.setValue( TaskNamespace2 );
|
|
|
|
mDoc.documentElement().setAttributeNode( att_t2 );
|
|
|
|
|
|
|
|
|
|
|
|
domDavProperty( "contentclass", "urn:content-classes:task" );
|
|
|
|
domProperty( "http://schemas.microsoft.com/exchange/",
|
|
|
|
"outlookmessageclass", "IPM.Task" );
|
|
|
|
|
|
|
|
/* FIXME:
|
|
|
|
domCalendarProperty( "uid", todo->uid() );
|
|
|
|
domCalendarProperty( "created", todo->created().toString( TQt::ISODate ) );
|
|
|
|
domCalendarProperty( "lastmodified", todo->lastModified().toString( TQt::ISODate ) );*/
|
|
|
|
// TODO
|
|
|
|
/*propertyTask1( TaskProp_Owner );
|
|
|
|
propertyTask2( TaskProp_ContactNames );
|
|
|
|
propertyTask1( TaskProp_DtStart );
|
|
|
|
propertyTask1( TaskProp_DtDue );
|
|
|
|
propertyTask1( TaskProp_Duration );
|
|
|
|
propertyTask1( TaskProp_IsCompleted );
|
|
|
|
propertyTask1( TaskProp_PercentCompleted );
|
|
|
|
propertyTask1( TaskProp_CompetionDate );
|
|
|
|
propertyTask1( TaskProp_DoesRecur );
|
|
|
|
// What to do about recurrence rules?
|
|
|
|
propertyTask2( TaskProp_ReminderSet );
|
|
|
|
propertyTask2( TaskProp_ReminderTime );
|
|
|
|
propertyTask2( TaskProp_ReminderPlaySound );
|
|
|
|
propertyTask2( TaskProp_ReminderSoundFile );
|
|
|
|
propertyTask1( TaskProp_Status );*/
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool visit( Journal *journal )
|
|
|
|
{
|
|
|
|
if ( !visitIncidence(journal) ) return false;
|
|
|
|
domDavProperty( "contentclass", "urn:content-classes:journal" );
|
|
|
|
domProperty( "http://schemas.microsoft.com/exchange/",
|
|
|
|
"outlookmessageclass", "IPM.Journal" );
|
|
|
|
/* FIXME:
|
|
|
|
domCalendarProperty( "uid", todo->uid() );
|
|
|
|
domCalendarProperty( "created", todo->created().toString( TQt::ISODate ) );
|
|
|
|
domCalendarProperty( "lastmodified", todo->lastModified().toString( TQt::ISODate ) );*/
|
|
|
|
// TODO
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
TQDomDocument mDoc;
|
|
|
|
TQDomElement mElement;
|
|
|
|
TQString mTimeZoneId;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Prefixes for the namespaces:
|
|
|
|
// d... DAV:
|
|
|
|
// b... urn:schemas-microsoft-com:datatypes
|
|
|
|
// c... calendar
|
|
|
|
// m... httpmail
|
|
|
|
// h... httpheader
|
|
|
|
// p... mapi
|
|
|
|
// o... office
|
|
|
|
//
|
|
|
|
|
|
|
|
TQDomDocument ExchangeConverterCalendar::createWebDAV( Incidence *incidence )
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
TQDomDocument doc;
|
|
|
|
TQDomElement root = WebdavHandler::addDavElement( doc, doc, "d:propertyupdate" );
|
|
|
|
TQDomElement set = WebdavHandler::addElement( doc, root, "d:set" );
|
|
|
|
TQDomElement prop = WebdavHandler::addElement( doc, set, "d:prop" );
|
|
|
|
|
|
|
|
TQDomAttr att_b = doc.createAttribute( "xmlns:b" );
|
|
|
|
att_b.setValue( "urn:schemas-microsoft-com:datatypes" );
|
|
|
|
root.setAttributeNode( att_b );
|
|
|
|
|
|
|
|
TQDomAttr att_h = doc.createAttribute( "xmlns:h" );
|
|
|
|
att_h.setValue( "urn:schemas:mailheader:" );
|
|
|
|
root.setAttributeNode( att_h );
|
|
|
|
|
|
|
|
TQDomAttr att_m = doc.createAttribute( "xmlns:m" );
|
|
|
|
att_m.setValue( "urn:schemas:httpmail:" );
|
|
|
|
root.setAttributeNode( att_m );
|
|
|
|
|
|
|
|
// TQDomAttr att1 = doc.createAttributeNS( "do:whatever:you:like", "x:attname");
|
|
|
|
// att1.setValue( "value" );
|
|
|
|
// prop.setAttributeNodeNS( att1 );
|
|
|
|
// root.setAttributeNodeNS( att1 );
|
|
|
|
// set.setAttributeNode( att1 );
|
|
|
|
// // prop.setAttributeNS ( "xmlns:b", "xmlns:b", "urn:schemas-microsoft-com:datatypes" );
|
|
|
|
|
|
|
|
ExchangeConverterCalendar::createWebDAVVisitor v;
|
|
|
|
v.act( doc, prop, incidence, mFormat.timeZoneId() );
|
|
|
|
kdDebug() << "ExchangeConverterCalendar::createWebDAVVisitor=" << doc.toString() << "\n";
|
|
|
|
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
#undef domDavProperty
|
|
|
|
#undef domProperty
|
|
|
|
#undef domCalendarProperty
|
|
|
|
#undef domHTTPMailProperty
|
|
|
|
#undef domMailHeaderProperty
|