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