~ubuntu-branches/ubuntu/raring/kde-runtime/raring-proposed

« back to all changes in this revision

Viewing changes to nepomuk/kioslaves/common/resourcestat.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2012-12-07 17:43:09 UTC
  • mfrom: (1.1.19)
  • Revision ID: package-import@ubuntu.com-20121207174309-21o5e6dsbo9judu8
Tags: 4:4.9.90-0ubuntu1
* New upstream beta release
* Update .install files

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
   Copyright 2008-2010 Sebastian Trueg <trueg@kde.org>
3
 
 
4
 
   This program is free software; you can redistribute it and/or
5
 
   modify it under the terms of the GNU General Public License as
6
 
   published by the Free Software Foundation; either version 2 of
7
 
   the License or (at your option) version 3 or any later version
8
 
   accepted by the membership of KDE e.V. (or its successor approved
9
 
   by the membership of KDE e.V.), which shall act as a proxy
10
 
   defined in Section 14 of version 3 of the license.
11
 
 
12
 
   This program is distributed in the hope that it will be useful,
13
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
   GNU General Public License for more details.
16
 
 
17
 
   You should have received a copy of the GNU General Public License
18
 
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 
*/
20
 
 
21
 
#include "resourcestat.h"
22
 
#include "nepomuksearchurltools.h"
23
 
 
24
 
#include <QtCore/QEventLoop>
25
 
#include <QtCore/QTimer>
26
 
#include <QtCore/QFile>
27
 
 
28
 
#include <KUrl>
29
 
#include <KMimeType>
30
 
#include <KUser>
31
 
#include <kio/udsentry.h>
32
 
#include <KDebug>
33
 
 
34
 
#include <Nepomuk2/Variant>
35
 
#include <Nepomuk2/Types/Class>
36
 
#include <Nepomuk2/ResourceManager>
37
 
#include <Nepomuk2/Query/Query>
38
 
#include <Nepomuk2/Query/ComparisonTerm>
39
 
#include <Nepomuk2/Query/ResourceTerm>
40
 
#include <Nepomuk2/Vocabulary/NFO>
41
 
#include <Nepomuk2/Vocabulary/NIE>
42
 
#include <Nepomuk2/Vocabulary/PIMO>
43
 
 
44
 
#include <Soprano/Vocabulary/RDF>
45
 
#include <Soprano/Vocabulary/NAO>
46
 
#include <Soprano/QueryResultIterator>
47
 
#include <Soprano/NodeIterator>
48
 
#include <Soprano/Node>
49
 
#include <Soprano/Model>
50
 
 
51
 
#include <Solid/Device>
52
 
#include <Solid/StorageAccess>
53
 
 
54
 
 
55
 
KUrl Nepomuk2::stripQuery( const KUrl& url )
56
 
{
57
 
    KUrl newUrl( url );
58
 
    newUrl.setEncodedQuery( QByteArray() );
59
 
    return newUrl;
60
 
}
61
 
 
62
 
 
63
 
Nepomuk2::Resource Nepomuk2::splitNepomukUrl( const KUrl& url, QString* filename )
64
 
{
65
 
    //
66
 
    // let's try to extract the resource from the url in case we listed a tag or
67
 
    // filesystem and need to stat the entries in those virtual folders
68
 
    //
69
 
    // pre KDE 4.4 resources had just a single section, in KDE 4.4 we have "/res/<UUID>"
70
 
    //
71
 
    if(url.hasQueryItem(QLatin1String("resource"))) {
72
 
        return KUrl(url.queryItemValue(QLatin1String("resource")));
73
 
    }
74
 
    else {
75
 
        const QString urlStr = stripQuery( url ).url();
76
 
        int pos = urlStr.indexOf( '/', urlStr.startsWith( QLatin1String( "nepomuk:/res/" ) ) ? 13 : 9 );
77
 
        if ( pos > 0 ) {
78
 
            KUrl resourceUri = urlStr.left(pos);
79
 
            if ( filename )
80
 
                *filename = urlStr.mid( pos+1 );
81
 
            return resourceUri;
82
 
        }
83
 
        else {
84
 
            return stripQuery( url );
85
 
        }
86
 
    }
87
 
}
88
 
 
89
 
 
90
 
bool Nepomuk2::isRemovableMediaFile( const Nepomuk2::Resource& res )
91
 
{
92
 
    if ( res.hasProperty( Nepomuk2::Vocabulary::NIE::url() ) ) {
93
 
        KUrl url = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
94
 
        return ( url.protocol() == QLatin1String( "filex" ) );
95
 
    }
96
 
    else {
97
 
        return false;
98
 
    }
99
 
}
100
 
 
101
 
 
102
 
Solid::StorageAccess* Nepomuk2::storageFromUUID( const QString& uuid )
103
 
