Logo Search packages:      
Sourcecode: kdebluetooth version File versions

irmcsyncthreadbase.cpp

/***************************************************************************
                          irmcsyncthreadbase.cpp  -  description
                             -------------------
    begin                : Sat Feb 14 2003
    copyright            : (C) 2003 by Simone Gotti
    email                : simone.gotti@email.it
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <qobject.h>
#include <qapplication.h>
#include <qthread.h>

#include <kdebug.h>
#include <qregexp.h>
#include <qfile.h>
#include <qdir.h>
#include <idhelper.h>

#include "obexclient.h"
#include "irmcsyncthreadbase.h"


#include "changelog.h"
#include "device.h"

using namespace KSync;


/**
   Constructor.
*/
00041 IrMCSyncThreadBase::IrMCSyncThreadBase(const QString& appName, const QString& fileType, const QString& extension, const IrMCSyncHelper::Device::Model phoneModel, QObject* reciver )
            : ClientThread( reciver ) {
      this->fileType = fileType;
      this->extension = extension;
      this->appName = appName;
      deviceDir = "telecom/" + fileType;

  // Create the device helper
  mDeviceHelper = new IrMCSyncHelper::Device(phoneModel);

      //FIXME
      //get the sync anchor type from the devinfo.txt file
      SyncAnchorType = ChangeCounters;
}

/**
   Destructor.
*/
00059 IrMCSyncThreadBase::~IrMCSyncThreadBase() {}

void IrMCSyncThreadBase::setObexClient(Client* obexClient) {
      obex = obexClient;
}

bool IrMCSyncThreadBase::readSyncees() {
      kdDebug() << "readSyncees()" << endl;

      // Remove All the entries in the Syncees */
      while(SyncEntry *entry = mSyncee->firstEntry()) {
            mSyncee->removeEntry(entry);
      }

      // SYNC START!!!

      // get the Initial chengelog "0.log" because we have to retrieve SN a DID infos
      getInitialCalendarChangelog();
      // Now that we have databaseDir we can initialize the uidHelper.
      // FIXME Debugging puorpose: needs to be removed.
      printuids();
      uidHelper = new KonnectorUIDHelper (databaseDir);
      // FIXME Debugging puorpose: needs to be removed.
      printuids();
      uidHelper->save();
      printuids();

      bool firstSync = checkAndSetDirs();

      if(firstSync) {
            // We have to get the current changecounter from the device
            getCurrentChangeCounter();
      } else {
            // We have to get the current changelog with the local saved changecounter
            getCalendarChangelog();
            // Now that we have retrieved the changelog we can update the changecounter with the device value
            getCurrentChangeCounter();
      }
      // Now we can get the records from the device
      getRecords();
      //Now we save the ChangeCounter.
      saveChangeCounter();

      SyncEntry* entry;
      // for all entries set the parent Syncee to mSyncee
      for ( entry = mSyncee->firstEntry(); entry != 0; entry = mSyncee->nextEntry() ) {
            entry->setSyncee(mSyncee);
      }

  uidHelper->save();
  printuids();

      return true;
}

bool IrMCSyncThreadBase::writeSyncees() {
      kdDebug() << "writeSyncees()" << endl;

      writeAddedModifiedEntries();

      return true;
}

QByteArray IrMCSyncThreadBase::getFile(const QString& fileName) {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "Getting file from remote device: " << fileName << endl;
      if(obex->get(fileName)) {
            kdDebug() << "OK" << endl;
            QByteArray data = obex->getData();
            return data;
      }
      else {
            kdDebug() << "Error Retrieving file!" << endl;
            return 0;
      }
}

void IrMCSyncThreadBase::saveChangeCounter() {
      QString changeCounterStr = QString::number(changeCounter);
      QFile file(databaseDir + "/" + "cc.log");
      file.open( IO_WriteOnly );
      file.writeBlock(changeCounterStr.ascii(), changeCounterStr.length());
      file.close();

}

void IrMCSyncThreadBase::loadChangeCounter() {
      QString changeCounterStr;
      QFile file(databaseDir + "/" + "cc.log");
      file.open( IO_ReadOnly );
      file.readLine(changeCounterStr, 1000);
      file.close();
      changeCounter = changeCounterStr.toLong();
}

