/* Implementation for NSURLResponse for GNUstep Copyright (C) 2006 Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: 2006 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #import "common.h" #define EXPOSE_NSURLResponse_IVARS 1 #import "GSURLPrivate.h" #import "GSPrivate.h" #import "Foundation/NSCoder.h" #import "Foundation/NSDictionary.h" #import "Foundation/NSScanner.h" #import "NSCallBacks.h" #import "GNUstepBase/GSMime.h" // Internal data storage typedef struct { long long expectedContentLength; NSURL *URL; NSString *MIMEType; NSString *textEncodingName; NSString *statusText; NSMutableDictionary *headers; /* _GSMutableInsensitiveDictionary */ int statusCode; } Internal; #define this ((Internal*)(self->_NSURLResponseInternal)) #define inst ((Internal*)(o->_NSURLResponseInternal)) @interface _GSMutableInsensitiveDictionary : NSMutableDictionary @end @implementation NSURLResponse (Private) - (void) _checkHeaders { if (NSURLResponseUnknownLength == this->expectedContentLength) { NSString *s= [self _valueForHTTPHeaderField: @"content-length"]; if ([s length] > 0) { this->expectedContentLength = [s intValue]; } } if (nil == this->MIMEType) { GSMimeHeader *c; GSMimeParser *p; NSScanner *s; NSString *v; v = [self _valueForHTTPHeaderField: @"content-type"]; if (v == nil) { v = @"text/plain"; // No content type given. } s = [NSScanner scannerWithString: v]; p = [GSMimeParser new]; c = AUTORELEASE([GSMimeHeader new]); /* We just set the header body, so we know it will scan and don't need * to check the retrurn type. */ (void)[p scanHeaderBody: s into: c]; RELEASE(p); ASSIGNCOPY(this->MIMEType, [c value]); v = [c parameterForKey: @"charset"]; ASSIGNCOPY(this->textEncodingName, v); } } - (void) _setHeaders: (id)headers { NSEnumerator *e; NSString *v; if ([headers isKindOfClass: [NSDictionary class]] == YES) { NSString *k; e = [(NSDictionary*)headers keyEnumerator]; while ((k = [e nextObject]) != nil) { v = [(NSDictionary*)headers objectForKey: k]; [self _setValue: v forHTTPHeaderField: k]; } } else if ([headers isKindOfClass: [NSArray class]] == YES) { GSMimeHeader *h; e = [(NSArray*)headers objectEnumerator]; while ((h = [e nextObject]) != nil) { NSString *n = [h namePreservingCase: YES]; NSString *v = [h fullValue]; [self _setValue: v forHTTPHeaderField: n]; } } [self _checkHeaders]; } - (void) _setStatusCode: (NSInteger)code text: (NSString*)text { this->statusCode = code; ASSIGNCOPY(this->statusText, text); } - (void) _setValue: (NSString *)value forHTTPHeaderField: (NSString *)field { if (this->headers == 0) { this->headers = [_GSMutableInsensitiveDictionary new]; } [this->headers setObject: value forKey: field]; } - (NSString *) _valueForHTTPHeaderField: (NSString *)field { return [this->headers objectForKey: field]; } @end @implementation NSURLResponse + (id) allocWithZone: (NSZone*)z { NSURLResponse *o = [super allocWithZone: z]; if (o != nil) { o->_NSURLResponseInternal = NSZoneCalloc(z, 1, sizeof(Internal)); } return o; } - (id) copyWithZone: (NSZone*)z { NSURLResponse *o; if (NSShouldRetainWithZone(self, z) == YES) { o = RETAIN(self); } else { o = [[self class] allocWithZone: z]; o = [o initWithURL: [self URL] MIMEType: [self MIMEType] expectedContentLength: [self expectedContentLength] textEncodingName: [self textEncodingName]]; if (o != nil) { ASSIGN(inst->statusText, this->statusText); inst->statusCode = this->statusCode; if (this->headers == 0) { inst->headers = 0; } else { inst->headers = [this->headers mutableCopy]; } } } return o; } - (void) dealloc { if (this != 0) { RELEASE(this->URL); RELEASE(this->MIMEType); RELEASE(this->textEncodingName); RELEASE(this->statusText); RELEASE(this->headers); NSZoneFree([self zone], this); } [super dealloc]; } - (void) encodeWithCoder: (NSCoder*)aCoder { // FIXME if ([aCoder allowsKeyedCoding]) { } else { } } - (long long) expectedContentLength { return this->expectedContentLength; } - (id) initWithCoder: (NSCoder*)aCoder { // FIXME if ([aCoder allowsKeyedCoding]) { } else { } return self; } /** * Initialises the receiver with the URL, MIMEType, expected length and * text encoding name provided. */ - (id) initWithURL: (NSURL *)URL MIMEType: (NSString *)MIMEType expectedContentLength: (NSInteger)length textEncodingName: (NSString *)name { if ((self = [super init]) != nil) { ASSIGN(this->URL, URL); ASSIGNCOPY(this->MIMEType, MIMEType); ASSIGNCOPY(this->textEncodingName, name); this->expectedContentLength = length; } return self; } - (id) initWithURL: (NSURL*)URL statusCode: (NSInteger)statusCode HTTPVersion: (NSString*)HTTPVersion headerFields: (NSDictionary*)headerFields { self = [self initWithURL: URL MIMEType: nil expectedContentLength: NSURLResponseUnknownLength textEncodingName: nil]; if (nil != self) { this->statusCode = statusCode; this->headers = [headerFields copy]; [self _checkHeaders]; } return self; } - (NSString *) MIMEType { return this->MIMEType; } /** * Returns a suggested file name for storing the response data, with * suggested names being found in the following order:
* * content-disposition header * last path component of URL * host name from URL * 'unknown' * * If possible, an extension based on the MIME type of the response * is also appended.
* The result should always be a valid file name. */ - (NSString *) suggestedFilename { NSString *disp = [self _valueForHTTPHeaderField: @"content-disposition"]; NSString *name = nil; if (disp != nil) { GSMimeParser *p; GSMimeHeader *h; NSScanner *sc; // Try to get name from content disposition header. p = AUTORELEASE([GSMimeParser new]); h = [[GSMimeHeader alloc] initWithName: @"content-displosition" value: disp]; IF_NO_GC([h autorelease];) sc = [NSScanner scannerWithString: [h value]]; if ([p scanHeaderBody: sc into: h] == YES) { name = [h parameterForKey: @"filename"]; name = [name stringByDeletingPathExtension]; } } if ([name length] == 0) { name = [[[self URL] absoluteString] lastPathComponent]; name = [name stringByDeletingPathExtension]; } if ([name length] == 0) { name = [[self URL] host]; } if ([name length] == 0) { name = @"unknown"; } // FIXME ... add type specific extension return name; } - (NSString *) textEncodingName { return this->textEncodingName; } - (NSURL *) URL { return this->URL; } @end @implementation NSHTTPURLResponse + (NSString *) localizedStringForStatusCode: (NSInteger)statusCode { // FIXME ... put real responses in here return [NSString stringWithFormat: @"%"PRIdPTR, statusCode]; } - (NSDictionary *) allHeaderFields { return AUTORELEASE([this->headers copy]); } - (NSInteger) statusCode { return this->statusCode; } @end