2
Copyright (C) 2000-2005 SKYRIX Software AG
4
This file is part of SOPE.
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
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.
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
22
#include "NGMimeType.h"
23
#include "NGConcreteMimeType.h"
24
#include "NGMimeUtilities.h"
27
NGMime_DECLARE NSString *NGMimeTypeText = @"text";
28
NGMime_DECLARE NSString *NGMimeTypeAudio = @"audio";
29
NGMime_DECLARE NSString *NGMimeTypeVideo = @"video";
30
NGMime_DECLARE NSString *NGMimeTypeImage = @"image";
31
NGMime_DECLARE NSString *NGMimeTypeApplication = @"application";
32
NGMime_DECLARE NSString *NGMimeTypeMultipart = @"multipart";
33
NGMime_DECLARE NSString *NGMimeTypeMessage = @"message";
34
NGMime_DECLARE NSString *NGMimeParameterTextCharset = @"charset";
36
static BOOL _parseMimeType(id self, NSString *_str, NSString **type,
37
NSString **subType, NSDictionary **parameters);
39
@implementation NGMimeType
45
static NSMutableDictionary *typeToClass = nil;
48
classForType(NSString *_type, NSString *_subType, NSDictionary *_parameters)
51
if (_type == nil) return Nil;
53
if ([_type isEqualToString:@"*"] || [_subType isEqualToString:@"*"])
54
return [NGConcreteWildcardType class];
56
if ([_type isEqualToString:NGMimeTypeApplication]) {
57
if ([_subType isEqualToString:@"octet"])
58
return [NGConcreteAppOctetMimeType class];
60
if ([_type isEqualToString:NGMimeTypeText]) {
61
if ([_subType isEqualToString:@"x-vcard"])
62
return [NGConcreteTextVcardMimeType class];
65
c = [typeToClass objectForKey:_type];
66
return c ? c : [NGConcreteGenericMimeType class];
68
static Class NSStringClass = Nil;
71
static BOOL isInitialized = NO;
75
typeToClass = [[NSMutableDictionary alloc] initWithCapacity:10];
76
[typeToClass setObject:[NGConcreteTextMimeType class]
77
forKey:NGMimeTypeText];
78
[typeToClass setObject:[NGConcreteVideoMimeType class]
79
forKey:NGMimeTypeVideo];
80
[typeToClass setObject:[NGConcreteAudioMimeType class]
81
forKey:NGMimeTypeAudio];
82
[typeToClass setObject:[NGConcreteImageMimeType class]
83
forKey:NGMimeTypeImage];
84
[typeToClass setObject:[NGConcreteApplicationMimeType class]
85
forKey:NGMimeTypeApplication];
86
[typeToClass setObject:[NGConcreteMultipartMimeType class]
87
forKey:NGMimeTypeMultipart];
88
[typeToClass setObject:[NGConcreteMessageMimeType class]
89
forKey:NGMimeTypeMessage];
93
+ (NSStringEncoding)stringEncodingForCharset:(NSString *)_s {
95
NSStringEncoding encoding;
96
BOOL foundUnsupported;
98
foundUnsupported = NO;
99
charset = [_s lowercaseString];
101
if ([charset length] == 0)
102
encoding = [NSString defaultCStringEncoding];
105
else if ([charset isEqualToString:@"us-ascii"])
106
encoding = NSASCIIStringEncoding;
107
else if ([charset isEqualToString:@"utf-8"])
108
encoding = NSUTF8StringEncoding;
109
else if ([charset isEqualToString:@"utf-16"])
110
encoding = NSUnicodeStringEncoding;
113
else if ([charset isEqualToString:@"iso-latin-1"])
114
encoding = NSISOLatin1StringEncoding;
115
else if ([charset isEqualToString:@"iso-8859-1"])
116
encoding = NSISOLatin1StringEncoding;
117
else if ([charset isEqualToString:@"8859-1"])
118
encoding = NSISOLatin1StringEncoding;
120
/* some unsupported, but known encoding */
121
else if ([charset isEqualToString:@"ks_c_5601-1987"]) {
122
encoding = [NSString defaultCStringEncoding];
123
foundUnsupported = YES;
125
else if ([charset isEqualToString:@"euc-kr"]) {
126
encoding = [NSString defaultCStringEncoding];
127
foundUnsupported = YES;
129
else if ([charset isEqualToString:@"big5"]) {
130
encoding = [NSString defaultCStringEncoding];
131
foundUnsupported = YES;
133
else if ([charset isEqualToString:@"iso-2022-jp"]) {
134
encoding = [NSString defaultCStringEncoding];
135
foundUnsupported = YES;
137
else if ([charset isEqualToString:@"gb2312"]) {
138
encoding = [NSString defaultCStringEncoding];
139
foundUnsupported = YES;
141
else if ([charset isEqualToString:@"koi8-r"]) {
142
encoding = [NSString defaultCStringEncoding];
143
foundUnsupported = YES;
146
else if ([charset isEqualToString:@"windows-1252"]) {
147
encoding = NSWindowsCP1252StringEncoding;
149
else if ([charset isEqualToString:@"iso-8859-2"]) {
150
encoding = NSISOLatin2StringEncoding;
152
else if ([charset isEqualToString:@"x-unknown"] ||
153
[charset isEqualToString:@"unknown"]) {
154
encoding = NSASCIIStringEncoding;
157
#if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
158
else if ([charset isEqualToString:@"iso-latin-9"])
159
encoding = NSISOLatin9StringEncoding;
160
else if ([charset isEqualToString:@"iso-8859-15"])
161
encoding = NSISOLatin9StringEncoding;
162
else if ([charset isEqualToString:@"8859-15"])
163
encoding = NSISOLatin9StringEncoding;
166
[self logWithFormat:@"%s: unknown charset '%@'",
167
__PRETTY_FUNCTION__, _s];
168
encoding = [NSString defaultCStringEncoding];
175
- (id)initWithType:(NSString *)_type subType:(NSString *)_subType
176
parameters:(NSDictionary *)_parameters
180
c = classForType(_type, _subType, _parameters);
183
return [[c alloc] initWithType:_type subType:_subType
184
parameters:_parameters];
187
+ (id)mimeType:(NSString *)_type subType:(NSString *)_subType {
190
c = classForType(_type, _subType, nil);
192
NSAssert(c, @"did not find class for mimetype ..");
194
return [[[c alloc] initWithType:_type subType:_subType
195
parameters:nil] autorelease];
198
+ (id)mimeType:(NSString *)_type subType:(NSString *)_subType
199
parameters:(NSDictionary *)_parameters
203
c = classForType(_type, _subType, _parameters);
204
NSAssert(c, @"did not find class for mimetype ..");
206
return [[[c alloc] initWithType:_type subType:_subType
207
parameters:_parameters] autorelease];
210
+ (id)mimeType:(NSString *)_stringValue {
211
NSString *type, *subType;
212
NSDictionary *parameters;
214
if ([_stringValue length] == 0)
222
if (_parseMimeType(self, _stringValue, &type, &subType, ¶meters)) {
226
c = classForType(type, subType, nil);
227
NSAssert(c, @"did not find class for mimetype ..");
228
NSAssert(type, @"didn't parse type ..");
229
NSAssert(subType, @"didn't parse subtype ..");
232
NSAssert(result, @"allocation of mimetype failed ..");
234
result = [result initWithType:type subType:subType parameters:parameters];
235
NSAssert(result, @"initialization of mimetype failed ..");
237
result = [result autorelease];
238
NSAssert(result, @"autorelease of mimetype failed ..");
243
[self logWithFormat:@"ERROR[%s]: parsing of mimetype '%@' failed !",
244
__PRETTY_FUNCTION__, _stringValue];
245
return nil; // parsing failed
252
[self subclassResponsibility:_cmd];
255
- (NSString *)subType {
256
[self subclassResponsibility:_cmd];
259
- (BOOL)isCompositeType {
260
[self subclassResponsibility:_cmd];
264
/* comparing types */
266
- (BOOL)isEqual:(id)_other {
267
if (_other == nil) return NO;
268
if (_other == self) return YES;
270
return ([_other isKindOfClass:[NGMimeType class]])
271
? [self isEqualToMimeType:_other]
275
- (BOOL)isEqualToMimeType:(NGMimeType *)_type {
276
if (_type == nil) return NO;
277
if (_type == self) return YES;
279
if (![self hasSameType:_type])
282
if (![[_type parametersAsDictionary] isEqual:[self parametersAsDictionary]])
288
- (BOOL)hasSameGeneralType:(NGMimeType *)_other { // only the 'type' must match
289
if (_other == self) return YES;
290
if ([_other isCompositeType] != [self isCompositeType]) return NO;
291
if (![[_other type] isEqualToString:[self type]]) return NO;
294
- (BOOL)hasSameType:(NGMimeType *)_other { // parameters need not match
295
if (_other == nil) return NO;
296
if (_other == self) return YES;
297
if ([_other isCompositeType] != [self isCompositeType]) return NO;
298
if (![[_other type] isEqualToString:[self type]]) return NO;
299
if (![[_other subType] isEqualToString:[self subType]]) return NO;
303
- (BOOL)doesMatchType:(NGMimeType *)_other { // interpretes wildcards
304
NSString *t, *st, *ot, *ost;
309
ost = [_other subType];
311
if ([t isEqualToString:@"*"] || [ot isEqualToString:@"*"]) {
315
if (![t isEqualToString:ot]) return NO;
317
if ([st isEqualToString:@"*"] || [ost isEqualToString:@"*"]) {
321
if (![st isEqualToString:ost]) return NO;
328
- (NSEnumerator *)parameterNames {
329
[self doesNotRecognizeSelector:_cmd]; // subclass
332
- (id)valueOfParameter:(NSString *)_parameterName {
333
[self doesNotRecognizeSelector:_cmd]; // subclass
337
/* representations */
339
- (NSDictionary *)parametersAsDictionary {
340
NSMutableDictionary *parameters;
345
if ((names = [self parameterNames]) == nil)
348
parameters = [[NSMutableDictionary alloc] init];
349
while ((name = [names nextObject]))
350
[parameters setObject:[self valueOfParameter:name] forKey:name];
352
d = [parameters copy];
353
[parameters release];
354
return [d autorelease];
357
- (NSString *)parametersAsString {
359
NSMutableString *result;
362
if ((names = [self parameterNames]) == nil)
365
result = [NSMutableString stringWithCapacity:64];
366
while ((name = [names nextObject])) {
369
value = [[self valueOfParameter:name] stringValue];
371
[result appendString:@"; "];
372
[result appendString:name];
373
[result appendString:@"="];
375
if ([self valueNeedsQuotes:value]) {
376
[result appendString:@"\""];
377
[result appendString:value];
378
[result appendString:@"\""];
381
[result appendString:value];
386
- (BOOL)valueNeedsQuotes:(NSString *)_parameterValue {
387
unsigned len = [_parameterValue cStringLength];
393
[_parameterValue getCString:cstr]; cstr[len] = '\0';
395
if (isMime_SpecialByte(*cstr))
406
- (NSString *)stringValue {
407
[self subclassResponsibility:_cmd];
413
- (Class)classForCoder {
414
return [NGMimeType class];
417
- (void)encodeWithCoder:(NSCoder *)_encoder {
418
[_encoder encodeObject:[self type]];
419
[_encoder encodeObject:[self subType]];
420
[_encoder encodeObject:[self parametersAsDictionary]];
423
- (id)initWithCoder:(NSCoder *)_decoder {
424
NSString *type, *subType;
427
type = [_decoder decodeObject];
428
subType = [_decoder decodeObject];
429
paras = [_decoder decodeObject];
431
return [self initWithType:type subType:subType parameters:paras];
436
- (id)copyWithZone:(NSZone *)_zone {
437
return [[NGMimeType allocWithZone:_zone]
438
initWithType:[self type] subType:[self subType]
439
parameters:[self parametersAsDictionary]];
444
- (NSString *)description {
445
return [NSString stringWithFormat:@"<NGMimeType: %@>", [self stringValue]];
448
@end /* NGMimeType */
456
NSString *application;
459
} NGMimeTypeConstants;
474
NSString *octetStream;
475
} NGMimeSubTypeConstants;
477
static NGMimeTypeConstants *MimeTypeConstants = NULL;
478
static NGMimeSubTypeConstants *MimeSubTypeConstants = NULL;
480
static NSString *_stringForType(char *_type, int _len) {
481
if (NSStringClass == Nil) NSStringClass = [NSString class];
483
if (MimeTypeConstants == NULL) {
484
MimeTypeConstants = malloc(sizeof(NGMimeTypeConstants));
485
MimeTypeConstants->image = NGMimeTypeImage;
486
MimeTypeConstants->video = NGMimeTypeVideo;
487
MimeTypeConstants->audio = NGMimeTypeAudio;
488
MimeTypeConstants->text = NGMimeTypeText;
489
MimeTypeConstants->star = @"*";
490
MimeTypeConstants->application = NGMimeTypeApplication;
491
MimeTypeConstants->multipart = NGMimeTypeMultipart;
492
MimeTypeConstants->message = NGMimeTypeMessage;
499
return MimeTypeConstants->star;
502
if (strncmp(_type, "text", 4) == 0)
503
return MimeTypeConstants->text;
506
if (_type[0] == 'i') {
507
if (strncmp(_type, "image", 5) == 0)
508
return MimeTypeConstants->image;
510
else if (_type[0] == 'v') {
511
if (strncmp(_type, "video", 5) == 0)
512
return MimeTypeConstants->video;
514
else if (_type[0] == 'a') {
515
if (strncmp(_type, "audio", 5) == 0)
516
return MimeTypeConstants->audio;
520
if (strncmp(_type, "message", 7) == 0)
521
return MimeTypeConstants->message;
524
if (strncmp(_type, "multipart", 9) == 0)
525
return MimeTypeConstants->multipart;
527
if (strncmp(_type, "application", 11) == 0)
528
return MimeTypeConstants->application;
531
return [NSStringClass stringWithCString:_type length:_len];
534
static NSString *_stringForSubType(char *_type, int _len) {
535
if (NSStringClass == Nil) NSStringClass = [NSString class];
537
if (MimeSubTypeConstants == NULL) {
538
MimeSubTypeConstants = malloc(sizeof(NGMimeSubTypeConstants));
540
MimeSubTypeConstants->plain = @"plain";
541
MimeSubTypeConstants->star = @"*";
542
MimeSubTypeConstants->mixed = @"mixed";
543
MimeSubTypeConstants->jpeg = @"jpeg";
544
MimeSubTypeConstants->png = @"png";
545
MimeSubTypeConstants->gif = @"gif";
546
MimeSubTypeConstants->xml = @"xml";
547
MimeSubTypeConstants->html = @"html";
548
MimeSubTypeConstants->css = @"css";
549
MimeSubTypeConstants->xMng = @"xMng";
550
MimeSubTypeConstants->xhtmlXml = @"xhtmlXml";
551
MimeSubTypeConstants->rfc822 = @"rfc822";
552
MimeSubTypeConstants->octetStream = @"octet-stream";
560
return MimeSubTypeConstants->star;
563
if (_type[0] == 'p') {
564
if (strncmp(_type, "png", 3) == 0)
565
return MimeSubTypeConstants->png;
567
else if (_type[0] == 'g') {
568
if (strncmp(_type, "gif", 3) == 0)
569
return MimeSubTypeConstants->gif;
571
else if (_type[0] == 'c') {
572
if (strncmp(_type, "css", 3) == 0)
573
return MimeSubTypeConstants->css;
575
else if (_type[0] == 'x') {
576
if (strncmp(_type, "xml", 3) == 0)
577
return MimeSubTypeConstants->xml;
581
if (_type[0] == 'h') {
582
if (strncmp(_type, "html", 4) == 0)
583
return MimeSubTypeConstants->html;
585
else if (_type[0] == 'j') {
586
if (strncmp(_type, "jpeg", 4) == 0)
587
return MimeSubTypeConstants->jpeg;
591
if (_type[0] == 'p') {
592
if (strncmp(_type, "plain", 5) == 0)
593
return MimeSubTypeConstants->plain;
595
else if (_type[0] == 'm') {
596
if (strncmp(_type, "mixed", 5) == 0)
597
return MimeSubTypeConstants->mixed;
599
else if (_type[0] == 'x') {
600
if (strncmp(_type, "x-mng", 5) == 0)
601
return MimeSubTypeConstants->xMng;
605
if (strncmp(_type, "rfc822", 6) == 0)
606
return MimeSubTypeConstants->rfc822;
609
if (strncmp(_type, "xhtml+xml", 9) == 0)
610
return MimeSubTypeConstants->xhtmlXml;
613
if (strncmp(_type, "octet-stream", 12) == 0)
614
return MimeSubTypeConstants->octetStream;
617
return [NSStringClass stringWithCString:_type length:_len];
620
static BOOL _parseMimeType(id self, NSString *_str, NSString **type,
621
NSString **subType, NSDictionary **parameters)
625
unsigned slen = [_str length];
626
unichar buf[slen + 1];
631
[_str getCharacters:buf]; buf[slen] = '\0';
633
/* skip leading spaces */
634
while (isRfc822_LWSP(*cstr) && (*cstr != '\0'))
638
tmp = cstr; // keep beginning of type name
640
while ((*cstr != '/') && (*cstr != '\0') && (*cstr != ';')) {
644
if (len == 0) return NO; // no type was read
647
unsigned char buf[len + 1];
651
for (i = 0; i < len; i++) buf[i] = tolower(tmp[i]);
652
*type = _stringForType(buf, len);
655
if (*cstr == '/') { // subtype name
658
tmp = cstr; // keep beginning of subtype name
660
while ((*cstr != ';') && (!isRfc822_LWSP(*cstr)) && (*cstr != '\0')) {
666
return YES; // no subtype was read
669
unsigned char buf[len + 1];
673
for (i = 0; i < len; i++) buf[i] = tolower(tmp[i]);
674
*subType = _stringForSubType(buf, len);
682
while (isRfc822_LWSP(*cstr) && (*cstr != '\0'))
685
if (*cstr == ';') // skip ';' (parameter separator)
689
while (isRfc822_LWSP(*cstr) && (*cstr != '\0'))
692
if (*cstr == '\0') { // string ends, no parameters defined
697
*parameters = parseParameters(self, _str, cstr);
698
if (![*parameters count])