1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim:set ts=2 sts=2 sw=2 et cin:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org Code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Scott MacGregor <mscott@netscape.com>
26 : * Dan Mosedale <dmose@mozilla.org>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsIURI.h"
43 : #include "nsIURL.h"
44 : #include "nsExternalProtocolHandler.h"
45 : #include "nsXPIDLString.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsCOMPtr.h"
48 : #include "nsIServiceManager.h"
49 : #include "nsServiceManagerUtils.h"
50 : #include "nsIInterfaceRequestor.h"
51 : #include "nsIInterfaceRequestorUtils.h"
52 : #include "nsIStringBundle.h"
53 : #include "nsIPrefService.h"
54 : #include "nsIPrompt.h"
55 : #include "nsNetUtil.h"
56 : #include "nsExternalHelperAppService.h"
57 :
58 : // used to dispatch urls to default protocol handlers
59 : #include "nsCExternalHandlerService.h"
60 : #include "nsIExternalProtocolService.h"
61 :
62 : ////////////////////////////////////////////////////////////////////////
63 : // a stub channel implemenation which will map calls to AsyncRead and OpenInputStream
64 : // to calls in the OS for loading the url.
65 : ////////////////////////////////////////////////////////////////////////
66 :
67 : class nsExtProtocolChannel : public nsIChannel
68 : {
69 : public:
70 : NS_DECL_ISUPPORTS
71 : NS_DECL_NSICHANNEL
72 : NS_DECL_NSIREQUEST
73 :
74 : nsExtProtocolChannel();
75 : virtual ~nsExtProtocolChannel();
76 :
77 : nsresult SetURI(nsIURI*);
78 :
79 : private:
80 : nsresult OpenURL();
81 : void Finish(nsresult aResult);
82 :
83 : nsCOMPtr<nsIURI> mUrl;
84 : nsCOMPtr<nsIURI> mOriginalURI;
85 : nsresult mStatus;
86 : nsLoadFlags mLoadFlags;
87 : bool mWasOpened;
88 :
89 : nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
90 : nsCOMPtr<nsILoadGroup> mLoadGroup;
91 : };
92 :
93 0 : NS_IMPL_THREADSAFE_ADDREF(nsExtProtocolChannel)
94 0 : NS_IMPL_THREADSAFE_RELEASE(nsExtProtocolChannel)
95 :
96 0 : NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel)
97 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
98 0 : NS_INTERFACE_MAP_ENTRY(nsIChannel)
99 0 : NS_INTERFACE_MAP_ENTRY(nsIRequest)
100 0 : NS_INTERFACE_MAP_END_THREADSAFE
101 :
102 0 : nsExtProtocolChannel::nsExtProtocolChannel() : mStatus(NS_OK),
103 0 : mWasOpened(false)
104 : {
105 0 : }
106 :
107 0 : nsExtProtocolChannel::~nsExtProtocolChannel()
108 0 : {}
109 :
110 0 : NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
111 : {
112 0 : NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
113 0 : return NS_OK;
114 : }
115 :
116 0 : NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
117 : {
118 0 : mLoadGroup = aLoadGroup;
119 0 : return NS_OK;
120 : }
121 :
122 0 : NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
123 : {
124 0 : NS_IF_ADDREF(*aCallbacks = mCallbacks);
125 0 : return NS_OK;
126 : }
127 :
128 0 : NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
129 : {
130 0 : mCallbacks = aCallbacks;
131 0 : return NS_OK;
132 : }
133 :
134 : NS_IMETHODIMP
135 0 : nsExtProtocolChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
136 : {
137 0 : *aSecurityInfo = nsnull;
138 0 : return NS_OK;
139 : }
140 :
141 0 : NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI* *aURI)
142 : {
143 0 : NS_ADDREF(*aURI = mOriginalURI);
144 0 : return NS_OK;
145 : }
146 :
147 0 : NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI)
148 : {
149 0 : NS_ENSURE_ARG_POINTER(aURI);
150 0 : mOriginalURI = aURI;
151 0 : return NS_OK;
152 : }
153 :
154 0 : NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI* *aURI)
155 : {
156 0 : *aURI = mUrl;
157 0 : NS_IF_ADDREF(*aURI);
158 0 : return NS_OK;
159 : }
160 :
161 0 : nsresult nsExtProtocolChannel::SetURI(nsIURI* aURI)
162 : {
163 0 : mUrl = aURI;
164 0 : return NS_OK;
165 : }
166 :
167 0 : nsresult nsExtProtocolChannel::OpenURL()
168 : {
169 0 : nsresult rv = NS_ERROR_FAILURE;
170 0 : nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
171 :
172 0 : if (extProtService)
173 : {
174 : #ifdef DEBUG
175 0 : nsCAutoString urlScheme;
176 0 : mUrl->GetScheme(urlScheme);
177 0 : bool haveHandler = false;
178 0 : extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler);
179 0 : NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?");
180 : #endif
181 :
182 0 : nsCOMPtr<nsIInterfaceRequestor> aggCallbacks;
183 : rv = NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
184 0 : getter_AddRefs(aggCallbacks));
185 0 : if (NS_FAILED(rv)) {
186 : goto finish;
187 : }
188 :
189 0 : rv = extProtService->LoadURI(mUrl, aggCallbacks);
190 0 : if (NS_SUCCEEDED(rv)) {
191 : // despite success, we need to abort this channel, at the very least
192 : // to make it clear to the caller that no on{Start,Stop}Request
193 : // should be expected.
194 0 : rv = NS_ERROR_NO_CONTENT;
195 : }
196 : }
197 :
198 : finish:
199 0 : mCallbacks = 0;
200 0 : return rv;
201 : }
202 :
203 0 : NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval)
204 : {
205 0 : return OpenURL();
206 : }
207 :
208 0 : NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
209 : {
210 0 : NS_ENSURE_ARG_POINTER(listener);
211 0 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
212 :
213 0 : mWasOpened = true;
214 :
215 0 : return OpenURL();
216 : }
217 :
218 0 : NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
219 : {
220 0 : *aLoadFlags = mLoadFlags;
221 0 : return NS_OK;
222 : }
223 :
224 0 : NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
225 : {
226 0 : mLoadFlags = aLoadFlags;
227 0 : return NS_OK;
228 : }
229 :
230 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType)
231 : {
232 0 : return NS_ERROR_NOT_IMPLEMENTED;
233 : }
234 :
235 0 : NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType)
236 : {
237 0 : return NS_ERROR_FAILURE;
238 : }
239 :
240 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(nsACString &aContentCharset)
241 : {
242 0 : return NS_ERROR_NOT_IMPLEMENTED;
243 : }
244 :
245 0 : NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
246 : {
247 0 : return NS_ERROR_NOT_IMPLEMENTED;
248 : }
249 :
250 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(PRUint32 *aContentDisposition)
251 : {
252 0 : return NS_ERROR_NOT_AVAILABLE;
253 : }
254 :
255 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
256 : {
257 0 : return NS_ERROR_NOT_AVAILABLE;
258 : }
259 :
260 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
261 : {
262 0 : return NS_ERROR_NOT_AVAILABLE;
263 : }
264 :
265 0 : NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(PRInt32 * aContentLength)
266 : {
267 0 : *aContentLength = -1;
268 0 : return NS_OK;
269 : }
270 :
271 : NS_IMETHODIMP
272 0 : nsExtProtocolChannel::SetContentLength(PRInt32 aContentLength)
273 : {
274 0 : NS_NOTREACHED("SetContentLength");
275 0 : return NS_ERROR_NOT_IMPLEMENTED;
276 : }
277 :
278 0 : NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports * *aPrincipal)
279 : {
280 0 : NS_NOTREACHED("GetOwner");
281 0 : return NS_ERROR_NOT_IMPLEMENTED;
282 : }
283 :
284 0 : NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports * aPrincipal)
285 : {
286 0 : NS_NOTREACHED("SetOwner");
287 0 : return NS_ERROR_NOT_IMPLEMENTED;
288 : }
289 :
290 : ////////////////////////////////////////////////////////////////////////////////
291 : // From nsIRequest
292 : ////////////////////////////////////////////////////////////////////////////////
293 :
294 0 : NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString &result)
295 : {
296 0 : return mUrl->GetSpec(result);
297 : }
298 :
299 0 : NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool *result)
300 : {
301 0 : *result = false;
302 0 : return NS_OK;
303 : }
304 :
305 0 : NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult *status)
306 : {
307 0 : *status = mStatus;
308 0 : return NS_OK;
309 : }
310 :
311 0 : NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status)
312 : {
313 0 : mStatus = status;
314 0 : return NS_OK;
315 : }
316 :
317 0 : NS_IMETHODIMP nsExtProtocolChannel::Suspend()
318 : {
319 0 : NS_NOTREACHED("Suspend");
320 0 : return NS_ERROR_NOT_IMPLEMENTED;
321 : }
322 :
323 0 : NS_IMETHODIMP nsExtProtocolChannel::Resume()
324 : {
325 0 : NS_NOTREACHED("Resume");
326 0 : return NS_ERROR_NOT_IMPLEMENTED;
327 : }
328 :
329 : ///////////////////////////////////////////////////////////////////////
330 : // the default protocol handler implementation
331 : //////////////////////////////////////////////////////////////////////
332 :
333 13 : nsExternalProtocolHandler::nsExternalProtocolHandler()
334 : {
335 13 : m_schemeName = "default";
336 13 : }
337 :
338 :
339 13 : nsExternalProtocolHandler::~nsExternalProtocolHandler()
340 13 : {}
341 :
342 811 : NS_IMPL_THREADSAFE_ADDREF(nsExternalProtocolHandler)
343 824 : NS_IMPL_THREADSAFE_RELEASE(nsExternalProtocolHandler)
344 :
345 522 : NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler)
346 522 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler)
347 522 : NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
348 0 : NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler)
349 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
350 0 : NS_INTERFACE_MAP_END_THREADSAFE
351 :
352 0 : NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme)
353 : {
354 0 : aScheme = m_schemeName;
355 0 : return NS_OK;
356 : }
357 :
358 0 : NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
359 : {
360 0 : *aDefaultPort = 0;
361 0 : return NS_OK;
362 : }
363 :
364 : NS_IMETHODIMP
365 0 : nsExternalProtocolHandler::AllowPort(PRInt32 port, const char *scheme, bool *_retval)
366 : {
367 : // don't override anything.
368 0 : *_retval = false;
369 0 : return NS_OK;
370 : }
371 : // returns TRUE if the OS can handle this protocol scheme and false otherwise.
372 3 : bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI * aURI)
373 : {
374 3 : bool haveHandler = false;
375 3 : if (aURI)
376 : {
377 6 : nsCAutoString scheme;
378 3 : aURI->GetScheme(scheme);
379 6 : nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
380 3 : if (extProtSvc)
381 3 : extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler);
382 : }
383 :
384 3 : return haveHandler;
385 : }
386 :
387 24 : NS_IMETHODIMP nsExternalProtocolHandler::GetProtocolFlags(PRUint32 *aUritype)
388 : {
389 : // Make it norelative since it is a simple uri
390 : *aUritype = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE |
391 24 : URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA;
392 24 : return NS_OK;
393 : }
394 :
395 235 : NS_IMETHODIMP nsExternalProtocolHandler::NewURI(const nsACString &aSpec,
396 : const char *aCharset, // ignore charset info
397 : nsIURI *aBaseURI,
398 : nsIURI **_retval)
399 : {
400 : nsresult rv;
401 470 : nsCOMPtr<nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
402 235 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 235 : rv = uri->SetSpec(aSpec);
405 235 : NS_ENSURE_SUCCESS(rv, rv);
406 :
407 230 : NS_ADDREF(*_retval = uri);
408 230 : return NS_OK;
409 : }
410 :
411 3 : NS_IMETHODIMP nsExternalProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
412 : {
413 : // Only try to return a channel if we have a protocol handler for the url.
414 : // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness
415 : // for some platforms at least. (win uses ::ShellExecute and unix uses
416 : // gnome_url_show.)
417 3 : bool haveExternalHandler = HaveExternalProtocolHandler(aURI);
418 3 : if (haveExternalHandler)
419 : {
420 0 : nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel();
421 0 : if (!channel) return NS_ERROR_OUT_OF_MEMORY;
422 :
423 0 : ((nsExtProtocolChannel*) channel.get())->SetURI(aURI);
424 0 : channel->SetOriginalURI(aURI);
425 :
426 0 : if (_retval)
427 : {
428 0 : *_retval = channel;
429 0 : NS_IF_ADDREF(*_retval);
430 0 : return NS_OK;
431 : }
432 : }
433 :
434 3 : return NS_ERROR_UNKNOWN_PROTOCOL;
435 : }
436 :
437 : ///////////////////////////////////////////////////////////////////////
438 : // External protocol handler interface implementation
439 : //////////////////////////////////////////////////////////////////////
440 0 : NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(const nsACString& aScheme, bool *_retval)
441 : {
442 0 : nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
443 0 : if (extProtSvc)
444 0 : return extProtSvc->ExternalProtocolHandlerExists(
445 0 : PromiseFlatCString(aScheme).get(), _retval);
446 :
447 : // In case we don't have external protocol service.
448 0 : *_retval = false;
449 0 : return NS_OK;
450 : }
|