kpilot/lib

pilotLocalDatabase.cc

00001 /* KPilot
00002 **
00003 ** Copyright (C) 1998-2001 by Dan Pilone
00004 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 **
00006 ** This defines an interface to Pilot databases on the local disk.
00007 */
00008 
00009 /*
00010 ** This program is free software; you can redistribute it and/or modify
00011 ** it under the terms of the GNU Lesser General Public License as published by
00012 ** the Free Software Foundation; either version 2.1 of the License, or
00013 ** (at your option) any later version.
00014 **
00015 ** This program is distributed in the hope that it will be useful,
00016 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00018 ** GNU Lesser General Public License for more details.
00019 **
00020 ** You should have received a copy of the GNU Lesser General Public License
00021 ** along with this program in a file called COPYING; if not, write to
00022 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00023 ** MA 02110-1301, USA.
00024 */
00025 
00026 /*
00027 ** Bug reports and questions can be sent to kde-pim@kde.org
00028 */
00029 
00030 
00031 #include <config.h>
00032 #include "options.h"
00033 #include "fakes.h"
00034 
00035 #include <stdio.h>
00036 #include <unistd.h>
00037 #include <assert.h>
00038 
00039 #include <iostream>
00040 
00041 #include <qstring.h>
00042 #include <qfile.h>
00043 #include <qregexp.h>
00044 #include <qdatetime.h>
00045 #include <qvaluevector.h>
00046 
00047 #include <kdebug.h>
00048 #include <kglobal.h>
00049 #include <kstandarddirs.h>
00050 #include <ksavefile.h>
00051 
00052 #include "pilotRecord.h"
00053 #include "pilotLocalDatabase.h"
00054 
00055 typedef QValueVector<PilotRecord *> Records;
00056 
00057 class PilotLocalDatabase::Private : public Records
00058 {
00059 public:
00060     static const int DEFAULT_SIZE = 128;
00061     Private(int size=DEFAULT_SIZE) : Records(size) { resetIndex(); }
00062     ~Private() { deleteRecords(); }
00063 
00064     void deleteRecords()
00065     {
00066         for (unsigned int i=0; i<size(); i++)
00067         {
00068             delete at(i);
00069         }
00070         clear();
00071         resetIndex();
00072     }
00073 
00074     void resetIndex() { current = 0; pending = -1; }
00075 
00076     unsigned int current;
00077     int pending;
00078 } ;
00079 
00080 PilotLocalDatabase::PilotLocalDatabase(const QString & path,
00081     const QString & dbName, bool useDefaultPath) :
00082     PilotDatabase(dbName),
00083     fPathName(path),
00084     fDBName(dbName),
00085     fAppInfo(0L),
00086     fAppLen(0),
00087     d(0L)
00088 {
00089     FUNCTIONSETUP;
00090     fixupDBName();
00091     openDatabase();
00092 
00093     if (!isOpen() && useDefaultPath)
00094     {
00095         if (fPathBase && !fPathBase->isEmpty())
00096         {
00097             fPathName = *fPathBase;
00098         }
00099         else
00100         {
00101             fPathName = KGlobal::dirs()->saveLocation("data",
00102                 CSL1("kpilot/DBBackup/"));
00103         }
00104         fixupDBName();
00105         openDatabase();
00106         if (!isOpen())
00107         {
00108             fPathName=path;
00109         }
00110     }
00111 
00112 }
00113 
00114 PilotLocalDatabase::PilotLocalDatabase(const QString &dbName) :
00115     PilotDatabase( QString() ),
00116     fPathName( QString() ),
00117     fDBName( QString() ),
00118     fAppInfo(0L),
00119     fAppLen(0),
00120     d(0L)
00121 {
00122     FUNCTIONSETUP;
00123 
00124     int p = dbName.findRev( '/' );
00125     if (p<0)
00126     {
00127         // No slash
00128         fPathName = CSL1(".");
00129         fDBName = dbName;
00130     }
00131     else
00132     {
00133         fPathName = dbName.left(p);
00134         fDBName = dbName.mid(p+1);
00135     }
00136     openDatabase();
00137 }
00138 
00139 PilotLocalDatabase::~PilotLocalDatabase()
00140 {
00141     FUNCTIONSETUP;
00142 
00143     closeDatabase();
00144     delete[]fAppInfo;
00145     delete d;
00146 }
00147 
00148 // Changes any forward slashes to underscores
00149 void PilotLocalDatabase::fixupDBName()
00150 {
00151     FUNCTIONSETUP;
00152     fDBName = fDBName.replace(CSL1("/"),CSL1("_"));
00153 }
00154 
00155 bool PilotLocalDatabase::createDatabase(long creator, long type, int, int flags, int version)
00156 {
00157     FUNCTIONSETUP;
00158 
00159     // if the database is already open, we cannot create it again. How about completely resetting it? (i.e. deleting it and the createing it again)
00160     if (isOpen()) {
00161 #ifdef DEBUG
00162         DEBUGLIBRARY<<"Database "<<fDBName<<" already open. Cannot recreate it."<<endl;
00163 #endif
00164         return true;
00165     }
00166 
00167 #ifdef DEBUG
00168     DEBUGLIBRARY<<"Creating database "<<fDBName<<endl;
00169 #endif
00170 
00171     // Database names seem to be latin1.
00172     memcpy(&fDBInfo.name[0], Pilot::toPilot(fDBName), 34*sizeof(char));
00173     fDBInfo.creator=creator;
00174     fDBInfo.type=type;
00175     fDBInfo.more=0;
00176     fDBInfo.flags=flags;
00177     fDBInfo.miscFlags=0;
00178     fDBInfo.version=version;
00179     fDBInfo.modnum=0;
00180     fDBInfo.index=0;
00181     fDBInfo.createDate=(QDateTime::currentDateTime()).toTime_t();
00182     fDBInfo.modifyDate=(QDateTime::currentDateTime()).toTime_t();
00183     fDBInfo.backupDate=(QDateTime::currentDateTime()).toTime_t();
00184 
00185     delete[] fAppInfo;
00186     fAppInfo=0L;
00187     fAppLen=0;
00188 
00189     d = new Private;
00190 
00191     // TODO: Do I have to open it explicitly???
00192     setDBOpen(true);
00193     return true;
00194 }
00195 
00196 int PilotLocalDatabase::deleteDatabase()
00197 {
00198     FUNCTIONSETUP;
00199     if (isOpen()) closeDatabase();
00200 
00201     QString dbpath=dbPathName();
00202     QFile fl(dbpath);
00203     if (QFile::remove(dbPathName()))
00204         return 0;
00205     else
00206         return -1;
00207 }
00208 
00209 
00210 
00211 // Reads the application block info
00212 int PilotLocalDatabase::readAppBlock(unsigned char *buffer, int size)
00213 {
00214     FUNCTIONSETUP;
00215 
00216     size_t m = kMin((size_t)size,(size_t)fAppLen);
00217 
00218     if (!isOpen())
00219     {
00220         kdError() << k_funcinfo << ": DB not open!" << endl;
00221         memset(buffer,0,m);
00222         return -1;
00223     }
00224 
00225     memcpy((void *) buffer, fAppInfo, m);
00226     return fAppLen;
00227 }
00228 
00229 int PilotLocalDatabase::writeAppBlock(unsigned char *buffer, int len)
00230 {
00231     FUNCTIONSETUP;
00232 
00233     if (!isOpen())
00234     {
00235         kdError() << k_funcinfo << ": DB not open!" << endl;
00236         return -1;
00237     }
00238     delete[]fAppInfo;
00239     fAppLen = len;
00240     fAppInfo = new char[fAppLen];
00241 
00242     memcpy(fAppInfo, (void *) buffer, fAppLen);
00243     return 0;
00244 }
00245 
00246 
00247     // returns the number of records in the database
00248 int PilotLocalDatabase::recordCount()
00249 {
00250     return d->size();
00251 }
00252 
00253 
00254 // Returns a QValueList of all record ids in the database.
00255 QValueList<recordid_t> PilotLocalDatabase::idList()
00256 {
00257     int idlen=recordCount();
00258     QValueList<recordid_t> idlist;
00259     if (idlen<=0) return idlist;
00260 
00261     // now create the QValue list from the idarr:
00262     for (int i=0; i<idlen; i++)
00263     {
00264         idlist.append((*d)[i]->id());
00265     }
00266 
00267     return idlist;
00268 }
00269 
00270 // Reads a record from database by id, returns record length
00271 PilotRecord *PilotLocalDatabase::readRecordById(recordid_t id)
00272 {
00273     FUNCTIONSETUP;
00274 
00275     d->pending = -1;
00276     if (!isOpen())
00277     {
00278         kdWarning() << k_funcinfo << fDBName << ": DB not open!" << endl;
00279         return 0L;
00280     }
00281 
00282 
00283     for (unsigned int i = 0; i < d->size(); i++)
00284     {
00285         if ((*d)[i]->id() == id)
00286         {
00287             PilotRecord *newRecord = new PilotRecord((*d)[i]);
00288             d->current = i;
00289             return newRecord;
00290         }
00291     }
00292     return 0L;
00293 }
00294 
00295 // Reads a record from database, returns the record
00296 PilotRecord *PilotLocalDatabase::readRecordByIndex(int index)
00297 {
00298     FUNCTIONSETUP;
00299     d->pending = -1;
00300     if (!isOpen())
00301     {
00302         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00303         return 0L;
00304     }
00305 #ifdef DEBUG
00306     DEBUGLIBRARY << fname << ": Index=" << index << " Count=" << recordCount() << endl;
00307 #endif
00308     if (index >= recordCount())
00309         return 0L;
00310     PilotRecord *newRecord = new PilotRecord((*d)[index]);
00311     d->current = index;
00312 
00313     return newRecord;
00314 }
00315 
00316 // Reads the next record from database in category 'category'
00317 PilotRecord *PilotLocalDatabase::readNextRecInCategory(int category)
00318 {
00319     FUNCTIONSETUP;
00320     d->pending  = -1;
00321     if (!isOpen())
00322     {
00323         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00324         return 0L;
00325     }
00326 
00327     while ((d->current < d->size())
00328         && ((*d)[d->current]->category() != category))
00329     {
00330         d->current++;
00331     }
00332 
00333     if (d->current >= d->size())
00334         return 0L;
00335     PilotRecord *newRecord = new PilotRecord((*d)[d->current]);
00336 
00337     d->current++;   // so we skip it next time
00338     return newRecord;
00339 }
00340 
00341 const PilotRecord *PilotLocalDatabase::findNextNewRecord()
00342 {
00343     FUNCTIONSETUP;
00344 
00345     if (!isOpen())
00346     {
00347         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00348         return 0L;
00349     }
00350 #ifdef DEBUG
00351     DEBUGLIBRARY << fname << ": looking for new record from " << d->current << endl;
00352 #endif
00353     // Should this also check for deleted?
00354     while ((d->current < d->size())
00355         && ((*d)[d->current]->id() != 0 ))
00356     {
00357         d->current++;
00358     }
00359 
00360     if (d->current >= d->size())
00361         return 0L;
00362 
00363     d->pending = d->current;    // Record which one needs the new id
00364     d->current++;   // so we skip it next time
00365     return (*d)[d->pending];
00366 }
00367 
00368 PilotRecord *PilotLocalDatabase::readNextModifiedRec(int *ind)
00369 {
00370     FUNCTIONSETUP;
00371 
00372     if (!isOpen())
00373     {
00374         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00375         return 0L;
00376     }
00377 
00378     d->pending = -1;
00379     // Should this also check for deleted?
00380     while ((d->current < d->size())
00381         && !((*d)[d->current]->isModified())  && ((*d)[d->current]->id()>0 ))
00382     {
00383         d->current++;
00384     }
00385 
00386     if (d->current >= d->size())
00387         return 0L;
00388     PilotRecord *newRecord = new PilotRecord((*d)[d->current]);
00389     if (ind) *ind=d->current;
00390 
00391     d->pending = d->current;    // Record which one needs the new id
00392     d->current++;   // so we skip it next time
00393     return newRecord;
00394 }
00395 
00396 // Writes a new ID to the record specified the index.  Not supported on Serial connections
00397 recordid_t PilotLocalDatabase::updateID(recordid_t id)
00398 {
00399     FUNCTIONSETUP;
00400 
00401     if (!isOpen())
00402     {
00403         kdError() << k_funcinfo << ": DB not open!" << endl;
00404         return 0;
00405     }
00406     if (d->pending  < 0)
00407     {
00408         kdError() << k_funcinfo <<
00409             ": Last call was _NOT_ readNextModifiedRec()" << endl;
00410         return 0;
00411     }
00412     (*d)[d->pending]->setID(id);
00413     d->pending = -1;
00414     return id;
00415 }
00416 
00417 // Writes a new record to database (if 'id' == 0, it is assumed that this is a new record to be installed on pilot)
00418 recordid_t PilotLocalDatabase::writeRecord(PilotRecord * newRecord)
00419 {
00420     FUNCTIONSETUP;
00421 
00422     if (!isOpen())
00423     {
00424         kdError() << k_funcinfo << ": DB not open!" << endl;
00425         return 0;
00426     }
00427 
00428     d->pending = -1;
00429     if (!newRecord)
00430     {
00431         kdError() << k_funcinfo << ": Record to be written is invalid!" << endl;
00432         return 0;
00433     }
00434 
00435     // Instead of making the app do it, assume that whenever a record is
00436     // written to the database it is dirty.  (You can clean up the database with
00437     // resetSyncFlags().)  This will make things get copied twice during a hot-sync
00438     // but shouldn't cause any other major headaches.
00439     newRecord->setModified( true );
00440 
00441     // First check to see if we have this record:
00442     if (newRecord->id() != 0)
00443     {
00444         for (unsigned int i = 0; i < d->size(); i++)
00445             if ((*d)[i]->id() == newRecord->id())
00446             {
00447                 delete (*d)[i];
00448 
00449                 (*d)[i] = new PilotRecord(newRecord);
00450                 return 0;
00451             }
00452     }
00453     // Ok, we don't have it, so just tack it on.
00454     d->append( new PilotRecord(newRecord) );
00455     return newRecord->id();
00456 }
00457 
00458 // Deletes a record with the given recordid_t from the database, or all records, if all is set to true. The recordid_t will be ignored in this case
00459 int PilotLocalDatabase::deleteRecord(recordid_t id, bool all)
00460 {
00461     FUNCTIONSETUP;
00462     if (!isOpen())
00463     {
00464         kdError() << k_funcinfo <<": DB not open"<<endl;
00465         return -1;
00466     }
00467     d->resetIndex();
00468     if (all)
00469     {
00470         d->deleteRecords();
00471         d->clear();
00472         return 0;
00473     }
00474     else
00475     {
00476         Private::Iterator i;
00477         for ( i=d->begin() ; i!=d->end(); ++i)
00478         {
00479             if ((*i) && (*i)->id() == id) break;
00480         }
00481         if ( (i!=d->end()) && (*i) && (*i)->id() == id)
00482         {
00483             d->erase(i);
00484         }
00485         else
00486         {
00487             // Record with this id does not exist!
00488             return -1;
00489         }
00490     }
00491     return 0;
00492 }
00493 
00494 
00495 // Resets all records in the database to not dirty.
00496 int PilotLocalDatabase::resetSyncFlags()
00497 {
00498     FUNCTIONSETUP;
00499 
00500     if (!isOpen())
00501     {
00502         kdError() << k_funcinfo << ": DB not open!" << endl;
00503         return -1;
00504     }
00505     d->pending = -1;
00506     for (unsigned int i = 0; i < d->size(); i++)
00507     {
00508         (*d)[i]->setModified( false );
00509     }
00510     return 0;
00511 }
00512 
00513 // Resets next record index to beginning
00514 int PilotLocalDatabase::resetDBIndex()
00515 {
00516     FUNCTIONSETUP;
00517     if (!isOpen())
00518     {
00519         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00520         return -1;
00521     }
00522     d->resetIndex();
00523     return 0;
00524 }
00525 
00526 // Purges all Archived/Deleted records from Palm Pilot database
00527 int PilotLocalDatabase::cleanup()
00528 {
00529     FUNCTIONSETUP;
00530     if (!isOpen())
00531     {
00532         kdWarning() << k_funcinfo << ": DB not open!" << endl;
00533         return -1;
00534     }
00535     d->resetIndex();
00536 
00537     /* Not the for loop one might expect since when we erase()
00538     * a record the iterator changes too.
00539     */
00540     Private::Iterator i = d->begin();
00541     while ( i!=d->end() )
00542     {
00543         if ( (*i)->isDeleted() || (*i)->isArchived() )
00544         {
00545             delete (*i);
00546             i = d->erase(i);
00547         }
00548         else
00549         {
00550             ++i;
00551         }
00552     }
00553 
00554     // Don't have to do anything.  Will be taken care of by closeDatabase()...
00555     // Changed!
00556     return 0;
00557 }
00558 
00559 QString PilotLocalDatabase::dbPathName() const
00560 {
00561     FUNCTIONSETUP;
00562     QString tempName(fPathName);
00563     QString slash = CSL1("/");
00564 
00565     if (!tempName.endsWith(slash)) tempName += slash;
00566     tempName += getDBName();
00567     tempName += CSL1(".pdb");
00568     return tempName;
00569 }
00570 
00571 void PilotLocalDatabase::openDatabase()
00572 {
00573     FUNCTIONSETUP;
00574 
00575     pi_file *dbFile;
00576 
00577     setDBOpen(false);
00578 
00579     dbFile = pi_file_open( QFile::encodeName(dbPathName()) );
00580     if (dbFile == 0L)
00581     {
00582 #ifdef DEBUG
00583         QString path = dbPathName();
00584         DEBUGLIBRARY << fname << ": Failed to open " << path << endl;
00585 #endif
00586         return;
00587     }
00588 
00589 
00590     PI_SIZE_T size = 0;
00591     void *tmpBuffer;
00592     pi_file_get_info(dbFile, &fDBInfo);
00593     pi_file_get_app_info(dbFile, &tmpBuffer, &size);
00594     fAppLen = size;
00595     fAppInfo = new char[fAppLen];
00596     memcpy(fAppInfo, tmpBuffer, fAppLen);
00597 
00598     int count;
00599     pi_file_get_entries(dbFile, &count);
00600     if (count >= 0)
00601     {
00602         KPILOT_DELETE(d);
00603         d = new Private(count);
00604     }
00605 
00606     int attr, cat;
00607     recordid_t id;
00608     unsigned int i = 0;
00609     while (pi_file_read_record(dbFile, i,
00610             &tmpBuffer, &size, &attr, &cat, &id) == 0)
00611     {
00612         pi_buffer_t *b = pi_buffer_new(size);
00613         memcpy(b->data,tmpBuffer,size);
00614         b->used = size;
00615         (*d)[i] = new PilotRecord(b, attr, cat, id);
00616         i++;
00617     }
00618     pi_file_close(dbFile);  // We done with it once we've read it in.
00619 
00620     KSaveFile::backupFile( dbPathName() );
00621 
00622     setDBOpen(true);
00623 }
00624 
00625 void PilotLocalDatabase::closeDatabase()
00626 {
00627     FUNCTIONSETUP;
00628     pi_file *dbFile;
00629 
00630     if (!isOpen())
00631     {
00632 #ifdef DEBUG
00633         DEBUGLIBRARY << fname << ": Database " << fDBName
00634             << " is not open. Cannot close and write it"
00635             << endl;
00636 #endif
00637         return;
00638     }
00639 
00640     QString newName = dbPathName() + CSL1(".new");
00641     char buf[PATH_MAX];
00642     memset(buf,0,PATH_MAX);
00643     strlcpy(buf,QFile::encodeName(newName),PATH_MAX);
00644 
00645 #ifdef DEBUG
00646     QString path = dbPathName();
00647     DEBUGLIBRARY << fname
00648         << ": Creating temp file " << buf
00649         << " for the database file " << path << endl;
00650 #endif
00651 
00652     dbFile = pi_file_create(buf,&fDBInfo);
00653     pi_file_set_app_info(dbFile, fAppInfo, fAppLen);
00654     for (unsigned int i = 0; i < d->size(); i++)
00655     {
00656         if (((*d)[i]->id() == 0) && ((*d)[i]->isDeleted()))
00657         {
00658             // Just ignore it
00659         }
00660         else
00661         {
00662             pi_file_append_record(dbFile,
00663                 (*d)[i]->data(),
00664                 (*d)[i]->size(),
00665                 (*d)[i]->attributes(), (*d)[i]->category(),
00666                 (*d)[i]->id());
00667         }
00668     }
00669 
00670     pi_file_close(dbFile);
00671     QFile::remove(dbPathName());
00672     rename((const char *) QFile::encodeName(newName),
00673         (const char *) QFile::encodeName(dbPathName()));
00674     setDBOpen(false);
00675 }
00676 
00677 
00678 QString *PilotLocalDatabase::fPathBase = 0L;
00679 
00680 void PilotLocalDatabase::setDBPath(const QString &s)
00681 {
00682     FUNCTIONSETUP;
00683 
00684 #ifdef DEBUG
00685     DEBUGLIBRARY << fname
00686         << ": Setting default DB path to "
00687         << s
00688         << endl;
00689 #endif
00690 
00691     if (!fPathBase)
00692     {
00693         fPathBase = new QString(s);
00694     }
00695     else
00696     {
00697         *fPathBase = s;
00698     }
00699 }
00700 
00701 /* virtual */ PilotDatabase::DBType PilotLocalDatabase::dbType() const
00702 {
00703     return eLocalDB;
00704 }
00705 
00706 
00707 /* static */ bool PilotLocalDatabase::infoFromFile( const QString &path, DBInfo *d )
00708 {
00709     FUNCTIONSETUP;
00710 
00711     pi_file *f = 0L;
00712 
00713     if (!d)
00714     {
00715         return false;
00716     }
00717     if (!QFile::exists(path))
00718     {
00719         return false;
00720     }
00721 
00722     const char * fileName = QFile::encodeName( path );
00723     f = pi_file_open( fileName );
00724     if (!f)
00725     {
00726         kdWarning() << k_funcinfo
00727             << ": Can't open " << path << endl;
00728         return false;
00729     }
00730 
00731     pi_file_get_info(f,d);
00732     pi_file_close(f);
00733 
00734     return true;
00735 }
00736 
KDE Home | KDE Accessibility Home | Description of Access Keys