{
104
 
    QString solidQuery = QString::fromLatin1( "[ StorageVolume.usage=='FileSystem' AND StorageVolume.uuid=='%1' ]" ).arg( uuid.toLower() );
105
 
    QList<Solid::Device> devices = Solid::Device::listFromQuery( solidQuery );
106
 
    kDebug() << uuid << solidQuery << devices.count();
107
 
    if ( !devices.isEmpty() )
108
 
        return devices.first().as<Solid::StorageAccess>();
109
 
    else
110
 
        return 0;
111
 
}
112
 
 
113
 
 
114
 
bool Nepomuk2::mountAndWait( Solid::StorageAccess* storage )
115
 
{
116
 
    kDebug() << storage;
117
 
    QEventLoop loop;
118
 
    loop.connect( storage,
119
 
                  SIGNAL(accessibilityChanged(bool, QString)),
120
 
                  SLOT(quit()) );
121
 
    // timeout 20 second
122
 
    QTimer::singleShot( 20000, &loop, SLOT(quit()) );
123
 
 
124
 
    storage->setup();
125
 
    loop.exec();
126
 
 
127
 
    kDebug() << storage << storage->isAccessible();
128
 
 
129
 
    return storage->isAccessible();
130
 
}
131
 
 
132
 
 
133
 
KUrl Nepomuk2::determineFilesystemPath( const Nepomuk2::Resource& fsRes )
134
 
{
135
 
    QString uuidQuery = QString::fromLatin1( "select ?uuid where { %1 %2 ?uuid . }" )
136
 
                        .arg( Soprano::Node::resourceToN3( fsRes.uri() ),
137
 
                              Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::identifier() ) );
138
 
    Soprano::QueryResultIterator it = Nepomuk2::ResourceManager::instance()->mainModel()->executeQuery( uuidQuery, Soprano::Query::QueryLanguageSparql );
139
 
    if ( it.next() ) {
140
 
        Solid::StorageAccess* storage = storageFromUUID( it["uuid"].toString() );
141
 
        it.close();
142
 
        if ( storage &&
143
 
             ( storage->isAccessible() ||
144
 
               mountAndWait( storage ) ) ) {
145
 
            return storage->filePath();
146
 
        }
147
 
    }
148
 
    return KUrl();
149
 
}
150
 
 
151
 
 
152
 
QString Nepomuk2::getFileSystemLabelForRemovableMediaFileUrl( const Nepomuk2::Resource& res )
153
 
{
154
 
    QList<Soprano::Node> labelNodes
155
 
        = Nepomuk2::ResourceManager::instance()->mainModel()->executeQuery( QString::fromLatin1( "select ?label where { "
156
 
                                                                                                "%1 nie:isPartOf ?fs . "
157
 
                                                                                                "?fs a nfo:Filesystem . "
158
 
                                                                                                "?fs nao:prefLabel ?label . "
159
 
                                                                                                "} LIMIT 1" )
160
 
                                                                           .arg( Soprano::Node::resourceToN3( res.uri() ) ),
161
 
                                                                           Soprano::Query::QueryLanguageSparql ).iterateBindings( "label" ).allNodes();
162
 
 
163
 
    if ( !labelNodes.isEmpty() )
164
 
        return labelNodes.first().toString();
165
 
    else
166
 
        return res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl().host(); // Solid UUID
167
 
}
168
 
 
169
 
 
170
 
KUrl Nepomuk2::convertRemovableMediaFileUrl( const KUrl& url, bool evenMountIfNecessary )
171
 
{
172
 
    Solid::StorageAccess* storage = Nepomuk2::storageFromUUID( url.host() );
173
 
    kDebug() << url << storage;
174
 
    if ( storage &&
175
 
         ( storage->isAccessible() ||
176
 
           ( evenMountIfNecessary && Nepomuk2::mountAndWait( storage ) ) ) ) {
177
 
        kDebug() << "converted:" << KUrl( storage->filePath() + QLatin1String( "/" ) + url.path() );
178
 
        return QString( storage->filePath() + QLatin1String( "/" ) + url.path() );
179
 
    }
180
 
    else {
181
 
        return KUrl();
182
 
    }
183
 
}
184
 
 
185
 
 
186
 
void Nepomuk2::addGenericNepomukResourceData( const Nepomuk2::Resource& res, KIO::UDSEntry& uds, bool includeMimeType )
187
 
{
188
 
    //
189
 
    // Add some random values
190
 
    //
191
 
    uds.insert( KIO::UDSEntry::UDS_ACCESS, 0700 );
192
 
    uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() );
193
 
    if ( res.hasProperty( Vocabulary::NIE::lastModified() ) ) {
194
 
        // remotely stored files
195
 
        uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, res.property( Vocabulary::NIE::lastModified() ).toDateTime().toTime_t() );