void IrMCSyncThreadBase::getInitialCalendarChangelog() {
      kdDebug() << "Getting Initial Changelog (0.log)" << endl;

      QByteArray data = getFile(deviceDir +"/luid/0.log");
      changelog = new Changelog(data, Changelog::ChangeCounters);

      //mSyncee->setFirstSync( false );

      serialNumber = changelog->serialNumber();
      databaseId = changelog->databaseId();

      databaseDir = QDir::homeDirPath() + "/.kitchensync/IrMCSyncKonnector/" + serialNumber + "/" + databaseId;

      kdDebug() << "Serial Number: " << serialNumber << endl;
      kdDebug() << "Database ID: " << databaseId << endl;
}

bool IrMCSyncThreadBase::checkAndSetDirs() {
      bool firstsync;
      QDir di( QDir::homeDirPath() + "/.kitchensync/IrMCSyncKonnector/" + serialNumber + "/" + databaseId );
      /*
       * if our meta path exists do not recreate it
       */
      if ( di.exists() ) {
            firstsync = false;
            kdDebug() << "Dirs Already existing!" << endl;
      } else {
            firstsync = true;
            kdDebug() << "Creating Dirs: " << ("/.kitchensync/IrMCSyncKonnector/" + serialNumber + "/" + databaseId) << endl;
            // Create the dirs
            QDir dir;
            dir.mkdir(QDir::homeDirPath() + "/.kitchensync/");
            dir.mkdir(QDir::homeDirPath() + "/.kitchensync/IrMCSyncKonnector/");
            dir.mkdir(QDir::homeDirPath() + "/.kitchensync/IrMCSyncKonnector/" + serialNumber);
            dir.mkdir(QDir::homeDirPath() + "/.kitchensync/IrMCSyncKonnector/" + serialNumber + "/" + databaseId);
      }
      return firstsync;
}

void IrMCSyncThreadBase::getCalendarChangelog() {
      kdDebug() << "getCalendarChangelog()" << endl;
      // open the local file containing the current change counter
      loadChangeCounter();

      QString fileName = deviceDir + "/luid/" + QString::number(changeCounter) + ".log";
      kdDebug() << "Getting Changelog: " << changeCounter << ".log" << endl;
      QByteArray data = getFile(fileName);
      changelog = new Changelog(data, Changelog::ChangeCounters);
}

void IrMCSyncThreadBase::getCurrentChangeCounter() {
      kdDebug() << "Getting current ChangeCounter from the device: " << endl;
      QString fileName = deviceDir + "/luid/cc.log";
      QByteArray data = getFile(fileName);

      changeCounter = QString(data).toLong();

      // Extract the changecounter from the file
      QTextStream changelogStream(data, IO_ReadOnly );
      QString cc = changelogStream.readLine();
      changeCounter = cc.toLong();

      kdDebug() << "changeCounter = " << changeCounter << endl;
}

void IrMCSyncThreadBase::recreateFullSyncee(bool slowsync) {
      // Now we can recreate the full Syncee (Calendar/AddressBook)!!!
      // We get the various entries finding matching file in the serialnumber/database dir.
      kdDebug() << "Recreating the FULL Syncee!" << endl;

      QDir dir(databaseDir);
      dir.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );

      const QFileInfoList *fileInfolist = dir.entryInfoList();
      QFileInfoListIterator fileInfoIt( *fileInfolist );
      QFileInfo *fi;

      while ( (fi = fileInfoIt.current()) != 0 ) {
            QString fileName = fi->fileName();
            kdDebug() << "Found file: " << fileName << endl;

            QRegExp rxp1 = QRegExp("^"+appName+"-");
            QRegExp rxp2 = QRegExp("~$");
            if(fileName.contains( rxp1 ) && !fileName.contains(rxp2)) {
                  kdDebug() << "Selected file: " << fileName << endl;

                  KSync::Syncee* syncee = localGetEntry(databaseDir + "/" + fileName);

                  KSync::SyncEntry* curEntry;
                  for( curEntry = syncee->firstEntry(); curEntry; curEntry = syncee->nextEntry() ) {
                        kdDebug() << "Added entry : " << curEntry->id() << endl;
                        if(slowsync) {
                              // Change the state to "Modified", N.B. The STATE is NEEDED by kitchensyc > 3.3.0
                              curEntry->setState(SyncEntry::Modified);
                        } else {
                              // Change the state to "Undefined", N.B. The STATE is NEEDED by kitchensyc > 3.3.0
                              curEntry->setState(SyncEntry::Undefined);
                        }
                        // The entries are cloned so "entry" isn't direct changed
                        mSyncee->addEntry ( curEntry->clone() );
                  }
                  delete syncee;
            }
            ++fileInfoIt;
      }
}

