1 : //* -*- Mode: C++; tab-width: 8; 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 Mozilla Annotation Service
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2005
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Brett Wilson <brettw@gmail.com> (original author)
24 : * Shawn Wilsher <me@shawnwilsher.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /**
41 : * Implementation of moz-anno: URLs for accessing annotation values. This just
42 : * reads binary data from the annotation service.
43 : *
44 : * There is a special case for favicons. Annotation URLs with the name "favicon"
45 : * will be sent to the favicon service. If the favicon service doesn't have the
46 : * data, a stream containing the default favicon will be returned.
47 : */
48 :
49 : #include "nsAnnoProtocolHandler.h"
50 : #include "nsFaviconService.h"
51 : #include "nsIChannel.h"
52 : #include "nsIInputStreamChannel.h"
53 : #include "nsILoadGroup.h"
54 : #include "nsIStandardURL.h"
55 : #include "nsIStringStream.h"
56 : #include "nsISupportsUtils.h"
57 : #include "nsIURI.h"
58 : #include "nsNetUtil.h"
59 : #include "nsServiceManagerUtils.h"
60 : #include "nsStringStream.h"
61 : #include "mozilla/storage.h"
62 : #include "nsIPipe.h"
63 : #include "Helpers.h"
64 :
65 : using namespace mozilla;
66 : using namespace mozilla::places;
67 :
68 : ////////////////////////////////////////////////////////////////////////////////
69 : //// Global Functions
70 :
71 : /**
72 : * Creates a channel to obtain the default favicon.
73 : */
74 : static
75 : nsresult
76 1 : GetDefaultIcon(nsIChannel **aChannel)
77 : {
78 2 : nsCOMPtr<nsIURI> defaultIconURI;
79 1 : nsresult rv = NS_NewURI(getter_AddRefs(defaultIconURI),
80 2 : NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
81 1 : NS_ENSURE_SUCCESS(rv, rv);
82 1 : return NS_NewChannel(aChannel, defaultIconURI);
83 : }
84 :
85 : ////////////////////////////////////////////////////////////////////////////////
86 : //// faviconAsyncLoader
87 :
88 : namespace {
89 :
90 : /**
91 : * An instance of this class is passed to the favicon service as the callback
92 : * for getting favicon data from the database. We'll get this data back in
93 : * HandleResult, and on HandleCompletion, we'll close our output stream which
94 : * will close the original channel for the favicon request.
95 : *
96 : * However, if an error occurs at any point, we do not set mReturnDefaultIcon to
97 : * false, so we will open up another channel to get the default favicon, and
98 : * pass that along to our output stream in HandleCompletion. If anything
99 : * happens at that point, the world must be against us, so we return nothing.
100 : */
101 : class faviconAsyncLoader : public AsyncStatementCallback
102 : , public nsIRequestObserver
103 8 : {
104 : public:
105 : NS_DECL_ISUPPORTS_INHERITED
106 :
107 2 : faviconAsyncLoader(nsIChannel *aChannel, nsIOutputStream *aOutputStream) :
108 : mChannel(aChannel)
109 : , mOutputStream(aOutputStream)
110 2 : , mReturnDefaultIcon(true)
111 : {
112 2 : NS_ASSERTION(aChannel,
113 : "Not providing a channel will result in crashes!");
114 2 : NS_ASSERTION(aOutputStream,
115 : "Not providing an output stream will result in crashes!");
116 2 : }
117 :
118 : //////////////////////////////////////////////////////////////////////////////
119 : //// mozIStorageStatementCallback
120 :
121 1 : NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet)
122 : {
123 : // We will only get one row back in total, so we do not need to loop.
124 2 : nsCOMPtr<mozIStorageRow> row;
125 1 : nsresult rv = aResultSet->GetNextRow(getter_AddRefs(row));
126 1 : NS_ENSURE_SUCCESS(rv, rv);
127 :
128 : // We do not allow favicons without a MIME type, so we'll return the default
129 : // icon.
130 2 : nsCAutoString mimeType;
131 1 : (void)row->GetUTF8String(1, mimeType);
132 1 : NS_ENSURE_FALSE(mimeType.IsEmpty(), NS_OK);
133 :
134 : // Set our mimeType now that we know it.
135 1 : rv = mChannel->SetContentType(mimeType);
136 1 : NS_ENSURE_SUCCESS(rv, rv);
137 :
138 : // Obtain the binary blob that contains our favicon data.
139 : PRUint8 *favicon;
140 1 : PRUint32 size = 0;
141 1 : rv = row->GetBlob(0, &size, &favicon);
142 1 : NS_ENSURE_SUCCESS(rv, rv);
143 :
144 1 : PRUint32 totalWritten = 0;
145 1 : do {
146 : PRUint32 bytesWritten;
147 1 : rv = mOutputStream->Write(
148 : &(reinterpret_cast<const char *>(favicon)[totalWritten]),
149 : size - totalWritten,
150 : &bytesWritten
151 1 : );
152 1 : if (NS_FAILED(rv) || !bytesWritten)
153 0 : break;
154 1 : totalWritten += bytesWritten;
155 : } while (size != totalWritten);
156 1 : NS_ASSERTION(NS_FAILED(rv) || size == totalWritten,
157 : "Failed to write all of our data out to the stream!");
158 :
159 : // Free our favicon array.
160 1 : NS_Free(favicon);
161 :
162 : // Handle an error to write if it occurred, but only after we've freed our
163 : // favicon.
164 1 : NS_ENSURE_SUCCESS(rv, rv);
165 :
166 : // At this point, we should have written out all of our data to our stream.
167 : // HandleCompletion will close the output stream, so we are done here.
168 1 : mReturnDefaultIcon = false;
169 1 : return NS_OK;
170 : }
171 :
172 2 : NS_IMETHOD HandleCompletion(PRUint16 aReason)
173 : {
174 2 : if (!mReturnDefaultIcon)
175 1 : return mOutputStream->Close();
176 :
177 : // We need to return our default icon, so we'll open up a new channel to get
178 : // that data, and push it to our output stream. If at any point we get an
179 : // error, we can't do anything, so we'll just close our output stream.
180 2 : nsCOMPtr<nsIStreamListener> listener;
181 1 : nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(listener),
182 1 : mOutputStream, this);
183 1 : NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
184 :
185 2 : nsCOMPtr<nsIChannel> newChannel;
186 1 : rv = GetDefaultIcon(getter_AddRefs(newChannel));
187 1 : NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
188 :
189 1 : rv = newChannel->AsyncOpen(listener, nsnull);
190 1 : NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
191 :
192 1 : return NS_OK;
193 : }
194 :
195 : //////////////////////////////////////////////////////////////////////////////
196 : //// nsIRequestObserver
197 :
198 1 : NS_IMETHOD OnStartRequest(nsIRequest *, nsISupports *)
199 : {
200 1 : return NS_OK;
201 : }
202 :
203 1 : NS_IMETHOD OnStopRequest(nsIRequest *, nsISupports *, nsresult aStatusCode)
204 : {
205 : // We always need to close our output stream, regardless of the status code.
206 1 : (void)mOutputStream->Close();
207 :
208 : // But, we'll warn about it not being successful if it wasn't.
209 1 : NS_WARN_IF_FALSE(NS_SUCCEEDED(aStatusCode),
210 : "Got an error when trying to load our default favicon!");
211 :
212 1 : return NS_OK;
213 : }
214 :
215 : private:
216 : nsCOMPtr<nsIChannel> mChannel;
217 : nsCOMPtr<nsIOutputStream> mOutputStream;
218 : bool mReturnDefaultIcon;
219 : };
220 :
221 22 : NS_IMPL_ISUPPORTS_INHERITED1(
222 : faviconAsyncLoader,
223 : AsyncStatementCallback,
224 : nsIRequestObserver
225 : )
226 :
227 : } // anonymous namespace
228 :
229 : ////////////////////////////////////////////////////////////////////////////////
230 : //// nsAnnoProtocolHandler
231 :
232 238 : NS_IMPL_ISUPPORTS1(nsAnnoProtocolHandler, nsIProtocolHandler)
233 :
234 : // nsAnnoProtocolHandler::GetScheme
235 :
236 : NS_IMETHODIMP
237 0 : nsAnnoProtocolHandler::GetScheme(nsACString& aScheme)
238 : {
239 0 : aScheme.AssignLiteral("moz-anno");
240 0 : return NS_OK;
241 : }
242 :
243 :
244 : // nsAnnoProtocolHandler::GetDefaultPort
245 : //
246 : // There is no default port for annotation URLs
247 :
248 : NS_IMETHODIMP
249 0 : nsAnnoProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
250 : {
251 0 : *aDefaultPort = -1;
252 0 : return NS_OK;
253 : }
254 :
255 :
256 : // nsAnnoProtocolHandler::GetProtocolFlags
257 :
258 : NS_IMETHODIMP
259 2 : nsAnnoProtocolHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
260 : {
261 : *aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
262 2 : URI_IS_LOCAL_RESOURCE);
263 2 : return NS_OK;
264 : }
265 :
266 :
267 : // nsAnnoProtocolHandler::NewURI
268 :
269 : NS_IMETHODIMP
270 24 : nsAnnoProtocolHandler::NewURI(const nsACString& aSpec,
271 : const char *aOriginCharset,
272 : nsIURI *aBaseURI, nsIURI **_retval)
273 : {
274 48 : nsCOMPtr <nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID);
275 24 : if (!uri)
276 0 : return NS_ERROR_OUT_OF_MEMORY;
277 24 : nsresult rv = uri->SetSpec(aSpec);
278 24 : NS_ENSURE_SUCCESS(rv, rv);
279 :
280 24 : *_retval = nsnull;
281 24 : uri.swap(*_retval);
282 24 : return NS_OK;
283 : }
284 :
285 :
286 : // nsAnnoProtocolHandler::NewChannel
287 : //
288 :
289 : NS_IMETHODIMP
290 2 : nsAnnoProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
291 : {
292 2 : NS_ENSURE_ARG_POINTER(aURI);
293 : nsresult rv;
294 :
295 4 : nsCAutoString path;
296 2 : rv = aURI->GetPath(path);
297 2 : NS_ENSURE_SUCCESS(rv, rv);
298 :
299 : nsCOMPtr<nsIAnnotationService> annotationService = do_GetService(
300 4 : "@mozilla.org/browser/annotation-service;1", &rv);
301 2 : NS_ENSURE_SUCCESS(rv, rv);
302 :
303 : // annotation info
304 4 : nsCOMPtr<nsIURI> annoURI;
305 4 : nsCAutoString annoName;
306 2 : rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName);
307 2 : NS_ENSURE_SUCCESS(rv, rv);
308 :
309 : // If this is a favicon annotation, we create a different channel that will
310 : // ask the favicon service for information about the favicon.
311 2 : if (annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME))
312 2 : return NewFaviconChannel(aURI, annoURI, _retval);
313 :
314 : // normal handling for annotations
315 : PRUint8* data;
316 : PRUint32 dataLen;
317 0 : nsCAutoString mimeType;
318 :
319 : // get the data from the annotation service and hand it off to the stream
320 0 : rv = annotationService->GetPageAnnotationBinary(annoURI, annoName, &data,
321 0 : &dataLen, mimeType);
322 0 : NS_ENSURE_SUCCESS(rv, rv);
323 :
324 : // disallow annotations with no MIME types
325 0 : if (mimeType.IsEmpty()) {
326 0 : NS_Free(data);
327 0 : return NS_ERROR_NOT_AVAILABLE;
328 : }
329 :
330 : nsCOMPtr<nsIStringInputStream> stream = do_CreateInstance(
331 0 : NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
332 0 : if (NS_FAILED(rv)) {
333 0 : NS_Free(data);
334 0 : return rv;
335 : }
336 0 : rv = stream->AdoptData((char*)data, dataLen);
337 0 : if (NS_FAILED(rv)) {
338 0 : NS_Free(data);
339 0 : return rv;
340 : }
341 :
342 0 : nsCOMPtr<nsIChannel> channel;
343 0 : rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, stream, mimeType);
344 0 : NS_ENSURE_SUCCESS(rv, rv);
345 :
346 0 : *_retval = channel;
347 0 : NS_ADDREF(*_retval);
348 0 : return NS_OK;
349 : }
350 :
351 :
352 : // nsAnnoProtocolHandler::AllowPort
353 : //
354 : // Don't override any bans on bad ports.
355 :
356 : NS_IMETHODIMP
357 0 : nsAnnoProtocolHandler::AllowPort(PRInt32 port, const char *scheme,
358 : bool *_retval)
359 : {
360 0 : *_retval = false;
361 0 : return NS_OK;
362 : }
363 :
364 :
365 : // nsAnnoProtocolHandler::ParseAnnoURI
366 : //
367 : // Splits an annotation URL into its URI and name parts
368 :
369 : nsresult
370 2 : nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI,
371 : nsIURI** aResultURI, nsCString& aName)
372 : {
373 : nsresult rv;
374 4 : nsCAutoString path;
375 2 : rv = aURI->GetPath(path);
376 2 : NS_ENSURE_SUCCESS(rv, rv);
377 :
378 2 : PRInt32 firstColon = path.FindChar(':');
379 2 : if (firstColon <= 0)
380 0 : return NS_ERROR_MALFORMED_URI;
381 :
382 2 : rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1));
383 2 : NS_ENSURE_SUCCESS(rv, rv);
384 :
385 2 : aName = Substring(path, 0, firstColon);
386 2 : return NS_OK;
387 : }
388 :
389 : nsresult
390 2 : nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI,
391 : nsIChannel **_channel)
392 : {
393 : // Create our pipe. This will give us our input stream and output stream
394 : // that will be written to when we get data from the database.
395 4 : nsCOMPtr<nsIInputStream> inputStream;
396 4 : nsCOMPtr<nsIOutputStream> outputStream;
397 2 : nsresult rv = NS_NewPipe(getter_AddRefs(inputStream),
398 2 : getter_AddRefs(outputStream),
399 : MAX_FAVICON_SIZE, MAX_FAVICON_SIZE, true,
400 2 : true);
401 2 : NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
402 :
403 : // Create our channel. We'll call SetContentType with the right type when
404 : // we know what it actually is.
405 4 : nsCOMPtr<nsIChannel> channel;
406 2 : rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inputStream,
407 2 : EmptyCString());
408 2 : NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
409 :
410 : // Now we go ahead and get our data asynchronously for the favicon.
411 : nsCOMPtr<mozIStorageStatementCallback> callback =
412 6 : new faviconAsyncLoader(channel, outputStream);
413 2 : NS_ENSURE_TRUE(callback, GetDefaultIcon(_channel));
414 2 : nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
415 2 : NS_ENSURE_TRUE(faviconService, GetDefaultIcon(_channel));
416 :
417 2 : rv = faviconService->GetFaviconDataAsync(aAnnotationURI, callback);
418 2 : NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
419 :
420 2 : channel.forget(_channel);
421 2 : return NS_OK;
422 : }
|