chiark / gitweb /
DisOrder 3.0
[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 <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 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 or NULL
144  */
145 char *urldecodestring(const char *s, size_t ns) {
146   struct dynstr d;
147
148   dynstr_init(&d);
149   if(urldecode(sink_dynstr(&d), s, ns))
150     return NULL;
151   dynstr_terminate(&d);
152   return d.vec;
153 }
154
155 char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
156   struct dynstr d;
157   struct sink *sink;
158
159   dynstr_init(&d);
160   sink = sink_dynstr(&d);
161   while(kvp) {
162     urlencode(sink, kvp->name, strlen(kvp->name));
163     dynstr_append(&d, '=');
164     urlencode(sink, kvp->value, strlen(kvp->value));
165     if((kvp = kvp->next))
166       dynstr_append(&d, '&');
167     
168   }
169   dynstr_terminate(&d);
170   if(np)
171     *np = d.nvec;
172   return d.vec;
173 }
174
175 int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
176   struct kvp *k, **kk;
177
178   for(kk = kvpp; (k = *kk) && strcmp(name, k->name); kk = &k->next)
179     ;
180   if(k) {
181     if(value) {
182       if(strcmp(k->value, value)) {
183         k->value = xstrdup(value);
184         return 1;
185       } else
186         return 0;
187     } else {
188       *kk = k->next;
189       return 1;
190     }
191   } else {
192     if(value) {
193       *kk = k = xmalloc(sizeof *k);
194       k->name = xstrdup(name);
195       k->value = xstrdup(value);
196       return 1;
197     } else
198       return 0;
199   }
200 }
201
202 const char *kvp_get(const struct kvp *kvp, const char *name) {
203   for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
204     ;
205   return kvp ? kvp->value : 0;
206 }
207
208 /*
209 Local Variables:
210 c-basic-offset:2
211 comment-column:40
212 End:
213 */