void IrMCSyncThreadBase::getRecords() {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "getRecords()" << endl;
      kdDebug() << "SLOW SYNC: " << changelog->meta() << endl;

      if (changelog->meta() == true) {
            // SLOW SYNC!!! or SEMISLOW SYNC
            // We have to get all the Records!!!
            kdDebug() << "Getting Whole Calendar" << endl;
            /* Set the Sync Mode to MetaLess */
            //mSyncee->setSyncMode( KSync::Syncee::MetaLess );

            QString calendarFileName = "telecom/" + fileType + "." + extension;
            getSyncees(calendarFileName);
            // We recreate the full Calendar setting ALL entries to Modified (because we don't know their state)
            recreateFullSyncee(changelog->meta());

            // Now we have ALL the LUIDS, if there's a file with a different LUID
            // we have to delete it because it was deleted on the device
      } else {
            // FAST SYNC!!!
            QString recordFileName;
            QString LUID, Uid, dstFileName;
            // FIRST: We have to read all the saved entries and set them as undefined.
            recreateFullSyncee(changelog->meta());
            // THEN we can retrieve the added/modifed ones and delete the deleted ones

            // Set the Sync Mode to MetaMode
            //mSyncee->setSyncMode( KSync::Syncee::MetaMode );

            kdDebug() << "Getting Record List" << endl;
            QValueList<Changelog::record> mRecordsList = changelog->recordsList();

            kdDebug() << "Getting curRecord Iterator" << endl;

            QValueList<Changelog::record>::Iterator curRecordIt;
            for(curRecordIt = mRecordsList.begin(); curRecordIt != mRecordsList.end(); ++curRecordIt) {
                  recordFileName = QString(deviceDir + "/luid/") + (*curRecordIt).LUID() + "." + extension ;

                  if((*curRecordIt).isModified()) {
                        kdDebug() << "Getting " << recordFileName << endl;
                        SyncEntry* curEntry = getSyncees(recordFileName);
                        SyncEntry* savedEntry = 0;
                        if(curEntry)
                              savedEntry = mSyncee->findEntry(curEntry->id());
                        if(!savedEntry) {
                              kdDebug() << "The entry isn't already in mSyncee: mark as \"Added\"" << endl;
                              if(curEntry) {
                                    curEntry->setState(SyncEntry::Added);
                                    mSyncee->addEntry ( curEntry->clone() );
                              }
                        } else {
                              kdDebug() << "The entry is already in mSyncee: mark as \"Modified\" and substitute" << endl;
                              curEntry->setState(SyncEntry::Modified);
                              mSyncee->removeEntry (savedEntry);
                              mSyncee->addEntry ( curEntry->clone() );
                        }
                  } else if((*curRecordIt).isDeleted() || (*curRecordIt).isHardDeleted()) {
                        kdDebug() << "=====================================================================================" << endl;
                        kdDebug() << "Record with LUID: " << (*curRecordIt).LUID() << " Reported by the changelog as DELETED" << endl;
                        QString Uid = uidHelper->kdeId(appName, (*curRecordIt).LUID());
        // Now we have the UID and we can remove from the idhelper.
        // We'll save the idhelper only at the end of the ReadSyncee.
        while ( uidHelper->kdeId(appName, (*curRecordIt).LUID()) != QString::null || uidHelper->kdeId(appName, Uid) != QString::null )
          uidHelper->removeId(appName, (*curRecordIt).LUID());
                        dstFileName = GenerateFullPathFileName((*curRecordIt).LUID());
                        if( QFile::exists(dstFileName) ) {
                              SyncEntry* savedEntry = mSyncee->findEntry(Uid);
                              if(!savedEntry) {
                                    kdDebug() << "This can't HAPPEN!!! while trying to get an entry to delete from mSyncee" << endl;
                              } else {
                                    savedEntry->setState(SyncEntry::Removed);
                              }
                              // Remove the entry from the disk
                              kdDebug() << "Deleting: " << dstFileName << endl;
                              QDir dir;
                              dir.remove(dstFileName);
                        } else {
                              kdDebug() << "Error! There's no file for LUID: " << (*curRecordIt).LUID() << endl;
                              //kdDebug() << "Creating an empty entry, so we can set his status to \"Deleted\"" << endl;
                              kdDebug() << "This should'n happen... Perhaps was it deleted by hand?" << endl;
                              /*SyncEntry* curEntry = new SyncEntry(mSyncee);
                              curEntry->setState(SyncEntry::Deleted);
                              mSyncee->addEntry ( curEntry );*/
                        }
                  }
            }
      }
}

