]> err.no Git - sope/blob - sope-appserver/NGObjWeb/NGHttp/NGUrlFormCoder.m
added svn:keywords and svn:ignore where appropriate. removed CVS artifacts.
[sope] / sope-appserver / NGObjWeb / NGHttp / NGUrlFormCoder.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #import "common.h"
24 #import "NGUrlFormCoder.h"
25
26 static inline BOOL isURLSafeChar(unsigned char _c) {
27   if ((_c > 64) && (_c < 91))
28     return YES;
29
30   if ((_c > 96) && (_c < 123))
31     return YES;
32
33   if (_c == 95)
34     return YES;
35
36   if ((_c > 47) && (_c < 58))
37     return YES;
38   
39   return NO;
40 }
41
42 static inline int _valueOfHexChar(unsigned char _c) {
43   switch (_c) {
44     case '0': case '1': case '2': case '3': case '4':
45     case '5': case '6': case '7': case '8': case '9':
46       return (_c - 48); // 0-9 (ascii-char)'0' - 48 => (int)0
47       
48     case 'A': case 'B': case 'C':
49     case 'D': case 'E': case 'F':
50       return (_c - 55); // A-F, A=10..F=15, 'A'=65..'F'=70
51       
52     case 'a': case 'b': case 'c':
53     case 'd': case 'e': case 'f':
54       return (_c - 87); // a-f, a=10..F=15, 'a'=97..'f'=102
55
56     default:
57       return -1;
58   }
59 }
60
61 static inline unsigned _unescapeUrl(const char *_src, unsigned _len, char *_dest) {
62   register unsigned i, i2;
63
64   for (i = 0, i2 = 0; i < _len; i++, i2++) {
65     register char c = _src[i];
66     
67     switch (c) {
68       case '+': // encoded space
69         _dest[i2] = ' ';
70         break;
71           
72       case '%': // encoded hex ('%FF')
73         _dest[i2] = _valueOfHexChar(_src[i + 1]) * 16 +
74           _valueOfHexChar(_src[i + 2]);
75         i += 2; // skip the two hexchars
76         break;
77
78       default:  // normal char
79         _dest[i2] = c;
80         break;
81     }
82   }
83   return i2; // return unescaped length
84 }
85
86 NGHashMap *NGDecodeUrlFormParameters(const char *_buffer, unsigned _len) {
87   Class StrClass = [NSString class];
88   NGMutableHashMap *dict = nil;
89   unsigned pos = 0;
90   
91   if (_len == 0) return nil;
92
93   dict = [[NGMutableHashMap alloc] initWithCapacity:16];
94
95   do {
96     NSString *key = nil, *value = nil;
97     unsigned tmp, len;
98     char     buffer[_len];
99
100     /* read key */
101     tmp = pos;
102     while ((pos < _len) && (_buffer[pos] != '='))
103       pos++;
104
105     len = _unescapeUrl(&(_buffer[tmp]), (pos - tmp), buffer);
106     if (len > 0) {
107       key = [[StrClass allocWithZone:[dict zone]]
108                        initWithCString:buffer length:len];
109     }
110     else
111       key = @"";
112
113     //NSLog(@"read key %@", key);
114
115     if (pos < _len) { // value pending
116       NSCAssert(_buffer[pos] == '=', @"invalid parser state ..");
117       pos++; // skip '='
118
119       /* read value */
120       tmp = pos;
121       while ((pos < _len) && (_buffer[pos] != '&') && (_buffer[pos] != '?')) {
122         pos++;
123       }
124       
125       len = _unescapeUrl(&(_buffer[tmp]), (pos - tmp), buffer);
126
127       if (len > 0) {
128         value = [[StrClass allocWithZone:[dict zone]]
129                            initWithCString:buffer length:len];
130       }
131       else
132         value = @"";
133
134       //NSLog(@"read value: %@", value);
135       
136       // skip '&'
137       if (_buffer[pos] == '&' || _buffer[pos] == '?') pos++;
138     }
139     
140     if (value == nil)
141       value = @"";
142
143     /* store in dictionary */
144     if (key)
145       [dict addObject:value forKey:key];
146
147     RELEASE(key);   key   = nil;
148     RELEASE(value); value = nil;
149   }
150   while (pos < _len);
151
152   return dict;
153 }
154
155 @implementation NSString(FormURLCoding)
156
157 - (NSString *)stringByApplyingURLEncoding {
158 #if 1
159   /* NGExtensions/NSString+misc.h */
160   return [self stringByEscapingURL];
161 #else
162   char     *buf, *encBuf;
163   unsigned clen, i, j;
164
165   if ((clen = [self cStringLength]) == 0)
166     return self;
167   
168   buf  = malloc(clen + 1);
169   [self getCString:buf]; buf[clen] = '\0';
170   
171   encBuf = malloc(clen * 3 + 1);
172   
173   for (i = 0, j = 0; i < clen; i++) {
174     register unsigned char c = buf[i];
175
176     if (isURLSafeChar(c)) {
177       encBuf[j] = c;
178       j++;
179     }
180     else if (c == ' ') {
181       encBuf[j] = '+';
182       j++;
183     }
184     else {
185       sprintf(&(encBuf[j]), "%%%02X", (int)c);
186       j += 3;
187     }
188   }
189   return [NSString stringWithCString:encBuf length:j];
190   /* was buggy, does not release encbuf .. */
191 #endif
192 }
193
194 @end /* NSString(FormURLCoding) */