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