diff options
-rw-r--r-- | Foundation/GTMBase64.h | 189 | ||||
-rw-r--r-- | Foundation/GTMBase64.m | 692 | ||||
-rw-r--r-- | Foundation/GTMBase64Test.m | 437 | ||||
-rw-r--r-- | Foundation/GTMHTTPFetcher.h | 22 | ||||
-rw-r--r-- | Foundation/GTMHTTPServer.h | 144 | ||||
-rw-r--r-- | Foundation/GTMHTTPServer.m | 624 | ||||
-rw-r--r-- | Foundation/GTMHTTPServerTest.m | 652 | ||||
-rw-r--r-- | Foundation/GTMNSData+Hex.h | 43 | ||||
-rw-r--r-- | Foundation/GTMNSData+Hex.m | 102 | ||||
-rw-r--r-- | Foundation/GTMNSData+HexTest.m | 58 | ||||
-rw-r--r-- | GTM.xcodeproj/project.pbxproj | 42 | ||||
-rw-r--r-- | GTMiPhone.xcodeproj/project.pbxproj | 36 | ||||
-rw-r--r-- | ReleaseNotes.txt | 13 | ||||
-rw-r--r-- | UnitTesting/GTMTestHTTPServer.h | 39 | ||||
-rw-r--r-- | UnitTesting/GTMTestHTTPServer.m | 166 | ||||
-rw-r--r-- | iPhone/GTMABAddressBook.h | 328 | ||||
-rw-r--r-- | iPhone/GTMABAddressBook.m | 901 | ||||
-rw-r--r-- | iPhone/GTMABAddressBook.strings | bin | 1428 -> 0 bytes | |||
-rw-r--r-- | iPhone/GTMABAddressBookTest.m | 608 |
19 files changed, 13 insertions, 5083 deletions
diff --git a/Foundation/GTMBase64.h b/Foundation/GTMBase64.h deleted file mode 100644 index 8a3b919..0000000 --- a/Foundation/GTMBase64.h +++ /dev/null @@ -1,189 +0,0 @@ -// -// GTMBase64.h -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - - -// WARNING: This class provides a subset of the functionality available in -// GTMStringEncoding and may go away in the future. -// Please consider using GTMStringEncoding instead. - - -#import <Foundation/Foundation.h> -#import "GTMDefines.h" - -// GTMBase64 -// -/// Helper for handling Base64 and WebSafeBase64 encodings -// -/// The webSafe methods use different character set and also the results aren't -/// always padded to a multiple of 4 characters. This is done so the resulting -/// data can be used in urls and url query arguments without needing any -/// encoding. You must use the webSafe* methods together, the data does not -/// interop with the RFC methods. -// -@interface GTMBase64 : NSObject - -// -// Standard Base64 (RFC) handling -// - -// encodeData: -// -/// Base64 encodes contents of the NSData object. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)encodeData:(NSData *)data; - -// decodeData: -// -/// Base64 decodes contents of the NSData object. -// -/// Returns: -/// A new autoreleased NSData with the decoded payload. nil for any error. -// -+(NSData *)decodeData:(NSData *)data; - -// encodeBytes:length: -// -/// Base64 encodes the data pointed at by |bytes|. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length; - -// decodeBytes:length: -// -/// Base64 decodes the data pointed at by |bytes|. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length; - -// stringByEncodingData: -// -/// Base64 encodes contents of the NSData object. -// -/// Returns: -/// A new autoreleased NSString with the encoded payload. nil for any error. -// -+(NSString *)stringByEncodingData:(NSData *)data; - -// stringByEncodingBytes:length: -// -/// Base64 encodes the data pointed at by |bytes|. -// -/// Returns: -/// A new autoreleased NSString with the encoded payload. nil for any error. -// -+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length; - -// decodeString: -// -/// Base64 decodes contents of the NSString. -// -/// Returns: -/// A new autoreleased NSData with the decoded payload. nil for any error. -// -+(NSData *)decodeString:(NSString *)string; - -// -// Modified Base64 encoding so the results can go onto urls. -// -// The changes are in the characters generated and also allows the result to -// not be padded to a multiple of 4. -// Must use the matching call to encode/decode, won't interop with the -// RFC versions. -// - -// webSafeEncodeData:padded: -// -/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES -/// then padding characters are added so the result length is a multiple of 4. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)webSafeEncodeData:(NSData *)data - padded:(BOOL)padded; - -// webSafeDecodeData: -// -/// WebSafe Base64 decodes contents of the NSData object. -// -/// Returns: -/// A new autoreleased NSData with the decoded payload. nil for any error. -// -+(NSData *)webSafeDecodeData:(NSData *)data; - -// webSafeEncodeBytes:length:padded: -// -/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES -/// then padding characters are added so the result length is a multiple of 4. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)webSafeEncodeBytes:(const void *)bytes - length:(NSUInteger)length - padded:(BOOL)padded; - -// webSafeDecodeBytes:length: -// -/// WebSafe Base64 decodes the data pointed at by |bytes|. -// -/// Returns: -/// A new autoreleased NSData with the encoded payload. nil for any error. -// -+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length; - -// stringByWebSafeEncodingData:padded: -// -/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES -/// then padding characters are added so the result length is a multiple of 4. -// -/// Returns: -/// A new autoreleased NSString with the encoded payload. nil for any error. -// -+(NSString *)stringByWebSafeEncodingData:(NSData *)data - padded:(BOOL)padded; - -// stringByWebSafeEncodingBytes:length:padded: -// -/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES -/// then padding characters are added so the result length is a multiple of 4. -// -/// Returns: -/// A new autoreleased NSString with the encoded payload. nil for any error. -// -+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes - length:(NSUInteger)length - padded:(BOOL)padded; - -// webSafeDecodeString: -// -/// WebSafe Base64 decodes contents of the NSString. -// -/// Returns: -/// A new autoreleased NSData with the decoded payload. nil for any error. -// -+(NSData *)webSafeDecodeString:(NSString *)string; - -@end diff --git a/Foundation/GTMBase64.m b/Foundation/GTMBase64.m deleted file mode 100644 index 7c4c68c..0000000 --- a/Foundation/GTMBase64.m +++ /dev/null @@ -1,692 +0,0 @@ -// -// GTMBase64.m -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMBase64.h" -#import "GTMDefines.h" - -static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -static const char kBase64PaddingChar = '='; -static const char kBase64InvalidChar = 99; - -static const char kBase64DecodeChars[] = { - // This array was generated by the following code: - // #include <sys/time.h> - // #include <stdlib.h> - // #include <string.h> - // main() - // { - // static const char Base64[] = - // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - // char *pos; - // int idx, i, j; - // printf(" "); - // for (i = 0; i < 255; i += 8) { - // for (j = i; j < i + 8; j++) { - // pos = strchr(Base64, j); - // if ((pos == NULL) || (j == 0)) - // idx = 99; - // else - // idx = pos - Base64; - // if (idx == 99) - // printf(" %2d, ", idx); - // else - // printf(" %2d/*%c*/,", idx, j); - // } - // printf("\n "); - // } - // } - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 62/*+*/, 99, 99, 99, 63/*/ */, - 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, - 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, - 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, - 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, - 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, - 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 99, - 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, - 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, - 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, - 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -static const char kWebSafeBase64DecodeChars[] = { - // This array was generated by the following code: - // #include <sys/time.h> - // #include <stdlib.h> - // #include <string.h> - // main() - // { - // static const char Base64[] = - // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - // char *pos; - // int idx, i, j; - // printf(" "); - // for (i = 0; i < 255; i += 8) { - // for (j = i; j < i + 8; j++) { - // pos = strchr(Base64, j); - // if ((pos == NULL) || (j == 0)) - // idx = 99; - // else - // idx = pos - Base64; - // if (idx == 99) - // printf(" %2d, ", idx); - // else - // printf(" %2d/*%c*/,", idx, j); - // } - // printf("\n "); - // } - // } - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 62/*-*/, 99, 99, - 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, - 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, - 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, - 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, - 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, - 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 63/*_*/, - 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, - 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, - 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, - 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - - -// Tests a character to see if it's a whitespace character. -// -// Returns: -// YES if the character is a whitespace character. -// NO if the character is not a whitespace character. -// -GTM_INLINE BOOL IsSpace(unsigned char c) { - // we use our own mapping here because we don't want anything w/ locale - // support. - static BOOL kSpaces[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0-9 - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 10-19 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29 - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 30-39 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-49 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-59 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-69 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-79 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-89 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-99 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100-109 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 110-119 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120-129 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 130-139 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140-149 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 150-159 - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-169 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 170-179 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180-189 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 190-199 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200-209 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 210-219 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220-229 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 230-239 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-249 - 0, 0, 0, 0, 0, 1, // 250-255 - }; - return kSpaces[c]; -} - -// Calculate how long the data will be once it's base64 encoded. -// -// Returns: -// The guessed encoded length for a source length -// -GTM_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen, BOOL padded) { - NSUInteger intermediate_result = 8 * srcLen + 5; - NSUInteger len = intermediate_result / 6; - if (padded) { - len = ((len + 3) / 4) * 4; - } - return len; -} - -// Tries to calculate how long the data will be once it's base64 decoded. -// Unlike the above, this is always an upperbound, since the source data -// could have spaces and might end with the padding characters on them. -// -// Returns: -// The guessed decoded length for a source length -// -GTM_INLINE NSUInteger GuessDecodedLength(NSUInteger srcLen) { - return (srcLen + 3) / 4 * 3; -} - - -@interface GTMBase64 (PrivateMethods) - -+(NSData *)baseEncode:(const void *)bytes - length:(NSUInteger)length - charset:(const char *)charset - padded:(BOOL)padded; - -+(NSData *)baseDecode:(const void *)bytes - length:(NSUInteger)length - charset:(const char*)charset - requirePadding:(BOOL)requirePadding; - -+(NSUInteger)baseEncode:(const char *)srcBytes - srcLen:(NSUInteger)srcLen - destBytes:(char *)destBytes - destLen:(NSUInteger)destLen - charset:(const char *)charset - padded:(BOOL)padded; - -+(NSUInteger)baseDecode:(const char *)srcBytes - srcLen:(NSUInteger)srcLen - destBytes:(char *)destBytes - destLen:(NSUInteger)destLen - charset:(const char *)charset - requirePadding:(BOOL)requirePadding; - -@end - - -@implementation GTMBase64 - -// -// Standard Base64 (RFC) handling -// - -+(NSData *)encodeData:(NSData *)data { - return [self baseEncode:[data bytes] - length:[data length] - charset:kBase64EncodeChars - padded:YES]; -} - -+(NSData *)decodeData:(NSData *)data { - return [self baseDecode:[data bytes] - length:[data length] - charset:kBase64DecodeChars - requirePadding:YES]; -} - -+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length { - return [self baseEncode:bytes - length:length - charset:kBase64EncodeChars - padded:YES]; -} - -+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length { - return [self baseDecode:bytes - length:length - charset:kBase64DecodeChars - requirePadding:YES]; -} - -+(NSString *)stringByEncodingData:(NSData *)data { - NSString *result = nil; - NSData *converted = [self baseEncode:[data bytes] - length:[data length] - charset:kBase64EncodeChars - padded:YES]; - if (converted) { - result = [[[NSString alloc] initWithData:converted - encoding:NSASCIIStringEncoding] autorelease]; - } - return result; -} - -+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length { - NSString *result = nil; - NSData *converted = [self baseEncode:bytes - length:length - charset:kBase64EncodeChars - padded:YES]; - if (converted) { - result = [[[NSString alloc] initWithData:converted - encoding:NSASCIIStringEncoding] autorelease]; - } - return result; -} - -+(NSData *)decodeString:(NSString *)string { - NSData *result = nil; - NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; - if (data) { - result = [self baseDecode:[data bytes] - length:[data length] - charset:kBase64DecodeChars - requirePadding:YES]; - } - return result; -} - -// -// Modified Base64 encoding so the results can go onto urls. -// -// The changes are in the characters generated and also the result isn't -// padded to a multiple of 4. -// Must use the matching call to encode/decode, won't interop with the -// RFC versions. -// - -+(NSData *)webSafeEncodeData:(NSData *)data - padded:(BOOL)padded { - return [self baseEncode:[data bytes] - length:[data length] - charset:kWebSafeBase64EncodeChars - padded:padded]; -} - -+(NSData *)webSafeDecodeData:(NSData *)data { - return [self baseDecode:[data bytes] - length:[data length] - charset:kWebSafeBase64DecodeChars - requirePadding:NO]; -} - -+(NSData *)webSafeEncodeBytes:(const void *)bytes - length:(NSUInteger)length - padded:(BOOL)padded { - return [self baseEncode:bytes - length:length - charset:kWebSafeBase64EncodeChars - padded:padded]; -} - -+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length { - return [self baseDecode:bytes - length:length - charset:kWebSafeBase64DecodeChars - requirePadding:NO]; -} - -+(NSString *)stringByWebSafeEncodingData:(NSData *)data - padded:(BOOL)padded { - NSString *result = nil; - NSData *converted = [self baseEncode:[data bytes] - length:[data length] - charset:kWebSafeBase64EncodeChars - padded:padded]; - if (converted) { - result = [[[NSString alloc] initWithData:converted - encoding:NSASCIIStringEncoding] autorelease]; - } - return result; -} - -+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes - length:(NSUInteger)length - padded:(BOOL)padded { - NSString *result = nil; - NSData *converted = [self baseEncode:bytes - length:length - charset:kWebSafeBase64EncodeChars - padded:padded]; - if (converted) { - result = [[[NSString alloc] initWithData:converted - encoding:NSASCIIStringEncoding] autorelease]; - } - return result; -} - -+(NSData *)webSafeDecodeString:(NSString *)string { - NSData *result = nil; - NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; - if (data) { - result = [self baseDecode:[data bytes] - length:[data length] - charset:kWebSafeBase64DecodeChars - requirePadding:NO]; - } - return result; -} - -@end - -@implementation GTMBase64 (PrivateMethods) - -// -// baseEncode:length:charset:padded: -// -// Does the common lifting of creating the dest NSData. it creates & sizes the -// data for the results. |charset| is the characters to use for the encoding -// of the data. |padding| controls if the encoded data should be padded to a -// multiple of 4. -// -// Returns: -// an autorelease NSData with the encoded data, nil if any error. -// -+(NSData *)baseEncode:(const void *)bytes - length:(NSUInteger)length - charset:(const char *)charset - padded:(BOOL)padded { - // how big could it be? - NSUInteger maxLength = CalcEncodedLength(length, padded); - // make space - NSMutableData *result = [NSMutableData data]; - [result setLength:maxLength]; - // do it - NSUInteger finalLength = [self baseEncode:bytes - srcLen:length - destBytes:[result mutableBytes] - destLen:[result length] - charset:charset - padded:padded]; - if (finalLength) { - _GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?"); - } else { - // shouldn't happen, this means we ran out of space - result = nil; - } - return result; -} - -// -// baseDecode:length:charset:requirePadding: -// -// Does the common lifting of creating the dest NSData. it creates & sizes the -// data for the results. |charset| is the characters to use for the decoding -// of the data. -// -// Returns: -// an autorelease NSData with the decoded data, nil if any error. -// -// -+(NSData *)baseDecode:(const void *)bytes - length:(NSUInteger)length - charset:(const char *)charset - requirePadding:(BOOL)requirePadding { - // could try to calculate what it will end up as - NSUInteger maxLength = GuessDecodedLength(length); - // make space - NSMutableData *result = [NSMutableData data]; - [result setLength:maxLength]; - // do it - NSUInteger finalLength = [self baseDecode:bytes - srcLen:length - destBytes:[result mutableBytes] - destLen:[result length] - charset:charset - requirePadding:requirePadding]; - if (finalLength) { - if (finalLength != maxLength) { - // resize down to how big it was - [result setLength:finalLength]; - } - } else { - // either an error in the args, or we ran out of space - result = nil; - } - return result; -} - -// -// baseEncode:srcLen:destBytes:destLen:charset:padded: -// -// Encodes the buffer into the larger. returns the length of the encoded -// data, or zero for an error. -// |charset| is the characters to use for the encoding -// |padded| tells if the result should be padded to a multiple of 4. -// -// Returns: -// the length of the encoded data. zero if any error. -// -+(NSUInteger)baseEncode:(const char *)srcBytes - srcLen:(NSUInteger)srcLen - destBytes:(char *)destBytes - destLen:(NSUInteger)destLen - charset:(const char *)charset - padded:(BOOL)padded { - if (!srcLen || !destLen || !srcBytes || !destBytes) { - return 0; - } - - char *curDest = destBytes; - const unsigned char *curSrc = (const unsigned char *)(srcBytes); - - // Three bytes of data encodes to four characters of cyphertext. - // So we can pump through three-byte chunks atomically. - while (srcLen > 2) { - // space? - _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong"); - curDest[0] = charset[curSrc[0] >> 2]; - curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)]; - curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)]; - curDest[3] = charset[curSrc[2] & 0x3f]; - - curDest += 4; - curSrc += 3; - srcLen -= 3; - destLen -= 4; - } - - // now deal with the tail (<=2 bytes) - switch (srcLen) { - case 0: - // Nothing left; nothing more to do. - break; - case 1: - // One byte left: this encodes to two characters, and (optionally) - // two pad characters to round out the four-character cypherblock. - _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong"); - curDest[0] = charset[curSrc[0] >> 2]; - curDest[1] = charset[(curSrc[0] & 0x03) << 4]; - curDest += 2; - if (padded) { - _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong"); - curDest[0] = kBase64PaddingChar; - curDest[1] = kBase64PaddingChar; - curDest += 2; - } - break; - case 2: - // Two bytes left: this encodes to three characters, and (optionally) - // one pad character to round out the four-character cypherblock. - _GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong"); - curDest[0] = charset[curSrc[0] >> 2]; - curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)]; - curDest[2] = charset[(curSrc[1] & 0x0f) << 2]; - curDest += 3; - if (padded) { - _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong"); - curDest[0] = kBase64PaddingChar; - curDest += 1; - } - break; - } - // return the length - return (curDest - destBytes); -} - -// -// baseDecode:srcLen:destBytes:destLen:charset:requirePadding: -// -// Decodes the buffer into the larger. returns the length of the decoded -// data, or zero for an error. -// |charset| is the character decoding buffer to use -// -// Returns: -// the length of the encoded data. zero if any error. -// -+(NSUInteger)baseDecode:(const char *)srcBytes - srcLen:(NSUInteger)srcLen - destBytes:(char *)destBytes - destLen:(NSUInteger)destLen - charset:(const char *)charset - requirePadding:(BOOL)requirePadding { - if (!srcLen || !destLen || !srcBytes || !destBytes) { - return 0; - } - - int decode; - NSUInteger destIndex = 0; - int state = 0; - char ch = 0; - while (srcLen-- && (ch = *srcBytes++) != 0) { - if (IsSpace(ch)) // Skip whitespace - continue; - - if (ch == kBase64PaddingChar) - break; - - decode = charset[(unsigned int)ch]; - if (decode == kBase64InvalidChar) - return 0; - - // Four cyphertext characters decode to three bytes. - // Therefore we can be in one of four states. - switch (state) { - case 0: - // We're at the beginning of a four-character cyphertext block. - // This sets the high six bits of the first byte of the - // plaintext block. - _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong"); - destBytes[destIndex] = decode << 2; - state = 1; - break; - case 1: - // We're one character into a four-character cyphertext block. - // This sets the low two bits of the first plaintext byte, - // and the high four bits of the second plaintext byte. - _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong"); - destBytes[destIndex] |= decode >> 4; - destBytes[destIndex+1] = (decode & 0x0f) << 4; - destIndex++; - state = 2; - break; - case 2: - // We're two characters into a four-character cyphertext block. - // This sets the low four bits of the second plaintext - // byte, and the high two bits of the third plaintext byte. - // However, if this is the end of data, and those two - // bits are zero, it could be that those two bits are - // leftovers from the encoding of data that had a length - // of two mod three. - _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong"); - destBytes[destIndex] |= decode >> 2; - destBytes[destIndex+1] = (decode & 0x03) << 6; - destIndex++; - state = 3; - break; - case 3: - // We're at the last character of a four-character cyphertext block. - // This sets the low six bits of the third plaintext byte. - _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong"); - destBytes[destIndex] |= decode; - destIndex++; - state = 0; - break; - } - } - - // We are done decoding Base-64 chars. Let's see if we ended - // on a byte boundary, and/or with erroneous trailing characters. - if (ch == kBase64PaddingChar) { // We got a pad char - if ((state == 0) || (state == 1)) { - return 0; // Invalid '=' in first or second position - } - if (srcLen == 0) { - if (state == 2) { // We run out of input but we still need another '=' - return 0; - } - // Otherwise, we are in state 3 and only need this '=' - } else { - if (state == 2) { // need another '=' - while ((ch = *srcBytes++) && (srcLen-- > 0)) { - if (!IsSpace(ch)) - break; - } - if (ch != kBase64PaddingChar) { - return 0; - } - } - // state = 1 or 2, check if all remain padding is space - while ((ch = *srcBytes++) && (srcLen-- > 0)) { - if (!IsSpace(ch)) { - return 0; - } - } - } - } else { - // We ended by seeing the end of the string. - - if (requirePadding) { - // If we require padding, then anything but state 0 is an error. - if (state != 0) { - return 0; - } - } else { - // Make sure we have no partial bytes lying around. Note that we do not - // require trailing '=', so states 2 and 3 are okay too. - if (state == 1) { - return 0; - } - } - } - - // If then next piece of output was valid and got written to it means we got a - // very carefully crafted input that appeared valid but contains some trailing - // bits past the real length, so just toss the thing. - if ((destIndex < destLen) && - (destBytes[destIndex] != 0)) { - return 0; - } - - return destIndex; -} - -@end diff --git a/Foundation/GTMBase64Test.m b/Foundation/GTMBase64Test.m deleted file mode 100644 index 084ea1e..0000000 --- a/Foundation/GTMBase64Test.m +++ /dev/null @@ -1,437 +0,0 @@ -// -// GTMBase64Test.m -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMSenTestCase.h" -#import "GTMBase64.h" -#include <stdlib.h> // for randiom/srandomdev - -static void FillWithRandom(char *data, NSUInteger len) { - char *max = data + len; - for ( ; data < max ; ++data) { - *data = random() & 0xFF; - } -} - -static BOOL NoEqualChar(NSData *data) { - const char *scan = [data bytes]; - const char *max = scan + [data length]; - for ( ; scan < max ; ++scan) { - if (*scan == '=') { - return NO; - } - } - return YES; -} - -@interface GTMBase64Test : GTMTestCase -@end - -@implementation GTMBase64Test - -- (void)setUp { - // seed random from /dev/random - srandomdev(); -} - -- (void)testBase64 { - // generate a range of sizes w/ random content - for (int x = 1 ; x < 1024 ; ++x) { - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:x]; - FillWithRandom([data mutableBytes], [data length]); - - // w/ *Bytes apis - NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]]; - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes] - length:[encoded length]]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Bytes apis"); - - // w/ *Data apis - encoded = [GTMBase64 encodeData:data]; - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - dataPrime = [GTMBase64 decodeData:encoded]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Data apis"); - - // Bytes to String and back - NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes] - length:[data length]]; - STAssertEquals(([encodedString length] % 4), (NSUInteger)0, - @"encoded size for Bytes to Strings should be a multiple of 4"); - dataPrime = [GTMBase64 decodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Bytes to Strings"); - - // Data to String and back - encodedString = [GTMBase64 stringByEncodingData:data]; - STAssertEquals(([encodedString length] % 4), (NSUInteger)0, - @"encoded size for Data to Strings should be a multiple of 4"); - dataPrime = [GTMBase64 decodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Bytes to Strings"); - } - - { - // now test all byte values - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:256]; - unsigned char *scan = (unsigned char*)[data mutableBytes]; - for (int x = 0 ; x <= 255 ; ++x) { - *scan++ = x; - } - - // w/ *Bytes apis - NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]]; - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes] - length:[encoded length]]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Bytes apis"); - - // w/ *Data apis - encoded = [GTMBase64 encodeData:data]; - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - dataPrime = [GTMBase64 decodeData:encoded]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Data apis"); - - // Bytes to String and back - NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes] - length:[data length]]; - STAssertEquals(([encodedString length] % 4), (NSUInteger)0, - @"encoded size for Bytes to Strings should be a multiple of 4"); - dataPrime = [GTMBase64 decodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Bytes to Strings"); - - // Data to String and back - encodedString = [GTMBase64 stringByEncodingData:data]; - STAssertEquals(([encodedString length] % 4), (NSUInteger)0, - @"encoded size for Data to Strings should be a multiple of 4"); - dataPrime = [GTMBase64 decodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Data to Strings"); - } - - { - // test w/ a mix of spacing characters - - // generate some data, encode it, and add spaces - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:253]; // should get some padding chars on the end - FillWithRandom([data mutableBytes], [data length]); - - NSString *encodedString = [GTMBase64 stringByEncodingData:data]; - NSMutableString *encodedAndSpaced = - [[encodedString mutableCopy] autorelease]; - - NSString *spaces[] = { @"\t", @"\n", @"\r", @" " }; - const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*); - for (int x = 0 ; x < 512 ; ++x) { - NSUInteger offset = random() % ([encodedAndSpaced length] + 1); - [encodedAndSpaced insertString:spaces[random() % numSpaces] - atIndex:offset]; - } - - // we'll need it as data for apis - NSData *encodedAsData = - [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding]; - STAssertNotNil(encodedAsData, @"failed to extract from string"); - STAssertEquals([encodedAsData length], [encodedAndSpaced length], - @"lengths for encoded string and data didn't match?"); - - // all the decode modes - NSData *dataPrime = [GTMBase64 decodeData:encodedAsData]; - STAssertEqualObjects(data, dataPrime, - @"failed Data decode w/ spaces"); - dataPrime = [GTMBase64 decodeBytes:[encodedAsData bytes] - length:[encodedAsData length]]; - STAssertEqualObjects(data, dataPrime, - @"failed Bytes decode w/ spaces"); - dataPrime = [GTMBase64 decodeString:encodedAndSpaced]; - STAssertEqualObjects(data, dataPrime, - @"failed String decode w/ spaces"); - } -} - -- (void)testWebSafeBase64 { - // loop to test w/ and w/o padding - for (int paddedLoop = 0; paddedLoop < 2 ; ++paddedLoop) { - BOOL padded = (paddedLoop == 1); - - // generate a range of sizes w/ random content - for (int x = 1 ; x < 1024 ; ++x) { - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:x]; - FillWithRandom([data mutableBytes], [data length]); - - // w/ *Bytes apis - NSData *encoded = [GTMBase64 webSafeEncodeBytes:[data bytes] - length:[data length] - padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via *Bytes apis had a base64 padding char"); - } - NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes] - length:[encoded length]]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Bytes apis"); - - // w/ *Data apis - encoded = [GTMBase64 webSafeEncodeData:data padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via *Data apis had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeData:encoded]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Data apis"); - - // Bytes to String and back - NSString *encodedString = - [GTMBase64 stringByWebSafeEncodingBytes:[data bytes] - length:[data length] - padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via Bytes to Strings had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Bytes to Strings"); - - // Data to String and back - encodedString = - [GTMBase64 stringByWebSafeEncodingData:data padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via Data to Strings had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Data to Strings"); - } - - { - // now test all byte values - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:256]; - unsigned char *scan = (unsigned char*)[data mutableBytes]; - for (int x = 0 ; x <= 255 ; ++x) { - *scan++ = x; - } - - // w/ *Bytes apis - NSData *encoded = - [GTMBase64 webSafeEncodeBytes:[data bytes] - length:[data length] - padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via *Bytes apis had a base64 padding char"); - } - NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes] - length:[encoded length]]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Bytes apis"); - - // w/ *Data apis - encoded = [GTMBase64 webSafeEncodeData:data padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via *Data apis had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeData:encoded]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Data apis"); - - // Bytes to String and back - NSString *encodedString = - [GTMBase64 stringByWebSafeEncodingBytes:[data bytes] - length:[data length] - padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Bytes apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via Bytes to Strings had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Bytes to Strings"); - - // Data to String and back - encodedString = - [GTMBase64 stringByWebSafeEncodingData:data padded:padded]; - if (padded) { - STAssertEquals(([encoded length] % 4), (NSUInteger)0, - @"encoded size via *Data apis should be a multiple of 4"); - } else { - STAssertTrue(NoEqualChar(encoded), - @"encoded via Data to Strings had a base64 padding char"); - } - dataPrime = [GTMBase64 webSafeDecodeString:encodedString]; - STAssertEqualObjects(data, dataPrime, - @"failed to round trip for Data to Strings"); - } - - { - // test w/ a mix of spacing characters - - // generate some data, encode it, and add spaces - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - [data setLength:253]; // should get some padding chars on the end - FillWithRandom([data mutableBytes], [data length]); - - NSString *encodedString = [GTMBase64 stringByWebSafeEncodingData:data - padded:padded]; - NSMutableString *encodedAndSpaced = - [[encodedString mutableCopy] autorelease]; - - NSString *spaces[] = { @"\t", @"\n", @"\r", @" " }; - const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*); - for (int x = 0 ; x < 512 ; ++x) { - NSUInteger offset = random() % ([encodedAndSpaced length] + 1); - [encodedAndSpaced insertString:spaces[random() % numSpaces] - atIndex:offset]; - } - - // we'll need it as data for apis - NSData *encodedAsData = - [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding]; - STAssertNotNil(encodedAsData, @"failed to extract from string"); - STAssertEquals([encodedAsData length], [encodedAndSpaced length], - @"lengths for encoded string and data didn't match?"); - - // all the decode modes - NSData *dataPrime = [GTMBase64 webSafeDecodeData:encodedAsData]; - STAssertEqualObjects(data, dataPrime, - @"failed Data decode w/ spaces"); - dataPrime = [GTMBase64 webSafeDecodeBytes:[encodedAsData bytes] - length:[encodedAsData length]]; - STAssertEqualObjects(data, dataPrime, - @"failed Bytes decode w/ spaces"); - dataPrime = [GTMBase64 webSafeDecodeString:encodedAndSpaced]; - STAssertEqualObjects(data, dataPrime, - @"failed String decode w/ spaces"); - } - } // paddedLoop -} - -- (void)testErrors { - const int something = 0; - NSString *nonAscString = [NSString stringWithUTF8String:"This test ©™®๒०᠐٧"]; - - STAssertNil([GTMBase64 encodeData:nil], @"it worked?"); - STAssertNil([GTMBase64 decodeData:nil], @"it worked?"); - STAssertNil([GTMBase64 encodeBytes:NULL length:10], @"it worked?"); - STAssertNil([GTMBase64 encodeBytes:&something length:0], @"it worked?"); - STAssertNil([GTMBase64 decodeBytes:NULL length:10], @"it worked?"); - STAssertNil([GTMBase64 decodeBytes:&something length:0], @"it worked?"); - STAssertNil([GTMBase64 stringByEncodingData:nil], @"it worked?"); - STAssertNil([GTMBase64 stringByEncodingBytes:NULL length:10], @"it worked?"); - STAssertNil([GTMBase64 stringByEncodingBytes:&something length:0], @"it worked?"); - STAssertNil([GTMBase64 decodeString:nil], @"it worked?"); - // test some pads at the end that aren't right - STAssertNil([GTMBase64 decodeString:@"=="], @"it worked?"); // just pads - STAssertNil([GTMBase64 decodeString:@"vw="], @"it worked?"); // missing pad (in state 2) - STAssertNil([GTMBase64 decodeString:@"vw"], @"it worked?"); // missing pad (in state 2) - STAssertNil([GTMBase64 decodeString:@"NNw"], @"it worked?"); // missing pad (in state 3) - STAssertNil([GTMBase64 decodeString:@"vw=v"], @"it worked?"); // missing pad, has something else - STAssertNil([GTMBase64 decodeString:@"v="], @"it worked?"); // missing a needed char, has pad instead - STAssertNil([GTMBase64 decodeString:@"v"], @"it worked?"); // missing a needed char - STAssertNil([GTMBase64 decodeString:@"vw== vw"], @"it worked?"); - STAssertNil([GTMBase64 decodeString:nonAscString], @"it worked?"); - STAssertNil([GTMBase64 decodeString:@"@@@not valid###"], @"it worked?"); - // carefully crafted bad input to make sure we don't overwalk - STAssertNil([GTMBase64 decodeString:@"WD=="], @"it worked?"); - - STAssertNil([GTMBase64 webSafeEncodeData:nil padded:YES], @"it worked?"); - STAssertNil([GTMBase64 webSafeDecodeData:nil], @"it worked?"); - STAssertNil([GTMBase64 webSafeEncodeBytes:NULL length:10 padded:YES], - @"it worked?"); - STAssertNil([GTMBase64 webSafeEncodeBytes:&something length:0 padded:YES], - @"it worked?"); - STAssertNil([GTMBase64 webSafeDecodeBytes:NULL length:10], @"it worked?"); - STAssertNil([GTMBase64 webSafeDecodeBytes:&something length:0], @"it worked?"); - STAssertNil([GTMBase64 stringByWebSafeEncodingData:nil padded:YES], - @"it worked?"); - STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:NULL - length:10 - padded:YES], - @"it worked?"); - STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:&something - length:0 - padded:YES], - @"it worked?"); - STAssertNil([GTMBase64 webSafeDecodeString:nil], @"it worked?"); - // test some pads at the end that aren't right - STAssertNil([GTMBase64 webSafeDecodeString:@"=="], @"it worked?"); // just pad chars - STAssertNil([GTMBase64 webSafeDecodeString:@"aw="], @"it worked?"); // missing pad - STAssertNil([GTMBase64 webSafeDecodeString:@"aw=a"], @"it worked?"); // missing pad, has something else - STAssertNil([GTMBase64 webSafeDecodeString:@"a"], @"it worked?"); // missing a needed char - STAssertNil([GTMBase64 webSafeDecodeString:@"a="], @"it worked?"); // missing a needed char, has pad instead - STAssertNil([GTMBase64 webSafeDecodeString:@"aw== a"], @"it worked?"); // missing pad - STAssertNil([GTMBase64 webSafeDecodeString:nonAscString], @"it worked?"); - STAssertNil([GTMBase64 webSafeDecodeString:@"@@@not valid###"], @"it worked?"); - // carefully crafted bad input to make sure we don't overwalk - STAssertNil([GTMBase64 webSafeDecodeString:@"WD=="], @"it worked?"); - - // make sure our local helper is working right - STAssertFalse(NoEqualChar([NSData dataWithBytes:"aa=zz" length:5]), @""); -} - -@end diff --git a/Foundation/GTMHTTPFetcher.h b/Foundation/GTMHTTPFetcher.h deleted file mode 100644 index 7377d1c..0000000 --- a/Foundation/GTMHTTPFetcher.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// GTMHTTPFetcher.h -// -// Copyright 2007-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -// This class is no more. If you want something like it's functionality, look -// at using the version in the Objective-C GData Client -// (http://code.google.com/p/gdata-objectivec-client/). It provides the same -// functionality and will continue to be maintained. diff --git a/Foundation/GTMHTTPServer.h b/Foundation/GTMHTTPServer.h deleted file mode 100644 index 3920968..0000000 --- a/Foundation/GTMHTTPServer.h +++ /dev/null @@ -1,144 +0,0 @@ -// -// GTMHTTPServer.h -// -// This is a *very* *simple* webserver that can be built into something, it is -// not meant to stand up a site, it sends all requests to its delegate for -// processing on the main thread. It does not support pipelining, etc. It's -// great for places where you need a simple webserver to unittest some code -// that hits a server. -// -// NOTE: there are several TODOs left in here as markers for things that could -// be done if one wanted to add more to this class. -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// -// Based a little on HTTPServer, part of the CocoaHTTPServer sample code -// http://developer.apple.com/samplecode/CocoaHTTPServer/index.html -// - -#import <Foundation/Foundation.h> -#import "GTMDefines.h" - -#if GTM_IPHONE_SDK -#import <CFNetwork/CFNetwork.h> -#endif // GTM_IPHONE_SDK - -// Global contants needed for errors from start - -#undef _EXTERN -#undef _INITIALIZE_AS -#ifdef GTMHTTPSERVER_DEFINE_GLOBALS -#define _EXTERN -#define _INITIALIZE_AS(x) =x -#else -#define _EXTERN GTM_EXTERN -#define _INITIALIZE_AS(x) -#endif - -_EXTERN NSString* kGTMHTTPServerErrorDomain _INITIALIZE_AS(@"com.google.mactoolbox.HTTPServerDomain"); -enum { - kGTMHTTPServerSocketCreateFailedError = -100, - kGTMHTTPServerBindFailedError = -101, - kGTMHTTPServerListenFailedError = -102, - kGTMHTTPServerHandleCreateFailedError = -103, -}; - -@class GTMHTTPRequestMessage, GTMHTTPResponseMessage; - -// ---------------------------------------------------------------------------- - -// See comment at top of file for the intened use of this class. -@interface GTMHTTPServer : NSObject { - @private - __weak id delegate_; - uint16_t port_; - BOOL reusePort_; - BOOL localhostOnly_; - NSFileHandle *listenHandle_; - NSMutableArray *connections_; -} - -// The delegate must support the httpServer:handleRequest: method in -// NSObject(GTMHTTPServerDelegateMethods) below. -- (id)initWithDelegate:(id)delegate; - -- (id)delegate; - -// Passing port zero will let one get assigned. -- (uint16_t)port; -- (void)setPort:(uint16_t)port; - -// Controls listening socket behavior: SO_REUSEADDR vs SO_REUSEPORT. -// The default is NO (SO_REUSEADDR) -- (BOOL)reusePort; -- (void)setReusePort:(BOOL)reusePort; - -// Receive connections on the localHost loopback address only or on all -// interfaces for this machine. The default is to only listen on localhost. -- (BOOL)localhostOnly; -- (void)setLocalhostOnly:(BOOL)yesno; - -// Start/Stop the web server. If there is an error starting up the server, |NO| -// is returned, and the specific startup failure can be returned in |error| (see -// above for the error domain and error codes). If the server is started, |YES| -// is returned and the server's delegate is called for any requests that come -// in. -- (BOOL)start:(NSError **)error; -- (void)stop; - -// returns the number of requests currently active in the server (i.e.-being -// read in, sent replies). -- (NSUInteger)activeRequestCount; - -@end - -@interface NSObject (GTMHTTPServerDelegateMethods) -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request; -@end - -// ---------------------------------------------------------------------------- - -// Encapsulates an http request, one of these is sent to the server's delegate -// for each request. -@interface GTMHTTPRequestMessage : NSObject { - @private - CFHTTPMessageRef message_; -} -- (NSString *)version; -- (NSURL *)URL; -- (NSString *)method; -- (NSData *)body; -- (NSDictionary *)allHeaderFieldValues; -@end - -// ---------------------------------------------------------------------------- - -// Encapsulates an http response, the server's delegate should return one for -// each request received. -@interface GTMHTTPResponseMessage : NSObject { - @private - CFHTTPMessageRef message_; -} -+ (id)responseWithString:(NSString *)plainText; -+ (id)responseWithHTMLString:(NSString *)htmlString; -+ (id)responseWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode; -+ (id)emptyResponseWithCode:(int)statusCode; -// TODO: class method for redirections? -// TODO: add helper for expire/no-cache -- (void)setValue:(NSString*)value forHeaderField:(NSString*)headerField; -@end diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m deleted file mode 100644 index 57e6077..0000000 --- a/Foundation/GTMHTTPServer.m +++ /dev/null @@ -1,624 +0,0 @@ -// -// GTMHTTPServer.m -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// -// Based a little on HTTPServer, part of the CocoaHTTPServer sample code -// http://developer.apple.com/samplecode/CocoaHTTPServer/index.html -// - -#import <netinet/in.h> -#import <sys/socket.h> -#import <unistd.h> - -#define GTMHTTPSERVER_DEFINE_GLOBALS -#import "GTMHTTPServer.h" -#import "GTMDebugSelectorValidation.h" -#import "GTMGarbageCollection.h" -#import "GTMDefines.h" -#import "GTMTypeCasting.h" - -@interface GTMHTTPServer (PrivateMethods) -- (void)acceptedConnectionNotification:(NSNotification *)notification; -- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle; -- (void)dataAvailableNotification:(NSNotification *)notification; -- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle; -- (void)closeConnection:(NSMutableDictionary *)connDict; -- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict; -- (void)sentResponse:(NSMutableDictionary *)connDict; -@end - -// keys for our connection dictionaries -static NSString *kFileHandle = @"FileHandle"; -static NSString *kRequest = @"Request"; -static NSString *kResponse = @"Response"; - -@interface GTMHTTPRequestMessage (PrivateHelpers) -- (BOOL)isHeaderComplete; -- (BOOL)appendData:(NSData *)data; -- (NSString *)headerFieldValueForKey:(NSString *)key; -- (UInt32)contentLength; -- (void)setBody:(NSData *)body; -@end - -@interface GTMHTTPResponseMessage (PrivateMethods) -- (id)initWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode; -- (NSData*)serializedData; -@end - -@implementation GTMHTTPServer - -- (id)init { - return [self initWithDelegate:nil]; -} - -- (id)initWithDelegate:(id)delegate { - self = [super init]; - if (self) { - if (!delegate) { - _GTMDevLog(@"missing delegate"); - [self release]; - return nil; - } - delegate_ = delegate; - GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(delegate_, - @selector(httpServer:handleRequest:), - // return type - @encode(GTMHTTPResponseMessage *), - // args - @encode(GTMHTTPServer *), - @encode(GTMHTTPRequestMessage *), - NULL); - localhostOnly_ = YES; - connections_ = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)dealloc { - [self stop]; - [connections_ release]; - [super dealloc]; -} - -#if GTM_SUPPORT_GC -- (void)finalize { - [self stop]; - [super finalize]; -} -#endif - -- (id)delegate { - return delegate_; -} - -- (uint16_t)port { - return port_; -} - -- (void)setPort:(uint16_t)port { - port_ = port; -} - -- (BOOL)reusePort { - return reusePort_; -} - -- (void)setReusePort:(BOOL)yesno { - reusePort_ = yesno; -} - -- (BOOL)localhostOnly { - return localhostOnly_; -} - -- (void)setLocalhostOnly:(BOOL)yesno { - localhostOnly_ = yesno; -} - -- (BOOL)start:(NSError **)error { - _GTMDevAssert(listenHandle_ == nil, - @"start called when we already have a listenHandle_"); - - if (error) *error = NULL; - - NSInteger startFailureCode = 0; - int fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd <= 0) { - // COV_NF_START - we'd need to use up *all* sockets to test this? - startFailureCode = kGTMHTTPServerSocketCreateFailedError; - goto startFailed; - // COV_NF_END - } - - // enable address reuse quicker after we are done w/ our socket - int yes = 1; - int sock_opt = reusePort_ ? SO_REUSEPORT : SO_REUSEADDR; - if (setsockopt(fd, SOL_SOCKET, sock_opt, - (void *)&yes, (socklen_t)sizeof(yes)) != 0) { - _GTMDevLog(@"failed to mark the socket as reusable"); // COV_NF_LINE - } - - // bind - struct sockaddr_in addr; - bzero(&addr, sizeof(addr)); - addr.sin_len = sizeof(addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(port_); - if (localhostOnly_) { - addr.sin_addr.s_addr = htonl(0x7F000001); - } else { - // COV_NF_START - testing this could cause a leopard firewall prompt during tests. - addr.sin_addr.s_addr = htonl(INADDR_ANY); - // COV_NF_END - } - if (bind(fd, (struct sockaddr*)(&addr), (socklen_t)sizeof(addr)) != 0) { - startFailureCode = kGTMHTTPServerBindFailedError; - goto startFailed; - } - - // collect the port back out - if (port_ == 0) { - socklen_t len = (socklen_t)sizeof(addr); - if (getsockname(fd, (struct sockaddr*)(&addr), &len) == 0) { - port_ = ntohs(addr.sin_port); - } - } - - // tell it to listen for connections - if (listen(fd, 5) != 0) { - // COV_NF_START - startFailureCode = kGTMHTTPServerListenFailedError; - goto startFailed; - // COV_NF_END - } - - // now use a filehandle to accept connections - listenHandle_ = - [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES]; - if (listenHandle_ == nil) { - // COV_NF_START - we'd need to run out of memory to test this? - startFailureCode = kGTMHTTPServerHandleCreateFailedError; - goto startFailed; - // COV_NF_END - } - - // setup notifications for connects - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(acceptedConnectionNotification:) - name:NSFileHandleConnectionAcceptedNotification - object:listenHandle_]; - [listenHandle_ acceptConnectionInBackgroundAndNotify]; - - // TODO: maybe hit the delegate incase it wants to register w/ NSNetService, - // or just know we're up and running? - - return YES; - -startFailed: - if (error) { - *error = [[[NSError alloc] initWithDomain:kGTMHTTPServerErrorDomain - code:startFailureCode - userInfo:nil] autorelease]; - } - if (fd > 0) { - close(fd); - } - return NO; -} - -- (void)stop { - if (listenHandle_) { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self - name:NSFileHandleConnectionAcceptedNotification - object:listenHandle_]; - [listenHandle_ release]; - listenHandle_ = nil; - // TODO: maybe hit the delegate in case it wants to unregister w/ - // NSNetService, or just know we've stopped running? - } - [connections_ removeAllObjects]; -} - -- (NSUInteger)activeRequestCount { - return [connections_ count]; -} - -- (NSString *)description { - NSString *result = - [NSString stringWithFormat:@"%@<%p>{ port=%d localHostOnly=%@ status=%@ }", - [self class], self, port_, (localhostOnly_ ? @"YES" : @"NO"), - (listenHandle_ != nil ? @"Started" : @"Stopped") ]; - return result; -} - - -@end - -@implementation GTMHTTPServer (PrivateMethods) - -- (void)acceptedConnectionNotification:(NSNotification *)notification { - NSDictionary *userInfo = [notification userInfo]; - NSFileHandle *newConnection = - [userInfo objectForKey:NSFileHandleNotificationFileHandleItem]; - _GTMDevAssert(newConnection != nil, - @"failed to get the connection in the notification: %@", - notification); - - // make sure we accept more... - [listenHandle_ acceptConnectionInBackgroundAndNotify]; - - // TODO: could let the delegate look at the address, before we start working - // on it. - - NSMutableDictionary *connDict = - [self connectionWithFileHandle:newConnection]; - [connections_ addObject:connDict]; -} - -- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle { - NSMutableDictionary *result = [NSMutableDictionary dictionary]; - - [result setObject:fileHandle forKey:kFileHandle]; - - GTMHTTPRequestMessage *request = - [[[GTMHTTPRequestMessage alloc] init] autorelease]; - [result setObject:request forKey:kRequest]; - - // setup for data notifications - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(dataAvailableNotification:) - name:NSFileHandleReadCompletionNotification - object:fileHandle]; - [fileHandle readInBackgroundAndNotify]; - - return result; -} - -- (void)dataAvailableNotification:(NSNotification *)notification { - NSFileHandle *connectionHandle = GTM_STATIC_CAST(NSFileHandle, - [notification object]); - NSMutableDictionary *connDict = [self lookupConnection:connectionHandle]; - if (connDict == nil) return; // we are no longer tracking this one - - NSDictionary *userInfo = [notification userInfo]; - NSData *readData = [userInfo objectForKey:NSFileHandleNotificationDataItem]; - if ([readData length] == 0) { - // remote side closed - [self closeConnection:connDict]; - return; - } - - // Use a local pool to keep memory down incase the runloop we're in doesn't - // drain until it gets a UI event. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - @try { - // Like Apple's sample, we just keep adding data until we get a full header - // and any referenced body. - - GTMHTTPRequestMessage *request = [connDict objectForKey:kRequest]; - [request appendData:readData]; - - // Is the header complete yet? - if (![request isHeaderComplete]) { - // more data... - [connectionHandle readInBackgroundAndNotify]; - } else { - - // Do we have all the body? - UInt32 contentLength = [request contentLength]; - NSData *body = [request body]; - NSUInteger bodyLength = [body length]; - if (contentLength > bodyLength) { - // need more data... - [connectionHandle readInBackgroundAndNotify]; - } else { - - if (contentLength < bodyLength) { - // We got extra (probably someone trying to pipeline on us), trim - // and let the extra data go... - NSData *newBody = [NSData dataWithBytes:[body bytes] - length:contentLength]; - [request setBody:newBody]; - _GTMDevLog(@"Got %lu extra bytes on http request, ignoring them", - (unsigned long)(bodyLength - contentLength)); - } - - GTMHTTPResponseMessage *response = nil; - @try { - // Off to the delegate - response = [delegate_ httpServer:self handleRequest:request]; - } @catch (NSException *e) { - _GTMDevLog(@"Exception trying to handle http request: %@", e); - } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't testable - - if (response) { - // We don't support connection reuse, so we add (force) the header to - // close every connection. - [response setValue:@"close" forHeaderField:@"Connection"]; - - // spawn thread to send reply (since we do a blocking send) - [connDict setObject:response forKey:kResponse]; - [NSThread detachNewThreadSelector:@selector(sendResponseOnNewThread:) - toTarget:self - withObject:connDict]; - } else { - // No response, shut it down - [self closeConnection:connDict]; - } - - } - } - } @catch (NSException *e) { // COV_NF_START - _GTMDevLog(@"exception while read data: %@", e); - // exception while dealing with the connection, close it - } // COV_NF_END - @finally { - [pool drain]; - } -} - -- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle { - NSMutableDictionary *result = nil; - NSMutableDictionary *connDict; - GTM_FOREACH_OBJECT(connDict, connections_) { - if (fileHandle == [connDict objectForKey:kFileHandle]) { - result = connDict; - break; - } - } - return result; -} - -- (void)closeConnection:(NSMutableDictionary *)connDict { - // remove the notification - NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self - name:NSFileHandleReadCompletionNotification - object:connectionHandle]; - // in a non GC world, we're fine just letting the connect get closed when - // the object is release when it comes out of connections_, but in a GC world - // it won't get cleaned up - [connectionHandle closeFile]; - - // remove it from the list - [connections_ removeObject:connDict]; -} - -- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - @try { - GTMHTTPResponseMessage *response = [connDict objectForKey:kResponse]; - NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; - NSData *serialized = [response serializedData]; - [connectionHandle writeData:serialized]; - } @catch (NSException *e) { // COV_NF_START - causing an exception here is to hard in a test - // TODO: let the delegate know about the exception (but do it on the main - // thread) - _GTMDevLog(@"exception while sending reply: %@", e); - } // COV_NF_END - - // back to the main thread to close things down - [self performSelectorOnMainThread:@selector(sentResponse:) - withObject:connDict - waitUntilDone:NO]; - - [pool release]; -} - -- (void)sentResponse:(NSMutableDictionary *)connDict { - // make sure we're still tracking this connection (in case server was stopped) - NSFileHandle *connection = [connDict objectForKey:kFileHandle]; - NSMutableDictionary *connDict2 = [self lookupConnection:connection]; - if (connDict != connDict2) return; - - // TODO: message the delegate that it was sent - - // close it down - [self closeConnection:connDict]; -} - -@end - -#pragma mark - - -@implementation GTMHTTPRequestMessage - -- (id)init { - self = [super init]; - if (self) { - message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES); - } - return self; -} - -- (void)dealloc { - if (message_) { - CFRelease(message_); - } - [super dealloc]; -} - -- (NSString *)version { - return GTMCFAutorelease(CFHTTPMessageCopyVersion(message_)); -} - -- (NSURL *)URL { - return GTMCFAutorelease(CFHTTPMessageCopyRequestURL(message_)); -} - -- (NSString *)method { - return GTMCFAutorelease(CFHTTPMessageCopyRequestMethod(message_)); -} - -- (NSData *)body { - return GTMCFAutorelease(CFHTTPMessageCopyBody(message_)); -} - -- (NSDictionary *)allHeaderFieldValues { - return GTMCFAutorelease(CFHTTPMessageCopyAllHeaderFields(message_)); -} - -- (NSString *)description { - CFStringRef desc = CFCopyDescription(message_); - NSString *result = - [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; - CFRelease(desc); - return result; -} - -@end - -@implementation GTMHTTPRequestMessage (PrivateHelpers) - -- (BOOL)isHeaderComplete { - return CFHTTPMessageIsHeaderComplete(message_) ? YES : NO; -} - -- (BOOL)appendData:(NSData *)data { - return CFHTTPMessageAppendBytes(message_, - [data bytes], [data length]) ? YES : NO; -} - -- (NSString *)headerFieldValueForKey:(NSString *)key { - CFStringRef value = NULL; - if (key) { - value = CFHTTPMessageCopyHeaderFieldValue(message_, (CFStringRef)key); - } - return GTMCFAutorelease(value); -} - -- (UInt32)contentLength { - return [[self headerFieldValueForKey:@"Content-Length"] intValue]; -} - -- (void)setBody:(NSData *)body { - if (!body) { - body = [NSData data]; // COV_NF_LINE - can only happen in we fail to make the new data object - } - CFHTTPMessageSetBody(message_, (CFDataRef)body); -} - -@end - -#pragma mark - - -@implementation GTMHTTPResponseMessage - -- (id)init { - return [self initWithBody:nil contentType:nil statusCode:0]; -} - -- (void)dealloc { - if (message_) { - CFRelease(message_); - } - [super dealloc]; -} - -+ (id)responseWithString:(NSString *)plainText { - NSData *body = [plainText dataUsingEncoding:NSUTF8StringEncoding]; - return [self responseWithBody:body - contentType:@"text/plain; charset=UTF-8" - statusCode:200]; -} - -+ (id)responseWithHTMLString:(NSString *)htmlString { - return [self responseWithBody:[htmlString dataUsingEncoding:NSUTF8StringEncoding] - contentType:@"text/html; charset=UTF-8" - statusCode:200]; -} - -+ (id)responseWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode { - return [[[[self class] alloc] initWithBody:body - contentType:contentType - statusCode:statusCode] autorelease]; -} - -+ (id)emptyResponseWithCode:(int)statusCode { - return [[[[self class] alloc] initWithBody:nil - contentType:nil - statusCode:statusCode] autorelease]; -} - -- (void)setValue:(NSString*)value forHeaderField:(NSString*)headerField { - if ([headerField length] == 0) return; - if (value == nil) { - value = @""; - } - CFHTTPMessageSetHeaderFieldValue(message_, - (CFStringRef)headerField, (CFStringRef)value); -} - -- (NSString *)description { - CFStringRef desc = CFCopyDescription(message_); - NSString *result = - [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; - CFRelease(desc); - return result; -} - -@end - -@implementation GTMHTTPResponseMessage (PrivateMethods) - -- (id)initWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode { - self = [super init]; - if (self) { - if ((statusCode < 100) || (statusCode > 599)) { - [self release]; - return nil; - } - message_ = CFHTTPMessageCreateResponse(kCFAllocatorDefault, - statusCode, NULL, - kCFHTTPVersion1_0); - if (!message_) { - // COV_NF_START - [self release]; - return nil; - // COV_NF_END - } - NSUInteger bodyLength = 0; - if (body) { - bodyLength = [body length]; - CFHTTPMessageSetBody(message_, (CFDataRef)body); - } - if ([contentType length] == 0) { - contentType = @"text/html"; - } - NSString *bodyLenStr = - [NSString stringWithFormat:@"%lu", (unsigned long)bodyLength]; - [self setValue:bodyLenStr forHeaderField:@"Content-Length"]; - [self setValue:contentType forHeaderField:@"Content-Type"]; - } - return self; -} - -- (NSData *)serializedData { - return GTMCFAutorelease(CFHTTPMessageCopySerializedMessage(message_)); -} - -@end diff --git a/Foundation/GTMHTTPServerTest.m b/Foundation/GTMHTTPServerTest.m deleted file mode 100644 index 0cafaa2..0000000 --- a/Foundation/GTMHTTPServerTest.m +++ /dev/null @@ -1,652 +0,0 @@ -// -// GTMHTTPServerTest.m -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import <netinet/in.h> -#import <sys/socket.h> -#import <unistd.h> -#import "GTMSenTestCase.h" -#import "GTMUnitTestDevLog.h" -#import "GTMHTTPServer.h" -#import "GTMRegex.h" -#import "GTMGarbageCollection.h" - -@interface GTMHTTPServerTest : GTMTestCase { - CFHTTPMessageRef fetchedMessage_; - BOOL complete_; -} -@end - -@interface GTMHTTPServerTest (PrivateMethods) -- (NSData *)fetchFromPort:(unsigned short)port - payload:(NSString *)payload - chunkSize:(NSUInteger)chunkSize; -- (NSFileHandle *)fileHandleSendingToPort:(unsigned short)port - payload:(NSString *)payload - chunkSize:(NSUInteger)chunkSize; -- (void)readData:(NSNotification *)notification; -@end - -// helper class -@interface TestServerDelegate : NSObject { - NSMutableArray *requests_; - NSMutableArray *responses_; -} -+ (id)testServerDelegate; -- (NSUInteger)requestCount; -- (GTMHTTPRequestMessage *)popRequest; -- (void)pushResponse:(GTMHTTPResponseMessage *)message; -@end - -// helper that throws while handling its request -@interface TestThrowingServerDelegate : TestServerDelegate -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request; -@end - -// The timings used for waiting for replies -const NSTimeInterval kGiveUpInterval = 5.0; -const NSTimeInterval kRunLoopInterval = 0.01; - -// the size we break writes up into to test the reading code and how long to -// wait between writes. -const NSUInteger kSendChunkSize = 12; -const NSTimeInterval kSendChunkInterval = 0.05; - -// ---------------------------------------------------------------------------- - -@implementation GTMHTTPServerTest - -- (void)testInit { - // bad delegates - [GTMUnitTestDevLog expectString:@"missing delegate"]; - STAssertNil([[GTMHTTPServer alloc] init], nil); - [GTMUnitTestDevLog expectString:@"missing delegate"]; - STAssertNil([[GTMHTTPServer alloc] initWithDelegate:nil], nil); - - TestServerDelegate *delegate = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - - // some attributes - - STAssertTrue([server delegate] == delegate, nil); - - [server setLocalhostOnly:NO]; - STAssertFalse([server localhostOnly], nil); - [server setLocalhostOnly:YES]; - STAssertTrue([server localhostOnly], nil); - - STAssertEquals([server port], (uint16_t)0, nil); - [server setPort:8080]; - STAssertEquals([server port], (uint16_t)8080, nil); - [server setPort:80]; - STAssertEquals([server port], (uint16_t)80, nil); - - // description (atleast 10 chars) - STAssertGreaterThan([[server description] length], (NSUInteger)10, nil); -} - -- (void)testStartStop { - TestServerDelegate *delegate1 = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate1, nil); - GTMHTTPServer *server1 = - [[[GTMHTTPServer alloc] initWithDelegate:delegate1] autorelease]; - STAssertNotNil(server1, nil); - NSError *error = nil; - STAssertTrue([server1 start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - STAssertGreaterThanOrEqual([server1 port], (uint16_t)1024, - @"how'd we get a reserved port?"); - - TestServerDelegate *delegate2 = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate2, nil); - GTMHTTPServer *server2 = - [[[GTMHTTPServer alloc] initWithDelegate:delegate2] autorelease]; - STAssertNotNil(server2, nil); - - // try the reserved port - [server2 setPort:666]; - error = nil; - STAssertFalse([server2 start:&error], nil); - STAssertNotNil(error, nil); - STAssertEqualObjects([error domain], kGTMHTTPServerErrorDomain, nil); - STAssertEquals([error code], (NSInteger)kGTMHTTPServerBindFailedError, - @"port should have been reserved"); - - // try the same port - [server2 setPort:[server1 port]]; - error = nil; - STAssertFalse([server2 start:&error], nil); - STAssertNotNil(error, nil); - STAssertEqualObjects([error domain], kGTMHTTPServerErrorDomain, nil); - STAssertEquals([error code], (NSInteger)kGTMHTTPServerBindFailedError, - @"port should have been in use"); - - // try a random port again so we really start (prove two can run at once) - [server2 setPort:0]; - error = nil; - STAssertTrue([server2 start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - - // shut them down - [server1 stop]; - [server2 stop]; -} - -- (void)testRestart { - TestServerDelegate *delegate = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - [server setLocalhostOnly:YES]; - [server setReusePort:YES]; - NSError *error = nil; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - uint16_t prevPort = [server port]; - STAssertGreaterThanOrEqual(prevPort, (uint16_t)1024, - @"how'd we get a reserved port?"); - // restart and make sure it works - [server stop]; - error = nil; - [server setPort:prevPort]; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); -} - -- (void)testRequests { - TestServerDelegate *delegate = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - NSError *error = nil; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - - // a request to test all the fields of a request object - - NSString *payload = - @"PUT /some/server/path HTTP/1.0\r\n" - @"Content-Length: 16\r\n" - @"Custom-Header: Custom_Value\r\n" - @"\r\n" - @"this is the body"; - NSData *reply = - [self fetchFromPort:[server port] payload:payload chunkSize:kSendChunkSize]; - STAssertNotNil(reply, nil); - - GTMHTTPRequestMessage *request = [delegate popRequest]; - STAssertEqualObjects([request version], @"HTTP/1.0", nil); - STAssertEqualObjects([[request URL] absoluteString], @"/some/server/path", nil); - STAssertEqualObjects([request method], @"PUT", nil); - STAssertEqualObjects([request body], - [@"this is the body" dataUsingEncoding:NSUTF8StringEncoding], - nil); - NSDictionary *allHeaders = [request allHeaderFieldValues]; - STAssertNotNil(allHeaders, nil); - STAssertEquals([allHeaders count], (NSUInteger)2, nil); - STAssertEqualObjects([allHeaders objectForKey:@"Content-Length"], - @"16", nil); - STAssertEqualObjects([allHeaders objectForKey:@"Custom-Header"], - @"Custom_Value", nil); - STAssertGreaterThan([[request description] length], (NSUInteger)10, nil); - - // test different request types (in simple form) - - typedef struct { - NSString *method; - NSString *url; - } TestData; - - TestData data[] = { - { @"GET", @"/foo/bar" }, - { @"HEAD", @"/foo/baz" }, - { @"POST", @"/foo" }, - { @"PUT", @"/foo/spam" }, - { @"DELETE", @"/fooby/doo" }, - { @"TRACE", @"/something.html" }, - { @"CONNECT", @"/spam" }, - { @"OPTIONS", @"/wee/doggies" }, - }; - - for (size_t i = 0; i < sizeof(data) / sizeof(TestData); i++) { - payload = [NSString stringWithFormat:@"%@ %@ HTTP/1.0\r\n\r\n", - data[i].method, data[i].url]; - STAssertNotNil(payload, nil); - reply = [self fetchFromPort:[server port] - payload:payload - chunkSize:kSendChunkSize]; - STAssertNotNil(reply, // just want a reply in this test - @"failed of method %@", data[i].method); - request = [delegate popRequest]; - STAssertEqualObjects([[request URL] absoluteString], data[i].url, - @"urls didn't match for index %d", i); - STAssertEqualObjects([request method], data[i].method, - @"methods didn't match for index %d", i); - } - - [server stop]; -} - -- (void)testResponses { - - // some quick init tests for invalid things - STAssertNil([[GTMHTTPResponseMessage alloc] init], nil); - STAssertNil([GTMHTTPResponseMessage responseWithBody:nil - contentType:nil - statusCode:99], - nil); - STAssertNil([GTMHTTPResponseMessage responseWithBody:nil - contentType:nil - statusCode:602], - nil); - - TestServerDelegate *delegate = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - NSError *error = nil; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - - // test the html helper - - GTMHTTPResponseMessage *expectedResponse = - [GTMHTTPResponseMessage responseWithHTMLString:@"Success!"]; - STAssertNotNil(expectedResponse, nil); - STAssertGreaterThan([[expectedResponse description] length], - (NSUInteger)0, nil); - [delegate pushResponse:expectedResponse]; - NSData *responseData = [self fetchFromPort:[server port] - payload:@"GET /foo HTTP/1.0\r\n\r\n" - chunkSize:kSendChunkSize]; - STAssertNotNil(responseData, nil); - NSString *responseString = - [[[NSString alloc] initWithData:responseData - encoding:NSUTF8StringEncoding] autorelease]; - STAssertNotNil(responseString, nil); - STAssertTrue([responseString hasPrefix:@"HTTP/1.0 200 OK"], - @"String: %@", responseString); - STAssertTrue([responseString hasSuffix:@"Success!"], - @"String: %@ should end w/ our data", responseString); - STAssertNotEquals([responseString rangeOfString:@"Content-Length: 8"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/html; charset=UTF-8"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - - // test the plain code response - - expectedResponse = [GTMHTTPResponseMessage emptyResponseWithCode:299]; - STAssertNotNil(expectedResponse, nil); - STAssertGreaterThan([[expectedResponse description] length], - (NSUInteger)0, nil); - [delegate pushResponse:expectedResponse]; - responseData = [self fetchFromPort:[server port] - payload:@"GET /foo HTTP/1.0\r\n\r\n" - chunkSize:kSendChunkSize]; - STAssertNotNil(responseData, nil); - responseString = - [[[NSString alloc] initWithData:responseData - encoding:NSUTF8StringEncoding] autorelease]; - STAssertNotNil(responseString, nil); - STAssertTrue([responseString hasPrefix:@"HTTP/1.0 299 "], - @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Content-Length: 0"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/html"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - - // test the general api w/ extra header add - - expectedResponse = - [GTMHTTPResponseMessage responseWithBody:[@"FOO" dataUsingEncoding:NSUTF8StringEncoding] - contentType:@"some/type" - statusCode:298]; - STAssertNotNil(expectedResponse, nil); - STAssertGreaterThan([[expectedResponse description] length], - (NSUInteger)0, nil); - [expectedResponse setValue:@"Custom_Value" - forHeaderField:@"Custom-Header"]; - [expectedResponse setValue:nil - forHeaderField:@"Custom-Header2"]; - [delegate pushResponse:expectedResponse]; - responseData = [self fetchFromPort:[server port] - payload:@"GET /foo HTTP/1.0\r\n\r\n" - chunkSize:kSendChunkSize]; - STAssertNotNil(responseData, nil); - responseString = - [[[NSString alloc] initWithData:responseData - encoding:NSUTF8StringEncoding] autorelease]; - STAssertNotNil(responseString, nil); - STAssertTrue([responseString hasPrefix:@"HTTP/1.0 298"], - @"String: %@", responseString); - STAssertTrue([responseString hasSuffix:@"FOO"], @"should end w/ our data"); - STAssertNotEquals([responseString rangeOfString:@"Content-Length: 3"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Content-Type: some/type"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Custom-Header: Custom_Value"].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - STAssertNotEquals([responseString rangeOfString:@"Custom-Header2: "].location, - (NSUInteger)NSNotFound, @"String: %@", responseString); - - // test plain text response - - expectedResponse = [GTMHTTPResponseMessage responseWithString:@"BAR"]; - STAssertNotNil(expectedResponse, nil); - STAssertGreaterThan([[expectedResponse description] length], - (NSUInteger)3, nil); - [delegate pushResponse:expectedResponse]; - responseData = [self fetchFromPort:[server port] - payload:@"GET /bar HTTP/1.0\r\n\r\n" - chunkSize:kSendChunkSize]; - STAssertNotNil(responseData, nil); - responseString = - [[[NSString alloc] initWithData:responseData - encoding:NSUTF8StringEncoding] autorelease]; - STAssertNotNil(responseString, nil); - STAssertTrue([responseString hasPrefix:@"HTTP/1.0 200 "], nil); - STAssertTrue([responseString hasSuffix:@"BAR"], @"should end w/ our data"); - STAssertNotEquals([responseString rangeOfString:@"Content-Length: 3"].location, - (NSUInteger)NSNotFound, nil); - STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/plain"].location, - (NSUInteger)NSNotFound, nil); - - [server stop]; -} - -- (void)testRequstEdgeCases { - // test all the odd things about requests - - TestServerDelegate *delegate = [TestServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - NSError *error = nil; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - - // extra data (ie-pipelining) - - NSString *payload = - @"GET /some/server/path HTTP/1.0\r\n" - @"\r\n" - @"GET /some/server/path/too HTTP/1.0\r\n" - @"\r\n"; - // don't chunk this, we want to make sure both requests get to our server - [GTMUnitTestDevLog expectString:@"Got 38 extra bytes on http request, " - "ignoring them"]; - NSData *reply = - [self fetchFromPort:[server port] payload:payload chunkSize:0]; - STAssertNotNil(reply, nil); - STAssertEquals([delegate requestCount], (NSUInteger)1, nil); - - // close w/o full request - { - // local pool so we can force our handle to close - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - NSFileHandle *handle = - [self fileHandleSendingToPort:[server port] - payload:@"GET /some/server/path HTTP/" - chunkSize:kSendChunkSize]; - STAssertNotNil(handle, nil); - // spin the run loop so reads the start of the request - NSDate* loopIntervalDate = - [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate]; - // make sure we see the request at this point - STAssertEquals([server activeRequestCount], (NSUInteger)1, - @"should have started the request by now"); - // force the connection closed and drop the pool to get all the cleanup to - // happen. - [handle closeFile]; - [localPool drain]; - // spin the run loop so it should see the close - loopIntervalDate = [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate]; - // make sure we didn't get a request (1 is from test before) and make sure - // we don't have some in flight. - STAssertEquals([delegate requestCount], (NSUInteger)1, - @"shouldn't have gotten another request"); - STAssertEquals([server activeRequestCount], (NSUInteger)0, - @"should have cleaned up the pending connection"); - } - -} - -- (void)testExceptionDuringRequest { - - TestServerDelegate *delegate = [TestThrowingServerDelegate testServerDelegate]; - STAssertNotNil(delegate, nil); - GTMHTTPServer *server = - [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease]; - STAssertNotNil(server, nil); - NSError *error = nil; - STAssertTrue([server start:&error], @"failed to start (error=%@)", error); - STAssertNil(error, @"error: %@", error); - [GTMUnitTestDevLog expectString:@"Exception trying to handle http request: " - "To test our handling"]; - NSData *responseData = [self fetchFromPort:[server port] - payload:@"GET /foo HTTP/1.0\r\n\r\n" - chunkSize:kSendChunkSize]; - STAssertNil(responseData, nil); - STAssertEquals([delegate requestCount], (NSUInteger)1, nil); - STAssertEquals([server activeRequestCount], (NSUInteger)0, nil); -} - -@end - -// ---------------------------------------------------------------------------- - -@implementation GTMHTTPServerTest (PrivateMethods) - -- (NSData *)fetchFromPort:(unsigned short)port - payload:(NSString *)payload - chunkSize:(NSUInteger)chunkSize { - STAssertNULL(fetchedMessage_, nil); - fetchedMessage_ = CFHTTPMessageCreateEmpty(NULL, false); - complete_ = NO; - - NSFileHandle *handle = [self fileHandleSendingToPort:port - payload:payload - chunkSize:chunkSize]; - - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - // Do not use NSFileHandleReadToEndOfFileCompletionNotification on Snow - // Leopard as we can run into a deadlock when we call close on the - // file handle while it is still waiting for data. - [center addObserver:self - selector:@selector(readData:) - name:NSFileHandleReadCompletionNotification - object:handle]; - [handle readInBackgroundAndNotify]; - - // wait for our reply - NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval]; - while (!complete_ && [giveUpDate timeIntervalSinceNow] > 0) { - NSDate* loopIntervalDate = - [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate]; - } - - [center removeObserver:self - name:NSFileHandleReadCompletionNotification - object:handle]; - NSData *data = nil; - if (complete_) { - data = GTMCFAutorelease(CFHTTPMessageCopySerializedMessage(fetchedMessage_)); - } - CFRelease(fetchedMessage_); - fetchedMessage_ = NULL; - complete_ = NO; - return data; -} - -- (NSFileHandle *)fileHandleSendingToPort:(unsigned short)port - payload:(NSString *)payload - chunkSize:(NSUInteger)chunkSize { - int fd = socket(AF_INET, SOCK_STREAM, 0); - STAssertGreaterThan(fd, 0, @"failed to create socket"); - - struct sockaddr_in addr; - bzero(&addr, sizeof(addr)); - addr.sin_len = sizeof(addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(0x7F000001); - int connectResult = - connect(fd, (struct sockaddr*)(&addr), (socklen_t)sizeof(addr)); - STAssertEquals(connectResult, 0, nil); - - NSFileHandle *handle = - [[[NSFileHandle alloc] initWithFileDescriptor:fd - closeOnDealloc:YES] autorelease]; - STAssertNotNil(handle, nil); - - NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding]; - - // we can send in one block or in chunked mode - if (chunkSize > 0) { - // we don't write the data in one large block, instead of write it out - // in bits to help test the data collection code. - NSUInteger length = [payloadData length]; - for (NSUInteger x = 0 ; x < length ; x += chunkSize) { - NSUInteger dataChunkSize = length - x; - if (dataChunkSize > chunkSize) { - dataChunkSize = chunkSize; - } - NSData *dataChunk - = [payloadData subdataWithRange:NSMakeRange(x, dataChunkSize)]; - [handle writeData:dataChunk]; - // delay after all but the last chunk to give it time to be read. - if ((x + chunkSize) < length) { - NSDate* loopIntervalDate = - [NSDate dateWithTimeIntervalSinceNow:kSendChunkInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate]; - } - } - } else { - [handle writeData:payloadData]; - } - - return handle; -} - -- (void)readData:(NSNotification *)notification { - NSDictionary *userInfo = [notification userInfo]; - NSData *readData = [userInfo objectForKey:NSFileHandleNotificationDataItem]; - STAssertNotNil(readData, nil); - STAssertNotNULL(fetchedMessage_, nil); - STAssertTrue(CFHTTPMessageAppendBytes(fetchedMessage_, - [readData bytes], - [readData length]), @"Data %@", - [[[NSString alloc] initWithData:readData - encoding:NSUTF8StringEncoding] - autorelease]); - if (CFHTTPMessageIsHeaderComplete(fetchedMessage_)) { - NSString *lengthString - = GTMCFAutorelease(CFHTTPMessageCopyHeaderFieldValue(fetchedMessage_, - CFSTR("Content-Length"))); - if (lengthString) { - NSUInteger length = [lengthString intValue]; - NSData *messageData = GTMCFAutorelease(CFHTTPMessageCopyBody(fetchedMessage_)); - if ([messageData length] >= length) { - complete_ = YES; - } - } - } -} - -@end - -// ---------------------------------------------------------------------------- - -@implementation TestServerDelegate - -- (id)init { - self = [super init]; - if (self) { - requests_ = [[NSMutableArray alloc] init]; - responses_ = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)dealloc { - [requests_ release]; - [responses_ release]; - [super dealloc]; -} - -+ (id)testServerDelegate { - return [[[[self class] alloc] init] autorelease]; -} - -- (NSUInteger)requestCount { - return [requests_ count]; -} - -- (GTMHTTPRequestMessage *)popRequest { - GTMHTTPRequestMessage *result = [[[requests_ lastObject] retain] autorelease]; - [requests_ removeLastObject]; - return result; -} - -- (void)pushResponse:(GTMHTTPResponseMessage *)message { - [responses_ addObject:message]; -} - -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request { - [requests_ addObject:request]; - - GTMHTTPResponseMessage *result = nil; - if ([responses_ count] > 0) { - result = [[[responses_ lastObject] retain] autorelease]; - [responses_ removeLastObject]; - } else { - result = [GTMHTTPResponseMessage responseWithHTMLString:@"success"]; - } - return result; -} - -@end - -// ---------------------------------------------------------------------------- - -@implementation TestThrowingServerDelegate - -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request { - // let the base do its normal work for counts, etc. - [super httpServer:server handleRequest:request]; - NSException *exception = - [NSException exceptionWithName:@"InternalTestingException" - reason:@"To test our handling" - userInfo:nil]; - @throw exception; -} - -@end diff --git a/Foundation/GTMNSData+Hex.h b/Foundation/GTMNSData+Hex.h deleted file mode 100644 index ed21f17..0000000 --- a/Foundation/GTMNSData+Hex.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// GTMNSData+Hex.h -// -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - - -// WARNING: This class provides a subset of the functionality available in -// GTMStringEncoding and may go away in the future. -// Please consider using GTMStringEncoding instead. - - -#import <Foundation/Foundation.h> - -/// Helpers for dealing w/ hex encoded strings. -@interface NSData (GTMHexAdditions) - -/// Return an autoreleased NSData w/ the result of decoding |hexString| to -/// binary data. -/// -/// Will return |nil| if |hexString| contains any non-hex characters (i.e. -/// 0-9, a-f, A-F) or if the length of |hexString| is not cleanly divisible by -/// two. -/// Leading 0x prefix is not supported and will result in a |nil| return value. -+ (NSData *)gtm_dataWithHexString:(NSString *)hexString; - -/// Return an autoreleased NSString w/ the result of encoding the NSData bytes -/// as hex. No leading 0x prefix is included. -- (NSString *)gtm_hexString; - -@end diff --git a/Foundation/GTMNSData+Hex.m b/Foundation/GTMNSData+Hex.m deleted file mode 100644 index c7e740d..0000000 --- a/Foundation/GTMNSData+Hex.m +++ /dev/null @@ -1,102 +0,0 @@ -// -// GTMNSData+Hex.m -// -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMNSData+Hex.h" -#import "GTMDefines.h" - -@implementation NSData (GTMHexAdditions) - -+ (NSData *)gtm_dataWithHexString:(NSString *)hexString { - NSData *hexData = [hexString dataUsingEncoding:NSASCIIStringEncoding]; - const char *hexBuf = [hexData bytes]; - NSUInteger hexLen = [hexData length]; - - // This indicates an error converting to ASCII. - if (hexString && !hexData) { - return nil; - } - - if ((hexLen % 2) != 0) { - return nil; - } - - NSMutableData *binaryData = [NSMutableData dataWithLength:(hexLen / 2)]; - unsigned char *binaryPtr = [binaryData mutableBytes]; - unsigned char value = 0; - for (NSUInteger i = 0; i < hexLen; i++) { - char c = hexBuf[i]; - - if (!isxdigit(c)) { - return nil; - } - - if (isdigit(c)) { - value += c - '0'; - } else if (islower(c)) { - value += 10 + c - 'a'; - } else { - value += 10 + c - 'A'; - } - - if (i & 1) { - *binaryPtr++ = value; - value = 0; - } else { - value <<= 4; - } - } - - return [NSData dataWithData:binaryData]; -} - -- (NSString *)gtm_hexString { - static const char kHexTable[] = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; - - const unsigned char *binaryPtr = [self bytes]; - NSUInteger binaryLen = [self length]; - - NSMutableData *hexData = [NSMutableData dataWithLength:(2 * binaryLen)]; - char *hexPtr = [hexData mutableBytes]; - - for (NSUInteger i = 0; i < binaryLen; i++) { - *hexPtr++ = kHexTable[(*binaryPtr)*2]; - *hexPtr++ = kHexTable[(*binaryPtr)*2 + 1]; - ++binaryPtr; - } - - return [[[NSString alloc] initWithData:hexData - encoding:NSASCIIStringEncoding] autorelease]; -} - -@end diff --git a/Foundation/GTMNSData+HexTest.m b/Foundation/GTMNSData+HexTest.m deleted file mode 100644 index 6bc1608..0000000 --- a/Foundation/GTMNSData+HexTest.m +++ /dev/null @@ -1,58 +0,0 @@ -// -// GTMNSData+HexTest.m -// -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMSenTestCase.h" -#import "GTMNSData+Hex.h" - -@interface GTMNSData_HexTest : GTMTestCase -@end - -@implementation GTMNSData_HexTest - -- (void)testNSDataHexAdditions { - NSString *testString = @"1c2f0032f40123456789abcdef"; - char testBytes[] = { 0x1c, 0x2f, 0x00, 0x32, 0xf4, 0x01, 0x23, - 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; - NSData *testData = [NSData dataWithBytes:testBytes length:sizeof(testBytes)]; - - STAssertTrue([[testData gtm_hexString] isEqual:testString], - @"gtm_hexString doesn't encode as expected"); - - STAssertEqualStrings([[NSData data] gtm_hexString], @"", - @"gtm_hexString empty data should return empty string"); - - STAssertTrue([[NSData gtm_dataWithHexString:testString] isEqual:testData], - @"gtm_dataWithHexString: doesn't decode as expected"); - - STAssertNil([NSData gtm_dataWithHexString:@"1c2f003"], - @"gtm_dataWithHexString: parsed hex from an odd size string"); - - STAssertNil([NSData gtm_dataWithHexString:@"1c2f00ft"], - @"gtm_dataWithHexString: parsed hex from a non hex string"); - - STAssertNil([NSData gtm_dataWithHexString:@"abcdéf"], - @"gtm_dataWithHexString: parsed a non-ASCII character"); - - STAssertNotNil([NSData gtm_dataWithHexString:@""], - @"gtm_dataWithHexString: empty input resulted in nil output"); - - STAssertNotNil([NSData gtm_dataWithHexString:nil], - @"gtm_dataWithHexString: nil input resulted in nil output"); -} - -@end diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj index 4f471d5..4f77c6c 100644 --- a/GTM.xcodeproj/project.pbxproj +++ b/GTM.xcodeproj/project.pbxproj @@ -40,11 +40,8 @@ 0B1B9B8710FECD870084EE4B /* GTMStringEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B1B9B8410FECD870084EE4B /* GTMStringEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0B1B9B8810FECD870084EE4B /* GTMStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B1B9B8510FECD870084EE4B /* GTMStringEncoding.m */; }; 0B1B9B8A10FECDA00084EE4B /* GTMStringEncodingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B1B9B8610FECD870084EE4B /* GTMStringEncodingTest.m */; }; - 0BFAD4C5104D06EF002BEB27 /* GTMNSData+Hex.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFAD4BF104D06EF002BEB27 /* GTMNSData+Hex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0BFAD4C6104D06EF002BEB27 /* GTMNSData+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAD4C0104D06EF002BEB27 /* GTMNSData+Hex.m */; }; 0BFAD4C8104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFAD4C2104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0BFAD4C9104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAD4C3104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.m */; }; - 0BFAD4CB104D06FE002BEB27 /* GTMNSData+HexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAD4C1104D06EF002BEB27 /* GTMNSData+HexTest.m */; }; 0BFAD4CC104D06FE002BEB27 /* GTMNSDictionary+CaseInsensitiveTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFAD4C4104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitiveTest.m */; }; 1012DF560F4252BD004794DB /* GTMAbstractDOListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 1012DF540F4252BD004794DB /* GTMAbstractDOListener.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1012DF570F4252BD004794DB /* GTMAbstractDOListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 1012DF550F4252BD004794DB /* GTMAbstractDOListener.m */; }; @@ -298,9 +295,6 @@ F41A6F820E02EC3600788A6C /* GTMSignalHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = F41A6F7F0E02EC3600788A6C /* GTMSignalHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; F41A6F830E02EC3600788A6C /* GTMSignalHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F41A6F800E02EC3600788A6C /* GTMSignalHandler.m */; }; F41A6F850E02EC4D00788A6C /* GTMSignalHandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F41A6F810E02EC3600788A6C /* GTMSignalHandlerTest.m */; }; - F41D258B0DBD21A300774EEB /* GTMBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = F41D25880DBD21A300774EEB /* GTMBase64.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = F41D25890DBD21A300774EEB /* GTMBase64.m */; }; - F41D258F0DBD21B900774EEB /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F41D258A0DBD21A300774EEB /* GTMBase64Test.m */; }; F424F7010D9AA02B000B87EF /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */; }; F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1A16050D90344B00CA1E8E /* GTMDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; F42597480E23AA57003BEA3E /* GTMNSString+Replace.h in Headers */ = {isa = PBXBuildFile; fileRef = F42597450E23AA57003BEA3E /* GTMNSString+Replace.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -393,10 +387,6 @@ F4A420F00EDDF8E000397A11 /* GTMHotKeyTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = F4A420ED0EDDF8E000397A11 /* GTMHotKeyTextField.m */; }; F4A486ED1097EA0A00513483 /* GTMUnitTestingView.10.6.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F4A486EC1097EA0A00513483 /* GTMUnitTestingView.10.6.tiff */; }; F4AA2CB2109B37650025C956 /* GTMUILocalizerAndLayoutTweakerTest3-4.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F4AA2CB1109B37650025C956 /* GTMUILocalizerAndLayoutTweakerTest3-4.tiff */; }; - F4BC1C880DDDD45D00108B7D /* GTMHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */; }; - F4BC1E8D0DE1FC4A00108B7D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */; }; - F4BC22D10DE4C39000108B7D /* GTMTestHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */; }; F4C0B9C3108E27EE002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4C0B9C2108E27EE002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5.xib */; }; F4C0B9DA108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-0.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F4C0B9D7108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-0.tiff */; }; F4C0B9DB108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-1.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F4C0B9D8108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-1.tiff */; }; @@ -527,9 +517,6 @@ 0B1B9B8410FECD870084EE4B /* GTMStringEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMStringEncoding.h; path = Foundation/GTMStringEncoding.h; sourceTree = SOURCE_ROOT; }; 0B1B9B8510FECD870084EE4B /* GTMStringEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMStringEncoding.m; path = Foundation/GTMStringEncoding.m; sourceTree = SOURCE_ROOT; }; 0B1B9B8610FECD870084EE4B /* GTMStringEncodingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMStringEncodingTest.m; path = Foundation/GTMStringEncodingTest.m; sourceTree = SOURCE_ROOT; }; - 0BFAD4BF104D06EF002BEB27 /* GTMNSData+Hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSData+Hex.h"; sourceTree = "<group>"; }; - 0BFAD4C0104D06EF002BEB27 /* GTMNSData+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+Hex.m"; sourceTree = "<group>"; }; - 0BFAD4C1104D06EF002BEB27 /* GTMNSData+HexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+HexTest.m"; sourceTree = "<group>"; }; 0BFAD4C2104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSDictionary+CaseInsensitive.h"; sourceTree = "<group>"; }; 0BFAD4C3104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+CaseInsensitive.m"; sourceTree = "<group>"; }; 0BFAD4C4104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitiveTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+CaseInsensitiveTest.m"; sourceTree = "<group>"; }; @@ -786,9 +773,6 @@ F41A6F7F0E02EC3600788A6C /* GTMSignalHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSignalHandler.h; sourceTree = "<group>"; }; F41A6F800E02EC3600788A6C /* GTMSignalHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSignalHandler.m; sourceTree = "<group>"; }; F41A6F810E02EC3600788A6C /* GTMSignalHandlerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSignalHandlerTest.m; sourceTree = "<group>"; }; - F41D25880DBD21A300774EEB /* GTMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMBase64.h; sourceTree = "<group>"; }; - F41D25890DBD21A300774EEB /* GTMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64.m; sourceTree = "<group>"; }; - F41D258A0DBD21A300774EEB /* GTMBase64Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64Test.m; sourceTree = "<group>"; }; F42597450E23AA57003BEA3E /* GTMNSString+Replace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Replace.h"; sourceTree = "<group>"; }; F42597460E23AA57003BEA3E /* GTMNSString+Replace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Replace.m"; sourceTree = "<group>"; }; F42597470E23AA57003BEA3E /* GTMNSString+ReplaceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+ReplaceTest.m"; sourceTree = "<group>"; }; @@ -898,11 +882,6 @@ F4A420EE0EDDF8E000397A11 /* GTMHotKeyTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHotKeyTextFieldTest.m; sourceTree = "<group>"; }; F4A486EC1097EA0A00513483 /* GTMUnitTestingView.10.6.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingView.10.6.tiff; sourceTree = "<group>"; }; F4AA2CB1109B37650025C956 /* GTMUILocalizerAndLayoutTweakerTest3-4.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMUILocalizerAndLayoutTweakerTest3-4.tiff"; sourceTree = "<group>"; }; - F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = "<group>"; }; - F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = "<group>"; }; - F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServerTest.m; sourceTree = "<group>"; }; - F4BC22CF0DE4C39000108B7D /* GTMTestHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTestHTTPServer.h; sourceTree = "<group>"; }; - F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTestHTTPServer.m; sourceTree = "<group>"; }; F4C0B9C2108E27EE002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GTMUILocalizerAndLayoutTweakerTest5.xib; sourceTree = "<group>"; }; F4C0B9D7108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-0.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMUILocalizerAndLayoutTweakerTest5-0.tiff"; sourceTree = "<group>"; }; F4C0B9D8108E3142002FC8E4 /* GTMUILocalizerAndLayoutTweakerTest5-1.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMUILocalizerAndLayoutTweakerTest5-1.tiff"; sourceTree = "<group>"; }; @@ -1382,9 +1361,6 @@ 0B1B9B8410FECD870084EE4B /* GTMStringEncoding.h */, 0B1B9B8510FECD870084EE4B /* GTMStringEncoding.m */, 0B1B9B8610FECD870084EE4B /* GTMStringEncodingTest.m */, - 0BFAD4BF104D06EF002BEB27 /* GTMNSData+Hex.h */, - 0BFAD4C0104D06EF002BEB27 /* GTMNSData+Hex.m */, - 0BFAD4C1104D06EF002BEB27 /* GTMNSData+HexTest.m */, 0BFAD4C2104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.h */, 0BFAD4C3104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.m */, 0BFAD4C4104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitiveTest.m */, @@ -1404,9 +1380,6 @@ 1012DF540F4252BD004794DB /* GTMAbstractDOListener.h */, 1012DF550F4252BD004794DB /* GTMAbstractDOListener.m */, 1012DF590F425525004794DB /* GTMAbstractDOListenerTest.m */, - F41D25880DBD21A300774EEB /* GTMBase64.h */, - F41D25890DBD21A300774EEB /* GTMBase64.m */, - F41D258A0DBD21A300774EEB /* GTMBase64Test.m */, 8B1B49160E5F8E2100A08972 /* GTMExceptionalInlines.h */, 8B1B49170E5F8E2100A08972 /* GTMExceptionalInlines.m */, 8B1B491B0E5F904C00A08972 /* GTMExceptionalInlinesTest.m */, @@ -1423,9 +1396,6 @@ F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */, F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */, F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */, - F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */, - F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */, - F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */, F41711320ECDFBD500B9B276 /* GTMLightweightProxy.h */, F41711330ECDFBD500B9B276 /* GTMLightweightProxy.m */, F41711340ECDFBD500B9B276 /* GTMLightweightProxyTest.m */, @@ -1544,8 +1514,6 @@ F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */, F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */, 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */, - F4BC22CF0DE4C39000108B7D /* GTMTestHTTPServer.h */, - F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */, 8B3590150E8190FA0041E21C /* GTMTestTimer.h */, 8B35901A0E8191750041E21C /* GTMTestTimerTest.m */, 8B7DCBF00DFF1A610017E983 /* GTMUnitTestDevLog.h */, @@ -1611,7 +1579,6 @@ buildActionMask = 2147483647; files = ( 0B1B9B8710FECD870084EE4B /* GTMStringEncoding.h in Headers */, - 0BFAD4C5104D06EF002BEB27 /* GTMNSData+Hex.h in Headers */, 0BFAD4C8104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.h in Headers */, F93207DE0F4B82DB005F37EA /* GTMSQLite.h in Headers */, F42E09490D199BBF00D5DDE0 /* GTMDelegatingTableColumn.h in Headers */, @@ -1637,11 +1604,9 @@ F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */, F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */, 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */, - F41D258B0DBD21A300774EEB /* GTMBase64.h in Headers */, F431221D0DD4E3B800F45252 /* GTMStackTrace.h in Headers */, 33C372A60DD8A88500E97817 /* GTMNSString+URLArguments.h in Headers */, 33C374380DD8D44800E97817 /* GTMNSDictionary+URLArguments.h in Headers */, - F4BC1C880DDDD45D00108B7D /* GTMHTTPServer.h in Headers */, 8BE281B00DEC7E930035B3F8 /* GTMNSAppleScript+Handler.h in Headers */, 8BE281B10DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Handler.h in Headers */, 8BE281B20DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Foundation.h in Headers */, @@ -2208,12 +2173,9 @@ 8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */, 8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */, 8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */, - F41D258F0DBD21B900774EEB /* GTMBase64Test.m in Sources */, F431221F0DD4E3C900F45252 /* GTMStackTraceTest.m in Sources */, 33C372B40DD8A93000E97817 /* GTMNSString+URLArgumentsTest.m in Sources */, 33C3745F0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m in Sources */, - F4BC1E8D0DE1FC4A00108B7D /* GTMHTTPServerTest.m in Sources */, - F4BC22D10DE4C39000108B7D /* GTMTestHTTPServer.m in Sources */, 8B3344210DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m in Sources */, 8B3344230DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m in Sources */, 8B3344250DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m in Sources */, @@ -2241,7 +2203,6 @@ 10998F8B0F4B5F1B007F179D /* GTMTransientRootProxyTest.m in Sources */, 108930850F4CCB380018D4A0 /* GTMTransientRootPortProxyTest.m in Sources */, 8BD35B940FB22986009058F5 /* GTMNSScanner+JSONTest.m in Sources */, - 0BFAD4CB104D06FE002BEB27 /* GTMNSData+HexTest.m in Sources */, 0BFAD4CC104D06FE002BEB27 /* GTMNSDictionary+CaseInsensitiveTest.m in Sources */, 8B3080151056B917006C4C7A /* GTMNSNumber+64BitTest.m in Sources */, 0B1B9B8A10FECDA00084EE4B /* GTMStringEncodingTest.m in Sources */, @@ -2276,10 +2237,8 @@ F47A79890D746EE9002302AB /* GTMScriptRunner.m in Sources */, F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */, 8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */, - F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */, 33C372A70DD8A88500E97817 /* GTMNSString+URLArguments.m in Sources */, 33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */, - F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */, 8B7DCB9B0DFF0E850017E983 /* GTMFourCharCode.m in Sources */, 8B7DCBBD0DFF0F5D0017E983 /* GTMMethodCheck.m in Sources */, 8B7DCBD20DFF16070017E983 /* GTMNSAppleScript+Handler.m in Sources */, @@ -2318,7 +2277,6 @@ 8207B89C0FEA7AA1008A527B /* GTMWindowSheetController.m in Sources */, F43C7A581021FAA300ABF03C /* GTMUILocalizerAndLayoutTweaker.m in Sources */, 7F97DB33104EBCA3004DDDEE /* GTMFadeTruncatingTextFieldCell.m in Sources */, - 0BFAD4C6104D06EF002BEB27 /* GTMNSData+Hex.m in Sources */, 0BFAD4C9104D06EF002BEB27 /* GTMNSDictionary+CaseInsensitive.m in Sources */, 7FF768E51051B17E00D34F4B /* GTMNSImage+SearchCache.m in Sources */, 8B307FF81056B773006C4C7A /* GTMNSNumber+64Bit.m in Sources */, diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj index 651b263..7e2a5e0 100644 --- a/GTMiPhone.xcodeproj/project.pbxproj +++ b/GTMiPhone.xcodeproj/project.pbxproj @@ -21,8 +21,6 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 0B859D9F104D08160064FE46 /* GTMNSData+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B859D9A104D08050064FE46 /* GTMNSData+Hex.m */; }; - 0B859DA0104D08160064FE46 /* GTMNSData+HexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B859D9B104D08050064FE46 /* GTMNSData+HexTest.m */; }; 0B859DA1104D08160064FE46 /* GTMNSDictionary+CaseInsensitive.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B859D9D104D08050064FE46 /* GTMNSDictionary+CaseInsensitive.m */; }; 0B859DA2104D08160064FE46 /* GTMNSDictionary+CaseInsensitiveTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B859D9E104D08050064FE46 /* GTMNSDictionary+CaseInsensitiveTest.m */; }; 0BBC768B10FEF62C0006FABE /* GTMStringEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BBC768910FEF61D0006FABE /* GTMStringEncoding.m */; }; @@ -62,8 +60,6 @@ 8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */; }; 8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */; }; 8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */; }; - 8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */; }; - 8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */; }; 8B3AA9340E0336AC007E31B5 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */; }; 8B41EC0F0E0711D40040CF9F /* GTMValidatingContainersTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */; }; 8B41EC100E0711D40040CF9F /* GTMValidatingContainers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */; }; @@ -103,7 +99,6 @@ 8BCB5AB211C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BCB5AB011C02D7D009B6C40 /* GTMNSScanner+UnsignedTest.m */; }; 8BD35C920FB234E1009058F5 /* GTMNSScanner+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C900FB234E1009058F5 /* GTMNSScanner+JSON.m */; }; 8BD35C930FB234E1009058F5 /* GTMNSScanner+JSONTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD35C910FB234E1009058F5 /* GTMNSScanner+JSONTest.m */; }; - 8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; }; 8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; }; 8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */; }; 8BE83A660E8B059A00C611B0 /* GTMDebugThreadValidationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE83A650E8B059A00C611B0 /* GTMDebugThreadValidationTest.m */; }; @@ -124,8 +119,6 @@ F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFCC0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m */; }; F418AFD70E755D44004FB565 /* GTMPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD50E755D44004FB565 /* GTMPath.m */; }; F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD60E755D44004FB565 /* GTMPathTest.m */; }; - F439ADEB0DBD3C0000BE9B91 /* GTMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */; }; - F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */; }; F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */; }; F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */; }; F4E3B3D80EB5EF2400CB713D /* GTMUIFont+LineHeight.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E3B3D70EB5EF2400CB713D /* GTMUIFont+LineHeight.m */; }; @@ -145,9 +138,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0B859D99104D08050064FE46 /* GTMNSData+Hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSData+Hex.h"; sourceTree = "<group>"; }; - 0B859D9A104D08050064FE46 /* GTMNSData+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+Hex.m"; sourceTree = "<group>"; }; - 0B859D9B104D08050064FE46 /* GTMNSData+HexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+HexTest.m"; sourceTree = "<group>"; }; 0B859D9C104D08050064FE46 /* GTMNSDictionary+CaseInsensitive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSDictionary+CaseInsensitive.h"; sourceTree = "<group>"; }; 0B859D9D104D08050064FE46 /* GTMNSDictionary+CaseInsensitive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+CaseInsensitive.m"; sourceTree = "<group>"; }; 0B859D9E104D08050064FE46 /* GTMNSDictionary+CaseInsensitiveTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+CaseInsensitiveTest.m"; sourceTree = "<group>"; }; @@ -202,11 +192,6 @@ 8B3AA8F00E032FC7007E31B5 /* GTMNSString+URLArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+URLArguments.h"; sourceTree = "<group>"; }; 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArguments.m"; sourceTree = "<group>"; }; 8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArgumentsTest.m"; sourceTree = "<group>"; }; - 8B3AA91F0E033624007E31B5 /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = "<group>"; }; - 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = "<group>"; }; - 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServerTest.m; sourceTree = "<group>"; }; - 8B3AA9270E033647007E31B5 /* GTMTestHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTestHTTPServer.h; sourceTree = "<group>"; }; - 8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTestHTTPServer.m; sourceTree = "<group>"; }; 8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMValidatingContainersTest.m; sourceTree = "<group>"; }; 8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMValidatingContainers.m; sourceTree = "<group>"; }; @@ -310,9 +295,6 @@ F418AFD40E755D44004FB565 /* GTMPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMPath.h; sourceTree = "<group>"; }; F418AFD50E755D44004FB565 /* GTMPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPath.m; sourceTree = "<group>"; }; F418AFD60E755D44004FB565 /* GTMPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPathTest.m; sourceTree = "<group>"; }; - F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMBase64.h; sourceTree = "<group>"; }; - F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64.m; sourceTree = "<group>"; }; - F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64Test.m; sourceTree = "<group>"; }; F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGeometryUtils.h; sourceTree = "<group>"; }; F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtils.m; sourceTree = "<group>"; }; F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtilsTest.m; sourceTree = "<group>"; }; @@ -456,15 +438,9 @@ 0BBC768810FEF61D0006FABE /* GTMStringEncoding.h */, 0BBC768910FEF61D0006FABE /* GTMStringEncoding.m */, 0BBC768A10FEF61D0006FABE /* GTMStringEncodingTest.m */, - 0B859D99104D08050064FE46 /* GTMNSData+Hex.h */, - 0B859D9A104D08050064FE46 /* GTMNSData+Hex.m */, - 0B859D9B104D08050064FE46 /* GTMNSData+HexTest.m */, 0B859D9C104D08050064FE46 /* GTMNSDictionary+CaseInsensitive.h */, 0B859D9D104D08050064FE46 /* GTMNSDictionary+CaseInsensitive.m */, 0B859D9E104D08050064FE46 /* GTMNSDictionary+CaseInsensitiveTest.m */, - F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */, - F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */, - F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */, 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */, 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */, 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */, @@ -475,9 +451,6 @@ F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */, F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */, F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */, - 8B3AA91F0E033624007E31B5 /* GTMHTTPServer.h */, - 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */, - 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */, F41711570ECDFF0400B9B276 /* GTMLightweightProxy.h */, F41711580ECDFF0400B9B276 /* GTMLightweightProxy.m */, F41711590ECDFF0400B9B276 /* GTMLightweightProxyTest.m */, @@ -566,8 +539,6 @@ isa = PBXGroup; children = ( 8B7DCEA90DFF4C760017E983 /* GTMDevLogUnitTestingBridge.m */, - 8B3AA9270E033647007E31B5 /* GTMTestHTTPServer.h */, - 8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */, 8BC047A00DAE928A00C2D1CA /* GTMCALayer+UnitTesting.h */, 8BC047A10DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m */, 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */, @@ -812,8 +783,6 @@ 8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */, 8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */, 8B5547CB0DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m in Sources */, - F439ADEB0DBD3C0000BE9B91 /* GTMBase64.m in Sources */, - F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */, F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */, F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */, 8B7DCEAA0DFF4C760017E983 /* GTMDevLogUnitTestingBridge.m in Sources */, @@ -821,8 +790,6 @@ 67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */, 8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */, 8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */, - 8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */, - 8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */, 8B41EC0F0E0711D40040CF9F /* GTMValidatingContainersTest.m in Sources */, 8B41EC100E0711D40040CF9F /* GTMValidatingContainers.m in Sources */, F418AF990E7558EC004FB565 /* GTMExceptionalInlines.m in Sources */, @@ -835,7 +802,6 @@ F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */, F418AFD70E755D44004FB565 /* GTMPath.m in Sources */, F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */, - 8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */, 8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */, 8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */, 8BE83A660E8B059A00C611B0 /* GTMDebugThreadValidationTest.m in Sources */, @@ -856,8 +822,6 @@ 64D0F5C90FD3E65C00506CC7 /* GTMUIImage+Resize.m in Sources */, 13C1ED4F104896C900907CD8 /* GTMUIView+SubtreeDescription.m in Sources */, 13C1ED50104896C900907CD8 /* GTMUIView+SubtreeDescriptionTest.m in Sources */, - 0B859D9F104D08160064FE46 /* GTMNSData+Hex.m in Sources */, - 0B859DA0104D08160064FE46 /* GTMNSData+HexTest.m in Sources */, 0B859DA1104D08160064FE46 /* GTMNSDictionary+CaseInsensitive.m in Sources */, 0B859DA2104D08160064FE46 /* GTMNSDictionary+CaseInsensitiveTest.m in Sources */, 0BBC768B10FEF62C0006FABE /* GTMStringEncoding.m in Sources */, diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index d5ff42a..2f26e92 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -3,6 +3,19 @@ Google Toolbox for Mac Release Notes Project site: http://code.google.com/p/google-toolbox-for-mac/ Discussion group: http://groups.google.com/group/google-toolbox-for-mac +Release 2.0.?? +Changes since 1.6.0 +??-??-?? + +- Removed iPhone/GTMABAddressBook in favor of AddressBook/GTMABAddressBook. + +- Removed Foundation/GTMHTTPServer and UnitTesting/GTMTestHTTPServer, they + are going to go live with the fetcher used by GData (since they were done + for that testing). + +- Removed Foundation/GTMBase64 and Foundation/GTMNSData+Hex in favor of + Foundation/GTMStringEncoding. + Release 1.6.0 Changes since 1.5.1 diff --git a/UnitTesting/GTMTestHTTPServer.h b/UnitTesting/GTMTestHTTPServer.h deleted file mode 100644 index 0128718..0000000 --- a/UnitTesting/GTMTestHTTPServer.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// GTMTestHTTPServer.h -// -// Copyright 2007-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import <Foundation/Foundation.h> - -@class GTMHTTPServer; - -// This is a HTTP Server that can respond to certain requests that look like -// Google service logins. It takes extra url arguments to tell it what to -// return for testing the code using it. See GTMHTTPFetcherTest for an example -// of its usage. -@interface GTMTestHTTPServer : NSObject { - NSString *docRoot_; - GTMHTTPServer *server_; -} - -// Any url that isn't a specific server request (login, etc.), will be fetched -// off |docRoot| (to allow canned repsonses). -- (id)initWithDocRoot:(NSString *)docRoot; - -// fetch the port the server is running on -- (uint16_t)port; - -@end diff --git a/UnitTesting/GTMTestHTTPServer.m b/UnitTesting/GTMTestHTTPServer.m deleted file mode 100644 index 7744b5e..0000000 --- a/UnitTesting/GTMTestHTTPServer.m +++ /dev/null @@ -1,166 +0,0 @@ -// -// GTMTestHTTPServer.m -// -// Copyright 2007-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMTestHTTPServer.h" -#import "GTMHTTPServer.h" -#import "GTMRegex.h" - -static NSArray *GetSubPatternsOfFirstStringMatchedByPattern(NSString *stringToSearch, - NSString *pattern) { - GTMRegex *regex = [GTMRegex regexWithPattern:pattern]; - NSString *firstMatch = [regex firstSubStringMatchedInString:stringToSearch]; - NSArray *subPatterns = [regex subPatternsOfString:firstMatch]; - return subPatterns; -} - -@implementation GTMTestHTTPServer - -- (id)initWithDocRoot:(NSString *)docRoot { - self = [super init]; - if (self) { - docRoot_ = [docRoot copy]; - server_ = [[GTMHTTPServer alloc] initWithDelegate:self]; - NSError *error = nil; - if ((docRoot == nil) || (![server_ start:&error])) { - _GTMDevLog(@"Failed to start up the webserver (docRoot='%@', error=%@)", - docRoot_, error); - [self release]; - return nil; - } - } - return self; -} - -- (void)dealloc { - [docRoot_ release]; - [server_ release]; - [super dealloc]; -} - -- (uint16_t)port { - return [server_ port]; -} - -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request { - _GTMDevAssert(server == server_, @"how'd we get a different server?!"); - UInt32 resultStatus = 0; - NSData *data = nil; - // clients should treat dates as opaque, generally - NSString *modifiedDate = @"thursday"; - - NSString *postString = @""; - NSData *postData = [request body]; - if ([postData length] > 0) { - postString = [[[NSString alloc] initWithData:postData - encoding:NSUTF8StringEncoding] autorelease]; - } - - NSDictionary *allHeaders = [request allHeaderFieldValues]; - NSString *ifModifiedSince = [allHeaders objectForKey:@"If-Modified-Since"]; - NSString *authorization = [allHeaders objectForKey:@"Authorization"]; - NSString *path = [[request URL] absoluteString]; - - if ([path hasSuffix:@".auth"]) { - if (![authorization isEqualToString:@"GoogleLogin auth=GoodAuthToken"]) { - GTMHTTPResponseMessage *response = - [GTMHTTPResponseMessage emptyResponseWithCode:401]; - return response; - } else { - path = [path substringToIndex:[path length] - 5]; - } - } - - NSString *overrideHeader = [allHeaders objectForKey:@"X-HTTP-Method-Override"]; - NSString *httpCommand = [request method]; - if ([httpCommand isEqualToString:@"POST"] && - [overrideHeader length] > 1) { - httpCommand = overrideHeader; - } - NSArray *searchResult = nil; - if ([path hasSuffix:@"/accounts/ClientLogin"]) { - // it's a sign-in attempt; it's good unless the password is "bad" or - // "captcha" - - // use regular expression to find the password - NSString *password = @""; - searchResult = GetSubPatternsOfFirstStringMatchedByPattern(path, @"Passwd=([^&\n]*)"); - if ([searchResult count] == 2) { - password = [searchResult objectAtIndex:1]; - } - - if ([password isEqualToString:@"bad"]) { - resultStatus = 403; - } else if ([password isEqualToString:@"captcha"]) { - NSString *loginToken = @""; - NSString *loginCaptcha = @""; - - searchResult = GetSubPatternsOfFirstStringMatchedByPattern(postString, @"logintoken=([^&\n]*)"); - if ([searchResult count] == 2) { - loginToken = [searchResult objectAtIndex:1]; - } - - searchResult = GetSubPatternsOfFirstStringMatchedByPattern(postString, @"logincaptcha=([^&\n]*)"); - if ([searchResult count] == 2) { - loginCaptcha = [searchResult objectAtIndex:1]; - } - - if ([loginToken isEqualToString:@"CapToken"] && - [loginCaptcha isEqualToString:@"good"]) { - resultStatus = 200; - } else { - // incorrect captcha token or answer provided - resultStatus = 403; - } - } else { - // valid username/password - resultStatus = 200; - } - } else if ([httpCommand isEqualToString:@"DELETE"]) { - // it's an object delete; read and return empty data - resultStatus = 200; - } else { - // queries that have something like "?status=456" should fail with the - // status code - searchResult = GetSubPatternsOfFirstStringMatchedByPattern(path, @"status=([0-9]+)"); - if ([searchResult count] == 2) { - resultStatus = [[searchResult objectAtIndex:1] intValue]; - } else if ([ifModifiedSince isEqualToString:modifiedDate]) { - resultStatus = 304; - } else { - NSString *docPath = [docRoot_ stringByAppendingPathComponent:path]; - data = [NSData dataWithContentsOfFile:docPath]; - if (data) { - resultStatus = 200; - } else { - resultStatus = 404; - } - } - } - - GTMHTTPResponseMessage *response = - [GTMHTTPResponseMessage responseWithBody:data - contentType:@"text/plain" - statusCode:resultStatus]; - [response setValue:modifiedDate forHeaderField:@"Last-Modified"]; - [response setValue:[NSString stringWithFormat:@"TestCookie=%@", [path lastPathComponent]] - forHeaderField:@"Set-Cookie"]; - return response; -} - -@end diff --git a/iPhone/GTMABAddressBook.h b/iPhone/GTMABAddressBook.h deleted file mode 100644 index 828f05a..0000000 --- a/iPhone/GTMABAddressBook.h +++ /dev/null @@ -1,328 +0,0 @@ -// -// GTMABAddressBook.h -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -// These classes wrap up the iPhone AddressBook 'C' API in a manner very -// similar to that found on Mac OS X. They differ only in that none of these -// routines throws, and some of the types are different as necessitated by -// the APIs that they wrap. These wrappers also protect you from a number -// of issues in the AddressBook API (as of iPhone SDK 2.0/2.1) -// -// Note that there is a strings file that you may want to localize -// (GTMABAddressBook.strings). -// -// If things seem strange, it may be due to one of the following radars: -// 6240394 AddressBook framework constants not initialized until -// ABCreateAddressBook called -// -- CLOSED as designed -// 6208390 Integer and real values don't work in ABMultiValueRefs -// (and this isn't part of the title, but dictionaries don't work -// either) -// 6207605 RecordIDs for people and groups are not unique in AddressBook -// -- CLOSED as designed -// 6204021 kABGroupNameProperty and kABPersonFirstNameProperty have the same -// value -// 6203982 ABPersonCopyLocalizedPropertyName returns name for -// kABGroupNameProperty -// 6203961 ABPersonGetTypeOfProperty returns a type for kABGroupNameProperty -// 6203854 ABMultiValues hash to their address -// 6203836 ABRecords hash to their address -// -- CLOSED behaves correctly -// 6203606 Need CFTypeIDs for AddressBook CFTypes -// 6202868 ABPersonSetImageData should validate image data -// 6202860 Passing nil person into ABGroupAddMember crashes -// -- CLOSED behaves correctly -// 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash -// -- CLOSED behaves correctly -// 6202807 ABMultiValueInsertValueAndLabelAtIndex allows you to insert values -// past end -// 6201276 Removing a NULL record using ABAddressBookRemoveRecord crashes -// -- CLOSED behaves correctly -// 6201258 Adding a NULL record using ABAddressBookAddRecord crashes -// -- CLOSED behaves correctly -// 6201046 ABRecordSetValue returns true even if you pass in a bad type for a -// value -// 6201005 ABRecordRemoveValue returns true for value that aren't in the record -// -- CLOSED behaves correctly -// 6200703 ABAddressBookAddRecord doesn't add an item to the people array until -// it's saved -// 6200638 ABAddressBookHasUnsavedChanges doesn't work - -#import <UIKit/UIKit.h> -#import <AddressBook/AddressBook.h> -#import "GTMDefines.h" - -#if !GTM_IPHONE_SDK -#error This file is for iPhone use only use ABAddressBook on Mac OS X -#endif - -@class GTMABPerson; -@class GTMABGroup; -@class GTMABRecord; - -GTM_EXTERN NSString *const kGTMABUnknownPropertyName; - -// Wrapper for an AddressBook on iPhone -@interface GTMABAddressBook : NSObject { - @private - ABAddressBookRef addressBook_; -} - -// Returns a new instance of an address book. -+ (GTMABAddressBook *)addressBook; - -// Return the address book reference -- (ABAddressBookRef)addressBookRef; - -// Saves changes made since the last save -// Return YES if successful (or there was no change) -- (BOOL)save; - -// Saves changes made since the last save -// Return YES if successful (or there was no change) -- (BOOL)saveAndReturnError:(NSError **)error; - -// Returns YES if there are unsaved changes -// The unsaved changes flag is automatically set when changes are made -// As of iPhone 2.1, this does not work, and will always return NO. -// Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work -- (BOOL)hasUnsavedChanges; - -// Reverts any changes that have been made and resets the unsaved flag -// Be sure to read notes for -hasUnsavedChanges and -people and -groups. -- (void)revert; - -// Returns a GTMABPerson matching an ID -// Returns nil if the record could not be found -- (GTMABPerson *)personForId:(ABRecordID)uniqueId; - -// Returns a GTMABGroup matching an ID -// Returns nil if the record could not be found -- (GTMABGroup *)groupForId:(ABRecordID)uniqueId; - -// Adds a record (ABPerson or ABGroup) to the AddressBook database -// Be sure to read notes for -people and -group. -- (BOOL)addRecord:(GTMABRecord *)record; - -// Removes a record (ABPerson or ABGroup) from the AddressBook database -- (BOOL)removeRecord:(GTMABRecord *)record; - -// Returns an array (GTMABPerson) of all the people in the AddressBook database -// As of iPhone 2.1, this array will not contain new entries until you save -// the address book. -// Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people array -// until it's saved -- (NSArray *)people; - -// Returns an array of all the groups (GTMABGroup) in the AddressBook database -// As of iPhone 2.1, this array will not contain new entries until you save -// the address book. -// Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people array -// until it's saved -- (NSArray *)groups; - -// Returns a localized name for a given label -+ (NSString *)localizedLabel:(CFStringRef)label; - -@end - -// Wrapper for a ABRecord on iPhone. -// A abstract class. Instantiate one of the concrete subclasses, GTMABPerson or -// GTMABGroup. -@interface GTMABRecord : NSObject { - @private - ABRecordRef record_; -} - -// Create a record with a recordRef. -// Since GTMABRecord is an abstract base class, attempting to create one -// of these directly will throw an exception. Use with one of the concrete -// subclasses. -+ (id)recordWithRecord:(ABRecordRef)record; - -// Designated initializer -// Since GTMABRecord is an abstract base class, attempting to create one -// of these directly will throw an exception. Use with one of the concrete -// subclasses. -- (id)initWithRecord:(ABRecordRef)record; - -// Return our recordRef -- (ABRecordRef)recordRef; - -// Return the recordID for the record -- (ABRecordID)recordID; - -// Returns the value of a given property. -// The type of the value depends on the property type. -- (id)valueForProperty:(ABPropertyID)property; - -// Set the value of a given property. -// The type of the value must match the property type. -// Returns YES if value set properly -- (BOOL)setValue:(id)value forProperty:(ABPropertyID)property; - -// Removes the value for the property -// Returns yes if value removed -- (BOOL)removeValueForProperty:(ABPropertyID)property; - -// returns a human friendly name for the record -- (NSString *)compositeName; - -// returns the type of a property -+ (ABPropertyType)typeOfProperty:(ABPropertyID)property; - -// returns a human friendly localized name for a property -+ (NSString *)localizedPropertyName:(ABPropertyID)property; -@end - -// Wrapper for an ABPerson on iPhone -@interface GTMABPerson : GTMABRecord - -// Creates a person with a first name and a last name. -+ (GTMABPerson *)personWithFirstName:(NSString *)first - lastName:(NSString *)last; - -// Sets image data for a person. Data must be to a block of data that -// will create a valid UIImage. -- (BOOL)setImageData:(NSData *)data; - -// Returns the image data. -- (NSData *)imageData; - -// Returns the image for a person -- (UIImage *)image; - -// Sets a the image for a person -- (BOOL)setImage:(UIImage *)image; - -// Returns the format in with names are composited -+ (ABPersonCompositeNameFormat)compositeNameFormat; -@end - -// Wrapper for a ABGroup on iPhone -@interface GTMABGroup : GTMABRecord -// Create a new group named |name| -+ (GTMABGroup *)groupNamed:(NSString *)name; - -// Return an array of members (GTMABPerson) -- (NSArray *)members; - -// Add a member to a group -- (BOOL)addMember:(GTMABPerson *)person; - -// Remove a member from a group -- (BOOL)removeMember:(GTMABPerson *)person; -@end - -// GTMABMultiValue does not supprt NSFastEnumeration because in -// the Apple frameworks it returns identifiers which are already NSStrings. -// In our case identifiers aren't NS types, and it doesn't make sense -// to convert them to NSNumbers just to convert them back so you can -// actually get at the values and labels. -// Instead we supply valueEnumerator and labelEnumerator which you can -// fast enumerate on to get values and labels directly. -@interface GTMABMultiValue : NSObject <NSCopying, NSMutableCopying> { - @protected - ABMultiValueRef multiValue_; -} - -// Create a multi value -- (id)initWithMultiValue:(ABMultiValueRef)multiValue; - -// return it's ref -- (ABMultiValueRef)multiValueRef; - -// Returns the number of value/label pairs -- (NSUInteger)count; - -// Returns a value at a given index -// Returns nil if index is out of bounds -- (id)valueAtIndex:(NSUInteger)idx; - -// Returns a label at a given index -// Returns nil if index is out of bounds -- (NSString *)labelAtIndex:(NSUInteger)idx; - -// Returns an identifier at a given index -// Returns kABMultiValueInvalidIdentifier if index is out of bounds -- (ABMultiValueIdentifier)identifierAtIndex:(NSUInteger)idx; - -// Returns the index of a given identifier -// Returns NSNotFound if not found -- (NSUInteger)indexForIdentifier:(ABMultiValueIdentifier)identifier; - -// Type of the contents of this multivalue -- (ABPropertyType)propertyType; - -// Returns the value for a given identifier -// Returns nil if the identifier is not found -- (id)valueForIdentifier:(ABMultiValueIdentifier)identifier; - -// Returns the value for a given identifier -// Returns nil if the identifier is not found -- (NSString *)labelForIdentifier:(ABMultiValueIdentifier)identifier; - -// Returns an enumerator for enumerating through values -- (NSEnumerator *)valueEnumerator; - -// Returns an enumerator for enumerating through labels -- (NSEnumerator *)labelEnumerator; - -@end - -@interface GTMABMutableMultiValue : GTMABMultiValue { - @private - // Use unsigned long here instead of NSUInteger because that's what - // NSFastEnumeration Protocol wants currently (iPhone 2.1) - unsigned long mutations_; -} - -// Create a new mutable multivalue with a given type -+ (id)valueWithPropertyType:(ABPropertyType)type; - -// Create a new mutable multivalue with a given type -- (id)initWithPropertyType:(ABPropertyType)type; - -// Create a new mutable multivalue based on |multiValue| -- (id)initWithMutableMultiValue:(ABMutableMultiValueRef)multiValue; - -// Adds a value with its label -// Returns the identifier if successful, kABMultiValueInvalidIdentifier -// otherwise. -- (ABMultiValueIdentifier)addValue:(id)value withLabel:(CFStringRef)label; - -// Insert a value/label pair at a given index -// Returns the identifier if successful. kABMultiValueInvalidIdentifier -// otherwise -// If index is out of bounds, returns kABMultiValueInvalidIdentifier. -- (ABMultiValueIdentifier)insertValue:(id)value - withLabel:(CFStringRef)label - atIndex:(NSUInteger)index; - -// Removes a value/label pair at a given index -// Returns NO if index out of bounds -- (BOOL)removeValueAndLabelAtIndex:(NSUInteger)index; - -// Replaces a value at a given index -// Returns NO if index out of bounds -- (BOOL)replaceValueAtIndex:(NSUInteger)index withValue:(id)value; - -// Replaces a label at a given index -// Returns NO if index out of bounds -- (BOOL)replaceLabelAtIndex:(NSUInteger)index withLabel:(CFStringRef)label; - -@end diff --git a/iPhone/GTMABAddressBook.m b/iPhone/GTMABAddressBook.m deleted file mode 100644 index bb84e61..0000000 --- a/iPhone/GTMABAddressBook.m +++ /dev/null @@ -1,901 +0,0 @@ -// -// GTMAddressBook.m -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMABAddressBook.h" -#import "GTMGarbageCollection.h" - -NSString *const kGTMABUnknownPropertyName = @"UNKNOWN_PROPERTY"; - -typedef struct { - ABPropertyType pType; - Class class; -} TypeClassNameMap; - -@interface GTMABMultiValue () -- (unsigned long*)mutations; -@end - -@interface GTMABMutableMultiValue () -// Checks to see if a value is a valid type to be stored in this multivalue -- (BOOL)checkValueType:(id)value; -@end - -@interface GTMABMultiValueEnumerator : NSEnumerator { - @private - __weak ABMultiValueRef ref_; // ref_ cached from enumeree_ - GTMABMultiValue *enumeree_; - unsigned long mutations_; - NSUInteger count_; - NSUInteger index_; - BOOL useLabels_; -} -+ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree; -+ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree; -- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels; -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - objects:(id *)stackbuf - count:(NSUInteger)len; -@end - -@implementation GTMABAddressBook -+ (GTMABAddressBook *)addressBook { - return [[[self alloc] init] autorelease]; -} - -- (id)init { - if ((self = [super init])) { - addressBook_ = ABAddressBookCreate(); - if (!addressBook_) { - // COV_NF_START - [self release]; - self = nil; - // COV_NF_END - } - } - return self; -} - -- (void)dealloc { - if (addressBook_) { - CFRelease(addressBook_); - } - [super dealloc]; -} - -- (BOOL)save { - return [self saveAndReturnError:NULL]; -} - -- (BOOL)saveAndReturnError:(NSError **)error { - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookSave(addressBook_, &cfError); - GTMCFAutorelease(cfError); - if (error) { - *error = (NSError *)cfError; // COV_NF_LINE - } - return wasGood ? YES : NO; -} - -- (BOOL)hasUnsavedChanges { - return ABAddressBookHasUnsavedChanges(addressBook_); -} - -- (void)revert { - ABAddressBookRevert(addressBook_); -} - -- (BOOL)addRecord:(GTMABRecord *)record { - // Note: we check for bad data here because of radar - // 6201258 Adding a NULL record using ABAddressBookAddRecord crashes - if (!record) return NO; - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookAddRecord(addressBook_, - [record recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (BOOL)removeRecord:(GTMABRecord *)record { - // Note: we check for bad data here because of radar - // 6201276 Removing a NULL record using ABAddressBookRemoveRecord crashes - if (!record) return NO; - CFErrorRef cfError = NULL; - bool wasGood = ABAddressBookRemoveRecord(addressBook_, - [record recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (NSArray *)people { - NSArray *people - = GTMCFAutorelease(ABAddressBookCopyArrayOfAllPeople(addressBook_)); - NSMutableArray *result = [NSMutableArray arrayWithCapacity:[people count]]; - for (id person in people) { - [result addObject:[GTMABPerson recordWithRecord:person]]; - } - return result; -} - -- (NSArray *)groups { - NSArray *groups - = GTMCFAutorelease(ABAddressBookCopyArrayOfAllGroups(addressBook_)); - NSMutableArray *result = [NSMutableArray arrayWithCapacity:[groups count]]; - for (id group in groups) { - [result addObject:[GTMABGroup recordWithRecord:group]]; - } - return result; -} - -- (ABAddressBookRef)addressBookRef { - return addressBook_; -} - -- (GTMABPerson *)personForId:(ABRecordID)uniqueId { - GTMABPerson *person = nil; - ABRecordRef ref = ABAddressBookGetPersonWithRecordID(addressBook_, uniqueId); - if (ref) { - person = [GTMABPerson recordWithRecord:ref]; - } - return person; -} - -- (GTMABGroup *)groupForId:(ABRecordID)uniqueId { - GTMABGroup *group = nil; - ABRecordRef ref = ABAddressBookGetGroupWithRecordID(addressBook_, uniqueId); - if (ref) { - group = [GTMABGroup recordWithRecord:ref]; - } - return group; -} - -+ (NSString *)localizedLabel:(CFStringRef)label { - return GTMCFAutorelease(ABAddressBookCopyLocalizedLabel(label)); -} - -@end - -@implementation GTMABRecord -+ (id)recordWithRecord:(ABRecordRef)record { - return [[[self alloc] initWithRecord:record] autorelease]; -} - -- (id)initWithRecord:(ABRecordRef)record { - if ((self = [super init])) { - if ([self class] == [GTMABRecord class]) { - [self autorelease]; - [self doesNotRecognizeSelector:_cmd]; - } - if (!record) { - [self release]; - self = nil; - } else { - record_ = CFRetain(record); - } - } - return self; -} - -- (NSUInteger)hash { - // This really isn't completely valid due to - // 6203836 ABRecords hash to their address - // but it's the best we can do without knowing what properties - // are in a record, and we don't have an API for that. - return CFHash(record_); -} - -- (BOOL)isEqual:(id)object { - // This really isn't completely valid due to - // 6203836 ABRecords hash to their address - // but it's the best we can do without knowing what properties - // are in a record, and we don't have an API for that. - return [object respondsToSelector:@selector(recordRef)] - && CFEqual(record_, [object recordRef]); -} - -- (void)dealloc { - if (record_) { - CFRelease(record_); - } - [super dealloc]; -} - -- (ABRecordRef)recordRef { - return record_; -} - -- (ABRecordID)recordID { - return ABRecordGetRecordID(record_); -} - -- (id)valueForProperty:(ABPropertyID)property { - id value = GTMCFAutorelease(ABRecordCopyValue(record_, property)); - if (value) { - if ([[self class] typeOfProperty:property] & kABMultiValueMask) { - value = [[[GTMABMultiValue alloc] initWithMultiValue:value] autorelease]; - } - } - return value; -} - -- (BOOL)setValue:(id)value forProperty:(ABPropertyID)property { - if (!value) return NO; - // We check the type here because of - // Radar 6201046 ABRecordSetValue returns true even if you pass in a bad type - // for a value - TypeClassNameMap fullTypeMap[] = { - { kABStringPropertyType, [NSString class] }, - { kABIntegerPropertyType, [NSNumber class] }, - { kABRealPropertyType, [NSNumber class] }, - { kABDateTimePropertyType, [NSDate class] }, - { kABDictionaryPropertyType, [NSDictionary class] }, - { kABMultiStringPropertyType, [GTMABMultiValue class] }, - { kABMultiRealPropertyType, [GTMABMultiValue class] }, - { kABMultiDateTimePropertyType, [GTMABMultiValue class] }, - { kABMultiDictionaryPropertyType, [GTMABMultiValue class] } - }; - ABPropertyType type = [[self class] typeOfProperty:property]; - BOOL wasFound = NO; - for (size_t i = 0; i < sizeof(fullTypeMap) / sizeof(TypeClassNameMap); ++i) { - if (fullTypeMap[i].pType == type) { - wasFound = YES; - if (![[value class] isSubclassOfClass:fullTypeMap[i].class]) { - return NO; - } - } - } - if (!wasFound) { - return NO; - } - if (type & kABMultiValueMask) { - value = (id)[value multiValueRef]; - } - CFErrorRef cfError = nil; - bool wasGood = ABRecordSetValue(record_, property, (CFTypeRef)value, &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (BOOL)removeValueForProperty:(ABPropertyID)property { - CFErrorRef cfError = nil; - // We check to see if the value is in the property because of: - // Radar 6201005 ABRecordRemoveValue returns true for value that aren't - // in the record - id value = [self valueForProperty:property]; - bool wasGood = value && ABRecordRemoveValue(record_, property, &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (NSString *)compositeName { - return GTMCFAutorelease(ABRecordCopyCompositeName(record_)); -} - -// COV_NF_START -// Both of these methods are to be overridden by their subclasses -+ (ABPropertyType)typeOfProperty:(ABPropertyID)property { - [self doesNotRecognizeSelector:_cmd]; - return kABInvalidPropertyType; -} - -+ (NSString *)localizedPropertyName:(ABPropertyID)property { - [self doesNotRecognizeSelector:_cmd]; - return nil; -} -// COV_NF_END -@end - -@implementation GTMABPerson - -+ (GTMABPerson *)personWithFirstName:(NSString *)first - lastName:(NSString *)last { - GTMABPerson *person = [[[self alloc] init] autorelease]; - if (person) { - BOOL isGood = YES; - if (first) { - isGood = [person setValue:first forProperty:kABPersonFirstNameProperty]; - } - if (isGood && last) { - isGood = [person setValue:last forProperty:kABPersonLastNameProperty]; - } - if (!isGood) { - // COV_NF_START - // Marked as NF because I don't know how to force an error - person = nil; - // COV_NF_END - } - } - return person; -} - -- (id)init { - ABRecordRef person = ABPersonCreate(); - self = [super initWithRecord:person]; - if (person) { - CFRelease(person); - } - return self; -} - -- (BOOL)setImageData:(NSData *)data { - CFErrorRef cfError = NULL; - bool wasGood = NO; - if (!data) { - wasGood = ABPersonRemoveImageData([self recordRef], &cfError); - } else { - // We verify that the data is good because of: - // Radar 6202868 ABPersonSetImageData should validate image data - UIImage *image = [UIImage imageWithData:data]; - wasGood = image && ABPersonSetImageData([self recordRef], - (CFDataRef)data, &cfError); - } - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (UIImage *)image { - return [UIImage imageWithData:[self imageData]]; -} - -- (BOOL)setImage:(UIImage *)image { - NSData *data = UIImagePNGRepresentation(image); - return [self setImageData:data]; -} - -- (NSData *)imageData { - return GTMCFAutorelease(ABPersonCopyImageData([self recordRef])); -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%@ %@ %@ %d", - [self class], - [self valueForProperty:kABPersonFirstNameProperty], - [self valueForProperty:kABPersonLastNameProperty], - [self recordID]]; -} - -+ (NSString *)localizedPropertyName:(ABPropertyID)property { - return GTMCFAutorelease(ABPersonCopyLocalizedPropertyName(property)); -} - -+ (ABPersonCompositeNameFormat)compositeNameFormat { - return ABPersonGetCompositeNameFormat(); -} - -+ (ABPropertyType)typeOfProperty:(ABPropertyID)property { - return ABPersonGetTypeOfProperty(property); -} -@end - -@implementation GTMABGroup - -+ (GTMABGroup *)groupNamed:(NSString *)name { - GTMABGroup *group = [[[self alloc] init] autorelease]; - if (group) { - if (![group setValue:name forProperty:kABGroupNameProperty]) { - // COV_NF_START - // Can't get setValue to fail for me - group = nil; - // COV_NF_END - } - } - return group; -} - -- (id)init { - ABRecordRef group = ABGroupCreate(); - self = [super initWithRecord:group]; - if (group) { - CFRelease(group); - } - return self; -} - -- (NSArray *)members { - NSArray *people - = GTMCFAutorelease(ABGroupCopyArrayOfAllMembers([self recordRef])); - NSMutableArray *gtmPeople = [NSMutableArray arrayWithCapacity:[people count]]; - for (id person in people) { - [gtmPeople addObject:[GTMABPerson recordWithRecord:(ABRecordRef)person]]; - } - return gtmPeople; -} - -- (BOOL)addMember:(GTMABPerson *)person { - CFErrorRef cfError = nil; - // We check for person because of - // Radar 6202860 Passing nil person into ABGroupAddMember crashes - bool wasGood = person && ABGroupAddMember([self recordRef], - [person recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -- (BOOL)removeMember:(GTMABPerson *)person { - CFErrorRef cfError = nil; - // We check for person because of - // Radar 6202860 Passing nil person into ABGroupAddMember crashes - // (I know this is remove, but it crashes there too) - bool wasGood = person && ABGroupRemoveMember([self recordRef], - [person recordRef], &cfError); - if (cfError) { - // COV_NF_START - _GTMDevLog(@"Error in [%@ %@]: %@", - [self class], NSStringFromSelector(_cmd), cfError); - CFRelease(cfError); - // COV_NF_END - } - return wasGood ? YES : NO; -} - -+ (ABPropertyType)typeOfProperty:(ABPropertyID)property { - ABPropertyType type = kABInvalidPropertyType; - if (property == kABGroupNameProperty) { - type = kABStringPropertyType; - } - return type; -} - -+ (NSString *)localizedPropertyName:(ABPropertyID)property { - NSString *name = kGTMABUnknownPropertyName; - if (property == kABGroupNameProperty) { - name = NSLocalizedStringFromTable(@"Name", - @"GTMABAddressBook", - @"name property"); - } - return name; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%@ %@ %d", - [self class], - [self valueForProperty:kABGroupNameProperty], - [self recordID]]; -} -@end - -@implementation GTMABMultiValue -- (id)init { - // Call super init and release so we don't leak - [[super init] autorelease]; - [self doesNotRecognizeSelector:_cmd]; - return nil; // COV_NF_LINE -} - -- (id)initWithMultiValue:(ABMultiValueRef)multiValue { - if ((self = [super init])) { - if (!multiValue) { - [self release]; - self = nil; - } else { - multiValue_ = CFRetain(multiValue); - } - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - return [[GTMABMultiValue alloc] initWithMultiValue:multiValue_]; -} - -- (id)mutableCopyWithZone:(NSZone *)zone { - return [[GTMABMutableMultiValue alloc] initWithMultiValue:multiValue_]; -} - -- (NSUInteger)hash { - // I'm implementing hash instead of using CFHash(multiValue_) because - // 6203854 ABMultiValues hash to their address - NSUInteger count = [self count]; - NSUInteger hash = 0; - for (NSUInteger i = 0; i < count; ++i) { - NSString *label = [self labelAtIndex:i]; - id value = [self valueAtIndex:i]; - hash += [label hash]; - hash += [value hash]; - } - return hash; -} - -- (BOOL)isEqual:(id)object { - // I'm implementing isEqual instea of using CFEquals(multiValue,...) because - // 6203854 ABMultiValues hash to their address - // and it appears CFEquals just calls through to hash to compare them. - BOOL isEqual = NO; - if ([object respondsToSelector:@selector(multiValueRef)]) { - isEqual = multiValue_ == [object multiValueRef]; - if (!isEqual) { - NSUInteger count = [self count]; - NSUInteger objCount = [object count]; - isEqual = count == objCount; - for (NSUInteger i = 0; isEqual && i < count; ++i) { - NSString *label = [self labelAtIndex:i]; - NSString *objLabel = [object labelAtIndex:i]; - isEqual = [label isEqual:objLabel]; - if (isEqual) { - id value = [self valueAtIndex:i]; - id objValue = [object valueAtIndex:i]; - isEqual = [value isEqual:objValue]; - } - } - } - } - return isEqual; -} - -- (void)dealloc { - if (multiValue_) { - CFRelease(multiValue_); - } - [super dealloc]; -} - -- (ABMultiValueRef)multiValueRef { - return multiValue_; -} - -- (NSUInteger)count { - return ABMultiValueGetCount(multiValue_); -} - -- (id)valueAtIndex:(NSUInteger)idx { - id value = nil; - if (idx < [self count]) { - value = GTMCFAutorelease(ABMultiValueCopyValueAtIndex(multiValue_, idx)); - ABPropertyType type = [self propertyType]; - if (type == kABIntegerPropertyType - || type == kABRealPropertyType - || type == kABDictionaryPropertyType) { - // This is because of - // 6208390 Integer and real values don't work in ABMultiValueRefs - // Apparently they forget to add a ref count on int, real and - // dictionary values in ABMultiValueCopyValueAtIndex, although they do - // remember them for all other types. - // Once they fix this, this will lead to a leak, but I figure the leak - // is better than the crash. Our unittests will test to make sure that - // this is the case, and once we find a system that has this fixed, we - // can conditionalize this code. Look for testRadar6208390 in - // GTMABAddressBookTest.m - // Also, search for 6208390 below and fix the fast enumerator to actually - // be somewhat performant when this is fixed. - [value retain]; - } - } - return value; -} - -- (NSString *)labelAtIndex:(NSUInteger)idx { - NSString *label = nil; - if (idx < [self count]) { - label = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(multiValue_, idx)); - } - return label; -} - -- (ABMultiValueIdentifier)identifierAtIndex:(NSUInteger)idx { - ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; - if (idx < [self count]) { - identifier = ABMultiValueGetIdentifierAtIndex(multiValue_, idx); - } - return identifier; -} - -- (NSUInteger)indexForIdentifier:(ABMultiValueIdentifier)identifier { - NSUInteger idx = ABMultiValueGetIndexForIdentifier(multiValue_, identifier); - return idx == (NSUInteger)kCFNotFound ? (NSUInteger)NSNotFound : idx; -} - -- (ABPropertyType)propertyType { - return ABMultiValueGetPropertyType(multiValue_); -} - -- (id)valueForIdentifier:(ABMultiValueIdentifier)identifier { - return [self valueAtIndex:[self indexForIdentifier:identifier]]; -} - -- (NSString *)labelForIdentifier:(ABMultiValueIdentifier)identifier { - return [self labelAtIndex:[self indexForIdentifier:identifier]]; -} - -- (unsigned long*)mutations { - // We just need some constant non-zero value here so fast enumeration works. - // Dereferencing self should give us the isa which will stay constant - // over the enumeration. - return (unsigned long*)self; -} - -- (NSEnumerator *)valueEnumerator { - return [GTMABMultiValueEnumerator valueEnumeratorFor:self]; -} - -- (NSEnumerator *)labelEnumerator { - return [GTMABMultiValueEnumerator labelEnumeratorFor:self]; -} - -@end - -@implementation GTMABMutableMultiValue -+ (id)valueWithPropertyType:(ABPropertyType)type { - return [[[self alloc] initWithPropertyType:type] autorelease]; -} - -- (id)initWithPropertyType:(ABPropertyType)type { - ABMutableMultiValueRef ref = nil; - if (type != kABInvalidPropertyType) { - ref = ABMultiValueCreateMutable(type); - } - self = [super initWithMultiValue:ref]; - if (ref) { - CFRelease(ref); - } - return self; -} - -- (id)initWithMultiValue:(ABMultiValueRef)multiValue { - ABMutableMultiValueRef ref = nil; - if (multiValue) { - ref = ABMultiValueCreateMutableCopy(multiValue); - } - self = [super initWithMultiValue:ref]; - if (ref) { - CFRelease(ref); - } - return self; -} - -- (id)initWithMutableMultiValue:(ABMutableMultiValueRef)multiValue { - return [super initWithMultiValue:multiValue]; -} - -- (BOOL)checkValueType:(id)value { - BOOL isGood = NO; - if (value) { - TypeClassNameMap singleValueTypeMap[] = { - { kABStringPropertyType, [NSString class] }, - { kABIntegerPropertyType, [NSNumber class] }, - { kABRealPropertyType, [NSNumber class] }, - { kABDateTimePropertyType, [NSDate class] }, - { kABDictionaryPropertyType, [NSDictionary class] }, - }; - ABPropertyType type = [self propertyType]; - for (size_t i = 0; - i < sizeof(singleValueTypeMap) / sizeof(TypeClassNameMap); ++i) { - if (singleValueTypeMap[i].pType == type) { - if ([[value class] isSubclassOfClass:singleValueTypeMap[i].class]) { - isGood = YES; - break; - } - } - } - } - return isGood; -} - -- (ABMultiValueIdentifier)addValue:(id)value withLabel:(CFStringRef)label { - ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; - // We check label and value here because of - // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash - if (!label - || ![self checkValueType:value] - || !ABMultiValueAddValueAndLabel(multiValue_, - value, - label, - &identifier)) { - identifier = kABMultiValueInvalidIdentifier; - } else { - mutations_++; - } - return identifier; -} - -- (ABMultiValueIdentifier)insertValue:(id)value - withLabel:(CFStringRef)label - atIndex:(NSUInteger)idx { - ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; - // We perform a check here to ensure that we don't get bitten by - // Radar 6202807 ABMultiValueInsertValueAndLabelAtIndex allows you to insert - // values past end - NSUInteger count = [self count]; - // We check label and value here because of - // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash - if (idx > count - || !label - || ![self checkValueType:value] - || !ABMultiValueInsertValueAndLabelAtIndex(multiValue_, - value, - label, - idx, - &identifier)) { - identifier = kABMultiValueInvalidIdentifier; - } else { - mutations_++; - } - return identifier; -} - -- (BOOL)removeValueAndLabelAtIndex:(NSUInteger)idx { - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count) { - if (ABMultiValueRemoveValueAndLabelAtIndex(multiValue_, - idx)) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (BOOL)replaceValueAtIndex:(NSUInteger)idx withValue:(id)value { - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count && [self checkValueType:value]) { - if (ABMultiValueReplaceValueAtIndex(multiValue_, - value, idx)) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (BOOL)replaceLabelAtIndex:(NSUInteger)idx withLabel:(CFStringRef)label{ - BOOL isGood = NO; - NSUInteger count = [self count]; - if (idx < count) { - if (ABMultiValueReplaceLabelAtIndex(multiValue_, - label, - idx)) { - mutations_++; - isGood = YES; - } - } - return isGood; -} - -- (unsigned long*)mutations { - return &mutations_; -} -@end - - -@implementation GTMABMultiValueEnumerator - -+ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree { - return [[[self alloc] initWithEnumeree:enumeree useLabels:NO] autorelease]; -} - -+ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree { - return [[[self alloc] initWithEnumeree:enumeree useLabels:YES] autorelease]; -} - -- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels { - if ((self = [super init])) { - if (enumeree) { - enumeree_ = [enumeree retain]; - useLabels_ = useLabels; - } else { - // COV_NF_START - // Since this is a private class where the enumeree creates us - // there is no way we should ever get here. - [self release]; - self = nil; - // COV_NF_END - } - } - return self; -} - -- (void)dealloc { - [enumeree_ release]; - [super dealloc]; -} - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - objects:(id *)stackbuf - count:(NSUInteger)len { - NSUInteger i; - if (!ref_) { - count_ = [enumeree_ count]; - ref_ = [enumeree_ multiValueRef]; - } - - for (i = 0; state->state < count_ && i < len; ++i, ++state->state) { - if (useLabels_) { - stackbuf[i] = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, - state->state)); - } else { - // Yes this is slow, but necessary in light of radar 6208390 - // Once this is fixed we can go to something similar to the label - // case which should speed stuff up again. Hopefully anybody who wants - // real performance is willing to move down to the C API anyways. - stackbuf[i] = [enumeree_ valueAtIndex:state->state]; - } - } - - state->itemsPtr = stackbuf; - state->mutationsPtr = [enumeree_ mutations]; - return i; -} - -- (id)nextObject { - id value = nil; - if (!ref_) { - count_ = [enumeree_ count]; - mutations_ = *[enumeree_ mutations]; - ref_ = [enumeree_ multiValueRef]; - - } - if (mutations_ != *[enumeree_ mutations]) { - NSString *reason = [NSString stringWithFormat:@"*** Collection <%@> was " - "mutated while being enumerated", enumeree_]; - [[NSException exceptionWithName:NSGenericException - reason:reason - userInfo:nil] raise]; - } - if (index_ < count_) { - if (useLabels_) { - value = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, - index_)); - } else { - // Yes this is slow, but necessary in light of radar 6208390 - // Once this is fixed we can go to something similar to the label - // case which should speed stuff up again. Hopefully anybody who wants - // real performance is willing to move down to the C API anyways. - value = [enumeree_ valueAtIndex:index_]; - } - index_ += 1; - } - return value; -} -@end - diff --git a/iPhone/GTMABAddressBook.strings b/iPhone/GTMABAddressBook.strings Binary files differdeleted file mode 100644 index b5e010e..0000000 --- a/iPhone/GTMABAddressBook.strings +++ /dev/null diff --git a/iPhone/GTMABAddressBookTest.m b/iPhone/GTMABAddressBookTest.m deleted file mode 100644 index 17a3096..0000000 --- a/iPhone/GTMABAddressBookTest.m +++ /dev/null @@ -1,608 +0,0 @@ -// -// GTMAddressBookTest.m -// -// Copyright 2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMSenTestCase.h" -#import "GTMABAddressBook.h" - -@interface GTMABAddressBookTest : GTMTestCase { - @private - GTMABAddressBook *book_; -} -@end - - -@implementation GTMABAddressBookTest -- (void)setUp { - // Create a book forcing it out of it's autorelease pool. - // I force it out of the release pool, so that we will see any errors - // for it immediately at teardown, and it will be clear which release - // caused us problems. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - book_ = [[GTMABAddressBook addressBook] retain]; - [pool release]; - STAssertNotNil(book_, nil); - STAssertFalse([book_ hasUnsavedChanges], nil); -} - -- (void)tearDown { - [book_ release]; -} - -- (void)testGenericAddressBook { - STAssertEqualObjects([GTMABAddressBook localizedLabel:kABHomeLabel], - @"home", - nil); - STAssertThrows([GTMABRecord recordWithRecord:nil], nil); -} - -- (void)testAddingAndRemovingPerson { - // Create a person - GTMABPerson *person = [GTMABPerson personWithFirstName:@"Bart" - lastName:@"Simpson"]; - STAssertNotNil(person, nil); - - // Add person - NSArray *people = [book_ people]; - STAssertFalse([people containsObject:person], nil); - STAssertTrue([book_ addRecord:person], nil); - - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - people = [book_ people]; - STAssertNotNil(people, nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people - // array until it's saved - // We will check to make sure it stays broken ;-) - STAssertFalse([people containsObject:person], nil); - - // Save book_ - STAssertTrue([book_ save], nil); - people = [book_ people]; - STAssertNotNil(people, nil); - STAssertTrue([people containsObject:person], nil); - - ABRecordID recordID = [person recordID]; - STAssertNotEquals(recordID, kABRecordInvalidID, nil); - - GTMABRecord *record = [book_ personForId:recordID]; - STAssertEqualObjects(record, person, nil); - - // Remove person - STAssertTrue([book_ removeRecord:person], nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - // Normally this next line would be STAssertFalse, however due to - // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people - // array until it's saved - // We will check to make sure it stays broken ;-) - STAssertTrue([people containsObject:person], nil); - - // Save Book - STAssertTrue([book_ save], nil); - people = [book_ people]; - STAssertFalse([book_ hasUnsavedChanges], nil); - STAssertFalse([people containsObject:person], nil); - record = [book_ personForId:recordID]; - STAssertNil(record, nil); - - // Revert book_ - STAssertTrue([book_ addRecord:person], nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - [book_ revert]; - STAssertFalse([book_ hasUnsavedChanges], nil); - - // Bogus data - STAssertFalse([book_ addRecord:nil], nil); - STAssertFalse([book_ removeRecord:nil], nil); - - STAssertNotNULL([book_ addressBookRef], nil); - -} - -- (void)testAddingAndRemovingGroup { - // Create a group - GTMABGroup *group = [GTMABGroup groupNamed:@"Test"]; - STAssertNotNil(group, nil); - - // Add group - NSArray *groups = [book_ groups]; - STAssertFalse([groups containsObject:group], nil); - STAssertTrue([book_ addRecord:group], nil); - - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - groups = [book_ groups]; - STAssertNotNil(groups, nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the groups - // array until it's saved - // We will check to make sure it stays broken ;-) - STAssertFalse([groups containsObject:group], nil); - - // Save book_ - STAssertTrue([book_ save], nil); - groups = [book_ groups]; - STAssertNotNil(groups, nil); - STAssertTrue([groups containsObject:group], nil); - - ABRecordID recordID = [group recordID]; - STAssertNotEquals(recordID, kABRecordInvalidID, nil); - - GTMABRecord *record = [book_ groupForId:recordID]; - STAssertEqualObjects(record, group, nil); - - // Remove group - STAssertTrue([book_ removeRecord:group], nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - // Normally this next line would be STAssertFalse, however due to - // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the groups - // array until it's saved - // We will check to make sure it stays broken ;-) - STAssertTrue([groups containsObject:group], nil); - - // Save Book - STAssertTrue([book_ save], nil); - groups = [book_ groups]; - STAssertFalse([book_ hasUnsavedChanges], nil); - STAssertFalse([groups containsObject:group], nil); - record = [book_ groupForId:recordID]; - STAssertNil(record, nil); - - // Revert book_ - STAssertTrue([book_ addRecord:group], nil); - // Normally this next line would be STAssertTrue, however due to - // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work - // We will check to make sure it stays broken ;-) - STAssertFalse([book_ hasUnsavedChanges], nil); - - [book_ revert]; - STAssertFalse([book_ hasUnsavedChanges], nil); -} - -- (void)testPerson { - GTMABPerson *person = [[[GTMABPerson alloc] initWithRecord:nil] autorelease]; - STAssertNil(person, nil); - person = [GTMABPerson personWithFirstName:@"Bart" - lastName:nil]; - STAssertNotNil(person, nil); - STAssertEqualObjects([person compositeName], @"Bart", nil); - NSString *firstName = [person valueForProperty:kABPersonFirstNameProperty]; - STAssertEqualObjects(firstName, @"Bart", nil); - NSString *lastName = [person valueForProperty:kABPersonLastNameProperty]; - STAssertNil(lastName, nil); - STAssertTrue([person removeValueForProperty:kABPersonFirstNameProperty], nil); - STAssertFalse([person removeValueForProperty:kABPersonFirstNameProperty], nil); - STAssertFalse([person removeValueForProperty:kABPersonLastNameProperty], nil); - STAssertFalse([person setValue:nil forProperty:kABPersonFirstNameProperty], nil); - STAssertFalse([person setValue:[NSNumber numberWithInt:1] - forProperty:kABPersonFirstNameProperty], nil); - STAssertFalse([person setValue:@"Bart" - forProperty:kABPersonBirthdayProperty], nil); - - ABPropertyType property - = [GTMABPerson typeOfProperty:kABPersonLastNameProperty]; - STAssertEquals(property, (ABPropertyType)kABStringPropertyType, nil); - - NSString *string - = [GTMABPerson localizedPropertyName:kABPersonLastNameProperty]; - STAssertEqualObjects(string, @"Last", nil); - - string = [GTMABPerson localizedPropertyName:kABRecordInvalidID]; - STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); - - string = [person description]; - STAssertNotNil(string, nil); - - ABPersonCompositeNameFormat format = [GTMABPerson compositeNameFormat]; - STAssertTrue(format == kABPersonCompositeNameFormatFirstNameFirst || - format == kABPersonCompositeNameFormatLastNameFirst, nil); - - NSData *data = [person imageData]; - STAssertNil(data, nil); - STAssertTrue([person setImageData:nil], nil); - data = [person imageData]; - STAssertNil(data, nil); - UIImage *image = [UIImage imageNamed:@"phone.png"]; - STAssertNotNil(image, nil); - data = UIImagePNGRepresentation(image); - STAssertTrue([person setImageData:data], nil); - NSData *data2 = [person imageData]; - STAssertEqualObjects(data, data2, nil); - STAssertTrue([person setImageData:nil], nil); - data = [person imageData]; - STAssertNil(data, nil); - - STAssertTrue([person setImage:image], nil); - UIImage *image2 = [person image]; - STAssertNotNil(image2, nil); - STAssertEqualObjects(UIImagePNGRepresentation(image), - UIImagePNGRepresentation(image2), nil); - - person = [GTMABPerson personWithFirstName:@"Bart" - lastName:@"Simpson"]; - - data = [NSData dataWithBytes:"a" length:1]; - STAssertFalse([person setImageData:data], nil); - - GTMABMutableMultiValue *value - = [GTMABMutableMultiValue valueWithPropertyType:kABStringPropertyType]; - STAssertNotNil(value, nil); - STAssertNotEquals([value addValue:@"222-222-2222" - withLabel:kABHomeLabel], - kABMultiValueInvalidIdentifier, nil); - STAssertNotEquals([value addValue:@"333-333-3333" - withLabel:kABWorkLabel], - kABMultiValueInvalidIdentifier, nil); - STAssertTrue([person setValue:value forProperty:kABPersonPhoneProperty], nil); - id value2 = [person valueForProperty:kABPersonPhoneProperty]; - STAssertNotNil(value2, nil); - STAssertEqualObjects(value, value2, nil); - STAssertEquals([value hash], [value2 hash], nil); - STAssertNotEquals([person hash], (NSUInteger)0, nil); -} - -- (void)testGroup { - GTMABGroup *group = [[[GTMABGroup alloc] initWithRecord:nil] autorelease]; - STAssertNil(group, nil); - group = [GTMABGroup groupNamed:@"TestGroup"]; - STAssertNotNil(group, nil); - STAssertEqualObjects([group compositeName], @"TestGroup", nil); - NSString *name = [group valueForProperty:kABGroupNameProperty]; - STAssertEqualObjects(name, @"TestGroup", nil); - NSString *lastName = [group valueForProperty:kABPersonLastNameProperty]; - STAssertNil(lastName, nil); - STAssertTrue([group removeValueForProperty:kABGroupNameProperty], nil); - STAssertFalse([group removeValueForProperty:kABGroupNameProperty], nil); - STAssertFalse([group removeValueForProperty:kABPersonLastNameProperty], nil); - STAssertFalse([group setValue:nil forProperty:kABGroupNameProperty], nil); - STAssertFalse([group setValue:[NSNumber numberWithInt:1] - forProperty:kABGroupNameProperty], nil); - STAssertFalse([group setValue:@"Bart" - forProperty:kABPersonBirthdayProperty], nil); - - ABPropertyType property = [GTMABGroup typeOfProperty:kABGroupNameProperty]; - STAssertEquals(property, (ABPropertyType)kABStringPropertyType, nil); - - property = [GTMABGroup typeOfProperty:kABPersonLastNameProperty]; - STAssertEquals(property, (ABPropertyType)kABInvalidPropertyType, nil); - - NSString *string = [GTMABGroup localizedPropertyName:kABGroupNameProperty]; - STAssertEqualObjects(string, @"Name", nil); - - string = [GTMABGroup localizedPropertyName:kABPersonLastNameProperty]; - STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); - - string = [GTMABGroup localizedPropertyName:kABRecordInvalidID]; - STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); - - string = [group description]; - STAssertNotNil(string, nil); - - // Adding and removing members - group = [GTMABGroup groupNamed:@"TestGroup2"]; - NSArray *members = [group members]; - STAssertEquals([members count], (NSUInteger)0, @"Members: %@", members); - - STAssertFalse([group addMember:nil], nil); - - members = [group members]; - STAssertEquals([members count], (NSUInteger)0, @"Members: %@", members); - - GTMABPerson *person = [GTMABPerson personWithFirstName:@"Bart" - lastName:@"Simpson"]; - STAssertNotNil(person, nil); - STAssertTrue([book_ addRecord:person], nil); - STAssertTrue([book_ save], nil); - STAssertTrue([group addMember:person], nil); - STAssertTrue([book_ addRecord:group], nil); - STAssertTrue([book_ save], nil); - members = [group members]; - STAssertEquals([members count], (NSUInteger)1, @"Members: %@", members); - STAssertTrue([group removeMember:person], nil); - STAssertFalse([group removeMember:person], nil); - STAssertFalse([group removeMember:nil], nil); - STAssertTrue([book_ removeRecord:group], nil); - STAssertTrue([book_ removeRecord:person], nil); - STAssertTrue([book_ save], nil); -} - - -- (void)testMultiValues { - STAssertThrows([[GTMABMultiValue alloc] init], nil); - STAssertThrows([[GTMABMutableMultiValue alloc] init], nil); - GTMABMultiValue *value = [[GTMABMultiValue alloc] initWithMultiValue:nil]; - STAssertNil(value, nil); - GTMABMutableMultiValue *mutValue - = [GTMABMutableMultiValue valueWithPropertyType:kABInvalidPropertyType]; - STAssertNil(mutValue, nil); - mutValue - = [[[GTMABMutableMultiValue alloc] - initWithMutableMultiValue:nil] autorelease]; - STAssertNil(mutValue, nil); - mutValue - = [[[GTMABMutableMultiValue alloc] - initWithMultiValue:nil] autorelease]; - STAssertNil(mutValue, nil); - const ABPropertyType types[] = { - kABStringPropertyType, - kABIntegerPropertyType, - kABRealPropertyType, - kABDateTimePropertyType, - kABDictionaryPropertyType, - kABMultiStringPropertyType, - kABMultiIntegerPropertyType, - kABMultiRealPropertyType, - kABMultiDateTimePropertyType, - kABMultiDictionaryPropertyType - }; - for (size_t i = 0; i < sizeof(types) / sizeof(ABPropertyType); ++i) { - mutValue = [GTMABMutableMultiValue valueWithPropertyType:types[i]]; - STAssertNotNil(mutValue, nil); - // Oddly the Apple APIs allow you to create a mutable multi value with - // either a property type of kABFooPropertyType or kABMultiFooPropertyType - // and apparently you get back basically the same thing. However if you - // ask a type that you created with kABMultiFooPropertyType for it's type - // it returns just kABFooPropertyType. - STAssertEquals([mutValue propertyType], - types[i] & ~kABMultiValueMask, nil); - } - mutValue = [GTMABMutableMultiValue valueWithPropertyType:kABStringPropertyType]; - STAssertNotNil(mutValue, nil); - value = [[mutValue copy] autorelease]; - STAssertEqualObjects([value class], [GTMABMultiValue class], nil); - mutValue = [[value mutableCopy] autorelease]; - STAssertEqualObjects([mutValue class], [GTMABMutableMultiValue class], nil); - STAssertEquals([mutValue count], (NSUInteger)0, nil); - STAssertNil([mutValue valueAtIndex:0], nil); - STAssertNil([mutValue labelAtIndex:0], nil); - STAssertEquals([mutValue identifierAtIndex:0], - kABMultiValueInvalidIdentifier, nil); - STAssertEquals([mutValue propertyType], - (ABPropertyType)kABStringPropertyType, nil); - ABMultiValueIdentifier ident = [mutValue addValue:nil - withLabel:kABHomeLabel]; - STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); - ident = [mutValue addValue:@"val1" - withLabel:nil]; - STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); - ident = [mutValue insertValue:@"val1" - withLabel:nil - atIndex:0]; - STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); - ident = [mutValue insertValue:nil - withLabel:kABHomeLabel - atIndex:0]; - STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); - ident = [mutValue addValue:@"val1" - withLabel:kABHomeLabel]; - STAssertNotEquals(ident, kABMultiValueInvalidIdentifier, nil); - ABMultiValueIdentifier identCheck = [mutValue identifierAtIndex:0]; - STAssertEquals(ident, identCheck, nil); - NSUInteger idx = [mutValue indexForIdentifier:ident]; - STAssertEquals(idx, (NSUInteger)0, nil); - STAssertTrue([mutValue replaceLabelAtIndex:0 - withLabel:kABWorkLabel], nil); - STAssertFalse([mutValue replaceLabelAtIndex:10 - withLabel:kABWorkLabel], nil); - STAssertTrue([mutValue replaceValueAtIndex:0 - withValue:@"newVal1"], nil); - STAssertFalse([mutValue replaceValueAtIndex:10 - withValue:@"newVal1"], nil); - - STAssertEqualObjects([mutValue valueForIdentifier:ident], @"newVal1", nil); - STAssertEqualObjects([mutValue labelForIdentifier:ident], - (NSString *)kABWorkLabel, nil); - - ABMultiValueIdentifier ident2 = [mutValue insertValue:@"val2" - withLabel:kABOtherLabel - atIndex:0]; - STAssertNotEquals(ident2, kABMultiValueInvalidIdentifier, nil); - STAssertNotEquals(ident2, ident, nil); - ABMultiValueIdentifier ident3 = [mutValue insertValue:@"val3" - withLabel:kABPersonPhoneMainLabel - atIndex:10]; - STAssertEquals(ident3, kABMultiValueInvalidIdentifier, nil); - NSUInteger idx3 = [mutValue indexForIdentifier:ident3]; - STAssertEquals(idx3, (NSUInteger)NSNotFound, nil); - STAssertTrue([mutValue removeValueAndLabelAtIndex:1], nil); - STAssertFalse([mutValue removeValueAndLabelAtIndex:1], nil); - - NSUInteger idx4 - = [mutValue indexForIdentifier:kABMultiValueInvalidIdentifier]; - STAssertEquals(idx4, (NSUInteger)NSNotFound, nil); - - STAssertNotNULL([mutValue multiValueRef], nil); - - // Enumerator test - mutValue = [GTMABMutableMultiValue valueWithPropertyType:kABIntegerPropertyType]; - STAssertNotNil(mutValue, nil); - for (int i = 0; i < 100; i++) { - NSString *label = [NSString stringWithFormat:@"label %d", i]; - NSNumber *val = [NSNumber numberWithInt:i]; - STAssertNotEquals([mutValue addValue:val - withLabel:(CFStringRef)label], - kABMultiValueInvalidIdentifier, nil); - } - int count = 0; - for (NSString *label in [mutValue labelEnumerator]) { - NSString *testLabel = [NSString stringWithFormat:@"label %d", count++]; - STAssertEqualObjects(label, testLabel, nil); - } - count = 0; - value = [[mutValue copy] autorelease]; - for (NSNumber *val in [value valueEnumerator]) { - STAssertEqualObjects(val, [NSNumber numberWithInt:count++], nil); - } - - // Test messing with the values while we're enumerating them - NSEnumerator *labelEnum = [mutValue labelEnumerator]; - NSEnumerator *valueEnum = [mutValue valueEnumerator]; - STAssertNotNil(labelEnum, nil); - STAssertNotNil(valueEnum, nil); - STAssertNotNil([labelEnum nextObject], nil); - STAssertNotNil([valueEnum nextObject], nil); - STAssertTrue([mutValue removeValueAndLabelAtIndex:0], nil); - STAssertThrows([labelEnum nextObject], nil); - STAssertThrows([valueEnum nextObject], nil); - - // Test messing with the values while we're fast enumerating them - // Should throw an exception on the second access. - BOOL exceptionThrown = NO; - // Start at one because we removed index 0 above. - count = 1; - @try { - for (NSString *label in [mutValue labelEnumerator]) { - NSString *testLabel = [NSString stringWithFormat:@"label %d", count++]; - STAssertEqualObjects(label, testLabel, nil); - STAssertTrue([mutValue removeValueAndLabelAtIndex:50], nil); - } - } @catch(NSException *e) { - STAssertEqualObjects([e name], NSGenericException, @"Got %@ instead", e); - STAssertEquals(count, 2, - @"Should have caught it on the second access"); - exceptionThrown = YES; - } // COV_NF_LINE - because we always catch, this brace doesn't get exec'd - STAssertTrue(exceptionThrown, @"We should have thrown an exception" - @" because the values under the enumerator were modified"); - -} - -- (void)testRadar6208390 { - ABPropertyType types[] = { - kABStringPropertyType, - kABIntegerPropertyType, - kABRealPropertyType, - kABDateTimePropertyType, - kABDictionaryPropertyType - }; - for (size_t j = 0; j < sizeof(types) / sizeof(ABPropertyType); ++j) { - ABPropertyType type = types[j]; - ABMultiValueRef ref = ABMultiValueCreateMutable(type); - STAssertNotNULL(ref, nil); - NSString *label = [[NSString alloc] initWithString:@"label"]; - STAssertNotNil(label, nil); - id val = nil; - switch (type) { - case kABDictionaryPropertyType: - val = [[NSDictionary alloc] initWithObjectsAndKeys:@"1", @"1", nil]; - break; - - case kABStringPropertyType: - val = [[NSString alloc] initWithFormat:@"value %d"]; - break; - - case kABIntegerPropertyType: - case kABRealPropertyType: - val = [[NSNumber alloc] initWithInt:143]; - break; - - case kABDateTimePropertyType: - val = [[NSDate alloc] init]; - break; - } - STAssertNotNil(val, - @"Testing type %d, %@", type, val); - NSUInteger firstRetainCount = [val retainCount]; - STAssertNotEquals(firstRetainCount, - (NSUInteger)0, - @"Testing type %d, %@", type, val); - - ABMultiValueIdentifier identifier; - STAssertTrue(ABMultiValueAddValueAndLabel(ref, - val, - (CFStringRef)label, - &identifier), - @"Testing type %d, %@", type, val); - NSUInteger secondRetainCount = [val retainCount]; - STAssertEquals(firstRetainCount + 1, - secondRetainCount, - @"Testing type %d, %@", type, val); - [label release]; - [val release]; - NSUInteger thirdRetainCount = [val retainCount]; - STAssertEquals(firstRetainCount, - thirdRetainCount, - @"Testing type %d, %@", type, val); - - id oldVal = val; - val = (id)ABMultiValueCopyValueAtIndex(ref, 0); - NSUInteger fourthRetainCount = [val retainCount]; - - // kABDictionaryPropertyTypes appear to do an actual copy, so the retain - // count checking trick won't work. We only check the retain count if - // we didn't get a new version. - if (val == oldVal) { - if (type == kABIntegerPropertyType - || type == kABRealPropertyType) { - // We are verifying that yes indeed 6208390 is still broken - STAssertEquals(fourthRetainCount, - thirdRetainCount, - @"Testing type %d, %@. If you see this error it may " - @"be time to update the code to change retain behaviors" - @"with this os version", type, val); - } else { - STAssertEquals(fourthRetainCount, - thirdRetainCount + 1, - @"Testing type %d, %@", type, val); - [val release]; - } - } else { - [val release]; - } - CFRelease(ref); - } -} - -// Globals used by testRadar6240394. -static ABPropertyID gGTMTestID; -static const ABPropertyID *gGTMTestIDPtr; - -void __attribute__((constructor))SetUpIDForTestRadar6240394(void) { - // These must be set up BEFORE ABAddressBookCreate is called. - gGTMTestID = kABPersonLastNameProperty; - gGTMTestIDPtr = &kABPersonLastNameProperty; -} - -- (void)testRadar6240394 { - // As of iPhone SDK 2.1, the property IDs aren't initialized until - // ABAddressBookCreate is actually called. They will return zero until - // then. Logged as radar 6240394. - STAssertEquals(gGTMTestID, 0, @"If this isn't zero, Apple has fixed 6240394"); - (void)ABAddressBookCreate(); - STAssertEquals(*gGTMTestIDPtr, kABPersonLastNameProperty, - @"If this doesn't work, something else has broken"); -} -@end |