libopie PIM API Documentation

odatebookaccessbackend_xml.cpp

Go to the documentation of this file.
00001 #include <errno.h>
00002 #include <fcntl.h>
00003 
00004 #include <stdio.h>
00005 #include <stdlib.h>
00006 
00007 #include <sys/types.h>
00008 #include <sys/mman.h>
00009 #include <sys/stat.h>
00010 
00011 #include <unistd.h>
00012 
00013 #include <qasciidict.h>
00014 #include <qfile.h>
00015 
00016 #include <qtopia/global.h>
00017 #include <qtopia/stringutil.h>
00018 #include <qtopia/timeconversion.h>
00019 
00020 #include "opimnotifymanager.h"
00021 #include "orecur.h"
00022 #include "otimezone.h"
00023 #include "odatebookaccessbackend_xml.h"
00024 
00025 namespace {
00026     // FROM TT again
00027 char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
00028 {
00029     char needleChar;
00030     char haystackChar;
00031     if (!needle || !haystack || !hLen || !nLen)
00032     return 0;
00033 
00034     const char* hsearch = haystack;
00035 
00036     if ((needleChar = *needle++) != 0) {
00037     nLen--; //(to make up for needle++)
00038     do {
00039         do {
00040         if ((haystackChar = *hsearch++) == 0)
00041             return (0);
00042         if (hsearch >= haystack + hLen)
00043             return (0);
00044         } while (haystackChar != needleChar);
00045     } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
00046     hsearch--;
00047     }
00048     return ((char *)hsearch);
00049 }
00050 }
00051 
00052 namespace {
00053     time_t start, end, created, rp_end;
00054     ORecur* rec;
00055     ORecur* recur() {
00056         if (!rec)
00057             rec = new ORecur;
00058 
00059         return rec;
00060     }
00061     int alarmTime;
00062     int snd;
00063     enum Attribute{
00064         FDescription = 0,
00065         FLocation,
00066         FCategories,
00067         FUid,
00068         FType,
00069     FAlarm,
00070     FSound,
00071     FRType,
00072     FRWeekdays,
00073     FRPosition,
00074     FRFreq,
00075     FRHasEndDate,
00076     FREndDate,
00077     FRStart,
00078     FREnd,
00079     FNote,
00080     FCreated,      // Should't this be called FRCreated ?
00081         FTimeZone,
00082         FRecParent,
00083         FRecChildren,
00084         FExceptions
00085     };
00086 
00087     // FIXME: Use OEvent::toMap() here !! (eilers)
00088     inline void save( const OEvent& ev, QString& buf ) {
00089         qWarning("Saving %d %s", ev.uid(), ev.description().latin1() );
00090         buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\"";
00091         if (!ev.location().isEmpty() )
00092             buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\"";
00093 
00094         buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\"";
00095         buf += " uid=\"" + QString::number( ev.uid() ) + "\"";
00096 
00097         if (ev.isAllDay() )
00098         buf += " type=\"AllDay\""; // is that all ?? (eilers)
00099 
00100         if (ev.hasNotifiers() ) {
00101             OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first
00102             int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60;
00103             buf += " alarm=\"" + QString::number(minutes) + "\" sound=\"";
00104             if ( alarm.sound() == OPimAlarm::Loud )
00105                 buf += "loud";
00106             else
00107                 buf += "silent";
00108             buf += "\"";
00109         }
00110         if ( ev.hasRecurrence() ) {
00111             buf += ev.recurrence().toString();
00112         }
00113 
00114         /*
00115          * fscking timezones :) well, we'll first convert
00116          * the QDateTime to a QDateTime in UTC time
00117          * and then we'll create a nice time_t
00118          */
00119         OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
00120         buf += " start=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.startDateTime(), OTimeZone::utc() ) ) )  + "\"";
00121         buf += " end=\""   + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.endDateTime()  , OTimeZone::utc() ) ) )   + "\"";
00122         if (!ev.note().isEmpty() ) {
00123             buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\"";
00124         }
00125 
00126         buf += " timezone=\"";
00127         if ( ev.timeZone().isEmpty() )
00128             buf += "None";
00129         else
00130             buf += ev.timeZone();
00131         buf += "\"";
00132 
00133         if (ev.parent() != 0 ) {
00134             buf += " recparent=\""+QString::number(ev.parent() )+"\"";
00135         }
00136 
00137         if (ev.children().count() != 0 ) {
00138             QArray<int> children = ev.children();
00139             buf += " recchildren=\"";
00140             for ( uint i = 0; i < children.count(); i++ ) {
00141                 if ( i != 0 ) buf += " ";
00142                 buf += QString::number( children[i] );
00143             }
00144             buf+= "\"";
00145         }
00146 
00147         // skip custom writing
00148     }
00149 
00150     inline bool forAll( const QMap<int, OEvent>& list, QFile& file ) {
00151         QMap<int, OEvent>::ConstIterator it;
00152         QString buf;
00153         QCString str;
00154         int total_written;
00155         for ( it = list.begin(); it != list.end(); ++it ) {
00156             buf = "<event";
00157             save( it.data(), buf );
00158             buf += " />\n";
00159             str = buf.utf8();
00160 
00161             total_written = file.writeBlock(str.data(), str.length() );
00162             if ( total_written != int(str.length() ) )
00163                 return false;
00164         }
00165         return true;
00166     }
00167 }
00168 
00169 ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& ,
00170                                                         const QString& fileName )
00171     : ODateBookAccessBackend() {
00172     m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName;
00173     m_changed = false;
00174 }
00175 ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() {
00176 }
00177 bool ODateBookAccessBackend_XML::load() {
00178     return loadFile();
00179 }
00180 bool ODateBookAccessBackend_XML::reload() {
00181     clear();
00182     return load();
00183 }
00184 bool ODateBookAccessBackend_XML::save() {
00185     if (!m_changed) return true;
00186 
00187     int total_written;
00188     QString strFileNew = m_name + ".new";
00189 
00190     QFile f( strFileNew );
00191     if (!f.open( IO_WriteOnly | IO_Raw ) ) return false;
00192 
00193     QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
00194     buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
00195     buf += "<events>\n";
00196     QCString str = buf.utf8();
00197     total_written = f.writeBlock( str.data(), str.length() );
00198     if ( total_written != int(str.length() ) ) {
00199         f.close();
00200         QFile::remove( strFileNew );
00201         return false;
00202     }
00203 
00204     if (!forAll( m_raw, f ) ) {
00205         f.close();
00206         QFile::remove( strFileNew );
00207         return false;
00208     }
00209     if (!forAll( m_rep, f ) ) {
00210         f.close();
00211         QFile::remove( strFileNew );
00212         return false;
00213     }
00214 
00215     buf = "</events>\n</DATEBOOK>\n";
00216     str = buf.utf8();
00217     total_written = f.writeBlock( str.data(), str.length() );
00218     if ( total_written != int(str.length() ) ) {
00219         f.close();
00220         QFile::remove( strFileNew );
00221         return false;
00222     }
00223     f.close();
00224 
00225     if ( ::rename( strFileNew, m_name ) < 0 ) {
00226         QFile::remove( strFileNew );
00227         return false;
00228     }
00229 
00230     m_changed = false;
00231     return true;
00232 }
00233 QArray<int> ODateBookAccessBackend_XML::allRecords()const {
00234     QArray<int> ints( m_raw.count()+ m_rep.count() );
00235     uint i = 0;
00236     QMap<int, OEvent>::ConstIterator it;
00237 
00238     for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
00239         ints[i] = it.key();
00240         i++;
00241     }
00242     for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
00243         ints[i] = it.key();
00244         i++;
00245     }
00246 
00247     return ints;
00248 }
00249 QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int,  const QDateTime& ) {
00250     return QArray<int>();
00251 }
00252 void ODateBookAccessBackend_XML::clear() {
00253     m_changed = true;
00254     m_raw.clear();
00255     m_rep.clear();
00256 }
00257 OEvent ODateBookAccessBackend_XML::find( int uid ) const{
00258     if ( m_raw.contains( uid ) )
00259         return m_raw[uid];
00260     else
00261         return m_rep[uid];
00262 }
00263 bool ODateBookAccessBackend_XML::add( const OEvent& ev ) {
00264     m_changed = true;
00265     if (ev.hasRecurrence() )
00266         m_rep.insert( ev.uid(), ev );
00267     else
00268         m_raw.insert( ev.uid(), ev );
00269 
00270     return true;
00271 }
00272 bool ODateBookAccessBackend_XML::remove( int uid ) {
00273     m_changed = true;
00274     m_rep.remove( uid );
00275     m_rep.remove( uid );
00276 
00277     return true;
00278 }
00279 bool ODateBookAccessBackend_XML::replace( const OEvent& ev ) {
00280     replace( ev.uid() ); // ??? Shouldn't this be "remove( ev.uid() ) ??? (eilers)
00281     return add( ev );
00282 }
00283 QArray<int> ODateBookAccessBackend_XML::rawEvents()const {
00284     return allRecords();
00285 }
00286 QArray<int> ODateBookAccessBackend_XML::rawRepeats()const {
00287     QArray<int> ints( m_rep.count() );
00288     uint i = 0;
00289     QMap<int, OEvent>::ConstIterator it;
00290 
00291     for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
00292         ints[i] = it.key();
00293         i++;
00294     }
00295 
00296     return ints;
00297 }
00298 QArray<int> ODateBookAccessBackend_XML::nonRepeats()const {
00299     QArray<int> ints( m_raw.count() );
00300     uint i = 0;
00301     QMap<int, OEvent>::ConstIterator it;
00302 
00303     for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
00304         ints[i] = it.key();
00305         i++;
00306     }
00307 
00308     return ints;
00309 }
00310 OEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats() {
00311     OEvent::ValueList list;
00312     QMap<int, OEvent>::ConstIterator it;
00313     for (it = m_raw.begin(); it != m_raw.end(); ++it )
00314         list.append( it.data() );
00315 
00316     return list;
00317 }
00318 OEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats() {
00319     OEvent::ValueList list;
00320     QMap<int, OEvent>::ConstIterator it;
00321     for (it = m_rep.begin(); it != m_rep.end(); ++it )
00322         list.append( it.data() );
00323 
00324     return list;
00325 }
00326 
00327 // FIXME: Use OEvent::fromMap() (eilers)
00328 bool ODateBookAccessBackend_XML::loadFile() {
00329     m_changed = false;
00330 
00331     int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY );
00332     if ( fd < 0 ) return false;
00333 
00334     struct stat attribute;
00335     if ( ::fstat(fd, &attribute ) == -1 ) {
00336         ::close( fd );
00337         return false;
00338     }
00339     void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 );
00340     if ( map_addr == ( (caddr_t)-1) ) {
00341         ::close( fd );
00342         return false;
00343     }
00344 
00345     ::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL );
00346     ::close( fd );
00347 
00348     QAsciiDict<int> dict(FExceptions+1);
00349     dict.setAutoDelete( true );
00350     dict.insert( "description", new int(FDescription) );
00351     dict.insert( "location", new int(FLocation) );
00352     dict.insert( "categories", new int(FCategories) );
00353     dict.insert( "uid", new int(FUid) );
00354     dict.insert( "type", new int(FType) );
00355     dict.insert( "alarm", new int(FAlarm) );
00356     dict.insert( "sound", new int(FSound) );
00357     dict.insert( "rtype", new int(FRType) );
00358     dict.insert( "rweekdays", new int(FRWeekdays) );
00359     dict.insert( "rposition", new int(FRPosition) );
00360     dict.insert( "rfreq", new int(FRFreq) );
00361     dict.insert( "rhasenddate", new int(FRHasEndDate) );
00362     dict.insert( "enddt", new int(FREndDate) );
00363     dict.insert( "start", new int(FRStart) );
00364     dict.insert( "end", new int(FREnd) );
00365     dict.insert( "note", new int(FNote) );
00366     dict.insert( "created", new int(FCreated) );  // Shouldn't this be FRCreated ??
00367     dict.insert( "recparent", new int(FRecParent) );
00368     dict.insert( "recchildren", new int(FRecChildren) );
00369     dict.insert( "exceptions", new int(FExceptions) );
00370     dict.insert( "timezone", new int(FTimeZone) );
00371 
00372     char* dt = (char*)map_addr;
00373     int len = attribute.st_size;
00374     int i = 0;
00375     char* point;
00376     const char* collectionString = "<event ";
00377     int strLen = ::strlen(collectionString);
00378     int *find;
00379     while ( (  point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0  ) {
00380         i = point -dt;
00381         i+= strLen;
00382 
00383         alarmTime = -1;
00384         snd = 0; // silent
00385 
00386         OEvent ev;
00387         rec = 0;
00388 
00389         while ( TRUE ) {
00390             while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
00391         ++i;
00392         if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
00393         break;
00394 
00395 
00396         // we have another attribute, read it.
00397         int j = i;
00398         while ( j < len && dt[j] != '=' )
00399         ++j;
00400         QCString attr( dt+i, j-i+1);
00401 
00402         i = ++j; // skip =
00403 
00404         // find the start of quotes
00405         while ( i < len && dt[i] != '"' )
00406         ++i;
00407         j = ++i;
00408 
00409         bool haveUtf = FALSE;
00410         bool haveEnt = FALSE;
00411         while ( j < len && dt[j] != '"' ) {
00412         if ( ((unsigned char)dt[j]) > 0x7f )
00413             haveUtf = TRUE;
00414         if ( dt[j] == '&' )
00415             haveEnt = TRUE;
00416         ++j;
00417         }
00418         if ( i == j ) {
00419         // empty value
00420         i = j + 1;
00421         continue;
00422         }
00423 
00424         QCString value( dt+i, j-i+1 );
00425         i = j + 1;
00426 
00427         QString str = (haveUtf ? QString::fromUtf8( value )
00428             : QString::fromLatin1( value ) );
00429         if ( haveEnt )
00430         str = Qtopia::plainString( str );
00431 
00432             /*
00433              * add key + value
00434              */
00435             find = dict[attr.data()];
00436             if (!find)
00437                 ev.setCustomField( attr, str );
00438             else {
00439                 setField( ev, *find, str );
00440             }
00441         }
00442         /* time to finalize */
00443         finalizeRecord( ev );
00444         delete rec;
00445     }
00446     ::munmap(map_addr, attribute.st_size );
00447     m_changed = false; // changed during add
00448 
00449     return true;
00450 }
00451 
00452 // FIXME: Use OEvent::fromMap() which makes this obsolete.. (eilers)
00453 void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) {
00454     /* AllDay is alway in UTC */
00455     if ( ev.isAllDay() ) {
00456         OTimeZone utc = OTimeZone::utc();
00457         ev.setStartDateTime( utc.fromUTCDateTime( start ) );
00458         ev.setEndDateTime  ( utc.fromUTCDateTime( end   ) );
00459         ev.setTimeZone( "UTC"); // make sure it is really utc
00460     }else {
00461         /* to current date time */
00462         // qWarning(" Start is %d", start );
00463         OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
00464         QDateTime date = zone.toDateTime( start );
00465         qWarning(" Start is %s", date.toString().latin1() );
00466         ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) );
00467 
00468         date = zone.toDateTime( end );
00469         ev.setEndDateTime  ( zone.toDateTime( date, OTimeZone::current()   ) );
00470     }
00471     if ( rec && rec->doesRecur() ) {
00472         OTimeZone utc = OTimeZone::utc();
00473         ORecur recu( *rec ); // call copy c'tor;
00474         recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() );
00475         recu.setCreatedDateTime( utc.fromUTCDateTime( created ) );
00476         recu.setStart( ev.startDateTime().date() );
00477         ev.setRecurrence( recu );
00478     }
00479 
00480     if (alarmTime != -1 ) {
00481         QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 );
00482         OPimAlarm al( snd ,  dt  );
00483         ev.notifiers().add( al );
00484     }
00485     if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) {
00486         qWarning("already contains assign uid");
00487         ev.setUid( 1 );
00488     }
00489     qWarning("addind %d %s", ev.uid(), ev.description().latin1() );
00490     if ( ev.hasRecurrence() )
00491         m_rep.insert( ev.uid(), ev );
00492     else
00493         m_raw.insert( ev.uid(), ev );
00494 
00495 }
00496 void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& value) {
00497 //    qWarning(" setting %s", value.latin1() );
00498     switch( id ) {
00499     case FDescription:
00500         e.setDescription( value );
00501         break;
00502     case FLocation:
00503         e.setLocation( value );
00504         break;
00505     case FCategories:
00506         e.setCategories( e.idsFromString( value ) );
00507         break;
00508     case FUid:
00509         e.setUid( value.toInt() );
00510         break;
00511     case FType:
00512         if ( value == "AllDay" ) {
00513             e.setAllDay( true );
00514             e.setTimeZone( "UTC" );
00515         }
00516         break;
00517     case FAlarm:
00518         alarmTime = value.toInt();
00519         break;
00520     case FSound:
00521         snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent;
00522         break;
00523         // recurrence stuff
00524     case FRType:
00525         if ( value == "Daily" )
00526             recur()->setType( ORecur::Daily );
00527         else if ( value == "Weekly" )
00528             recur()->setType( ORecur::Weekly);
00529         else if ( value == "MonthlyDay" )
00530             recur()->setType( ORecur::MonthlyDay );
00531         else if ( value == "MonthlyDate" )
00532             recur()->setType( ORecur::MonthlyDate );
00533         else if ( value == "Yearly" )
00534             recur()->setType( ORecur::Yearly );
00535         else
00536             recur()->setType( ORecur::NoRepeat );
00537         break;
00538     case FRWeekdays:
00539         recur()->setDays( value.toInt() );
00540         break;
00541     case FRPosition:
00542         recur()->setPosition( value.toInt() );
00543         break;
00544     case FRFreq:
00545         recur()->setFrequency( value.toInt() );
00546         break;
00547     case FRHasEndDate:
00548         recur()->setHasEndDate( value.toInt() );
00549         break;
00550     case FREndDate: {
00551         rp_end = (time_t) value.toLong();
00552         break;
00553     }
00554     case FRStart: {
00555         start =  (time_t) value.toLong();
00556         break;
00557     }
00558     case FREnd: {
00559         end = ( (time_t) value.toLong() );
00560         break;
00561     }
00562     case FNote:
00563         e.setNote( value );
00564         break;
00565     case FCreated:
00566         created = value.toInt();
00567         break;
00568     case FRecParent:
00569         e.setParent( value.toInt() );
00570         break;
00571     case FRecChildren:{
00572         QStringList list = QStringList::split(' ', value );
00573         for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00574             e.addChild( (*it).toInt() );
00575         }
00576     }
00577         break;
00578     case FExceptions:{
00579         QStringList list = QStringList::split(' ', value );
00580         for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00581             QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() );
00582             qWarning("adding exception %s", date.toString().latin1() );
00583             recur()->exceptions().append( date );
00584         }
00585     }
00586         break;
00587     case FTimeZone:
00588         if ( value != "None" )
00589             e.setTimeZone( value );
00590         break;
00591     default:
00592         break;
00593     }
00594 }
00595 QArray<int> ODateBookAccessBackend_XML::matchRegexp(  const QRegExp &r ) const
00596 {
00597     QArray<int> m_currentQuery( m_raw.count()+ m_rep.count() );
00598     uint arraycounter = 0;
00599     QMap<int, OEvent>::ConstIterator it;
00600 
00601     for ( it = m_raw.begin(); it != m_raw.end(); ++it )
00602         if ( it.data().match( r ) )
00603             m_currentQuery[arraycounter++] = it.data().uid();
00604     for ( it = m_rep.begin(); it != m_rep.end(); ++it )
00605         if ( it.data().match( r ) )
00606             m_currentQuery[arraycounter++] = it.data().uid();
00607 
00608     // Shrink to fit..
00609     m_currentQuery.resize(arraycounter);
00610 
00611     return m_currentQuery;
00612 }
KDE Logo
This file is part of the documentation for OPIE Version 1.1.
Documentation copyright © 1997-2003 the KDE developers. 2003 OPIE developers
Generated on Tue Feb 10 20:25:20 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001