1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla browser.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications, Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Scott MacGregor <mscott@netscape.com>
24 : * Christian Biesinger <cbiesinger@web.de>
25 : * Dan Mosedale <dmose@mozilla.org>
26 : * Myk Melez <myk@mozilla.org>
27 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #ifndef nsExternalHelperAppService_h__
44 : #define nsExternalHelperAppService_h__
45 :
46 : #ifdef MOZ_LOGGING
47 : #define FORCE_PR_LOG
48 : #endif
49 : #include "prlog.h"
50 : #include "prtime.h"
51 :
52 : #include "nsIExternalHelperAppService.h"
53 : #include "nsIExternalProtocolService.h"
54 : #include "nsIWebProgressListener2.h"
55 : #include "nsIHelperAppLauncherDialog.h"
56 :
57 : #include "nsIMIMEInfo.h"
58 : #include "nsIMIMEService.h"
59 : #include "nsIStreamListener.h"
60 : #include "nsIFile.h"
61 : #include "nsIFileStreams.h"
62 : #include "nsIOutputStream.h"
63 : #include "nsString.h"
64 : #include "nsIInterfaceRequestor.h"
65 : #include "nsIInterfaceRequestorUtils.h"
66 : #include "nsILocalFile.h"
67 : #include "nsIChannel.h"
68 : #include "nsITimer.h"
69 :
70 : #include "nsIHandlerService.h"
71 : #include "nsCOMPtr.h"
72 : #include "nsIObserver.h"
73 : #include "nsCOMArray.h"
74 : #include "nsWeakReference.h"
75 : #include "nsIPrompt.h"
76 : #include "nsAutoPtr.h"
77 :
78 : class nsExternalAppHandler;
79 : class nsIMIMEInfo;
80 : class nsITransfer;
81 : class nsIDOMWindow;
82 :
83 : /**
84 : * The helper app service. Responsible for handling content that Mozilla
85 : * itself can not handle
86 : */
87 : class nsExternalHelperAppService
88 : : public nsIExternalHelperAppService,
89 : public nsPIExternalAppLauncher,
90 : public nsIExternalProtocolService,
91 : public nsIMIMEService,
92 : public nsIObserver,
93 : public nsSupportsWeakReference
94 : {
95 : public:
96 : NS_DECL_ISUPPORTS
97 : NS_DECL_NSIEXTERNALHELPERAPPSERVICE
98 : NS_DECL_NSPIEXTERNALAPPLAUNCHER
99 : NS_DECL_NSIEXTERNALPROTOCOLSERVICE
100 : NS_DECL_NSIMIMESERVICE
101 : NS_DECL_NSIOBSERVER
102 :
103 : nsExternalHelperAppService();
104 : virtual ~nsExternalHelperAppService();
105 :
106 : /**
107 : * Initializes internal state. Will be called automatically when
108 : * this service is first instantiated.
109 : */
110 : NS_HIDDEN_(nsresult) Init();
111 :
112 : /**
113 : * Given a mimetype and an extension, looks up a mime info from the OS.
114 : * The mime type is given preference. This function follows the same rules
115 : * as nsIMIMEService::GetFromTypeAndExtension.
116 : * This is supposed to be overridden by the platform-specific
117 : * nsOSHelperAppService!
118 : * @param aFileExt The file extension; may be empty. UTF-8 encoded.
119 : * @param [out] aFound
120 : * Should be set to true if the os has a mapping, to
121 : * false otherwise. Must not be null.
122 : * @return A MIMEInfo. This function must return a MIMEInfo object if it
123 : * can allocate one. The only justifiable reason for not
124 : * returning one is an out-of-memory error.
125 : * If null, the value of aFound is unspecified.
126 : */
127 : virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
128 : const nsACString& aFileExt,
129 : bool * aFound) = 0;
130 :
131 : /**
132 : * Given a string identifying an application, create an nsIFile representing
133 : * it. This function should look in $PATH for the application.
134 : * The base class implementation will first try to interpret platformAppPath
135 : * as an absolute path, and if that fails it will look for a file next to the
136 : * mozilla executable. Subclasses can override this method if they want a
137 : * different behaviour.
138 : * @param platformAppPath A platform specific path to an application that we
139 : * got out of the rdf data source. This can be a mac
140 : * file spec, a unix path or a windows path depending
141 : * on the platform
142 : * @param aFile [out] An nsIFile representation of that platform
143 : * application path.
144 : */
145 : virtual nsresult GetFileTokenForPath(const PRUnichar * platformAppPath,
146 : nsIFile ** aFile);
147 :
148 : virtual NS_HIDDEN_(nsresult) OSProtocolHandlerExists(const char *aScheme,
149 : bool *aExists) = 0;
150 :
151 : /**
152 : * Simple accessor to let nsExternalAppHandler know if we are currently
153 : * inside the private browsing mode.
154 : */
155 0 : bool InPrivateBrowsing() const { return mInPrivateBrowsing; }
156 :
157 : protected:
158 : /**
159 : * Searches the "extra" array of MIMEInfo objects for an object
160 : * with a specific type. If found, it will modify the passed-in
161 : * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
162 : * will be untouched.
163 : * @param aContentType The type to search for.
164 : * @param aMIMEInfo [inout] The mime info, if found
165 : */
166 : NS_HIDDEN_(nsresult) FillMIMEInfoForMimeTypeFromExtras(
167 : const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo);
168 : /**
169 : * Searches the "extra" array of MIMEInfo objects for an object
170 : * with a specific extension.
171 : *
172 : * Does not change the MIME Type of the MIME Info.
173 : *
174 : * @see FillMIMEInfoForMimeTypeFromExtras
175 : */
176 : NS_HIDDEN_(nsresult) FillMIMEInfoForExtensionFromExtras(
177 : const nsACString& aExtension, nsIMIMEInfo * aMIMEInfo);
178 :
179 : /**
180 : * Searches the "extra" array for a MIME type, and gets its extension.
181 : * @param aExtension The extension to search for
182 : * @param aMIMEType [out] The found MIME type.
183 : * @return true if the extension was found, false otherwise.
184 : */
185 : NS_HIDDEN_(bool) GetTypeFromExtras(const nsACString& aExtension,
186 : nsACString& aMIMEType);
187 :
188 : /**
189 : * Fixes the file permissions to be correct. Base class has a no-op
190 : * implementation, subclasses can use this to correctly inherit ACLs from the
191 : * parent directory, to make the permissions obey the umask, etc.
192 : */
193 : virtual void FixFilePermissions(nsILocalFile* aFile);
194 :
195 : #ifdef PR_LOGGING
196 : /**
197 : * NSPR Logging Module. Usage: set NSPR_LOG_MODULES=HelperAppService:level,
198 : * where level should be 2 for errors, 3 for debug messages from the cross-
199 : * platform nsExternalHelperAppService, and 4 for os-specific debug messages.
200 : */
201 : static PRLogModuleInfo* mLog;
202 :
203 : #endif
204 : // friend, so that it can access the nspr log module and FixFilePermissions
205 : friend class nsExternalAppHandler;
206 : friend class nsExternalLoadRequest;
207 :
208 : /**
209 : * Helper function for ExpungeTemporaryFiles and ExpungeTemporaryPrivateFiles
210 : */
211 : static void ExpungeTemporaryFilesHelper(nsCOMArray<nsILocalFile> &fileList);
212 : /**
213 : * Functions related to the tempory file cleanup service provided by
214 : * nsExternalHelperAppService
215 : */
216 : void ExpungeTemporaryFiles();
217 : /**
218 : * Functions related to the tempory file cleanup service provided by
219 : * nsExternalHelperAppService (for the temporary files added during
220 : * the private browsing mode)
221 : */
222 : void ExpungeTemporaryPrivateFiles();
223 : /**
224 : * Array for the files that should be deleted
225 : */
226 : nsCOMArray<nsILocalFile> mTemporaryFilesList;
227 : /**
228 : * Array for the files that should be deleted (for the temporary files
229 : * added during the private browsing mode)
230 : */
231 : nsCOMArray<nsILocalFile> mTemporaryPrivateFilesList;
232 : /**
233 : * Whether we are in private browsing mode
234 : */
235 : bool mInPrivateBrowsing;
236 : };
237 :
238 : /**
239 : * An external app handler is just a small little class that presents itself as
240 : * a nsIStreamListener. It saves the incoming data into a temp file. The handler
241 : * is bound to an application when it is created. When it receives an
242 : * OnStopRequest it launches the application using the temp file it has
243 : * stored the data into. We create a handler every time we have to process
244 : * data using a helper app.
245 : */
246 : class nsExternalAppHandler : public nsIStreamListener,
247 : public nsIHelperAppLauncher,
248 : public nsITimerCallback
249 : {
250 : public:
251 : NS_DECL_ISUPPORTS
252 : NS_DECL_NSISTREAMLISTENER
253 : NS_DECL_NSIREQUESTOBSERVER
254 : NS_DECL_NSIHELPERAPPLAUNCHER
255 : NS_DECL_NSICANCELABLE
256 : NS_DECL_NSITIMERCALLBACK
257 :
258 : /**
259 : * @param aMIMEInfo MIMEInfo object, representing the type of the
260 : * content that should be handled
261 : * @param aFileExtension The extension we need to append to our temp file,
262 : * INCLUDING the ".". e.g. .mp3
263 : * @param aWindowContext Window context, as passed to DoContent
264 : * @param mExtProtSvc nsExternalHelperAppService on creation
265 : * @param aFileName The filename to use
266 : * @param aReason A constant from nsIHelperAppLauncherDialog indicating
267 : * why the request is handled by a helper app.
268 : */
269 : nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension,
270 : nsIInterfaceRequestor * aWindowContext,
271 : nsExternalHelperAppService * aExtProtSvc,
272 : const nsAString& aFilename,
273 : PRUint32 aReason, bool aForceSave);
274 :
275 : ~nsExternalAppHandler();
276 :
277 : protected:
278 : nsCOMPtr<nsIFile> mTempFile;
279 : nsCOMPtr<nsIURI> mSourceUrl;
280 : nsString mTempFileExtension;
281 : /**
282 : * The MIME Info for this load. Will never be null.
283 : */
284 : nsCOMPtr<nsIMIMEInfo> mMimeInfo;
285 : nsCOMPtr<nsIOutputStream> mOutStream; /**< output stream to the temp file */
286 : nsCOMPtr<nsIInterfaceRequestor> mWindowContext;
287 :
288 : /**
289 : * Used to close the window on a timer, to avoid any exceptions that are
290 : * thrown if we try to close the window before it's fully loaded.
291 : */
292 : nsCOMPtr<nsIDOMWindow> mWindowToClose;
293 : nsCOMPtr<nsITimer> mTimer;
294 :
295 : /**
296 : * The following field is set if we were processing an http channel that had
297 : * a content disposition header which specified the SUGGESTED file name we
298 : * should present to the user in the save to disk dialog.
299 : */
300 : nsString mSuggestedFileName;
301 :
302 : /**
303 : * If set, this handler should forcibly save the file to disk regardless of
304 : * MIME info settings or anything else, without ever popping up the
305 : * unknown content type handling dialog.
306 : */
307 : bool mForceSave;
308 :
309 : /**
310 : * The canceled flag is set if the user canceled the launching of this
311 : * application before we finished saving the data to a temp file.
312 : */
313 : bool mCanceled;
314 :
315 : /**
316 : * This is set based on whether the channel indicates that a new window
317 : * was opened specifically for this download. If so, then we
318 : * close it.
319 : */
320 : bool mShouldCloseWindow;
321 :
322 : /**
323 : * have we received information from the user about how they want to
324 : * dispose of this content
325 : */
326 : bool mReceivedDispositionInfo;
327 : bool mStopRequestIssued;
328 : bool mProgressListenerInitialized;
329 :
330 : bool mIsFileChannel;
331 :
332 : /**
333 : * One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
334 : * reason the dialog was shown (unknown content type, server requested it,
335 : * etc).
336 : */
337 : PRUint32 mReason;
338 :
339 : /**
340 : * Track the executable-ness of the temporary file.
341 : */
342 : bool mTempFileIsExecutable;
343 :
344 : PRTime mTimeDownloadStarted;
345 : PRInt64 mContentLength;
346 : PRInt64 mProgress; /**< Number of bytes received (for sending progress notifications). */
347 :
348 : /**
349 : * When we are told to save the temp file to disk (in a more permament
350 : * location) before we are done writing the content to a temp file, then
351 : * we need to remember the final destination until we are ready to use it.
352 : */
353 : nsCOMPtr<nsIFile> mFinalFileDestination;
354 :
355 : PRUint32 mBufferSize;
356 : char *mDataBuffer;
357 :
358 : /**
359 : * Creates the temporary file for the download and an output stream for it.
360 : * Upon successful return, both mTempFile and mOutStream will be valid.
361 : */
362 : nsresult SetUpTempFile(nsIChannel * aChannel);
363 : /**
364 : * When we download a helper app, we are going to retarget all load
365 : * notifications into our own docloader and load group instead of
366 : * using the window which initiated the load....RetargetLoadNotifications
367 : * contains that information...
368 : */
369 : void RetargetLoadNotifications(nsIRequest *request);
370 : /**
371 : * If the user tells us how they want to dispose of the content and
372 : * we still haven't finished downloading while they were deciding,
373 : * then create a progress listener of some kind so they know
374 : * what's going on...
375 : */
376 : nsresult CreateProgressListener();
377 : nsresult PromptForSaveToFile(nsILocalFile ** aNewFile,
378 : const nsAFlatString &aDefaultFile,
379 : const nsAFlatString &aDefaultFileExt);
380 :
381 : /**
382 : * After we're done prompting the user for any information, if the original
383 : * channel had a refresh url associated with it (which might point to a
384 : * "thank you for downloading" kind of page, then process that....It is safe
385 : * to invoke this method multiple times. We'll clear mOriginalChannel after
386 : * it's called and this ensures we won't call it again....
387 : */
388 : void ProcessAnyRefreshTags();
389 :
390 : /**
391 : * An internal method used to actually move the temp file to the final
392 : * destination once we done receiving data AND have showed the progress dialog
393 : */
394 : nsresult MoveFile(nsIFile * aNewFileLocation);
395 : /**
396 : * An internal method used to actually launch a helper app given the temp file
397 : * once we are done receiving data AND have showed the progress dialog.
398 : * Uses the application specified in the mime info.
399 : */
400 : nsresult OpenWithApplication();
401 :
402 : /**
403 : * Helper routine which peaks at the mime action specified by mMimeInfo
404 : * and calls either MoveFile or OpenWithApplication
405 : */
406 : nsresult ExecuteDesiredAction();
407 : /**
408 : * Helper routine that searches a pref string for a given mime type
409 : */
410 : bool GetNeverAskFlagFromPref(const char * prefName, const char * aContentType);
411 :
412 : /**
413 : * Initialize an nsITransfer object for use as a progress object
414 : */
415 : nsresult InitializeDownload(nsITransfer*);
416 :
417 : /**
418 : * Helper routine to ensure mSuggestedFileName is "correct";
419 : * this ensures that mTempFileExtension only contains an extension when it
420 : * is different from mSuggestedFileName's extension.
421 : */
422 : void EnsureSuggestedFileName();
423 :
424 : typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
425 : /**
426 : * Utility function to send proper error notification to web progress listener
427 : */
428 : void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsAFlatString &path);
429 :
430 : /**
431 : * Closes the window context if it does not have a refresh header
432 : * and it never displayed content before the external helper app
433 : * service was invoked.
434 : */
435 : nsresult MaybeCloseWindow();
436 :
437 : nsCOMPtr<nsIWebProgressListener2> mWebProgressListener;
438 : nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
439 : nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;
440 :
441 : /**
442 : * Keep request alive in case when helper non-modal dialog shown.
443 : * Thus in OnStopRequest the mRequest will not be set to null (it will be set to null further).
444 : */
445 : bool mKeepRequestAlive;
446 :
447 : /**
448 : * The request that's being loaded. Initialized in OnStartRequest.
449 : * Nulled out in OnStopRequest or once we know what we're doing
450 : * with the data, whichever happens later.
451 : */
452 : nsCOMPtr<nsIRequest> mRequest;
453 :
454 : nsRefPtr<nsExternalHelperAppService> mExtProtSvc;
455 : };
456 :
457 : #endif // nsExternalHelperAppService_h__
|