uriloader/exthandler/unix/nsOSHelperAppService.cpp

branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
equal deleted inserted replaced
-1:000000000000 0:b75585e3a67c
1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9
10 #if defined(MOZ_ENABLE_CONTENTACTION)
11 #include <contentaction/contentaction.h>
12 #include <QString>
13 #endif
14
15 #include "nsOSHelperAppService.h"
16 #include "nsMIMEInfoUnix.h"
17 #ifdef MOZ_WIDGET_GTK
18 #include "nsGNOMERegistry.h"
19 #endif
20 #include "nsISupports.h"
21 #include "nsString.h"
22 #include "nsReadableUtils.h"
23 #include "nsUnicharUtils.h"
24 #include "nsXPIDLString.h"
25 #include "nsIURL.h"
26 #include "nsIFileStreams.h"
27 #include "nsILineInputStream.h"
28 #include "nsIFile.h"
29 #include "nsIProcess.h"
30 #include "nsNetCID.h"
31 #include "nsXPCOM.h"
32 #include "nsISupportsPrimitives.h"
33 #include "nsCRT.h"
34 #include "nsDirectoryServiceDefs.h"
35 #include "nsDirectoryServiceUtils.h"
36 #include "prenv.h" // for PR_GetEnv()
37 #include "nsAutoPtr.h"
38 #include "mozilla/Preferences.h"
39
40 using namespace mozilla;
41
42 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args)
43 #define LOG_ENABLED() PR_LOG_TEST(mLog, PR_LOG_DEBUG)
44
45 static nsresult
46 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
47 const nsAString::const_iterator& aEnd_iter);
48 static nsresult
49 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
50 nsAString::const_iterator& aMajorTypeStart,
51 nsAString::const_iterator& aMajorTypeEnd,
52 nsAString::const_iterator& aMinorTypeStart,
53 nsAString::const_iterator& aMinorTypeEnd,
54 const nsAString::const_iterator& aEnd_iter);
55
56 inline bool
57 IsNetscapeFormat(const nsACString& aBuffer);
58
59 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
60 {
61 mode_t mask = umask(0777);
62 umask(mask);
63 mPermissions = 0666 & ~mask;
64 }
65
66 nsOSHelperAppService::~nsOSHelperAppService()
67 {}
68
69 /*
70 * Take a command with all the mailcap escapes in it and unescape it
71 * Ideally this needs the mime type, mime type options, and location of the
72 * temporary file, but this last can't be got from here
73 */
74 // static
75 nsresult
76 nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand,
77 const nsAString& aMajorType,
78 const nsAString& aMinorType,
79 nsACString& aUnEscapedCommand) {
80 LOG(("-- UnescapeCommand"));
81 LOG(("Command to escape: '%s'\n",
82 NS_LossyConvertUTF16toASCII(aEscapedCommand).get()));
83 // XXX This function will need to get the mime type and various stuff like that being passed in to work properly
84
85 LOG(("UnescapeCommand really needs some work -- it should actually do some unescaping\n"));
86
87 CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand);
88 LOG(("Escaped command: '%s'\n",
89 PromiseFlatCString(aUnEscapedCommand).get()));
90 return NS_OK;
91 }
92
93 /* Put aSemicolon_iter at the first non-escaped semicolon after
94 * aStart_iter but before aEnd_iter
95 */
96
97 static nsresult
98 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
99 const nsAString::const_iterator& aEnd_iter) {
100 bool semicolonFound = false;
101 while (aSemicolon_iter != aEnd_iter && !semicolonFound) {
102 switch(*aSemicolon_iter) {
103 case '\\':
104 aSemicolon_iter.advance(2);
105 break;
106 case ';':
107 semicolonFound = true;
108 break;
109 default:
110 ++aSemicolon_iter;
111 break;
112 }
113 }
114 return NS_OK;
115 }
116
117 static nsresult
118 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
119 nsAString::const_iterator& aMajorTypeStart,
120 nsAString::const_iterator& aMajorTypeEnd,
121 nsAString::const_iterator& aMinorTypeStart,
122 nsAString::const_iterator& aMinorTypeEnd,
123 const nsAString::const_iterator& aEnd_iter) {
124 nsAString::const_iterator iter(aStart_iter);
125
126 // skip leading whitespace
127 while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) {
128 ++iter;
129 }
130
131 if (iter == aEnd_iter) {
132 return NS_ERROR_INVALID_ARG;
133 }
134
135 aMajorTypeStart = iter;
136
137 // find major/minor separator ('/')
138 while (iter != aEnd_iter && *iter != '/') {
139 ++iter;
140 }
141
142 if (iter == aEnd_iter) {
143 return NS_ERROR_INVALID_ARG;
144 }
145
146 aMajorTypeEnd = iter;
147
148 // skip '/'
149 ++iter;
150
151 if (iter == aEnd_iter) {
152 return NS_ERROR_INVALID_ARG;
153 }
154
155 aMinorTypeStart = iter;
156
157 // find end of minor type, delimited by whitespace or ';'
158 while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') {
159 ++iter;
160 }
161
162 aMinorTypeEnd = iter;
163
164 return NS_OK;
165 }
166
167 // static
168 nsresult
169 nsOSHelperAppService::GetFileLocation(const char* aPrefName,
170 const char* aEnvVarName,
171 nsAString& aFileLocation) {
172 LOG(("-- GetFileLocation. Pref: '%s' EnvVar: '%s'\n",
173 aPrefName,
174 aEnvVarName));
175 NS_PRECONDITION(aPrefName, "Null pref name passed; don't do that!");
176
177 aFileLocation.Truncate();
178 /* The lookup order is:
179 1) user pref
180 2) env var
181 3) pref
182 */
183 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
184
185 /*
186 If we have an env var we should check whether the pref is a user
187 pref. If we do not, we don't care.
188 */
189 if (Preferences::HasUserValue(aPrefName) &&
190 NS_SUCCEEDED(Preferences::GetString(aPrefName, &aFileLocation))) {
191 return NS_OK;
192 }
193
194 if (aEnvVarName && *aEnvVarName) {
195 char* prefValue = PR_GetEnv(aEnvVarName);
196 if (prefValue && *prefValue) {
197 // the pref is in the system charset and it's a filepath... The
198 // natural way to do the charset conversion is by just initing
199 // an nsIFile with the native path and asking it for the Unicode
200 // version.
201 nsresult rv;
202 nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
203 NS_ENSURE_SUCCESS(rv, rv);
204
205 rv = file->InitWithNativePath(nsDependentCString(prefValue));
206 NS_ENSURE_SUCCESS(rv, rv);
207
208 rv = file->GetPath(aFileLocation);
209 NS_ENSURE_SUCCESS(rv, rv);
210
211 return NS_OK;
212 }
213 }
214
215 return Preferences::GetString(aPrefName, &aFileLocation);
216 }
217
218
219 /* Get the mime.types file names from prefs and look up info in them
220 based on extension */
221 // static
222 nsresult
223 nsOSHelperAppService::LookUpTypeAndDescription(const nsAString& aFileExtension,
224 nsAString& aMajorType,
225 nsAString& aMinorType,
226 nsAString& aDescription,
227 bool aUserData) {
228 LOG(("-- LookUpTypeAndDescription for extension '%s'\n",
229 NS_LossyConvertUTF16toASCII(aFileExtension).get()));
230 nsresult rv = NS_OK;
231 nsAutoString mimeFileName;
232
233 const char* filenamePref = aUserData ?
234 "helpers.private_mime_types_file" : "helpers.global_mime_types_file";
235
236 rv = GetFileLocation(filenamePref, nullptr, mimeFileName);
237 if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
238 rv = GetTypeAndDescriptionFromMimetypesFile(mimeFileName,
239 aFileExtension,
240 aMajorType,
241 aMinorType,
242 aDescription);
243 } else {
244 rv = NS_ERROR_NOT_AVAILABLE;
245 }
246
247 return rv;
248 }
249
250 inline bool
251 IsNetscapeFormat(const nsACString& aBuffer) {
252 return StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--Netscape Communications Corporation MIME Information")) ||
253 StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--MCOM MIME Information"));
254 }
255
256 /*
257 * Create a file stream and line input stream for the filename.
258 * Leaves the first line of the file in aBuffer and sets the format to
259 * true for netscape files and false for normail ones
260 */
261 // static
262 nsresult
263 nsOSHelperAppService::CreateInputStream(const nsAString& aFilename,
264 nsIFileInputStream ** aFileInputStream,
265 nsILineInputStream ** aLineInputStream,
266 nsACString& aBuffer,
267 bool * aNetscapeFormat,
268 bool * aMore) {
269 LOG(("-- CreateInputStream"));
270 nsresult rv = NS_OK;
271
272 nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
273 if (NS_FAILED(rv))
274 return rv;
275 rv = file->InitWithPath(aFilename);
276 if (NS_FAILED(rv))
277 return rv;
278
279 nsCOMPtr<nsIFileInputStream> fileStream(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
280 if (NS_FAILED(rv))
281 return rv;
282 rv = fileStream->Init(file, -1, -1, false);
283 if (NS_FAILED(rv))
284 return rv;
285
286 nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
287
288 if (NS_FAILED(rv)) {
289 LOG(("Interface trouble in stream land!"));
290 return rv;
291 }
292
293 rv = lineStream->ReadLine(aBuffer, aMore);
294 if (NS_FAILED(rv)) {
295 fileStream->Close();
296 return rv;
297 }
298
299 *aNetscapeFormat = IsNetscapeFormat(aBuffer);
300
301 *aFileInputStream = fileStream;
302 NS_ADDREF(*aFileInputStream);
303 *aLineInputStream = lineStream;
304 NS_ADDREF(*aLineInputStream);
305
306 return NS_OK;
307 }
308
309 /* Open the file, read the first line, decide what type of file it is,
310 then get info based on extension */
311 // static
312 nsresult
313 nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(const nsAString& aFilename,
314 const nsAString& aFileExtension,
315 nsAString& aMajorType,
316 nsAString& aMinorType,
317 nsAString& aDescription) {
318 LOG(("-- GetTypeAndDescriptionFromMimetypesFile\n"));
319 LOG(("Getting type and description from types file '%s'\n",
320 NS_LossyConvertUTF16toASCII(aFilename).get()));
321 LOG(("Using extension '%s'\n",
322 NS_LossyConvertUTF16toASCII(aFileExtension).get()));
323 nsresult rv = NS_OK;
324 nsCOMPtr<nsIFileInputStream> mimeFile;
325 nsCOMPtr<nsILineInputStream> mimeTypes;
326 bool netscapeFormat;
327 nsAutoString buf;
328 nsAutoCString cBuf;
329 bool more = false;
330 rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
331 cBuf, &netscapeFormat, &more);
332
333 if (NS_FAILED(rv)) {
334 return rv;
335 }
336
337 nsAutoString extensions;
338 nsString entry;
339 entry.SetCapacity(100);
340 nsAString::const_iterator majorTypeStart, majorTypeEnd,
341 minorTypeStart, minorTypeEnd,
342 descriptionStart, descriptionEnd;
343
344 do {
345 CopyASCIItoUTF16(cBuf, buf);
346 // read through, building up an entry. If we finish an entry, check for
347 // a match and return out of the loop if we match
348
349 // skip comments and empty lines
350 if (!buf.IsEmpty() && buf.First() != '#') {
351 entry.Append(buf);
352 if (entry.Last() == '\\') {
353 entry.Truncate(entry.Length() - 1);
354 entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line
355 } else { // we have a full entry
356 LOG(("Current entry: '%s'\n",
357 NS_LossyConvertUTF16toASCII(entry).get()));
358 if (netscapeFormat) {
359 rv = ParseNetscapeMIMETypesEntry(entry,
360 majorTypeStart, majorTypeEnd,
361 minorTypeStart, minorTypeEnd,
362 extensions,
363 descriptionStart, descriptionEnd);
364 if (NS_FAILED(rv)) {
365 // We sometimes get things like RealPlayer appending
366 // "normal" entries to "Netscape" .mime.types files. Try
367 // to handle that. Bug 106381.
368 LOG(("Bogus entry; trying 'normal' mode\n"));
369 rv = ParseNormalMIMETypesEntry(entry,
370 majorTypeStart, majorTypeEnd,
371 minorTypeStart, minorTypeEnd,
372 extensions,
373 descriptionStart, descriptionEnd);
374 }
375 } else {
376 rv = ParseNormalMIMETypesEntry(entry,
377 majorTypeStart, majorTypeEnd,
378 minorTypeStart, minorTypeEnd,
379 extensions,
380 descriptionStart, descriptionEnd);
381 if (NS_FAILED(rv)) {
382 // We sometimes get things like StarOffice prepending
383 // "normal" entries to "Netscape" .mime.types files. Try
384 // to handle that. Bug 136670.
385 LOG(("Bogus entry; trying 'Netscape' mode\n"));
386 rv = ParseNetscapeMIMETypesEntry(entry,
387 majorTypeStart, majorTypeEnd,
388 minorTypeStart, minorTypeEnd,
389 extensions,
390 descriptionStart, descriptionEnd);
391 }
392 }
393
394 if (NS_SUCCEEDED(rv)) { // entry parses
395 nsAString::const_iterator start, end;
396 extensions.BeginReading(start);
397 extensions.EndReading(end);
398 nsAString::const_iterator iter(start);
399
400 while (start != end) {
401 FindCharInReadable(',', iter, end);
402 if (Substring(start, iter).Equals(aFileExtension,
403 nsCaseInsensitiveStringComparator())) {
404 // it's a match. Assign the type and description and run
405 aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd));
406 aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd));
407 aDescription.Assign(Substring(descriptionStart, descriptionEnd));
408 mimeFile->Close();
409 return NS_OK;
410 }
411 if (iter != end) {
412 ++iter;
413 }
414 start = iter;
415 }
416 } else {
417 LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get()));
418 }
419 // truncate the entry for the next iteration
420 entry.Truncate();
421 }
422 }
423 if (!more) {
424 rv = NS_ERROR_NOT_AVAILABLE;
425 break;
426 }
427 // read the next line
428 rv = mimeTypes->ReadLine(cBuf, &more);
429 } while (NS_SUCCEEDED(rv));
430
431 mimeFile->Close();
432 return rv;
433 }
434
435 /* Get the mime.types file names from prefs and look up info in them
436 based on mimetype */
437 // static
438 nsresult
439 nsOSHelperAppService::LookUpExtensionsAndDescription(const nsAString& aMajorType,
440 const nsAString& aMinorType,
441 nsAString& aFileExtensions,
442 nsAString& aDescription) {
443 LOG(("-- LookUpExtensionsAndDescription for type '%s/%s'\n",
444 NS_LossyConvertUTF16toASCII(aMajorType).get(),
445 NS_LossyConvertUTF16toASCII(aMinorType).get()));
446 nsresult rv = NS_OK;
447 nsAutoString mimeFileName;
448
449 rv = GetFileLocation("helpers.private_mime_types_file", nullptr, mimeFileName);
450 if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
451 rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
452 aMajorType,
453 aMinorType,
454 aFileExtensions,
455 aDescription);
456 } else {
457 rv = NS_ERROR_NOT_AVAILABLE;
458 }
459 if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) {
460 rv = GetFileLocation("helpers.global_mime_types_file",
461 nullptr, mimeFileName);
462 if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
463 rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
464 aMajorType,
465 aMinorType,
466 aFileExtensions,
467 aDescription);
468 } else {
469 rv = NS_ERROR_NOT_AVAILABLE;
470 }
471 }
472 return rv;
473 }
474
475 /* Open the file, read the first line, decide what type of file it is,
476 then get info based on extension */
477 // static
478 nsresult
479 nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(const nsAString& aFilename,
480 const nsAString& aMajorType,
481 const nsAString& aMinorType,
482 nsAString& aFileExtensions,
483 nsAString& aDescription) {
484 LOG(("-- GetExtensionsAndDescriptionFromMimetypesFile\n"));
485 LOG(("Getting extensions and description from types file '%s'\n",
486 NS_LossyConvertUTF16toASCII(aFilename).get()));
487 LOG(("Using type '%s/%s'\n",
488 NS_LossyConvertUTF16toASCII(aMajorType).get(),
489 NS_LossyConvertUTF16toASCII(aMinorType).get()));
490
491 nsresult rv = NS_OK;
492 nsCOMPtr<nsIFileInputStream> mimeFile;
493 nsCOMPtr<nsILineInputStream> mimeTypes;
494 bool netscapeFormat;
495 nsAutoCString cBuf;
496 nsAutoString buf;
497 bool more = false;
498 rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
499 cBuf, &netscapeFormat, &more);
500
501 if (NS_FAILED(rv)) {
502 return rv;
503 }
504
505 nsAutoString extensions;
506 nsString entry;
507 entry.SetCapacity(100);
508 nsAString::const_iterator majorTypeStart, majorTypeEnd,
509 minorTypeStart, minorTypeEnd,
510 descriptionStart, descriptionEnd;
511
512 do {
513 CopyASCIItoUTF16(cBuf, buf);
514 // read through, building up an entry. If we finish an entry, check for
515 // a match and return out of the loop if we match
516
517 // skip comments and empty lines
518 if (!buf.IsEmpty() && buf.First() != '#') {
519 entry.Append(buf);
520 if (entry.Last() == '\\') {
521 entry.Truncate(entry.Length() - 1);
522 entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line
523 } else { // we have a full entry
524 LOG(("Current entry: '%s'\n",
525 NS_LossyConvertUTF16toASCII(entry).get()));
526 if (netscapeFormat) {
527 rv = ParseNetscapeMIMETypesEntry(entry,
528 majorTypeStart, majorTypeEnd,
529 minorTypeStart, minorTypeEnd,
530 extensions,
531 descriptionStart, descriptionEnd);
532
533 if (NS_FAILED(rv)) {
534 // We sometimes get things like RealPlayer appending
535 // "normal" entries to "Netscape" .mime.types files. Try
536 // to handle that. Bug 106381.
537 LOG(("Bogus entry; trying 'normal' mode\n"));
538 rv = ParseNormalMIMETypesEntry(entry,
539 majorTypeStart, majorTypeEnd,
540 minorTypeStart, minorTypeEnd,
541 extensions,
542 descriptionStart, descriptionEnd);
543 }
544 } else {
545 rv = ParseNormalMIMETypesEntry(entry,
546 majorTypeStart, majorTypeEnd,
547 minorTypeStart,
548 minorTypeEnd, extensions,
549 descriptionStart, descriptionEnd);
550
551 if (NS_FAILED(rv)) {
552 // We sometimes get things like StarOffice prepending
553 // "normal" entries to "Netscape" .mime.types files. Try
554 // to handle that. Bug 136670.
555 LOG(("Bogus entry; trying 'Netscape' mode\n"));
556 rv = ParseNetscapeMIMETypesEntry(entry,
557 majorTypeStart, majorTypeEnd,
558 minorTypeStart, minorTypeEnd,
559 extensions,
560 descriptionStart, descriptionEnd);
561 }
562 }
563
564 if (NS_SUCCEEDED(rv) &&
565 Substring(majorTypeStart,
566 majorTypeEnd).Equals(aMajorType,
567 nsCaseInsensitiveStringComparator())&&
568 Substring(minorTypeStart,
569 minorTypeEnd).Equals(aMinorType,
570 nsCaseInsensitiveStringComparator())) {
571 // it's a match
572 aFileExtensions.Assign(extensions);
573 aDescription.Assign(Substring(descriptionStart, descriptionEnd));
574 mimeFile->Close();
575 return NS_OK;
576 } else if (NS_FAILED(rv)) {
577 LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get()));
578 }
579
580 entry.Truncate();
581 }
582 }
583 if (!more) {
584 rv = NS_ERROR_NOT_AVAILABLE;
585 break;
586 }
587 // read the next line
588 rv = mimeTypes->ReadLine(cBuf, &more);
589 } while (NS_SUCCEEDED(rv));
590
591 mimeFile->Close();
592 return rv;
593 }
594
595 /*
596 * This parses a Netscape format mime.types entry. There are two
597 * possible formats:
598 *
599 * type=foo/bar; options exts="baz" description="Some type"
600 *
601 * and
602 *
603 * type=foo/bar; options description="Some type" exts="baz"
604 */
605 // static
606 nsresult
607 nsOSHelperAppService::ParseNetscapeMIMETypesEntry(const nsAString& aEntry,
608 nsAString::const_iterator& aMajorTypeStart,
609 nsAString::const_iterator& aMajorTypeEnd,
610 nsAString::const_iterator& aMinorTypeStart,
611 nsAString::const_iterator& aMinorTypeEnd,
612 nsAString& aExtensions,
613 nsAString::const_iterator& aDescriptionStart,
614 nsAString::const_iterator& aDescriptionEnd) {
615 LOG(("-- ParseNetscapeMIMETypesEntry\n"));
616 NS_ASSERTION(!aEntry.IsEmpty(), "Empty Netscape MIME types entry being parsed.");
617
618 nsAString::const_iterator start_iter, end_iter, match_start, match_end;
619
620 aEntry.BeginReading(start_iter);
621 aEntry.EndReading(end_iter);
622
623 // skip trailing whitespace
624 do {
625 --end_iter;
626 } while (end_iter != start_iter &&
627 nsCRT::IsAsciiSpace(*end_iter));
628 // if we're pointing to a quote, don't advance -- we don't want to
629 // include the quote....
630 if (*end_iter != '"')
631 ++end_iter;
632 match_start = start_iter;
633 match_end = end_iter;
634
635 // Get the major and minor types
636 // First the major type
637 if (! FindInReadable(NS_LITERAL_STRING("type="), match_start, match_end)) {
638 return NS_ERROR_FAILURE;
639 }
640
641 match_start = match_end;
642
643 while (match_end != end_iter &&
644 *match_end != '/') {
645 ++match_end;
646 }
647 if (match_end == end_iter) {
648 return NS_ERROR_FAILURE;
649 }
650
651 aMajorTypeStart = match_start;
652 aMajorTypeEnd = match_end;
653
654 // now the minor type
655 if (++match_end == end_iter) {
656 return NS_ERROR_FAILURE;
657 }
658
659 match_start = match_end;
660
661 while (match_end != end_iter &&
662 !nsCRT::IsAsciiSpace(*match_end) &&
663 *match_end != ';') {
664 ++match_end;
665 }
666 if (match_end == end_iter) {
667 return NS_ERROR_FAILURE;
668 }
669
670 aMinorTypeStart = match_start;
671 aMinorTypeEnd = match_end;
672
673 // ignore everything up to the end of the mime type from here on
674 start_iter = match_end;
675
676 // get the extensions
677 match_start = match_end;
678 match_end = end_iter;
679 if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
680 nsAString::const_iterator extStart, extEnd;
681
682 if (match_end == end_iter ||
683 (*match_end == '"' && ++match_end == end_iter)) {
684 return NS_ERROR_FAILURE;
685 }
686
687 extStart = match_end;
688 match_start = extStart;
689 match_end = end_iter;
690 if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
691 // exts= before desc=, so we have to find the actual end of the extensions
692 extEnd = match_start;
693 if (extEnd == extStart) {
694 return NS_ERROR_FAILURE;
695 }
696
697 do {
698 --extEnd;
699 } while (extEnd != extStart &&
700 nsCRT::IsAsciiSpace(*extEnd));
701
702 if (extEnd != extStart && *extEnd == '"') {
703 --extEnd;
704 }
705 } else {
706 // desc= before exts=, so we can use end_iter as the end of the extensions
707 extEnd = end_iter;
708 }
709 aExtensions = Substring(extStart, extEnd);
710 } else {
711 // no extensions
712 aExtensions.Truncate();
713 }
714
715 // get the description
716 match_start = start_iter;
717 match_end = end_iter;
718 if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
719 aDescriptionStart = match_end;
720 match_start = aDescriptionStart;
721 match_end = end_iter;
722 if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
723 // exts= after desc=, so have to find actual end of description
724 aDescriptionEnd = match_start;
725 if (aDescriptionEnd == aDescriptionStart) {
726 return NS_ERROR_FAILURE;
727 }
728
729 do {
730 --aDescriptionEnd;
731 } while (aDescriptionEnd != aDescriptionStart &&
732 nsCRT::IsAsciiSpace(*aDescriptionEnd));
733
734 if (aDescriptionStart != aDescriptionStart && *aDescriptionEnd == '"') {
735 --aDescriptionEnd;
736 }
737 } else {
738 // desc= after exts=, so use end_iter for the description end
739 aDescriptionEnd = end_iter;
740 }
741 } else {
742 // no description
743 aDescriptionStart = start_iter;
744 aDescriptionEnd = start_iter;
745 }
746
747 return NS_OK;
748 }
749
750 /*
751 * This parses a normal format mime.types entry. The format is:
752 *
753 * major/minor ext1 ext2 ext3
754 */
755 // static
756 nsresult
757 nsOSHelperAppService::ParseNormalMIMETypesEntry(const nsAString& aEntry,
758 nsAString::const_iterator& aMajorTypeStart,
759 nsAString::const_iterator& aMajorTypeEnd,
760 nsAString::const_iterator& aMinorTypeStart,
761 nsAString::const_iterator& aMinorTypeEnd,
762 nsAString& aExtensions,
763 nsAString::const_iterator& aDescriptionStart,
764 nsAString::const_iterator& aDescriptionEnd) {
765 LOG(("-- ParseNormalMIMETypesEntry\n"));
766 NS_ASSERTION(!aEntry.IsEmpty(), "Empty Normal MIME types entry being parsed.");
767
768 nsAString::const_iterator start_iter, end_iter, iter;
769
770 aEntry.BeginReading(start_iter);
771 aEntry.EndReading(end_iter);
772
773 // no description
774 aDescriptionStart = start_iter;
775 aDescriptionEnd = start_iter;
776
777 // skip leading whitespace
778 while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) {
779 ++start_iter;
780 }
781 if (start_iter == end_iter) {
782 return NS_ERROR_FAILURE;
783 }
784 // skip trailing whitespace
785 do {
786 --end_iter;
787 } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter));
788
789 ++end_iter; // point to first whitespace char (or to end of string)
790 iter = start_iter;
791
792 // get the major type
793 if (! FindCharInReadable('/', iter, end_iter))
794 return NS_ERROR_FAILURE;
795
796 nsAString::const_iterator equals_sign_iter(start_iter);
797 if (FindCharInReadable('=', equals_sign_iter, iter))
798 return NS_ERROR_FAILURE; // see bug 136670
799
800 aMajorTypeStart = start_iter;
801 aMajorTypeEnd = iter;
802
803 // get the minor type
804 if (++iter == end_iter) {
805 return NS_ERROR_FAILURE;
806 }
807 start_iter = iter;
808
809 while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
810 ++iter;
811 }
812 aMinorTypeStart = start_iter;
813 aMinorTypeEnd = iter;
814
815 // get the extensions
816 aExtensions.Truncate();
817 while (iter != end_iter) {
818 while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) {
819 ++iter;
820 }
821
822 start_iter = iter;
823 while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
824 ++iter;
825 }
826 aExtensions.Append(Substring(start_iter, iter));
827 if (iter != end_iter) { // not the last extension
828 aExtensions.Append(char16_t(','));
829 }
830 }
831
832 return NS_OK;
833 }
834
835 // static
836 nsresult
837 nsOSHelperAppService::LookUpHandlerAndDescription(const nsAString& aMajorType,
838 const nsAString& aMinorType,
839 nsAString& aHandler,
840 nsAString& aDescription,
841 nsAString& aMozillaFlags) {
842
843 // The mailcap lookup is two-pass to handle the case of mailcap files
844 // that have something like:
845 //
846 // text/*; emacs %s
847 // text/rtf; soffice %s
848 //
849 // in that order. We want to pick up "soffice" for text/rtf in such cases
850 nsresult rv = DoLookUpHandlerAndDescription(aMajorType,
851 aMinorType,
852 aHandler,
853 aDescription,
854 aMozillaFlags,
855 true);
856 if (NS_FAILED(rv)) {
857 rv = DoLookUpHandlerAndDescription(aMajorType,
858 aMinorType,
859 aHandler,
860 aDescription,
861 aMozillaFlags,
862 false);
863 }
864
865 // maybe we have an entry for "aMajorType/*"?
866 if (NS_FAILED(rv)) {
867 rv = DoLookUpHandlerAndDescription(aMajorType,
868 NS_LITERAL_STRING("*"),
869 aHandler,
870 aDescription,
871 aMozillaFlags,
872 true);
873 }
874
875 if (NS_FAILED(rv)) {
876 rv = DoLookUpHandlerAndDescription(aMajorType,
877 NS_LITERAL_STRING("*"),
878 aHandler,
879 aDescription,
880 aMozillaFlags,
881 false);
882 }
883
884 return rv;
885 }
886
887 // static
888 nsresult
889 nsOSHelperAppService::DoLookUpHandlerAndDescription(const nsAString& aMajorType,
890 const nsAString& aMinorType,
891 nsAString& aHandler,
892 nsAString& aDescription,
893 nsAString& aMozillaFlags,
894 bool aUserData) {
895 LOG(("-- LookUpHandlerAndDescription for type '%s/%s'\n",
896 NS_LossyConvertUTF16toASCII(aMajorType).get(),
897 NS_LossyConvertUTF16toASCII(aMinorType).get()));
898 nsresult rv = NS_OK;
899 nsAutoString mailcapFileName;
900
901 const char * filenamePref = aUserData ?
902 "helpers.private_mailcap_file" : "helpers.global_mailcap_file";
903 const char * filenameEnvVar = aUserData ?
904 "PERSONAL_MAILCAP" : "MAILCAP";
905
906 rv = GetFileLocation(filenamePref, filenameEnvVar, mailcapFileName);
907 if (NS_SUCCEEDED(rv) && !mailcapFileName.IsEmpty()) {
908 rv = GetHandlerAndDescriptionFromMailcapFile(mailcapFileName,
909 aMajorType,
910 aMinorType,
911 aHandler,
912 aDescription,
913 aMozillaFlags);
914 } else {
915 rv = NS_ERROR_NOT_AVAILABLE;
916 }
917
918 return rv;
919 }
920
921 // static
922 nsresult
923 nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(const nsAString& aFilename,
924 const nsAString& aMajorType,
925 const nsAString& aMinorType,
926 nsAString& aHandler,
927 nsAString& aDescription,
928 nsAString& aMozillaFlags) {
929
930 LOG(("-- GetHandlerAndDescriptionFromMailcapFile\n"));
931 LOG(("Getting handler and description from mailcap file '%s'\n",
932 NS_LossyConvertUTF16toASCII(aFilename).get()));
933 LOG(("Using type '%s/%s'\n",
934 NS_LossyConvertUTF16toASCII(aMajorType).get(),
935 NS_LossyConvertUTF16toASCII(aMinorType).get()));
936
937 nsresult rv = NS_OK;
938 bool more = false;
939
940 nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
941 if (NS_FAILED(rv))
942 return rv;
943 rv = file->InitWithPath(aFilename);
944 if (NS_FAILED(rv))
945 return rv;
946
947 nsCOMPtr<nsIFileInputStream> mailcapFile(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
948 if (NS_FAILED(rv))
949 return rv;
950 rv = mailcapFile->Init(file, -1, -1, false);
951 if (NS_FAILED(rv))
952 return rv;
953
954 nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv));
955
956 if (NS_FAILED(rv)) {
957 LOG(("Interface trouble in stream land!"));
958 return rv;
959 }
960
961 nsString entry, buffer;
962 nsAutoCString cBuffer;
963 entry.SetCapacity(128);
964 cBuffer.SetCapacity(80);
965 rv = mailcap->ReadLine(cBuffer, &more);
966 if (NS_FAILED(rv)) {
967 mailcapFile->Close();
968 return rv;
969 }
970
971 do { // return on end-of-file in the loop
972
973 CopyASCIItoUTF16(cBuffer, buffer);
974 if (!buffer.IsEmpty() && buffer.First() != '#') {
975 entry.Append(buffer);
976 if (entry.Last() == '\\') { // entry continues on next line
977 entry.Truncate(entry.Length()-1);
978 entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line
979 } else { // we have a full entry in entry. Check it for the type
980 LOG(("Current entry: '%s'\n",
981 NS_LossyConvertUTF16toASCII(entry).get()));
982
983 nsAString::const_iterator semicolon_iter,
984 start_iter, end_iter,
985 majorTypeStart, majorTypeEnd,
986 minorTypeStart, minorTypeEnd;
987 entry.BeginReading(start_iter);
988 entry.EndReading(end_iter);
989 semicolon_iter = start_iter;
990 FindSemicolon(semicolon_iter, end_iter);
991 if (semicolon_iter != end_iter) { // we have something resembling a valid entry
992 rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
993 minorTypeStart, minorTypeEnd, semicolon_iter);
994 if (NS_SUCCEEDED(rv) &&
995 Substring(majorTypeStart,
996 majorTypeEnd).Equals(aMajorType,
997 nsCaseInsensitiveStringComparator()) &&
998 Substring(minorTypeStart,
999 minorTypeEnd).Equals(aMinorType,
1000 nsCaseInsensitiveStringComparator())) { // we have a match
1001 bool match = true;
1002 ++semicolon_iter; // point at the first char past the semicolon
1003 start_iter = semicolon_iter; // handler string starts here
1004 FindSemicolon(semicolon_iter, end_iter);
1005 while (start_iter != semicolon_iter &&
1006 nsCRT::IsAsciiSpace(*start_iter)) {
1007 ++start_iter;
1008 }
1009
1010 LOG(("The real handler is: '%s'\n",
1011 NS_LossyConvertUTF16toASCII(Substring(start_iter,
1012 semicolon_iter)).get()));
1013
1014 // XXX ugly hack. Just grab the executable name
1015 nsAString::const_iterator end_handler_iter = semicolon_iter;
1016 nsAString::const_iterator end_executable_iter = start_iter;
1017 while (end_executable_iter != end_handler_iter &&
1018 !nsCRT::IsAsciiSpace(*end_executable_iter)) {
1019 ++end_executable_iter;
1020 }
1021 // XXX End ugly hack
1022
1023 aHandler = Substring(start_iter, end_executable_iter);
1024
1025 nsAString::const_iterator start_option_iter, end_optionname_iter, equal_sign_iter;
1026 bool equalSignFound;
1027 while (match &&
1028 semicolon_iter != end_iter &&
1029 ++semicolon_iter != end_iter) { // there are options left and we still match
1030 start_option_iter = semicolon_iter;
1031 // skip over leading whitespace
1032 while (start_option_iter != end_iter &&
1033 nsCRT::IsAsciiSpace(*start_option_iter)) {
1034 ++start_option_iter;
1035 }
1036 if (start_option_iter == end_iter) { // nothing actually here
1037 break;
1038 }
1039 semicolon_iter = start_option_iter;
1040 FindSemicolon(semicolon_iter, end_iter);
1041 equal_sign_iter = start_option_iter;
1042 equalSignFound = false;
1043 while (equal_sign_iter != semicolon_iter && !equalSignFound) {
1044 switch(*equal_sign_iter) {
1045 case '\\':
1046 equal_sign_iter.advance(2);
1047 break;
1048 case '=':
1049 equalSignFound = true;
1050 break;
1051 default:
1052 ++equal_sign_iter;
1053 break;
1054 }
1055 }
1056 end_optionname_iter = start_option_iter;
1057 // find end of option name
1058 while (end_optionname_iter != equal_sign_iter &&
1059 !nsCRT::IsAsciiSpace(*end_optionname_iter)) {
1060 ++end_optionname_iter;
1061 }
1062 nsDependentSubstring optionName(start_option_iter, end_optionname_iter);
1063 if (equalSignFound) {
1064 // This is an option that has a name and value
1065 if (optionName.EqualsLiteral("description")) {
1066 aDescription = Substring(++equal_sign_iter, semicolon_iter);
1067 } else if (optionName.EqualsLiteral("x-mozilla-flags")) {
1068 aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter);
1069 } else if (optionName.EqualsLiteral("test")) {
1070 nsAutoCString testCommand;
1071 rv = UnescapeCommand(Substring(++equal_sign_iter, semicolon_iter),
1072 aMajorType,
1073 aMinorType,
1074 testCommand);
1075 if (NS_FAILED(rv))
1076 continue;
1077 nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv);
1078 if (NS_FAILED(rv))
1079 continue;
1080 nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
1081 if (NS_FAILED(rv))
1082 continue;
1083 rv = file->InitWithNativePath(NS_LITERAL_CSTRING("/bin/sh"));
1084 if (NS_FAILED(rv))
1085 continue;
1086 rv = process->Init(file);
1087 if (NS_FAILED(rv))
1088 continue;
1089 const char *args[] = { "-c", testCommand.get() };
1090 LOG(("Running Test: %s\n", testCommand.get()));
1091 rv = process->Run(true, args, 2);
1092 if (NS_FAILED(rv))
1093 continue;
1094 int32_t exitValue;
1095 rv = process->GetExitValue(&exitValue);
1096 if (NS_FAILED(rv))
1097 continue;
1098 LOG(("Exit code: %d\n", exitValue));
1099 if (exitValue) {
1100 match = false;
1101 }
1102 }
1103 } else {
1104 // This is an option that just has a name but no value (eg "copiousoutput")
1105 if (optionName.EqualsLiteral("needsterminal")) {
1106 match = false;
1107 }
1108 }
1109 }
1110
1111
1112 if (match) { // we did not fail any test clauses; all is good
1113 // get out of here
1114 mailcapFile->Close();
1115 return NS_OK;
1116 } else { // pretend that this match never happened
1117 aDescription.Truncate();
1118 aMozillaFlags.Truncate();
1119 aHandler.Truncate();
1120 }
1121 }
1122 }
1123 // zero out the entry for the next cycle
1124 entry.Truncate();
1125 }
1126 }
1127 if (!more) {
1128 rv = NS_ERROR_NOT_AVAILABLE;
1129 break;
1130 }
1131 rv = mailcap->ReadLine(cBuffer, &more);
1132 } while (NS_SUCCEEDED(rv));
1133 mailcapFile->Close();
1134 return rv;
1135 }
1136
1137 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
1138 {
1139 LOG(("-- nsOSHelperAppService::OSProtocolHandlerExists for '%s'\n",
1140 aProtocolScheme));
1141 *aHandlerExists = false;
1142
1143 #if defined(MOZ_ENABLE_CONTENTACTION)
1144 // libcontentaction requires character ':' after scheme
1145 ContentAction::Action action =
1146 ContentAction::Action::defaultActionForScheme(QString(aProtocolScheme) + ':');
1147
1148 if (action.isValid())
1149 *aHandlerExists = true;
1150 #endif
1151
1152 #ifdef MOZ_WIDGET_GTK
1153 // Check the GConf registry for a protocol handler
1154 *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
1155 #endif
1156
1157 return NS_OK;
1158 }
1159
1160 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
1161 {
1162 #ifdef MOZ_WIDGET_GTK
1163 nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
1164 return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
1165 #else
1166 return NS_ERROR_NOT_AVAILABLE;
1167 #endif
1168 }
1169
1170 nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile)
1171 {
1172 LOG(("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n",
1173 NS_LossyConvertUTF16toASCII(platformAppPath).get()));
1174 if (! *platformAppPath) { // empty filename--return error
1175 NS_WARNING("Empty filename passed in.");
1176 return NS_ERROR_INVALID_ARG;
1177 }
1178
1179 // first check if the base class implementation finds anything
1180 nsresult rv = nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile);
1181 if (NS_SUCCEEDED(rv))
1182 return rv;
1183 // If the reason for failure was that the file doesn't exist, return too
1184 // (because it means the path was absolute, and so that we shouldn't search in
1185 // the path)
1186 if (rv == NS_ERROR_FILE_NOT_FOUND)
1187 return rv;
1188
1189 // If we get here, we really should have a relative path.
1190 NS_ASSERTION(*platformAppPath != char16_t('/'), "Unexpected absolute path");
1191
1192 nsCOMPtr<nsIFile> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
1193
1194 if (!localFile) return NS_ERROR_NOT_INITIALIZED;
1195
1196 bool exists = false;
1197 // ugly hack. Walk the PATH variable...
1198 char* unixpath = PR_GetEnv("PATH");
1199 nsAutoCString path(unixpath);
1200
1201 const char* start_iter = path.BeginReading(start_iter);
1202 const char* colon_iter = start_iter;
1203 const char* end_iter = path.EndReading(end_iter);
1204
1205 while (start_iter != end_iter && !exists) {
1206 while (colon_iter != end_iter && *colon_iter != ':') {
1207 ++colon_iter;
1208 }
1209 localFile->InitWithNativePath(Substring(start_iter, colon_iter));
1210 rv = localFile->AppendRelativePath(nsDependentString(platformAppPath));
1211 // Failing AppendRelativePath is a bad thing - it should basically always
1212 // succeed given a relative path. Show a warning if it does fail.
1213 // To prevent infinite loops when it does fail, return at this point.
1214 NS_ENSURE_SUCCESS(rv, rv);
1215 localFile->Exists(&exists);
1216 if (!exists) {
1217 if (colon_iter == end_iter) {
1218 break;
1219 }
1220 ++colon_iter;
1221 start_iter = colon_iter;
1222 }
1223 }
1224
1225 if (exists) {
1226 rv = NS_OK;
1227 } else {
1228 rv = NS_ERROR_NOT_AVAILABLE;
1229 }
1230
1231 *aFile = localFile;
1232 NS_IF_ADDREF(*aFile);
1233
1234 return rv;
1235 }
1236
1237 already_AddRefed<nsMIMEInfoBase>
1238 nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) {
1239 // if the extension is empty, return immediately
1240 if (aFileExt.IsEmpty())
1241 return nullptr;
1242
1243 LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get()));
1244
1245 nsAutoString majorType, minorType,
1246 mime_types_description, mailcap_description,
1247 handler, mozillaFlags;
1248
1249 nsresult rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt),
1250 majorType,
1251 minorType,
1252 mime_types_description,
1253 true);
1254
1255 if (NS_FAILED(rv) || majorType.IsEmpty()) {
1256
1257 #ifdef MOZ_WIDGET_GTK
1258 LOG(("Looking in GNOME registry\n"));
1259 nsRefPtr<nsMIMEInfoBase> gnomeInfo =
1260 nsGNOMERegistry::GetFromExtension(aFileExt);
1261 if (gnomeInfo) {
1262 LOG(("Got MIMEInfo from GNOME registry\n"));
1263 return gnomeInfo.forget();
1264 }
1265 #endif
1266
1267 rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt),
1268 majorType,
1269 minorType,
1270 mime_types_description,
1271 false);
1272 }
1273
1274 if (NS_FAILED(rv))
1275 return nullptr;
1276
1277 NS_LossyConvertUTF16toASCII asciiMajorType(majorType);
1278 NS_LossyConvertUTF16toASCII asciiMinorType(minorType);
1279
1280 LOG(("Type/Description results: majorType='%s', minorType='%s', description='%s'\n",
1281 asciiMajorType.get(),
1282 asciiMinorType.get(),
1283 NS_LossyConvertUTF16toASCII(mime_types_description).get()));
1284
1285 if (majorType.IsEmpty() && minorType.IsEmpty()) {
1286 // we didn't get a type mapping, so we can't do anything useful
1287 return nullptr;
1288 }
1289
1290 nsAutoCString mimeType(asciiMajorType + NS_LITERAL_CSTRING("/") + asciiMinorType);
1291 nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(mimeType);
1292
1293 mimeInfo->AppendExtension(aFileExt);
1294 rv = LookUpHandlerAndDescription(majorType, minorType,
1295 handler, mailcap_description,
1296 mozillaFlags);
1297 LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n",
1298 NS_LossyConvertUTF16toASCII(handler).get(),
1299 NS_LossyConvertUTF16toASCII(mailcap_description).get(),
1300 NS_LossyConvertUTF16toASCII(mozillaFlags).get()));
1301 mailcap_description.Trim(" \t\"");
1302 mozillaFlags.Trim(" \t");
1303 if (!mime_types_description.IsEmpty()) {
1304 mimeInfo->SetDescription(mime_types_description);
1305 } else {
1306 mimeInfo->SetDescription(mailcap_description);
1307 }
1308
1309 if (NS_SUCCEEDED(rv) && handler.IsEmpty()) {
1310 rv = NS_ERROR_NOT_AVAILABLE;
1311 }
1312
1313 if (NS_SUCCEEDED(rv)) {
1314 nsCOMPtr<nsIFile> handlerFile;
1315 rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
1316
1317 if (NS_SUCCEEDED(rv)) {
1318 mimeInfo->SetDefaultApplication(handlerFile);
1319 mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
1320 mimeInfo->SetDefaultDescription(handler);
1321 }
1322 }
1323
1324 if (NS_FAILED(rv)) {
1325 mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
1326 }
1327
1328 return mimeInfo.forget();
1329 }
1330
1331 already_AddRefed<nsMIMEInfoBase>
1332 nsOSHelperAppService::GetFromType(const nsCString& aMIMEType) {
1333 // if the type is empty, return immediately
1334 if (aMIMEType.IsEmpty())
1335 return nullptr;
1336
1337 LOG(("Here we do a mimetype lookup for '%s'\n", aMIMEType.get()));
1338
1339 // extract the major and minor types
1340 NS_ConvertASCIItoUTF16 mimeType(aMIMEType);
1341 nsAString::const_iterator start_iter, end_iter,
1342 majorTypeStart, majorTypeEnd,
1343 minorTypeStart, minorTypeEnd;
1344
1345 mimeType.BeginReading(start_iter);
1346 mimeType.EndReading(end_iter);
1347
1348 // XXX FIXME: add typeOptions parsing in here
1349 nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
1350 minorTypeStart, minorTypeEnd, end_iter);
1351
1352 if (NS_FAILED(rv)) {
1353 return nullptr;
1354 }
1355
1356 nsDependentSubstring majorType(majorTypeStart, majorTypeEnd);
1357 nsDependentSubstring minorType(minorTypeStart, minorTypeEnd);
1358
1359 // First check the user's private mailcap file
1360 nsAutoString mailcap_description, handler, mozillaFlags;
1361 DoLookUpHandlerAndDescription(majorType,
1362 minorType,
1363 handler,
1364 mailcap_description,
1365 mozillaFlags,
1366 true);
1367
1368 LOG(("Private Handler/Description results: handler='%s', description='%s'\n",
1369 NS_LossyConvertUTF16toASCII(handler).get(),
1370 NS_LossyConvertUTF16toASCII(mailcap_description).get()));
1371
1372 #ifdef MOZ_WIDGET_GTK
1373 nsRefPtr<nsMIMEInfoBase> gnomeInfo;
1374 if (handler.IsEmpty()) {
1375 // No useful data yet. Check the GNOME registry. Unfortunately, newer
1376 // GNOME versions no longer have type-to-extension mappings, so we might
1377 // get back a MIMEInfo without any extensions set. In that case we'll have
1378 // to look in our mime.types files for the extensions.
1379 LOG(("Looking in GNOME registry\n"));
1380 gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType);
1381 if (gnomeInfo && gnomeInfo->HasExtensions()) {
1382 LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n"));
1383 return gnomeInfo.forget();
1384 }
1385 }
1386 #endif
1387
1388 // Now look up our extensions
1389 nsAutoString extensions, mime_types_description;
1390 LookUpExtensionsAndDescription(majorType,
1391 minorType,
1392 extensions,
1393 mime_types_description);
1394
1395 #ifdef MOZ_WIDGET_GTK
1396 if (gnomeInfo) {
1397 LOG(("Got MIMEInfo from GNOME registry without extensions; setting them "
1398 "to %s\n", NS_LossyConvertUTF16toASCII(extensions).get()));
1399
1400 NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?");
1401 gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
1402 return gnomeInfo.forget();
1403 }
1404 #endif
1405
1406 if (handler.IsEmpty()) {
1407 DoLookUpHandlerAndDescription(majorType,
1408 minorType,
1409 handler,
1410 mailcap_description,
1411 mozillaFlags,
1412 false);
1413 }
1414
1415 if (handler.IsEmpty()) {
1416 DoLookUpHandlerAndDescription(majorType,
1417 NS_LITERAL_STRING("*"),
1418 handler,
1419 mailcap_description,
1420 mozillaFlags,
1421 true);
1422 }
1423
1424 if (handler.IsEmpty()) {
1425 DoLookUpHandlerAndDescription(majorType,
1426 NS_LITERAL_STRING("*"),
1427 handler,
1428 mailcap_description,
1429 mozillaFlags,
1430 false);
1431 }
1432
1433 LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n",
1434 NS_LossyConvertUTF16toASCII(handler).get(),
1435 NS_LossyConvertUTF16toASCII(mailcap_description).get(),
1436 NS_LossyConvertUTF16toASCII(mozillaFlags).get()));
1437
1438 mailcap_description.Trim(" \t\"");
1439 mozillaFlags.Trim(" \t");
1440
1441 if (handler.IsEmpty() && extensions.IsEmpty() &&
1442 mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) {
1443 // No real useful info
1444 return nullptr;
1445 }
1446
1447 nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(aMIMEType);
1448
1449 mimeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
1450 if (! mime_types_description.IsEmpty()) {
1451 mimeInfo->SetDescription(mime_types_description);
1452 } else {
1453 mimeInfo->SetDescription(mailcap_description);
1454 }
1455
1456 rv = NS_ERROR_NOT_AVAILABLE;
1457 nsCOMPtr<nsIFile> handlerFile;
1458 if (!handler.IsEmpty()) {
1459 rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
1460 }
1461
1462 if (NS_SUCCEEDED(rv)) {
1463 mimeInfo->SetDefaultApplication(handlerFile);
1464 mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
1465 mimeInfo->SetDefaultDescription(handler);
1466 } else {
1467 mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
1468 }
1469
1470 return mimeInfo.forget();
1471 }
1472
1473
1474 already_AddRefed<nsIMIMEInfo>
1475 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
1476 const nsACString& aFileExt,
1477 bool *aFound) {
1478 *aFound = true;
1479 nsRefPtr<nsMIMEInfoBase> retval = GetFromType(PromiseFlatCString(aType));
1480 bool hasDefault = false;
1481 if (retval)
1482 retval->GetHasDefaultHandler(&hasDefault);
1483 if (!retval || !hasDefault) {
1484 nsRefPtr<nsMIMEInfoBase> miByExt = GetFromExtension(PromiseFlatCString(aFileExt));
1485 // If we had no extension match, but a type match, use that
1486 if (!miByExt && retval)
1487 return retval.forget();
1488 // If we had an extension match but no type match, set the mimetype and use
1489 // it
1490 if (!retval && miByExt) {
1491 if (!aType.IsEmpty())
1492 miByExt->SetMIMEType(aType);
1493 miByExt.swap(retval);
1494
1495 return retval.forget();
1496 }
1497 // If we got nothing, make a new mimeinfo
1498 if (!retval) {
1499 *aFound = false;
1500 retval = new nsMIMEInfoUnix(aType);
1501 if (retval) {
1502 if (!aFileExt.IsEmpty())
1503 retval->AppendExtension(aFileExt);
1504 }
1505
1506 return retval.forget();
1507 }
1508
1509 // Copy the attributes of retval (mimeinfo from type) onto miByExt, to
1510 // return it
1511 // but reset to just collected mDefaultAppDescription (from ext)
1512 nsAutoString byExtDefault;
1513 miByExt->GetDefaultDescription(byExtDefault);
1514 retval->SetDefaultDescription(byExtDefault);
1515 retval->CopyBasicDataTo(miByExt);
1516
1517 miByExt.swap(retval);
1518 }
1519 return retval.forget();
1520 }
1521
1522 NS_IMETHODIMP
1523 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
1524 bool *found,
1525 nsIHandlerInfo **_retval)
1526 {
1527 NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
1528
1529 // We must check that a registered handler exists so that gnome_url_show
1530 // doesn't fallback to gnomevfs.
1531 // See nsGNOMERegistry::LoadURL and bug 389632.
1532 nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
1533 found);
1534 if (NS_FAILED(rv))
1535 return rv;
1536
1537 nsMIMEInfoUnix *handlerInfo =
1538 new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo);
1539 NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
1540 NS_ADDREF(*_retval = handlerInfo);
1541
1542 if (!*found) {
1543 // Code that calls this requires an object regardless if the OS has
1544 // something for us, so we return the empty object.
1545 return NS_OK;
1546 }
1547
1548 nsAutoString desc;
1549 GetApplicationDescription(aScheme, desc);
1550 handlerInfo->SetDefaultDescription(desc);
1551
1552 return NS_OK;
1553 }

mercurial