void OwncloudPropagator::start(const SyncFileItemVector& items) { Q_ASSERT(std::is_sorted(items.begin(), items.end())); /* Check and log the transmission checksum type */ ConfigFile cfg; const QString checksumType = cfg.transmissionChecksum().toUpper(); /* if the checksum type is empty, it is not send. No error */ if( !checksumType.isEmpty() ) { if( checksumType == checkSumAdlerUpperC || checksumType == checkSumMD5C || checksumType == checkSumSHA1C ) { qDebug() << "Client sends and expects transmission checksum type" << checksumType; } else { qWarning() << "Unknown transmission checksum type from config" << checksumType; } } /* This builds all the job needed for the propagation. * Each directories is a PropagateDirectory job, which contains the files in it. * In order to do that we loop over the items. (which are sorted by destination) * When we enter adirectory, we can create the directory job and push it on the stack. */ _rootJob.reset(new PropagateDirectory(this)); QStack<QPair<QString /* directory name */, PropagateDirectory* /* job */> > directories; directories.push(qMakePair(QString(), _rootJob.data())); QVector<PropagatorJob*> directoriesToRemove; QString removedDirectory; foreach(const SyncFileItemPtr &item, items) { if (!removedDirectory.isEmpty() && item->_file.startsWith(removedDirectory)) { // this is an item in a directory which is going to be removed. PropagateDirectory *delDirJob = dynamic_cast<PropagateDirectory*>(directoriesToRemove.last()); if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) { //already taken care of. (by the removal of the parent directory) // increase the number of subjobs that would be there. if( delDirJob ) { delDirJob->increaseAffectedCount(); } continue; } else if (item->_instruction == CSYNC_INSTRUCTION_NEW && item->_isDirectory) { // create a new directory within a deleted directory? That can happen if the directory // etag were not fetched properly on the previous sync because the sync was aborted // while uploading this directory (which is now removed). We can ignore it. if( delDirJob ) { delDirJob->increaseAffectedCount(); } continue; } else if (item->_instruction == CSYNC_INSTRUCTION_IGNORE) { continue; } qWarning() << "WARNING: Job within a removed directory? This should not happen!" << item->_file << item->_instruction; } while (!item->destination().startsWith(directories.top().first)) { directories.pop(); } if (item->_isDirectory) { PropagateDirectory *dir = new PropagateDirectory(this, item); dir->_firstJob.reset(createJob(item)); if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) { //We do the removal of directories at the end, because there might be moves from // this directories that will happen later. directoriesToRemove.append(dir); removedDirectory = item->_file + "/"; // We should not update the etag of parent directories of the removed directory // since it would be done before the actual remove (issue #1845) // NOTE: Currently this means that we don't update those etag at all in this sync, // but it should not be a problem, they will be updated in the next sync. for (int i = 0; i < directories.size(); ++i) { directories[i].second->_item->_should_update_metadata = false; } } else { PropagateDirectory* currentDirJob = directories.top().second; currentDirJob->append(dir); } directories.push(qMakePair(item->destination() + "/" , dir)); } else if (PropagateItemJob* current = createJob(item)) { directories.top().second->append(current); } } foreach(PropagatorJob* it, directoriesToRemove) { _rootJob->append(it); }
void OwncloudPropagator::start(const SyncFileItemVector& items) { Q_ASSERT(std::is_sorted(items.begin(), items.end())); /* This builds all the jobs needed for the propagation. * Each directory is a PropagateDirectory job, which contains the files in it. * In order to do that we loop over the items. (which are sorted by destination) * When we enter a directory, we can create the directory job and push it on the stack. */ _rootJob.reset(new PropagateDirectory(this)); QStack<QPair<QString /* directory name */, PropagateDirectory* /* job */> > directories; directories.push(qMakePair(QString(), _rootJob.data())); QVector<PropagatorJob*> directoriesToRemove; QString removedDirectory; foreach(const SyncFileItemPtr &item, items) { if (!removedDirectory.isEmpty() && item->_file.startsWith(removedDirectory)) { // this is an item in a directory which is going to be removed. PropagateDirectory *delDirJob = dynamic_cast<PropagateDirectory*>(directoriesToRemove.first()); if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) { // already taken care of. (by the removal of the parent directory) // increase the number of subjobs that would be there. if( delDirJob ) { delDirJob->increaseAffectedCount(); } continue; } else if (item->_isDirectory && (item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE)) { // create a new directory within a deleted directory? That can happen if the directory // etag was not fetched properly on the previous sync because the sync was aborted // while uploading this directory (which is now removed). We can ignore it. if( delDirJob ) { delDirJob->increaseAffectedCount(); } continue; } else if (item->_instruction == CSYNC_INSTRUCTION_IGNORE) { continue; } else if (item->_instruction == CSYNC_INSTRUCTION_RENAME) { // all is good, the rename will be executed before the directory deletion } else { qWarning() << "WARNING: Job within a removed directory? This should not happen!" << item->_file << item->_instruction; } } while (!item->destination().startsWith(directories.top().first)) { directories.pop(); } if (item->_isDirectory) { PropagateDirectory *dir = new PropagateDirectory(this, item); dir->_firstJob.reset(createJob(item)); if (item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE && item->_direction == SyncFileItem::Up) { // Skip all potential uploads to the new folder. // Processing them now leads to problems with permissions: // checkForPermissions() has already run and used the permissions // of the file we're about to delete to decide whether uploading // to the new dir is ok... foreach(const SyncFileItemPtr &item2, items) { if (item2->destination().startsWith(item->destination() + "/")) { item2->_instruction = CSYNC_INSTRUCTION_NONE; _anotherSyncNeeded = true; } } } if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) { // We do the removal of directories at the end, because there might be moves from // these directories that will happen later. directoriesToRemove.prepend(dir); removedDirectory = item->_file + "/"; // We should not update the etag of parent directories of the removed directory // since it would be done before the actual remove (issue #1845) // NOTE: Currently this means that we don't update those etag at all in this sync, // but it should not be a problem, they will be updated in the next sync. for (int i = 0; i < directories.size(); ++i) { if (directories[i].second->_item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA) directories[i].second->_item->_instruction = CSYNC_INSTRUCTION_NONE; } } else { PropagateDirectory* currentDirJob = directories.top().second; currentDirJob->append(dir); } directories.push(qMakePair(item->destination() + "/" , dir)); } else if (PropagateItemJob* current = createJob(item)) {