1/* 2 Copyright (C) 2000-2008 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE 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 SOPE 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 SOPE; 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 <NGObjWeb/WOCookie.h> 23#include "common.h" 24 25@interface WOCookie(PrivateMethods) 26 27- (id)initWithName:(NSString *)_name value:(NSString *)_value 28 path:(NSString *)_path domain:(NSString *)_domain 29 expires:(NSDate *)_date 30 isSecure:(BOOL)_secure 31 httpOnly: (BOOL)_httpOnly; 32 33@end 34 35@implementation WOCookie 36 37static WOCookie *_parseCookie(const char *_bytes, unsigned _len); 38 39// abbr weekday, day-of-month, abbr-month, year hour:min:sec GMT 40static NSString *cookieDateFormat = @"%a, %d-%b-%Y %H:%M:%S %Z"; 41 42+ (id)cookieWithString:(NSString *)_string { 43 /* private method ! */ 44 const char *utf8 = [_string UTF8String]; 45 if (utf8 == NULL) return nil; 46 return _parseCookie(utf8, strlen(utf8)); 47} 48 49+ (id)cookieWithName:(NSString *)_name value:(NSString *)_value { 50 return [[[self alloc] initWithName:_name value:_value 51 path:nil domain:nil 52 expires:nil isSecure:NO httpOnly:NO] 53 autorelease]; 54} 55 56+ (id)cookieWithName:(NSString *)_name value:(NSString *)_value 57 path:(NSString *)_path domain:(NSString *)_domain 58 expires:(NSDate *)_date 59 isSecure:(BOOL)_secure 60{ 61 return [[[self alloc] initWithName:_name value:_value 62 path:_path domain:_domain 63 expires:_date isSecure:_secure httpOnly:NO] 64 autorelease]; 65} 66 67+ (id)cookieWithName:(NSString *)_name value:(NSString *)_value 68 path:(NSString *)_path domain:(NSString *)_domain 69 expires:(NSDate *)_date 70 isSecure:(BOOL)_secure 71 httpOnly: (BOOL)_httpOnly 72{ 73 return [[[self alloc] initWithName:_name value:_value 74 path:_path domain:_domain 75 expires:_date isSecure:_secure httpOnly:_httpOnly] 76 autorelease]; 77} 78 79- (id)initWithName:(NSString *)_name value:(NSString *)_value 80 path:(NSString *)_path domain:(NSString *)_domain 81 expires:(NSDate *)_date 82 isSecure:(BOOL)_secure 83 httpOnly:(BOOL)_httpOnly 84{ 85 if ((self = [super init]) != nil) { 86 NSZone *z = [self zone]; 87 self->name = [_name copyWithZone:z]; 88 self->value = [_value copyWithZone:z]; 89 self->path = [_path copyWithZone:z]; 90 self->domainName = [_domain copyWithZone:z]; 91 self->expireDate = [_date retain]; // TBD: should be copy? 92 self->onlyIfSecure = _secure; 93 self->httpOnly = _httpOnly; 94 } 95 return self; 96} 97 98- (void)dealloc { 99 [self->name release]; 100 [self->value release]; 101 [self->expireDate release]; 102 [self->path release]; 103 [self->domainName release]; 104 [super dealloc]; 105} 106 107/* accessors */ 108 109- (NSString *)cookieName { 110 /* ?? */ 111 return self->name; 112} 113 114- (void)setName:(NSString *)_name { 115 ASSIGNCOPY(self->name, _name); 116} 117- (NSString *)name { 118 return self->name; 119} 120 121- (void)setValue:(NSString *)_value { 122 ASSIGNCOPY(self->value, _value); 123} 124- (NSString *)value { 125 return self->value; 126} 127 128- (void)setPath:(NSString *)_path { 129 ASSIGNCOPY(self->path, _path); 130} 131- (NSString *)path { 132 return self->path; 133} 134 135- (void)setExpires:(NSDate *)_date { 136 ASSIGNCOPY(self->expireDate, _date); 137} 138- (NSDate *)expires { 139 return self->expireDate; 140} 141 142- (void)setDomain:(NSString *)_domain { 143 ASSIGNCOPY(self->domainName, _domain); 144} 145- (NSString *)domain { 146 return self->domainName; 147} 148 149- (void)setIsSecure:(BOOL)_flag { 150 self->onlyIfSecure = _flag ? YES : NO; 151} 152- (BOOL)isSecure { 153 return self->onlyIfSecure; 154} 155 156- (void)setHttpOnly:(BOOL)_flag { 157 self->httpOnly = _flag ? YES : NO; 158} 159- (BOOL)isHttpOnly { 160 return self->httpOnly; 161} 162 163- (NSDate *)expireDate { 164 // DEPRECATED 165 return self->expireDate; 166} 167 168/* NSCopying */ 169 170- (id)copyWithZone:(NSZone *)_zone { 171 return [[WOCookie alloc] initWithName:self->name value:self->value 172 path:self->path domain:self->domainName 173 expires:self->expireDate 174 isSecure:self->onlyIfSecure 175 httpOnly:self->httpOnly]; 176} 177 178/* description */ 179 180- (NSString *)headerString { 181 return [@"set-cookie: " stringByAppendingString:[self stringValue]]; 182} 183 184- (NSString *)stringValue { 185 NSMutableString *str; 186 187 str = [NSMutableString stringWithCapacity:512]; 188 [str appendString:[self->name stringByEscapingURL]]; 189 [str appendString:@"="]; 190 [str appendString:[[self->value stringValue] stringByEscapingURL]]; 191 192 if (self->expireDate) { 193 static NSTimeZone *gmt = nil; 194 static NSMutableDictionary *localeDict = nil; 195 NSString *s; 196 if (gmt == nil) 197 gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain]; 198 if (localeDict == nil) 199 { 200 localeDict = [NSMutableDictionary new]; 201 202 [localeDict setObject: [NSArray arrayWithObjects: @"Jan", @"Feb", 203 @"Mar", @"Apr", @"May", @"Jun", 204 @"Jul", @"Aug", @"Sep", @"Oct", 205 @"Nov", @"Dec", nil] 206 forKey: @"NSShortMonthNameArray"]; 207 [localeDict setObject: [NSArray arrayWithObjects: @"Sun", @"Mon", 208 @"Tue", @"Wed", @"Thu", @"Fri", 209 @"Sat", nil] 210 forKey: @"NSShortWeekDayNameArray"]; 211 } 212 213 // TODO: replace, -descriptionWithCalendarFormat is *slow* 214 s = [self->expireDate descriptionWithCalendarFormat:cookieDateFormat 215 timeZone:gmt 216 locale:localeDict]; 217 218 [str appendString:@"; expires="]; 219 [str appendString:s]; 220 } 221 if (self->path) { 222 [str appendString:@"; path="]; 223 [str appendString:self->path]; 224 } 225 if (self->domainName) { 226 [str appendString:@"; domain="]; 227 [str appendString:self->domainName]; 228 } 229 if (self->onlyIfSecure) 230 [str appendString:@"; secure"]; 231 232 if (self->httpOnly) 233 [str appendString:@"; HttpOnly"]; 234 235 return str; 236} 237 238- (NSString *)description { 239 NSMutableString *str; 240 241 str = [NSMutableString stringWithCapacity:128]; 242 [str appendFormat:@"<%@[0x%p]: name=%@ value=%@", 243 NSStringFromClass([self class]), self, 244 self->name, self->value]; 245 246 if (self->expireDate) { 247 [str appendString:@" expires="]; 248 [str appendString:[self->expireDate description]]; 249 } 250 251 if (self->path) { 252 [str appendString:@" path="]; 253 [str appendString:self->path]; 254 } 255 if (self->domainName) { 256 [str appendString:@" domain="]; 257 [str appendString:self->domainName]; 258 } 259 if (self->onlyIfSecure) 260 [str appendString:@" secure"]; 261 262 if (self->httpOnly) 263 [str appendString:@" HttpOnly"]; 264 265 [str appendString:@">"]; 266 267 return str; 268} 269 270/* cookie parsing */ 271 272static WOCookie *_parseCookie(const char *_bytes, unsigned _len) { 273 WOCookie *cookie = nil; 274 unsigned pos, toGo; 275 276 for (pos = 0, toGo = _len; (toGo > 0) && (_bytes[pos] != '='); toGo--, pos++) 277 ; 278 279 if (toGo > 0) { 280 NSString *name = nil; 281 NSString *value = nil; 282 283 // NSLog(@"pos=%i toGo=%i", pos, toGo); 284 285 name = [[NSString alloc] 286 initWithCString:_bytes 287 length:pos]; 288 value = [[NSString alloc] 289 initWithCString:&(_bytes[pos + 1]) 290 length:(toGo - 1)]; 291 292 //NSLog(@"pair='%@'", [NSString stringWithCString:_bytes length:_len]); 293 //NSLog(@"name='%@' value='%@'", name, value); 294 295 if ((name == nil) || (value == nil)) { 296 NSLog(@"ERROR: invalid cookie pair%s%s: %@", 297 value ? "" : ", no value", 298 name ? "" : ", no name", 299 [NSString stringWithCString:_bytes length:_len]); 300 [name release]; 301 [value release]; 302 return nil; 303 } 304 else { 305 cookie = [WOCookie cookieWithName:[name stringByUnescapingURL] 306 value:[value stringByUnescapingURL]]; 307 } 308 309 [name release]; name = nil; 310 [value release]; value = nil; 311 } 312#if DEBUG 313 else { 314 NSLog(@"ERROR(%s:%i): invalid cookie pair: %@", 315 __PRETTY_FUNCTION__, __LINE__, 316 [NSString stringWithCString:_bytes length:_len]); 317 } 318#endif 319 return cookie; 320} 321 322@end /* WOCookie */ 323