OSDN Git Service

2ca76bf389fd332781cef026d7311c80e0ac3520
[pf3gnuchains/gcc-fork.git] / libgo / go / html / template / url.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package template
6
7 import (
8         "bytes"
9         "fmt"
10         "strings"
11 )
12
13 // urlFilter returns its input unless it contains an unsafe protocol in which
14 // case it defangs the entire URL.
15 func urlFilter(args ...interface{}) string {
16         s, t := stringify(args...)
17         if t == contentTypeURL {
18                 return s
19         }
20         if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
21                 protocol := strings.ToLower(s[:i])
22                 if protocol != "http" && protocol != "https" && protocol != "mailto" {
23                         return "#" + filterFailsafe
24                 }
25         }
26         return s
27 }
28
29 // urlEscaper produces an output that can be embedded in a URL query.
30 // The output can be embedded in an HTML attribute without further escaping.
31 func urlEscaper(args ...interface{}) string {
32         return urlProcessor(false, args...)
33 }
34
35 // urlEscaper normalizes URL content so it can be embedded in a quote-delimited
36 // string or parenthesis delimited url(...).
37 // The normalizer does not encode all HTML specials. Specifically, it does not
38 // encode '&' so correct embedding in an HTML attribute requires escaping of
39 // '&' to '&amp;'.
40 func urlNormalizer(args ...interface{}) string {
41         return urlProcessor(true, args...)
42 }
43
44 // urlProcessor normalizes (when norm is true) or escapes its input to produce
45 // a valid hierarchical or opaque URL part.
46 func urlProcessor(norm bool, args ...interface{}) string {
47         s, t := stringify(args...)
48         if t == contentTypeURL {
49                 norm = true
50         }
51         var b bytes.Buffer
52         written := 0
53         // The byte loop below assumes that all URLs use UTF-8 as the
54         // content-encoding. This is similar to the URI to IRI encoding scheme
55         // defined in section 3.1 of  RFC 3987, and behaves the same as the
56         // EcmaScript builtin encodeURIComponent.
57         // It should not cause any misencoding of URLs in pages with
58         // Content-type: text/html;charset=UTF-8.
59         for i, n := 0, len(s); i < n; i++ {
60                 c := s[i]
61                 switch c {
62                 // Single quote and parens are sub-delims in RFC 3986, but we
63                 // escape them so the output can be embedded in single
64                 // quoted attributes and unquoted CSS url(...) constructs.
65                 // Single quotes are reserved in URLs, but are only used in
66                 // the obsolete "mark" rule in an appendix in RFC 3986
67                 // so can be safely encoded.
68                 case '!', '#', '$', '&', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']':
69                         if norm {
70                                 continue
71                         }
72                 // Unreserved according to RFC 3986 sec 2.3
73                 // "For consistency, percent-encoded octets in the ranges of
74                 // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
75                 // period (%2E), underscore (%5F), or tilde (%7E) should not be
76                 // created by URI producers
77                 case '-', '.', '_', '~':
78                         continue
79                 case '%':
80                         // When normalizing do not re-encode valid escapes.
81                         if norm && i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) {
82                                 continue
83                         }
84                 default:
85                         // Unreserved according to RFC 3986 sec 2.3
86                         if 'a' <= c && c <= 'z' {
87                                 continue
88                         }
89                         if 'A' <= c && c <= 'Z' {
90                                 continue
91                         }
92                         if '0' <= c && c <= '9' {
93                                 continue
94                         }
95                 }
96                 b.WriteString(s[written:i])
97                 fmt.Fprintf(&b, "%%%02x", c)
98                 written = i + 1
99         }
100         if written == 0 {
101                 return s
102         }
103         b.WriteString(s[written:])
104         return b.String()
105 }