xref: /haiku/src/kits/storage/mime/database_support.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /*
2  * Copyright 2002-2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  */
8 
9 /*!
10 	\file database_support.cpp
11 	Private mime database functions and constants
12 */
13 
14 #include <AppMisc.h>
15 #include <DataIO.h>
16 #include <Directory.h>
17 #include <Entry.h>
18 #include <FindDirectory.h>
19 #include <Message.h>
20 #include <Node.h>
21 #include <Path.h>
22 #include <storage_support.h>
23 #include <TypeConstants.h>
24 
25 #include <fs_attr.h>	// For struct attr_info
26 #include <new>			// For new(nothrow)
27 #include <stdio.h>
28 #include <string>
29 
30 #include "mime/database_support.h"
31 
32 //#define DBG(x) x
33 #define DBG(x)
34 #define OUT printf
35 
36 
37 namespace BPrivate {
38 namespace Storage {
39 namespace Mime {
40 
41 static const char *get_user_settings_dir(BPath &path);
42 
43 static BPath sSettingsDirPath;
44 static const std::string sSettingsDir = get_user_settings_dir(sSettingsDirPath);
45 
46 static const char *sHaikuDBDirName	= "beos_mime";
47 	// when running natively under Haiku
48 static const char *sBeOSDBDirName	= "obos_mime";
49 	// when running under BeOS
50 const std::string kDatabaseDir = sSettingsDir + "/"
51 	+ (is_running_on_haiku() ? sHaikuDBDirName : sBeOSDBDirName);
52 const std::string kApplicationDatabaseDir		= kDatabaseDir + "/application";
53 
54 #define ATTR_PREFIX "META:"
55 #define MINI_ICON_ATTR_PREFIX ATTR_PREFIX "M:"
56 #define LARGE_ICON_ATTR_PREFIX ATTR_PREFIX "L:"
57 
58 const char *kMiniIconAttrPrefix		= MINI_ICON_ATTR_PREFIX;
59 const char *kLargeIconAttrPrefix	= LARGE_ICON_ATTR_PREFIX;
60 const char *kIconAttrPrefix			= ATTR_PREFIX;
61 
62 // attribute names
63 const char *kFileTypeAttr			= "BEOS:TYPE";
64 const char *kTypeAttr				= ATTR_PREFIX "TYPE";
65 const char *kAppHintAttr			= ATTR_PREFIX "PPATH";
66 const char *kAttrInfoAttr			= ATTR_PREFIX "ATTR_INFO";
67 const char *kShortDescriptionAttr	= ATTR_PREFIX "S:DESC";
68 const char *kLongDescriptionAttr	= ATTR_PREFIX "L:DESC";
69 const char *kFileExtensionsAttr		= ATTR_PREFIX "EXTENS";
70 const char *kMiniIconAttr			= MINI_ICON_ATTR_PREFIX "STD_ICON";
71 const char *kLargeIconAttr			= LARGE_ICON_ATTR_PREFIX "STD_ICON";
72 const char *kIconAttr				= ATTR_PREFIX "ICON";
73 const char *kPreferredAppAttr		= ATTR_PREFIX "PREF_APP";
74 const char *kSnifferRuleAttr		= ATTR_PREFIX "SNIFF_RULE";
75 const char *kSupportedTypesAttr		= ATTR_PREFIX "FILE_TYPES";
76 
77 // attribute data types (as used in the R5 database)
78 const int32 kFileTypeType			= 'MIMS';	// B_MIME_STRING_TYPE
79 const int32 kTypeType				= B_STRING_TYPE;
80 const int32 kAppHintType			= 'MPTH';
81 const int32 kAttrInfoType			= B_MESSAGE_TYPE;
82 const int32 kShortDescriptionType	= 'MSDC';
83 const int32 kLongDescriptionType	= 'MLDC';
84 const int32 kFileExtensionsType		= B_MESSAGE_TYPE;
85 const int32 kMiniIconType			= B_MINI_ICON_TYPE;
86 const int32 kLargeIconType			= B_LARGE_ICON_TYPE;
87 const int32 kIconType				= B_VECTOR_ICON_TYPE;
88 const int32 kPreferredAppType		= 'MSIG';
89 const int32 kSnifferRuleType		= B_STRING_TYPE;
90 const int32 kSupportedTypesType		= B_MESSAGE_TYPE;
91 
92 // Message fields
93 const char *kApplicationsField				= "applications";
94 const char *kExtensionsField				= "extensions";
95 const char *kSupertypesField				= "super_types";
96 const char *kSupportingAppsSubCountField	= "be:sub";
97 const char *kSupportingAppsSuperCountField	= "be:super";
98 const char *kTypesField						= "types";
99 
100 // Mime types
101 const char *kGenericFileType	= "application/octet-stream";
102 const char *kDirectoryType		= "application/x-vnd.Be-directory";
103 const char *kSymlinkType		= "application/x-vnd.Be-symlink";
104 const char *kMetaMimeType		= "application/x-vnd.Be-meta-mime";
105 
106 // Error codes
107 const status_t kMimeGuessFailureError	= B_ERRORS_END+1;
108 
109 // get_settings_dir
110 /*!	\brief Sets the supplied BPath to the user settings directory and returns
111 		   it as C string.
112 	\param path BPath to be set to the user settings path.
113 	\return the user settings path as C string (\code path.Path() \endcode).
114 */
115 static
116 const char*
117 get_user_settings_dir(BPath &path)
118 {
119 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
120 		path.SetTo("/boot/home/config/settings");
121 	return path.Path();
122 }
123 
124 // type_to_filename
125 //! Converts the given MIME type to an absolute path in the MIME database.
126 std::string
127 type_to_filename(const char *type)
128 {
129 	return kDatabaseDir + "/" + BPrivate::Storage::to_lower(type);
130 }
131 
132 // open_type
133 /*! \brief Opens a BNode on the given type, failing if the type has no
134            corresponding file in the database.
135 	\param type The MIME type to open
136 	\param result Pointer to a pre-allocated BNode into which
137 	              is opened on the given MIME type
138 */
139 status_t
140 open_type(const char *type, BNode *result)
141 {
142 	if (type == NULL || result == NULL)
143 		return B_BAD_VALUE;
144 
145 	status_t status = result->SetTo(type_to_filename(type).c_str());
146 
147 	// TODO: this can be removed again later - we just didn't write this
148 	//	attribute is before	at all...
149 #if 1
150 	if (status == B_OK) {
151 		// check if the MIME:TYPE attribute exist, and create it if not
152 		attr_info info;
153 		if (result->GetAttrInfo(kTypeAttr, &info) != B_OK)
154 			result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1);
155 	}
156 #endif
157 
158 	return status;
159 }
160 
161 // open_or_create_type
162 /*! \brief Opens a BNode on the given type, creating a node of the
163 	       appropriate flavor if necessary.
164 
165 	All MIME types are converted to lowercase for use in the filesystem.
166 	\param type The MIME type to open
167 	\param result Pointer to a pre-allocated BNode into which
168 	              is opened on the given MIME type
169 */
170 status_t
171 open_or_create_type(const char *type, BNode *result, bool *didCreate)
172 {
173 	if (didCreate)
174 		*didCreate = false;
175 	std::string filename;
176 	std::string typeLower = BPrivate::Storage::to_lower(type);
177 	status_t err = (type && result ? B_OK : B_BAD_VALUE);
178 	if (!err) {
179 		filename = type_to_filename(type);
180 		err = result->SetTo(filename.c_str());
181 	}
182 	if (err == B_ENTRY_NOT_FOUND) {
183 		// Figure out what type of node we need to create
184 		// + Supertype == directory
185 		// + Non-supertype == file
186 		uint32 pos = typeLower.find_first_of('/');
187 		if (pos == std::string::npos) {
188 			// Supertype == directory
189 			BDirectory parent(kDatabaseDir.c_str());
190 			err = parent.InitCheck();
191 			if (!err)
192 				err = parent.CreateDirectory(typeLower.c_str(), NULL);
193 		} else {
194 			// Non-supertype == file
195 			std::string super(typeLower, 0, pos);
196 			std::string sub(typeLower, pos+1);
197 			BDirectory parent((kDatabaseDir + "/" + super).c_str());
198 			err = parent.InitCheck();
199 			if (!err)
200 				err = parent.CreateFile(sub.c_str(), NULL);
201 		}
202 		// Now try opening again
203 		if (err == B_OK)
204 			err = result->SetTo(filename.c_str());
205 		if (err == B_OK && didCreate)
206 			*didCreate = true;
207 		if (err == B_OK) {
208 			// write META:TYPE attribute
209 			result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1);
210 		}
211 	}
212 	return err;
213 }
214 
215 // read_mime_attr
216 /*! \brief Reads up to \c len bytes of the given data from the given attribute
217 	       for the given MIME type.
218 
219 	If no entry for the given type exists in the database, the function fails,
220 	and the contents of \c data are undefined.
221 
222 	\param type The MIME type
223 	\param attr The attribute name
224 	\param data Pointer to a memory buffer into which the data should be copied
225 	\param len The maximum number of bytes to read
226 	\param datatype The expected data type
227 	\return If successful, the number of bytes read is returned, otherwise, an
228 	        error code is returned.
229 */
230 ssize_t
231 read_mime_attr(const char *type, const char *attr, void *data,
232 	size_t len, type_code datatype)
233 {
234 	BNode node;
235 	ssize_t err = (type && attr && data ? B_OK : B_BAD_VALUE);
236 	if (!err)
237 		err = open_type(type, &node);
238 	if (!err)
239 		err = node.ReadAttr(attr, datatype, 0, data, len);
240 	return err;
241 }
242 
243 // read_mime_attr_message
244 /*! \brief Reads a flattened BMessage from the given attribute of the given
245 	MIME type.
246 
247 	If no entry for the given type exists in the database, or if the data
248 	stored in the attribute is not a flattened BMessage, the function fails
249 	and the contents of \c msg are undefined.
250 
251 	\param type The MIME type
252 	\param attr The attribute name
253 	\param data Pointer to a pre-allocated BMessage into which the attribute
254 			    data is unflattened.
255 */
256 status_t
257 read_mime_attr_message(const char *type, const char *attr, BMessage *msg)
258 {
259 	BNode node;
260 	attr_info info;
261 	char *buffer = NULL;
262 	ssize_t err = (type && attr && msg ? B_OK : B_BAD_VALUE);
263 	if (!err)
264 		err = open_type(type, &node);
265 	if (!err)
266 		err = node.GetAttrInfo(attr, &info);
267 	if (!err)
268 		err = info.type == B_MESSAGE_TYPE ? B_OK : B_BAD_VALUE;
269 	if (!err) {
270 		buffer = new(std::nothrow) char[info.size];
271 		if (!buffer)
272 			err = B_NO_MEMORY;
273 	}
274 	if (!err)
275 		err = node.ReadAttr(attr, B_MESSAGE_TYPE, 0, buffer, info.size);
276 	if (err >= 0)
277 		err = err == info.size ? (status_t)B_OK : (status_t)B_FILE_ERROR;
278 	if (!err)
279 		err = msg->Unflatten(buffer);
280 	delete [] buffer;
281 	return err;
282 }
283 
284 // read_mime_attr_string
285 /*! \brief Reads a BString from the given attribute of the given
286 	MIME type.
287 
288 	If no entry for the given type exists in the database, the function fails
289 	and the contents of \c str are undefined.
290 
291 	\param type The MIME type
292 	\param attr The attribute name
293 	\param str Pointer to a pre-allocated BString into which the attribute
294 			   data stored.
295 */
296 status_t
297 read_mime_attr_string(const char *type, const char *attr, BString *str)
298 {
299 	BNode node;
300 	status_t err = (type && attr && str ? B_OK : B_BAD_VALUE);
301 	if (!err)
302 		err = open_type(type, &node);
303 	if (!err)
304 		err = node.ReadAttrString(attr, str);
305 	return err;
306 }
307 
308 // write_mime_attr
309 /*! \brief Writes \c len bytes of the given data to the given attribute
310 	       for the given MIME type.
311 
312 	If no entry for the given type exists in the database, it is created.
313 
314 	\param type The MIME type
315 	\param attr The attribute name
316 	\param data Pointer to the data to write
317 	\param len The number of bytes to write
318 	\param datatype The data type of the given data
319 */
320 status_t
321 write_mime_attr(const char *type, const char *attr, const void *data,
322 	size_t len, type_code datatype, bool *didCreate)
323 {
324 	BNode node;
325 	status_t err = (type && attr && data ? B_OK : B_BAD_VALUE);
326 	if (!err)
327 		err = open_or_create_type(type, &node, didCreate);
328 	if (!err) {
329 		ssize_t bytes = node.WriteAttr(attr, datatype, 0, data, len);
330 		if (bytes < B_OK)
331 			err = bytes;
332 		else
333 			err = (bytes != (ssize_t)len ? (status_t)B_FILE_ERROR : (status_t)B_OK);
334 	}
335 	return err;
336 }
337 
338 // write_mime_attr_message
339 /*! \brief Flattens the given \c BMessage and writes it to the given attribute
340 	       of the given MIME type.
341 
342 	If no entry for the given type exists in the database, it is created.
343 
344 	\param type The MIME type
345 	\param attr The attribute name
346 	\param msg The BMessage to flatten and write
347 */
348 status_t
349 write_mime_attr_message(const char *type, const char *attr, const BMessage *msg, bool *didCreate)
350 {
351 	BNode node;
352 	BMallocIO data;
353 	ssize_t bytes;
354 	ssize_t err = (type && attr && msg ? B_OK : B_BAD_VALUE);
355 	if (!err)
356 		err = data.SetSize(msg->FlattenedSize());
357 	if (!err)
358 		err = msg->Flatten(&data, &bytes);
359 	if (!err)
360 		err = bytes == msg->FlattenedSize() ? B_OK : B_ERROR;
361 	if (!err)
362 		err = open_or_create_type(type, &node, didCreate);
363 	if (!err)
364 		err = node.WriteAttr(attr, B_MESSAGE_TYPE, 0, data.Buffer(), data.BufferLength());
365 	if (err >= 0)
366 		err = err == (ssize_t)data.BufferLength() ? (ssize_t)B_OK : (ssize_t)B_FILE_ERROR;
367 	return err;
368 }
369 
370 // delete_attribute
371 //! Deletes the given attribute for the given type
372 /*!
373 	\param type The mime type
374 	\param attr The attribute name
375 	\return
376     - B_OK: success
377     - B_ENTRY_NOT_FOUND: no such type or attribute
378     - "error code": failure
379 */
380 status_t
381 delete_attribute(const char *type, const char *attr)
382 {
383 	status_t err = type ? B_OK : B_BAD_VALUE;
384 	BNode node;
385 	if (!err)
386 		err = open_type(type, &node);
387 	if (!err)
388 		err = node.RemoveAttr(attr);
389 	return err;
390 }
391 
392 
393 
394 } // namespace Mime
395 } // namespace Storage
396 } // namespace BPrivate
397 
398