diff options
Diffstat (limited to 'src/tdeioslave/digikamalbums.cpp')
-rw-r--r-- | src/tdeioslave/digikamalbums.cpp | 1969 |
1 files changed, 1969 insertions, 0 deletions
diff --git a/src/tdeioslave/digikamalbums.cpp b/src/tdeioslave/digikamalbums.cpp new file mode 100644 index 00000000..c16080af --- /dev/null +++ b/src/tdeioslave/digikamalbums.cpp @@ -0,0 +1,1969 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : a tdeio-slave to process file operations on + * digiKam albums. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * + * Lots of the file io code is copied from KDE file tdeioslave. + * Copyright for the KDE file tdeioslave follows: + * Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org> + * Copyright (C) 2000-2002 David Faure <faure@kde.org> + * Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org> + * + * 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, 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 General Public License for more details. + * + * ============================================================ */ + +#define MAX_IPC_SIZE (1024*32) + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <utime.h> +} + +// C++ includes. + +#include <cstdlib> +#include <cstdio> +#include <ctime> +#include <cerrno> + +// TQt includes. + +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqdatastream.h> +#include <tqregexp.h> +#include <tqdir.h> + +// KDE includes. + +#include <tdeglobal.h> +#include <tdelocale.h> +#include <kinstance.h> +#include <tdefilemetainfo.h> +#include <kmimetype.h> +#include <kdebug.h> +#include <tdeio/global.h> +#include <tdeio/ioslave_defaults.h> +#include <klargefile.h> +#include <tdeversion.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "dmetadata.h" +#include "sqlitedb.h" +#include "digikam_export.h" +#include "digikamalbums.h" + +tdeio_digikamalbums::tdeio_digikamalbums(const TQCString &pool_socket, + const TQCString &app_socket) + : SlaveBase("tdeio_digikamalbums", pool_socket, app_socket) +{ +} + +tdeio_digikamalbums::~tdeio_digikamalbums() +{ +} + +static TQValueList<TQRegExp> makeFilterList( const TQString &filter ) +{ + TQValueList<TQRegExp> regExps; + if ( filter.isEmpty() ) + return regExps; + + TQChar sep( ';' ); + int i = filter.find( sep, 0 ); + if ( i == -1 && filter.find( ' ', 0 ) != -1 ) + sep = TQChar( ' ' ); + + TQStringList list = TQStringList::split( sep, filter ); + TQStringList::Iterator it = list.begin(); + while ( it != list.end() ) { + regExps << TQRegExp( (*it).stripWhiteSpace(), false, true ); + ++it; + } + return regExps; +} + +static bool matchFilterList( const TQValueList<TQRegExp>& filters, + const TQString &fileName ) +{ + TQValueList<TQRegExp>::ConstIterator rit = filters.begin(); + while ( rit != filters.end() ) { + if ( (*rit).exactMatch(fileName) ) + return true; + ++rit; + } + return false; +} + +void tdeio_digikamalbums::special(const TQByteArray& data) +{ + bool folders = (metaData("folders") == "yes"); + + TQString libraryPath; + KURL kurl; + TQString url; + TQString urlWithTrailingSlash; + TQString filter; + int getDimensions; + int scan = 0; + int recurseAlbums; + int recurseTags; + + TQDataStream ds(data, IO_ReadOnly); + ds >> libraryPath; + ds >> kurl; + ds >> filter; + ds >> getDimensions; + ds >> recurseAlbums; + ds >> recurseTags; + if (!ds.atEnd()) + ds >> scan; + + libraryPath = TQDir::cleanDirPath(libraryPath); + + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(libraryPath); + } + + url = kurl.path(); + + if (scan) + { + scanAlbum(url); + finished(); + return; + } + + TQValueList<TQRegExp> regex = makeFilterList(filter); + TQByteArray ba; + + if (folders) // Special mode to stats all album items + { + TQMap<int, int> albumsStatMap; + TQStringList values, allAbumIDs; + int albumID; + + // initialize allAbumIDs with all existing albums from db to prevent + // wrong album image counters + m_sqlDB.execSql(TQString("SELECT id from Albums"), &allAbumIDs); + + for ( TQStringList::iterator it = allAbumIDs.begin(); it != allAbumIDs.end(); ++it) + { + albumID = (*it).toInt(); + albumsStatMap.insert(albumID, 0); + } + + // now we can count the images assigned to albums + m_sqlDB.execSql(TQString("SELECT dirid, Images.name FROM Images " + "WHERE Images.dirid IN (SELECT DISTINCT id FROM Albums)"), &values); + + for ( TQStringList::iterator it = values.begin(); it != values.end(); ) + { + albumID = (*it).toInt(); + ++it; + + if ( matchFilterList( regex, *it ) ) + { + TQMap<int, int>::iterator it2 = albumsStatMap.find(albumID); + if ( it2 == albumsStatMap.end() ) + albumsStatMap.insert(albumID, 1); + else + albumsStatMap.replace(albumID, it2.data() + 1); + } + + ++it; + } + + TQDataStream os(ba, IO_WriteOnly); + os << albumsStatMap; + } + else + { + TQStringList albumvalues; + if (recurseAlbums) + { + // Search for albums and sub-albums: + // For this, get the path with a trailing "/". + // Otherwise albums on the same level like "Paris", "Paris 2006", + // would be found in addition to "Paris/*". + urlWithTrailingSlash = kurl.path(1); + + m_sqlDB.execSql(TQString("SELECT DISTINCT id, url FROM Albums WHERE url='%1' OR url LIKE '%2\%';") + .arg(escapeString(url)).arg(escapeString(urlWithTrailingSlash)), &albumvalues); + } + else + { + // Search for albums + + m_sqlDB.execSql(TQString("SELECT DISTINCT id, url FROM Albums WHERE url='%1';") + .arg(escapeString(url)), &albumvalues); + } + + TQDataStream* os = new TQDataStream(ba, IO_WriteOnly); + + TQString base; + TQ_LLONG id; + TQString name; + TQString date; + TQSize dims; + + struct stat stbuf; + + TQStringList values; + TQString albumurl; + int albumid; + + // Loop over all albums: + int count = 0 ; + for (TQStringList::iterator albumit = albumvalues.begin(); albumit != albumvalues.end();) + { + albumid = (*albumit).toLongLong(); + ++albumit; + albumurl = *albumit; + ++albumit; + + base = libraryPath + albumurl + '/'; + + values.clear(); + m_sqlDB.execSql(TQString("SELECT id, name, datetime FROM Images " + "WHERE dirid = %1;") + .arg(albumid), &values); + + // Loop over all images in each album (specified by its albumid). + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + id = (*it).toLongLong(); + ++it; + name = *it; + ++it; + date = *it; + ++it; + + if (!matchFilterList(regex, name)) + continue; + + if (::stat(TQFile::encodeName(base + name), &stbuf) != 0) + continue; + + dims = TQSize(); + if (getDimensions) + { + TQFileInfo fileInfo(base + name); +#if KDCRAW_VERSION < 0x000106 + TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + TQString ext = fileInfo.extension(false).upper(); + + if (!ext.isEmpty() && rawFilesExt.upper().contains(ext)) + { + Digikam::DMetadata metaData(base + name); + dims = metaData.getImageDimensions(); + } + else + { + KFileMetaInfo metaInfo(base + name); + if (metaInfo.isValid()) + { + if (metaInfo.containsGroup("Jpeg EXIF Data")) + { + dims = metaInfo.group("Jpeg EXIF Data"). + item("Dimensions").value().toSize(); + } + else if (metaInfo.containsGroup("General")) + { + dims = metaInfo.group("General"). + item("Dimensions").value().toSize(); + } + else if (metaInfo.containsGroup("Technical")) + { + dims = metaInfo.group("Technical"). + item("Dimensions").value().toSize(); + } + } + } + } + + *os << id; + *os << albumid; + *os << name; + *os << date; + *os << static_cast<size_t>(stbuf.st_size); + *os << dims; + + count++; + + // Send images in batches of 200. + if (count > 200) + { + delete os; + os = 0; + + SlaveBase::data(ba); + ba.resize(0); + + count = 0; + os = new TQDataStream(ba, IO_WriteOnly); + } + } + count++; + } + } + + SlaveBase::data(ba); + + finished(); +} + +static int write_all(int fd, const char *buf, size_t len) +{ + while (len > 0) + { + ssize_t written = write(fd, buf, len); + if (written < 0) + { + if (errno == EINTR) + continue; + return -1; + } + buf += written; + len -= written; + } + return 0; +} + +void tdeio_digikamalbums::get( const KURL& url ) +{ +// Code duplication from file:// ioslave + kdDebug() << k_funcinfo << " : " << url << endl; + + // get the libraryPath + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + // no need to open the db. we don't need to read/write to it + + TQCString path(TQFile::encodeName(libraryPath + url.path())); + KDE_struct_stat buff; + if ( KDE_stat( path.data(), &buff ) == -1 ) + { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, url.url() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, url.url() ); + return; + } + + if ( S_ISDIR( buff.st_mode ) ) + { + error( TDEIO::ERR_IS_DIRECTORY, url.url() ); + return; + } + + if ( !S_ISREG( buff.st_mode ) ) + { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.url() ); + return; + } + + int fd = KDE_open( path.data(), O_RDONLY); + if ( fd < 0 ) + { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.url() ); + return; + } + + // Determine the mimetype of the file to be retrieved, and emit it. + // This is mandatory in all slaves (for KRun/BrowserRun to work). + KMimeType::Ptr mt = KMimeType::findByURL( libraryPath + url.path(), buff.st_mode, + true); + emit mimeType( mt->name() ); + + totalSize( buff.st_size ); + + char buffer[ MAX_IPC_SIZE ]; + TQByteArray array; + TDEIO::filesize_t processed_size = 0; + + while (1) + { + int n = ::read( fd, buffer, MAX_IPC_SIZE ); + if (n == -1) + { + if (errno == EINTR) + continue; + error( TDEIO::ERR_COULD_NOT_READ, url.url()); + close(fd); + return; + } + if (n == 0) + break; // Finished + + array.setRawData(buffer, n); + data( array ); + array.resetRawData(buffer, n); + + processed_size += n; + processedSize( processed_size ); + } + + data( TQByteArray() ); + close( fd ); + + processedSize( buff.st_size ); + finished(); +} + +void tdeio_digikamalbums::put(const KURL& url, int permissions, bool overwrite, bool /*resume*/) +{ +// Code duplication from file:// ioslave + kdDebug() << k_funcinfo << " : " << url.url() << endl; + + // get the libraryPath + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + // open the db if needed + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(m_libraryPath); + } + + // build the album list + buildAlbumList(); + + // get the parent album + AlbumInfo album = findAlbum(url.directory()); + if (album.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database") + .arg(url.directory())); + return; + } + + + TQString dest = libraryPath + url.path(); + TQCString _dest( TQFile::encodeName(dest)); + + // check if the original file exists and we are not allowed to overwrite it + KDE_struct_stat buff; + bool origExists = (KDE_lstat( _dest.data(), &buff ) != -1); + if ( origExists && !overwrite) + { + if (S_ISDIR(buff.st_mode)) + error( TDEIO::ERR_DIR_ALREADY_EXIST, url.url() ); + else + error( TDEIO::ERR_FILE_ALREADY_EXIST, url.url() ); + return; + } + + // get the permissions we are supposed to set + mode_t initialPerms; + if (permissions != -1) + initialPerms = permissions | S_IWUSR | S_IRUSR; + else + initialPerms = 0666; + + // open the destination file + int fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialPerms); + if ( fd < 0 ) + { + kdWarning() << "####################### COULD NOT OPEN " << dest << endl; + if ( errno == EACCES ) + error( TDEIO::ERR_WRITE_ACCESS_DENIED, url.url() ); + else + error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, url.url() ); + return; + } + + int result; + + // Loop until we get 0 (end of data) + do + { + TQByteArray buffer; + dataReq(); + result = readData( buffer ); + + if (result >= 0) + { + if (write_all( fd, buffer.data(), buffer.size())) + { + if ( errno == ENOSPC ) // disk full + { + error( TDEIO::ERR_DISK_FULL, url.url()); + result = -1; + } + else + { + kdWarning() << "Couldn't write. Error:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, url.url()); + result = -1; + } + } + } + } + while ( result > 0 ); + + // An error occurred deal with it. + if (result < 0) + { + kdDebug() << "Error during 'put'. Aborting." << endl; + + close(fd); + remove(_dest); + return; + } + + // close the file + if ( close(fd) ) + { + kdWarning() << "Error when closing file descriptor:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, url.url()); + return; + } + + // set final permissions + if ( permissions != -1 ) + { + if (::chmod(_dest.data(), permissions) != 0) + { + // couldn't chmod. Eat the error if the filesystem apparently doesn't support it. + if ( TDEIO::testFileSystemFlag( _dest, TDEIO::SupportsChmod ) ) + warning( i18n( "Could not change permissions for\n%1" ).arg( url.url() ) ); + } + } + + // set modification time + const TQString mtimeStr = metaData( "modified" ); + if ( !mtimeStr.isEmpty() ) { + TQDateTime dt = TQDateTime::fromString( mtimeStr, TQt::ISODate ); + if ( dt.isValid() ) { + KDE_struct_stat dest_statbuf; + if (KDE_stat( _dest.data(), &dest_statbuf ) == 0) { + struct utimbuf utbuf; + utbuf.actime = dest_statbuf.st_atime; // access time, unchanged + utbuf.modtime = dt.toTime_t(); // modification time + kdDebug() << k_funcinfo << "setting modtime to " << utbuf.modtime << endl; + utime( _dest.data(), &utbuf ); + } + } + + } + + // First check if the file is already in database + if (!findImage(album.id, url.fileName())) + { + // Now insert the file into the database + addImage(album.id, m_libraryPath + url.path()); + } + + // We have done our job => finish + finished(); +} + +void tdeio_digikamalbums::copy( const KURL &src, const KURL &dst, int mode, bool overwrite ) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << "Src: " << src.path() << ", Dst: " << dst.path() << endl; + + // get the album library path + TQString libraryPath = src.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + // check that the src and dst album library paths match + TQString dstLibraryPath = dst.user(); + if (libraryPath != dstLibraryPath) + { + error(TDEIO::ERR_UNKNOWN, + TQString("Source and Destination have different Album Library Paths. ") + + TQString("Src: ") + src.user() + + TQString(", Dest: ") + dst.user()); + return; + } + + // open the db if needed + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(m_libraryPath); + } + + // build the album list + buildAlbumList(); + + // find the src parent album + AlbumInfo srcAlbum = findAlbum(src.directory()); + if (srcAlbum.id == -1) + { + error(TDEIO::ERR_UNKNOWN, TQString("Source album %1 not found in database") + .arg(src.directory())); + return; + } + + // find the dst parent album + AlbumInfo dstAlbum = findAlbum(dst.directory()); + if (dstAlbum.id == -1) + { + error(TDEIO::ERR_UNKNOWN, TQString("Destination album %1 not found in database") + .arg(dst.directory())); + return; + } + + // if the filename is .digikam_properties, we have been asked to copy the + // metadata of the src album to the dst album + if (src.fileName() == ".digikam_properties") + { + // no duplication in AlbumDB? + // copy metadata of album to destination album + m_sqlDB.execSql( TQString("UPDATE Albums SET date='%1', caption='%2', " + "collection='%3', icon=%4 ") + .arg(srcAlbum.date.toString(TQt::ISODate), + escapeString(srcAlbum.caption), + escapeString(srcAlbum.collection), + TQString::number(srcAlbum.icon)) + + TQString( " WHERE id=%1" ) + .arg(dstAlbum.id) ); + finished(); + return; + } + + TQCString _src( TQFile::encodeName(libraryPath + src.path())); + TQCString _dst( TQFile::encodeName(libraryPath + dst.path())); + + // stat the src file + KDE_struct_stat buff_src; + if ( KDE_stat( _src.data(), &buff_src ) == -1 ) + { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, src.url() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, src.url() ); + return; + } + + // bail out if its a directory + if ( S_ISDIR( buff_src.st_mode ) ) + { + error( TDEIO::ERR_IS_DIRECTORY, src.url() ); + return; + } + + // bail out if its a socket or fifo + if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) + { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.url() ); + return; + } + + // stat the dst file + KDE_struct_stat buff_dest; + bool dest_exists = ( KDE_lstat( _dst.data(), &buff_dest ) != -1 ); + if ( dest_exists ) + { + // bail out if its a directory + if (S_ISDIR(buff_dest.st_mode)) + { + error( TDEIO::ERR_DIR_ALREADY_EXIST, dst.url() ); + return; + } + + // if !overwrite bail out + if (!overwrite) + { + error( TDEIO::ERR_FILE_ALREADY_EXIST, dst.url() ); + return; + } + + // If the destination is a symlink and overwrite is true, + // remove the symlink first to prevent the scenario where + // the symlink actually points to current source! + if (overwrite && S_ISLNK(buff_dest.st_mode)) + { + remove( _dst.data() ); + } + } + + // now open the src file + int src_fd = KDE_open( _src.data(), O_RDONLY); + if ( src_fd < 0 ) + { + error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.path() ); + return; + } + + // get the permissions we are supposed to set + mode_t initialMode; + if (mode != -1) + initialMode = mode | S_IWUSR; + else + initialMode = 0666; + + // open the destination file + int dest_fd = KDE_open(_dst.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode); + if ( dest_fd < 0 ) + { + kdDebug() << "###### COULD NOT WRITE " << dst.url() << endl; + if ( errno == EACCES ) + { + error( TDEIO::ERR_WRITE_ACCESS_DENIED, dst.url() ); + } + else + { + error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, dst.url() ); + } + close(src_fd); + return; + } + + // emit the total size for copying + totalSize( buff_src.st_size ); + + TDEIO::filesize_t processed_size = 0; + char buffer[ MAX_IPC_SIZE ]; + int n; + + while (1) + { + // read in chunks of MAX_IPC_SIZE + n = ::read( src_fd, buffer, MAX_IPC_SIZE ); + + if (n == -1) + { + if (errno == EINTR) + continue; + error( TDEIO::ERR_COULD_NOT_READ, src.path()); + close(src_fd); + close(dest_fd); + return; + } + + // Finished ? + if (n == 0) + break; + + // write to the destination file + if (write_all( dest_fd, buffer, n)) + { + close(src_fd); + close(dest_fd); + + if ( errno == ENOSPC ) // disk full + { + error( TDEIO::ERR_DISK_FULL, dst.url()); + remove( _dst.data() ); + } + else + { + kdWarning() << "Couldn't write[2]. Error:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, dst.url()); + } + return; + } + + processedSize( processed_size ); + } + + + close( src_fd ); + + if (close( dest_fd)) + { + kdWarning() << "Error when closing file descriptor[2]:" << strerror(errno) << endl; + error( TDEIO::ERR_COULD_NOT_WRITE, dst.url()); + return; + } + + // set final permissions + if ( mode != -1 ) + { + if (::chmod(_dst.data(), mode) != 0) + { + // Eat the error if the filesystem apparently doesn't support chmod. + if ( TDEIO::testFileSystemFlag( _dst, TDEIO::SupportsChmod ) ) + warning( i18n( "Could not change permissions for\n%1" ).arg( dst.url() ) ); + } + } + + // copy access and modification time + struct utimbuf ut; + ut.actime = buff_src.st_atime; + ut.modtime = buff_src.st_mtime; + if ( ::utime( _dst.data(), &ut ) != 0 ) + { + kdWarning() << TQString::fromLatin1("Couldn't preserve access and modification time for\n%1") + .arg( dst.url() ) << endl; + } + + // now copy the metadata over + copyImage(srcAlbum.id, src.fileName(), dstAlbum.id, dst.fileName()); + + processedSize( buff_src.st_size ); + finished(); +} + +void tdeio_digikamalbums::rename( const KURL& src, const KURL& dst, bool overwrite ) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << "Src: " << src << ", Dst: " << dst << endl; + + // if the filename is .digikam_properties fake that we renamed it + if (src.fileName() == ".digikam_properties") + { + finished(); + return; + } + + TQString libraryPath = src.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + TQString dstLibraryPath = dst.user(); + if (libraryPath != dstLibraryPath) + { + error(TDEIO::ERR_UNKNOWN, + i18n("Source and Destination have different Album Library Paths.\n" + "Source: %1\n" + "Destination: %2") + .arg(src.user()) + .arg(dst.user())); + return; + } + + // open album db if needed + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(m_libraryPath); + } + + TQCString csrc( TQFile::encodeName(libraryPath + src.path())); + TQCString cdst( TQFile::encodeName(libraryPath + dst.path())); + + // stat the source file/folder + KDE_struct_stat buff_src; + if ( KDE_stat( csrc.data(), &buff_src ) == -1 ) + { + if ( errno == EACCES ) + error( TDEIO::ERR_ACCESS_DENIED, src.url() ); + else + error( TDEIO::ERR_DOES_NOT_EXIST, src.url() ); + return; + } + + // stat the destination file/folder + KDE_struct_stat buff_dest; + bool dest_exists = ( KDE_stat( cdst.data(), &buff_dest ) != -1 ); + if ( dest_exists ) + { + if (S_ISDIR(buff_dest.st_mode)) + { + error( TDEIO::ERR_DIR_ALREADY_EXIST, dst.url() ); + return; + } + + if (!overwrite) + { + error( TDEIO::ERR_FILE_ALREADY_EXIST, dst.url() ); + return; + } + } + + + // build album list + buildAlbumList(); + + AlbumInfo srcAlbum, dstAlbum; + + // check if we are renaming an album or a image + bool renamingAlbum = S_ISDIR(buff_src.st_mode); + + if (renamingAlbum) + { + srcAlbum = findAlbum(src.path()); + if (srcAlbum.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database") + .arg(src.url())); + return; + } + } + else + { + srcAlbum = findAlbum(src.directory()); + if (srcAlbum.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database") + .arg(src.directory())); + return; + } + + dstAlbum = findAlbum(dst.directory()); + if (dstAlbum.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Destination album %1 not found in database") + .arg(dst.directory())); + return; + } + } + + // actually rename the file/folder + if ( ::rename(csrc.data(), cdst.data())) + { + if (( errno == EACCES ) || (errno == EPERM)) + { + TQFileInfo toCheck(libraryPath + src.path()); + if (!toCheck.isWritable()) + error( TDEIO::ERR_CANNOT_RENAME_ORIGINAL, src.path() ); + else + error( TDEIO::ERR_ACCESS_DENIED, dst.path() ); + } + else if (errno == EXDEV) + { + error( TDEIO::ERR_UNSUPPORTED_ACTION, i18n("This file/folder is on a different " + "filesystem through symlinks. " + "Moving/Renaming files between " + "them is currently unsupported ")); + } + else if (errno == EROFS) + { // The file is on a read-only filesystem + error( TDEIO::ERR_CANNOT_DELETE, src.url() ); + } + else { + error( TDEIO::ERR_CANNOT_RENAME, src.url() ); + } + return; + } + + // renaming done. now update the database + if (renamingAlbum) + { + renameAlbum(srcAlbum.url, dst.path()); + } + else + { + renameImage(srcAlbum.id, src.fileName(), + dstAlbum.id, dst.fileName()); + } + + finished(); +} + +void tdeio_digikamalbums::stat( const KURL& url ) +{ + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + TDEIO::UDSEntry entry; + if (!createUDSEntry(libraryPath + url.path(), entry)) + { + error(TDEIO::ERR_DOES_NOT_EXIST, url.path(-1)); + return; + } + + statEntry(entry); + finished(); +} + +void tdeio_digikamalbums::listDir( const KURL& url ) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << " : " << url.path() << endl; + + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + kdWarning() << "Album Library Path not supplied to tdeioslave" << endl; + return; + } + + KDE_struct_stat stbuf; + TQString path = libraryPath + url.path(); + if (KDE_stat(TQFile::encodeName(path), &stbuf) != 0) + { + error(TDEIO::ERR_DOES_NOT_EXIST, url.path(-1)); + return; + } + + TQDir dir(path); + if (!dir.isReadable()) + { + error( TDEIO::ERR_CANNOT_ENTER_DIRECTORY, url.path()); + return; + } + + const TQFileInfoList *list = dir.entryInfoList(TQDir::All|TQDir::Hidden); + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + TDEIO::UDSEntry entry; + createDigikamPropsUDSEntry(entry); + listEntry(entry, false); + while ((fi = it.current()) != 0) + { + if (fi->fileName() != "." && fi->fileName() != ".." || fi->extension(true) == "digikamtempfile.tmp") + { + createUDSEntry(fi->absFilePath(), entry); + listEntry(entry, false); + } + ++it; + } + + entry.clear(); + listEntry(entry, true); + finished(); +} + +void tdeio_digikamalbums::mkdir( const KURL& url, int permissions ) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << " : " << url.url() << endl; + + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(m_libraryPath); + } + + TQString path = libraryPath + url.path(); + TQCString _path( TQFile::encodeName(path)); + + KDE_struct_stat buff; + if ( KDE_stat( _path, &buff ) == -1 ) + { + if ( ::mkdir( _path.data(), 0777 /*umask will be applied*/ ) != 0 ) + { + if ( errno == EACCES ) + { + error( TDEIO::ERR_ACCESS_DENIED, path ); + return; + } + else if ( errno == ENOSPC ) + { + error( TDEIO::ERR_DISK_FULL, path ); + return; + } + else + { + error( TDEIO::ERR_COULD_NOT_MKDIR, path ); + return; + } + } + else + { + // code similar to AlbumDB::addAlbum + m_sqlDB.execSql( TQString("REPLACE INTO Albums (url, date) " + "VALUES('%1','%2')") + .arg(escapeString(url.path()), + TQDate::currentDate().toString(TQt::ISODate)) ); + + if ( permissions != -1 ) + { + if ( ::chmod( _path.data(), permissions ) == -1 ) + error( TDEIO::ERR_CANNOT_CHMOD, path ); + else + finished(); + } + else + finished(); + return; + } + } + + if ( S_ISDIR( buff.st_mode ) ) + { + error( TDEIO::ERR_DIR_ALREADY_EXIST, path ); + return; + } + + error( TDEIO::ERR_FILE_ALREADY_EXIST, path ); +} + +void tdeio_digikamalbums::chmod( const KURL& url, int permissions ) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << " : " << url.url() << endl; + + // get the album library path + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + TQCString path( TQFile::encodeName(libraryPath + url.path())); + if ( ::chmod( path.data(), permissions ) == -1 ) + error( TDEIO::ERR_CANNOT_CHMOD, url.url() ); + else + finished(); +} + +void tdeio_digikamalbums::del( const KURL& url, bool isfile) +{ +// Code duplication from file:// ioslave? + kdDebug() << k_funcinfo << " : " << url.url() << endl; + + // get the album library path + TQString libraryPath = url.user(); + if (libraryPath.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave"); + return; + } + + // open the db if needed + if (m_libraryPath != libraryPath) + { + m_libraryPath = libraryPath; + m_sqlDB.closeDB(); + m_sqlDB.openDB(m_libraryPath); + } + + // build the album list + buildAlbumList(); + + TQCString path( TQFile::encodeName(libraryPath + url.path())); + + if (isfile) + { + kdDebug( ) << "Deleting file "<< url.url() << endl; + + // if the filename is .digikam_properties fake that we deleted it + if (url.fileName() == ".digikam_properties") + { + finished(); + return; + } + + // find the Album to which this file belongs. + AlbumInfo album = findAlbum(url.directory()); + if (album.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database") + .arg(url.directory())); + return; + } + + // actually delete the file + if ( unlink( path.data() ) == -1 ) + { + if ((errno == EACCES) || (errno == EPERM)) + error( TDEIO::ERR_ACCESS_DENIED, url.url()); + else if (errno == EISDIR) + error( TDEIO::ERR_IS_DIRECTORY, url.url()); + else + error( TDEIO::ERR_CANNOT_DELETE, url.url() ); + return; + } + + // successful deletion. now remove file entry from the database + delImage(album.id, url.fileName()); + } + else + { + kdDebug( ) << "Deleting directory " << url.url() << endl; + + // find the corresponding album entry + AlbumInfo album = findAlbum(url.path()); + if (album.id == -1) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database") + .arg(url.path())); + return; + } + + if ( ::rmdir( path.data() ) == -1 ) + { + // TODO handle symlink delete + + if ((errno == EACCES) || (errno == EPERM)) + { + error( TDEIO::ERR_ACCESS_DENIED, url.url()); + return; + } + else + { + kdDebug() << "could not rmdir " << perror << endl; + error( TDEIO::ERR_COULD_NOT_RMDIR, url.url() ); + return; + } + } + + // successful deletion. now remove album entry from the database + delAlbum(album.id); + } + + finished(); + +} + +bool tdeio_digikamalbums::createUDSEntry(const TQString& path, TDEIO::UDSEntry& entry) +{ + entry.clear(); + + KDE_struct_stat stbuf; + if (KDE_stat(TQFile::encodeName(path), &stbuf) != 0) + return false; + + TDEIO::UDSAtom atom; + + atom.m_uds = TDEIO::UDS_FILE_TYPE; + atom.m_long = stbuf.st_mode & S_IFMT; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS; + atom.m_long = stbuf.st_mode & 07777; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_SIZE; + atom.m_long = stbuf.st_size; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; + atom.m_long = stbuf.st_mtime; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS_TIME; + atom.m_long = stbuf.st_atime; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_NAME; + atom.m_str = TQFileInfo(path).fileName(); + entry.append(atom); + + /* + // If we provide the local path, a TDEIO::CopyJob will optimize away + // the use of our custom digikamalbums:/ ioslave, which breaks + // copying the database entry: + // Disabling this as a temporary solution for bug #137282 + // This code is intended as a fix for bug #122653. +#if KDE_IS_VERSION(3,4,0) + atom.m_uds = TDEIO::UDS_LOCAL_PATH; + atom.m_str = path; + entry.append(atom); +#endif + */ + + return true; +} + +void tdeio_digikamalbums::createDigikamPropsUDSEntry(TDEIO::UDSEntry& entry) +{ + entry.clear(); + + TDEIO::UDSAtom atom; + + atom.m_uds = TDEIO::UDS_FILE_TYPE; + atom.m_long = S_IFREG; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS; + atom.m_long = 00666; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_SIZE; + atom.m_long = 0; + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; + atom.m_long = TQDateTime::currentDateTime().toTime_t(); + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_ACCESS_TIME; + atom.m_long = TQDateTime::currentDateTime().toTime_t(); + entry.append( atom ); + + atom.m_uds = TDEIO::UDS_NAME; + atom.m_str = ".digikam_properties"; + entry.append(atom); +} + +void tdeio_digikamalbums::buildAlbumList() +{ +// simplified from AlbumDB::scanAlbums() + m_albumList.clear(); + + TQStringList values; + m_sqlDB.execSql( TQString("SELECT id, url, date, caption, collection, icon " + "FROM Albums;"), &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + AlbumInfo info; + + info.id = (*it).toInt(); + ++it; + info.url = *it; + ++it; + info.date = TQDate::fromString(*it, TQt::ISODate); + ++it; + info.caption = *it; + ++it; + info.collection = *it; + ++it; + info.icon = (*it).toLongLong(); + ++it; + + m_albumList.append(info); + } +} + +AlbumInfo tdeio_digikamalbums::findAlbum(const TQString& url, bool addIfNotExists) +{ +// similar to AlbumDB::getOrCreateAlbumId + AlbumInfo album; + for (TQValueList<AlbumInfo>::const_iterator it = m_albumList.begin(); + it != m_albumList.end(); ++it) + { + if ((*it).url == url) + { + album = *it; + return album; + } + } + + album.id = -1; + + if (addIfNotExists) + { + TQFileInfo fi(m_libraryPath + url); + if (!fi.exists() || !fi.isDir()) + return album; + + m_sqlDB.execSql(TQString("INSERT INTO Albums (url, date) " + "VALUES('%1', '%2')") + .arg(escapeString(url), + fi.lastModified().date().toString(TQt::ISODate))); + + album.id = m_sqlDB.lastInsertedRow(); + album.url = url; + album.date = fi.lastModified().date(); + album.icon = 0; + + m_albumList.append(album); + } + + return album; +} + +void tdeio_digikamalbums::delAlbum(int albumID) +{ +// code duplication from AlbumDB::deleteAlbum + m_sqlDB.execSql(TQString("DELETE FROM Albums WHERE id='%1'") + .arg(albumID)); +} + +void tdeio_digikamalbums::renameAlbum(const TQString& oldURL, const TQString& newURL) +{ +// similar to AlbumDB::setAlbumURL, but why more extended? + // first update the url of the album which was renamed + + m_sqlDB.execSql( TQString("UPDATE Albums SET url='%1' WHERE url='%2'") + .arg(escapeString(newURL), + escapeString(oldURL))); + + // now find the list of all subalbums which need to be updated + TQStringList values; + m_sqlDB.execSql( TQString("SELECT url FROM Albums WHERE url LIKE '%1/%';") + .arg(oldURL), &values ); + + // and update their url + TQString newChildURL; + for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) + { + newChildURL = *it; + newChildURL.replace(oldURL, newURL); + m_sqlDB.execSql(TQString("UPDATE Albums SET url='%1' WHERE url='%2'") + .arg(escapeString(newChildURL), + escapeString(*it))); + } +} + +bool tdeio_digikamalbums::findImage(int albumID, const TQString& name) const +{ +// no similar method in AlbumDB? + TQStringList values; + + m_sqlDB.execSql( TQString("SELECT name FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(name)), + &values ); + + return !(values.isEmpty()); +} + +// from albuminfo.h +class TagInfo +{ +public: + + typedef TQValueList<TagInfo> List; + + int id; + int pid; + TQString name; + TQString icon; +}; + +void tdeio_digikamalbums::addImage(int albumID, const TQString& filePath) +{ +// Code duplication: ScanLib::storeItemInDatabase, AlbumDB::addItem, +// AlbumDB::setItemRating, AlbumDB::addItemTag, AlbumDB::addTag + + // from ScanLib::storeItemInDatabase + TQString comment; + TQDateTime datetime; + int rating = 0; + + Digikam::DMetadata metadata(filePath); + + // Trying to get comments from image : + // In first, from standard JPEG comments, or + // In second, from EXIF comments tag, or + // In third, from IPTC comments tag. + + comment = metadata.getImageComment(); + + // Trying to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + // Trying to get image rating from IPTC Urgency tag. + rating = metadata.getImageRating(); + + if (!datetime.isValid()) + { + TQFileInfo info(filePath); + datetime = info.lastModified(); + } + + // Try to get image tags from IPTC keywords tags. + TQStringList keywordsList = metadata.getImageKeywords(); + + // from AlbumDB::addItem + m_sqlDB.execSql(TQString("REPLACE INTO Images " + "(dirid, name, datetime, caption) " + "VALUES(%1, '%2', '%3', '%4')") + .arg(TQString::number(albumID), + escapeString(TQFileInfo(filePath).fileName()), + datetime.toString(TQt::ISODate), + escapeString(comment))); + + TQ_LLONG imageID = m_sqlDB.lastInsertedRow(); + + // from AlbumDB::setItemRating + if (imageID != -1 && rating != -1) + { + m_sqlDB.execSql(TQString("REPLACE INTO ImageProperties " + "(imageid, property, value) " + "VALUES(%1, '%2', '%3');") + .arg(imageID) + .arg("Rating") + .arg(rating) ); + } + + // Set existing tags in database or create new tags if not exist. + + if ( imageID != -1 && !keywordsList.isEmpty() ) + { + TQStringList keywordsList2Create; + + // Create a list of the tags currently in database + + TagInfo::List tagsList; + + TQStringList values; + m_sqlDB.execSql( "SELECT id, pid, name FROM Tags;", &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TagInfo info; + + info.id = (*it).toInt(); + ++it; + info.pid = (*it).toInt(); + ++it; + info.name = *it; + ++it; + tagsList.append(info); + } + + // For every tag in keywordsList, scan taglist to check if tag already exists. + + for (TQStringList::iterator kwd = keywordsList.begin(); + kwd != keywordsList.end(); ++kwd ) + { + // split full tag "url" into list of single tag names + TQStringList tagHierarchy = TQStringList::split('/', *kwd); + if (tagHierarchy.isEmpty()) + continue; + + // last entry in list is the actual tag name + bool foundTag = false; + TQString tagName = tagHierarchy.back(); + tagHierarchy.pop_back(); + + for (TagInfo::List::iterator tag = tagsList.begin(); + tag != tagsList.end(); ++tag ) + { + // There might be multiple tags with the same name, but in different + // hierarchies. We must check them all until we find the correct hierarchy + if ((*tag).name == tagName) + { + int parentID = (*tag).pid; + + // Check hierarchy, from bottom to top + bool foundParentTag = true; + TQStringList::iterator parentTagName = tagHierarchy.end(); + + while (foundParentTag && parentTagName != tagHierarchy.begin()) + { + --parentTagName; + + foundParentTag = false; + + for (TagInfo::List::iterator parentTag = tagsList.begin(); + parentTag != tagsList.end(); ++parentTag ) + { + // check if name is the same, and if ID is identical + // to the parent ID we got from the child tag + if ( (*parentTag).id == parentID && + (*parentTag).name == (*parentTagName) ) + { + parentID = (*parentTag).pid; + foundParentTag = true; + break; + } + } + + // If we traversed the list without a match, + // foundParentTag will be false, the while loop breaks. + } + + // If we managed to traverse the full hierarchy, + // we have our tag. + if (foundParentTag) + { + // from AlbumDB::addItemTag + m_sqlDB.execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) " + "VALUES(%1, %2);") + .arg(imageID) + .arg((*tag).id) ); + foundTag = true; + break; + } + } + } + + if (!foundTag) + keywordsList2Create.append(*kwd); + } + + // If tags do not exist in database, create them. + + if (!keywordsList2Create.isEmpty()) + { + for (TQStringList::iterator kwd = keywordsList2Create.begin(); + kwd != keywordsList2Create.end(); ++kwd ) + { + // split full tag "url" into list of single tag names + TQStringList tagHierarchy = TQStringList::split('/', *kwd); + + if (tagHierarchy.isEmpty()) + continue; + + int parentTagID = 0; + int tagID = 0; + bool parentTagExisted = true; + + // Traverse hierarchy from top to bottom + for (TQStringList::iterator tagName = tagHierarchy.begin(); + tagName != tagHierarchy.end(); ++tagName) + { + tagID = 0; + + // if the parent tag did not exist, we need not check if the child exists + if (parentTagExisted) + { + for (TagInfo::List::iterator tag = tagsList.begin(); + tag != tagsList.end(); ++tag ) + { + // find the tag with tag name according to tagHierarchy, + // and parent ID identical to the ID of the tag we found in + // the previous run. + if ((*tag).name == (*tagName) && (*tag).pid == parentTagID) + { + tagID = (*tag).id; + break; + } + } + } + + if (tagID != 0) + { + // tag already found in DB + parentTagID = tagID; + continue; + } + + // Tag does not yet exist in DB, add it + // from AlbumDB::addTag + m_sqlDB.execSql( TQString("INSERT INTO Tags (pid, name, icon) " + "VALUES( %1, '%2', 0)") + .arg(parentTagID) + .arg(escapeString(*tagName))); + tagID = m_sqlDB.lastInsertedRow(); + + if (tagID == -1) + { + // Something is wrong in database. Abort. + break; + } + + // append to our list of existing tags (for following keywords) + TagInfo info; + info.id = tagID; + info.pid = parentTagID; + info.name = (*tagName); + tagsList.append(info); + + parentTagID = tagID; + parentTagExisted = false; + } + + // from AlbumDB::addItemTag + m_sqlDB.execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) " + "VALUES(%1, %2);") + .arg(imageID) + .arg(tagID) ); + } + } + } +} + +void tdeio_digikamalbums::delImage(int albumID, const TQString& name) +{ +// code duplication from AlbumDB::deleteItem + m_sqlDB.execSql( TQString("DELETE FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(name)) ); +} + +void tdeio_digikamalbums::renameImage(int oldAlbumID, const TQString& oldName, + int newAlbumID, const TQString& newName) +{ +// code duplication from AlbumDB::deleteItem, AlbumDB::moveItem + // first delete any stale entries for the destination file + m_sqlDB.execSql( TQString("DELETE FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(newAlbumID) + .arg(escapeString(newName)) ); + + // now update the dirid and/or name of the file + m_sqlDB.execSql( TQString("UPDATE Images SET dirid=%1, name='%2' " + "WHERE dirid=%3 AND name='%4';") + .arg(TQString::number(newAlbumID), + escapeString(newName), + TQString::number(oldAlbumID), + escapeString(oldName)) ); +} + +void tdeio_digikamalbums::copyImage(int srcAlbumID, const TQString& srcName, + int dstAlbumID, const TQString& dstName) +{ +// code duplication from AlbumDB::copyItem + // check for src == dest + if (srcAlbumID == dstAlbumID && srcName == dstName) + { + error( TDEIO::ERR_FILE_ALREADY_EXIST, dstName ); + return; + } + + // find id of src image + TQStringList values; + m_sqlDB.execSql( TQString("SELECT id FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(TQString::number(srcAlbumID), escapeString(srcName)), + &values); + + if (values.isEmpty()) + { + error(TDEIO::ERR_UNKNOWN, i18n("Source image %1 not found in database") + .arg(srcName)); + return; + } + + int srcId = values[0].toInt(); + + // first delete any stale entries for the destination file + m_sqlDB.execSql( TQString("DELETE FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(TQString::number(dstAlbumID), escapeString(dstName)) ); + + // copy entry in Images table + m_sqlDB.execSql( TQString("INSERT INTO Images (dirid, name, caption, datetime) " + "SELECT %1, '%2', caption, datetime FROM Images " + "WHERE id=%3;") + .arg(TQString::number(dstAlbumID), escapeString(dstName), + TQString::number(srcId)) ); + + int dstId = m_sqlDB.lastInsertedRow(); + + // copy tags + m_sqlDB.execSql( TQString("INSERT INTO ImageTags (imageid, tagid) " + "SELECT %1, tagid FROM ImageTags " + "WHERE imageid=%2;") + .arg(TQString::number(dstId), TQString::number(srcId)) ); + + // copy properties (rating) + m_sqlDB.execSql( TQString("INSERT INTO ImageProperties (imageid, property, value) " + "SELECT %1, property, value FROM ImageProperties " + "WHERE imageid=%2;") + .arg(TQString::number(dstId), TQString::number(srcId)) ); +} + +void tdeio_digikamalbums::scanAlbum(const TQString& url) +{ + scanOneAlbum(url); + removeInvalidAlbums(); +} + +void tdeio_digikamalbums::scanOneAlbum(const TQString& url) +{ + TQDir dir(m_libraryPath + url); + if (!dir.exists() || !dir.isReadable()) + { + return; + } + + TQString subURL = url; + if (!url.endsWith("/")) + subURL += '/'; + subURL = escapeString( subURL); + + { + // scan albums + + TQStringList currAlbumList; + m_sqlDB.execSql( TQString("SELECT url FROM Albums WHERE ") + + TQString("url LIKE '") + subURL + TQString("%' ") + + TQString("AND url NOT LIKE '") + subURL + TQString("%/%' "), + &currAlbumList ); + + + const TQFileInfoList* infoList = dir.entryInfoList(TQDir::Dirs); + if (!infoList) + return; + + TQFileInfoListIterator it(*infoList); + TQFileInfo* fi; + + TQStringList newAlbumList; + while ((fi = it.current()) != 0) + { + ++it; + + if (fi->fileName() == "." || fi->fileName() == "..") + { + continue; + } + + TQString u = TQDir::cleanDirPath(url + '/' + fi->fileName()); + + if (currAlbumList.contains(u)) + { + continue; + } + + newAlbumList.append(u); + } + + for (TQStringList::iterator it = newAlbumList.begin(); + it != newAlbumList.end(); ++it) + { + kdDebug() << "New Album: " << *it << endl; + + TQFileInfo fi(m_libraryPath + *it); + m_sqlDB.execSql(TQString("INSERT INTO Albums (url, date) " + "VALUES('%1', '%2')") + .arg(escapeString(*it), + fi.lastModified().date().toString(TQt::ISODate))); + + scanAlbum(*it); + } + } + + if (url != "/") + { + // scan files + + TQStringList values; + + m_sqlDB.execSql( TQString("SELECT id FROM Albums WHERE url='%1'") + .arg(escapeString(url)), &values ); + if (values.isEmpty()) + return; + + int albumID = values.first().toInt(); + + TQStringList currItemList; + m_sqlDB.execSql( TQString("SELECT name FROM Images WHERE dirid=%1") + .arg(albumID), &currItemList ); + + const TQFileInfoList* infoList = dir.entryInfoList(TQDir::Files); + if (!infoList) + return; + + TQFileInfoListIterator it(*infoList); + TQFileInfo* fi; + + // add any new files we find to the db + while ((fi = it.current()) != 0) + { + ++it; + + // ignore temp files we created ourselves + if (fi->extension(true) == "digikamtempfile.tmp") + { + continue; + } + + if (currItemList.contains(fi->fileName())) + { + currItemList.remove(fi->fileName()); + continue; + } + + addImage(albumID, m_libraryPath + url + '/' + fi->fileName()); + } + + // currItemList now contains deleted file list. remove them from db + for (TQStringList::iterator it = currItemList.begin(); + it != currItemList.end(); ++it) + { + delImage(albumID, *it); + } + } +} + +void tdeio_digikamalbums::removeInvalidAlbums() +{ + TQStringList urlList; + + m_sqlDB.execSql(TQString("SELECT url FROM Albums;"), + &urlList); + + m_sqlDB.execSql("BEGIN TRANSACTION"); + + struct stat stbuf; + + for (TQStringList::iterator it = urlList.begin(); + it != urlList.end(); ++it) + { + if (::stat(TQFile::encodeName(m_libraryPath + *it), &stbuf) == 0) + continue; + + kdDebug() << "Deleted Album: " << *it << endl; + m_sqlDB.execSql(TQString("DELETE FROM Albums WHERE url='%1'") + .arg(escapeString(*it))); + } + + m_sqlDB.execSql("COMMIT TRANSACTION"); +} + +/* TDEIO slave registration */ + +extern "C" +{ + DIGIKAM_EXPORT int kdemain(int argc, char **argv) + { + TDELocale::setMainCatalogue("digikam"); + TDEInstance instance( "tdeio_digikamalbums" ); + TDEGlobal::locale(); + + if (argc != 4) { + kdDebug() << "Usage: tdeio_digikamalbums protocol domain-socket1 domain-socket2" + << endl; + exit(-1); + } + + tdeio_digikamalbums slave(argv[2], argv[3]); + slave.dispatchLoop(); + + return 0; + } +} |