chiark / gitweb /
c56249d2e707df687768fd4708e2ae96786e9226
[disorder] / lib / kvp.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2007, 2008 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 "common.h"
22
23 #include "mem.h"
24 #include "kvp.h"
25 #include "log.h"
26 #include "vector.h"
27 #include "hex.h"
28 #include "sink.h"
29
30 int urldecode(struct sink *sink, const char *ptr, size_t n) {
31   int c, d1, d2;
32   
33   while(n-- > 0) {
34     switch(c = *ptr++) {
35     case '%':
36       if((d1 = unhexdigit(ptr[0])) == -1
37          || (d2 = unhexdigit(ptr[1])) == -1)
38         return -1;
39       c  = d1 * 16 + d2;
40       ptr += 2;
41       n -= 2;
42       break;
43     case '+':
44       c = ' ';
45       break;
46     default:
47       break;
48     }
49     if(sink_writec(sink,c) < 0)
50       return -1;
51   }
52   return 0;
53 }
54
55 static char *decode(const char *ptr, size_t n) {
56   struct dynstr d;
57   struct sink *s;
58
59   dynstr_init(&d);
60   s = sink_dynstr(&d);
61   if(urldecode(s, ptr, n))
62     return 0;
63   dynstr_terminate(&d);
64   return d.vec;
65 }
66
67 struct kvp *kvp_urldecode(const char *ptr, size_t n) {
68   struct kvp *kvp, **kk = &kvp, *k;
69   const char *q, *r, *top = ptr + n, *next;
70
71   while(ptr < top) {
72     *kk = k = xmalloc(sizeof *k);
73     if(!(q = memchr(ptr, '=', top - ptr)))
74       break;
75     if(!(k->name = decode(ptr, q - ptr))) break;
76     if((r = memchr(ptr, '&', top - ptr)))
77       next = r + 1;
78     else
79       next = r = top;
80     if(r < q)
81       break;
82     if(!(k->value = decode(q + 1, r - (q + 1)))) break;
83     kk = &k->next;
84     ptr = next;
85   }
86   *kk = 0;
87   return kvp;
88 }
89
90 int urlencode(struct sink *sink, const char *s, size_t n) {
91   unsigned char c;
92
93   while(n > 0) {
94     c = *s++;
95     n--;
96     switch(c) {
97     default:
98       if((c >= '0' && c <= '9')
99          || (c >= 'a' && c <= 'z')
100          || (c >= 'A' && c <= 'Z')) {
101         /* RFC2396 2.3 unreserved characters */
102       case '-':
103       case '_':
104       case '.':
105       case '!':
106       case '~':
107       case '*':
108       case '\'':
109       case '(':
110       case ')':
111         /* additional unreserved characters */
112       case '/':
113         if(sink_writec(sink, c) < 0)
114           return -1;
115       } else
116         if(sink_printf(sink, "%%%02x", (unsigned int)c) < 0)
117           return -1;
118     }
119   }
120   return 0;
121 }
122
123 /** @brief URL-encode @p s
124  * @param s String to encode
125  * @return Encoded string
126  */
127 char *urlencodestring(const char *s) {
128   struct dynstr d;
129
130   dynstr_init(&d);
131   urlencode(sink_dynstr(&d), s, strlen(s));
132   dynstr_terminate(&d);
133   return d.vec;
134 }
135
136 /** @brief URL-decode @p s
137  * @param s String to decode
138  * @param ns Length of string
139  * @return Decoded string or NULL
140  */
141 char *urldecodestring(const char *s, size_t ns) {
142   struct dynstr d;
143
144   dynstr_init(&d);
145   if(urldecode(sink_dynstr(&d), s, ns))
146     return NULL;
147   dynstr_terminate(&d);
148   return d.vec;
149 }
150
151 char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
152   struct dynstr d;
153   struct sink *sink;
154
155   dynstr_init(&d);
156   sink = sink_dynstr(&d);
157   while(kvp) {
158     urlencode(sink, kvp->name, strlen(kvp->name));
159     dynstr_append(&d, '=');
160     urlencode(sink, kvp->value, strlen(kvp->value));
161     if((kvp = kvp->next))
162       dynstr_append(&d, '&');
163     
164   }
165   dynstr_terminate(&d);
166   if(np)
167     *np = d.nvec;
168   return d.vec;
169 }
170
171 int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
172   struct kvp *k, **kk;
173
174   for(kk = kvpp; (k = *kk) && strcmp(name, k->name); kk = &k->next)
175     ;
176   if(k) {
177     if(value) {
178       if(strcmp(k->value, value)) {
179         k->value = xstrdup(value);
180         return 1;
181       } else
182         return 0;
183     } else {
184       *kk = k->next;
185       return 1;
186     }
187   } else {
188     if(value) {
189       *kk = k = xmalloc(sizeof *k);
190       k->name = xstrdup(name);
191       k->value = xstrdup(value);
192       return 1;
193     } else
194       return 0;
195   }
196 }
197
198 const char *kvp_get(const struct kvp *kvp, const char *name) {
199   for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
200     ;
201   return kvp ? kvp->value : 0;
202 }
203
204 struct kvp *kvp_make(const char *name, ...) {
205   const char *value;
206   struct kvp *kvp = 0, *k;
207   va_list ap;
208
209   va_start(ap, name);
210   while(name) {
211     value = va_arg(ap, const char *);
212     k = xmalloc(sizeof *k);
213     k->name = name;
214     k->value = value ? xstrdup(value) : value;
215     k->next = kvp;
216     kvp = k;
217     name = va_arg(ap, const char *);
218   }
219   va_end(ap);
220   return kvp;
221 }
222
223 /*
224 Local Variables:
225 c-basic-offset:2
226 comment-column:40
227 End:
228 */