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