• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.2 API Reference
  • KDE Home
  • Contact Us
 

KDECore

  • kdecore
  • services
kmimetype.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
3  * 2000-2007 David Faure <faure@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License version 2 as published by the Free Software Foundation;
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB. If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  **/
19 
20 #include "kmimetype.h"
21 #include "kmimetype_p.h"
22 #include "kmimetypefactory.h"
23 #include "kmimetyperepository_p.h"
24 
25 #include <kdebug.h>
26 #include <kde_file.h> // KDE::stat
27 #include <kdeversion.h> // KDE_MAKE_VERSION
28 #include <klocale.h>
29 #include <kprotocolinfo.h>
30 #include <kprotocolinfofactory.h>
31 #include <kstandarddirs.h>
32 #include <kurl.h>
33 
34 #include <QtCore/QFile>
35 #include <QtDBus/QtDBus>
36 #include <QtCore/QHash>
37 #include <QBuffer>
38 
39 extern int servicesDebugArea();
40 
41 template class KSharedPtr<KMimeType>;
42 
43 KMimeType::Ptr KMimeType::defaultMimeTypePtr()
44 {
45  return KMimeTypeRepository::self()->defaultMimeTypePtr();
46 }
47 
48 bool KMimeType::isDefault() const
49 {
50  return name() == defaultMimeType();
51 }
52 
53 void KMimeType::checkEssentialMimeTypes()
54 {
55  KMimeTypeRepository::self()->checkEssentialMimeTypes();
56 }
57 
58 KMimeType::Ptr KMimeType::mimeType(const QString& name, FindByNameOption options)
59 {
60  return KMimeTypeRepository::self()->findMimeTypeByName(name, options);
61 }
62 
63 KMimeType::List KMimeType::allMimeTypes()
64 {
65  // This could be done faster...
66  KMimeType::List lst;
67  Q_FOREACH(const QString& mimeType, KMimeTypeFactory::self()->allMimeTypes()) {
68  if (!mimeType.startsWith(QLatin1String("x-scheme-handler")))
69  lst.append(KMimeType::mimeType(mimeType));
70  }
71  return lst;
72 }
73 
74 bool KMimeType::isBufferBinaryData(const QByteArray& data)
75 {
76  // Check the first 32 bytes (see shared-mime spec)
77  const char* p = data.data();
78  const int end = qMin(32, data.size());
79  for (int i = 0; i < end; ++i) {
80  if ((unsigned char)(p[i]) < 32 && p[i] != 9 && p[i] != 10 && p[i] != 13) // ASCII control character
81  return true;
82  }
83  return false;
84 }
85 
86 static KMimeType::Ptr findFromMode( const QString& path /*only used if is_local_file*/,
87  mode_t mode /*0 if unknown*/,
88  bool is_local_file )
89 {
90  if ( is_local_file && (mode == 0 || mode == (mode_t)-1) ) {
91  KDE_struct_stat buff;
92  if ( KDE::stat( path, &buff ) != -1 )
93  mode = buff.st_mode;
94  }
95 
96  if ( S_ISDIR( mode ) ) {
97  // KDE4 TODO: use an overlay instead
98 #if 0
99  // Special hack for local files. We want to see whether we
100  // are allowed to enter the directory
101  if ( is_local_file )
102  {
103  if ( KDE::access( path, R_OK ) == -1 )
104  return KMimeType::mimeType( "inode/directory-locked" );
105  }
106 #endif
107  return KMimeType::mimeType( QLatin1String("inode/directory") );
108  }
109  if ( S_ISCHR( mode ) )
110  return KMimeType::mimeType( QLatin1String("inode/chardevice") );
111  if ( S_ISBLK( mode ) )
112  return KMimeType::mimeType( QLatin1String("inode/blockdevice") );
113  if ( S_ISFIFO( mode ) )
114  return KMimeType::mimeType( QLatin1String("inode/fifo") );
115  if ( S_ISSOCK( mode ) )
116  return KMimeType::mimeType( QLatin1String("inode/socket") );
117 #ifdef Q_OS_WIN
118  // FIXME: distinguish between mounted & unmounted
119  int size = path.size();
120  if ( size == 2 || size == 3 ) {
121  //GetDriveTypeW is not defined in wince
122 #ifndef _WIN32_WCE
123  unsigned int type = GetDriveTypeW( (LPCWSTR) path.utf16() );
124  switch( type ) {
125  case DRIVE_REMOVABLE:
126  return KMimeType::mimeType( QLatin1String("media/floppy_mounted") );
127  case DRIVE_FIXED:
128  return KMimeType::mimeType( QLatin1String("media/hdd_mounted") );
129  case DRIVE_REMOTE:
130  return KMimeType::mimeType( QLatin1String("media/smb_mounted") );
131  case DRIVE_CDROM:
132  return KMimeType::mimeType( QLatin1String("media/cdrom_mounted") );
133  case DRIVE_RAMDISK:
134  return KMimeType::mimeType( QLatin1String("media/hdd_mounted") );
135  default:
136  break;
137  };
138 #else
139  return KMimeType::mimeType( QLatin1String("media/hdd_mounted") );
140 #endif
141  }
142 #endif
143  // remote executable file? stop here (otherwise findFromContent can do that better for local files)
144  if ( !is_local_file && S_ISREG( mode ) && ( mode & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) )
145  return KMimeType::mimeType( QLatin1String("application/x-executable") );
146 
147  return KMimeType::Ptr();
148 }
149 
150 /*
151 
152 As agreed on the XDG list (and unlike the current shared-mime spec):
153 
154 Glob-matching should prefer derived mimetype over base mimetype, and longer matches
155 over shorter ones. However if two globs of the same length match the file, and the two
156 matches are not related in the inheritance tree, then we have a "glob conflict", which
157 will be resolved below.
158 
159 If only one glob matches, use that
160 
161 If no glob matches, sniff and use that
162 
163 If several globs matches, and sniffing gives a result we do:
164  if sniffed prio >= 80, use sniffed type
165  for glob_match in glob_matches:
166  if glob_match is subclass or equal to sniffed_type, use glob_match
167 
168 If several globs matches, and sniffing fails, or doesn't help:
169  fall back to the first glob match
170 
171 This algorithm only sniffs when there is some uncertainty with the
172 extension matching (thus, it's usable for a file manager).
173 
174 Note: in KDE we want the file views to sniff in a delayed manner.
175 So there's also a fast mode which is:
176  if no glob matches, or if more than one glob matches, use default mimetype and mark as "can be refined".
177 
178 */
179 
180 KMimeType::Ptr KMimeType::findByUrlHelper( const KUrl& _url, mode_t mode,
181  bool is_local_file,
182  QIODevice* device,
183  int* accuracy )
184 {
185  checkEssentialMimeTypes();
186  const QString path = is_local_file ? _url.toLocalFile() : _url.path();
187 
188  if (accuracy)
189  *accuracy = 100;
190 
191  // Look at mode first
192  KMimeType::Ptr mimeFromMode = findFromMode( path, mode, is_local_file );
193  if (mimeFromMode)
194  return mimeFromMode;
195 
196  // First try to find out by looking at the filename (if there's one)
197  const QString fileName( _url.fileName() );
198  QStringList mimeList;
199  if ( !fileName.isEmpty() && !path.endsWith( QLatin1Char('/') ) ) {
200  // and if we can trust it (e.g. don't trust *.pl over HTTP, could be anything)
201  if ( is_local_file || _url.hasSubUrl() || // Explicitly trust suburls
202  KProtocolInfo::determineMimetypeFromExtension( _url.protocol() ) ) {
203  mimeList = KMimeTypeRepository::self()->findFromFileName( fileName );
204  // Found one glob match exactly: OK, use that.
205  // We disambiguate multiple glob matches by sniffing, below.
206  if ( mimeList.count() == 1 ) {
207  const QString selectedMime = mimeList.at(0);
208  KMimeType::Ptr mime = mimeType(selectedMime);
209  if (!mime) {
210  // #265188 - this can happen when an old globs file is lying around after
211  // the packages xml file was removed.
212  kWarning() << "Glob file refers to" << selectedMime << "but this mimetype does not exist!";
213  mimeList.clear();
214  } else {
215  return mime;
216  }
217  }
218  }
219  }
220 
221  if ( device && !device->isOpen() ) {
222  if ( !device->open(QIODevice::ReadOnly) ) {
223  device = 0;
224  }
225  }
226 
227  // Try the magic matches (if we can read the data)
228  QByteArray beginning;
229  if ( device ) {
230  int magicAccuracy;
231  KMimeType::Ptr mime = KMimeTypeRepository::self()->findFromContent(device, &magicAccuracy, beginning);
232  // mime can't be 0, except in case of install problems.
233  // However we get magicAccuracy==0 for octet-stream, i.e. no magic match found.
234  //kDebug(servicesDebugArea()) << "findFromContent said" << (mime?mime->name():QString()) << "with accuracy" << magicAccuracy;
235  if (mime && magicAccuracy > 0) {
236 
237  // Disambiguate conflicting extensions (if magic found something and the magicrule was <80)
238  if (magicAccuracy < 80 && !mimeList.isEmpty()) {
239  // "for glob_match in glob_matches:"
240  // "if glob_match is subclass or equal to sniffed_type, use glob_match"
241  const QString sniffedMime = mime->name();
242  foreach(const QString &m, mimeList) {
243  KMimeType::Ptr mimeFromPattern = KMimeType::mimeType(m);
244  //kDebug(servicesDebugArea()) << "sniffedMime=" << sniffedMime << "mimeFromPattern=" << mimeFromPattern->name();
245  if (mimeFromPattern && mimeFromPattern->is(sniffedMime)) {
246  // We have magic + pattern pointing to this, so it's a pretty good match
247  if (accuracy)
248  *accuracy = 100;
249  return mimeFromPattern;
250  }
251  }
252  }
253 
254  if (accuracy)
255  *accuracy = magicAccuracy;
256  return mime;
257  }
258  }
259 
260  // Not a local file, or no magic allowed, or magic found nothing
261 
262  // Maybe we had multiple matches from globs?
263  if (!mimeList.isEmpty()) {
264  if (accuracy)
265  *accuracy = 20;
266  // We have to pick one...
267  // At least make this deterministic
268  qSort(mimeList.begin(), mimeList.end());
269  Q_FOREACH(const QString& mimeName, mimeList) {
270  KMimeType::Ptr mime = mimeType(mimeName);
271  if (!mime)
272  kWarning() << "Glob file refers to" << mimeName << "but this mimetype does not exist!";
273  else
274  return mime;
275  }
276  }
277 
278  // Find a fallback from the protocol
279  if (accuracy)
280  *accuracy = 10;
281  // ## this breaks with proxying; find a way to move proxying info to kdecore's kprotocolinfo?
282  // ## or hardcode the only case of proxying that we ever had? (ftp-over-http)
283  KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() );
284  QString def;
285  if (prot)
286  def = prot->defaultMimeType();
287  if ( !def.isEmpty() && def != defaultMimeType() ) {
288  // The protocol says it always returns a given mimetype (e.g. text/html for "man:")
289  KMimeType::Ptr mime = mimeType( def );
290  if (mime)
291  return mime;
292  }
293  if ( path.endsWith( QLatin1Char('/') ) || path.isEmpty() ) {
294  // We have no filename at all. Maybe the protocol has a setting for
295  // which mimetype this means (e.g. directory).
296  // For HTTP (def==defaultMimeType()) we don't assume anything,
297  // because of redirections (e.g. freshmeat downloads).
298  if ( def.isEmpty() ) {
299  // Assume inode/directory, if the protocol supports listing.
300  KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _url.protocol() );
301  if ( prot && prot->supportsListing() ) {
302  KMimeType::Ptr mime = mimeType( QLatin1String("inode/directory") );
303  if (mime) { // only 0 if no mimetypes installed
304  return mime;
305  }
306  } else
307  return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/"
308  }
309  }
310 
311  if (accuracy)
312  *accuracy = 0;
313  return defaultMimeTypePtr();
314 }
315 
316 KMimeType::Ptr KMimeType::findByUrl( const KUrl& url, mode_t mode,
317  bool is_local_file, bool fast_mode,
318  int *accuracy )
319 {
320  if ( !is_local_file && url.isLocalFile() )
321  is_local_file = true;
322  if (is_local_file && !fast_mode) {
323  QFile file(url.toLocalFile());
324  return findByUrlHelper(url, mode, is_local_file, &file, accuracy);
325  }
326  return findByUrlHelper(url, mode, is_local_file, 0, accuracy);
327 }
328 
329 KMimeType::Ptr KMimeType::findByPath( const QString& path, mode_t mode,
330  bool fast_mode, int* accuracy )
331 {
332  KUrl url;
333  url.setPath(path);
334  return findByUrl(url, mode, true, fast_mode, accuracy);
335 }
336 
337 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, const QByteArray& data,
338  mode_t mode, int* accuracy )
339 {
340  KUrl url;
341  url.setPath(name);
342  QBuffer buffer(const_cast<QByteArray *>(&data));
343  return findByUrlHelper(url, mode, false, &buffer, accuracy);
344 }
345 
346 KMimeType::Ptr KMimeType::findByNameAndContent( const QString& name, QIODevice* device,
347  mode_t mode, int* accuracy )
348 {
349  KUrl url;
350  url.setPath(name);
351  return findByUrlHelper(url, mode, false, device, accuracy);
352 }
353 
354 QString KMimeType::extractKnownExtension(const QString &fileName)
355 {
356  QString pattern;
357  KMimeTypeRepository::self()->findFromFileName( fileName, &pattern );
358  return pattern;
359 }
360 
361 KMimeType::Ptr KMimeType::findByContent( const QByteArray &data, int *accuracy )
362 {
363  QBuffer buffer(const_cast<QByteArray *>(&data));
364  buffer.open(QIODevice::ReadOnly);
365  QByteArray cache;
366  return KMimeTypeRepository::self()->findFromContent(&buffer, accuracy, cache);
367 }
368 
369 KMimeType::Ptr KMimeType::findByContent( QIODevice* device, int* accuracy )
370 {
371  QByteArray cache;
372  return KMimeTypeRepository::self()->findFromContent(device, accuracy, cache);
373 }
374 
375 KMimeType::Ptr KMimeType::findByFileContent( const QString &fileName, int *accuracy )
376 {
377  checkEssentialMimeTypes();
378 
379  QFile device(fileName);
380  // Look at mode first
381  KMimeType::Ptr mimeFromMode = findFromMode( fileName, 0, true );
382  if (mimeFromMode) {
383  if (accuracy)
384  *accuracy = 100;
385  return mimeFromMode;
386  }
387  if (!device.open(QIODevice::ReadOnly)) {
388  if (accuracy)
389  *accuracy = 0;
390  return KMimeType::defaultMimeTypePtr();
391  }
392 
393  QByteArray cache;
394  return KMimeTypeRepository::self()->findFromContent(&device, accuracy, cache);
395 }
396 
397 bool KMimeType::isBinaryData( const QString &fileName )
398 {
399  QFile file(fileName);
400  if (!file.open(QIODevice::ReadOnly))
401  return false; // err, whatever
402  const QByteArray data = file.read(32);
403  return isBufferBinaryData(data);
404 }
405 
406 KMimeType::KMimeType( KMimeTypePrivate &dd, const QString& name,
407  const QString& comment )
408  : KServiceType( dd, name, comment )
409 {
410 }
411 
412 KMimeType::KMimeType( const QString & fullpath, const QString& name,
413  const QString& comment )
414  : KServiceType( *new KMimeTypePrivate(fullpath), name, comment )
415 {
416 }
417 
418 KMimeType::KMimeType( KMimeTypePrivate &dd)
419  : KServiceType(dd)
420 {
421 }
422 
423 KMimeType::KMimeType( QDataStream& _str, int offset )
424  : KServiceType( *new KMimeTypePrivate(_str, offset ))
425 {
426 }
427 
428 void KMimeTypePrivate::save( QDataStream& _str )
429 {
430  KServiceTypePrivate::save( _str );
431  // Warning adding fields here involves a binary incompatible change - update version
432  // number in ksycoca.h. Never remove fields.
433  _str << m_lstPatterns << QString() << QStringList() << m_iconName;
434 }
435 
436 QVariant KMimeTypePrivate::property( const QString& _name ) const
437 {
438  if ( _name == QLatin1String("Patterns") )
439  return QVariant( m_lstPatterns );
440  if ( _name == QLatin1String("Icon") )
441  return QVariant( iconName(KUrl()) );
442 
443  return KServiceTypePrivate::property( _name );
444 }
445 
446 QStringList KMimeTypePrivate::propertyNames() const
447 {
448  QStringList res = KServiceTypePrivate::propertyNames();
449  res.append( QString::fromLatin1("Patterns") );
450  res.append( QString::fromLatin1("Icon") );
451  return res;
452 }
453 
454 KMimeType::~KMimeType()
455 {
456 }
457 
458 QString KMimeType::iconNameForUrl( const KUrl & _url, mode_t mode )
459 {
460  const KMimeType::Ptr mt = findByUrl( _url, mode, _url.isLocalFile(),
461  false /*HACK*/);
462  if (!mt) {
463  return QString();
464  }
465  static const QString& unknown = KGlobal::staticQString("unknown");
466  const QString mimeTypeIcon = mt->iconName( _url );
467  QString i = mimeTypeIcon;
468 
469  // if we don't find an icon, maybe we can use the one for the protocol
470  if ( i == unknown || i.isEmpty() || mt->name() == defaultMimeType()
471  // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon
472  || _url.path().length() <= 1 )
473  {
474  i = favIconForUrl( _url ); // maybe there is a favicon?
475 
476  if ( i.isEmpty() )
477  i = KProtocolInfo::icon( _url.protocol() );
478 
479  // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder")
480  if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) )
481  i = mimeTypeIcon;
482  }
483  return !i.isEmpty() ? i : unknown;
484 }
485 
486 QString KMimeType::favIconForUrl( const KUrl& url )
487 {
488  /* The kded module also caches favicons, for one week, without any way
489  * to clean up the cache meanwhile.
490  * On the other hand, this QHash will get cleaned up after 5000 request
491  * (a selection in konsole of 80 chars generates around 500 requests)
492  * or by simply restarting the application (or the whole desktop,
493  * more likely, for the case of konqueror or konsole).
494  */
495  static QHash<QUrl, QString> iconNameCache;
496  static int autoClearCache = 0;
497  const QString notFound = QLatin1String("NOTFOUND");
498 
499  if (url.isLocalFile()
500  || !url.protocol().startsWith(QLatin1String("http"))
501  || !KMimeTypeRepository::self()->useFavIcons())
502  return QString();
503 
504  QString iconNameFromCache = iconNameCache.value(url, notFound);
505  if ( iconNameFromCache != notFound ) {
506  if ( (++autoClearCache) < 5000 ) {
507  return iconNameFromCache;
508  }
509  else {
510  iconNameCache.clear();
511  autoClearCache = 0;
512  }
513  }
514 
515  QDBusInterface kded( QString::fromLatin1("org.kde.kded"),
516  QString::fromLatin1("/modules/favicons"),
517  QString::fromLatin1("org.kde.FavIcon") );
518  QDBusReply<QString> result = kded.call( QString::fromLatin1("iconForUrl"), url.url() );
519  iconNameCache.insert(url, result.value());
520  return result; // default is QString()
521 }
522 
523 QString KMimeType::comment( const KUrl &url) const
524 {
525  Q_D(const KMimeType);
526  return d->comment(url);
527 }
528 
529 #ifndef KDE_NO_DEPRECATED
530 QString KMimeType::parentMimeType() const
531 {
532  const QStringList parents = parentMimeTypes();
533  if (!parents.isEmpty())
534  return parents.first();
535  return QString();
536 }
537 #endif
538 
539 bool KMimeTypePrivate::inherits(const QString& mime) const
540 {
541  QStack<QString> toCheck;
542  toCheck.push(m_strName);
543  while (!toCheck.isEmpty()) {
544  const QString current = toCheck.pop();
545  if (current == mime)
546  return true;
547  Q_FOREACH(const QString& parent, KMimeTypeRepository::self()->parents(current)) {
548  toCheck.push(parent);
549  }
550  }
551  return false;
552 }
553 
554 bool KMimeType::is( const QString& mimeTypeName ) const
555 {
556  Q_D(const KMimeType);
557  if (name() == mimeTypeName)
558  return true;
559  const QString mime = KMimeTypeRepository::self()->canonicalName(mimeTypeName);
560  return d->inherits(mime);
561 }
562 
563 QStringList KMimeType::parentMimeTypes() const
564 {
565  Q_D(const KMimeType);
566  return KMimeTypeRepository::self()->parents(d->m_strName);
567 }
568 
569 static void collectParentMimeTypes(const QString& mime, QStringList& allParents)
570 {
571  QStringList parents = KMimeTypeRepository::self()->parents(mime);
572  Q_FOREACH(const QString& parent, parents) {
573  // I would use QSet, but since order matters I better not
574  if (!allParents.contains(parent))
575  allParents.append(parent);
576  }
577  // We want a breadth-first search, so that the least-specific parent (octet-stream) is last
578  // This means iterating twice, unfortunately.
579  Q_FOREACH(const QString& parent, parents) {
580  collectParentMimeTypes(parent, allParents);
581  }
582 }
583 
584 QStringList KMimeType::allParentMimeTypes() const
585 {
586  Q_D(const KMimeType);
587  QStringList allParents;
588  const QString canonical = KMimeTypeRepository::self()->resolveAlias(name());
589  if (!canonical.isEmpty())
590  allParents.append(canonical);
591  collectParentMimeTypes(d->m_strName, allParents);
592  return allParents;
593 }
594 
595 QString KMimeType::defaultMimeType()
596 {
597  static const QString & s_strDefaultMimeType =
598  KGlobal::staticQString( "application/octet-stream" );
599  return s_strDefaultMimeType;
600 }
601 
602 QString KMimeType::iconName( const KUrl& url) const
603 {
604  Q_D(const KMimeType);
605  return d->iconName(url);
606 }
607 
608 QStringList KMimeType::patterns() const
609 {
610  Q_D(const KMimeType);
611  d->ensureXmlDataLoaded();
612  return d->m_lstPatterns;
613 }
614 
615 // loads comment, icon, mainPattern, m_lstPatterns
616 void KMimeTypePrivate::ensureXmlDataLoaded() const
617 {
618  if (m_xmlDataLoaded)
619  return;
620 
621  m_xmlDataLoaded = true;
622 
623  const QString file = m_strName + QLatin1String(".xml");
624  const QStringList mimeFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", file);
625  if (mimeFiles.isEmpty()) {
626  kWarning() << "No file found for" << file << ", even though the file appeared in a directory listing.";
627  kWarning() << "Either it was just removed, or the directory doesn't have executable permission...";
628  kWarning() << KGlobal::dirs()->resourceDirs("xdgdata-mime");
629  return;
630  }
631 
632  QString comment;
633  QString mainPattern;
634  const QStringList languageList = KGlobal::locale()->languageList();
635  QString preferredLanguage = languageList.first();
636  QMap<QString, QString> commentsByLanguage;
637 
638  QListIterator<QString> mimeFilesIter(mimeFiles);
639  mimeFilesIter.toBack();
640  while (mimeFilesIter.hasPrevious()) { // global first, then local.
641  const QString fullPath = mimeFilesIter.previous();
642  QFile qfile(fullPath);
643  if (!qfile.open(QFile::ReadOnly))
644  continue;
645 
646  QXmlStreamReader xml(&qfile);
647  if (xml.readNextStartElement()) {
648  if (xml.name() != "mime-type") {
649  continue;
650  }
651  const QString name = xml.attributes().value(QLatin1String("type")).toString();
652  if (name.isEmpty())
653  continue;
654  if (name != m_strName) {
655  kWarning() << "Got name" << name << "in file" << file << "expected" << m_strName;
656  }
657 
658  while (xml.readNextStartElement()) {
659  const QStringRef tag = xml.name();
660  if (tag == "comment") {
661  QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString();
662  const QString text = xml.readElementText();
663  if (lang.isEmpty()) {
664  lang = QLatin1String("en_US");
665  }
666  if (lang == preferredLanguage) {
667  comment = text;
668  } else {
669  commentsByLanguage.insert(lang, text);
670  }
671  continue; // we called readElementText, so we're at the EndElement already.
672  } else if (tag == "icon") { // as written out by shared-mime-info >= 0.40
673  m_iconName = xml.attributes().value(QLatin1String("name")).toString();
674  } else if (tag == "glob-deleteall") { // as written out by shared-mime-info >= 0.70
675  mainPattern.clear();
676  m_lstPatterns.clear();
677  } else if (tag == "glob") { // as written out by shared-mime-info >= 0.70
678  const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString();
679  if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) {
680  mainPattern = pattern;
681  }
682  if (!m_lstPatterns.contains(pattern))
683  m_lstPatterns.append(pattern);
684  }
685  xml.skipCurrentElement();
686  }
687  if (xml.name() != "mime-type") {
688  kFatal() << "Programming error in KMimeType XML loading, please create a bug report on http://bugs.kde.org and attach the file" << fullPath;
689  }
690  }
691  }
692 
693  if (comment.isEmpty()) {
694  Q_FOREACH(const QString& lang, languageList) {
695  const QString comm = commentsByLanguage.value(lang);
696  if (!comm.isEmpty()) {
697  comment = comm;
698  break;
699  }
700  const int pos = lang.indexOf(QLatin1Char('_'));
701  if (pos != -1) {
702  // "pt_BR" not found? try just "pt"
703  const QString shortLang = lang.left(pos);
704  const QString comm = commentsByLanguage.value(shortLang);
705  if (!comm.isEmpty()) {
706  comment = comm;
707  break;
708  }
709  }
710  }
711  if (comment.isEmpty()) {
712  kWarning() << "Missing <comment> field in" << file;
713  }
714  }
715  m_strComment = comment;
716 
717  const bool globsInXml = (KMimeType::sharedMimeInfoVersion() >= KDE_MAKE_VERSION(0, 70, 0));
718  if (globsInXml) {
719  if (!mainPattern.isEmpty() && m_lstPatterns.first() != mainPattern) {
720  // ensure it's first in the list of patterns
721  m_lstPatterns.removeAll(mainPattern);
722  m_lstPatterns.prepend(mainPattern);
723  }
724  } else {
725  // Fallback: get the patterns from the globs file
726  m_lstPatterns = KMimeTypeRepository::self()->patternsForMimetype(m_strName);
727  }
728 }
729 
730 QString KMimeType::userSpecifiedIconName() const
731 {
732  Q_D(const KMimeType);
733  d->ensureXmlDataLoaded();
734  return d->m_iconName;
735 }
736 
737 int KMimeType::sharedMimeInfoVersion()
738 {
739  return KMimeTypeRepository::self()->sharedMimeInfoVersion();
740 }
741 
742 QString KMimeType::mainExtension() const
743 {
744  Q_D(const KMimeType);
745 
746 #if 1 // HACK START - can be removed once shared-mime-info >= 0.70 is used/required.
747  // The idea was: first usable pattern from m_lstPatterns.
748  // But update-mime-database makes a mess of the order of the patterns,
749  // because it uses a hash internally.
750  static const struct { const char* mime; const char* extension; } s_hardcodedMimes[] = {
751  { "text/plain", ".txt" } };
752  if (d->m_lstPatterns.count() > 1) {
753  const QByteArray me = name().toLatin1();
754  for (uint i = 0; i < sizeof(s_hardcodedMimes)/sizeof(*s_hardcodedMimes); ++i) {
755  if (me == s_hardcodedMimes[i].mime)
756  return QString::fromLatin1(s_hardcodedMimes[i].extension);
757  }
758  }
759 #endif // HACK END
760 
761  Q_FOREACH(const QString& pattern, patterns()) {
762  // Skip if if looks like: README or *. or *.*
763  // or *.JP*G or *.JP?
764  if (pattern.startsWith(QLatin1String("*.")) &&
765  pattern.length() > 2 &&
766  pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
767  return pattern.mid(1);
768  }
769  }
770  // TODO we should also look into the parent mimetype's patterns, no?
771  return QString();
772 }
773 
774 bool KMimeType::matchFileName( const QString &filename, const QString &pattern )
775 {
776  return KMimeTypeRepository::matchFileName( filename, pattern );
777 }
778 
779 int KMimeTypePrivate::serviceOffersOffset() const
780 {
781  return KMimeTypeFactory::self()->serviceOffersOffset(name());
782 }
783 
784 QString KMimeTypePrivate::iconName(const KUrl &) const
785 {
786  static QHash<QUrl, QString> iconNameCache;
787  QString iconNameFromCache = iconNameCache.value(m_strName);
788  if (!iconNameFromCache.isEmpty())
789  return iconNameFromCache;
790 
791  ensureXmlDataLoaded();
792  QString result;
793  if (!m_iconName.isEmpty()) {
794  result = m_iconName;
795  } else {
796  // Make default icon name from the mimetype name
797  // Don't store this in m_iconName, it would make the filetype editor
798  // write out icon names in every local mimetype definition file.
799  QString icon = m_strName;
800  const int slashindex = icon.indexOf(QLatin1Char('/'));
801  if (slashindex != -1) {
802  icon[slashindex] = QLatin1Char('-');
803  }
804  result = icon;
805  }
806  iconNameCache.insert(m_strName, result);
807  return result;
808 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Apr 20 2013 05:59:11 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.10.2 API Reference

Skip menu "kdelibs-4.10.2 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal