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