libkcal

vcalformat.cpp

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