Bug 1029: Use JS_CallFunctionValue in elinks_object.c
[elinks.git] / src / protocol / data.c
blobc783239e11fb594baf3297aed5615653882eb43c
1 /* The "data" URI protocol implementation (RFC 2397) */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
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.
29 * The syntax:
31 * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
32 * mediatype := [ type "/" subtype ] *( ";" parameter )
33 * data := *urlchar
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
45 * "tspecial".
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)
68 unsigned char *head;
70 assert(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",
77 (unsigned char *) NULL);
78 mem_free(type);
79 if (!head) return NULL;
81 mem_free_set(&cached->head, head);
82 return head;
85 static unsigned char *
86 parse_data_protocol_header(struct connection *conn, int *base64)
88 struct uri *uri = conn->uri;
89 unsigned char *end = memchr(uri->data, ',', uri->datalen);
90 unsigned char *type = DEFAULT_DATA_MEDIATYPE;
91 int typelen = sizeof(DEFAULT_DATA_MEDIATYPE) - 1;
93 if (end) {
94 int headerlen = end - uri->data;
96 if (data_has_base64_attribute(headerlen, end)) {
97 *base64 = 1;
98 headerlen -= sizeof(";base64") - 1;
101 if (data_has_mediatype(uri->data, headerlen)) {
102 type = uri->data;
103 typelen = headerlen;
107 if (!init_data_protocol_header(conn->cached, type, typelen))
108 return NULL;
110 /* Return char after ',' or complete data part */
111 return end ? end + 1 : uri->data;
114 void
115 data_protocol_handler(struct connection *conn)
117 struct uri *uri = conn->uri;
118 struct cache_entry *cached = get_cache_entry(uri);
119 unsigned char *data_start, *data;
120 int base64 = 0;
122 if (!cached) {
123 abort_connection(conn, S_OUT_OF_MEM);
124 return;
127 conn->cached = cached;
129 data_start = parse_data_protocol_header(conn, &base64);
130 if (!data_start) {
131 abort_connection(conn, S_OUT_OF_MEM);
132 return;
135 /* Allocate the data string because URI decoding will possibly modify
136 * it. */
137 data = memacpy(data_start, uri->datalen - (data_start - uri->data));
138 if (!data) {
139 abort_connection(conn, S_OUT_OF_MEM);
140 return;
143 if (base64) {
144 unsigned char *decoded = base64_encode(data);
146 if (!decoded) {
147 abort_connection(conn, S_OUT_OF_MEM);
148 return;
151 mem_free_set(&data, decoded);
152 } else {
153 decode_uri(data);
157 /* Use strlen() to get the correct decoded length */
158 int datalen = strlen(data);
160 add_fragment(cached, conn->from, data, datalen);
161 conn->from += datalen;
164 mem_free(data);
166 abort_connection(conn, S_OK);