196
 
    }
197
 
    else {
198
 
        // all nepomuk resources
199
 
        uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, res.property( Soprano::Vocabulary::NAO::lastModified() ).toDateTime().toTime_t() );
200
 
        uds.insert( KIO::UDSEntry::UDS_CREATION_TIME, res.property( Soprano::Vocabulary::NAO::created() ).toDateTime().toTime_t() );
201
 
    }
202
 
 
203
 
    if ( res.hasProperty( Vocabulary::NIE::contentSize() ) ) {
204
 
        // remotely stored files
205
 
        uds.insert( KIO::UDSEntry::UDS_SIZE, res.property( Vocabulary::NIE::contentSize() ).toInt() );
206
 
    }
207
 
 
208
 
 
209
 
    //
210
 
    // Starting with KDE 4.4 we have the pretty UDS_NEPOMUK_URI which makes
211
 
    // everything much cleaner since kio slaves can decide if the resources can be
212
 
    // annotated or not.
213
 
    //
214
 
    uds.insert( KIO::UDSEntry::UDS_NEPOMUK_URI, KUrl( res.uri() ).url() );
215
 
 
216
 
    if ( includeMimeType ) {
217
 
        // Use nice display types like "Person", "Project" and so on
218
 
        Nepomuk2::Types::Class type( res.type() );
219
 
        if (!type.label().isEmpty())
220
 
            uds.insert( KIO::UDSEntry::UDS_DISPLAY_TYPE, type.label() );
221
 
 
222
 
        QString icon = res.genericIcon();
223
 
        if ( !icon.isEmpty() ) {
224
 
            uds.insert( KIO::UDSEntry::UDS_ICON_NAME, icon );
225
 
        }
226
 
        else {
227
 
            // a fallback icon for nepomuk resources
228
 
            uds.insert( KIO::UDSEntry::UDS_ICON_NAME, QLatin1String( "nepomuk" ) );
229
 
        }
230
 
 
231
 
        if ( uds.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) != QLatin1String( "nepomuk" ) )
232
 
            uds.insert( KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES, QLatin1String( "nepomuk" ) );
233
 
    }
234
 
}
235
 
 
236
 
 
237
 
KIO::UDSEntry Nepomuk2::statNepomukResource( const Nepomuk2::Resource& res, bool doNotForward )
238
 
{
239
 
    //
240
 
    // We do not have a local file
241
 
    // This is where the magic starts to happen.
242
 
    // This is where we only use Nepomuk properties
243
 
    //
244
 
    KIO::UDSEntry uds;
245
 
 
246
 
    // we handle files on removable media which are not mounted
247
 
    // as a special case
248
 
    bool isFileOnRemovableMedium = isRemovableMediaFile( res );
249
 
 
250
 
    // The display name can be anything
251
 
    QString displayName;
252
 
    if ( isFileOnRemovableMedium ) {
253
 
        displayName = i18nc( "%1 is a filename of a file on a removable device, "
254
 
                             "%2 is the name of the removable medium which often is something like "
255
 
                             "'X GiB Removable Media.",
256
 
                             "%1 (on unmounted medium <resource>%2</resource>)",
257
 
                             res.genericLabel(),
258
 
                             getFileSystemLabelForRemovableMediaFileUrl( res ) );
259
 
    }
260
 
    else {
261
 
        displayName = res.genericLabel();
262
 
    }
263
 
    uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, displayName );
264
 
 
265
 
    // UDS_NAME needs to be unique but can be ugly
266
 
    uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( res.uri() ) );
267
 
 
268
 
    //
269
 
    // There can still be file resources that have a mimetype but are
270
 
    // stored remotely, thus they do not have a local nie:url
271
 
    //
272
 
    // Sadly Strigi's mimetype is not very useful (yet)
273
 
    /* QStringList mimeTypes = res.property( Vocabulary::NIE::mimeType() ).toStringList();
274
 
    if ( !mimeTypes.isEmpty() ) {
275
 
        uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimeTypes.first() );
276
 
    }
277
 
    else */
278
 
    if ( !doNotForward && isFileOnRemovableMedium ) {
279
 
        KMimeType::Ptr mt = KMimeType::findByUrl( res.property( Vocabulary::NIE::url() ).toUrl(),
280
 
                                                  0,
281
 
                                                  false, /* no local file as it is not accessible at the moment */
282
 
                                                  true   /* fast mode */ );
283
 
        if ( mt ) {
284
 
            uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mt->name() );
285
 
        }
286
 
    }
287
 
 
288
 
    addGenericNepomukResourceData( res, uds, !uds.contains( KIO::UDSEntry::UDS_MIME_TYPE ) );