SyncEntry* IrMCSyncThreadBase::getSyncees(const QString& fileName) {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "getSyncees()" << endl;

      int entriesCounter = 0;
      QString lastEntryFileName;

      QByteArray data = getFile(fileName);

      QString dataStr(data);
      kdDebug() << dataStr << endl;

      QStringList XIRMCLUIDs;
      KSync::Syncee* syncee;
      syncee = rawDataToSyncee(data, XIRMCLUIDs);

      // And now put them in the full calendar
      QStringList::Iterator XIRMCLUIDsIt = XIRMCLUIDs.begin();
      KSync::SyncEntry* curEntry;
      for( curEntry = syncee->firstEntry(); curEntry; curEntry = syncee->nextEntry() ) {
            // Get the associated LUID
            if(XIRMCLUIDsIt == XIRMCLUIDs.end()) {
                  kdDebug() << "Error: More Events then LUIDs!!!" << endl;
            }
            //Save the entry in the serialnumber/database dir.
            QString LUID, Uid, dstFileName;
            LUID = (*XIRMCLUIDsIt);

            // We check if there's a local file with the same LUID -> SEMISLOW SYNC
            // so we can get the real Uid and not the device LUID
            Uid = uidHelper->kdeId(appName, LUID);

            kdDebug() << "LUID = " << LUID << "     UID = " << Uid << endl;
            dstFileName = GenerateFullPathFileName(LUID);
            localPutEntry(curEntry, dstFileName);

            lastEntryFileName = dstFileName;

            *XIRMCLUIDsIt++;
            entriesCounter++;
      }
      // When we get a single entry and not the whole phonebook, we return it.
      if(entriesCounter == 1) {
            syncee = localGetEntry(lastEntryFileName);
            curEntry = syncee->firstEntry()->clone();
            delete syncee;
            return curEntry;
      } else
            delete syncee;
            return 0;
}


