kpilot

pilotLocalDatabase.cc

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