289
 
 
290
 
    if ( !doNotForward ) {
291
 
        KUrl reUrl = Nepomuk2::redirectionUrl( res );
292
 
        if ( !reUrl.isEmpty() ) {
293
 
            uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String( "inode/directory" ) );
294
 
            uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
295
 
        }
296
 
    }
297
 
 
298
 
    return uds;
299
 
}
300
 
 
301
 
 
302
 
bool Nepomuk2::willBeRedirected( const Nepomuk2::Resource& res )
303
 
{
304
 
    // here the same canditions as in redirectionUrl need to be listed
305
 
    return( res.hasType( Nepomuk2::Vocabulary::NFO::Folder() ) ||
306
 
            res.hasType( Soprano::Vocabulary::NAO::Tag() ) ||
307
 
            res.hasType( Nepomuk2::Vocabulary::NFO::Filesystem() ) ||
308
 
            !res.hasType( Nepomuk2::Vocabulary::NFO::FileDataObject() ) );
309
 
}
310
 
 
311
 
 
312
 
KUrl Nepomuk2::redirectionUrl( const Nepomuk2::Resource& res )
313
 
{
314
 
    // list folders by forwarding to the actual folder on disk
315
 
    if ( res.hasType( Nepomuk2::Vocabulary::NFO::Folder() ) ) {
316
 
        return res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
317
 
    }
318
 
 
319
 
    // list filesystems by forwarding to the mounted path on disk (in case the fs is mounted)
320
 
    else if ( res.hasType( Nepomuk2::Vocabulary::NFO::Filesystem() ) ) {
321
 
        KUrl fsUrl = determineFilesystemPath( res );
322
 
        if ( fsUrl.isValid() ) {
323
 
            return fsUrl;
324
 
        }
325
 
    }
326
 
 
327
 
    // list tags by listing everything tagged with that tag
328
 
    else if ( res.hasType( Soprano::Vocabulary::NAO::Tag() ) ) {
329
 
        Query::ComparisonTerm term( Soprano::Vocabulary::NAO::hasTag(), Query::ResourceTerm( res ), Query::ComparisonTerm::Equal );
330
 
        KUrl url = Query::Query( term ).toSearchUrl( i18n( "Things tagged '%1'", res.genericLabel() ) );
331
 
        url.addQueryItem( QLatin1String( "resource" ), KUrl( res.uri() ).url() );
332
 
        return url;
333
 
    }
334
 
 
335
 
    // list everything else besides files by querying things related to the resource in some way
336
 
    // this works for music albums or artists but it would also work for tags
337
 
    else if ( !res.hasType( Nepomuk2::Vocabulary::NFO::FileDataObject() ) ) {
338
 
        Query::ComparisonTerm term( QUrl(), Query::ResourceTerm( res ), Query::ComparisonTerm::Equal );
339
 
        KUrl url = Query::Query( term ).toSearchUrl( res.genericLabel() );
340
 
        url.addQueryItem( QLatin1String( "resource" ), KUrl( res.uri() ).url() );
341
 
        kDebug() << url;
342
 
        return url;
343
 
    }
344
 
 
345
 
    // no forwarding done
346
 
    return KUrl();
347
 
}
348
 
 
349
 
 
350
 
namespace {
351
 
 
352
 
    /**
353
 
     * Check if the resource represents a local file with an existing nie:url property.
354
 
     */
355
 
    bool isLocalFile( const Nepomuk2::Resource& res )
356
 
    {
357
 
        if ( res.hasProperty( Nepomuk2::Vocabulary::NIE::url() ) ) {
358
 
            KUrl url = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
359
 
            return ( !url.isEmpty() &&
360
 
                     QFile::exists( url.toLocalFile() ) );
361
 
        }
362
 
        else {
363
 
            return false;
364
 
        }
365
 
    }
366
 
}
367
 
 
368
 
KUrl Nepomuk2::nepomukToFileUrl( const KUrl& url, bool evenMountIfNecessary )
369
 
{
370
 
    QString filename;
371
 
    Nepomuk2::Resource res = splitNepomukUrl( url, &filename );
372
 
 
373
 
    if ( !res.exists() )
374
 
        return KUrl();
375
 
 
376
 
    KUrl newURL;
377
 
 
378
 
    if ( isLocalFile( res ) ) {
379
 
        newURL = res.property( Vocabulary::NIE::url() ).toUrl();
380
 
    }
381
 
    else if ( isRemovableMediaFile( res ) ) {
382
 
        const KUrl removableMediaUrl = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
383
 
        newURL = convertRemovableMediaFileUrl( removableMediaUrl, evenMountIfNecessary );
384
 
    }
385
 
 
386
 
    if ( newURL.isValid() && !filename.isEmpty() ) {
387
 
        newURL.addPath( filename );
388
 
    }
389
 
 
390
 
    kDebug() << url << newURL;
391
 
 
392
 
    return newURL;
393
 
}