PPOTString.m 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. //
  2. // PPOTString.m
  3. // Copyright © 2015 PayPal, Inc. All rights reserved.
  4. //
  5. #import "PPOTString.h"
  6. #import "PPOTMacros.h"
  7. #import <CommonCrypto/CommonDigest.h>
  8. static const short _base64DecodingTable[256] = {
  9. -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
  10. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  11. -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
  12. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
  13. -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  14. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
  15. -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  16. 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
  17. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  18. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  19. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  20. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  21. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  22. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  23. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
  24. -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
  25. };
  26. @implementation PPOTString
  27. + (NSString *)stringByURLEncodingAllCharactersInString:(NSString *)aString {
  28. NSString *reservedCharacters = @"&()<>@,;:\\\"/[]?=+$|^~`{}";
  29. NSMutableCharacterSet *URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
  30. [URLQueryPartAllowedCharacterSet removeCharactersInString:reservedCharacters];
  31. return [aString stringByAddingPercentEncodingWithAllowedCharacters:URLQueryPartAllowedCharacterSet];
  32. }
  33. // This base 64 encoding adapted from Colloquy's BSD-licensed Chat Core library
  34. static char base64encodingTable[64] = {
  35. 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
  36. 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
  37. 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
  38. 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
  39. };
  40. + (NSString *)stringByBase64EncodingData:(NSData *)data {
  41. return [self stringByBase64EncodingData:data lineLength:0];
  42. }
  43. + (NSString *)stringByBase64EncodingData:(NSData *)data lineLength:(NSUInteger)lineLength {
  44. const unsigned char *bytes = [data bytes];
  45. NSMutableString *result = [NSMutableString stringWithCapacity:[data length]];
  46. unsigned long ixtext = 0;
  47. unsigned long lentext = [data length];
  48. long ctremaining = 0;
  49. unsigned char inbuf[3], outbuf[4];
  50. unsigned short i = 0;
  51. unsigned short charsonline = 0, ctcopy = 0;
  52. unsigned long ix = 0;
  53. while( YES ) {
  54. ctremaining = lentext - ixtext;
  55. if( ctremaining <= 0 ) break;
  56. for( i = 0; i < 3; i++ ) {
  57. ix = ixtext + i;
  58. if( ix < lentext ) inbuf[i] = bytes[ix];
  59. else inbuf [i] = 0;
  60. }
  61. outbuf [0] = (unsigned char)((inbuf [0] & 0xFC) >> 2);
  62. outbuf [1] = (unsigned char)(((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4));
  63. outbuf [2] = (unsigned char)(((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6));
  64. outbuf [3] = inbuf [2] & 0x3F;
  65. ctcopy = 4;
  66. switch( ctremaining ) {
  67. case 1:
  68. ctcopy = 2;
  69. break;
  70. case 2:
  71. ctcopy = 3;
  72. break;
  73. }
  74. for( i = 0; i < ctcopy; i++ )
  75. [result appendFormat:@"%c", base64encodingTable[outbuf[i]]];
  76. for( i = ctcopy; i < 4; i++ )
  77. [result appendString:@"="];
  78. ixtext += 3;
  79. charsonline += 4;
  80. if( lineLength > 0 ) {
  81. if( charsonline >= lineLength ) {
  82. charsonline = 0;
  83. [result appendString:@"\n"];
  84. }
  85. }
  86. }
  87. return [NSString stringWithString:result];
  88. }
  89. + (NSData *)decodeBase64WithString:(NSString *)strBase64 {
  90. const char * objPointer = [strBase64 cStringUsingEncoding:NSASCIIStringEncoding];
  91. if (objPointer == NULL) return nil;
  92. size_t intLength = strlen(objPointer);
  93. int intCurrent;
  94. int i = 0, j = 0, k;
  95. unsigned char * objResult;
  96. objResult = calloc(intLength, sizeof(unsigned char));
  97. // Run through the whole string, converting as we go
  98. while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) {
  99. if (intCurrent == '=') {
  100. if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) {
  101. // the padding character is invalid at this point -- so this entire string is invalid
  102. free(objResult);
  103. return nil;
  104. }
  105. continue;
  106. }
  107. intCurrent = _base64DecodingTable[intCurrent];
  108. if (intCurrent == -1) {
  109. // we're at a whitespace -- simply skip over
  110. continue;
  111. } else if (intCurrent == -2) {
  112. // we're at an invalid character
  113. free(objResult);
  114. return nil;
  115. }
  116. switch (i % 4) {
  117. case 0:
  118. objResult[j] = (unsigned char)(intCurrent << 2);
  119. break;
  120. case 1:
  121. objResult[j++] |= (unsigned char)(intCurrent >> 4);
  122. objResult[j] = (unsigned char)((intCurrent & 0x0f) << 4);
  123. break;
  124. case 2:
  125. objResult[j++] |= (unsigned char)(intCurrent >>2);
  126. objResult[j] = (unsigned char)((intCurrent & 0x03) << 6);
  127. break;
  128. case 3:
  129. objResult[j++] |= (unsigned char)intCurrent;
  130. break;
  131. }
  132. i++;
  133. }
  134. // mop things up if we ended on a boundary
  135. k = j;
  136. if (intCurrent == '=') {
  137. switch (i % 4) {
  138. case 1:
  139. // Invalid state
  140. free(objResult);
  141. return nil;
  142. case 2:
  143. k++;
  144. // flow through
  145. case 3:
  146. objResult[k] = 0;
  147. }
  148. }
  149. // Cleanup and setup the return NSData
  150. return [[NSData alloc] initWithBytesNoCopy:objResult length:j freeWhenDone:YES];
  151. }
  152. + (NSUInteger)numberOfLinesInString:(NSString *)str {
  153. // probably not the most efficient implementation (see e.g. https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html)
  154. // ...but very obviously correct :)
  155. __block NSUInteger nLines = 0;
  156. [str enumerateLinesUsingBlock:^(__attribute__((unused)) NSString *line, BOOL *stop) {
  157. nLines++;
  158. *stop = NO;
  159. }];
  160. return nLines;
  161. }
  162. + (NSString *)generateUniquishIdentifier {
  163. CFUUIDRef uuid = CFUUIDCreate(NULL);
  164. CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);
  165. CFRelease(uuid);
  166. NSString *result = (__bridge_transfer NSString *)uuidStr;
  167. return result;
  168. }
  169. #pragma mark - Hex String
  170. + (NSString *)hexStringFromData:(NSData *)data {
  171. NSMutableString *hexString = [NSMutableString string];
  172. const char* bytes = [data bytes];
  173. for (NSUInteger index = 0; index < data.length; index++) {
  174. [hexString appendFormat:@"%02hhX", bytes[index]];
  175. }
  176. return hexString;
  177. }
  178. + (NSData *)dataWithHexString:(NSString *)hexString {
  179. //
  180. // NSData+HexString.m
  181. // libsecurity_transform
  182. //
  183. // Copyright (c) 2011 Apple, Inc. All rights reserved.
  184. //
  185. char buf[3];
  186. buf[2] = '\0';
  187. NSAssert(0 == [hexString length] % 2, @"Hex strings should have an even number of digits (%@)", hexString);
  188. unsigned char *bytes = malloc([hexString length]/2);
  189. unsigned char *bp = bytes;
  190. for (NSUInteger i = 0; i < [hexString length]; i += 2) {
  191. buf[0] = [hexString characterAtIndex:i];
  192. buf[1] = [hexString characterAtIndex:i+1];
  193. char *b2 = NULL;
  194. *bp++ = strtol(buf, &b2, 16);
  195. //NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %ld)", hexString, i);
  196. }
  197. return [NSData dataWithBytesNoCopy:bytes length:[hexString length]/2 freeWhenDone:YES];
  198. }
  199. @end