libkcal

vcalformat.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include <qapplication.h>
00024 #include <qdatetime.h>
00025 #include <qstring.h>
00026 #include <qptrlist.h>
00027 #include <qregexp.h>
00028 #include <qclipboard.h>
00029 #include <qdialog.h>
00030 #include <qfile.h>
00031 
00032 #include <kdebug.h>
00033 #include <kmessagebox.h>
00034 #include <kiconloader.h>
00035 #include <klocale.h>
00036 
00037 #include "vcc.h"
00038 #include "vobject.h"
00039 extern "C" {
00040 #include "icaltime.h"
00041 #include "icaltimezone.h"
00042 }
00043 #include "vcaldrag.h"
00044 #include "calendar.h"
00045 
00046 #include "vcalformat.h"
00047 
00048 using namespace KCal;
00049 
00050 VCalFormat::VCalFormat()
00051 {
00052 }
00053 
00054 VCalFormat::~VCalFormat()
00055 {
00056 }
00057 
00058 bool VCalFormat::load(Calendar *calendar, const QString &fileName)
00059 {
00060   mCalendar = calendar;
00061 
00062   clearException();
00063 
00064   kdDebug(5800) << "VCalFormat::load() " << fileName << endl;
00065 
00066   VObject *vcal = 0;
00067 
00068   // this is not necessarily only 1 vcal.  Could be many vcals, or include
00069   // a vcard...
00070   vcal = Parse_MIME_FromFileName(const_cast<char *>(QFile::encodeName(fileName).data()));
00071 
00072   if (!vcal) {
00073     setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
00074     return FALSE;
00075   }
00076 
00077   // any other top-level calendar stuff should be added/initialized here
00078 
00079   // put all vobjects into their proper places
00080   populate(vcal);
00081 
00082   // clean up from vcal API stuff
00083   cleanVObjects(vcal);
00084   cleanStrTbl();
00085 
00086   return true;
00087 }
00088 
00089 
00090 bool VCalFormat::save(Calendar *calendar, const QString &fileName)
00091 {
00092   mCalendar = calendar;
00093 
00094   QString tmpStr;
00095   VObject *vcal, *vo;
00096 
00097   kdDebug(5800) << "VCalFormat::save(): " << fileName << endl;
00098 
00099   vcal = newVObject(VCCalProp);
00100 
00101   //  addPropValue(vcal,VCLocationProp, "0.0");
00102   addPropValue(vcal,VCProdIdProp, productId().latin1());
00103   addPropValue(vcal,VCVersionProp, _VCAL_VERSION);
00104 
00105   // TODO STUFF
00106   Todo::List todoList = mCalendar->rawTodos();
00107   Todo::List::ConstIterator it;
00108   for ( it = todoList.begin(); it != todoList.end(); ++it ) {
00109     vo = eventToVTodo( *it );
00110     addVObjectProp( vcal, vo );
00111   }
00112 
00113   // EVENT STUFF
00114   Event::List events = mCalendar->rawEvents();
00115   Event::List::ConstIterator it2;
00116   for( it2 = events.begin(); it2 != events.end(); ++it2 ) {
00117     vo = eventToVEvent( *it2 );
00118     addVObjectProp( vcal, vo );
00119   }
00120 
00121   writeVObjectToFile(QFile::encodeName(fileName).data() ,vcal);
00122   cleanVObjects(vcal);
00123   cleanStrTbl();
00124 
00125   if (QFile::exists(fileName)) {
00126     kdDebug(5800) << "No error" << endl;
00127     return true;
00128   } else  {
00129     kdDebug(5800) << "Error" << endl;
00130     return false; // error
00131   }
00132 
00133   return false;
00134 }
00135 
00136 bool VCalFormat::fromString( Calendar *calendar, const QString &text )
00137 {
00138   // TODO: Factor out VCalFormat::fromString()
00139   mCalendar = calendar;
00140 
00141   QCString data = text.utf8();
00142 
00143   if ( !data.size() ) return false;
00144 
00145   VObject *vcal = Parse_MIME( data.data(), data.size());
00146   if ( !vcal ) return false;
00147 
00148   VObjectIterator i;
00149   VObject *curvo;
00150   initPropIterator( &i, vcal );
00151 
00152   // we only take the first object. TODO: parse all incidences.
00153   do  {
00154     curvo = nextVObject( &i );
00155   } while ( strcmp( vObjectName( curvo ), VCEventProp ) &&
00156             strcmp( vObjectName( curvo ), VCTodoProp ) );
00157 
00158   if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) {
00159     Event *event = VEventToEvent( curvo );
00160     calendar->addEvent( event );
00161   } else {
00162     kdDebug(5800) << "VCalFormat::fromString(): Unknown object type." << endl;
00163     deleteVObject( vcal );
00164     return false;
00165   }
00166 
00167   deleteVObject( vcal );
00168 
00169   return true;
00170 }
00171 
00172 QString VCalFormat::toString( Calendar *calendar )
00173 {
00174   // TODO: Factor out VCalFormat::asString()
00175   mCalendar = calendar;
00176 
00177   VObject *vcal = newVObject(VCCalProp);
00178 
00179   addPropValue( vcal, VCProdIdProp, CalFormat::productId().latin1() );
00180   addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
00181 
00182   // TODO: Use all data.
00183   Event::List events = calendar->events();
00184   Event *event = events.first();
00185   if ( !event ) {
00186     cleanVObject( vcal );
00187     return QString::null;
00188   }
00189 
00190   VObject *vevent = eventToVEvent( event );
00191 
00192   addVObjectProp( vcal, vevent );
00193 
00194   char *buf = writeMemVObject( 0, 0, vcal );
00195 
00196   QString result( buf );
00197 
00198   cleanVObject( vcal );
00199 
00200   return result;
00201 }
00202 
00203 VObject *VCalFormat::eventToVTodo(const Todo *anEvent)
00204 {
00205   VObject *vtodo;
00206   QString tmpStr;
00207 
00208   vtodo = newVObject(VCTodoProp);
00209 
00210   // due date
00211   if (anEvent->hasDueDate()) {
00212     tmpStr = qDateTimeToISO(anEvent->dtDue(),
00213                             !anEvent->doesFloat());
00214     addPropValue(vtodo, VCDueProp, tmpStr.local8Bit());
00215   }
00216 
00217   // start date
00218   if (anEvent->hasStartDate()) {
00219     tmpStr = qDateTimeToISO(anEvent->dtStart(),
00220                             !anEvent->doesFloat());
00221     addPropValue(vtodo, VCDTstartProp, tmpStr.local8Bit());
00222   }
00223 
00224   // creation date
00225   tmpStr = qDateTimeToISO(anEvent->created());
00226   addPropValue(vtodo, VCDCreatedProp, tmpStr.local8Bit());
00227 
00228   // unique id
00229   addPropValue(vtodo, VCUniqueStringProp,
00230                anEvent->uid().local8Bit());
00231 
00232   // revision
00233   tmpStr.sprintf("%i", anEvent->revision());
00234   addPropValue(vtodo, VCSequenceProp, tmpStr.local8Bit());
00235 
00236   // last modification date
00237   tmpStr = qDateTimeToISO(anEvent->lastModified());
00238   addPropValue(vtodo, VCLastModifiedProp, tmpStr.local8Bit());
00239 
00240   // organizer stuff
00241   // @TODO: How about the common name?
00242   tmpStr = "MAILTO:" + anEvent->organizer().email();
00243   addPropValue(vtodo, ICOrganizerProp, tmpStr.local8Bit());
00244 
00245   // attendees
00246   if ( anEvent->attendeeCount() > 0 ) {
00247     Attendee::List::ConstIterator it;
00248     Attendee *curAttendee;
00249     for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end();
00250           ++it ) {
00251       curAttendee = *it;
00252       if (!curAttendee->email().isEmpty() &&
00253           !curAttendee->name().isEmpty())
00254         tmpStr = "MAILTO:" + curAttendee->name() + " <" +
00255                  curAttendee->email() + ">";
00256       else if (curAttendee->name().isEmpty())
00257         tmpStr = "MAILTO: " + curAttendee->email();
00258       else if (curAttendee->email().isEmpty())
00259         tmpStr = "MAILTO: " + curAttendee->name();
00260       else if (curAttendee->name().isEmpty() &&
00261                curAttendee->email().isEmpty())
00262         kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl;
00263       VObject *aProp = addPropValue(vtodo, VCAttendeeProp, tmpStr.local8Bit());
00264       addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE");
00265       addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status()));
00266     }
00267   }
00268 
00269   // description BL:
00270   if (!anEvent->description().isEmpty()) {
00271     VObject *d = addPropValue(vtodo, VCDescriptionProp,
00272                               anEvent->description().local8Bit());
00273     if (anEvent->description().find('\n') != -1)
00274       addPropValue(d, VCEncodingProp, VCQuotedPrintableProp);
00275   }
00276 
00277   // summary
00278   if (!anEvent->summary().isEmpty())
00279     addPropValue(vtodo, VCSummaryProp, anEvent->summary().local8Bit());
00280 
00281   // location
00282   if (!anEvent->location().isEmpty())
00283     addPropValue(vtodo, VCLocationProp, anEvent->location().local8Bit());
00284 
00285   // completed
00286   // status
00287   // backward compatibility, KOrganizer used to interpret only these two values
00288   addPropValue(vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" :
00289                                                              "NEEDS_ACTION");
00290   // completion date
00291   if (anEvent->hasCompletedDate()) {
00292     tmpStr = qDateTimeToISO(anEvent->completed());
00293     addPropValue(vtodo, VCCompletedProp, tmpStr.local8Bit());
00294   }
00295 
00296   // priority
00297   tmpStr.sprintf("%i",anEvent->priority());
00298   addPropValue(vtodo, VCPriorityProp, tmpStr.local8Bit());
00299 
00300   // related event
00301   if (anEvent->relatedTo()) {
00302     addPropValue(vtodo, VCRelatedToProp,
00303                  anEvent->relatedTo()->uid().local8Bit());
00304   }
00305 
00306   // categories
00307   QStringList tmpStrList = anEvent->categories();
00308   tmpStr = "";
00309   QString catStr;
00310   for ( QStringList::Iterator it = tmpStrList.begin();
00311         it != tmpStrList.end();
00312         ++it ) {
00313     catStr = *it;
00314     if (catStr[0] == ' ')
00315       tmpStr += catStr.mid(1);
00316     else
00317       tmpStr += catStr;
00318     // this must be a ';' character as the vCalendar specification requires!
00319     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00320     // read in.
00321     tmpStr += ";";
00322   }
00323   if (!tmpStr.isEmpty()) {
00324     tmpStr.truncate(tmpStr.length()-1);
00325     addPropValue(vtodo, VCCategoriesProp, tmpStr.local8Bit());
00326   }
00327 
00328   // alarm stuff
00329   kdDebug(5800) << "vcalformat::eventToVTodo was called" << endl;
00330   Alarm::List::ConstIterator it;
00331   for ( it = anEvent->alarms().begin(); it != anEvent->alarms().end(); ++it ) {
00332     Alarm *alarm = *it;
00333     if (alarm->enabled()) {
00334       VObject *a = addProp(vtodo, VCDAlarmProp);
00335       tmpStr = qDateTimeToISO(alarm->time());
00336       addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00337       addPropValue(a, VCRepeatCountProp, "1");
00338       addPropValue(a, VCDisplayStringProp, "beep!");
00339       if (alarm->type() == Alarm::Audio) {
00340         a = addProp(vtodo, VCAAlarmProp);
00341         addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00342         addPropValue(a, VCRepeatCountProp, "1");
00343         addPropValue(a, VCAudioContentProp, QFile::encodeName(alarm->audioFile()));
00344       }
00345       else if (alarm->type() == Alarm::Procedure) {
00346         a = addProp(vtodo, VCPAlarmProp);
00347         addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00348         addPropValue(a, VCRepeatCountProp, "1");
00349         addPropValue(a, VCProcedureNameProp, QFile::encodeName(alarm->programFile()));
00350       }
00351     }
00352   }
00353 
00354   if (anEvent->pilotId()) {
00355     // pilot sync stuff
00356     tmpStr.sprintf("%lu",anEvent->pilotId());
00357     addPropValue(vtodo, KPilotIdProp, tmpStr.local8Bit());
00358     tmpStr.sprintf("%i",anEvent->syncStatus());
00359     addPropValue(vtodo, KPilotStatusProp, tmpStr.local8Bit());
00360   }
00361 
00362   return vtodo;
00363 }
00364 
00365 VObject* VCalFormat::eventToVEvent(const Event *anEvent)
00366 {
00367   VObject *vevent;
00368   QString tmpStr;
00369 
00370   vevent = newVObject(VCEventProp);
00371 
00372   // start and end time
00373   tmpStr = qDateTimeToISO(anEvent->dtStart(),
00374                           !anEvent->doesFloat());
00375   addPropValue(vevent, VCDTstartProp, tmpStr.local8Bit());
00376 
00377   // events that have time associated but take up no time should
00378   // not have both DTSTART and DTEND.
00379   if (anEvent->dtStart() != anEvent->dtEnd()) {
00380     tmpStr = qDateTimeToISO(anEvent->dtEnd(),
00381                             !anEvent->doesFloat());
00382     addPropValue(vevent, VCDTendProp, tmpStr.local8Bit());
00383   }
00384 
00385   // creation date
00386   tmpStr = qDateTimeToISO(anEvent->created());
00387   addPropValue(vevent, VCDCreatedProp, tmpStr.local8Bit());
00388 
00389   // unique id
00390   addPropValue(vevent, VCUniqueStringProp,
00391                anEvent->uid().local8Bit());
00392 
00393   // revision
00394   tmpStr.sprintf("%i", anEvent->revision());
00395   addPropValue(vevent, VCSequenceProp, tmpStr.local8Bit());
00396 
00397   // last modification date
00398   tmpStr = qDateTimeToISO(anEvent->lastModified());
00399   addPropValue(vevent, VCLastModifiedProp, tmpStr.local8Bit());
00400 
00401   // attendee and organizer stuff
00402   // TODO: What to do with the common name?
00403   tmpStr = "MAILTO:" + anEvent->organizer().email();
00404   addPropValue(vevent, ICOrganizerProp, tmpStr.local8Bit());
00405 
00406   // TODO: Put this functionality into Attendee class
00407   if ( anEvent->attendeeCount() > 0 ) {
00408     Attendee::List::ConstIterator it;
00409     for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end();
00410           ++it ) {
00411       Attendee *curAttendee = *it;
00412       if (!curAttendee->email().isEmpty() &&
00413           !curAttendee->name().isEmpty())
00414         tmpStr = "MAILTO:" + curAttendee->name() + " <" +
00415                  curAttendee->email() + ">";
00416       else if (curAttendee->name().isEmpty())
00417         tmpStr = "MAILTO: " + curAttendee->email();
00418       else if (curAttendee->email().isEmpty())
00419         tmpStr = "MAILTO: " + curAttendee->name();
00420       else if (curAttendee->name().isEmpty() &&
00421                curAttendee->email().isEmpty())
00422         kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl;
00423       VObject *aProp = addPropValue(vevent, VCAttendeeProp, tmpStr.local8Bit());
00424       addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE");
00425       addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status()));
00426     }
00427   }
00428 
00429   // recurrence rule stuff
00430   const Recurrence *recur = anEvent->recurrence();
00431   if ( recur->doesRecur() ) {
00432     bool validRecur = true;
00433     QString tmpStr2;
00434     switch ( recur->recurrenceType() ) {
00435     case Recurrence::rDaily:
00436       tmpStr.sprintf("D%i ",recur->frequency());
00437       break;
00438     case Recurrence::rWeekly:
00439       tmpStr.sprintf("W%i ",recur->frequency());
00440       for (int i = 0; i < 7; i++ ) {
00441         QBitArray days ( recur->days() );
00442         if ( days.testBit(i) )
00443           tmpStr += dayFromNum(i);
00444       }
00445       break;
00446     case Recurrence::rMonthlyPos: {
00447       tmpStr.sprintf("MP%i ", recur->frequency());
00448       // write out all rMonthPos's
00449       QValueList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
00450       for ( QValueListConstIterator<RecurrenceRule::WDayPos> posit = tmpPositions.begin();
00451             posit != tmpPositions.end(); ++posit ) {
00452         int pos = (*posit).pos();
00453         tmpStr2.sprintf("%i", (pos>0) ? pos : (-pos) );
00454         if ( pos < 0)
00455           tmpStr2 += "- ";
00456         else
00457           tmpStr2 += "+ ";
00458         tmpStr += tmpStr2;
00459         tmpStr += dayFromNum( (*posit).day() - 1 );
00460       }
00461       break; }
00462     case Recurrence::rMonthlyDay: {
00463       tmpStr.sprintf("MD%i ", recur->frequency());
00464       // write out all rMonthDays;
00465       QValueList<int> tmpDays = recur->monthDays();
00466       for ( QValueListIterator<int> tmpDay = tmpDays.begin();
00467             tmpDay != tmpDays.end(); ++tmpDay ) {
00468         tmpStr2.sprintf( "%i ", *tmpDay );
00469         tmpStr += tmpStr2;
00470       }
00471       break; }
00472     case Recurrence::rYearlyMonth: {
00473       tmpStr.sprintf("YM%i ", recur->frequency());
00474       // write out all the months;'
00475       // TODO: Any way to write out the day within the month???
00476       QValueList<int> months = recur->yearMonths();
00477       for ( QValueListIterator<int> mit = months.begin();
00478             mit != months.end(); ++mit ) {
00479         tmpStr2.sprintf( "%i ", *mit );
00480         tmpStr += tmpStr2;
00481       }
00482       break; }
00483     case Recurrence::rYearlyDay: {
00484       tmpStr.sprintf("YD%i ", recur->frequency());
00485       // write out all the rYearNums;
00486       QValueList<int> tmpDays = recur->yearDays();
00487       for ( QValueListIterator<int> tmpDay = tmpDays.begin();
00488             tmpDay != tmpDays.end(); ++tmpDay ) {
00489         tmpStr2.sprintf( "%i ", *tmpDay );
00490         tmpStr += tmpStr2;
00491       }
00492       break; }
00493     default:
00494       // TODO: Write rYearlyPos and arbitrary rules!
00495       kdDebug(5800) << "ERROR, it should never get here in eventToVEvent!" << endl;
00496       validRecur = false;
00497       break;
00498     } // switch
00499 
00500     if (recur->duration() > 0) {
00501       tmpStr2.sprintf("#%i",recur->duration());
00502       tmpStr += tmpStr2;
00503     } else if (recur->duration() == -1) {
00504       tmpStr += "#0"; // defined as repeat forever
00505     } else {
00506       tmpStr += qDateTimeToISO(recur->endDateTime(), FALSE);
00507     }
00508     // Only write out the rrule if we have a valid recurrence (i.e. a known
00509     // type in thee switch above)
00510     if ( validRecur )
00511       addPropValue(vevent,VCRRuleProp, tmpStr.local8Bit());
00512 
00513   } // event repeats
00514 
00515   // exceptions to recurrence
00516   DateList dateList = recur->exDates();
00517   DateList::ConstIterator it;
00518   QString tmpStr2;
00519 
00520   for (it = dateList.begin(); it != dateList.end(); ++it) {
00521     tmpStr = qDateToISO(*it) + ";";
00522     tmpStr2 += tmpStr;
00523   }
00524   if (!tmpStr2.isEmpty()) {
00525     tmpStr2.truncate(tmpStr2.length()-1);
00526     addPropValue(vevent, VCExDateProp, tmpStr2.local8Bit());
00527   }
00528 
00529   // description
00530   if (!anEvent->description().isEmpty()) {
00531     VObject *d = addPropValue(vevent, VCDescriptionProp,
00532                               anEvent->description().local8Bit());
00533     if (anEvent->description().find('\n') != -1)
00534       addPropValue(d, VCEncodingProp, VCQuotedPrintableProp);
00535   }
00536 
00537   // summary
00538   if (!anEvent->summary().isEmpty())
00539     addPropValue(vevent, VCSummaryProp, anEvent->summary().local8Bit());
00540 
00541   // location
00542   if (!anEvent->location().isEmpty())
00543     addPropValue(vevent, VCLocationProp, anEvent->location().local8Bit());
00544 
00545   // status
00546 // TODO: define Event status
00547 //  addPropValue(vevent, VCStatusProp, anEvent->statusStr().local8Bit());
00548 
00549   // secrecy
00550   const char *text = 0;
00551   switch (anEvent->secrecy()) {
00552     case Incidence::SecrecyPublic:
00553       text = "PUBLIC";
00554       break;
00555     case Incidence::SecrecyPrivate:
00556       text = "PRIVATE";
00557       break;
00558     case Incidence::SecrecyConfidential:
00559       text = "CONFIDENTIAL";
00560       break;
00561   }
00562   if (text) {
00563     addPropValue(vevent, VCClassProp, text);
00564   }
00565 
00566   // categories
00567   QStringList tmpStrList = anEvent->categories();
00568   tmpStr = "";
00569   QString catStr;
00570   for ( QStringList::Iterator it = tmpStrList.begin();
00571         it != tmpStrList.end();
00572         ++it ) {
00573     catStr = *it;
00574     if (catStr[0] == ' ')
00575       tmpStr += catStr.mid(1);
00576     else
00577       tmpStr += catStr;
00578     // this must be a ';' character as the vCalendar specification requires!
00579     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00580     // read in.
00581     tmpStr += ";";
00582   }
00583   if (!tmpStr.isEmpty()) {
00584     tmpStr.truncate(tmpStr.length()-1);
00585     addPropValue(vevent, VCCategoriesProp, tmpStr.local8Bit());
00586   }
00587 
00588   // attachments
00589   // TODO: handle binary attachments!
00590   Attachment::List attachments = anEvent->attachments();
00591   Attachment::List::ConstIterator atIt;
00592   for ( atIt = attachments.begin(); atIt != attachments.end(); ++atIt )
00593     addPropValue( vevent, VCAttachProp, (*atIt)->uri().local8Bit() );
00594 
00595   // resources
00596   tmpStrList = anEvent->resources();
00597   tmpStr = tmpStrList.join(";");
00598   if (!tmpStr.isEmpty())
00599     addPropValue(vevent, VCResourcesProp, tmpStr.local8Bit());
00600 
00601   // alarm stuff
00602   Alarm::List::ConstIterator it2;
00603   for ( it2 = anEvent->alarms().begin(); it2 != anEvent->alarms().end(); ++it2 ) {
00604     Alarm *alarm = *it2;
00605     if (alarm->enabled()) {
00606       VObject *a = addProp(vevent, VCDAlarmProp);
00607       tmpStr = qDateTimeToISO(alarm->time());
00608       addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00609       addPropValue(a, VCRepeatCountProp, "1");
00610       addPropValue(a, VCDisplayStringProp, "beep!");
00611       if (alarm->type() == Alarm::Audio) {
00612         a = addProp(vevent, VCAAlarmProp);
00613         addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00614         addPropValue(a, VCRepeatCountProp, "1");
00615         addPropValue(a, VCAudioContentProp, QFile::encodeName(alarm->audioFile()));
00616       }
00617       if (alarm->type() == Alarm::Procedure) {
00618         a = addProp(vevent, VCPAlarmProp);
00619         addPropValue(a, VCRunTimeProp, tmpStr.local8Bit());
00620         addPropValue(a, VCRepeatCountProp, "1");
00621         addPropValue(a, VCProcedureNameProp, QFile::encodeName(alarm->programFile()));
00622       }
00623     }
00624   }
00625 
00626   // priority
00627   tmpStr.sprintf("%i",anEvent->priority());
00628   addPropValue(vevent, VCPriorityProp, tmpStr.local8Bit());
00629 
00630   // transparency
00631   tmpStr.sprintf("%i",anEvent->transparency());
00632   addPropValue(vevent, VCTranspProp, tmpStr.local8Bit());
00633 
00634   // related event
00635   if (anEvent->relatedTo()) {
00636     addPropValue(vevent, VCRelatedToProp,
00637                  anEvent->relatedTo()->uid().local8Bit());
00638   }
00639 
00640   if (anEvent->pilotId()) {
00641     // pilot sync stuff
00642     tmpStr.sprintf("%lu",anEvent->pilotId());
00643     addPropValue(vevent, KPilotIdProp, tmpStr.local8Bit());
00644     tmpStr.sprintf("%i",anEvent->syncStatus());
00645     addPropValue(vevent, KPilotStatusProp, tmpStr.local8Bit());
00646   }
00647 
00648   return vevent;
00649 }
00650 
00651 Todo *VCalFormat::VTodoToEvent(VObject *vtodo)
00652 {
00653   VObject *vo;
00654   VObjectIterator voi;
00655   char *s;
00656 
00657   Todo *anEvent = new Todo;
00658 
00659   // creation date
00660   if ((vo = isAPropertyOf(vtodo, VCDCreatedProp)) != 0) {
00661       anEvent->setCreated(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00662       deleteStr(s);
00663   }
00664 
00665   // unique id
00666   vo = isAPropertyOf(vtodo, VCUniqueStringProp);
00667   // while the UID property is preferred, it is not required.  We'll use the
00668   // default Event UID if none is given.
00669   if (vo) {
00670     anEvent->setUid(s = fakeCString(vObjectUStringZValue(vo)));
00671     deleteStr(s);
00672   }
00673 
00674   // last modification date
00675   if ((vo = isAPropertyOf(vtodo, VCLastModifiedProp)) != 0) {
00676     anEvent->setLastModified(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00677     deleteStr(s);
00678   }
00679   else
00680     anEvent->setLastModified(QDateTime(QDate::currentDate(),
00681                                        QTime::currentTime()));
00682 
00683   // organizer
00684   // if our extension property for the event's ORGANIZER exists, add it.
00685   if ((vo = isAPropertyOf(vtodo, ICOrganizerProp)) != 0) {
00686     anEvent->setOrganizer( s = fakeCString(vObjectUStringZValue(vo) ) );
00687     deleteStr(s);
00688   } else {
00689     anEvent->setOrganizer( mCalendar->getOwner() );
00690   }
00691 
00692   // attendees.
00693   initPropIterator(&voi, vtodo);
00694   while (moreIteration(&voi)) {
00695     vo = nextVObject(&voi);
00696     if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
00697       Attendee *a;
00698       VObject *vp;
00699       s = fakeCString(vObjectUStringZValue(vo));
00700       QString tmpStr = QString::fromLocal8Bit(s);
00701       deleteStr(s);
00702       tmpStr = tmpStr.simplifyWhiteSpace();
00703       int emailPos1, emailPos2;
00704       if ((emailPos1 = tmpStr.find('<')) > 0) {
00705         // both email address and name
00706         emailPos2 = tmpStr.findRev('>');
00707         a = new Attendee(tmpStr.left(emailPos1 - 1),
00708                          tmpStr.mid(emailPos1 + 1,
00709                                     emailPos2 - (emailPos1 + 1)));
00710       } else if (tmpStr.find('@') > 0) {
00711         // just an email address
00712         a = new Attendee(0, tmpStr);
00713       } else {
00714         // just a name
00715         // WTF??? Replacing the spaces of a name and using this as email?
00716         QString email = tmpStr.replace( ' ', '.' );
00717         a = new Attendee(tmpStr,email);
00718       }
00719 
00720       // is there an RSVP property?
00721       if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0)
00722         a->setRSVP(vObjectStringZValue(vp));
00723       // is there a status property?
00724       if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0)
00725         a->setStatus(readStatus(vObjectStringZValue(vp)));
00726       // add the attendee
00727       anEvent->addAttendee(a);
00728     }
00729   }
00730 
00731   // description for todo
00732   if ((vo = isAPropertyOf(vtodo, VCDescriptionProp)) != 0) {
00733     s = fakeCString(vObjectUStringZValue(vo));
00734     anEvent->setDescription(QString::fromLocal8Bit(s));
00735     deleteStr(s);
00736   }
00737 
00738   // summary
00739   if ((vo = isAPropertyOf(vtodo, VCSummaryProp))) {
00740     s = fakeCString(vObjectUStringZValue(vo));
00741     anEvent->setSummary(QString::fromLocal8Bit(s));
00742     deleteStr(s);
00743   }
00744 
00745 
00746   // location
00747   if ((vo = isAPropertyOf(vtodo, VCLocationProp)) != 0) {
00748     s = fakeCString(vObjectUStringZValue(vo));
00749     anEvent->setLocation( QString::fromLocal8Bit(s) );
00750     deleteStr(s);
00751   }
00752   // completed
00753   // was: status
00754   if ((vo = isAPropertyOf(vtodo, VCStatusProp)) != 0) {
00755     s = fakeCString(vObjectUStringZValue(vo));
00756     if (strcmp(s,"COMPLETED") == 0) {
00757       anEvent->setCompleted(true);
00758     } else {
00759       anEvent->setCompleted(false);
00760     }
00761     deleteStr(s);
00762   }
00763   else
00764     anEvent->setCompleted(false);
00765 
00766   // completion date
00767   if ((vo = isAPropertyOf(vtodo, VCCompletedProp)) != 0) {
00768     anEvent->setCompleted(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00769     deleteStr(s);
00770   }
00771 
00772   // priority
00773   if ((vo = isAPropertyOf(vtodo, VCPriorityProp))) {
00774     anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo))));
00775     deleteStr(s);
00776   }
00777 
00778   // due date
00779   if ((vo = isAPropertyOf(vtodo, VCDueProp)) != 0) {
00780     anEvent->setDtDue(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00781     deleteStr(s);
00782     anEvent->setHasDueDate(true);
00783   } else {
00784     anEvent->setHasDueDate(false);
00785   }
00786 
00787   // start time
00788   if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != 0) {
00789     anEvent->setDtStart(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00790     //    kdDebug(5800) << "s is " << //          s << ", ISO is " << ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl;
00791     deleteStr(s);
00792     anEvent->setHasStartDate(true);
00793   } else {
00794     anEvent->setHasStartDate(false);
00795   }
00796 
00797   /* alarm stuff */
00798   //kdDebug(5800) << "vcalformat::VTodoToEvent called" << endl;
00799   if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) {
00800     Alarm* alarm = anEvent->newAlarm();
00801     VObject *a;
00802     if ((a = isAPropertyOf(vo, VCRunTimeProp))) {
00803       alarm->setTime(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(a))));
00804       deleteStr(s);
00805     }
00806     alarm->setEnabled(true);
00807     if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) {
00808       if ((a = isAPropertyOf(vo, VCProcedureNameProp))) {
00809         s = fakeCString(vObjectUStringZValue(a));
00810         alarm->setProcedureAlarm(QFile::decodeName(s));
00811         deleteStr(s);
00812       }
00813     }
00814     if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) {
00815       if ((a = isAPropertyOf(vo, VCAudioContentProp))) {
00816         s = fakeCString(vObjectUStringZValue(a));
00817         alarm->setAudioAlarm(QFile::decodeName(s));
00818         deleteStr(s);
00819       }
00820     }
00821   }
00822 
00823   // related todo
00824   if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != 0) {
00825     anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo)));
00826     deleteStr(s);
00827     mTodosRelate.append(anEvent);
00828   }
00829 
00830   // categories
00831   if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != 0) {
00832     s = fakeCString(vObjectUStringZValue(vo));
00833     QString categories = QString::fromLocal8Bit(s);
00834     deleteStr(s);
00835     QStringList tmpStrList = QStringList::split( ';', categories );
00836     anEvent->setCategories(tmpStrList);
00837   }
00838 
00839   /* PILOT SYNC STUFF */
00840   if ((vo = isAPropertyOf(vtodo, KPilotIdProp))) {
00841     anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo))));
00842     deleteStr(s);
00843   }
00844   else
00845     anEvent->setPilotId(0);
00846 
00847   if ((vo = isAPropertyOf(vtodo, KPilotStatusProp))) {
00848     anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo))));
00849     deleteStr(s);
00850   }
00851   else
00852     anEvent->setSyncStatus(Event::SYNCMOD);
00853 
00854   return anEvent;
00855 }
00856 
00857 Event* VCalFormat::VEventToEvent(VObject *vevent)
00858 {
00859   VObject *vo;
00860   VObjectIterator voi;
00861   char *s;
00862 
00863   Event *anEvent = new Event;
00864 
00865   // creation date
00866   if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != 0) {
00867       anEvent->setCreated(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00868       deleteStr(s);
00869   }
00870 
00871   // unique id
00872   vo = isAPropertyOf(vevent, VCUniqueStringProp);
00873   // while the UID property is preferred, it is not required.  We'll use the
00874   // default Event UID if none is given.
00875   if (vo) {
00876     anEvent->setUid(s = fakeCString(vObjectUStringZValue(vo)));
00877     deleteStr(s);
00878   }
00879 
00880   // revision
00881   // again NSCAL doesn't give us much to work with, so we improvise...
00882   if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != 0) {
00883     anEvent->setRevision(atoi(s = fakeCString(vObjectUStringZValue(vo))));
00884     deleteStr(s);
00885   }
00886   else
00887     anEvent->setRevision(0);
00888 
00889   // last modification date
00890   if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != 0) {
00891     anEvent->setLastModified(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00892     deleteStr(s);
00893   }
00894   else
00895     anEvent->setLastModified(QDateTime(QDate::currentDate(),
00896                                        QTime::currentTime()));
00897 
00898   // organizer
00899   // if our extension property for the event's ORGANIZER exists, add it.
00900   if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != 0) {
00901     // FIXME:  Also use the full name, not just the email address
00902     anEvent->setOrganizer( s = fakeCString(vObjectUStringZValue(vo) ) );
00903     deleteStr(s);
00904   } else {
00905     anEvent->setOrganizer( mCalendar->getOwner() );
00906   }
00907 
00908   // deal with attendees.
00909   initPropIterator(&voi, vevent);
00910   while (moreIteration(&voi)) {
00911     vo = nextVObject(&voi);
00912     if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
00913       Attendee *a;
00914       VObject *vp;
00915       s = fakeCString(vObjectUStringZValue(vo));
00916       QString tmpStr = QString::fromLocal8Bit(s);
00917       deleteStr(s);
00918       tmpStr = tmpStr.simplifyWhiteSpace();
00919       int emailPos1, emailPos2;
00920       if ((emailPos1 = tmpStr.find('<')) > 0) {
00921         // both email address and name
00922         emailPos2 = tmpStr.findRev('>');
00923         a = new Attendee(tmpStr.left(emailPos1 - 1),
00924                          tmpStr.mid(emailPos1 + 1,
00925                                     emailPos2 - (emailPos1 + 1)));
00926       } else if (tmpStr.find('@') > 0) {
00927         // just an email address
00928         a = new Attendee(0, tmpStr);
00929       } else {
00930         // just a name
00931         QString email = tmpStr.replace( ' ', '.' );
00932         a = new Attendee(tmpStr,email);
00933       }
00934 
00935       // is there an RSVP property?
00936       if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0)
00937         a->setRSVP(vObjectStringZValue(vp));
00938       // is there a status property?
00939       if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0)
00940         a->setStatus(readStatus(vObjectStringZValue(vp)));
00941       // add the attendee
00942       anEvent->addAttendee(a);
00943     }
00944   }
00945 
00946   // This isn't strictly true.  An event that doesn't have a start time
00947   // or an end time doesn't "float", it has an anchor in time but it doesn't
00948   // "take up" any time.
00949   /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
00950       (isAPropertyOf(vevent, VCDTendProp) == 0)) {
00951     anEvent->setFloats(TRUE);
00952     } else {
00953     }*/
00954 
00955   anEvent->setFloats(FALSE);
00956 
00957   // start time
00958   if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != 0) {
00959     anEvent->setDtStart(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00960     //    kdDebug(5800) << "s is " << //          s << ", ISO is " << ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl;
00961     deleteStr(s);
00962     if (anEvent->dtStart().time().isNull())
00963       anEvent->setFloats(TRUE);
00964   }
00965 
00966   // stop time
00967   if ((vo = isAPropertyOf(vevent, VCDTendProp)) != 0) {
00968     anEvent->setDtEnd(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))));
00969       deleteStr(s);
00970       if (anEvent->dtEnd().time().isNull())
00971         anEvent->setFloats(TRUE);
00972   }
00973 
00974   // at this point, there should be at least a start or end time.
00975   // fix up for events that take up no time but have a time associated
00976   if (!(vo = isAPropertyOf(vevent, VCDTstartProp)))
00977     anEvent->setDtStart(anEvent->dtEnd());
00978   if (!(vo = isAPropertyOf(vevent, VCDTendProp)))
00979     anEvent->setDtEnd(anEvent->dtStart());
00980 
00982 
00983   // repeat stuff
00984   if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != 0) {
00985     QString tmpStr = (s = fakeCString(vObjectUStringZValue(vo)));
00986     deleteStr(s);
00987     tmpStr.simplifyWhiteSpace();
00988     tmpStr = tmpStr.upper();
00989 // kdDebug() <<" We have a recurrence rule: " << tmpStr<< endl;
00990 
00991     // first, read the type of the recurrence
00992     int typelen = 1;
00993     uint type = Recurrence::rNone;
00994     if ( tmpStr.left(1) == "D") {
00995       type = Recurrence::rDaily;
00996     } else if ( tmpStr.left(1) == "W") {
00997       type = Recurrence::rWeekly;
00998     } else {
00999       typelen = 2;
01000       if ( tmpStr.left(2) == "MP") {
01001         type = Recurrence::rMonthlyPos;
01002       } else if ( tmpStr.left(2) == "MD" ) {
01003         type = Recurrence::rMonthlyDay;
01004       } else if ( tmpStr.left(2) == "YM" ) {
01005         type = Recurrence::rYearlyMonth;
01006       } else if ( tmpStr.left(2) == "YD" ) {
01007         type = Recurrence::rYearlyDay;
01008       }
01009     }
01010 
01011     if ( type != Recurrence::rNone ) {
01012 // kdDebug() << " It's a supported type " << endl;
01013 
01014       // Immediately after the type is the frequency
01015       int index = tmpStr.find(' ');
01016       int last = tmpStr.findRev(' ') + 1; // find last entry
01017       int rFreq = tmpStr.mid(typelen, (index-1)).toInt();
01018       ++index; // advance to beginning of stuff after freq
01019 
01020       // Read the type-specific settings
01021       switch ( type ) {
01022         case Recurrence::rDaily:
01023                anEvent->recurrence()->setDaily(rFreq);
01024                break;
01025 
01026         case Recurrence::rWeekly: {
01027                QBitArray qba(7);
01028                QString dayStr;
01029                if( index == last ) {
01030                  // e.g. W1 #0
01031                  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
01032                }
01033                else {
01034                  // e.g. W1 SU #0
01035                  while (index < last) {
01036                    dayStr = tmpStr.mid(index, 3);
01037                    int dayNum = numFromDay(dayStr);
01038                    qba.setBit(dayNum);
01039                    index += 3; // advance to next day, or possibly "#"
01040                  }
01041                }
01042                anEvent->recurrence()->setWeekly( rFreq, qba );
01043                break; }
01044 
01045         case Recurrence::rMonthlyPos: {
01046                anEvent->recurrence()->setMonthly( rFreq );
01047 
01048                QBitArray qba(7);
01049                short tmpPos;
01050                if( index == last ) {
01051                  // e.g. MP1 #0
01052                  tmpPos = anEvent->dtStart().date().day()/7 + 1;
01053                  if( tmpPos == 5 )
01054                    tmpPos = -1;
01055                  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
01056                  anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01057                }
01058                else {
01059                  // e.g. MP1 1+ SU #0
01060                  while (index < last) {
01061                    tmpPos = tmpStr.mid(index,1).toShort();
01062                    index += 1;
01063                    if (tmpStr.mid(index,1) == "-")
01064                      // convert tmpPos to negative
01065                      tmpPos = 0 - tmpPos;
01066                    index += 2; // advance to day(s)
01067                    while (numFromDay(tmpStr.mid(index,3)) >= 0) {
01068                      int dayNum = numFromDay(tmpStr.mid(index,3));
01069                      qba.setBit(dayNum);
01070                      index += 3; // advance to next day, or possibly pos or "#"
01071                    }
01072                    anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01073                    qba.detach();
01074                    qba.fill(FALSE); // clear out
01075                  } // while != "#"
01076                }
01077              break;}
01078 
01079         case Recurrence::rMonthlyDay:
01080              anEvent->recurrence()->setMonthly( rFreq );
01081              if( index == last ) {
01082                // e.g. MD1 #0
01083                short tmpDay = anEvent->dtStart().date().day();
01084                anEvent->recurrence()->addMonthlyDate( tmpDay );
01085              }
01086              else {
01087                // e.g. MD1 3 #0
01088                while (index < last) {
01089                  int index2 = tmpStr.find(' ', index);
01090                  short tmpDay = tmpStr.mid(index, (index2-index)).toShort();
01091                  index = index2-1;
01092                  if (tmpStr.mid(index, 1) == "-")
01093                    tmpDay = 0 - tmpDay;
01094                  index += 2; // advance the index;
01095                  anEvent->recurrence()->addMonthlyDate( tmpDay );
01096                } // while != #
01097              }
01098              break;
01099 
01100         case Recurrence::rYearlyMonth:
01101              anEvent->recurrence()->setYearly( rFreq );
01102 
01103              if( index == last ) {
01104                // e.g. YM1 #0
01105                short tmpMonth = anEvent->dtStart().date().month();
01106                anEvent->recurrence()->addYearlyMonth( tmpMonth );
01107              }
01108              else {
01109                // e.g. YM1 3 #0
01110                while (index < last) {
01111                  int index2 = tmpStr.find(' ', index);
01112                  short tmpMonth = tmpStr.mid(index, (index2-index)).toShort();
01113                  index = index2 + 1;
01114                  anEvent->recurrence()->addYearlyMonth( tmpMonth );
01115                } // while != #
01116              }
01117              break;
01118 
01119         case Recurrence::rYearlyDay:
01120              anEvent->recurrence()->setYearly( rFreq );
01121 
01122              if( index == last ) {
01123                // e.g. YD1 #0
01124                short tmpDay = anEvent->dtStart().date().dayOfYear();
01125                anEvent->recurrence()->addYearlyDay( tmpDay );
01126              }
01127              else {
01128                // e.g. YD1 123 #0
01129                while (index < last) {
01130                  int index2 = tmpStr.find(' ', index);
01131                  short tmpDay = tmpStr.mid(index, (index2-index)).toShort();
01132                  index = index2+1;
01133                  anEvent->recurrence()->addYearlyDay( tmpDay );
01134                } // while != #
01135              }
01136              break;
01137 
01138         default: break;
01139       }
01140 
01141       // find the last field, which is either the duration or the end date
01142       index = last;
01143       if ( tmpStr.mid(index,1) == "#") {
01144         // Nr of occurrences
01145         index++;
01146         int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt();
01147         if ( rDuration > 0 )
01148           anEvent->recurrence()->setDuration( rDuration );
01149       } else if ( tmpStr.find('T', index) != -1 ) {
01150         QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date();
01151         anEvent->recurrence()->setEndDateTime( rEndDate );
01152       }
01153 // anEvent->recurrence()->dump();
01154 
01155     } else {
01156       kdDebug(5800) << "we don't understand this type of recurrence!" << endl;
01157     } // if known recurrence type
01158   } // repeats
01159 
01160 
01161   // recurrence exceptions
01162   if ((vo = isAPropertyOf(vevent, VCExDateProp)) != 0) {
01163     s = fakeCString(vObjectUStringZValue(vo));
01164     QStringList exDates = QStringList::split(",",s);
01165     QStringList::ConstIterator it;
01166     for(it = exDates.begin(); it != exDates.end(); ++it ) {
01167       anEvent->recurrence()->addExDate(ISOToQDate(*it));
01168     }
01169     deleteStr(s);
01170   }
01171 
01172   // summary
01173   if ((vo = isAPropertyOf(vevent, VCSummaryProp))) {
01174     s = fakeCString(vObjectUStringZValue(vo));
01175     anEvent->setSummary(QString::fromLocal8Bit(s));
01176     deleteStr(s);
01177   }
01178 
01179   // description
01180   if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != 0) {
01181     s = fakeCString(vObjectUStringZValue(vo));
01182     if (!anEvent->description().isEmpty()) {
01183       anEvent->setDescription(anEvent->description() + "\n" +
01184                               QString::fromLocal8Bit(s));
01185     } else {
01186       anEvent->setDescription(QString::fromLocal8Bit(s));
01187     }
01188     deleteStr(s);
01189   }
01190 
01191   // location
01192   if ((vo = isAPropertyOf(vevent, VCLocationProp)) != 0) {
01193     s = fakeCString(vObjectUStringZValue(vo));
01194     anEvent->setLocation( QString::fromLocal8Bit(s) );
01195     deleteStr(s);
01196   }
01197 
01198   // some stupid vCal exporters ignore the standard and use Description
01199   // instead of Summary for the default field.  Correct for this.
01200   if (anEvent->summary().isEmpty() &&
01201       !(anEvent->description().isEmpty())) {
01202     QString tmpStr = anEvent->description().simplifyWhiteSpace();
01203     anEvent->setDescription("");
01204     anEvent->setSummary(tmpStr);
01205   }
01206 
01207 #if 0
01208   // status
01209   if ((vo = isAPropertyOf(vevent, VCStatusProp)) != 0) {
01210     QString tmpStr(s = fakeCString(vObjectUStringZValue(vo)));
01211     deleteStr(s);
01212 // TODO: Define Event status
01213 //    anEvent->setStatus(tmpStr);
01214   }
01215   else
01216 //    anEvent->setStatus("NEEDS ACTION");
01217 #endif
01218 
01219   // secrecy
01220   int secrecy = Incidence::SecrecyPublic;
01221   if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) {
01222     s = fakeCString(vObjectUStringZValue(vo));
01223     if (strcmp(s,"PRIVATE") == 0) {
01224       secrecy = Incidence::SecrecyPrivate;
01225     } else if (strcmp(s,"CONFIDENTIAL") == 0) {
01226       secrecy = Incidence::SecrecyConfidential;
01227     }
01228     deleteStr(s);
01229   }
01230   anEvent->setSecrecy(secrecy);
01231 
01232   // categories
01233   if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != 0) {
01234     s = fakeCString(vObjectUStringZValue(vo));
01235     QString categories = QString::fromLocal8Bit(s);
01236     deleteStr(s);
01237     QStringList tmpStrList = QStringList::split( ',', categories );
01238     anEvent->setCategories(tmpStrList);
01239   }
01240 
01241   // attachments
01242   initPropIterator(&voi, vevent);
01243   while (moreIteration(&voi)) {
01244     vo = nextVObject(&voi);
01245     if (strcmp(vObjectName(vo), VCAttachProp) == 0) {
01246       s = fakeCString(vObjectUStringZValue(vo));
01247       anEvent->addAttachment(new Attachment(QString(s)));
01248       deleteStr(s);
01249     }
01250   }
01251 
01252   // resources
01253   if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) {
01254     QString resources = (s = fakeCString(vObjectUStringZValue(vo)));
01255     deleteStr(s);
01256     QStringList tmpStrList = QStringList::split( ';', resources );
01257     anEvent->setResources(tmpStrList);
01258   }
01259 
01260   /* alarm stuff */
01261   if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) {
01262     Alarm* alarm = anEvent->newAlarm();
01263     VObject *a;
01264     if ((a = isAPropertyOf(vo, VCRunTimeProp))) {
01265       alarm->setTime(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(a))));
01266       deleteStr(s);
01267     }
01268     alarm->setEnabled(true);
01269     if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) {
01270       if ((a = isAPropertyOf(vo, VCProcedureNameProp))) {
01271         s = fakeCString(vObjectUStringZValue(a));
01272         alarm->setProcedureAlarm(QFile::decodeName(s));
01273         deleteStr(s);
01274       }
01275     }
01276     if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) {
01277       if ((a = isAPropertyOf(vo, VCAudioContentProp))) {
01278         s = fakeCString(vObjectUStringZValue(a));
01279         alarm->setAudioAlarm(QFile::decodeName(s));
01280         deleteStr(s);
01281       }
01282     }
01283   }
01284 
01285   // priority
01286   if ((vo = isAPropertyOf(vevent, VCPriorityProp))) {
01287     anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo))));
01288     deleteStr(s);
01289   }
01290 
01291   // transparency
01292   if ((vo = isAPropertyOf(vevent, VCTranspProp)) != 0) {
01293     int i = atoi(s = fakeCString(vObjectUStringZValue(vo)));
01294     anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
01295     deleteStr(s);
01296   }
01297 
01298   // related event
01299   if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != 0) {
01300     anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo)));
01301     deleteStr(s);
01302     mEventsRelate.append(anEvent);
01303   }
01304 
01305   /* PILOT SYNC STUFF */
01306   if ((vo = isAPropertyOf(vevent, KPilotIdProp))) {
01307     anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo))));
01308     deleteStr(s);
01309   }
01310   else
01311     anEvent->setPilotId(0);
01312 
01313   if ((vo = isAPropertyOf(vevent, KPilotStatusProp))) {
01314     anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo))));
01315     deleteStr(s);
01316   }
01317   else
01318     anEvent->setSyncStatus(Event::SYNCMOD);
01319 
01320   return anEvent;
01321 }
01322 
01323 
01324 QString VCalFormat::qDateToISO(const QDate &qd)
01325 {
01326   QString tmpStr;
01327 
01328   Q_ASSERT(qd.isValid());
01329 
01330   tmpStr.sprintf("%.2d%.2d%.2d",
01331                  qd.year(), qd.month(), qd.day());
01332   return tmpStr;
01333 
01334 }
01335 
01336 /* Return the offset of the named zone as seconds. tt is a time
01337    indicating the date for which you want the offset */
01338 int vcaltime_utc_offset( QDateTime ictt, QString tzid )
01339 {
01340   // libical-0.23 stuff:
01341   //  struct icaltimetype tt = icaltime_from_timet( ictt.toTime_t(), false );
01342   //  return icaltime_utc_offset( tt, tzid.latin1() );
01343   int daylight;
01344   struct icaltimetype tt = icaltime_from_timet( ictt.toTime_t(), false );
01345   //source says this is DEPRECATED, but it doesn't say what to use instead
01346   //how to handle failure from icaltimezone_get_builtin_timezone_from_tzid()?
01347   return icaltimezone_get_utc_offset(
01348     icaltimezone_get_builtin_timezone( tzid.latin1() ),
01349     &tt, &daylight );
01350 }
01351 
01352 QString VCalFormat::qDateTimeToISO(const QDateTime &qdt, bool zulu)
01353 {
01354   QString tmpStr;
01355 
01356   Q_ASSERT(qdt.date().isValid());
01357   Q_ASSERT(qdt.time().isValid());
01358   if (zulu) {
01359     QDateTime tmpDT(qdt);
01360     // correct to GMT:
01361     tmpDT = tmpDT.addSecs(-vcaltime_utc_offset( tmpDT, mCalendar->timeZoneId()));
01362     tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2dZ",
01363                     tmpDT.date().year(), tmpDT.date().month(),
01364                     tmpDT.date().day(), tmpDT.time().hour(),
01365                     tmpDT.time().minute(), tmpDT.time().second());
01366   } else {
01367     tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
01368                     qdt.date().year(), qdt.date().month(),
01369                     qdt.date().day(), qdt.time().hour(),
01370                     qdt.time().minute(), qdt.time().second());
01371   }
01372   return tmpStr;
01373 }
01374 
01375 QDateTime VCalFormat::ISOToQDateTime(const QString & dtStr)
01376 {
01377   QDate tmpDate;
01378   QTime tmpTime;
01379   QString tmpStr;
01380   int year, month, day, hour, minute, second;
01381 
01382   tmpStr = dtStr;
01383   year = tmpStr.left(4).toInt();
01384   month = tmpStr.mid(4,2).toInt();
01385   day = tmpStr.mid(6,2).toInt();
01386   hour = tmpStr.mid(9,2).toInt();
01387   minute = tmpStr.mid(11,2).toInt();
01388   second = tmpStr.mid(13,2).toInt();
01389   tmpDate.setYMD(year, month, day);
01390   tmpTime.setHMS(hour, minute, second);
01391 
01392   Q_ASSERT(tmpDate.isValid());
01393   Q_ASSERT(tmpTime.isValid());
01394   QDateTime tmpDT(tmpDate, tmpTime);
01395   // correct for GMT if string is in Zulu format
01396   if (dtStr.at(dtStr.length()-1) == 'Z') {
01397     tmpDT = tmpDT.addSecs(vcaltime_utc_offset( tmpDT, mCalendar->timeZoneId()));
01398   }
01399   return tmpDT;
01400 }
01401 
01402 QDate VCalFormat::ISOToQDate(const QString &dateStr)
01403 {
01404   int year, month, day;
01405 
01406   year = dateStr.left(4).toInt();
01407   month = dateStr.mid(4,2).toInt();
01408   day = dateStr.mid(6,2).toInt();
01409 
01410   return(QDate(year, month, day));
01411 }
01412 
01413 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
01414 // and break it down from it's tree-like format into the dictionary format
01415 // that is used internally in the VCalFormat.
01416 void VCalFormat::populate(VObject *vcal)
01417 {
01418   // this function will populate the caldict dictionary and other event
01419   // lists. It turns vevents into Events and then inserts them.
01420 
01421   VObjectIterator i;
01422   VObject *curVO, *curVOProp;
01423   Event *anEvent;
01424 
01425   if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) {
01426     char *methodType = 0;
01427     methodType = fakeCString(vObjectUStringZValue(curVO));
01428     kdDebug(5800) << "This calendar is an iTIP transaction of type '"
01429               << methodType << "'" << endl;
01430     deleteStr(methodType);
01431   }
01432 
01433   // warn the user that we might have trouble reading non-known calendar.
01434   if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != 0) {
01435     char *s = fakeCString(vObjectUStringZValue(curVO));
01436     if (strcmp(productId().local8Bit(), s) != 0)
01437       kdDebug(5800) << "This vCalendar file was not created by KOrganizer "
01438                    "or any other product we support. Loading anyway..." << endl;
01439     mLoadedProductId = s;
01440     deleteStr(s);
01441   }
01442 
01443   // warn the user we might have trouble reading this unknown version.
01444   if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) {
01445     char *s = fakeCString(vObjectUStringZValue(curVO));
01446     if (strcmp(_VCAL_VERSION, s) != 0)
01447       kdDebug(5800) << "This vCalendar file has version " << s
01448                 << "We only support " << _VCAL_VERSION << endl;
01449     deleteStr(s);
01450   }
01451 
01452 #if 0
01453   // set the time zone (this is a property of the view, so just discard!)
01454   if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) {
01455     char *s = fakeCString(vObjectUStringZValue(curVO));
01456     mCalendar->setTimeZone(s);
01457     deleteStr(s);
01458   }
01459 #endif
01460 
01461   // Store all events with a relatedTo property in a list for post-processing
01462   mEventsRelate.clear();
01463   mTodosRelate.clear();
01464 
01465   initPropIterator(&i, vcal);
01466 
01467   // go through all the vobjects in the vcal
01468   while (moreIteration(&i)) {
01469     curVO = nextVObject(&i);
01470 
01471     /************************************************************************/
01472 
01473     // now, check to see that the object is an event or todo.
01474     if (strcmp(vObjectName(curVO), VCEventProp) == 0) {
01475 
01476       if ((curVOProp = isAPropertyOf(curVO, KPilotStatusProp)) != 0) {
01477         char *s;
01478         s = fakeCString(vObjectUStringZValue(curVOProp));
01479         // check to see if event was deleted by the kpilot conduit
01480         if (atoi(s) == Event::SYNCDEL) {
01481           deleteStr(s);
01482           kdDebug(5800) << "skipping pilot-deleted event" << endl;
01483           goto SKIP;
01484         }
01485         deleteStr(s);
01486       }
01487 
01488       // this code checks to see if we are trying to read in an event
01489       // that we already find to be in the calendar.  If we find this
01490       // to be the case, we skip the event.
01491       if ((curVOProp = isAPropertyOf(curVO, VCUniqueStringProp)) != 0) {
01492         char *s = fakeCString(vObjectUStringZValue(curVOProp));
01493         QString tmpStr(s);
01494         deleteStr(s);
01495 
01496         if (mCalendar->incidence(tmpStr)) {
01497           goto SKIP;
01498         }
01499       }
01500 
01501       if ((!(curVOProp = isAPropertyOf(curVO, VCDTstartProp))) &&
01502           (!(curVOProp = isAPropertyOf(curVO, VCDTendProp)))) {
01503         kdDebug(5800) << "found a VEvent with no DTSTART and no DTEND! Skipping..." << endl;
01504         goto SKIP;
01505       }
01506 
01507       anEvent = VEventToEvent(curVO);
01508       // we now use addEvent instead of insertEvent so that the
01509       // signal/slot get connected.
01510       if (anEvent) {
01511               if ( !anEvent->dtStart().isValid() || !anEvent->dtEnd().isValid() ) {
01512           kdDebug(5800) << "VCalFormat::populate(): Event has invalid dates."
01513                     << endl;
01514         } else {
01515           mCalendar->addEvent(anEvent);
01516               }
01517       } else {
01518         // some sort of error must have occurred while in translation.
01519         goto SKIP;
01520       }
01521     } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) {
01522       Todo *aTodo = VTodoToEvent(curVO);
01523       mCalendar->addTodo(aTodo);
01524     } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) ||
01525                (strcmp(vObjectName(curVO), VCProdIdProp) == 0) ||
01526                (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) {
01527       // do nothing, we know these properties and we want to skip them.
01528       // we have either already processed them or are ignoring them.
01529       ;
01530     } else {
01531       kdDebug(5800) << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"" << endl;
01532     }
01533   SKIP:
01534     ;
01535   } // while
01536 
01537   // Post-Process list of events with relations, put Event objects in relation
01538   Event::List::ConstIterator eIt;
01539   for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) {
01540     (*eIt)->setRelatedTo( mCalendar->incidence( (*eIt)->relatedToUid() ) );
01541   }
01542   Todo::List::ConstIterator tIt;
01543   for ( tIt = mTodosRelate.begin(); tIt != mTodosRelate.end(); ++tIt ) {
01544     (*tIt)->setRelatedTo( mCalendar->incidence( (*tIt)->relatedToUid() ) );
01545    }
01546 }
01547 
01548 const char *VCalFormat::dayFromNum(int day)
01549 {
01550   const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
01551 
01552   return days[day];
01553 }
01554 
01555 int VCalFormat::numFromDay(const QString &day)
01556 {
01557   if (day == "MO ") return 0;
01558   if (day == "TU ") return 1;
01559   if (day == "WE ") return 2;
01560   if (day == "TH ") return 3;
01561   if (day == "FR ") return 4;
01562   if (day == "SA ") return 5;
01563   if (day == "SU ") return 6;
01564 
01565   return -1; // something bad happened. :)
01566 }
01567 
01568 Attendee::PartStat VCalFormat::readStatus(const char *s) const
01569 {
01570   QString statStr = s;
01571   statStr = statStr.upper();
01572   Attendee::PartStat status;
01573 
01574   if (statStr == "X-ACTION")
01575     status = Attendee::NeedsAction;
01576   else if (statStr == "NEEDS ACTION")
01577     status = Attendee::NeedsAction;
01578   else if (statStr== "ACCEPTED")
01579     status = Attendee::Accepted;
01580   else if (statStr== "SENT")
01581     status = Attendee::NeedsAction;
01582   else if (statStr== "TENTATIVE")
01583     status = Attendee::Tentative;
01584   else if (statStr== "CONFIRMED")
01585     status = Attendee::Accepted;
01586   else if (statStr== "DECLINED")
01587     status = Attendee::Declined;
01588   else if (statStr== "COMPLETED")
01589     status = Attendee::Completed;
01590   else if (statStr== "DELEGATED")
01591     status = Attendee::Delegated;
01592   else {
01593     kdDebug(5800) << "error setting attendee mStatus, unknown mStatus!" << endl;
01594     status = Attendee::NeedsAction;
01595   }
01596 
01597   return status;
01598 }
01599 
01600 QCString VCalFormat::writeStatus(Attendee::PartStat status) const
01601 {
01602   switch(status) {
01603     default:
01604     case Attendee::NeedsAction:
01605       return "NEEDS ACTION";
01606       break;
01607     case Attendee::Accepted:
01608       return "ACCEPTED";
01609       break;
01610     case Attendee::Declined:
01611       return "DECLINED";
01612       break;
01613     case Attendee::Tentative:
01614       return "TENTATIVE";
01615       break;
01616     case Attendee::Delegated:
01617       return "DELEGATED";
01618       break;
01619     case Attendee::Completed:
01620       return "COMPLETED";
01621       break;
01622     case Attendee::InProcess:
01623       return "NEEDS ACTION";
01624       break;
01625   }
01626 }