4e7f19010606b5ba66dda999ee6785bbfc7e67a6
[mLib] / codec / url.c
1 /* -*-c-*-
2 *
3 * Parsing and construction of url-encoded name/value pairs
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "dstr.h"
36 #include "url.h"
37
38 /*----- Main code ---------------------------------------------------------*/
39
40 /* --- @url_initenc@ --- *
41 *
42 * Arguments: @url_ectx *ctx@ = pointer to context block
43 *
44 * Returns: ---
45 *
46 * Use: Initializes a URL encoding context.
47 */
48
49 void url_initenc(url_ectx *ctx) { ctx->f = 0; }
50
51 /* --- @encode@ --- *
52 *
53 * Arguments: @url_ectx *ctx@ = encoding context
54 * @dstr *d@ = pointer to output string
55 * @const char *p@ = pointer to thing to encode
56 *
57 * Returns: ---
58 *
59 * Use: Encodes the input string into the output string.
60 */
61
62 static void encode(url_ectx *ctx, dstr *d, const char *p)
63 {
64 while (*p) {
65 switch (*p) {
66 case ' ':
67 DPUTC(d, '+');
68 break;
69 default:
70 if (!isspace((unsigned char)*p) &&
71 ((ctx->f & URLF_LAX) || isalnum((unsigned char)*p)))
72 goto safe;
73 else
74 goto unsafe;
75 case '/':
76 case '~':
77 if (ctx->f & URLF_STRICT)
78 goto unsafe;
79 case '-':
80 case '.':
81 case '_':
82 safe:
83 DPUTC(d, *p);
84 break;
85 unsafe:
86 case '+':
87 case '%':
88 case '=':
89 case '&':
90 case ';':
91 dstr_putf(d, "%%%02x", *p);
92 break;
93 }
94 p++;
95 }
96 }
97
98 /* --- @url_enc@ --- *
99 *
100 * Arguments: @url_ectx *ctx@ = pointer to encoding context
101 * @dstr *d@ = pointer to output string
102 * @const char *name@ = pointer to name
103 * @const char *value@ = pointer to value
104 *
105 * Returns: ---
106 *
107 * Use: Writes an assignment between @name@ and @value@ to the
108 * output string, encoding the values properly.
109 */
110
111 void url_enc(url_ectx *ctx, dstr *d, const char *name, const char *value)
112 {
113 if (ctx->f & URLF_SEP)
114 DPUTC(d, (ctx->f & URLF_SEMI) ? ';' : '&');
115 encode(ctx, d, name);
116 DPUTC(d, '=');
117 encode(ctx, d, value);
118 DPUTZ(d);
119 ctx->f |= URLF_SEP;
120 }
121
122 /* --- @url_initdec@ --- *
123 *
124 * Arguments: @url_dctx *ctx@ = pointer to context block
125 * @const char *p@ = string to read data from
126 *
127 * Returns: ---
128 *
129 * Use: Initializes a URL decoding context.
130 */
131
132 void url_initdec(url_dctx *ctx, const char *p) { ctx->p = p; ctx->f = 0; }
133
134 /* --- @decode@ --- *
135 *
136 * Arguments: @url_dctx *ctx@ = pointer to the context
137 * @dstr *d@ = pointer to output string
138 * @const char *p@ = pointer to input data
139 * @int eq@ = whether to stop at `=' characters
140 *
141 * Returns: Pointer to next available character.
142 *
143 * Use: Does a URL decode.
144 */
145
146 static const char *decode(url_dctx *ctx, dstr *d, const char *p, int eq)
147 {
148 if (!*p)
149 return (0);
150 for (;;) {
151 switch (*p) {
152 case '=':
153 if (eq)
154 return (p);
155 goto boring;
156 case ';':
157 if (ctx->f & URLF_SEMI)
158 return (p);
159 goto boring;
160 case 0:
161 case '&':
162 return (p);
163 case '+':
164 DPUTC(d, ' ');
165 break;
166 case '%': {
167 unsigned int ch;
168 int n;
169 int x = sscanf(p + 1, "%2x%n", &ch, &n);
170 if (x == 1) {
171 DPUTC(d, ch);
172 p += n;
173 break;
174 }
175 }
176 default:
177 boring:
178 DPUTC(d, *p);
179 break;
180 }
181 p++;
182 }
183 }
184
185 /* --- @url_dec@ --- *
186 *
187 * Arguments: @url_dctx *ctx@ = pointer to decode context
188 * @dstr *n@ = pointer to output string for name
189 * @dstr *v@ = pointer to output string for value
190 *
191 * Returns: Nonzero if it read something, zero if there's nothing left
192 *
193 * Use: Decodes the next name/value pair from a urlencoded string.
194 */
195
196 int url_dec(url_dctx *ctx, dstr *n, dstr *v)
197 {
198 const char *p = ctx->p;
199 size_t l = n->len;
200
201 again:
202 if ((p = decode(ctx, n, p, 1)) == 0 || *p == 0)
203 return (0);
204 if (*p != '=') {
205 p++;
206 n->len = l;
207 goto again;
208 }
209 p++;
210 if ((p = decode(ctx, v, p, 0)) == 0)
211 return (0);
212 DPUTZ(n);
213 DPUTZ(v);
214 ctx->p = p;
215 return (1);
216 }
217
218 /*----- That's all, folks -------------------------------------------------*/