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