1 /* The "data" URI protocol implementation (RFC 2397) */
9 #include "cache/cache.h"
10 #include "network/connection.h"
11 #include "protocol/data.h"
12 #include "protocol/protocol.h"
13 #include "protocol/uri.h"
14 #include "util/base64.h"
15 #include "util/string.h"
17 /* The URLs are of the form:
19 * data:[<mediatype>][;base64],<data>
21 * The <mediatype> is an Internet media type specification (with optional
22 * parameters.) The appearance of ";base64" means that the data is encoded as
23 * base64. Without ";base64", the data (as a sequence of octets) is represented
24 * using ASCII encoding for octets inside the range of safe URL characters and
25 * using the standard %xx hex encoding of URLs for octets outside that range.
26 * If <mediatype> is omitted, it defaults to "text/plain;charset=US-ASCII". As a
27 * shorthand, "text/plain" can be omitted but the charset parameter supplied.
31 * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
32 * mediatype := [ type "/" subtype ] *( ";" parameter )
34 * parameter := attribute "=" value
36 * where "urlchar" is imported from [RFC2396], and "type", "subtype",
37 * "attribute" and "value" are the corresponding tokens from [RFC2045],
38 * represented using URL escaped encoding of [RFC2396] as necessary.
40 * Attribute values in [RFC2045] are allowed to be either represented as tokens
41 * or as quoted strings. However, within a "data" URL, the "quoted-string"
42 * representation would be awkward, since the quote mark is itself not a valid
43 * urlchar. For this reason, parameter values should use the URL Escaped
44 * encoding instead of quoted string if the parameter values contain any
47 * The ";base64" extension is distinguishable from a content-type parameter by
48 * the fact that it doesn't have a following "=" sign. */
50 /* FIXME: Maybe some kind of redirecting to common specialized data URI could
51 * be useful so "data:,blah" and data:text/plain,blah" are redirected to the
52 * most specialized "data:text/plain;charset=US-ASCII,blah". On the other hand
53 * for small entries it doesn't matter. */
55 #define DEFAULT_DATA_MEDIATYPE "text/plain;charset=US-ASCII"
57 #define data_has_mediatype(header, headerlen) \
58 ((headerlen) >= 3 && memchr(header, '/', headerlen))
60 #define data_has_base64_attribute(typelen, endstr) \
61 ((typelen) >= sizeof(";base64") - 1 \
62 && !memcmp(";base64", (end) - sizeof(";base64") + 1, sizeof(";base64") - 1))
64 static unsigned char *
65 init_data_protocol_header(struct cache_entry
*cached
,
66 unsigned char *type
, int typelen
)
72 type
= memacpy(type
, typelen
);
73 if (!type
) return NULL
;
75 /* Set fake content type */
76 head
= straconcat("\r\nContent-Type: ", type
, "\r\n", NULL
);
78 if (!head
) return NULL
;
80 mem_free_set(&cached
->head
, head
);
84 static unsigned char *
85 parse_data_protocol_header(struct connection
*conn
, int *base64
)
87 struct uri
*uri
= conn
->uri
;
88 unsigned char *end
= memchr(uri
->data
, ',', uri
->datalen
);
89 unsigned char *type
= DEFAULT_DATA_MEDIATYPE
;
90 int typelen
= sizeof(DEFAULT_DATA_MEDIATYPE
) - 1;
93 int headerlen
= end
- uri
->data
;
95 if (data_has_base64_attribute(headerlen
, end
)) {
97 headerlen
-= sizeof(";base64") - 1;
100 if (data_has_mediatype(uri
->data
, headerlen
)) {
106 if (!init_data_protocol_header(conn
->cached
, type
, typelen
))
109 /* Return char after ',' or complete data part */
110 return end
? end
+ 1 : uri
->data
;
114 data_protocol_handler(struct connection
*conn
)
116 struct uri
*uri
= conn
->uri
;
117 struct cache_entry
*cached
= get_cache_entry(uri
);
118 unsigned char *data_start
, *data
;
122 abort_connection(conn
, S_OUT_OF_MEM
);
126 conn
->cached
= cached
;
128 data_start
= parse_data_protocol_header(conn
, &base64
);
130 abort_connection(conn
, S_OUT_OF_MEM
);
134 /* Allocate the data string because URI decoding will possibly modify
136 data
= memacpy(data_start
, uri
->datalen
- (data_start
- uri
->data
));
138 abort_connection(conn
, S_OUT_OF_MEM
);
143 unsigned char *decoded
= base64_encode(data
);
146 abort_connection(conn
, S_OUT_OF_MEM
);
150 mem_free_set(&data
, decoded
);
156 /* Use strlen() to get the correct decoded length */
157 int datalen
= strlen(data
);
159 add_fragment(cached
, conn
->from
, data
, datalen
);
160 normalize_cache_entry(cached
, datalen
);
165 abort_connection(conn
, S_OK
);