Parent: [9db024] (diff)

Child: [92da4c] (diff)

Download this file

mimetype.cpp    247 lines (209 with data), 7.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* Copyright (C) 2004 J.F.Dockes
* 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.
*/
#ifndef TEST_MIMETYPE
#include "autoconfig.h"
#include "safesysstat.h"
#include <ctype.h>
#include <string>
#include <list>
using namespace std;
#include "mimetype.h"
#include "log.h"
#include "execmd.h"
#include "rclconfig.h"
#include "smallut.h"
#include "idfile.h"
#include "pxattr.h"
/// Identification of file from contents. This is called for files with
/// unrecognized extensions.
///
/// The system 'file' utility does not always work for us. For exemple
/// it will mistake mail folders for simple text files if there is no
/// 'Received' header, which would be the case, for exemple in a
/// 'Sent' folder. Also "file -i" does not exist on all systems, and
/// is quite costly to execute.
/// So we first call the internal file identifier, which currently
/// only knows about mail, but in which we can add the more
/// current/interesting file types.
/// As a last resort we execute 'file' or its configured replacement
/// (except if forbidden by config)
static string mimetypefromdata(RclConfig *cfg, const string &fn, bool usfc)
{
// First try the internal identifying routine
string mime = idFile(fn.c_str());
#ifdef USE_SYSTEM_FILE_COMMAND
if (usfc && mime.empty()) {
// Last resort: use "file -i", or its configured replacement.
// 'file' fallback if the configured command (default:
// xdg-mime) is not found
static const vector<string> tradfilecmd =
create_vector<string>(FILE_PROG) ("-i") (fn);
vector<string> cmd;
string scommand;
if (cfg->getConfParam("systemfilecommand", scommand)) {
LOGDEB2("mimetype: syscmd from config: " << (scommand) << "\n" );
stringToStrings(scommand, cmd);
string exe;
if (cmd.empty()) {
cmd = tradfilecmd;
} else if (!ExecCmd::which(cmd[0], exe)) {
cmd = tradfilecmd;
} else {
cmd[0] = exe;
}
cmd.push_back(fn);
} else {
LOGDEB("mimetype:systemfilecommand not found, using " << (stringsToString(tradfilecmd)) << "\n" );
cmd = tradfilecmd;
}
string result;
if (!ExecCmd::backtick(cmd, result)) {
LOGERR("mimetypefromdata: exec " << (stringsToString(cmd)) << " failed\n" );
return string();
}
trimstring(result, " \t\n\r");
LOGDEB2("mimetype: systemfilecommand output [" << (result) << "]\n" );
// The normal output from "file -i" looks like the following:
// thefilename.xxx: text/plain; charset=us-ascii
// Sometimes the semi-colon is missing like in:
// mimetype.cpp: text/x-c charset=us-ascii
// And sometimes we only get the mime type. This apparently happens
// when 'file' believes that the file name is binary
// xdg-mime only outputs the MIME type.
// If there is no colon and there is a slash, this is hopefuly
// the mime type
if (result.find_first_of(":") == string::npos &&
result.find_first_of("/") != string::npos) {
return result;
}
// Else the result should begin with the file name. Get rid of it:
if (result.find(fn) != 0) {
// Garbage "file" output. Maybe the result of a charset
// conversion attempt?
LOGERR("mimetype: can't interpret 'file' output: [" << (result) << "]\n" );
return string();
}
result = result.substr(fn.size());
// Now should look like ": text/plain; charset=us-ascii"
// Split it, and take second field
list<string> res;
stringToStrings(result, res);
if (res.size() <= 1)
return string();
list<string>::iterator it = res.begin();
mime = *++it;
// Remove possible semi-colon at the end
trimstring(mime, " \t;");
// File -i will sometimes return strange stuff (ie: "very small file")
if(mime.find("/") == string::npos)
mime.clear();
}
#endif //USE_SYSTEM_FILE_COMMAND
return mime;
}
/// Guess mime type, first from suffix, then from file data. We also
/// have a list of suffixes that we don't touch at all.
string mimetype(const string &fn, const struct stat *stp,
RclConfig *cfg, bool usfc)
{
// Use stat data if available to check for non regular files
if (stp) {
// Note: the value used for directories is different from what
// file -i would print on Linux (inode/directory). Probably
// comes from bsd. Thos may surprise a user trying to use a
// 'mime:' filter with the query language, but it's not work
// changing (would force a reindex).
if (S_ISDIR(stp->st_mode))
return "inode/directory";
if (S_ISLNK(stp->st_mode))
return "inode/symlink";
if (!S_ISREG(stp->st_mode))
return "inode/x-fsspecial";
// Empty files are just this: avoid further errors with actual filters.
if (stp->st_size == 0)
return "inode/x-empty";
}
string mtype;
#ifndef _WIN32
// Extended attribute has priority on everything, as per:
// http://freedesktop.org/wiki/CommonExtendedAttributes
if (pxattr::get(fn, "mime_type", &mtype)) {
LOGDEB0("Mimetype: 'mime_type' xattr : [" << (mtype) << "]\n" );
if (mtype.empty()) {
LOGDEB0("Mimetype: getxattr() returned empty mime type !\n" );
} else {
return mtype;
}
}
#endif
if (cfg == 0) {
LOGERR("Mimetype: null config ??\n" );
return mtype;
}
if (cfg->inStopSuffixes(fn)) {
LOGDEB("mimetype: fn [" << (fn) << "] in stopsuffixes\n" );
return mtype;
}
// Compute file name suffix and search the mimetype map
string::size_type dot = fn.find_first_of(".");
while (dot != string::npos) {
string suff = stringtolower(fn.substr(dot));
mtype = cfg->getMimeTypeFromSuffix(suff);
if (!mtype.empty() || dot >= fn.size() - 1)
break;
dot = fn.find_first_of(".", dot + 1);
}
// If type was not determined from suffix, examine file data. Can
// only do this if we have an actual file (as opposed to a pure
// name).
if (mtype.empty() && stp)
mtype = mimetypefromdata(cfg, fn, usfc);
return mtype;
}
#else // TEST->
#include <stdio.h>
#include "safesysstat.h"
#include <cstdlib>
#include <iostream>
#include "log.h"
#include "rclconfig.h"
#include "rclinit.h"
#include "mimetype.h"
using namespace std;
int main(int argc, const char **argv)
{
string reason;
RclConfig *config = recollinit(0, 0, reason);
if (config == 0 || !config->ok()) {
string str = "Configuration problem: ";
str += reason;
fprintf(stderr, "%s\n", str.c_str());
exit(1);
}
while (--argc > 0) {
string filename = *++argv;
struct stat st;
if (lstat(filename.c_str(), &st)) {
fprintf(stderr, "Can't stat %s\n", filename.c_str());
continue;
}
cout << filename << " -> " <<
mimetype(filename, &st, config, true) << endl;
}
return 0;
}
#endif // TEST