diff options
Diffstat (limited to 'lib/pilotLocalDatabase.cc')
-rw-r--r-- | lib/pilotLocalDatabase.cc | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/lib/pilotLocalDatabase.cc b/lib/pilotLocalDatabase.cc new file mode 100644 index 0000000..8e94d4e --- /dev/null +++ b/lib/pilotLocalDatabase.cc @@ -0,0 +1,762 @@ +/* KPilot +** +** Copyright (C) 1998-2001 by Dan Pilone +** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> +** +** This defines an interface to Pilot databases on the local disk. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation; either version 2.1 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to kde-pim@kde.org +*/ + + +#include "options.h" + +#include <stdio.h> +#include <unistd.h> +#include <assert.h> + +#include <iostream> + +#include <pi-file.h> + +#include <tqstring.h> +#include <tqfile.h> +#include <tqregexp.h> +#include <tqdatetime.h> +#include <tqvaluevector.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <ksavefile.h> + +#include "pilotRecord.h" +#include "pilotLocalDatabase.h" + +typedef TQValueVector<PilotRecord *> Records; + +class PilotLocalDatabase::Private : public Records +{ +public: + static const int DEFAULT_SIZE = 128; + Private(int size=DEFAULT_SIZE) : Records(size) { resetIndex(); } + ~Private() { deleteRecords(); } + + void deleteRecords() + { + for (unsigned int i=0; i<size(); i++) + { + delete at(i); + } + clear(); + resetIndex(); + } + + void resetIndex() + { + current = 0; + pending = -1; + } + + unsigned int current; + int pending; +} ; + +PilotLocalDatabase::PilotLocalDatabase(const TQString & path, + const TQString & dbName, bool useDefaultPath) : + PilotDatabase(dbName), + fPathName(path), + fDBName(dbName), + fAppInfo(0L), + fAppLen(0), + d(0L) +{ + FUNCTIONSETUP; + fixupDBName(); + openDatabase(); + + if (!isOpen() && useDefaultPath) + { + if (fPathBase && !fPathBase->isEmpty()) + { + fPathName = *fPathBase; + } + else + { + fPathName = KGlobal::dirs()->saveLocation("data", + CSL1("kpilot/DBBackup/")); + } + fixupDBName(); + openDatabase(); + if (!isOpen()) + { + fPathName=path; + } + } + +} + +PilotLocalDatabase::PilotLocalDatabase(const TQString &dbName) : + PilotDatabase( TQString() ), + fPathName( TQString() ), + fDBName( TQString() ), + fAppInfo(0L), + fAppLen(0), + d(0L) +{ + FUNCTIONSETUP; + + int p = dbName.findRev( '/' ); + if (p<0) + { + // No slash + fPathName = CSL1("."); + fDBName = dbName; + } + else + { + fPathName = dbName.left(p); + fDBName = dbName.mid(p+1); + } + openDatabase(); +} + +PilotLocalDatabase::~PilotLocalDatabase() +{ + FUNCTIONSETUP; + + closeDatabase(); + delete[]fAppInfo; + delete d; +} + +// Changes any forward slashes to underscores +void PilotLocalDatabase::fixupDBName() +{ + FUNCTIONSETUP; + fDBName = fDBName.tqreplace(CSL1("/"),CSL1("_")); +} + +bool PilotLocalDatabase::createDatabase(long creator, long type, int, int flags, int version) +{ + FUNCTIONSETUP; + + // if the database is already open, we cannot create it again. + // How about completely resetting it? (i.e. deleting it and then + // creating it again) + if (isOpen()) + { + DEBUGKPILOT << fname << ": Database " << fDBName + << " already open. Cannot recreate it." << endl; + return true; + } + + DEBUGKPILOT << fname << ": Creating database " << fDBName << endl; + + // Database names seem to be latin1. + Pilot::toPilot(fDBName, fDBInfo.name, sizeof(fDBInfo.name)); + fDBInfo.creator=creator; + fDBInfo.type=type; + fDBInfo.more=0; + fDBInfo.flags=flags; + fDBInfo.miscFlags=0; + fDBInfo.version=version; + fDBInfo.modnum=0; + fDBInfo.index=0; + fDBInfo.createDate=(TQDateTime::tqcurrentDateTime()).toTime_t(); + fDBInfo.modifyDate=(TQDateTime::tqcurrentDateTime()).toTime_t(); + fDBInfo.backupDate=(TQDateTime::tqcurrentDateTime()).toTime_t(); + + delete[] fAppInfo; + fAppInfo=0L; + fAppLen=0; + + d = new Private; + + // TODO: Do I have to open it explicitly??? + setDBOpen(true); + return true; +} + +int PilotLocalDatabase::deleteDatabase() +{ + FUNCTIONSETUP; + if (isOpen()) + { + closeDatabase(); + } + + TQString dbpath=dbPathName(); + TQFile fl(dbpath); + if (TQFile::remove(dbPathName())) + { + return 0; + } + else + { + return -1; + } +} + + + +// Reads the application block info +int PilotLocalDatabase::readAppBlock(unsigned char *buffer, int size) +{ + FUNCTIONSETUP; + + size_t m = kMin((size_t)size,(size_t)fAppLen); + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + memset(buffer,0,m); + return -1; + } + + memcpy((void *) buffer, fAppInfo, m); + return fAppLen; +} + +int PilotLocalDatabase::writeAppBlock(unsigned char *buffer, int len) +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return -1; + } + delete[]fAppInfo; + fAppLen = len; + fAppInfo = new char[fAppLen]; + + memcpy(fAppInfo, (void *) buffer, fAppLen); + return 0; +} + + +// returns the number of records in the database +unsigned int PilotLocalDatabase::recordCount() const +{ + if (d && isOpen()) + { + return d->size(); + } + else + { + return 0; + } +} + + +// Returns a TQValueList of all record ids in the database. +TQValueList<recordid_t> PilotLocalDatabase::idList() +{ + int idlen=recordCount(); + TQValueList<recordid_t> idlist; + if (idlen<=0) + { + return idlist; + } + + // now create the QValue list from the idarr: + for (int i=0; i<idlen; i++) + { + idlist.append((*d)[i]->id()); + } + + return idlist; +} + +// Reads a record from database by id, returns record length +PilotRecord *PilotLocalDatabase::readRecordById(recordid_t id) +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "Database '" << fDBName << " not open!" << endl; + return 0L; + } + + d->pending = -1; + + for (unsigned int i = 0; i < d->size(); i++) + { + if ((*d)[i]->id() == id) + { + PilotRecord *newRecord = new PilotRecord((*d)[i]); + d->current = i; + return newRecord; + } + } + return 0L; +} + +// Reads a record from database, returns the record +PilotRecord *PilotLocalDatabase::readRecordByIndex(int index) +{ + FUNCTIONSETUP; + + if (index < 0) + { + DEBUGKPILOT << fname << ": Index " << index << " is bogus." << endl; + return 0L; + } + + d->pending = -1; + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0L; + } + + DEBUGKPILOT << fname << ": Index=" << index << " Count=" << recordCount() << endl; + + if ( (unsigned int)index >= recordCount() ) + { + return 0L; + } + PilotRecord *newRecord = new PilotRecord((*d)[index]); + d->current = index; + + return newRecord; +} + +// Reads the next record from database in category 'category' +PilotRecord *PilotLocalDatabase::readNextRecInCategory(int category) +{ + FUNCTIONSETUP; + d->pending = -1; + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0L; + } + + while ((d->current < d->size()) + && ((*d)[d->current]->category() != category)) + { + d->current++; + } + + if (d->current >= d->size()) + return 0L; + PilotRecord *newRecord = new PilotRecord((*d)[d->current]); + + d->current++; // so we skip it next time + return newRecord; +} + +const PilotRecord *PilotLocalDatabase::findNextNewRecord() +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0L; + } + DEBUGKPILOT << fname << ": looking for new record from " << d->current << endl; + // Should this also check for deleted? + while ((d->current < d->size()) + && ((*d)[d->current]->id() != 0 )) + { + d->current++; + } + + if (d->current >= d->size()) + return 0L; + + d->pending = d->current; // Record which one needs the new id + d->current++; // so we skip it next time + return (*d)[d->pending]; +} + +PilotRecord *PilotLocalDatabase::readNextModifiedRec(int *ind) +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0L; + } + + d->pending = -1; + // Should this also check for deleted? + while ((d->current < d->size()) + && !((*d)[d->current]->isModified()) && ((*d)[d->current]->id()>0 )) + { + d->current++; + } + + if (d->current >= d->size()) + { + return 0L; + } + PilotRecord *newRecord = new PilotRecord((*d)[d->current]); + if (ind) + { + *ind=d->current; + } + + d->pending = d->current; // Record which one needs the new id + d->current++; // so we skip it next time + return newRecord; +} + +// Writes a new ID to the record specified the index. Not supported on Serial connections +recordid_t PilotLocalDatabase::updateID(recordid_t id) +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0; + } + if (d->pending < 0) + { + WARNINGKPILOT << "Last call was NOT readNextModifiedRec()" << endl; + return 0; + } + (*d)[d->pending]->setID(id); + d->pending = -1; + return id; +} + +// Writes a new record to database (if 'id' == 0, it is assumed that this is a new record to be installed on pilot) +recordid_t PilotLocalDatabase::writeRecord(PilotRecord * newRecord) +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return 0; + } + + d->pending = -1; + if (!newRecord) + { + WARNINGKPILOT << "Record to be written is invalid!" << endl; + return 0; + } + + // Instead of making the app do it, assume that whenever a record is + // written to the database it is dirty. (You can clean up the database with + // resetSyncFlags().) This will make things get copied twice during a hot-sync + // but shouldn't cause any other major headaches. + newRecord->setModified( true ); + + // First check to see if we have this record: + if (newRecord->id() != 0) + { + for (unsigned int i = 0; i < d->size(); i++) + if ((*d)[i]->id() == newRecord->id()) + { + delete (*d)[i]; + + (*d)[i] = new PilotRecord(newRecord); + return 0; + } + } + // Ok, we don't have it, so just tack it on. + d->append( new PilotRecord(newRecord) ); + return newRecord->id(); +} + +// 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 +int PilotLocalDatabase::deleteRecord(recordid_t id, bool all) +{ + FUNCTIONSETUP; + if (!isOpen()) + { + WARNINGKPILOT <<"DB not open"<<endl; + return -1; + } + d->resetIndex(); + if (all) + { + d->deleteRecords(); + d->clear(); + return 0; + } + else + { + Private::Iterator i; + for ( i=d->begin() ; i!=d->end(); ++i) + { + if ((*i) && (*i)->id() == id) break; + } + if ( (i!=d->end()) && (*i) && (*i)->id() == id) + { + d->erase(i); + } + else + { + // Record with this id does not exist! + return -1; + } + } + return 0; +} + + +// Resets all records in the database to not dirty. +int PilotLocalDatabase::resetSyncFlags() +{ + FUNCTIONSETUP; + + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return -1; + } + d->pending = -1; + for (unsigned int i = 0; i < d->size(); i++) + { + (*d)[i]->setModified( false ); + } + return 0; +} + +// Resets next record index to beginning +int PilotLocalDatabase::resetDBIndex() +{ + FUNCTIONSETUP; + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return -1; + } + d->resetIndex(); + return 0; +} + +// Purges all Archived/Deleted records from Palm Pilot database +int PilotLocalDatabase::cleanup() +{ + FUNCTIONSETUP; + if (!isOpen()) + { + WARNINGKPILOT << "DB not open!" << endl; + return -1; + } + d->resetIndex(); + + /* Not the for loop one might expect since when we erase() + * a record the iterator changes too. + */ + Private::Iterator i = d->begin(); + while ( i!=d->end() ) + { + if ( (*i)->isDeleted() || (*i)->isArchived() ) + { + delete (*i); + i = d->erase(i); + } + else + { + ++i; + } + } + + // Don't have to do anything. Will be taken care of by closeDatabase()... + // Changed! + return 0; +} + +TQString PilotLocalDatabase::dbPathName() const +{ + FUNCTIONSETUP; + TQString tempName(fPathName); + TQString slash = CSL1("/"); + + if (!tempName.endsWith(slash)) tempName += slash; + tempName += getDBName(); + tempName += CSL1(".pdb"); + return tempName; +} + +void PilotLocalDatabase::openDatabase() +{ + FUNCTIONSETUP; + + pi_file *dbFile; + + setDBOpen(false); + + dbFile = pi_file_open( TQFile::encodeName(dbPathName()) ); + if (dbFile == 0L) + { + TQString path = dbPathName(); + DEBUGKPILOT << fname << ": Failed to open " << path << endl; + return; + } + + + PI_SIZE_T size = 0; + void *tmpBuffer; + pi_file_get_info(dbFile, &fDBInfo); + pi_file_get_app_info(dbFile, &tmpBuffer, &size); + fAppLen = size; + fAppInfo = new char[fAppLen]; + memcpy(fAppInfo, tmpBuffer, fAppLen); + + int count; + pi_file_get_entries(dbFile, &count); + if (count >= 0) + { + KPILOT_DELETE(d); + d = new Private(count); + } + + int attr, cat; + recordid_t id; + unsigned int i = 0; + while (pi_file_read_record(dbFile, i, + &tmpBuffer, &size, &attr, &cat, &id) == 0) + { + pi_buffer_t *b = pi_buffer_new(size); + memcpy(b->data,tmpBuffer,size); + b->used = size; + (*d)[i] = new PilotRecord(b, attr, cat, id); + i++; + } + pi_file_close(dbFile); // We done with it once we've read it in. + + KSaveFile::backupFile( dbPathName() ); + + setDBOpen(true); +} + +void PilotLocalDatabase::closeDatabase() +{ + FUNCTIONSETUP; + pi_file *dbFile; + + if (!isOpen()) + { + DEBUGKPILOT << fname << ": Database " << fDBName + << " is not open. Cannot close and write it" + << endl; + return; + } + + TQString newName = dbPathName() + CSL1(".new"); + TQString path = dbPathName(); + DEBUGKPILOT << fname + << ": Creating temp file " << newName + << " for the database file " << path << endl; + + dbFile = pi_file_create(TQFile::encodeName(newName),&fDBInfo); + pi_file_set_app_info(dbFile, fAppInfo, fAppLen); + + for (unsigned int i = 0; i < d->size(); i++) + { + // How did a NULL pointer sneak in here? + if (!(*d)[i]) + { + continue; + } + + if (((*d)[i]->id() == 0) && ((*d)[i]->isDeleted())) + { + // Just ignore it + } + else + { + pi_file_append_record(dbFile, + (*d)[i]->data(), + (*d)[i]->size(), + (*d)[i]->attributes(), (*d)[i]->category(), + (*d)[i]->id()); + } + } + + pi_file_close(dbFile); + TQFile::remove(dbPathName()); + rename((const char *) TQFile::encodeName(newName), + (const char *) TQFile::encodeName(dbPathName())); + setDBOpen(false); +} + + +TQString *PilotLocalDatabase::fPathBase = 0L; + +void PilotLocalDatabase::setDBPath(const TQString &s) +{ + FUNCTIONSETUP; + + DEBUGKPILOT << fname + << ": Setting default DB path to " + << s + << endl; + + if (!fPathBase) + { + fPathBase = new TQString(s); + } + else + { + *fPathBase = s; + } +} + +/* virtual */ PilotDatabase::DBType PilotLocalDatabase::dbType() const +{ + return eLocalDB; +} + + +/* static */ bool PilotLocalDatabase::infoFromFile( const TQString &path, DBInfo *d ) +{ + FUNCTIONSETUP; + + pi_file *f = 0L; + + if (!d) + { + return false; + } + if (!TQFile::exists(path)) + { + return false; + } + + TQCString fileName = TQFile::encodeName( path ); + f = pi_file_open( fileName ); + if (!f) + { + WARNINGKPILOT << "Can't open " << path << endl; + return false; + } + + pi_file_get_info(f,d); + pi_file_close(f); + + return true; +} + |