void IrMCSyncThreadBase::writeAddedModifiedEntries() {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "writeAddedModifiedEntries()" << endl;
      QValueList<QObexHeader> headers;
      KSync::SyncEntry *curEntry;

      QString LUID, Uid, changeCounterStr, timeStamp;
      QString dstFileName = QString::null;
      for( curEntry = mSyncee->firstEntry(); curEntry; curEntry = mSyncee->nextEntry() ) {
            kdDebug() << "in the cycle" << endl;
            QByteArray data;
            data = syncEntryToRawData(curEntry, Uid);
            kdDebug() << "Received Entry with Uid: " << Uid << " data:" << endl;
            kdDebug() << QString(data) << endl;
            //Get the file in the local database backup and delete it.
            if(curEntry->wasAdded() || curEntry->wasModified()) {
                  LUID = uidHelper->konnectorId(appName, Uid);
                  if(LUID != QString::null && QFile::exists(GenerateFullPathFileName(LUID))) {
                        dstFileName=GenerateFullPathFileName(LUID);
                        kdDebug() << "Entry with Uid: " << Uid << " was Modified" << endl;
                        kdDebug() << "Filename: " << dstFileName << endl;
                        QByteArray localdata;
                        QFile file(dstFileName);
                        file.open(IO_ReadOnly);
                        localdata = file.readAll();
                        file.close();
                        kdDebug() << "Local Entry with Uid: " << Uid << " data:" << endl;
                        kdDebug() << localdata << endl;
                        kdDebug() << QString(localdata) << endl;

                        // The file is modified
                        // We read the existing file and check if the entries are the same or not
                        // because has no sense to save a record modified by us.
                        KSync::Syncee* syncee = localGetEntry(dstFileName);
                        if(!syncee->firstEntry()->equals(curEntry)) {

                              kdDebug() << "Writing Modified entry." << endl;

                              headers = sendToDevice(deviceDir + "/luid/" + LUID + "." + extension, data , true);
                              kdDebug() << "sendToDevice() end" << endl;
                              getInfosFromReturnedHeader(headers, LUID, changeCounterStr, timeStamp);
                              kdDebug() << "getInfosFromReturnedHeader() end" << endl;
                              kdDebug() << "LUID = " << LUID << endl;

                              localPutEntry(curEntry, dstFileName);

                              changeCounter = changeCounterStr.toLong();
                              saveChangeCounter();
                        } else {
                              kdDebug() << "The saved record is equal to the mSyncee record." << endl;
                        }
                  } else {
                        kdDebug() << "Entry with Uid: " << Uid << " was Added" << endl;
                        //The file must be added
                        kdDebug() << "Writing Added entry." << endl;
                        //Send the file to the remote device, get the returned LUID and ChangeCounter/TimeStamp
                        headers = sendToDevice(deviceDir + "/luid/." + extension, data , false);
                        kdDebug() << "sendToDevice() end" << endl;
                        getInfosFromReturnedHeader(headers, LUID, changeCounterStr, timeStamp);
                        kdDebug() << "getInfosFromReturnedHeader() end" << endl;
                        kdDebug() << "LUID = " << LUID << endl;
                        
                        dstFileName=GenerateFullPathFileName(LUID);
                        localPutEntry(curEntry, dstFileName);

                        changeCounter = changeCounterStr.toLong();
                        saveChangeCounter();
                        printuids();
                        uidHelper->addId(appName, LUID, Uid);
                        uidHelper->save();
                        printuids();
                  }
            } else if(curEntry->wasRemoved()) {
                  // The file is modified
                  kdDebug() << "Entry with Uid: " << Uid << " was Removed" << endl;
                  LUID = uidHelper->konnectorId(appName, Uid);
                  kdDebug() << "The assigned LUID is: " << LUID << endl;
                  
                  kdDebug() << "Removing Deleted entry." << endl;

                  QByteArray data; // An empty bytearray
                  headers = sendToDevice(deviceDir + "/luid/" + LUID + "." + extension, data , true);
                  getInfosFromReturnedHeader(headers, LUID, changeCounterStr, timeStamp);
                  kdDebug() << "getInfosFromReturnedHeader() end" << endl;
                  kdDebug() << "LUID = " <<  LUID << endl;

                  dstFileName=GenerateFullPathFileName(LUID);
                  kdDebug() << "Deleting: " << dstFileName << endl;
                  QDir dir;
                  dir.remove(dstFileName);

                  changeCounter = changeCounterStr.toLong();
                  saveChangeCounter();
                  printuids();
      while ( uidHelper->kdeId(appName, Uid) != QString::null || uidHelper->kdeId(appName, LUID) != QString::null)
        uidHelper->removeId(appName, LUID);
                  uidHelper->save();
                  printuids();
                  
            } else {
                  kdDebug() << "Entry with Uid: " << Uid << " Status Undefined (Probably it's unchanged)." << endl;
            }
      }
}

