/*************************************************************************** * Copyright (C) 2004 by Michael Schulze * * mike.s@genion.de * * * * 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. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include //Added by qt3to4: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipodslave.h" #include "ipodutility.h" #include "statisticsutility.h" #include "ejectutility.h" #include "syncutility.h" #include "itunesdb/itunesdbparser.h" #include "containerutils.h" #define TRANSFERS_HINT "Drag&Drop songs or directories here." static QString mimetype_InodeDir("inode/directory"); // Helper class to unlock the ipod when leaving methods class LockedIPodPtr { IPod * ipod; void tryUnlock() { if ( ipod ) { ipod->unlock(); kdDebug() << "ipod at " << ipod->getBasePath() << " unlocked."<< endl; } } IPod * release() { IPod * result = ipod; ipod = NULL; return result; } public: LockedIPodPtr( IPod * tolock = NULL, bool write_lock = false ) : ipod( tolock ) { if ( ipod ) { kdDebug() << "locking ipod at " << ipod->getBasePath() << endl; ipod->lock( write_lock ); } } LockedIPodPtr( const LockedIPodPtr& ipodref ) : ipod( const_cast(ipodref).release() ) { } ~LockedIPodPtr() { tryUnlock(); } IPod& operator * () { return *ipod; } IPod * operator -> () { return ipod; } void reset( IPod * newIpod = NULL ) { if ( ipod != newIpod ) { tryUnlock(); ipod = newIpod; } } operator bool () { return ipod; } LockedIPodPtr& operator= (LockedIPodPtr& toAssign) { reset( toAssign.release() ); return *this; } }; using namespace KIO; using namespace itunesdb; extern "C" { KDE_EXPORT int kdemain(int argc, char ** argv); int kdemain(int argc, char **argv) { putenv(strdup("SESSION_MANAGER=")); KCmdLineArgs::init(argc, argv, "kio_ipodslave", 0, KLocalizedString(), 0, KLocalizedString()); KCmdLineOptions options; options.add("+pool", ki18n("Socket name")); options.add("+app", ki18n("Socket name")); KCmdLineArgs::addCmdLineOptions(options); KApplication app(true); kDebug(7117) << "Starting " << getpid(); kdDebug() << "*** Starting kio_ipodslave " << endl; KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); kio_ipodslaveProtocol slave(argv[2], argv[3]); args->clear(); kdDebug() << "*** Dispatch Loop " << endl; slave.dispatchLoop(); kdDebug(7101) << "*** kio_ipodslave Done" << endl; return 0; } } extern int errno; kio_ipodslaveProtocol::kio_ipodslaveProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_ipodslave", pool_socket, app_socket) { kdDebug() << "kio_ipodslaveProtocol::kio_ipodslaveProtocol()" << endl; ipods.setAutoDelete( TRUE ); // fill the utility map IPodUtility * utility= new StatisticsUtility(); utilitymap.insert( utility->getName(), utility); // utility= new PropertyUtility(ipod); // utilitymap.insert( utility->getName(), utility);*/ utility= new SyncUtility(); utilitymap.insert( utility->getName(), utility); utility = new EjectUtility(); utilitymap.insert( utility->getName(), utility ); kdDebug() << "Construction complete"; } kio_ipodslaveProtocol::~kio_ipodslaveProtocol() { kdDebug() << "kio_ipodslaveProtocol::~kio_ipodslaveProtocol()" << endl; // cleanup utility map //Dave: no idea what this does..commenting out /* UtilityMap::iterator utility_it= utilitymap.begin(); for( ; utility_it!= utilitymap.end(); ++utility_it) { delete *utility_it; delete utility_it.key(); }*/ utilitymap.clear(); ipods.clear(); } void kio_ipodslaveProtocol::get(const KUrl &url ) { kdDebug() << "ipodslave::get()" << url.path() << endl; DirectoryModel dirmodel( url); if ( dirmodel.getCategory() == DirectoryModel::Transfer ) { finished(); // ignore get in here return; } if( dirmodel.type == DirectoryModel::UNKNOWN ) { error(ERR_DOES_NOT_EXIST, url.path()); return; } if( !dirmodel.isFile ) { error( ERR_IS_DIRECTORY, dirmodel.getFilename()); return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), false ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } switch( dirmodel.type) { case DirectoryModel::TRACK: { TrackMetadata * track= findTrack( *ipod, dirmodel ); if( track == NULL) { error( ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } // redirect to file redirection( KUrl( QString("file:")+ ipod->getRealPath( track->getPath()))); } break; case DirectoryModel::UTILITY: { QByteArray databuf; QString mimetypebuf; // get the utility UtilityMap::iterator utility_it= utilitymap.find( dirmodel.getFilename()); if( utility_it == utilitymap.end()) { error( ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } (*utility_it)->handleRequest( *ipod, url, databuf, mimetypebuf ); mimeType( mimetypebuf); data( databuf); } break; default: // we shouldn't come 'round here kdDebug() << "ipodslave::get() error: icky URL " << url.path() << endl; error( ERR_INTERNAL, dirmodel.getFilename()); return; } data(QByteArray()); // empty array means we're done sending the data kdDebug() << "ipodslave::get()" << url.path() << "finished." << endl; finished(); } void kio_ipodslaveProtocol::mimetype(const KUrl &url) { kdDebug() << "ipodslave::mimetype()" << url.path() << endl; DirectoryModel dirmodel( url); if( dirmodel.type == DirectoryModel::UNKNOWN) { error(ERR_DOES_NOT_EXIST, url.path()); return; } if( !dirmodel.isFile){ mimeType( "inode/directory"); } else { switch( dirmodel.type) { case DirectoryModel::TRACK: { LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), false ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } TrackMetadata * track= findTrack( *ipod, dirmodel ); if( track == NULL) { error( ERR_DOES_NOT_EXIST, url.path()); return; } mimeType( KMimeType::findByPath( ipod->getRealPath( track->getPath()))->name()); } break; case DirectoryModel::UTILITY: { // get the utility UtilityMap::iterator utility_it= utilitymap.find( dirmodel.getFilename()); if( utility_it == utilitymap.end()) { error( ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } mimeType( (*utility_it)->getDefaultMimeType()); } break; default: kdDebug() << "ipodslave::get() icky default section: " << url.path() << endl; get(url); } } kdDebug() << "ipodslave::mimetype()" << url.path() << "finished." << endl; finished(); } /*! \fn kio_ipodslaveProtocol::listDir (const KUrl &url) */ void kio_ipodslaveProtocol::listDir(const KUrl &url) { // TODO emit ERR_CANNOT_ENTER_DIRECTORY when unable to List kdDebug() << "ipodslave::listDir()" << url.path() << endl; DirectoryModel dirmodel( url); UDSEntry direntry; if( dirmodel.type == DirectoryModel::UNKNOWN) { error(ERR_DOES_NOT_EXIST, url.path()); return; } if( dirmodel.isFile) { error( ERR_IS_FILE, unsupportedActionErrorString(mProtocol, CMD_LISTDIR) ); return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), false ); if ( dirmodel.type > DirectoryModel::ROOT && ! ipod ) { kdDebug() << "IPod named " << dirmodel.getIPodName() << " not found" << endl; error( ERR_DOES_NOT_EXIST, url.path() ); return; } switch( dirmodel.type) { case DirectoryModel::ROOT: { uint currentTime = dirmodel.isFile ? 0 : QDateTime::currentDateTime().toTime_t(); updateIPodList(); if ( ipods.count() == 1 ) { // just one ipod found -> redirect to its URL redirection(KUrl("ipod:/" + IPod::createDistinctIPodName( *ipods.at(0) ))); finished(); return; } totalSize( ipods.count() ); for ( Q3PtrList::iterator iter = ipods.begin(); iter != ipods.end(); ++iter ) { IPod * toList = *iter; direntry.clear(); fillUDSEntry ( direntry, IPod::createDistinctIPodName( *toList ), 0, S_IFDIR, toList->isChanged(), currentTime); listEntry( direntry, false ); } } break; case DirectoryModel::IPOD: { uint currentTime = dirmodel.isFile ? 0 : QDateTime::currentDateTime().toTime_t(); // list categories totalSize( DirectoryModel::NumCategories ); for ( int cat = DirectoryModel::Artists; cat < DirectoryModel::NumCategories; ++cat ) { direntry.clear(); fillUDSEntry( direntry, DirectoryModel::getCategoryName( (DirectoryModel::Category)cat ), 0, S_IFDIR, false, currentTime); if ( cat == DirectoryModel::Transfer ) { appendUDSAtom( direntry, UDSEntry::UDS_ICON_NAME, "folder_sound" ); } listEntry( direntry, false); } } break; case DirectoryModel::CATEGORY: { switch( dirmodel.getCategory()) { case DirectoryModel::Artists: { // list artists QStringList artists; if (!ipod->getArtists(artists)) { listEntry(direntry, true); return; } totalSize( artists.count() ); uint currentTime = dirmodel.isFile ? 0 : QDateTime::currentDateTime().toTime_t(); for( QStringList::iterator artistiterator= artists.begin(); artistiterator!= artists.end(); ++artistiterator) { bool album_changed = false; direntry.clear(); Artist * artist = ipod->getArtistByName(*artistiterator); if(artist == NULL) continue; // check if there's a changed album for this artist if (!artist->isEmpty()) { for(ArtistIterator albums(*artist); !album_changed && albums.current(); ++albums) album_changed |= (*albums)->unsavedChanges(); } else { album_changed = true; } fillUDSEntry( direntry, *artistiterator, 0, S_IFDIR, album_changed, currentTime ); listEntry( direntry, false); } } break; case DirectoryModel::Playlists: { // list playlists QStringList playlisttitles; if(!ipod->getPlaylistTitles(playlisttitles)) { listEntry(direntry, true); return; } totalSize( playlisttitles.count() ); uint currentTime = dirmodel.isFile ? 0 : QDateTime::currentDateTime().toTime_t(); for(QStringList::iterator iter = playlisttitles.begin(); iter != playlisttitles.end(); ++iter) { TrackList * tracklist = ipod->getPlaylistByTitle(*iter); direntry.clear(); if(tracklist != NULL) { fillUDSEntry( direntry, tracklist->getTitle(), 0, S_IFDIR, tracklist->unsavedChanges(), currentTime); } else { kdDebug() << "ipodslave::listDir() " << url.path() << ": can't find playlist " << *iter << endl; fillUDSEntry( direntry, *iter, 0, S_IFDIR, true, currentTime ); } listEntry( direntry, false); } } break; case DirectoryModel::Utilites: { // list utilities totalSize( utilitymap.count() ); UtilityMap::iterator utility_it= utilitymap.begin(); for( ; utility_it!= utilitymap.end(); ++utility_it) { direntry.clear(); fillUDSEntry( direntry, utility_it.key(), 0, S_IFREG, false, 0, &((*utility_it)->getDefaultMimeType())); listEntry( direntry, false); } } break; case DirectoryModel::Transfer: totalSize( 0 ); break; default: error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } } break; case DirectoryModel::PLAYLIST: { // list tracks TrackList * playlist= ipod->getPlaylistByTitle( dirmodel.getFilename()); if( playlist == NULL) { error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } int tracknum= 0; totalSize( playlist->getNumTracks() ); unsigned short trackdigits= (unsigned short)log10( playlist->getNumTracks())+ 1; TrackList::Iterator trackiterator = playlist->getTrackIDs(); while( trackiterator.hasNext()) { Q_UINT32 trackid= trackiterator.next(); if( trackid == LISTITEM_DELETED) { // deleted playlist element ++tracknum; continue; } TrackMetadata * track= ipod->getTrackByID( trackid); if( track == NULL) { ++tracknum; continue; // shouldn't happen - ignore this entry } direntry.clear(); QString trackname= formatTrackname( *ipod, *track, ++tracknum, trackdigits, true ); fillUDSEntry(*ipod, direntry, trackname, *track, S_IFREG, false); listEntry( direntry, false); } } break; case DirectoryModel::ARTIST: { // list albums based on given artist Artist * artist = ipod->getArtistByName( dirmodel.getFilename() ); if( artist == NULL ) { error( ERR_DOES_NOT_EXIST, dirmodel.getFilename()); } else { totalSize( artist->count() ); uint currentTime = dirmodel.isFile ? 0 : QDateTime::currentDateTime().toTime_t(); for( ArtistIterator albumiterator(*artist); albumiterator.current(); ++albumiterator) { direntry.clear(); fillUDSEntry( direntry, albumiterator.currentKey(), 0, S_IFDIR, albumiterator.current()->unsavedChanges(), currentTime); listEntry( direntry, false); } } } break; case DirectoryModel::ALBUM: { // List tracks TrackList * album= ipod->getAlbum( dirmodel.getArtist(), dirmodel.getAlbum()); if (album != NULL) { int tracknum= 0; totalSize( album->getNumTracks() ); unsigned short trackdigits= (unsigned short)log10( album->getMaxTrackNumber())+ 1; TrackList::Iterator trackiterator= album->getTrackIDs(); while (trackiterator.hasNext()) { TrackMetadata * track= ipod->getTrackByID(trackiterator.next()); if( track == NULL) continue; // this shouldn't happen - just ignore direntry.clear(); QString trackname= formatTrackname( *ipod, *track, ++tracknum, trackdigits, false ); fillUDSEntry( *ipod, direntry, trackname, *track, S_IFREG, false ); listEntry( direntry, false); } } else { error( ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } } break; default: // we shouldn't come 'round here kdDebug() << "ipodslave::listDir() Don't know how to handle directory " << url.path() << endl; error(ERR_INTERNAL, url.path()); return; } kdDebug() << "ipodslave::listDir()" << url.path() << " finished." << endl; listEntry( direntry, true); finished(); } /*! \fn kio_ipodslaveProtocol::stat(const KUrl &url) */ void kio_ipodslaveProtocol::stat(const KUrl &url) { kdDebug() << "ipodslave::stat() " << url.path() << endl; DirectoryModel dirmodel(url); UDSEntry direntry; if( dirmodel.type == DirectoryModel::UNKNOWN) { kdDebug() << "ipodslave::stat() don't know how to handle URL " << url.path() << endl; error(ERR_DOES_NOT_EXIST, url.path()); return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), false ); if ( dirmodel.type > DirectoryModel::ROOT && ! ipod ) { kdDebug() << "IPod named " << dirmodel.getIPodName() << " not found" << endl; error( ERR_DOES_NOT_EXIST, url.path() ); return; } if( !dirmodel.isFile) { kdDebug() << "ipodslave: isn't a file" << endl; // directory fillUDSEntry( direntry, dirmodel.getFilename(), 0, S_IFDIR, false, QDateTime::currentDateTime().toTime_t()); statEntry( direntry); } else { kdDebug() << "ipodslave: case ipod " << endl; // file switch( dirmodel.type) { case DirectoryModel::TRACK: { if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } TrackMetadata * track= findTrack( *ipod, dirmodel ); if( track == NULL) { error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } fillUDSEntry( *ipod, direntry, dirmodel.getFilename(), *track, S_IFREG, false ); statEntry( direntry); } break; case DirectoryModel::UTILITY: { // get the utility UtilityMap::iterator utility_it= utilitymap.find( dirmodel.getUtilityName()); if( utility_it == utilitymap.end()) { error( ERR_DOES_NOT_EXIST, dirmodel.getUtilityName()); return; } IPodUtility * util = *utility_it; fillUDSEntry( direntry, util->getName(), 0, S_IFREG, false, 0, &(util->getDefaultMimeType())); statEntry( direntry); } break; default: kdDebug() << "ipodslave::stat() don't know how to handle " << dirmodel.getFilename() << endl; error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } } kdDebug() << "ipodslave::stat()" << url.path() << " finished." << endl; finished(); } void kio_ipodslaveProtocol::mkdir( const KUrl & url, int) { DirectoryModel dirmodel( url); bool ipodunchanged; kdDebug() << "ipodslave::mkdir() " << url.path() << endl; if ( dirmodel.ignoreMkDir() ) { finished(); return; } if ( !dirmodel.isMkDirAllowed() ) { kdDebug() << "ipodslave::mkdir() don't know how to handle " << dirmodel.getFilename() << endl; error(ERR_COULD_NOT_MKDIR, dirmodel.getFilename()); return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), true ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } ipodunchanged = !ipod->isChanged(); switch (dirmodel.type) { case DirectoryModel::PLAYLIST: { // create playlist if (ipod->getPlaylistByTitle( dirmodel.getFilename())) { kdDebug() << "ipodslave::mkdir() directory already exists " << dirmodel.getFilename() << endl; error(ERR_DIR_ALREADY_EXIST, dirmodel.getFilename()); return; } ipod->createPlaylist(dirmodel.getFilename()); } break; case DirectoryModel::ARTIST: { // create artist if ( ipod->getArtistByName( dirmodel.getFilename()) ) { kdDebug() << "ipodslave::mkdir() directory already exists " << dirmodel.getFilename() << endl; error(ERR_DIR_ALREADY_EXIST, dirmodel.getFilename()); return; } if ( !checkError( ipod->createArtist( dirmodel.getFilename() ), dirmodel.getFilename() ) ) { return; } } break; case DirectoryModel::ALBUM: { // create album if ( ipod->getAlbum( dirmodel.getArtist(), dirmodel.getAlbum() ) ) { kdDebug() << "ipodslave::mkdir() directory already exists " << dirmodel.getFilename() << endl; error(ERR_DIR_ALREADY_EXIST, dirmodel.getFilename()); return; } if ( ! checkError( ipod->createAlbum( dirmodel.getArtist(), dirmodel.getFilename() ), url.path() ) ) { return; } } break; default: kdDebug() << "ipodslave::mkdir() could not mkdir " << dirmodel.getFilename() << endl; error(ERR_COULD_NOT_MKDIR, dirmodel.getFilename()); return; } if (ipodunchanged) { showSyncInfoMessage(); } kdDebug() << "ipodslave::mkdir() " << url.path() << " finished" << endl; finished(); } void kio_ipodslaveProtocol::del( const KUrl& url, bool ) { bool ipodunchanged; kdDebug() << "ipodslave::del() " << url.path() << endl; DirectoryModel dirmodel(url); if( ! dirmodel.isDeleteAllowed() ) { if( ! dirmodel.isFile ) { error( ERR_COULD_NOT_RMDIR, dirmodel.getFilename()); } else { error( ERR_CANNOT_DELETE, dirmodel.getFilename()); } return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), true ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } ipodunchanged = !ipod->isChanged(); switch( dirmodel.type) { case DirectoryModel::PLAYLIST: // delete playlist if (ipod->deletePlaylist(dirmodel.getFilename()) == IPod::Err_DoesNotExist) { error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } break; case DirectoryModel::TRACK: { int tracknum= -1; Track * pTrack = findTrack( *ipod, dirmodel, &tracknum ); if( pTrack == NULL) { kdDebug() << "ipodslave::del() : track doesn't exist " << dirmodel.getFilename() << endl; error(ERR_DOES_NOT_EXIST, dirmodel.getFilename()); return; } if( dirmodel.getCategory() == DirectoryModel::Artists) { // remove Track QString trackfilename = ipod->getRealPath(pTrack->getPath()); if (QFile::exists(trackfilename) && !QFile::remove(trackfilename)) { kdDebug() << "ipodslave::del() : track could not be deleted " << dirmodel.getFilename() << endl; error(ERR_CANNOT_DELETE, dirmodel.getFilename()); return; } ipod->deleteTrack(pTrack->getID()); } else if (dirmodel.getCategory() == DirectoryModel::Playlists) { // remove Track from playlist if (ipod->removeFromPlaylist(tracknum - 1, dirmodel.getCurrentDirectory()) == IPod::Err_DoesNotExist) { kdDebug() << "ipodslave::del() : playlist doesn't exist " << dirmodel.getCurrentDirectory() << endl; error(ERR_DOES_NOT_EXIST, dirmodel.getCurrentDirectory()); return; } } else { // error for now. There may be other locations for tracks in the future // TODO if there are more possibilities here replace the if ... else if with a switch ... case kdDebug() << "ipodslave::del() : unknown directory" << endl; error(ERR_CANNOT_DELETE, url.path()); return; } } break; case DirectoryModel::ALBUM: { if ( ! checkError( ipod->deleteAlbum(dirmodel.getArtist(), dirmodel.getAlbum()), dirmodel.getAlbum()) ) { return; } } break; case DirectoryModel::ARTIST: { if (ipod->deleteArtist(dirmodel.getArtist()) != IPod::Err_None) { kdDebug() << "ipodslave::del() : artist not empty " << url.path() << endl; error(ERR_CANNOT_DELETE, dirmodel.getArtist()); return; } } break; default: error(ERR_CANNOT_DELETE, dirmodel.getFilename()); return; } if (ipodunchanged) { showSyncInfoMessage(); } kdDebug() << "ipodslave::del() " << url.path() << "finished." << endl; finished(); } void kio_ipodslaveProtocol::rename( const KUrl & src, const KUrl & dest, KIO::JobFlags) { bool ipodunchanged; kdDebug() << "ipodslave::rename() " << src.path() << "->" << dest.path() << endl; DirectoryModel dirmodel_src(src); DirectoryModel dirmodel_dest(dest); if ( dirmodel_src.getIPodName() != dirmodel_dest.getIPodName() ) { kdDebug() << "ipodslave::rename() : not able to move songs between iPods - redirect to copy + del" << endl; error(ERR_UNSUPPORTED_ACTION, src.path()); return; } if( !dirmodel_src.isRenameAllowed() ) { kdDebug() << "ipodslave::rename() : unknown source type" << endl; error(ERR_CANNOT_RENAME, src.path()); return; } if( !dirmodel_dest.isRenameAllowed() ) { kdDebug() << "ipodslave::rename() : unknown dest type" << endl; error(ERR_UNKNOWN, dest.path()); return; } LockedIPodPtr ipod = findIPod( dirmodel_src.getIPodName(), true ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel_src.getIPodName() ); return; } ipodunchanged = !ipod->isChanged(); switch( dirmodel_src.type ) { case DirectoryModel::PLAYLIST: // rename playlist if( dirmodel_dest.type != DirectoryModel::PLAYLIST) { // is "dest" a playlist? kdDebug() << "ipodslave::rename() : destination not a playlist" << endl; error(ERR_COULD_NOT_WRITE, dirmodel_dest.getFilename()); return; } switch(ipod->renamePlaylist(dirmodel_src.getFilename(), dirmodel_dest.getFilename())) { case IPod::Err_AlreadyExists: kdDebug() << "ipodslave::rename() : playlist already exists " << dirmodel_dest.getFilename() << endl; error(ERR_DIR_ALREADY_EXIST, dirmodel_dest.getFilename()); return; case IPod::Err_DoesNotExist: kdDebug() << "ipodslave::rename() : playlist doesn't exist " << dirmodel_src.getFilename() << endl; error(ERR_DOES_NOT_EXIST, dirmodel_src.getFilename()); return; case IPod::Err_None: break; default: error(ERR_INTERNAL, "ipodslave::rename"); return; } break; case DirectoryModel::TRACK: { if (dirmodel_src.getFilename() != dirmodel_dest.getFilename()) { error(ERR_CANNOT_RENAME, dirmodel_dest.getFilename()); // renaming tracks is not possible return; } switch (dirmodel_src.getCategory()) { case DirectoryModel::Playlists: error(ERR_UNSUPPORTED_ACTION, dirmodel_src.getFilename()); // redirect to copy and del return; case DirectoryModel::Artists: if ( dirmodel_dest.type == DirectoryModel::TRACK ) { if ( dirmodel_dest.getCategory() == DirectoryModel::Artists ) { TrackMetadata * track = findTrack( *ipod, dirmodel_src ); if (track == NULL) { error(ERR_DOES_NOT_EXIST, dirmodel_src.getFilename()); return; } ipod->moveTrack(*track, dirmodel_dest.getArtist(), dirmodel_dest.getAlbum()); } else if ( dirmodel_dest.getCategory() == DirectoryModel::Playlists ) { error(ERR_SLAVE_DEFINED, "Moving tracks here (" + dest.path() + ") doesn't make sense - use copy instead"); return; } } else { error(ERR_SLAVE_DEFINED, "Moving tracks here (" + dest.path() + ") is not allowed."); return; } break; default: error(ERR_DOES_NOT_EXIST, dirmodel_src.getFilename()); return; } } break; case DirectoryModel::ALBUM: { if (dirmodel_dest.type != DirectoryModel::ALBUM) { kdDebug() << "ipodslave::rename() : destination not an album " << dirmodel_dest.getFilename() << endl; error( ERR_COULD_NOT_MKDIR, dirmodel_src.getFilename()); return; } switch(ipod->renameAlbum(dirmodel_src.getArtist(), dirmodel_src.getAlbum(), dirmodel_dest.getArtist(), dirmodel_dest.getAlbum())) { case IPod::Err_AlreadyExists: kdDebug() << "ipodslave::rename() : album already exists " << dirmodel_dest.getFilename() << endl; error(ERR_DIR_ALREADY_EXIST, dest.path()); return; case IPod::Err_DoesNotExist: kdDebug() << "ipodslave::rename() : album doesn't exist " << dirmodel_src.getFilename() << endl; error(ERR_DOES_NOT_EXIST, src.path()); return; case IPod::Err_None: break; default: error(ERR_INTERNAL, "ipodslave::rename"); return; } } break; case DirectoryModel::ARTIST: { if ( dirmodel_dest.type != DirectoryModel::ARTIST ) { error( ERR_CANNOT_RENAME, dirmodel_src.getFilename() ); return; } if ( ! checkError( ipod->renameArtist( dirmodel_src.getFilename(), dirmodel_dest.getFilename() ), src.path() ) ) { return; } } break; default: kdDebug() << "ipodslave::rename() : cannot handle " << src.path() << endl; error( ERR_UNSUPPORTED_ACTION, dirmodel_src.getFilename()); return; } if (ipodunchanged) { showSyncInfoMessage(); } kdDebug() << "ipodslave::rename() " << src.path() << "->" << dest.path() << " finished." << endl; finished(); } bool kio_ipodslaveProtocol::doCopyFile( QFile& src_file, QFile& dest_file ) { // TODO use KIO::copy when we figured out why we got a segfault when calling it if ( !src_file.exists() ) { error( ERR_DOES_NOT_EXIST, src_file.fileName() ); return false; } totalSize( src_file.size() ); if ( dest_file.exists() ) { error( ERR_SLAVE_DEFINED, dest_file.fileName() + " already exists."); return false; } if ( !src_file.open( QIODevice::ReadOnly ) ) { error( ERR_CANNOT_OPEN_FOR_READING, src_file.fileName() ); return false; } if ( !dest_file.open ( QIODevice::WriteOnly ) ) { error( ERR_CANNOT_OPEN_FOR_WRITING, dest_file.fileName() ); return false; } QByteArray buffer( 7168 ); KIO::filesize_t processedBytes = 0; int result= 0; do { int remaining = 0; dataReq(); //Dave: result = read( src_file.handle(), buffer.data(), buffer.size() ); read( buffer.size() ); if (result > 0) { remaining = result; char * ptr = buffer.data(); //dave FIX THIS while ( remaining > 0 ) { //int byteswritten = write( dest_file.handle(), ptr, remaining); int byteswritten; if (byteswritten != -1) { ptr += byteswritten; remaining -= byteswritten; processedBytes += byteswritten; } else { remaining = -1; } } processedSize( processedBytes ); } if (remaining < 0 || result < 0 || wasKilled()) { // some error happened src_file.close(); dest_file.close(); dest_file.remove(); switch (errno) { case 0: break; // doesn't seem to be critical case ENOSPC: error( ERR_DISK_FULL, dest_file.fileName() ); break; default: error( ERR_COULD_NOT_WRITE, dest_file.fileName() ); break; } return false; } } while( result > 0 ); dest_file.close(); src_file.close(); return true; } void kio_ipodslaveProtocol::doCopyFromToIPod( const DirectoryModel& src, const DirectoryModel& dest ) { LockedIPodPtr src_ipod = findIPod( src.getIPodName(), false ); if ( ! src_ipod ) { error( ERR_DOES_NOT_EXIST, src.getIPodName() ); return; } LockedIPodPtr dest_ipod = findIPod( dest.getIPodName(), true ); if ( ! dest_ipod ) { error( ERR_DOES_NOT_EXIST, dest.getIPodName() ); return; } TrackMetadata * src_track = findTrack( *src_ipod, src ); if ( !src_track ) { kdDebug() << "ipodslave::copy() : track doesn't exist " << src.getFilename() << endl; error(ERR_DOES_NOT_EXIST, src.getFilename()); return; } // is the track already there? if ( dest_ipod->findTrack( src_track->getArtist(), src_track->getAlbum(), src_track->getTitle())) { // track exists already error(ERR_SLAVE_DEFINED, "Track " + src_track->getArtist() + "/" + src_track->getAlbum() + "/" + src_track->getTitle() + " already exists."); return; } // create new TrackID / filename etc TrackMetadata dest_track = dest_ipod->createNewTrackMetadata(); // append the real file extension to the path dest_track.setPath( dest_track.getPath() + dest.getFileExtension() ); QFile src_file( src_ipod->getRealPath( src_track->getPath() ) ); QFile dest_file( dest_ipod->getRealPath( dest_track.getPath() ) ); // check available disk space if ( ( src_file.size() >> 10 ) >= dest_ipod->getSysInfo()->getAvailableDiskSpaceKB() ) { error( ERR_DISK_FULL, src.getFilename() ); return; } // copy the file from src to dest if (! doCopyFile( src_file, dest_file )) { return; } // copy metadata from src to dest dest_track.copyMetaData( *src_track ); if ( !dest_ipod->isChanged() ) { showSyncInfoMessage(); } // add to database dest_ipod->addTrack( dest_track ); finished(); } void kio_ipodslaveProtocol::copy( const KUrl & src, const KUrl & dest, int, KIO::JobFlags) { bool ipodunchanged; kdDebug() << "ipodslave::copy() " << src.path() << "->" << dest.path() << endl; DirectoryModel dirmodel_src(src); DirectoryModel dirmodel_dest(dest); if( !dirmodel_src.isCopyAllowed() ) { kdDebug() << "ipodslave::copy() : unknown source type" << endl; error( ERR_UNSUPPORTED_ACTION, src.path() ); return; } if ( dirmodel_dest.type >= DirectoryModel::IPOD && dirmodel_src.getIPodName() != dirmodel_dest.getIPodName() ) { doCopyFromToIPod( dirmodel_src, dirmodel_dest ); return; } if( !dirmodel_dest.isCopyAllowed() ) { kdDebug() << "ipodslave::copy() : unknown dest type" << endl; error(ERR_SLAVE_DEFINED, "Copying tracks here (" + dest.path() + ") is not allowed."); return; } LockedIPodPtr ipod = findIPod( dirmodel_src.getIPodName(), true ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel_src.getIPodName() ); return; } ipodunchanged = !ipod->isChanged(); switch( dirmodel_src.type) { case DirectoryModel::TRACK: { if (dirmodel_dest.getCategory() == DirectoryModel::Artists) { kdDebug() << "ipodslave::copy() : Only one instance of this track is allowed here" << endl; error(ERR_SLAVE_DEFINED, "Copying tracks to another album is not allowed - use move instead"); return; } else if (dirmodel_dest.getCategory() != DirectoryModel::Playlists || !dirmodel_dest.isFile) { kdDebug() << "ipodslave::copy() : destination not a playlist" << endl; error(ERR_CANNOT_OPEN_FOR_WRITING, dest.path()); return; } TrackMetadata * track= findTrack( *ipod, dirmodel_src ); if (track == NULL) { kdDebug() << "ipodslave::copy() : track doesn't exist " << dirmodel_src.getFilename() << endl; error(ERR_DOES_NOT_EXIST, dirmodel_src.getFilename()); return; } switch(ipod->addTrackToPlaylist(track->getID(), dirmodel_dest.getPlaylist())) { case IPod::Err_DoesNotExist: kdDebug() << "ipodslave::copy() : playlist doesn't exist " << dirmodel_dest.getPlaylist() << endl; error(ERR_DOES_NOT_EXIST, dirmodel_src.getPlaylist()); return; case IPod::Err_None: break; default: error(ERR_INTERNAL, "ipodslave::copy"); return; } } break; default: kdDebug() << "ipodslave::copy() : cannot handle " << src.path() << endl; error( ERR_ACCESS_DENIED, dirmodel_dest.getFilename()); return; } if (ipodunchanged) { showSyncInfoMessage(); } kdDebug() << "ipodslave::copy() " << src.path() << "->" << dest.path() << " finished." << endl; finished(); } void kio_ipodslaveProtocol::doPut( IPod& ipod, const DirectoryModel& dirmodel ) { TrackMetadata track = ipod.createNewTrackMetadata(); // append the real file extension to the path track.setPath( track.getPath() + dirmodel.getFileExtension() ); QString trackpath = ipod.getRealPath( track.getPath() ); // real path in the filesystem // copy track to iPod (file wise) QFile outfile( trackpath ); outfile.open( QIODevice::WriteOnly ); dataReq(); int result= 0; do { QByteArray buffer; int remaining = 0; result = readData( buffer ); if (result > 0) { dataReq(); remaining = buffer.size(); char * ptr = buffer.data(); //Dave - FIX! while (remaining > 0) { //int byteswritten = write(outfile.handle(), ptr, remaining); int byteswritten; if (byteswritten != -1) { ptr += byteswritten; remaining -= byteswritten; } else { remaining = -1; } } } if (remaining < 0 || result < 0 || wasKilled()) { // some error happened outfile.close(); QFile::remove(trackpath); switch (errno) { case 0: break; // doesn't seem to be critical case ENOSPC: error(ERR_DISK_FULL, dirmodel.getFilename()); break; default: error(ERR_COULD_NOT_WRITE, dirmodel.getFilename()); break; } return; } } while( result > 0 ); outfile.close(); kdDebug() << "ipodslave::put() writing file " << trackpath << " done." << endl; // add the track to the TrackMap // read id3 information and fill in these into the track object if( !track.readFromFile( trackpath) ) { // if insufficient id3 information is given try to guess from the URL // if even that is not possible ask the user what to do // idea: one option could be to open a track information editor utiliy // that gets the trackID // another option would be "cancel" kdDebug() << "ipodslave::put() could not read id3tags in file " << dirmodel.getFilename() << endl; error( ERR_SLAVE_DEFINED, "Insufficient ID3 meta information found for
" + dirmodel.getFilename() + ". Please add at least artist, album and title and try again."); QFile::remove( trackpath ); return; } kdDebug() << "ipodslave::put() parsed Track(" << track.getArtist() << "/" << track.getAlbum() << "/" << track.getTitle() << ")" << endl; // is the track already there? if ( ipod.findTrack( track.getArtist(), track.getAlbum(), track.getTitle())) { // track exists already error(ERR_SLAVE_DEFINED, "Track " + track.getArtist() + "/" + track.getAlbum() + "/" + track.getTitle() + " already exists."); QFile::remove( trackpath ); return; } if ( !ipod.isChanged() ) { showSyncInfoMessage(); } // add the track object to the database ipod.addTrack(track); if ( dirmodel.type == DirectoryModel::TRACK && dirmodel.getPlaylist() != QString::null ) { ipod.addTrackToPlaylist( track, dirmodel.getPlaylist() ); } finished(); } /*! \fn kio_ipodslaveProtocol::put (const KUrl &url, int permissions, bool overwrite, bool resume) */ void kio_ipodslaveProtocol::put(const KUrl &url, int, JobFlags flags) { // TODO implement overwrite 'cause we're able to check for already existent tracks if (flags==KIO::Resume) { error(ERR_CANNOT_RESUME, url.path()); return; } canResume(0); kdDebug() << "ipodslave::put() " << url.path() << endl; DirectoryModel dirmodel(url); if ( !dirmodel.isFileExtSupported() ) { error( ERR_SLAVE_DEFINED, dirmodel.getFileExtension() + " is not a supported filetype for file " + dirmodel.getFilename() ); return; } LockedIPodPtr ipod = findIPod( dirmodel.getIPodName(), true ); if ( ! ipod ) { error( ERR_DOES_NOT_EXIST, dirmodel.getIPodName() ); return; } doPut( *ipod, dirmodel ); kdDebug() << "ipodslave::put() " << url.path() << " finished." << endl; } void kio_ipodslaveProtocol::showSyncInfoMessage() { if( !messageBox( Information, QString("Changes will NOT be saved automatically to the iPod. To save your changes you need to use the Sync Utility at ipod:/Utilities."))) { kdDebug() << "ipodslave::showSyncInfoMessage() messageBox communication failure" << endl; } } // Unary Predicate to find the ipod by a given mountpoint struct IPodMountpointMatcher { QString mountPoint; IPodMountpointMatcher(const QString& mountpoint) : mountPoint( mountpoint ) {} bool operator() (IPod * ipod) const { return mountPoint == ipod->getBasePath(); } }; void kio_ipodslaveProtocol::updateIPodList() { kdDebug() << "kio_ipodslaveProtocol::updateIPodList()" << endl; kdDebug() << "kio_ipodslaveProtocol::looking at existing list" << endl; // check already found ipods for (IPod * ipod = ipods.first(); ipod; ) { if ( ! checkIPod( *ipod ) ) { ipods.removeRef( ipod ); ipod = ipods.current(); } else { ipod = ipods.next(); } } //TODO upddate all this with Solid kdDebug() << "kio_ipodslaveProtocol::finding ipods from mount list" << endl; // try to find a mounted ipod KMountPoint::List currentmountpoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); KMountPoint::List::Iterator mountiter = currentmountpoints.begin(); for (; mountiter != currentmountpoints.end(); ++mountiter) { QString device = (*mountiter)->realDeviceName(); QString mountpoint = (*mountiter)->mountPoint(); kdDebug() << "kio_ipodslaveProtocol::investigating " << mountpoint << endl; // we need to filter out floppy disks and other mounted devices that can't possibly be an iPod if ((device.startsWith("/dev") && // normal device (!(device.startsWith("/dev/sd") || (!device.endsWith("2") && !device.endsWith("3"))))) || !QFile::exists(mountpoint + "/iPod_Control") || find(ipods.begin(), ipods.end(), IPodMountpointMatcher( mountpoint ))) { continue; } IPod * ipod = new IPod( mountpoint, device ); if ( ipod->open() ) { kdDebug() << "ipodslave::updateIPodList(): IPod at " << mountpoint.toAscii() << " found." << endl; ipods.append( ipod ); } else { kdDebug() << "found an ipod but couldn't open it" << endl; delete ipod; continue; } } kdDebug() << "end of updateIpodList()" << endl; } // Unary Predicate to find the ipod with a given distinct name struct IPodDistinctNameMatcher { QString distinctName; IPodDistinctNameMatcher(const QString& distinctname) : distinctName( distinctname ) {} bool operator() (IPod * ipod) const { return distinctName == IPod::createDistinctIPodName(*ipod); } }; LockedIPodPtr kio_ipodslaveProtocol::findIPod( const QString& distinctName, bool write_lock ) { if ( distinctName == QString::null ) { return NULL; } IPodDistinctNameMatcher matcher( distinctName ); IPod * ipod = *find( ipods.begin(), ipods.end(), matcher ); if ( ipod ) { if ( ! checkIPod( *ipod ) ) { ipods.removeRef( ipod ); return NULL; } } else { updateIPodList(); ipod = *find( ipods.begin(), ipods.end(), matcher ); if ( ipod == NULL ) { error(ERR_DOES_NOT_EXIST, distinctName); return NULL; } } LockedIPodPtr result( ipod, write_lock ); if ( ! result->ensureConsistency() ) { error(ERR_INTERNAL, "Apple iPod"); return NULL; } if ( write_lock && !result->isChanged() && result->hasPodcasts() ) { int button = messageBox( WarningContinueCancel, QString("You're about to write to an iPod with podcasts. Since we're not able to handle podcasts in the moment you'll lose them if you continue.") ); if ( button == KMessageBox::Cancel ) { return NULL; } } return result; } bool kio_ipodslaveProtocol::checkIPod( IPod& ipod ) { if ( ! ipod.isStillConnected() ) { kdDebug() << "ipodslave::checkIPod(): reopening iPod." << endl; ipod.close(); } if ( !ipod.isOpen() && !ipod.open() ) { return FALSE; } // some parse error if( !ipod.getItunesDBError().isEmpty()) { error( ERR_COULD_NOT_STAT, ipod.getItunesDBError()); return FALSE; } return TRUE; } /*! \fn kio_ipodslaveProtocol::createUDSEntry( QString &name, size_t size, long type) */ void kio_ipodslaveProtocol::fillUDSEntry(const IPod& ipod, UDSEntry &entry, const QString &name, TrackMetadata& track, long type, bool changed) { kdDebug() << "filling UDS entry" << endl; QString filename= QFile::decodeName(name.toLocal8Bit()); const QString localPath( QString("file:")+ ipod.getRealPath( track.getPath()) ); appendUDSAtom( entry, UDSEntry::UDS_NAME, filename); appendUDSAtom( entry, UDSEntry::UDS_FILE_TYPE, type); appendUDSAtom( entry, UDSEntry::UDS_SIZE, track.getFileSize()); if (type == S_IFDIR) { appendUDSAtom( entry, UDSEntry::UDS_ACCESS, 0755); appendUDSAtom(entry, UDSEntry::UDS_MIME_TYPE, mimetype_InodeDir); if (changed) { QString iconname("folder_important"); appendUDSAtom( entry, UDSEntry::UDS_ICON_NAME, iconname); } } else { appendUDSAtom( entry, UDSEntry::UDS_ACCESS, 0644); } appendUDSAtom(entry, UDSEntry::UDS_EXTRA, track.getArtist()); appendUDSAtom(entry, UDSEntry::UDS_EXTRA, track.getAlbum()); appendUDSAtom(entry, UDSEntry::UDS_EXTRA, track.getGenre()); QTime length = QTime().addMSecs(track.getTrackLength()); appendUDSAtom(entry, UDSEntry::UDS_EXTRA, length.toString("m:ss")); appendUDSAtom( entry, UDSEntry::UDS_EXTRA, localPath ); } /*! \fn kio_ipodslaveProtocol::createUDSEntry( QString &name, size_t size, long type) */ void kio_ipodslaveProtocol::fillUDSEntry( UDSEntry &entry, const QString &name, size_t size, long type, bool changed, uint lastmodified, const QString *mimetype) { kdDebug() << "filling UDS from entry" << name << endl; QString filename= QFile::decodeName(name.toLocal8Bit()); appendUDSAtom( entry, UDSEntry::UDS_NAME, filename); appendUDSAtom( entry, UDSEntry::UDS_FILE_TYPE, type); appendUDSAtom( entry, UDSEntry::UDS_SIZE, size); if (type == S_IFDIR) { appendUDSAtom( entry, UDSEntry::UDS_ACCESS, 0755); if ( ! mimetype ) { // implicit: inode/direcory appendUDSAtom(entry, UDSEntry::UDS_MIME_TYPE, mimetype_InodeDir); } if (changed) { kdDebug() << "folder " << filename << " changed" << endl; QString iconname("folder_important"); appendUDSAtom( entry, UDSEntry::UDS_ICON_NAME, iconname); } appendUDSAtom( entry, UDSEntry::UDS_MODIFICATION_TIME, lastmodified); } else { // normal file appendUDSAtom( entry, UDSEntry::UDS_ACCESS, 0644); } if (mimetype) { appendUDSAtom(entry, UDSEntry::UDS_MIME_TYPE, *mimetype); } } /*! \fn kio_ipodslaveProtocol::createUDSAtom( unsigned int uds, long type) */ void kio_ipodslaveProtocol::appendUDSAtom(UDSEntry &entry, unsigned int uds, long longinfo) { kdDebug() << "appending uds atom 1" << endl; entry.insert(uds,longinfo); } /*! \fn kio_ipodslaveProtocol::appendUDSAtom(UDSEntry &entry, unsigned int uds, const QString stringinfo) */ void kio_ipodslaveProtocol::appendUDSAtom(UDSEntry &entry, unsigned int uds, const QString stringinfo) { kdDebug() << "appending uds atom 2" << endl; entry.insert(uds,stringinfo); } /*! \fn kio_ipodslaveProtocol::findTrack( DirectoryModel& dirmodel) */ TrackMetadata * kio_ipodslaveProtocol::findTrack( const IPod& ipod, const DirectoryModel& dirmodel, int * tracknumber ) { TrackMetadata * track= NULL; TrackList * playlist; bool isplaylist = (dirmodel.getCategory() == DirectoryModel::Playlists); if( dirmodel.getTrack().isEmpty()) return NULL; QString filename= dirmodel.getTrack(); filename= filename.remove( QRegExp("^0+")); // remove leading zeroes // which path? switch( dirmodel.getCategory()) { case DirectoryModel::Artists: // got here by artist/album playlist= ipod.getAlbum( dirmodel.getArtist(), dirmodel.getAlbum()); break; case DirectoryModel::Playlists: // OK get the tracklist from the given playlist playlist= ipod.getPlaylistByTitle( dirmodel.getPlaylist()); break; default: // no alternatives yet : stop here return NULL; } if( playlist == NULL) return NULL; // TODO in a playlist the tracknumber is distinct, so it could really be simpler int tracknum= 0; TrackList::Iterator trackiterator= playlist->getTrackIDs(); while( trackiterator.hasNext()) { Q_UINT32 trackid= trackiterator.next(); if( trackid == LISTITEM_DELETED) { // this happens if a playlist has a "removed" element tracknum++; continue; } if( (track= ipod.getTrackByID( trackid))== NULL) { // track not found tracknum++; continue; } QString trackname = formatTrackname( ipod, *track, ++tracknum, 1, isplaylist ); if( filename.compare(trackname) == 0) { if( tracknumber != 0) *tracknumber= tracknum; break; } else { track = NULL; } } return track; } // does the reverse of formatTrackname() QString kio_ipodslaveProtocol::stripTrackname( const QString& trackfilename) { QString tracktitle= trackfilename; // remove leading numbers, remove file extension, replace %2f with / return tracktitle.remove( QRegExp("^[0-9]+ - ")).remove( QRegExp( ".[^. ]+$")).replace( "%2f", "/"); } bool kio_ipodslaveProtocol::checkError(IPod::IPodError ipodError, const QString& message) { bool result = false; switch ( ipodError ) { case IPod::Err_AlreadyExists: error(ERR_DIR_ALREADY_EXIST, message); break; case IPod::Err_DoesNotExist: error(ERR_DOES_NOT_EXIST, message); break; case IPod::Err_None: result = true; break; case IPod::Err_NotEmpty: case IPod::Err_NotOpen: case IPod::Err_Internal: default: error(ERR_INTERNAL, "ipodslave"); } return result; } /*! \fn kio_ipodslaveProtocol::formatTrackname( QString& buffer, Track& track, int tracknum) */ QString kio_ipodslaveProtocol::formatTrackname( const IPod& ipod, TrackMetadata& track, int tracknum, unsigned short tracknumdigits, bool isPlaylist ) { QString buffer; QString title= track.getTitle(); if( track.getFileExtension().isEmpty()) { QString extension= QFileInfo( ipod.getRealPath( track.getPath())).suffix(); track.setFileExtension( extension); } QString numberformat= "%0"+ QString::number( tracknumdigits)+ "d - "; buffer.sprintf( numberformat.toAscii(), (isPlaylist || track.getTrackNumber() == 0) ? tracknum : track.getTrackNumber()); buffer.append( title.replace( "/", "%2f")); buffer.append( "."+ track.getFileExtension()); // kdDebug() << "kio_ipodslaveProtocol::formatTrackname("<< tracknumdigits << ") trackname=" << buffer << endl; return buffer; }