1 /*
2
3    Licensed to the Apache Software Foundation (ASF) under one or more
4    contributor license agreements.  See the NOTICE file distributed with
5    this work for additional information regarding copyright ownership.
6    The ASF licenses this file to You under the Apache License, Version 2.0
7    (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
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17
18  */

19 package org.apache.batik.util;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Iterator;
25
26 /**
27  * Protocol Handler for the 'data' protocol.
28  * RFC: 2397
29  * http://www.ietf.org/rfc/rfc2397.txt
30  *
31  * @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
32  * @version $Id: ParsedURLDataProtocolHandler.java 1733416 2016-03-03 07:07:13Z gadams $ 
33  */

34 public class ParsedURLDataProtocolHandler 
35     extends AbstractParsedURLProtocolHandler {
36
37     static final String DATA_PROTOCOL = "data";
38     static final String BASE64 = "base64";
39     static final String CHARSET = "charset";
40
41     public ParsedURLDataProtocolHandler() {
42         super(DATA_PROTOCOL);
43     }
44
45     public ParsedURLData parseURL(ParsedURL baseURL, String urlStr) {
46         // No relative form...
47         return parseURL(urlStr);
48     }
49
50     public ParsedURLData parseURL(String urlStr) {
51         DataParsedURLData ret = new DataParsedURLData();
52
53         int pidx=0, idx;
54         int len = urlStr.length();
55
56         // Pull fragment id off first...
57         idx = urlStr.indexOf('#');
58         ret.ref = null;
59         if (idx != -1) {
60             if (idx + 1 < len) {
61                 ret.ref = urlStr.substring(idx + 1);
62             }
63             urlStr = urlStr.substring(0, idx);
64             len = urlStr.length();
65         }
66
67         idx = urlStr.indexOf(':');
68         if (idx != -1) {
69             // May have a protocol spec...
70             ret.protocol = urlStr.substring(pidx, idx);
71             if (ret.protocol.indexOf('/') == -1)
72                 pidx = idx+1;
73             else {
74                 // Got a slash in protocol probably means 
75                 // no protocol given, (host and port?)
76                 ret.protocol = null;
77                 pidx = 0;
78             }
79         }
80
81         idx = urlStr.indexOf(',',pidx);
82         if ((idx != -1) && (idx != pidx)) {
83             ret.host = urlStr.substring(pidx, idx);
84             pidx = idx+1;
85
86             int aidx = ret.host.lastIndexOf(';');
87             if ((aidx == -1) || (aidx==ret.host.length())) {
88                 ret.contentType = ret.host;
89             } else {
90                 String enc = ret.host.substring(aidx+1);
91                 idx = enc.indexOf('=');
92                 if (idx == -1) {
93                     // It is an encoding.
94                     ret.contentEncoding = enc;
95                     ret.contentType = ret.host.substring(0, aidx);
96                 } else {
97                     ret.contentType = ret.host;
98                 }
99                 // if theres a charset pull it out.
100                 aidx = 0;
101                 idx = ret.contentType.indexOf(';', aidx);
102                 if (idx != -1) {
103                     aidx = idx+1;
104                     while (aidx < ret.contentType.length()) {
105                         idx = ret.contentType.indexOf(';', aidx);
106                         if (idx == -1) idx = ret.contentType.length();
107                         String param = ret.contentType.substring(aidx, idx);
108                         int eqIdx = param.indexOf('=');
109                         if ((eqIdx != -1) &&
110                             (CHARSET.equals(param.substring(0,eqIdx)))) 
111                             ret.charset = param.substring(eqIdx+1);
112                         aidx = idx+1;
113                     }
114                 }
115             }
116         }
117         
118         if (pidx < urlStr.length()) {
119             ret.path = urlStr.substring(pidx);
120         }
121
122         return ret;
123     }
124
125     /**
126      * Overrides some of the methods to support data protocol weirdness
127      */

128     static class DataParsedURLData extends ParsedURLData {
129
130         String charset;
131
132         public boolean complete() {
133             return path != null;
134         }
135
136         public String getPortStr() {
137             String portStr = "data:";
138             if (host != null) {
139                 portStr += host;
140             }
141             portStr += ",";
142             return portStr;
143         }
144                 
145         public String toString() {
146             String ret = getPortStr();
147             if (path != null) {
148                 ret += path;
149             }
150             if (ref != null) {
151                 ret += '#' + ref;
152             }
153             return ret;
154         }
155
156         /**
157          * Returns the content type if available.  This is only available
158          * for some protocols.
159          */

160         public String getContentType(String userAgent) {
161             return contentType;
162         }
163
164         /**
165          * Returns the content encoding if available.  This is only available
166          * for some protocols.
167          */

168         public String getContentEncoding(String userAgent) {
169             return contentEncoding;
170         }
171
172         protected InputStream openStreamInternal
173             (String userAgent, Iterator mimeTypes, Iterator encodingTypes)
174             throws IOException {
175             stream = decode(path);
176             if (BASE64.equals(contentEncoding)) {
177                 stream = new Base64DecodeStream(stream);
178             }
179             return stream;
180         }
181
182         public static InputStream decode(String s) {
183             int len = s.length();
184             byte [] data = new byte[len];
185             int j=0;
186             for(int i=0; i<len; i++) {
187                 char c = s.charAt(i);
188                 switch (c) {
189                 default : data[j++]= (byte)c;   break;
190                 case '%': {
191                     if (i+2 < len) {
192                         i += 2;
193                         byte b; 
194                         char c1 = s.charAt(i-1);
195                         if      (c1 >= '0' && c1 <= '9') b=(byte)(c1-'0');
196                         else if (c1 >= 'a' && c1 <= 'z') b=(byte)(c1-'a'+10);
197                         else if (c1 >= 'A' && c1 <= 'Z') b=(byte)(c1-'A'+10);
198                         else break;
199                         b*=16;
200
201                         char c2 = s.charAt(i);
202                         if      (c2 >= '0' && c2 <= '9') b+=(byte)(c2-'0');
203                         else if (c2 >= 'a' && c2 <= 'z') b+=(byte)(c2-'a'+10);
204                         else if (c2 >= 'A' && c2 <= 'Z') b+=(byte)(c2-'A'+10);
205                         else break;
206                         data[j++] = b;
207                     }
208                 }
209                 break;
210                 }
211             }
212             return new ByteArrayInputStream(data, 0, j);
213         }
214     }
215 }
216
217