1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org Code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Scott MacGregor <mscott@netscape.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 : #include "mozilla/Util.h"
41 :
42 : #include "nsIconURI.h"
43 : #include "nsNetUtil.h"
44 : #include "nsIIOService.h"
45 : #include "nsIURL.h"
46 : #include "prprf.h"
47 : #include "plstr.h"
48 : #include <stdlib.h>
49 :
50 : using namespace mozilla;
51 :
52 : #define DEFAULT_IMAGE_SIZE 16
53 :
54 : #if defined(MAX_PATH)
55 : #define SANE_FILE_NAME_LEN MAX_PATH
56 : #elif defined(PATH_MAX)
57 : #define SANE_FILE_NAME_LEN PATH_MAX
58 : #else
59 : #define SANE_FILE_NAME_LEN 1024
60 : #endif
61 :
62 : // helper function for parsing out attributes like size, and contentType
63 : // from the icon url.
64 : static void extractAttributeValue(const char * searchString, const char * attributeName, nsCString& aResult);
65 :
66 : static const char *kSizeStrings[] =
67 : {
68 : "button",
69 : "toolbar",
70 : "toolbarsmall",
71 : "menu",
72 : "dnd",
73 : "dialog"
74 : };
75 :
76 : static const char *kStateStrings[] =
77 : {
78 : "normal",
79 : "disabled"
80 : };
81 :
82 : ////////////////////////////////////////////////////////////////////////////////
83 :
84 1634 : nsMozIconURI::nsMozIconURI()
85 : : mSize(DEFAULT_IMAGE_SIZE),
86 : mIconSize(-1),
87 1634 : mIconState(-1)
88 : {
89 1634 : }
90 :
91 3268 : nsMozIconURI::~nsMozIconURI()
92 : {
93 6536 : }
94 :
95 29451 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsMozIconURI, nsIMozIconURI, nsIURI)
96 :
97 : #define MOZICON_SCHEME "moz-icon:"
98 : #define MOZICON_SCHEME_LEN (sizeof(MOZICON_SCHEME) - 1)
99 :
100 : ////////////////////////////////////////////////////////////////////////////////
101 : // nsIURI methods:
102 :
103 : NS_IMETHODIMP
104 0 : nsMozIconURI::GetSpec(nsACString &aSpec)
105 : {
106 0 : aSpec = MOZICON_SCHEME;
107 :
108 0 : if (mIconURL)
109 : {
110 0 : nsCAutoString fileIconSpec;
111 0 : nsresult rv = mIconURL->GetSpec(fileIconSpec);
112 0 : NS_ENSURE_SUCCESS(rv, rv);
113 0 : aSpec += fileIconSpec;
114 : }
115 0 : else if (!mStockIcon.IsEmpty())
116 : {
117 0 : aSpec += "//stock/";
118 0 : aSpec += mStockIcon;
119 : }
120 : else
121 : {
122 0 : aSpec += "//";
123 0 : aSpec += mFileName;
124 : }
125 :
126 0 : aSpec += "?size=";
127 0 : if (mIconSize >= 0)
128 : {
129 0 : aSpec += kSizeStrings[mIconSize];
130 : }
131 : else
132 : {
133 : char buf[20];
134 0 : PR_snprintf(buf, sizeof(buf), "%d", mSize);
135 0 : aSpec.Append(buf);
136 : }
137 :
138 0 : if (mIconState >= 0) {
139 0 : aSpec += "&state=";
140 0 : aSpec += kStateStrings[mIconState];
141 : }
142 :
143 0 : if (!mContentType.IsEmpty())
144 : {
145 0 : aSpec += "&contentType=";
146 0 : aSpec += mContentType.get();
147 : }
148 :
149 0 : return NS_OK;
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : nsMozIconURI::GetSpecIgnoringRef(nsACString &result)
154 : {
155 0 : return GetSpec(result);
156 : }
157 :
158 : NS_IMETHODIMP
159 0 : nsMozIconURI::GetHasRef(bool *result)
160 : {
161 0 : *result = false;
162 0 : return NS_OK;
163 : }
164 :
165 : // takes a string like ?size=32&contentType=text/html and returns a new string
166 : // containing just the attribute value. i.e you could pass in this string with
167 : // an attribute name of 'size=', this will return 32
168 : // Assumption: attribute pairs in the string are separated by '&'.
169 4881 : void extractAttributeValue(const char * searchString, const char * attributeName, nsCString& result)
170 : {
171 : //NS_ENSURE_ARG_POINTER(extractAttributeValue);
172 :
173 4881 : result.Truncate();
174 :
175 4881 : if (searchString && attributeName)
176 : {
177 : // search the string for attributeName
178 4881 : PRUint32 attributeNameSize = strlen(attributeName);
179 4881 : const char * startOfAttribute = PL_strcasestr(searchString, attributeName);
180 6512 : if (startOfAttribute &&
181 1631 : ( *(startOfAttribute-1) == '?' || *(startOfAttribute-1) == '&') )
182 : {
183 1629 : startOfAttribute += attributeNameSize; // skip over the attributeName
184 1629 : if (*startOfAttribute) // is there something after the attribute name
185 : {
186 1629 : const char * endofAttribute = strchr(startOfAttribute, '&');
187 1629 : if (endofAttribute)
188 2 : result.Assign(Substring(startOfAttribute, endofAttribute));
189 : else
190 1627 : result.Assign(startOfAttribute);
191 : } // if we have a attribute value
192 : } // if we have a attribute name
193 : } // if we got non-null search string and attribute name values
194 4881 : }
195 :
196 : NS_IMETHODIMP
197 1634 : nsMozIconURI::SetSpec(const nsACString &aSpec)
198 : {
199 : // Reset everything to default values.
200 1634 : mIconURL = nsnull;
201 1634 : mSize = DEFAULT_IMAGE_SIZE;
202 1634 : mContentType.Truncate();
203 1634 : mFileName.Truncate();
204 1634 : mStockIcon.Truncate();
205 1634 : mIconSize = -1;
206 1634 : mIconState = -1;
207 :
208 3268 : nsCAutoString iconSpec(aSpec);
209 1634 : if (!Substring(iconSpec, 0, MOZICON_SCHEME_LEN).EqualsLiteral(MOZICON_SCHEME))
210 0 : return NS_ERROR_MALFORMED_URI;
211 :
212 1634 : PRInt32 questionMarkPos = iconSpec.Find("?");
213 1634 : if (questionMarkPos != -1 && static_cast<PRInt32>(iconSpec.Length()) > (questionMarkPos + 1))
214 : {
215 1627 : extractAttributeValue(iconSpec.get(), "contentType=", mContentType);
216 :
217 3254 : nsCAutoString sizeString;
218 1627 : extractAttributeValue(iconSpec.get(), "size=", sizeString);
219 1627 : if (!sizeString.IsEmpty())
220 : {
221 1627 : const char *sizeStr = sizeString.get();
222 6511 : for (PRUint32 i = 0; i < ArrayLength(kSizeStrings); i++)
223 : {
224 6509 : if (PL_strcasecmp(sizeStr, kSizeStrings[i]) == 0)
225 : {
226 1625 : mIconSize = i;
227 1625 : break;
228 : }
229 : }
230 :
231 1627 : PRInt32 sizeValue = atoi(sizeString.get());
232 1627 : if (sizeValue)
233 2 : mSize = sizeValue;
234 : }
235 :
236 3254 : nsCAutoString stateString;
237 1627 : extractAttributeValue(iconSpec.get(), "state=", stateString);
238 1627 : if (!stateString.IsEmpty())
239 : {
240 1 : const char *stateStr = stateString.get();
241 1 : for (PRUint32 i = 0; i < ArrayLength(kStateStrings); i++)
242 : {
243 1 : if (PL_strcasecmp(stateStr, kStateStrings[i]) == 0)
244 : {
245 1 : mIconState = i;
246 1 : break;
247 : }
248 : }
249 : }
250 : }
251 :
252 1634 : PRInt32 pathLength = iconSpec.Length() - MOZICON_SCHEME_LEN;
253 1634 : if (questionMarkPos != -1)
254 1627 : pathLength = questionMarkPos - MOZICON_SCHEME_LEN;
255 1634 : if (pathLength < 3)
256 0 : return NS_ERROR_MALFORMED_URI;
257 :
258 3268 : nsCAutoString iconPath(Substring(iconSpec, MOZICON_SCHEME_LEN, pathLength));
259 :
260 : // Icon URI path can have three forms:
261 : // (1) //stock/<icon-identifier>
262 : // (2) //<some dummy file with an extension>
263 : // (3) a valid URL
264 :
265 1634 : if (!strncmp("//stock/", iconPath.get(), 8))
266 : {
267 1626 : mStockIcon.Assign(Substring(iconPath, 8));
268 : // An icon identifier must always be specified.
269 1626 : if (mStockIcon.IsEmpty())
270 1 : return NS_ERROR_MALFORMED_URI;
271 1625 : return NS_OK;
272 : }
273 :
274 8 : if (StringBeginsWith(iconPath, NS_LITERAL_CSTRING("//")))
275 : {
276 : // Sanity check this supposed dummy file name.
277 4 : if (iconPath.Length() > SANE_FILE_NAME_LEN)
278 1 : return NS_ERROR_MALFORMED_URI;
279 3 : iconPath.Cut(0, 2);
280 3 : mFileName.Assign(iconPath);
281 : }
282 :
283 : nsresult rv;
284 14 : nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
285 7 : NS_ENSURE_SUCCESS(rv, rv);
286 :
287 14 : nsCOMPtr<nsIURI> uri;
288 7 : ioService->NewURI(iconPath, nsnull, nsnull, getter_AddRefs(uri));
289 7 : mIconURL = do_QueryInterface(uri);
290 7 : if (mIconURL)
291 3 : mFileName.Truncate();
292 4 : else if (mFileName.IsEmpty())
293 2 : return NS_ERROR_MALFORMED_URI;
294 :
295 5 : return NS_OK;
296 : }
297 :
298 : NS_IMETHODIMP
299 0 : nsMozIconURI::GetPrePath(nsACString &prePath)
300 : {
301 0 : prePath = MOZICON_SCHEME;
302 0 : return NS_OK;
303 : }
304 :
305 : NS_IMETHODIMP
306 1626 : nsMozIconURI::GetScheme(nsACString &aScheme)
307 : {
308 1626 : aScheme = "moz-icon";
309 1626 : return NS_OK;
310 : }
311 :
312 : NS_IMETHODIMP
313 0 : nsMozIconURI::SetScheme(const nsACString &aScheme)
314 : {
315 : // doesn't make sense to set the scheme of a moz-icon URL
316 0 : return NS_ERROR_FAILURE;
317 : }
318 :
319 : NS_IMETHODIMP
320 0 : nsMozIconURI::GetUsername(nsACString &aUsername)
321 : {
322 0 : return NS_ERROR_FAILURE;
323 : }
324 :
325 : NS_IMETHODIMP
326 0 : nsMozIconURI::SetUsername(const nsACString &aUsername)
327 : {
328 0 : return NS_ERROR_FAILURE;
329 : }
330 :
331 : NS_IMETHODIMP
332 0 : nsMozIconURI::GetPassword(nsACString &aPassword)
333 : {
334 0 : return NS_ERROR_FAILURE;
335 : }
336 :
337 : NS_IMETHODIMP
338 0 : nsMozIconURI::SetPassword(const nsACString &aPassword)
339 : {
340 0 : return NS_ERROR_FAILURE;
341 : }
342 :
343 : NS_IMETHODIMP
344 0 : nsMozIconURI::GetUserPass(nsACString &aUserPass)
345 : {
346 0 : return NS_ERROR_FAILURE;
347 : }
348 :
349 : NS_IMETHODIMP
350 0 : nsMozIconURI::SetUserPass(const nsACString &aUserPass)
351 : {
352 0 : return NS_ERROR_FAILURE;
353 : }
354 :
355 : NS_IMETHODIMP
356 0 : nsMozIconURI::GetHostPort(nsACString &aHostPort)
357 : {
358 0 : return NS_ERROR_FAILURE;
359 : }
360 :
361 : NS_IMETHODIMP
362 0 : nsMozIconURI::SetHostPort(const nsACString &aHostPort)
363 : {
364 0 : return NS_ERROR_FAILURE;
365 : }
366 :
367 : NS_IMETHODIMP
368 2 : nsMozIconURI::GetHost(nsACString &aHost)
369 : {
370 2 : return NS_ERROR_FAILURE;
371 : }
372 :
373 : NS_IMETHODIMP
374 0 : nsMozIconURI::SetHost(const nsACString &aHost)
375 : {
376 0 : return NS_ERROR_FAILURE;
377 : }
378 :
379 : NS_IMETHODIMP
380 0 : nsMozIconURI::GetPort(PRInt32 *aPort)
381 : {
382 0 : return NS_ERROR_FAILURE;
383 : }
384 :
385 : NS_IMETHODIMP
386 0 : nsMozIconURI::SetPort(PRInt32 aPort)
387 : {
388 0 : return NS_ERROR_FAILURE;
389 : }
390 :
391 : NS_IMETHODIMP
392 0 : nsMozIconURI::GetPath(nsACString &aPath)
393 : {
394 0 : aPath.Truncate();
395 0 : return NS_OK;
396 : }
397 :
398 : NS_IMETHODIMP
399 0 : nsMozIconURI::SetPath(const nsACString &aPath)
400 : {
401 0 : return NS_ERROR_FAILURE;
402 : }
403 :
404 : NS_IMETHODIMP
405 0 : nsMozIconURI::GetRef(nsACString &aRef)
406 : {
407 0 : aRef.Truncate();
408 0 : return NS_OK;
409 : }
410 :
411 : NS_IMETHODIMP
412 0 : nsMozIconURI::SetRef(const nsACString &aRef)
413 : {
414 0 : return NS_ERROR_FAILURE;
415 : }
416 :
417 : NS_IMETHODIMP
418 0 : nsMozIconURI::Equals(nsIURI *other, bool *result)
419 : {
420 0 : NS_ENSURE_ARG_POINTER(other);
421 0 : NS_PRECONDITION(result, "null pointer");
422 :
423 0 : nsCAutoString spec1;
424 0 : nsCAutoString spec2;
425 :
426 0 : other->GetSpec(spec2);
427 0 : GetSpec(spec1);
428 0 : if (!PL_strcasecmp(spec1.get(), spec2.get()))
429 0 : *result = true;
430 : else
431 0 : *result = false;
432 0 : return NS_OK;
433 : }
434 :
435 : NS_IMETHODIMP
436 0 : nsMozIconURI::EqualsExceptRef(nsIURI *other, bool *result)
437 : {
438 : // GetRef/SetRef not supported by nsMozIconURI, so
439 : // EqualsExceptRef() is the same as Equals().
440 0 : return Equals(other, result);
441 : }
442 :
443 : NS_IMETHODIMP
444 0 : nsMozIconURI::SchemeIs(const char *i_Scheme, bool *o_Equals)
445 : {
446 0 : NS_ENSURE_ARG_POINTER(o_Equals);
447 0 : if (!i_Scheme) return NS_ERROR_INVALID_ARG;
448 :
449 0 : *o_Equals = PL_strcasecmp("moz-icon", i_Scheme) ? false : true;
450 0 : return NS_OK;
451 : }
452 :
453 : NS_IMETHODIMP
454 0 : nsMozIconURI::Clone(nsIURI **result)
455 : {
456 0 : nsCOMPtr<nsIURL> newIconURL;
457 0 : if (mIconURL)
458 : {
459 0 : nsCOMPtr<nsIURI> newURI;
460 0 : nsresult rv = mIconURL->Clone(getter_AddRefs(newURI));
461 0 : if (NS_FAILED(rv))
462 0 : return rv;
463 0 : newIconURL = do_QueryInterface(newURI, &rv);
464 0 : if (NS_FAILED(rv))
465 0 : return rv;
466 : }
467 :
468 0 : nsMozIconURI *uri = new nsMozIconURI();
469 0 : newIconURL.swap(uri->mIconURL);
470 0 : uri->mSize = mSize;
471 0 : uri->mContentType = mContentType;
472 0 : uri->mFileName = mFileName;
473 0 : uri->mStockIcon = mStockIcon;
474 0 : uri->mIconSize = mIconSize;
475 0 : uri->mIconState = mIconState;
476 0 : NS_ADDREF(*result = uri);
477 :
478 0 : return NS_OK;
479 : }
480 :
481 : NS_IMETHODIMP
482 0 : nsMozIconURI::CloneIgnoringRef(nsIURI **result)
483 : {
484 : // GetRef/SetRef not supported by nsMozIconURI, so
485 : // CloneIgnoringRef() is the same as Clone().
486 0 : return Clone(result);
487 : }
488 :
489 : NS_IMETHODIMP
490 0 : nsMozIconURI::Resolve(const nsACString &relativePath, nsACString &result)
491 : {
492 0 : return NS_ERROR_NOT_IMPLEMENTED;
493 : }
494 :
495 : NS_IMETHODIMP
496 0 : nsMozIconURI::GetAsciiSpec(nsACString &aSpecA)
497 : {
498 0 : return GetSpec(aSpecA);
499 : }
500 :
501 : NS_IMETHODIMP
502 1 : nsMozIconURI::GetAsciiHost(nsACString &aHostA)
503 : {
504 1 : return GetHost(aHostA);
505 : }
506 :
507 : NS_IMETHODIMP
508 0 : nsMozIconURI::GetOriginCharset(nsACString &result)
509 : {
510 0 : result.Truncate();
511 0 : return NS_OK;
512 : }
513 :
514 : ////////////////////////////////////////////////////////////////////////////////
515 : // nsIIconUri methods:
516 :
517 : NS_IMETHODIMP
518 2 : nsMozIconURI::GetIconURL(nsIURL* * aFileUrl)
519 : {
520 2 : *aFileUrl = mIconURL;
521 2 : NS_IF_ADDREF(*aFileUrl);
522 2 : return NS_OK;
523 : }
524 :
525 : NS_IMETHODIMP
526 0 : nsMozIconURI::SetIconURL(nsIURL* aFileUrl)
527 : {
528 : // this isn't called anywhere, needs to go through SetSpec parsing
529 0 : return NS_ERROR_NOT_IMPLEMENTED;
530 : }
531 :
532 : NS_IMETHODIMP
533 1 : nsMozIconURI::GetImageSize(PRUint32 * aImageSize) // measured by # of pixels in a row. defaults to 16.
534 : {
535 1 : *aImageSize = mSize;
536 1 : return NS_OK;
537 : }
538 :
539 : NS_IMETHODIMP
540 0 : nsMozIconURI::SetImageSize(PRUint32 aImageSize) // measured by # of pixels in a row. defaults to 16.
541 : {
542 0 : mSize = aImageSize;
543 0 : return NS_OK;
544 : }
545 :
546 : NS_IMETHODIMP
547 1 : nsMozIconURI::GetContentType(nsACString &aContentType)
548 : {
549 1 : aContentType = mContentType;
550 1 : return NS_OK;
551 : }
552 :
553 : NS_IMETHODIMP
554 0 : nsMozIconURI::SetContentType(const nsACString &aContentType)
555 : {
556 0 : mContentType = aContentType;
557 0 : return NS_OK;
558 : }
559 :
560 : NS_IMETHODIMP
561 1 : nsMozIconURI::GetFileExtension(nsACString &aFileExtension)
562 : {
563 : // First, try to get the extension from mIconURL if we have one
564 1 : if (mIconURL)
565 : {
566 0 : nsCAutoString fileExt;
567 0 : if (NS_SUCCEEDED(mIconURL->GetFileExtension(fileExt)))
568 : {
569 0 : if (!fileExt.IsEmpty())
570 : {
571 : // unfortunately, this code doesn't give us the required '.' in front of the extension
572 : // so we have to do it ourselves..
573 0 : aFileExtension.Assign('.');
574 0 : aFileExtension.Append(fileExt);
575 : }
576 : }
577 0 : return NS_OK;
578 : }
579 :
580 1 : if (!mFileName.IsEmpty())
581 : {
582 : // truncate the extension out of the file path...
583 1 : const char * chFileName = mFileName.get(); // get the underlying buffer
584 1 : const char * fileExt = strrchr(chFileName, '.');
585 1 : if (!fileExt)
586 0 : return NS_OK;
587 1 : aFileExtension = fileExt;
588 : }
589 :
590 1 : return NS_OK;
591 : }
592 :
593 : NS_IMETHODIMP
594 1 : nsMozIconURI::GetStockIcon(nsACString &aStockIcon)
595 : {
596 1 : aStockIcon = mStockIcon;
597 1 : return NS_OK;
598 : }
599 :
600 : NS_IMETHODIMP
601 2 : nsMozIconURI::GetIconSize(nsACString &aSize)
602 : {
603 2 : if (mIconSize >= 0)
604 1 : aSize = kSizeStrings[mIconSize];
605 : else
606 1 : aSize.Truncate();
607 2 : return NS_OK;
608 : }
609 :
610 : NS_IMETHODIMP
611 1 : nsMozIconURI::GetIconState(nsACString &aState)
612 : {
613 1 : if (mIconState >= 0)
614 1 : aState = kStateStrings[mIconState];
615 : else
616 0 : aState.Truncate();
617 1 : return NS_OK;
618 : }
619 : ////////////////////////////////////////////////////////////////////////////////
|