vfs: check userland buffers before reading them.
[haiku.git] / src / kits / locale / DefaultCatalog.cpp
bloba0f08abef9d9af96ece7592ce3e2621559b5ae4d
1 /*
2 * Copyright 2003-2009, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Oliver Tappe, zooey@hirschkaefer.de
7 * Adrien Destugues, pulkomandy@gmail.com
8 */
11 #include <memory>
12 #include <new>
14 #include <AppFileInfo.h>
15 #include <Application.h>
16 #include <DataIO.h>
17 #include <Directory.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <fs_attr.h>
21 #include <Message.h>
22 #include <Mime.h>
23 #include <Path.h>
24 #include <Resources.h>
25 #include <Roster.h>
27 #include <DefaultCatalog.h>
28 #include <MutableLocaleRoster.h>
30 #include <cstdio>
33 using std::auto_ptr;
34 using std::min;
35 using std::max;
36 using std::pair;
39 /*! This file implements the default catalog-type for the opentracker locale
40 kit. Alternatively, this could be used as a full add-on, but currently this
41 is provided as part of liblocale.so.
45 static const char *kCatFolder = "catalogs";
46 static const char *kCatExtension = ".catalog";
49 namespace BPrivate {
52 const char *DefaultCatalog::kCatMimeType
53 = "locale/x-vnd.Be.locale-catalog.default";
55 static int16 kCatArchiveVersion = 1;
56 // version of the catalog archive structure, bump this if you change it!
58 const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1;
59 // give highest priority to our embedded catalog-add-on
62 /*! Constructs a DefaultCatalog with given signature and language and reads
63 the catalog from disk.
64 InitCheck() will be B_OK if catalog could be loaded successfully, it will
65 give an appropriate error-code otherwise.
67 DefaultCatalog::DefaultCatalog(const entry_ref &catalogOwner, const char *language,
68 uint32 fingerprint)
70 HashMapCatalog("", language, fingerprint)
72 // We created the catalog with an invalid signature, but we fix that now.
73 SetSignature(catalogOwner);
74 status_t status;
76 // search for catalog living in sub-folder of app's folder:
77 node_ref nref;
78 nref.device = catalogOwner.device;
79 nref.node = catalogOwner.directory;
80 BDirectory appDir(&nref);
81 BString catalogName("locale/");
82 catalogName << kCatFolder
83 << "/" << fSignature
84 << "/" << fLanguageName
85 << kCatExtension;
86 BPath catalogPath(&appDir, catalogName.String());
87 status = ReadFromFile(catalogPath.Path());
89 if (status != B_OK) {
90 // search in data folders
92 directory_which which[] = {
93 B_USER_NONPACKAGED_DATA_DIRECTORY,
94 B_USER_DATA_DIRECTORY,
95 B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
96 B_SYSTEM_DATA_DIRECTORY
99 for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
100 BPath path;
101 if (find_directory(which[i], &path) == B_OK) {
102 BString catalogName(path.Path());
103 catalogName << "/locale/" << kCatFolder
104 << "/" << fSignature
105 << "/" << fLanguageName
106 << kCatExtension;
107 status = ReadFromFile(catalogName.String());
108 if (status == B_OK)
109 break;
114 if (status != B_OK) {
115 // give lowest priority to catalog embedded as resource in application
116 // executable, so they can be overridden easily.
117 status = ReadFromResource(catalogOwner);
120 fInitCheck = status;
124 /*! Constructs a DefaultCatalog and reads it from the resources of the
125 given entry-ref (which usually is an app- or add-on-file).
126 InitCheck() will be B_OK if catalog could be loaded successfully, it will
127 give an appropriate error-code otherwise.
129 DefaultCatalog::DefaultCatalog(entry_ref *appOrAddOnRef)
131 HashMapCatalog("", "", 0)
133 fInitCheck = ReadFromResource(*appOrAddOnRef);
137 /*! Constructs an empty DefaultCatalog with given sig and language.
138 This is used for editing/testing purposes.
139 InitCheck() will always be B_OK.
141 DefaultCatalog::DefaultCatalog(const char *path, const char *signature,
142 const char *language)
144 HashMapCatalog(signature, language, 0),
145 fPath(path)
147 fInitCheck = B_OK;
151 DefaultCatalog::~DefaultCatalog()
156 void
157 DefaultCatalog::SetSignature(const entry_ref &catalogOwner)
159 // figure out mimetype from image
160 BFile objectFile(&catalogOwner, B_READ_ONLY);
161 BAppFileInfo objectInfo(&objectFile);
162 char objectSignature[B_MIME_TYPE_LENGTH];
163 if (objectInfo.GetSignature(objectSignature) != B_OK) {
164 fSignature = "";
165 return;
168 // drop supertype from mimetype (should be "application/"):
169 char* stripSignature = objectSignature;
170 while (*stripSignature != '/' && *stripSignature != '\0')
171 stripSignature ++;
173 if (*stripSignature == '\0')
174 stripSignature = objectSignature;
175 else
176 stripSignature ++;
178 fSignature = stripSignature;
182 status_t
183 DefaultCatalog::SetRawString(const CatKey& key, const char *translated)
185 return fCatMap.Put(key, translated);
189 status_t
190 DefaultCatalog::ReadFromFile(const char *path)
192 if (!path)
193 path = fPath.String();
195 BFile catalogFile;
196 status_t res = catalogFile.SetTo(path, B_READ_ONLY);
197 if (res != B_OK)
198 return B_ENTRY_NOT_FOUND;
200 fPath = path;
202 off_t sz = 0;
203 res = catalogFile.GetSize(&sz);
204 if (res != B_OK) {
205 return res;
208 auto_ptr<char> buf(new(std::nothrow) char [sz]);
209 if (buf.get() == NULL)
210 return B_NO_MEMORY;
211 res = catalogFile.Read(buf.get(), sz);
212 if (res < B_OK)
213 return res;
214 if (res < sz)
215 return res;
216 BMemoryIO memIO(buf.get(), sz);
217 res = Unflatten(&memIO);
219 if (res == B_OK) {
220 // some information living in member variables needs to be copied
221 // to attributes. Although these attributes should have been written
222 // when creating the catalog, we make sure that they exist there:
223 UpdateAttributes(catalogFile);
226 return res;
231 * this method is not currently being used, but it may be useful in the future...
233 status_t
234 DefaultCatalog::ReadFromAttribute(const entry_ref &appOrAddOnRef)
236 BNode node;
237 status_t res = node.SetTo(&appOrAddOnRef);
238 if (res != B_OK)
239 return B_ENTRY_NOT_FOUND;
241 attr_info attrInfo;
242 res = node.GetAttrInfo(BLocaleRoster::kEmbeddedCatAttr, &attrInfo);
243 if (res != B_OK)
244 return B_NAME_NOT_FOUND;
245 if (attrInfo.type != B_MESSAGE_TYPE)
246 return B_BAD_TYPE;
248 size_t size = attrInfo.size;
249 auto_ptr<char> buf(new(std::nothrow) char [size]);
250 if (buf.get() == NULL)
251 return B_NO_MEMORY;
252 res = node.ReadAttr(BLocaleRoster::kEmbeddedCatAttr, B_MESSAGE_TYPE, 0,
253 buf.get(), size);
254 if (res < (ssize_t)size)
255 return res < B_OK ? res : B_BAD_DATA;
257 BMemoryIO memIO(buf.get(), size);
258 res = Unflatten(&memIO);
260 return res;
264 status_t
265 DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef)
267 BFile file;
268 status_t res = file.SetTo(&appOrAddOnRef, B_READ_ONLY);
269 if (res != B_OK)
270 return B_ENTRY_NOT_FOUND;
272 BResources rsrc;
273 res = rsrc.SetTo(&file);
274 if (res != B_OK)
275 return res;
277 size_t sz;
278 const void *buf = rsrc.LoadResource('CADA', fLanguageName, &sz);
279 if (!buf)
280 return B_NAME_NOT_FOUND;
282 BMemoryIO memIO(buf, sz);
283 res = Unflatten(&memIO);
285 return res;
289 status_t
290 DefaultCatalog::WriteToFile(const char *path)
292 BFile catalogFile;
293 if (path)
294 fPath = path;
295 status_t res = catalogFile.SetTo(fPath.String(),
296 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
297 if (res != B_OK)
298 return res;
300 BMallocIO mallocIO;
301 mallocIO.SetBlockSize(max(fCatMap.Size() * 20, (int32)256));
302 // set a largish block-size in order to avoid reallocs
303 res = Flatten(&mallocIO);
304 if (res == B_OK) {
305 ssize_t wsz;
306 wsz = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength());
307 if (wsz != (ssize_t)mallocIO.BufferLength())
308 return B_FILE_ERROR;
310 // set mimetype-, language- and signature-attributes:
311 UpdateAttributes(catalogFile);
313 if (res == B_OK)
314 UpdateAttributes(catalogFile);
315 return res;
320 * this method is not currently being used, but it may be useful in the
321 * future...
323 status_t
324 DefaultCatalog::WriteToAttribute(const entry_ref &appOrAddOnRef)
326 BNode node;
327 status_t res = node.SetTo(&appOrAddOnRef);
328 if (res != B_OK)
329 return res;
331 BMallocIO mallocIO;
332 mallocIO.SetBlockSize(max(fCatMap.Size() * 20, (int32)256));
333 // set a largish block-size in order to avoid reallocs
334 res = Flatten(&mallocIO);
336 if (res == B_OK) {
337 ssize_t wsz;
338 wsz = node.WriteAttr(BLocaleRoster::kEmbeddedCatAttr, B_MESSAGE_TYPE, 0,
339 mallocIO.Buffer(), mallocIO.BufferLength());
340 if (wsz < B_OK)
341 res = wsz;
342 else if (wsz != (ssize_t)mallocIO.BufferLength())
343 res = B_ERROR;
345 return res;
349 status_t
350 DefaultCatalog::WriteToResource(const entry_ref &appOrAddOnRef)
352 BFile file;
353 status_t res = file.SetTo(&appOrAddOnRef, B_READ_WRITE);
354 if (res != B_OK)
355 return res;
357 BResources rsrc;
358 res = rsrc.SetTo(&file);
359 if (res != B_OK)
360 return res;
362 BMallocIO mallocIO;
363 mallocIO.SetBlockSize(max(fCatMap.Size() * 20, (int32)256));
364 // set a largish block-size in order to avoid reallocs
365 res = Flatten(&mallocIO);
367 int mangledLanguage = CatKey::HashFun(fLanguageName.String(), 0);
369 if (res == B_OK) {
370 res = rsrc.AddResource('CADA', mangledLanguage,
371 mallocIO.Buffer(), mallocIO.BufferLength(),
372 BString(fLanguageName));
375 return res;
379 /*! Writes mimetype, language-name and signature of catalog into the
380 catalog-file.
382 void
383 DefaultCatalog::UpdateAttributes(BFile& catalogFile)
385 static const int bufSize = 256;
386 char buf[bufSize];
387 uint32 temp;
388 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf,
389 bufSize) <= 0
390 || strcmp(kCatMimeType, buf) != 0) {
391 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
392 kCatMimeType, strlen(kCatMimeType)+1);
394 if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
395 &buf, bufSize) <= 0
396 || fLanguageName != buf) {
397 catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
398 fLanguageName.String(), fLanguageName.Length()+1);
400 if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
401 &buf, bufSize) <= 0
402 || fSignature != buf) {
403 catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
404 fSignature.String(), fSignature.Length()+1);
406 if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
407 0, &temp, sizeof(uint32)) <= 0) {
408 catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
409 0, &fFingerprint, sizeof(uint32));
414 status_t
415 DefaultCatalog::Flatten(BDataIO *dataIO)
417 UpdateFingerprint();
418 // make sure we have the correct fingerprint before we flatten it
420 status_t res;
421 BMessage archive;
422 int32 count = fCatMap.Size();
423 res = archive.AddString("class", "DefaultCatalog");
424 if (res == B_OK)
425 res = archive.AddInt32("c:sz", count);
426 if (res == B_OK)
427 res = archive.AddInt16("c:ver", kCatArchiveVersion);
428 if (res == B_OK)
429 res = archive.AddString("c:lang", fLanguageName.String());
430 if (res == B_OK)
431 res = archive.AddString("c:sig", fSignature.String());
432 if (res == B_OK)
433 res = archive.AddInt32("c:fpr", fFingerprint);
434 if (res == B_OK)
435 res = archive.Flatten(dataIO);
437 CatMap::Iterator iter = fCatMap.GetIterator();
438 CatMap::Entry entry;
439 while (res == B_OK && iter.HasNext()) {
440 entry = iter.Next();
441 archive.MakeEmpty();
442 res = archive.AddString("c:ostr", entry.key.fString.String());
443 if (res == B_OK)
444 res = archive.AddString("c:ctxt", entry.key.fContext.String());
445 if (res == B_OK)
446 res = archive.AddString("c:comt", entry.key.fComment.String());
447 if (res == B_OK)
448 res = archive.AddInt32("c:hash", entry.key.fHashVal);
449 if (res == B_OK)
450 res = archive.AddString("c:tstr", entry.value.String());
451 if (res == B_OK)
452 res = archive.Flatten(dataIO);
455 return res;
459 status_t
460 DefaultCatalog::Unflatten(BDataIO *dataIO)
462 fCatMap.Clear();
463 int32 count = 0;
464 int16 version;
465 BMessage archiveMsg;
466 status_t res = archiveMsg.Unflatten(dataIO);
468 if (res == B_OK) {
469 res = archiveMsg.FindInt16("c:ver", &version)
470 || archiveMsg.FindInt32("c:sz", &count);
472 if (res == B_OK) {
473 fLanguageName = archiveMsg.FindString("c:lang");
474 fSignature = archiveMsg.FindString("c:sig");
475 uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr");
477 // if a specific fingerprint has been requested and the catalog does in
478 // fact have a fingerprint, both are compared. If they mismatch, we do
479 // not accept this catalog:
480 if (foundFingerprint != 0 && fFingerprint != 0
481 && foundFingerprint != fFingerprint) {
482 res = B_MISMATCHED_VALUES;
483 } else
484 fFingerprint = foundFingerprint;
487 if (res == B_OK && count > 0) {
488 CatKey key;
489 const char *keyStr;
490 const char *keyCtx;
491 const char *keyCmt;
492 const char *translated;
494 // fCatMap.resize(count);
495 // There is no resize method in Haiku's HashMap to preallocate
496 // memory.
497 for (int i=0; res == B_OK && i < count; ++i) {
498 res = archiveMsg.Unflatten(dataIO);
499 if (res == B_OK)
500 res = archiveMsg.FindString("c:ostr", &keyStr);
501 if (res == B_OK)
502 res = archiveMsg.FindString("c:ctxt", &keyCtx);
503 if (res == B_OK)
504 res = archiveMsg.FindString("c:comt", &keyCmt);
505 if (res == B_OK)
506 res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal);
507 if (res == B_OK)
508 res = archiveMsg.FindString("c:tstr", &translated);
509 if (res == B_OK) {
510 key.fString = keyStr;
511 key.fContext = keyCtx;
512 key.fComment = keyCmt;
513 fCatMap.Put(key, translated);
516 uint32 checkFP = ComputeFingerprint();
517 if (fFingerprint != checkFP)
518 return B_BAD_DATA;
520 return res;
524 BCatalogData *
525 DefaultCatalog::Instantiate(const entry_ref &catalogOwner, const char *language,
526 uint32 fingerprint)
528 DefaultCatalog *catalog
529 = new(std::nothrow) DefaultCatalog(catalogOwner, language, fingerprint);
530 if (catalog && catalog->InitCheck() != B_OK) {
531 delete catalog;
532 return NULL;
534 return catalog;
538 BCatalogData *
539 DefaultCatalog::Create(const char *signature, const char *language)
541 DefaultCatalog *catalog
542 = new(std::nothrow) DefaultCatalog("", signature, language);
543 if (catalog && catalog->InitCheck() != B_OK) {
544 delete catalog;
545 return NULL;
547 return catalog;
551 } // namespace BPrivate
554 extern "C" status_t
555 default_catalog_get_available_languages(BMessage* availableLanguages,
556 const char* sigPattern, const char* langPattern, int32 fingerprint)
558 if (availableLanguages == NULL || sigPattern == NULL)
559 return B_BAD_DATA;
561 app_info appInfo;
562 be_app->GetAppInfo(&appInfo);
563 node_ref nref;
564 nref.device = appInfo.ref.device;
565 nref.node = appInfo.ref.directory;
566 BDirectory appDir(&nref);
567 BString catalogName("locale/");
568 catalogName << kCatFolder
569 << "/" << sigPattern ;
570 BPath catalogPath(&appDir, catalogName.String());
571 BEntry file(catalogPath.Path());
572 BDirectory dir(&file);
574 char fileName[B_FILE_NAME_LENGTH];
575 while(dir.GetNextEntry(&file) == B_OK) {
576 file.GetName(fileName);
577 BString langName(fileName);
578 langName.Replace(kCatExtension, "", 1);
579 availableLanguages->AddString("language", langName);
582 // search in data folders
584 directory_which which[] = {
585 B_USER_NONPACKAGED_DATA_DIRECTORY,
586 B_USER_DATA_DIRECTORY,
587 B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
588 B_SYSTEM_DATA_DIRECTORY
591 for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
592 BPath path;
593 if (find_directory(which[i], &path) == B_OK) {
594 catalogName = BString("locale/")
595 << kCatFolder
596 << "/" << sigPattern;
598 BPath catalogPath(path.Path(), catalogName.String());
599 BEntry file(catalogPath.Path());
600 BDirectory dir(&file);
602 char fileName[B_FILE_NAME_LENGTH];
603 while(dir.GetNextEntry(&file) == B_OK) {
604 file.GetName(fileName);
605 BString langName(fileName);
606 langName.Replace(kCatExtension, "", 1);
607 availableLanguages->AddString("language", langName);
612 return B_OK;