/* This file is part of the exchange resource. Copyright (c) 2004 Reinhold Kainhofer Parts are derived from the old libkpimexchange library: Copyright (c) 2002 Jan-Pascal van Best 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 #include #include #include #include #include #include #include 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"<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"<setUid( tmpstr ); } else { kdDebug()<<"ExchangeConverterCalendar::readIncidence: ERROR: No UID given"<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, Qt::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"<setUid( tmpstr ); } else { kdDebug()<<"ExchangeConverterCalendar::readIncidence: ERROR: No UID given"<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"<setUid( tmpstr ); } else { kdDebug()<<"ExchangeConverterCalendar::readJournal: ERROR: No UID given"<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( Qt::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 entyr1entry2 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( Qt::ISODate ) ); domCalendarProperty( "lastmodified", todo->lastModified().toString( Qt::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( Qt::ISODate ) ); domCalendarProperty( "lastmodified", todo->lastModified().toString( Qt::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