123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #import <WebKit/WebNSDataExtras.h>
- #import <WebKit/WebNSDataExtrasPrivate.h>
- #import <wtf/Assertions.h>
- @interface NSString (WebNSDataExtrasInternal)
- - (NSString *)_web_capitalizeRFC822HeaderFieldName;
- @end
- @implementation NSString (WebNSDataExtrasInternal)
- -(NSString *)_web_capitalizeRFC822HeaderFieldName
- {
- CFStringRef name = (CFStringRef)self;
- NSString *result = nil;
- CFIndex i;
- CFIndex len = CFStringGetLength(name);
- char *charPtr = NULL;
- UniChar *uniCharPtr = NULL;
- Boolean useUniCharPtr = FALSE;
- Boolean shouldCapitalize = TRUE;
- Boolean somethingChanged = FALSE;
-
- for (i = 0; i < len; i ++) {
- UniChar ch = CFStringGetCharacterAtIndex(name, i);
- Boolean replace = FALSE;
- if (shouldCapitalize && ch >= 'a' && ch <= 'z') {
- ch = ch + 'A' - 'a';
- replace = TRUE;
- }
- else if (!shouldCapitalize && ch >= 'A' && ch <= 'Z') {
- ch = ch + 'a' - 'A';
- replace = TRUE;
- }
- if (replace) {
- if (!somethingChanged) {
- somethingChanged = TRUE;
- if (CFStringGetBytes(name, CFRangeMake(0, len), kCFStringEncodingISOLatin1, 0, FALSE, NULL, 0, NULL) == len) {
- // Can be encoded in ISOLatin1
- useUniCharPtr = FALSE;
- charPtr = CFAllocatorAllocate(NULL, len + 1, 0);
- CFStringGetCString(name, charPtr, len+1, kCFStringEncodingISOLatin1);
- }
- else {
- useUniCharPtr = TRUE;
- uniCharPtr = CFAllocatorAllocate(NULL, len * sizeof(UniChar), 0);
- CFStringGetCharacters(name, CFRangeMake(0, len), uniCharPtr);
- }
- }
- if (useUniCharPtr) {
- uniCharPtr[i] = ch;
- }
- else {
- charPtr[i] = ch;
- }
- }
- if (ch == '-') {
- shouldCapitalize = TRUE;
- }
- else {
- shouldCapitalize = FALSE;
- }
- }
- if (somethingChanged) {
- if (useUniCharPtr) {
- result = (NSString *)CFMakeCollectable(CFStringCreateWithCharactersNoCopy(NULL, uniCharPtr, len, NULL));
- }
- else {
- result = (NSString *)CFMakeCollectable(CFStringCreateWithCStringNoCopy(NULL, charPtr, kCFStringEncodingISOLatin1, NULL));
- }
- }
- else {
- result = [self retain];
- }
-
- return [result autorelease];
- }
- @end
- @implementation NSData (WebKitExtras)
- -(NSString *)_webkit_guessedMIMETypeForXML
- {
- int length = [self length];
- const UInt8 *bytes = [self bytes];
-
- #define CHANNEL_TAG_LENGTH 7
-
- const char *p = (const char *)bytes;
- int remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (CHANNEL_TAG_LENGTH - 1);
-
- BOOL foundRDF = false;
-
- while (remaining > 0) {
- // Look for a "<".
- const char *hit = memchr(p, '<', remaining);
- if (!hit) {
- break;
- }
-
- // We are trying to identify RSS or Atom. RSS has a top-level
- // element of either <rss> or <rdf>. However, there are
- // non-RSS RDF files, so in the case of <rdf> we further look
- // for a <channel> element. In the case of an Atom file, a
- // top-level <feed> element is all we need to see. Only tags
- // starting with <? or <! can precede the root element. We
- // bail if we don't find an <rss>, <feed> or <rdf> element
- // right after those.
-
- if (foundRDF) {
- if (strncasecmp(hit, "<channel", strlen("<channel")) == 0) {
- return @"application/rss+xml";
- }
- } else if (strncasecmp(hit, "<rdf", strlen("<rdf")) == 0) {
- foundRDF = TRUE;
- } else if (strncasecmp(hit, "<rss", strlen("<rss")) == 0) {
- return @"application/rss+xml";
- } else if (strncasecmp(hit, "<feed", strlen("<feed")) == 0) {
- return @"application/atom+xml";
- } else if (strncasecmp(hit, "<?", strlen("<?")) != 0 && strncasecmp(hit, "<!", strlen("<!")) != 0) {
- return nil;
- }
-
- // Skip the "<" and continue.
- remaining -= (hit + 1) - p;
- p = hit + 1;
- }
-
- return nil;
- }
- -(NSString *)_webkit_guessedMIMEType
- {
- #define JPEG_MAGIC_NUMBER_LENGTH 4
- #define SCRIPT_TAG_LENGTH 7
- #define TEXT_HTML_LENGTH 9
- #define VCARD_HEADER_LENGTH 11
- #define VCAL_HEADER_LENGTH 15
-
- NSString *MIMEType = [self _webkit_guessedMIMETypeForXML];
- if ([MIMEType length])
- return MIMEType;
-
- int length = [self length];
- const char *bytes = [self bytes];
-
- const char *p = bytes;
- int remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (SCRIPT_TAG_LENGTH - 1);
- while (remaining > 0) {
- // Look for a "<".
- const char *hit = memchr(p, '<', remaining);
- if (!hit) {
- break;
- }
-
- // If we found a "<", look for "<html>" or "<a " or "<script".
- if (strncasecmp(hit, "<html>", strlen("<html>")) == 0 ||
- strncasecmp(hit, "<a ", strlen("<a ")) == 0 ||
- strncasecmp(hit, "<script", strlen("<script")) == 0 ||
- strncasecmp(hit, "<title>", strlen("<title>")) == 0) {
- return @"text/html";
- }
-
- // Skip the "<" and continue.
- remaining -= (hit + 1) - p;
- p = hit + 1;
- }
-
- // Test for a broken server which has sent the content type as part of the content.
- // This code could be improved to look for other mime types.
- p = bytes;
- remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (TEXT_HTML_LENGTH - 1);
- while (remaining > 0) {
- // Look for a "t" or "T".
- const char *hit = NULL;
- const char *lowerhit = memchr(p, 't', remaining);
- const char *upperhit = memchr(p, 'T', remaining);
- if (!lowerhit && !upperhit) {
- break;
- }
- if (!lowerhit) {
- hit = upperhit;
- }
- else if (!upperhit) {
- hit = lowerhit;
- }
- else {
- hit = MIN(lowerhit, upperhit);
- }
-
- // If we found a "t/T", look for "text/html".
- if (strncasecmp(hit, "text/html", TEXT_HTML_LENGTH) == 0) {
- return @"text/html";
- }
-
- // Skip the "t/T" and continue.
- remaining -= (hit + 1) - p;
- p = hit + 1;
- }
-
- if ((length >= VCARD_HEADER_LENGTH) && strncmp(bytes, "BEGIN:VCARD", VCARD_HEADER_LENGTH) == 0) {
- return @"text/vcard";
- }
- if ((length >= VCAL_HEADER_LENGTH) && strncmp(bytes, "BEGIN:VCALENDAR", VCAL_HEADER_LENGTH) == 0) {
- return @"text/calendar";
- }
-
- // Test for plain text.
- int i;
- for(i=0; i<length; i++){
- char c = bytes[i];
- if ((c < 0x20 || c > 0x7E) && (c != '\t' && c != '\r' && c != '\n')) {
- break;
- }
- }
- if (i == length) {
- // Didn't encounter any bad characters, looks like plain text.
- return @"text/plain";
- }
-
- // Looks like this is a binary file.
-
- // Sniff for the JPEG magic number.
- if ((length >= JPEG_MAGIC_NUMBER_LENGTH) && strncmp(bytes, "\xFF\xD8\xFF\xE0", JPEG_MAGIC_NUMBER_LENGTH) == 0) {
- return @"image/jpeg";
- }
-
- #undef JPEG_MAGIC_NUMBER_LENGTH
- #undef SCRIPT_TAG_LENGTH
- #undef TEXT_HTML_LENGTH
- #undef VCARD_HEADER_LENGTH
- #undef VCAL_HEADER_LENGTH
-
- return nil;
- }
- @end
- @implementation NSData (WebNSDataExtras)
- -(BOOL)_web_isCaseInsensitiveEqualToCString:(const char *)string
- {
- ASSERT(string);
-
- const char *bytes = [self bytes];
- return strncasecmp(bytes, string, [self length]) == 0;
- }
- static const UInt8 *_findEOL(const UInt8 *bytes, CFIndex len) {
-
- // According to the HTTP specification EOL is defined as
- // a CRLF pair. Unfortunately, some servers will use LF
- // instead. Worse yet, some servers will use a combination
- // of both (e.g. <header>CRLFLF<body>), so findEOL needs
- // to be more forgiving. It will now accept CRLF, LF, or
- // CR.
- //
- // It returns NULL if EOL is not found or it will return
- // a pointer to the first terminating character.
- CFIndex i;
- for (i = 0; i < len; i++)
- {
- UInt8 c = bytes[i];
- if ('\n' == c) return bytes + i;
- if ('\r' == c)
- {
- // Check to see if spanning buffer bounds
- // (CRLF is across reads). If so, wait for
- // next read.
- if (i + 1 == len) break;
-
- return bytes + i;
- }
- }
-
- return NULL;
- }
- -(NSMutableDictionary *)_webkit_parseRFC822HeaderFields
- {
- NSMutableDictionary *headerFields = [NSMutableDictionary dictionary];
- const UInt8 *bytes = [self bytes];
- unsigned length = [self length];
- NSString *lastKey = nil;
- const UInt8 *eol;
- // Loop over lines until we're past the header, or we can't find any more end-of-lines
- while ((eol = _findEOL(bytes, length))) {
- const UInt8 *line = bytes;
- SInt32 lineLength = eol - bytes;
- // Move bytes to the character after the terminator as returned by _findEOL.
- bytes = eol + 1;
- if (('\r' == *eol) && ('\n' == *bytes)) {
- bytes++; // Safe since _findEOL won't return a spanning CRLF.
- }
- length -= (bytes - line);
- if (lineLength == 0) {
- // Blank line; we're at the end of the header
- break;
- }
- else if (*line == ' ' || *line == '\t') {
- // Continuation of the previous header
- if (!lastKey) {
- // malformed header; ignore it and continue
- continue;
- }
- else {
- // Merge the continuation of the previous header
- NSString *currentValue = [headerFields objectForKey:lastKey];
- NSString *newValue = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, line, lineLength, kCFStringEncodingISOLatin1, FALSE));
- ASSERT(currentValue);
- ASSERT(newValue);
- NSString *mergedValue = [[NSString alloc] initWithFormat:@"%@%@", currentValue, newValue];
- [headerFields setObject:(NSString *)mergedValue forKey:lastKey];
- [newValue release];
- [mergedValue release];
- // Note: currentValue is autoreleased
- }
- }
- else {
- // Brand new header
- const UInt8 *colon;
- for (colon = line; *colon != ':' && colon != eol; colon ++) {
- // empty loop
- }
- if (colon == eol) {
- // malformed header; ignore it and continue
- continue;
- }
- else {
- lastKey = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, line, colon - line, kCFStringEncodingISOLatin1, FALSE));
- [lastKey autorelease];
- NSString *value = [lastKey _web_capitalizeRFC822HeaderFieldName];
- lastKey = value;
- for (colon++; colon != eol; colon++) {
- if (*colon != ' ' && *colon != '\t') {
- break;
- }
- }
- if (colon == eol) {
- value = [[NSString alloc] initWithString:@""];
- [value autorelease];
- }
- else {
- value = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, colon, eol-colon, kCFStringEncodingISOLatin1, FALSE));
- [value autorelease];
- }
- NSString *oldValue = [headerFields objectForKey:lastKey];
- if (oldValue) {
- NSString *newValue = [[NSString alloc] initWithFormat:@"%@, %@", oldValue, value];
- value = newValue;
- [newValue autorelease];
- }
- [headerFields setObject:(NSString *)value forKey:lastKey];
- }
- }
- }
- return headerFields;
- }
- - (BOOL)_web_startsWithBlankLine
- {
- return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
- }
- - (NSInteger)_web_locationAfterFirstBlankLine
- {
- const char *bytes = (const char *)[self bytes];
- unsigned length = [self length];
-
- unsigned i;
- for (i = 0; i < length - 4; i++) {
-
- // Support for Acrobat. It sends "\n\n".
- if (bytes[i] == '\n' && bytes[i+1] == '\n') {
- return i+2;
- }
-
- // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
- if (bytes[i] == '\r' && bytes[i+1] == '\n') {
- i += 2;
- if (i == 2) {
- return i;
- } else if (bytes[i] == '\n') {
- // Support for Director. It sends "\r\n\n" (3880387).
- return i+1;
- } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
- // Support for Flash. It sends "\r\n\r\n" (3758113).
- return i+2;
- }
- }
- }
- return NSNotFound;
- }
- @end
|