// Function used to send or delete an entry to/from the IrMC device.
// If the data is empty then we'll delete it. Otherwise we'll send it.
QValueList<QObexHeader> IrMCSyncThreadBase::sendToDevice(const QString& fileName, QByteArray& data, bool modify) {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "sendToDevice()" << endl;

      QValueList<QObexHeader> headers;
      // Send the file to the device
      // Set the appParam to the modified/deleted tag, with max changecounter set.
      if(modify) {
            // FIXME: The specs doesn't says what's the max changecounter value,
            // so we set it to a relatively high number.
            QString maxcc = "999999";
            QByteArray appParam(maxcc.length()+2);
            appParam[0]=0x11;
            appParam[1] = maxcc.length();
            for(uint i = 0; i < maxcc.length(); i++) {
                  appParam[2+i]=maxcc[i].latin1();
            }
            QObexHeader header1(QObexHeader::AppParameters, appParam);
            headers.append(header1);
      }

      if(data.size() > 0) {
            QObexHeader header2(QObexHeader::Length, data.size());
            headers.append(header2);
            obex->setData(data);
            obex->put(fileName, headers);
      } else { // If the size is 0 than the record must be deleted
            obex->del(fileName, headers);
      }
      headers = obex->getHeaders();
      kdDebug() << "getHeaders() end" << endl;

      return headers;
}

// Analyse the returned header and get useful infos from it.
void IrMCSyncThreadBase::getInfosFromReturnedHeader(QValueList<QObexHeader> headers, QString& LUID, QString& changeCounterStr, QString& timeStamp) {
      kdDebug() << "=====================================================================================" << endl;
      kdDebug() << "getInfosFromReturnedHeader()" << endl;
      // Parse the returned AppHeader
      QValueList<QObexHeader>::iterator headersIt;

      for(headersIt = headers.begin(); headersIt != headers.end(); ++headersIt) {
            if ((*headersIt).stringHeaderId() == "AppParameters") {
                  kdDebug() << "Found an AppParameters Header" << endl;

                  QByteArray appHeader = (*headersIt).arrayData();

                  int headerSize = appHeader.size();
                  int headerPos = 0;
                  int valueLength;

                  QString unknow;
                  while(headerPos < headerSize) {
                        if(appHeader[headerPos] == 0x01) { // LUID
                              kdDebug() << "Found a LUID in AppParameters Header" << endl;
                              valueLength = appHeader[++headerPos];
                              LUID = QString::fromAscii((char*)&appHeader[++headerPos], valueLength);
                              kdDebug() << "LUID = " << LUID << endl;
                              headerPos += valueLength;
                        } else if(appHeader[headerPos] == 0x02) { // ChangeCounter
                              valueLength = appHeader[++headerPos];
                              changeCounterStr = QString::fromAscii((char*)&appHeader[++headerPos], valueLength);
                              kdDebug() << "changeCounterStr = " << changeCounterStr << endl;
                              headerPos += valueLength;
                        } else if(appHeader[headerPos] == 0x03) { // TimeStamp
                              valueLength = appHeader[++headerPos];
                              timeStamp = QString::fromAscii((char*)&appHeader[++headerPos], valueLength);
                              kdDebug() << "timeStamp = " << timeStamp << endl;
                              headerPos += valueLength;
                        } else { // Unknow response
                              valueLength = appHeader[++headerPos];
                              unknow = QString::fromAscii((char*)&appHeader[++headerPos], valueLength);
                              kdDebug() << "unknow = " << unknow << endl;
                              headerPos += valueLength;
                        }
                  }
            } else {
                  kdDebug() << "Warning: Received and UNKNOW Header." << endl;
            }
      }
}

// Given a LUID generate a full Path for the saved entry filename
QString IrMCSyncThreadBase::GenerateFullPathFileName(const QString& LUID) {
      return QString(databaseDir + "/" + appName + "-" + LUID);
}

bool IrMCSyncThreadBase::printuids() {
      QByteArray localdata;
      QFile file(databaseDir + "/" + "konnector-ids.conf");
      file.open(IO_ReadOnly);
      localdata = file.readAll();
      file.close();
      kdDebug() << "konnector-ids.conf file data:" << endl;
      kdDebug() << QString(localdata) << endl;

      return true;
}

#include "irmcsyncthreadbase.moc"

Generated by  Doxygen 1.6.0   Back to index