00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "config.h"
00024 #include "kmimemagic.h"
00025 #include <kdebug.h>
00026 #include <tdeapplication.h>
00027 #include <tqfile.h>
00028 #include <ksimpleconfig.h>
00029 #include <kstandarddirs.h>
00030 #include <kstaticdeleter.h>
00031 #include <klargefile.h>
00032 #include <assert.h>
00033
00034 #include <magic.h>
00035
00036 #ifndef MAGIC_MIME_TYPE
00037 #define MAGIC_MIME_TYPE MAGIC_MIME
00038 #endif
00039
00040
00041
00042 #define FILE_LOAD 0
00043
00044 static void process(struct config_rec* conf, const TQString &);
00045
00046 KMimeMagic* KMimeMagic::s_pSelf;
00047 static KStaticDeleter<KMimeMagic> kmimemagicsd;
00048
00049 KMimeMagic* KMimeMagic::self() {
00050 if( !s_pSelf ) {
00051 initStatic();
00052 }
00053 return s_pSelf;
00054 }
00055
00056 void KMimeMagic::initStatic() {
00057 s_pSelf = kmimemagicsd.setObject( s_pSelf, new KMimeMagic() );
00058 s_pSelf->setFollowLinks( true );
00059 }
00060
00061 #include <stdio.h>
00062 #include <unistd.h>
00063 #include <stdlib.h>
00064 #include <sys/wait.h>
00065 #include <sys/types.h>
00066 #include <sys/stat.h>
00067 #include <fcntl.h>
00068 #include <errno.h>
00069 #include <ctype.h>
00070 #include <time.h>
00071 #include <utime.h>
00072 #include <stdarg.h>
00073 #include <tqregexp.h>
00074 #include <tqstring.h>
00075
00076 #define MIME_INODE_DIR "inode/directory"
00077 #define MIME_INODE_CDEV "inode/chardevice"
00078 #define MIME_INODE_BDEV "inode/blockdevice"
00079 #define MIME_INODE_FIFO "inode/fifo"
00080 #define MIME_INODE_LINK "inode/link"
00081 #define MIME_INODE_SOCK "inode/socket"
00082 #define MIME_BINARY_UNREADABLE "application/x-unreadable"
00083 #define MIME_BINARY_ZEROSIZE "application/x-zerosize"
00084
00095 class KMimeMagicUtimeConf {
00096 public:
00097 KMimeMagicUtimeConf() {
00098 tmpDirs << TQString::fromLatin1("/tmp");
00099
00100
00101
00102 TQStringList confDirs = TDEGlobal::dirs()->resourceDirs( "config" );
00103 if ( !confDirs.isEmpty() ) {
00104 TQString globalConf = confDirs.last() + "kmimemagicrc";
00105 if ( TQFile::exists( globalConf ) ) {
00106 KSimpleConfig cfg( globalConf );
00107 cfg.setGroup( "Settings" );
00108 tmpDirs = cfg.readListEntry( "atimeDirs" );
00109 }
00110 if ( confDirs.count() > 1 ) {
00111 TQString localConf = confDirs.first() + "kmimemagicrc";
00112 if ( TQFile::exists( localConf ) ) {
00113 KSimpleConfig cfg( localConf );
00114 cfg.setGroup( "Settings" );
00115 tmpDirs += cfg.readListEntry( "atimeDirs" );
00116 }
00117 }
00118 for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it ) {
00119 TQString dir = *it;
00120 if ( !dir.isEmpty() && dir[ dir.length()-1 ] != '/' ) {
00121 (*it) += '/';
00122 }
00123 }
00124 }
00125 #if 0
00126
00127 for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it ) {
00128 kdDebug(7018) << " atimeDir: " << *it << endl;
00129 }
00130 #endif
00131 }
00132
00133 bool restoreAccessTime( const TQString & file ) const {
00134 TQString dir = file.left( file.findRev( '/' ) );
00135 bool res = tmpDirs.contains( dir );
00136
00137 return res;
00138 }
00139 TQStringList tmpDirs;
00140 };
00141
00142 TQString fixupMagicOutput(TQString &mime) {
00143 if (mime == "inode/x-empty") {
00144 return MIME_BINARY_ZEROSIZE;
00145 }
00146 else if (mime.contains("no read permission")) {
00147 return MIME_BINARY_UNREADABLE;
00148 }
00149 else {
00150 return mime;
00151 }
00152 }
00153
00154
00155 struct config_rec {
00156 bool followLinks;
00157 TQString resultBuf;
00158 int accuracy;
00159
00160 magic_t magic;
00161 TQStringList databases;
00162
00163 KMimeMagicUtimeConf * utimeConf;
00164 };
00165
00166
00167
00168
00169 int KMimeMagic::apprentice( const TQString& magicfile ) {
00170 TQString maindatabase = magicfile;
00171 if (maindatabase == "") {
00172 #ifdef HAVE_LIBMAGIC_GETPATH
00173 maindatabase = magic_getpath(0, FILE_LOAD);
00174 #else
00175 maindatabase = TQString(LIBMAGIC_PATH);
00176 #endif
00177 if (maindatabase == "") {
00178 kdWarning() << k_funcinfo << "Unable to locate system mime magic database; mime type detection will not function correctly!" << endl;
00179 }
00180 }
00181 conf->databases.clear();
00182 conf->databases.append(maindatabase);
00183 return magic_load(conf->magic, conf->databases[0].latin1());
00184 }
00185
00186
00187
00188
00189
00190
00191 void process(struct config_rec* conf, const TQString & fn) {
00192 KDE_struct_stat sb;
00193 TQCString fileName = TQFile::encodeName( fn );
00194
00195 int magic_flags = MAGIC_ERROR|MAGIC_MIME_TYPE;
00196 if (conf->followLinks) {
00197 magic_flags |= MAGIC_SYMLINK;
00198 }
00199 magic_setflags(conf->magic, magic_flags);
00200 conf->resultBuf = TQString(magic_file(conf->magic, fileName));
00201 conf->resultBuf = fixupMagicOutput(conf->resultBuf);
00202
00203 if ( conf->utimeConf && conf->utimeConf->restoreAccessTime( fn ) ) {
00204
00205
00206
00207
00208
00209 struct utimbuf utbuf;
00210 utbuf.actime = sb.st_atime;
00211 utbuf.modtime = sb.st_mtime;
00212 (void) utime(fileName, &utbuf);
00213 }
00214 }
00215
00216 KMimeMagic::KMimeMagic() {
00217
00218 TQString mimefile = locate( "mime", "magic" );
00219 init( mimefile );
00220
00221 TQStringList snippets = TDEGlobal::dirs()->findAllResources( "config", "magic/*.magic", true );
00222 for ( TQStringList::Iterator it = snippets.begin() ; it != snippets.end() ; ++it ) {
00223 if ( !mergeConfig( *it ) ) {
00224 kdWarning() << k_funcinfo << "Failed to parse " << *it << endl;
00225 }
00226 }
00227 }
00228
00229 KMimeMagic::KMimeMagic(const TQString & _configfile) {
00230 init( _configfile );
00231 }
00232
00233 void KMimeMagic::init( const TQString& _configfile ) {
00234 int result;
00235 conf = new config_rec;
00236
00237
00238 conf->magic = magic_open(MAGIC_MIME_TYPE);
00239 magicResult = NULL;
00240 conf->followLinks = false;
00241
00242 conf->utimeConf = 0L;
00243
00244 result = apprentice(_configfile);
00245 if (result == -1) {
00246 return;
00247 }
00248 }
00249
00250
00251
00252
00253
00254 KMimeMagic::~KMimeMagic() {
00255 if (conf) {
00256 magic_close(conf->magic);
00257 delete conf->utimeConf;
00258 delete conf;
00259 }
00260 delete magicResult;
00261 }
00262
00263 bool KMimeMagic::mergeConfig(const TQString & _configfile) {
00264 conf->databases.append(_configfile);
00265 TQString merged_databases = conf->databases.join(":");
00266 #ifdef MAGIC_VERSION
00267 int magicvers = magic_version();
00268 #else // MAGIC_VERSION
00269 int magicvers = 0;
00270 #endif // MAGIC_VERSION
00271 if ((magicvers < 512) || (magicvers >= 518)) {
00272 if (magic_load(conf->magic, merged_databases.latin1()) == 0) {
00273 return true;
00274 }
00275 else {
00276 return false;
00277 }
00278 }
00279 else {
00280 kdWarning(7018) << k_funcinfo << "KMimeMagic::mergeConfig disabled due to buggy libmagic version " << magicvers << endl;
00281 return false;
00282 }
00283 }
00284
00285 void KMimeMagic::setFollowLinks( bool _enable ) {
00286 conf->followLinks = _enable;
00287 }
00288
00289 KMimeMagicResult *KMimeMagic::findBufferType(const TQByteArray &array) {
00290 conf->resultBuf = TQString::null;
00291 if ( !magicResult ) {
00292 magicResult = new KMimeMagicResult();
00293 }
00294 magicResult->setInvalid();
00295 conf->accuracy = 100;
00296
00297 int nbytes = array.size();
00298 if (nbytes == 0) {
00299 conf->resultBuf = MIME_BINARY_ZEROSIZE;
00300 }
00301 else {
00302 int magic_flags = MAGIC_ERROR|MAGIC_MIME_TYPE;
00303 if (conf->followLinks) {
00304 magic_flags |= MAGIC_SYMLINK;
00305 }
00306 magic_setflags(conf->magic, magic_flags);
00307 conf->resultBuf = TQString(magic_buffer(conf->magic, array.data(), nbytes));
00308 conf->resultBuf = fixupMagicOutput(conf->resultBuf);
00309 }
00310
00311 magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
00312 magicResult->setAccuracy(conf->accuracy);
00313 return magicResult;
00314 }
00315
00316 static void refineResult(KMimeMagicResult *r, const TQString & _filename) {
00317 TQString tmp = r->mimeType();
00318 if (tmp.isEmpty())
00319 return;
00320 if ( tmp == "text/x-c" || tmp == "text/x-objc" )
00321 {
00322 if ( _filename.right(2) == ".h" )
00323 tmp += "hdr";
00324 else
00325 tmp += "src";
00326 r->setMimeType(tmp);
00327 }
00328 else
00329 if ( tmp == "text/x-c++" )
00330 {
00331 if ( _filename.endsWith(".h")
00332 || _filename.endsWith(".hh")
00333 || _filename.endsWith(".H")
00334 || !_filename.right(4).contains('.'))
00335 tmp += "hdr";
00336 else
00337 tmp += "src";
00338 r->setMimeType(tmp);
00339 }
00340 else
00341 if ( tmp == "application/x-sharedlib" )
00342 {
00343 if ( _filename.find( ".so" ) == -1 )
00344 {
00345 tmp = "application/x-executable";
00346 r->setMimeType( tmp );
00347 }
00348 }
00349 }
00350
00351 KMimeMagicResult *KMimeMagic::findBufferFileType( const TQByteArray &data, const TQString &fn) {
00352 KMimeMagicResult * r = findBufferType( data );
00353 refineResult(r, fn);
00354 return r;
00355 }
00356
00357
00358
00359
00360 KMimeMagicResult* KMimeMagic::findFileType(const TQString & fn) {
00361 #ifdef DEBUG_MIMEMAGIC
00362 kdDebug(7018) << "KMimeMagic::findFileType " << fn << endl;
00363 #endif
00364 conf->resultBuf = TQString::null;
00365
00366 if ( !magicResult ) {
00367 magicResult = new KMimeMagicResult();
00368 }
00369 magicResult->setInvalid();
00370 conf->accuracy = 100;
00371
00372 if ( !conf->utimeConf ) {
00373 conf->utimeConf = new KMimeMagicUtimeConf();
00374 }
00375
00376
00377 process(conf, fn );
00378
00379
00380 magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
00381 magicResult->setAccuracy(conf->accuracy);
00382 refineResult(magicResult, fn);
00383 return magicResult;
00384 }