chiark / gitweb /
server side support for cookies, basic tests
[disorder] / lib / kvp.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include <config.h>
22 #include "types.h"
23
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "mem.h"
28 #include "kvp.h"
29 #include "log.h"
30 #include "vector.h"
31 #include "hex.h"
32 #include "sink.h"
33
34 int urldecode(struct sink *sink, const char *ptr, size_t n) {
35   int c, d1, d2;
36   
37   while(n-- > 0) {
38     switch(c = *ptr++) {
39     case '%':
40       if((d1 = unhexdigit(ptr[0])) == -1
41          || (d2 = unhexdigit(ptr[1])) == -1)
42         return -1;
43       c  = d1 * 16 + d2;
44       ptr += 2;
45       n -= 2;
46       break;
47     case '+':
48       c = ' ';
49       break;
50     default:
51       break;
52     }
53     if(sink_writec(sink,c) < 0)
54       return -1;
55   }
56   return 0;
57 }
58
59 static char *decode(const char *ptr, size_t n) {
60   struct dynstr d;
61   struct sink *s;
62
63   dynstr_init(&d);
64   s = sink_dynstr(&d);
65   if(urldecode(s, ptr, n))
66     return 0;
67   dynstr_terminate(&d);
68   return d.vec;
69 }
70
71 struct kvp *kvp_urldecode(const char *ptr, size_t n) {
72   struct kvp *kvp, **kk = &kvp, *k;
73   const char *q, *r, *top = ptr + n, *next;
74
75   while(ptr < top) {
76     *kk = k = xmalloc(sizeof *k);
77     if(!(q = memchr(ptr, '=', top - ptr)))
78       break;
79     if(!(k->name = decode(ptr, q - ptr))) break;
80     if((r = memchr(ptr, '&', top - ptr)))
81       next = r + 1;
82     else
83       next = r = top;
84     if(r < q)
85       break;
86     if(!(k->value = decode(q + 1, r - (q + 1)))) break;
87     kk = &k->next;
88     ptr = next;
89   }
90   *kk = 0;
91   return kvp;
92 }
93
94 int urlencode(struct sink *sink, const char *s, size_t n) {
95   unsigned char c;
96
97   while(n > 0) {
98     c = *s++;
99     n--;
100     switch(c) {
101     default:
102       if((c >= '0' && c <= '9')
103          || (c >= 'a' && c <= 'z')
104          || (c >= 'A' && c <= 'Z')) {
105         /* RFC2396 2.3 unreserved characters */
106       case '-':
107       case '_':
108       case '.':
109       case '!':
110       case '~':
111       case '*':
112       case '\'':
113       case '(':
114       case ')':
115         /* additional unreserved characters */
116       case '/':
117         if(sink_writec(sink, c) < 0)
118           return -1;
119       } else
120         if(sink_printf(sink, "%%%02x", (unsigned int)c) < 0)
121           return -1;
122     }
123   }
124   return 0;
125 }
126
127 /** @brief URL-encode @p s
128  * @param s String to encode
129  * @return Encoded string
130  */
131 const char *urlencodestring(const char *s) {
132   struct dynstr d;
133
134   dynstr_init(&d);
135   urlencode(sink_dynstr(&d), s, strlen(s));
136   dynstr_terminate(&d);
137   return d.vec;
138 }
139
140 /** @brief URL-decode @p s
141  * @param s String to decode
142  * @param ns Length of string
143  * @return Decoded string
144  */
145 const char *urldecodestring(const char *s, size_t ns) {
146   struct dynstr d;
147
148   dynstr_init(&d);
149   urldecode(sink_dynstr(&d), s, ns);
150   dynstr_terminate(&d);
151   return d.vec;
152 }
153
154 char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
155   struct dynstr d;
156   struct sink *sink;
157
158   dynstr_init(&d);
159   sink = sink_dynstr(&d);
160   while(kvp) {
161     urlencode(sink, kvp->name, strlen(kvp->name));
162     dynstr_append(&d, '=');
163     urlencode(sink, kvp->value, strlen(kvp->value));
164     if((kvp = kvp->next))
165       dynstr_append(&d, '&');
166     
167   }
168   dynstr_terminate(&d);
169   if(np)
170     *np = d.nvec;
171   return d.vec;
172 }
173
174 int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
175   struct kvp *k, **kk;
176
177   for(kk = kvpp; (k = *kk) && strcmp(name, k->name); kk = &k->next)
178     ;
179   if(k) {
180     if(value) {
181       if(strcmp(k->value, value)) {
182         k->value = xstrdup(value);
183         return 1;
184       } else
185         return 0;
186     } else {
187       *kk = k->next;
188       return 1;
189     }
190   } else {
191     if(value) {
192       *kk = k = xmalloc(sizeof *k);
193       k->name = xstrdup(name);
194       k->value = xstrdup(value);
195       return 1;
196     } else
197       return 0;
198   }
199 }
200
201 const char *kvp_get(const struct kvp *kvp, const char *name) {
202   for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
203     ;
204   return kvp ? kvp->value : 0;
205 }
206
207 /*
208 Local Variables:
209 c-basic-offset:2
210 comment-column:40
211 End:
212 */