summaryrefslogtreecommitdiffstats
path: root/lib/pilotLocalDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pilotLocalDatabase.cpp')
-rw-r--r--lib/pilotLocalDatabase.cpp762
1 files changed, 762 insertions, 0 deletions
diff --git a/lib/pilotLocalDatabase.cpp b/lib/pilotLocalDatabase.cpp
new file mode 100644
index 0000000..ea78ac0
--- /dev/null
+++ b/lib/pilotLocalDatabase.cpp
@@ -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 <tdeglobal.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 = TDEGlobal::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.replace(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::currentDateTime()).toTime_t();
+ fDBInfo.modifyDate=(TQDateTime::currentDateTime()).toTime_t();
+ fDBInfo.backupDate=(TQDateTime::currentDateTime()).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 TQValue 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;
+}
+