Coverage Report

Created: 2018-09-25 14:53

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