|
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 } |