]> err.no Git - sope/blob - sope-appserver/NGObjWeb/NGHttp+WO.m
fixed NGObjWeb for SOPE 3.3
[sope] / sope-appserver / NGObjWeb / NGHttp+WO.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
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 #include "NGHttp+WO.h"
24 #include <NGHttp/NGHttp.h>
25 #include <NGObjWeb/WOCookie.h>
26 #include <NGObjWeb/WORequest.h>
27 #include <NGMime/NGMime.h>
28 #include "common.h"
29
30 @interface WORequest(NGSupport)
31 - (void)_setHttpRequest:(NGHttpRequest *)_request;
32 @end
33
34 @implementation NGHttpRequest(WOSupport)
35
36 static Class NSArrayClass = Nil;
37
38 - (id)initWithWORequest:(WORequest *)_request {
39   NGHashMap *hm;
40   
41   hm = [NGHashMap hashMapWithDictionary:[_request headers]];
42     
43   self = [self initWithMethod:[_request method]
44                uri:[_request uri]
45                header:hm
46                version:[_request httpVersion]];
47   [self setBody:[_request content]];
48   
49   /* transfer cookies */
50   if ([[_request cookies] count] > 0)
51     NSLog(@"WARNING: cannot transfer cookies to NGHttpRequest yet !");
52
53   /* transfer headers !!! */
54   
55   return self;
56 }
57
58 - (WORequest *)woRequest {
59   NSAutoreleasePool *pool;
60   WORequest    *request;
61   NSDictionary *woHeaders;
62
63   pool = [[NSAutoreleasePool alloc] init];
64   
65   woHeaders = nil;
66   
67   request = [[WORequest alloc]
68                         initWithMethod:[self methodName]
69                         uri:[self uri]
70                         httpVersion:[self httpVersion]
71                         headers:woHeaders
72                         content:[self woContent]
73                         userInfo:nil];
74   request = [request autorelease];
75   
76   [request _setHttpRequest:self];
77   
78   /* process cookies */
79   {
80     NSEnumerator *cs;
81     WOCookie *cookie;
82     
83     cs = [[self woCookies] objectEnumerator];
84     while ((cookie = [cs nextObject]))
85       [request addCookie:cookie];
86   }
87   
88   /* process headers */
89   {
90     NSEnumerator *keys;
91     NSString *key;
92
93     if (NSArrayClass == Nil)
94       NSArrayClass = [NSArray class];
95     
96     keys = [self headerFieldNames];
97     while ((key = [keys nextObject])) {
98       NSEnumerator *values;
99       id value;
100       
101       values = [self valuesOfHeaderFieldWithName:key];
102       while ((value = [values nextObject])) {
103         if ([value isKindOfClass:NSArrayClass]) {
104           NSEnumerator *ev2;
105
106           ev2 = [value objectEnumerator];
107           while ((value = [ev2 nextObject])) {
108             value = [value stringValue];
109             [request appendHeader:value forKey:key];
110           }
111         }
112         else {
113           value = [value stringValue];
114           [request appendHeader:value forKey:key];
115         }
116       }
117     }
118   }
119   
120   request = [request retain];
121   [pool release]; pool = nil;
122   
123   return [request autorelease];
124 }
125
126 /* headers */
127
128 - (NSArray *)woHeaderKeys {
129   NSMutableArray *keys;
130   NSEnumerator   *ekeys;
131   NSString       *key   = nil;
132   
133   keys  = [[NSMutableArray alloc] init];
134   ekeys = [self headerFieldNames];
135   
136   while ((key = [ekeys nextObject]))
137     [keys addObject:key];
138   
139   return [keys autorelease];
140 }
141
142 - (NSString *)woHeaderForKey:(NSString *)_key {
143   return [[[self valuesOfHeaderFieldWithName:_key]
144                  nextObject]
145                  stringValue];
146 }
147
148 - (NSArray *)woHeadersForKey:(NSString *)_key {
149   NSMutableArray *vals;
150   NSEnumerator   *evals;
151   NSString       *value = nil;
152
153   vals  = [NSMutableArray arrayWithCapacity:2];
154   evals = [self valuesOfHeaderFieldWithName:_key];
155   
156   while ((value = [evals nextObject])) {
157     if ((value = [value stringValue]))
158       [vals addObject:value];
159   }
160
161   return vals;
162 }
163
164 /* cookies */
165
166 - (void)_addHTTPCookie:(id)_cookie to:(NSMutableArray *)_a {
167   static Class NGHttpCookieClass = Nil;
168   static Class WOCookieClass = Nil;
169   WOCookie *cookie;
170   id cookieValue;
171   
172   if (_cookie == nil)
173     return;
174   
175   if (NGHttpCookieClass == Nil) NGHttpCookieClass = [NGHttpCookie class];
176   if (WOCookieClass     == Nil) WOCookieClass = NSClassFromString(@"WOCookie");
177
178   if (![_cookie isKindOfClass:NGHttpCookieClass]) {
179     static NGHttpCookieFieldParser *cookieParser = nil;
180           
181     if (cookieParser == nil)
182       cookieParser = [[NGHttpCookieFieldParser alloc] init];
183           
184     _cookie = [_cookie stringValue];
185     _cookie = [_cookie dataUsingEncoding:NSISOLatin1StringEncoding];
186     _cookie = [cookieParser parseValue:_cookie ofHeaderField:@"cookie"];
187   }
188   
189   if ([_cookie isKindOfClass:[NSArray class]]) {
190     id singleCookie;
191       
192     _cookie = [_cookie objectEnumerator];
193     
194     while ((singleCookie = [_cookie nextObject]))
195       [self _addHTTPCookie:singleCookie to:_a];
196     return;
197   }
198
199     
200     cookieValue = [_cookie value];
201     if ([cookieValue isKindOfClass:[NSArray class]]) {
202       if ([cookieValue count] == 0)
203         cookieValue = @"";
204       else if ([cookieValue count] == 1)
205         cookieValue = [[cookieValue objectAtIndex:0] stringValue];
206       else {
207         [self logWithFormat:
208                 @"got %i values for cookie '%@', using first only.",
209                 [cookieValue count],
210                 [_cookie cookieName]];
211         cookieValue = [[cookieValue objectAtIndex:0] stringValue];
212       }
213     }
214     else
215       cookieValue = [cookieValue stringValue];
216     
217     cookie = [WOCookieClass cookieWithName:[_cookie cookieName]
218                             value:cookieValue
219                             path:[_cookie path]
220                             domain:[_cookie domainName]
221                             expires:[_cookie expireDate]
222                             isSecure:[_cookie needsSecureChannel]];
223   
224     /* WOMessage */
225     if (cookie)
226       [_a addObject:cookie];
227 }
228
229 - (NSArray *)woCookies {
230   NSMutableArray *womCookies;
231   NSArray        *woCookies;
232   NSEnumerator   *mcookies;
233   id             cookie;
234
235   if (NSArrayClass == Nil) NSArrayClass = [NSArray class];
236   
237   womCookies = [NSMutableArray arrayWithCapacity:8];
238   
239   mcookies = [self valuesOfHeaderFieldWithName:@"cookie"];
240   while ((cookie = [mcookies nextObject])) {
241     if ([cookie isKindOfClass:NSArrayClass]) {
242       id singleCookie;
243       
244       cookie = [cookie objectEnumerator];
245       
246       while ((singleCookie = [cookie nextObject]))
247         [self _addHTTPCookie:singleCookie to:womCookies];
248       
249       continue;
250     }
251     
252     [self _addHTTPCookie:cookie to:womCookies];
253   }
254   
255   woCookies = [womCookies copy];
256   return [woCookies autorelease];
257 }
258
259 /* content */
260
261 - (NSData *)woContent {
262   NGMimePartGenerator *gen;
263   id content;
264   
265   if ((content = [self body]) == nil) {
266     /* no body */
267     return nil;
268   }
269   
270   if ([content isKindOfClass:[NSData class]])
271     return content;
272   
273   if (![content conformsToProtocol:@protocol(NGMimePart)])
274     return [[content stringValue] dataUsingEncoding:NSASCIIStringEncoding];
275   
276   gen  = [[NGMimePartGenerator alloc] init];
277   content = [gen generateMimeFromPart:content];
278   [gen release];
279   return content;
280 }
281
282 /* form parameters */
283
284 static NGMimeType *multipartFormData = nil;
285 static Class      DispClass = Nil;
286
287 - (id)_decodeMultiPartFormDataContent {
288   NGMutableHashMap    *formContent;
289   NGMimeMultipartBody *ebody;
290   NSArray             *parts;
291   unsigned            i, count;
292
293   if (DispClass == Nil)
294     DispClass = [NGMimeContentDispositionHeaderField class];
295   
296   ebody = [self body];
297   if (![ebody isKindOfClass:[NGMimeMultipartBody class]]) {
298     [self logWithFormat:
299             @"ERROR: form-data parser expected MultipartBody, got %@", ebody];
300     return [[NGHashMap alloc] init];
301   }
302   
303   parts = [ebody parts];
304   count = [parts count];
305   
306   [self debugWithFormat:@"%s:   %i parts %@", __PRETTY_FUNCTION__, 
307           count, parts];
308   
309   formContent = [[NGMutableHashMap alloc] init];
310   for (i = 0; i < count; i++) {
311     NGMimeContentDispositionHeaderField *disposition;
312     id<NGMimePart> bodyPart;
313     NSString *name;
314     id       partBody;
315           
316     bodyPart = [parts objectAtIndex:i];
317     disposition =
318       [[bodyPart valuesOfHeaderFieldWithName:@"content-disposition"]nextObject];
319           
320     if (disposition == nil) {
321       [self logWithFormat:
322               @"ERROR: did not find content disposition in form part %@",
323               bodyPart];
324       continue;
325     }
326
327     /* morph to disposition field in case it's unparsed ... */
328     if (![disposition isKindOfClass:DispClass]) {
329               disposition =
330                 [[DispClass alloc] initWithString:[disposition stringValue]];
331               [disposition autorelease];
332     }
333             
334     name     = [disposition name];
335     partBody = [bodyPart body];
336     
337     if (partBody)
338       [(NGMutableHashMap *)formContent addObject:partBody forKey:name];
339   }
340   return formContent;
341 }
342
343 - (NGHashMap *)_decodeFormContentURLParameters:(id)formContent {
344   /* all this sounds expensive ;-) */
345   NSString   *s;
346   unsigned   urilen;
347   char       *uribuf;
348   const char *p = uribuf;
349   NGHashMap  *map;
350   
351   if ((s = [self uri]) == nil)
352     return formContent;
353   if ([s rangeOfString:@"?"].length == 0)
354     return formContent;
355   
356   urilen = [s cStringLength];
357   p = uribuf = malloc(urilen + 4);
358   [s getCString:uribuf]; // UNICODE?
359   
360   if ((p = index(p, '?')) == NULL) {
361     if (uribuf) free(uribuf);
362     return formContent;
363   }
364   
365   p++; // skip the '?'
366   map = NGDecodeUrlFormParameters(p, strlen(p));
367   if (uribuf) free(uribuf); uribuf = NULL; p = NULL;
368   
369   if (map == nil) 
370     return formContent;
371   if (formContent == nil)
372     return map;
373   
374   map = [map autorelease]; // NGDecodeUrlFormParameters returns a retained map!
375   
376   if ([formContent isKindOfClass:[NGHashMap class]]) {
377     NSEnumerator *keys;
378     id key, tmp;
379   
380     tmp = formContent;
381     formContent =  [[NGMutableHashMap alloc] initWithHashMap:tmp];
382     [tmp release];
383   
384     keys = [map keyEnumerator];
385     while ((key = [keys nextObject])) {
386       NSEnumerator *values;
387       id value;
388             
389       values = [map objectEnumeratorForKey:key];
390       while ((value = [values nextObject]))
391         [formContent addObject:value forKey:key];
392     }
393   }
394   else if ([formContent isKindOfClass:[NSDictionary class]]) {
395     NSEnumerator *keys;
396     id key, tmp;
397           
398     tmp = formContent;
399     formContent = [[NGMutableHashMap alloc] initWithDictionary:tmp];
400     [tmp release];
401   
402     keys = [map keyEnumerator];
403     while ((key = [keys nextObject])) {
404       NSEnumerator *values;
405       id value;
406   
407       values = [map objectEnumeratorForKey:key];
408       while ((value = [values nextObject]))
409         [formContent addObject:value forKey:key];
410     }
411   }
412   return formContent;
413 }
414
415 - (NGHashMap *)formParameters {
416   id formContent;
417   
418   if (multipartFormData == nil) {
419     multipartFormData = [NGMimeType mimeType:@"multipart/form-data"];
420     multipartFormData = [multipartFormData retain];
421   }
422   
423   if ([[self methodName] isEqualToString:@"POST"]) {
424     NGMimeType *contentType = [self contentType];
425     
426     //NSLog(@"%s: process POST, ctype %@", __PRETTY_FUNCTION__, contentType);
427     
428     formContent = [contentType hasSameType:multipartFormData]
429       ? [self _decodeMultiPartFormDataContent]
430       : [[self body] retain];
431   }
432   else
433     formContent = nil;
434   
435   /* decode URL parameters */
436   formContent = [self _decodeFormContentURLParameters:formContent];
437   
438   return [formContent autorelease];
439 }
440
441 @end /* NGHttpRequest */