1//----------------------------------------------------------------------
2//  This software is part of the OpenBeOS distribution and is covered
3//  by the MIT License.
4//---------------------------------------------------------------------
5/*!
6	\file MimeType.cpp
7	BMimeType implementation.
8*/
9#include "MimeType.h"
10
11#include <Bitmap.h>
12
13#include <ctype.h>			// For tolower()
14#include <new>			// For new(nothrow)
15#include <stdio.h>			// For printf()
16#include <string.h>			// For strncpy()
17
18using namespace BPrivate;
19
20// Private helper functions
21bool isValidMimeChar(const char ch);
22status_t toLower(const char *str, char *result);
23
24//using namespace BPrivate::Storage::Mime;
25using namespace std;
26
27const char *B_PEF_APP_MIME_TYPE		= "application/x-be-executable";
28const char *B_PE_APP_MIME_TYPE		= "application/x-vnd.be-peexecutable";
29const char *B_ELF_APP_MIME_TYPE		= "application/x-vnd.be-elfexecutable";
30const char *B_RESOURCE_MIME_TYPE	= "application/x-be-resource";
31const char *B_FILE_MIME_TYPE		= "application/octet-stream";
32// Might be defined platform depended, but ELF will certainly be the common
33// format for all platforms anyway.
34const char *B_APP_MIME_TYPE			= B_ELF_APP_MIME_TYPE;
35
36// constructor
37/*!	\brief Creates an uninitialized BMimeType object.
38*/
39BMimeType::BMimeType()
40	: fType(NULL)
41	, fCStatus(B_NO_INIT)
42{
43}
44
45// constructor
46/*!	\brief Creates a BMimeType object and initializes it to the supplied
47	MIME type.
48	The supplied string must specify a valid MIME type or supertype.
49	\see SetTo() for further information.
50	\param mimeType The MIME string.
51*/
52BMimeType::BMimeType(const char *mimeType)
53	: fType(NULL)
54	, fCStatus(B_NO_INIT)
55{
56	SetTo(mimeType);
57}
58
59// destructor
60/*!	\brief Frees all resources associated with this object.
61*/
62BMimeType::~BMimeType()
63{
64	Unset();
65}
66
67// SetTo
68/*!	\brief Initializes this object to the supplied MIME type.
69	The supplied string must specify a valid MIME type or supertype.
70	Valid MIME types are given by the following grammar:
71	MIMEType	::= Supertype "/" [ Subtype ]
72	Supertype	::= "application" | "audio" | "image" | "message"
73					| "multipart" | "text" | "video"
74	Subtype		::= MIMEChar MIMEChar*
75	MIMEChar	::= any character except white spaces, CTLs and '/', '<', '>',
76					'@',, ',', ';', ':', '"', '(', ')', '[', ']', '?', '=', '\'
77					(Note: RFC1341 also forbits '.', but it is allowed here.)
78
79	Currently the supertype is not restricted to one of the seven types given,
80	but can be an arbitrary string (obeying the same rule as the subtype).
81	Nevertheless it is a very bad idea to use another supertype.
82	The supplied MIME string is copied; the caller retains the ownership.
83	\param mimeType The MIME string.
84	\return
85	- \c B_OK: Everything went fine.
86	- \c B_BAD_VALUE: \c NULL or invalid \a mimeString.
87	- \c B_NO_MEMORY: Insufficient memory to copy the MIME string.
88*/
89status_t
90BMimeType::SetTo(const char *mimeType)
91{
92	if (!mimeType || !BMimeType::IsValid(mimeType)) {
93		fCStatus = B_BAD_VALUE;
94	} else {
95		Unset();
96		fType = new(std::nothrow) char[strlen(mimeType)+1];
97		if (fType) {
98			strcpy(fType, mimeType);
99			fCStatus = B_OK;
100		} else {
101			fCStatus = B_NO_MEMORY;
102		}
103	}
104	return fCStatus;
105}
106
107// Unset
108/*!	\brief Returns the object to an uninitialized state.
109*/
110void
111BMimeType::Unset()
112{
113	if (fType)
114		delete [] fType;
115	fType = NULL;
116	fCStatus = B_NO_INIT;
117}
118
119// InitCheck
120/*!	Returns the result of the most recent constructor or SetTo() call.
121	\return
122	- \c B_OK: The object is properly initialized.
123	- A specific error code otherwise.
124*/
125status_t
126BMimeType::InitCheck() const
127{
128	return fCStatus;
129}
130
131// Type
132/*!	\brief Returns the MIME string represented by this object.
133	\return The MIME string, if the object is properly initialized, \c NULL
134			otherwise.
135*/
136const char *
137BMimeType::Type() const
138{
139	return fType;
140}
141
142// IsValid
143/*!	\brief Returns whether the object represents a valid MIME type.
144	\see SetTo() for further information.
145	\return \c true, if the object is properly initialized, \c false
146			otherwise.
147*/
148bool
149BMimeType::IsValid() const
150{
151	return InitCheck() == B_OK && BMimeType::IsValid(Type());
152}
153
154// IsSupertypeOnly
155/*!	\brief Returns whether this objects represents a supertype.
156	\return \c true, if the object is properly initialized and represents a
157			supertype, \c false otherwise.
158*/
159bool
160BMimeType::IsSupertypeOnly() const
161{
162	if (fCStatus == B_OK) {
163		// We assume here fCStatus will be B_OK *only* if
164		// the MIME string is valid
165		int len = strlen(fType);
166		for (int i = 0; i < len; i++) {
167			if (fType[i] == '/')
168				return false;
169		}
170		return true;
171	} else
172		return false;
173}
174
175// IsInstalled
176//! Returns whether or not this type is currently installed in the MIME database
177/*! To add the MIME type to the database, call \c Install().
178	To remove the MIME type from the database, call \c Delete().
179
180	\return
181	- \c true: The MIME type is currently installed in the database
182	- \c false: The MIME type is not currently installed in the database
183*/
184// bool
185// BMimeType::IsInstalled() const
186// {
187// 	return InitCheck() == B_OK && is_installed(Type());
188// }
189
190// GetSupertype
191/*!	\brief Returns the supertype of the MIME type represented by this object.
192	The supplied object is initialized to this object's supertype. If this
193	BMimeType is not properly initialized, the supplied object will be Unset().
194	\param superType A pointer to the BMimeType object that shall be
195		   initialized to this object's supertype.
196	\return
197	- \c B_OK: Everything went fine.
198	- \c B_BAD_VALUE: \c NULL \a superType, this object is not initialized,
199	  or this object <i> is </i> a supertype.
200*/
201status_t
202BMimeType::GetSupertype(BMimeType *superType) const
203{
204	if (!superType)
205		return B_BAD_VALUE;
206	superType->Unset();
207
208	status_t err = (fCStatus == B_OK ? B_OK : B_BAD_VALUE);
209	if (!err) {
210		int len = strlen(fType);
211		int i;
212		for (i = 0; i < len; i++) {
213			if (fType[i] == '/')
214				break;
215		}
216		if (i == len)
217			err = B_BAD_VALUE;		// IsSupertypeOnly() == true
218		else {
219			char superMime[B_MIME_TYPE_LENGTH];
220			strncpy(superMime, fType, i);
221			superMime[i] = 0;
222			err = superType->SetTo(superMime);
223		}
224	}
225	return err;
226}
227
228// ==
229/*!	\brief Returns whether this and the supplied MIME type are equal.
230	Two BMimeType objects are said to be equal, if they represent the same
231	MIME string, ignoring case, or if both are not initialized.
232	\param type The BMimeType to be compared with.
233	\return \c true, if the objects are equal, \c false otherwise.
234*/
235bool
236BMimeType::operator==(const BMimeType &type) const
237{
238	char lower1[B_MIME_TYPE_LENGTH];
239	char lower2[B_MIME_TYPE_LENGTH];
240
241	if (InitCheck() == B_OK && type.InitCheck() == B_OK) {
242		status_t err = toLower(Type(), lower1);
243		if (!err)
244			err = toLower(type.Type(), lower2);
245		if (!err)
246			err = (strcmp(lower1, lower2) == 0 ? B_OK : B_ERROR);
247		return err == B_OK;
248	} else if (InitCheck() == B_NO_INIT && type.InitCheck() == B_NO_INIT) {
249		return true;
250	} else {
251		return false;
252	}
253}
254
255// ==
256/*!	\brief Returns whether this and the supplied MIME type are equal.
257	A BMimeType objects equals a MIME string, if its MIME string equals the
258	latter one, ignoring case, or if it is uninitialized and the MIME string
259	is \c NULL.
260	\param type The MIME string to be compared with.
261	\return \c true, if the MIME types are equal, \c false otherwise.
262*/
263bool
264BMimeType::operator==(const char *type) const
265{
266	BMimeType mime;
267	if (type)
268		mime.SetTo(type);
269	return (*this) == mime;
270}
271
272// Contains
273/*!	\brief Returns whether this MIME type is a supertype of or equals the
274	supplied one.
275	\param type The MIME type.
276	\return \c true, if this MIME type is a supertype of or equals the
277			supplied one, \c false otherwise.
278*/
279bool
280BMimeType::Contains(const BMimeType *type) const
281{
282	if (!type)
283		return false;
284	if (*this == *type)
285		return true;
286	BMimeType super;
287	if (type->GetSupertype(&super) == B_OK && *this == super)
288		return true;
289	return false;
290}
291
292// IsValid
293/*!	\brief Returns whether the given string represents a valid MIME type.
294	\see SetTo() for further information.
295	\return \c true, if the given string represents a valid MIME type.
296*/
297bool
298BMimeType::IsValid(const char *string)
299{
300	if (!string)
301		return false;
302
303	bool foundSlash = false;
304	int len = strlen(string);
305	if (len >= B_MIME_TYPE_LENGTH || len == 0)
306		return false;
307
308	for (int i = 0; i < len; i++) {
309		char ch = string[i];
310		if (ch == '/') {
311			if (foundSlash || i == 0 || i == len-1)
312				return false;
313			else
314				foundSlash = true;
315		} else if (!isValidMimeChar(ch)) {
316			return false;
317		}
318	}
319	return true;
320}
321
322bool isValidMimeChar(const char ch)
323{
324	return    ch > 32		// Handles white space and most CTLs
325	       && ch != '/'
326	       && ch != '<'
327	       && ch != '>'
328	       && ch != '@'
329	       && ch != ','
330	       && ch != ';'
331	       && ch != ':'
332	       && ch != '"'
333	       && ch != '('
334	       && ch != ')'
335	       && ch != '['
336	       && ch != ']'
337	       && ch != '?'
338	       && ch != '='
339	       && ch != '\\'
340	       && ch != 127;	// DEL
341}
342
343// SetType
344/*!	\brief Initializes this object to the supplied MIME type.
345	\deprecated This method has the same semantics as SetTo().
346				Use SetTo() instead.
347*/
348status_t
349BMimeType::SetType(const char *mimeType)
350{
351	return SetTo(mimeType);
352}
353
354// GuessMimeType
355status_t
356BMimeType::GuessMimeType(const entry_ref *ref, BMimeType *type)
357{
358	if (!ref || !type)
359		return B_BAD_VALUE;
360
361	// get BEntry
362	BEntry entry;
363	status_t error = entry.SetTo(ref);
364	if (error != B_OK)
365		return error;
366
367	// does entry exist?
368	if (!entry.Exists())
369		return B_ENTRY_NOT_FOUND;
370
371	// check entry type
372	if (entry.IsDirectory())
373		return type->SetType("application/x-vnd.be-directory");
374	if (entry.IsSymLink())
375		return type->SetType("application/x-vnd.be-symlink");
376	if (!entry.IsFile())
377		return B_ERROR;
378
379	// we have a file, read the first 4 bytes
380	BFile file;
381	char buffer[4];
382	if (file.SetTo(ref, B_READ_ONLY) == B_OK
383		&& file.Read(buffer, 4) == 4) {
384		return GuessMimeType(buffer, 4, type);
385	}
386
387	// we couldn't open or read the file
388	return type->SetType(B_FILE_MIME_TYPE);
389}
390
391// GuessMimeType
392status_t
393BMimeType::GuessMimeType(const void *_buffer, int32 length, BMimeType *type)
394{
395	const uint8 *buffer = (const uint8*)_buffer;
396	if (!buffer || !type)
397		return B_BAD_VALUE;
398
399	// we only know ELF files
400	if (length >= 4 && buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L'
401		&&  buffer[3] == 'F') {
402		return type->SetType(B_ELF_APP_MIME_TYPE);
403	}
404
405	return type->SetType(B_FILE_MIME_TYPE);
406}
407
408// GuessMimeType
409status_t
410BMimeType::GuessMimeType(const char *filename, BMimeType *type)
411{
412	if (!filename || !type)
413		return B_BAD_VALUE;
414
415	entry_ref ref;
416	status_t error = get_ref_for_path(filename, &ref);
417	return (error == B_OK ? GuessMimeType(&ref, type) : error);
418}
419
420void BMimeType::_ReservedMimeType1() {}
421void BMimeType::_ReservedMimeType2() {}
422void BMimeType::_ReservedMimeType3() {}
423
424// =
425/*!	\brief Unimplemented assignment operator.
426*/
427BMimeType &
428BMimeType::operator=(const BMimeType &)
429{
430	return *this;	// not implemented
431}
432
433// copy constructor
434/*!	\brief Unimplemented copy constructor.
435*/
436BMimeType::BMimeType(const BMimeType &)
437{
438}
439
440
441// Returns a lowercase version of str in result. Result must
442// be preallocated and is assumed to be of adequate length.
443status_t
444toLower(const char *str, char *result) {
445	if (!str || !result)
446		return B_BAD_VALUE;
447	int len = strlen(str);
448	int i;
449	for (i = 0; i < len; i++)
450		result[i] = tolower(str[i]);
451	result[i] = 0;
452	return B_OK;
453}
454
455
456
457