vfs: check userland buffers before reading them.
[haiku.git] / src / servers / registrar / RecentEntries.cpp
blob06ac2fd1ecf12d5bc19dbe219824a20c679fec9e
1 /*
2 * Copyright 2001-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Tyler Dauwalder
7 * Ingo Weinhold, bonefish@users.sf.net
8 * Axel Dörfler, axeld@pinc-software.de
9 */
12 //! Recently launched apps list
15 #include "RecentEntries.h"
17 #include <new>
18 #include <map>
20 #include <strings.h>
22 #include <AppFileInfo.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <Message.h>
26 #include <Mime.h>
27 #include <Path.h>
28 #include <Roster.h>
29 #include <String.h>
31 #include <storage_support.h>
33 #include "Debug.h"
36 using namespace std;
39 /*! \struct recent_entry
41 \brief A recent entry, the corresponding signature of the application
42 that launched/used/opened/viewed/whatevered it, and an index used for
43 keeping track of orderings when loading/storing the recent entries list
44 from/to disk.
48 /*! \brief Creates a new recent_entry object.
50 recent_entry::recent_entry(const entry_ref *ref, const char *appSig,
51 uint32 index)
53 ref(ref ? *ref : entry_ref()),
54 sig(appSig),
55 index(index)
60 // #pragma mark -
63 /*! \class RecentEntries
64 \brief Implements the common functionality used by the roster's recent
65 folders and recent documents lists.
69 /*! \var std::list<std::string> RecentEntries::fEntryList
70 \brief The list of entries and their corresponding app sigs, most recent first
72 The signatures are expected to be stored all lowercase, as MIME
73 signatures are case-independent.
77 /*! \brief Creates a new list.
79 The list is initially empty.
81 RecentEntries::RecentEntries()
86 /*! \brief Frees all resources associated with the object.
88 RecentEntries::~RecentEntries()
90 Clear();
94 /*! \brief Places the given entry Places the app with the given signature at the front of
95 the recent apps list.
97 If the app already exists elsewhere in the list, that item is
98 removed so only one instance exists in the list at any time.
100 \param appSig The application's signature
101 \param appFlags The application's flags. If \a appFlags contains
102 either \c B_ARGV_ONLY or \c B_BACKGROUND_APP, the
103 application is \b not added to the list (but \c B_OK
104 is still returned).
105 \return
106 - \c B_OK: success (even if the app was not added due to appFlags)
107 - error code: failure
109 status_t
110 RecentEntries::Add(const entry_ref *ref, const char *appSig)
112 if (ref == NULL || appSig == NULL)
113 return B_BAD_VALUE;
115 // Look for a previous instance of this entry
116 std::list<recent_entry*>::iterator item;
117 for (item = fEntryList.begin(); item != fEntryList.end(); item++) {
118 if ((*item)->ref == *ref && !strcasecmp((*item)->sig.c_str(), appSig)) {
119 fEntryList.erase(item);
120 break;
124 // Add this entry to the front of the list
125 recent_entry *entry = new (nothrow) recent_entry(ref, appSig, 0);
126 if (entry == NULL)
127 return B_NO_MEMORY;
129 try {
130 fEntryList.push_front(entry);
131 } catch (...) {
132 return B_NO_MEMORY;
135 return B_OK;
139 /*! \brief Returns the first \a maxCount recent apps in the \c BMessage
140 pointed to by \a list.
142 The message is cleared first, and \c entry_refs for the the apps are
143 stored in the \c "refs" field of the message (\c B_REF_TYPE).
145 If there are fewer than \a maxCount items in the list, the entire
146 list is returned.
148 Duplicate entries are never returned, i.e. if two instances of the
149 same entry were added under different app sigs, and both instances
150 match the given filter criterion, only the most recent instance is
151 returned; the latter instance is ignored and not counted towards
152 the \a maxCount number of entries to return.
154 Since BRoster::GetRecentEntries() returns \c void, the message pointed
155 to by \a list is simply cleared if maxCount is invalid (i.e. <= 0).
157 \param fileTypes An array of file type filters. These file types are
158 expected to be all lowercase.
160 status_t
161 RecentEntries::Get(int32 maxCount, const char *fileTypes[],
162 int32 fileTypesCount, const char *appSig, BMessage *result)
164 if (result == NULL
165 || fileTypesCount < 0
166 || (fileTypesCount > 0 && fileTypes == NULL))
167 return B_BAD_VALUE;
169 result->MakeEmpty();
171 std::list<recent_entry*> duplicateList;
172 std::list<recent_entry*>::iterator item;
173 status_t error = B_OK;
174 int count = 0;
176 for (item = fEntryList.begin();
177 error == B_OK && count < maxCount && item != fEntryList.end();
178 item++) {
179 // Filter by app sig
180 if (appSig != NULL && strcasecmp((*item)->sig.c_str(), appSig))
181 continue;
183 // Filter by file type
184 if (fileTypesCount > 0) {
185 char type[B_MIME_TYPE_LENGTH];
186 if (GetTypeForRef(&(*item)->ref, type) == B_OK) {
187 bool match = false;
188 for (int i = 0; i < fileTypesCount; i++) {
189 if (!strcasecmp(type, fileTypes[i])) {
190 match = true;
191 break;
194 if (!match)
195 continue;
199 // Check for duplicates
200 bool duplicate = false;
201 for (std::list<recent_entry*>::iterator dupItem = duplicateList.begin();
202 dupItem != duplicateList.end(); dupItem++) {
203 if ((*dupItem)->ref == (*item)->ref) {
204 duplicate = true;
205 break;
208 if (duplicate)
209 continue;
211 // Add the ref to the list used to check
212 // for duplicates, and then to the result
213 try {
214 duplicateList.push_back(*item);
215 } catch (...) {
216 error = B_NO_MEMORY;
218 if (error == B_OK)
219 error = result->AddRef("refs", &(*item)->ref);
220 if (error == B_OK)
221 count++;
224 return error;
228 /*! \brief Clears the list of recently launched apps
230 status_t
231 RecentEntries::Clear()
233 std::list<recent_entry*>::iterator i;
234 for (i = fEntryList.begin(); i != fEntryList.end(); i++) {
235 delete *i;
237 fEntryList.clear();
238 return B_OK;
242 /*! \brief Dumps the the current list of entries to stdout.
244 status_t
245 RecentEntries::Print()
247 std::list<recent_entry*>::iterator item;
248 int counter = 1;
249 for (item = fEntryList.begin(); item != fEntryList.end(); item++) {
250 printf("%d: device == '%" B_PRIdDEV "', dir == '%" B_PRIdINO "', "
251 "name == '%s', app == '%s', index == %" B_PRId32 "\n", counter++,
252 (*item)->ref.device, (*item)->ref.directory, (*item)->ref.name,
253 (*item)->sig.c_str(), (*item)->index);
255 return B_OK;
259 status_t
260 RecentEntries::Save(FILE* file, const char *description, const char *tag)
262 if (file == NULL || description == NULL || tag == NULL)
263 return B_BAD_VALUE;
265 fprintf(file, "# %s\n", description);
267 /* In order to write our entries out in the format used by the
268 Roster settings file, we need to collect all the signatures
269 for each entry in one place, while at the same time updating
270 the index values for each entry/sig pair to reflect the current
271 ordering of the list. I believe this is the data structure
272 R5 actually maintains all the time, as their indices do not
273 change over time (whereas ours will). If our implementation
274 proves to be slower that R5, we may want to consider using
275 the data structure pervasively.
277 std::map<entry_ref, std::list<recent_entry*> > map;
278 uint32 count = fEntryList.size();
280 try {
281 for (std::list<recent_entry*>::iterator item = fEntryList.begin();
282 item != fEntryList.end(); count--, item++) {
283 recent_entry *entry = *item;
284 if (entry) {
285 entry->index = count;
286 map[entry->ref].push_back(entry);
287 } else {
288 D(PRINT("WARNING: RecentEntries::Save(): The entry %ld entries "
289 "from the front of fEntryList was found to be NULL\n",
290 fEntryList.size() - count));
293 } catch (...) {
294 return B_NO_MEMORY;
297 for (std::map<entry_ref, std::list<recent_entry*> >::iterator mapItem = map.begin();
298 mapItem != map.end(); mapItem++) {
299 // We're going to need to properly escape the path name we
300 // get, which will at absolute worst double the length of
301 // the string.
302 BPath path;
303 char escapedPath[B_PATH_NAME_LENGTH*2];
304 status_t outputError = path.SetTo(&mapItem->first);
305 if (!outputError) {
306 BPrivate::Storage::escape_path(path.Path(), escapedPath);
307 fprintf(file, "%s %s", tag, escapedPath);
308 std::list<recent_entry*> &list = mapItem->second;
309 int32 i = 0;
310 for (std::list<recent_entry*>::iterator item = list.begin();
311 item != list.end(); i++, item++) {
312 recent_entry *entry = *item;
313 if (entry) {
314 fprintf(file, " \"%s\" %" B_PRId32, entry->sig.c_str(),
315 entry->index);
316 } else {
317 D(PRINT("WARNING: RecentEntries::Save(): The entry %"
318 B_PRId32 " entries from the front of the compiled "
319 "recent_entry* list for the entry ref (%" B_PRId32 ", %"
320 B_PRId64 ", '%s') was found to be NULL\n", i,
321 mapItem->first.device, mapItem->first.directory,
322 mapItem->first.name));
325 fprintf(file, "\n");
326 } else {
327 D(PRINT("WARNING: RecentEntries::Save(): entry_ref_to_path() "
328 "failed on the entry_ref (%" B_PRId32", %" B_PRId64 ", '%s') "
329 "with error 0x%" B_PRIx32 "\n",
330 mapItem->first.device, mapItem->first.directory,
331 mapItem->first.name, outputError));
335 fprintf(file, "\n");
336 return B_OK;
340 // GetTypeForRef
341 /*! \brief Fetches the file type of the given file.
343 If the file has no type, an empty string is returned. The file
344 is *not* sniffed.
346 status_t
347 RecentEntries::GetTypeForRef(const entry_ref *ref, char *result)
349 if (ref == NULL || result == NULL)
350 return B_BAD_VALUE;
352 // Read the type
353 BNode node;
354 status_t error = node.SetTo(ref);
355 if (error == B_OK) {
356 ssize_t bytes = node.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE,
357 0, result, B_MIME_TYPE_LENGTH - 1);
358 if (bytes < B_OK)
359 error = bytes;
360 else
361 result[bytes] = '\0';
364 return error;