You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1484 lines
32 KiB

5 years ago
5 years ago
  1. /*
  2. ** NSData+Extensions.m
  3. **
  4. ** Copyright (c) 2001-2007
  5. ** 2014
  6. **
  7. ** Author: Ludovic Marcotte <ludovic@Sophos.ca>
  8. ** Riccardo Mottola <rm@gnu.org>
  9. **
  10. ** This library is free software; you can redistribute it and/or
  11. ** modify it under the terms of the GNU Lesser General Public
  12. ** License as published by the Free Software Foundation; either
  13. ** version 2.1 of the License, or (at your option) any later version.
  14. **
  15. ** This library is distributed in the hope that it will be useful,
  16. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. ** Lesser General Public License for more details.
  19. **
  20. ** You should have received a copy of the GNU Lesser General Public
  21. ** License along with this library; if not, write to the Free Software
  22. ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23. */
  24. #import "NSData+Extensions.h"
  25. #import <Foundation/NSArray.h>
  26. #import <Foundation/NSException.h>
  27. #import <Foundation/NSString.h>
  28. #import "CWConstants.h"
  29. #import <stdlib.h>
  30. #import <string.h>
  31. //
  32. // C functions and constants
  33. //
  34. int getValue(char c);
  35. void nb64ChunkFor3Characters(char *buf, const char *inBuf, NSUInteger numChars);
  36. static const char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  37. static const char *hexDigit = "0123456789ABCDEF";
  38. //!
  39. // add an NSData cluster member NSSubrangeData that retaind its parent and
  40. // used its data. Would make almost all of these operations work without
  41. // copying.
  42. @implementation NSData (PantomimeExtensions)
  43. //
  44. //! this should ignore characters in the stream that aren't in
  45. // the base64 alphabet (as per the spec). would remove need for
  46. // ...removeLinefeeds... too
  47. //
  48. - (NSData *) decodeBase64
  49. {
  50. NSUInteger i, j, rawIndex, block, pad, data_len;
  51. NSInteger length;
  52. const unsigned char *bytes;
  53. char *raw;
  54. if ([self length] == 0)
  55. {
  56. return [NSData data];
  57. }
  58. data_len = [self length];
  59. bytes = [self bytes];
  60. pad = 0;
  61. for (i = data_len - 1; bytes[i] == '='; i--)
  62. {
  63. pad++;
  64. }
  65. length = data_len * 6 / 8 - pad;
  66. raw = (char *)malloc(length);
  67. rawIndex = 0;
  68. for (i = 0; i < data_len; i += 4)
  69. {
  70. block = (getValue(bytes[i]) << 18) +
  71. (getValue(bytes[i+1]) << 12) +
  72. (getValue(bytes[i+2]) << 6) +
  73. (getValue(bytes[i+3]));
  74. for (j = 0; j < 3 && rawIndex+j < length; j++)
  75. {
  76. raw[rawIndex+j] = (char)((block >> (8 * (2 - j))) & 0xff);
  77. }
  78. rawIndex += 3;
  79. }
  80. // This could happen for broken encoded base64 content such as the following example:
  81. // ------=_NextPart_KO_X1098V29876N91O412QM815
  82. // Content-Type: text/plain; charset=UTF-8
  83. // Content-Disposition: attachment; filename="teste.txt"
  84. // Content-Transfer-Encoding: base64
  85. //
  86. // ====
  87. //
  88. if (length < 0)
  89. {
  90. free(raw);
  91. return [NSData data];
  92. }
  93. return AUTORELEASE([[NSData alloc] initWithBytesNoCopy: raw length: length]);
  94. }
  95. //
  96. //
  97. //
  98. - (NSData *) encodeBase64WithLineLength: (NSUInteger) theLength
  99. {
  100. const char *inBytes = [self bytes];
  101. const char *inBytesPtr = inBytes;
  102. NSInteger inLength = [self length];
  103. char *outBytes = malloc(sizeof(char)*inLength*2);
  104. char *outBytesPtr = outBytes;
  105. NSUInteger numWordsPerLine = theLength/4;
  106. NSUInteger wordCounter = 0;
  107. // We memset 0 our buffer so with are sure to not have
  108. // any garbage in it.
  109. memset(outBytes, 0, sizeof(char)*inLength*2);
  110. while (inLength > 0)
  111. {
  112. nb64ChunkFor3Characters(outBytesPtr, inBytesPtr, inLength);
  113. outBytesPtr += 4;
  114. inBytesPtr += 3;
  115. inLength -= 3;
  116. wordCounter ++;
  117. if (theLength && wordCounter == numWordsPerLine)
  118. {
  119. wordCounter = 0;
  120. *outBytesPtr = '\n';
  121. outBytesPtr++;
  122. }
  123. }
  124. return AUTORELEASE([[NSData alloc] initWithBytesNoCopy: outBytes length: (outBytesPtr-outBytes)]);
  125. }
  126. //
  127. //
  128. //
  129. - (NSData *) unfoldLines
  130. {
  131. NSMutableData *aMutableData;
  132. NSUInteger i, length;
  133. const unsigned char *bytes, *b;
  134. length = [self length];
  135. b = bytes = [self bytes];
  136. aMutableData = [[NSMutableData alloc] initWithCapacity: length];
  137. [aMutableData appendBytes: b length: 1];
  138. b++;
  139. for (i = 1; i < length; i++,b++)
  140. {
  141. if (b[-1]=='\n' && (*b==' ' || *b=='\t'))
  142. {
  143. [aMutableData setLength: ([aMutableData length] - 1)];
  144. }
  145. [aMutableData appendBytes: b length: 1];
  146. }
  147. return AUTORELEASE(aMutableData);
  148. }
  149. - (NSData *)decodeQuotedPrintableInHeader:(BOOL)aBOOL
  150. {
  151. NSUInteger len = [self length];
  152. const unsigned char *bytes = [self bytes];
  153. unsigned char ch = 0;
  154. const unsigned char *b = bytes;
  155. NSMutableData *result = [[NSMutableData alloc] initWithCapacity:len];
  156. NSUInteger i = 0;
  157. for (i = 0; i < len; i++,b++) {
  158. if (b[0] == '=' && i + 1 == len) {
  159. // Trailing '=', ignore.
  160. // Example: "Let=E2=80=99s see.="
  161. break;
  162. } else if (b[0] == '=' && i + 1 < len && b[1] == '\n') {
  163. b++;
  164. i++;
  165. continue;
  166. } else if (*b == '=' && i + 2 < len) {
  167. b++;
  168. i++;
  169. if (*b >= 'A' && *b <= 'F') {
  170. ch = 16 * (*b - 'A' + 10);
  171. } else if (*b >= 'a' && *b <= 'f') {
  172. ch = 16 * (*b - 'a' + 10);
  173. } else if (*b >= '0' && *b <= '9') {
  174. ch = 16 * (*b - '0');
  175. } else {
  176. // The encoding is invalid (Hex data contained invalid char).
  177. // Nothing we can do.
  178. return nil;
  179. }
  180. b++;
  181. i++;
  182. if (*b >= 'A' && *b <= 'F') {
  183. ch += *b - 'A' + 10;
  184. } else if (*b >= 'a' && *b <= 'f') {
  185. ch += *b - 'a' + 10;
  186. } else if (*b >= '0' && *b <= '9') {
  187. ch += *b - '0';
  188. } else {
  189. // The encoding is invalid (Hex data contained invalid char).
  190. // Nothing we can do.
  191. return nil;
  192. }
  193. [result appendBytes:&ch length:1];
  194. } else if (aBOOL && *b == '_') {
  195. ch = 0x20;
  196. [result appendBytes:&ch length:1];
  197. } else {
  198. [result appendBytes:b length:1];
  199. }
  200. }
  201. return result;
  202. }
  203. //
  204. //
  205. //
  206. - (NSData *) encodeQuotedPrintableWithLineLength: (NSUInteger) theLength
  207. inHeader: (BOOL) aBOOL
  208. {
  209. NSMutableData *aMutableData;
  210. const unsigned char *b;
  211. NSUInteger i, length, line;
  212. char buf[4];
  213. aMutableData = [[NSMutableData alloc] initWithCapacity: [self length]];
  214. b = [self bytes];
  215. length = [self length];
  216. buf[3] = 0;
  217. buf[0] = '=';
  218. line = 0;
  219. for (i = 0; i < length; i++, b++)
  220. {
  221. if (theLength && line >= theLength)
  222. {
  223. [aMutableData appendBytes: "=\n" length: 2];
  224. line = 0;
  225. }
  226. // RFC says must encode space and tab right before end of line
  227. if ( (*b == ' ' || *b == '\t') && i < length - 1 && b[1] == '\n')
  228. {
  229. buf[1] = hexDigit[(*b)>>4];
  230. buf[2] = hexDigit[(*b)&15];
  231. [aMutableData appendBytes: buf
  232. length: 3];
  233. line += 3;
  234. }
  235. //! really always pass \n through here?
  236. else if (!aBOOL &&
  237. (*b == '\n' || *b == ' ' || *b == '\t'
  238. || (*b >= 33 && *b <= 60)
  239. || (*b >= 62 && *b <= 126)))
  240. {
  241. [aMutableData appendBytes: b length: 1];
  242. if (*b == '\n')
  243. {
  244. line = 0;
  245. }
  246. else
  247. {
  248. line++;
  249. }
  250. }
  251. else if (aBOOL && ((*b >= 'a' && *b <= 'z') || (*b >= 'A' && *b <= 'Z')))
  252. {
  253. [aMutableData appendBytes: b length: 1];
  254. if (*b == '\n')
  255. {
  256. line = 0;
  257. }
  258. else
  259. {
  260. line++;
  261. }
  262. }
  263. else if (aBOOL && *b == ' ')
  264. {
  265. [aMutableData appendBytes: "_" length: 1];
  266. }
  267. else
  268. {
  269. buf[1] = hexDigit[(*b)>>4];
  270. buf[2] = hexDigit[(*b)&15];
  271. [aMutableData appendBytes: buf length: 3];
  272. line += 3;
  273. }
  274. }
  275. return AUTORELEASE(aMutableData);
  276. }
  277. - (NSRange)rangeOfData:(NSData *)needle
  278. {
  279. return [self rangeOfData:needle options:0 range:[self fullRange]];
  280. }
  281. - (NSRange)fullRange;
  282. {
  283. return NSMakeRange(0, self.length);
  284. }
  285. //
  286. //
  287. //
  288. - (NSRange) rangeOfCString: (const char *) theCString
  289. {
  290. return [self rangeOfCString: theCString
  291. options: 0
  292. range: NSMakeRange(0,[self length])];
  293. }
  294. //
  295. //
  296. //
  297. -(NSRange) rangeOfCString: (const char *) theCString
  298. options: (NSUInteger) theOptions
  299. {
  300. return [self rangeOfCString: theCString
  301. options: theOptions
  302. range: NSMakeRange(0,[self length])];
  303. }
  304. //
  305. //
  306. //
  307. -(NSRange) rangeOfCString: (const char *) theCString
  308. options: (NSUInteger) theOptions
  309. range: (NSRange) theRange
  310. {
  311. const char *b, *bytes;
  312. NSUInteger i, len, slen;
  313. bytes = [self bytes];
  314. len = [self length];
  315. if (!bytes || !theCString || len == 0 || theCString[0] == '\0' ||
  316. theRange.length == 0) {
  317. return NSMakeRange(NSNotFound,0);
  318. }
  319. slen = strlen(theCString);
  320. if (theRange.length < slen) {
  321. return NSMakeRange(NSNotFound,0);
  322. }
  323. b = bytes;
  324. if (len > theRange.location + theRange.length)
  325. {
  326. len = theRange.location + theRange.length;
  327. }
  328. //! this could be optimized
  329. if (theOptions & NSCaseInsensitiveSearch)
  330. {
  331. i = theRange.location;
  332. b += i;
  333. for (; i <= len-slen; i++, b++)
  334. {
  335. if (!strncasecmp(theCString,b,slen))
  336. {
  337. return NSMakeRange(i,slen);
  338. }
  339. }
  340. }
  341. else
  342. {
  343. i = theRange.location;
  344. b += i;
  345. for (; i <= len-slen; i++, b++)
  346. {
  347. if (!memcmp(theCString,b,slen))
  348. {
  349. return NSMakeRange(i,slen);
  350. }
  351. }
  352. }
  353. return NSMakeRange(NSNotFound,0);
  354. }
  355. //
  356. //
  357. //
  358. - (NSData *) subdataFromIndex: (NSUInteger) theIndex
  359. {
  360. return [self subdataWithRange: NSMakeRange(theIndex, [self length] - theIndex)];
  361. }
  362. //
  363. //
  364. //
  365. - (NSData *) subdataToIndex: (NSUInteger) theIndex
  366. {
  367. return [self subdataWithRange: NSMakeRange(0, theIndex)];
  368. }
  369. - (NSData *)subdataUncopiedWithRange:(NSRange)range
  370. {
  371. const char *theBytes = [self bytes];
  372. const char *newBytes = theBytes + range.location;
  373. return [NSData dataWithBytesNoCopy:(void *) newBytes length:range.length];
  374. }
  375. - (NSData *)dataByTrimmingWhiteSpaces
  376. {
  377. const char *bytes = [self bytes];
  378. NSUInteger len = [self length];
  379. long i = 0;
  380. for (i = 0; i < len && (bytes[i] == ' ' || bytes[i] == '\t'); i++);
  381. long j = 0;
  382. for (j = len - 1; j >= 0 && (bytes[j] == ' ' || bytes[j] == '\t'); j--);
  383. if (j < i) {
  384. return [NSData new];
  385. }
  386. return [self subdataWithRange: NSMakeRange(i, j - i + 1)];
  387. }
  388. //
  389. //
  390. //
  391. - (NSData *) dataByRemovingLineFeedCharacters
  392. {
  393. NSMutableData *aMutableData;
  394. const char *bytes;
  395. NSUInteger i, j, len;
  396. char *dest;
  397. bytes = [self bytes];
  398. len = [self length];
  399. aMutableData = [[NSMutableData alloc] init];
  400. [aMutableData setLength: len];
  401. dest = [aMutableData mutableBytes];
  402. for (i = j = 0; i < len; i++)
  403. {
  404. if (bytes[i] != '\n')
  405. {
  406. dest[j++] = bytes[i];
  407. }
  408. }
  409. [aMutableData setLength: j];
  410. return AUTORELEASE(aMutableData);
  411. }
  412. //
  413. //
  414. //
  415. - (NSData *) dataFromQuotedData
  416. {
  417. const char *bytes;
  418. NSUInteger len;
  419. bytes = [self bytes];
  420. len = [self length];
  421. if (len < 2)
  422. {
  423. return AUTORELEASE(RETAIN(self));
  424. }
  425. if (bytes[0] == '"' && bytes[len-1] == '"')
  426. {
  427. return [self subdataWithRange: NSMakeRange(1, len-2)];
  428. }
  429. return AUTORELEASE(RETAIN(self));
  430. }
  431. //
  432. //
  433. //
  434. - (NSData *) dataFromSemicolonTerminatedData
  435. {
  436. const char *bytes;
  437. NSUInteger len;
  438. bytes = [self bytes];
  439. len = [self length];
  440. if (len < 2)
  441. {
  442. return AUTORELEASE(RETAIN(self));
  443. }
  444. if (bytes[len-1] == ';')
  445. {
  446. return [self subdataToIndex: len-1];
  447. }
  448. return AUTORELEASE(RETAIN(self));
  449. }
  450. //
  451. //
  452. //
  453. - (NSInteger) indexOfCharacter: (char) theCharacter
  454. {
  455. const char *b;
  456. NSUInteger i, len;
  457. b = [self bytes];
  458. len = [self length];
  459. for ( i = 0; i < len; i++, b++)
  460. if (*b == theCharacter)
  461. {
  462. return i;
  463. }
  464. return -1;
  465. }
  466. //
  467. //
  468. //
  469. - (BOOL) hasCPrefix: (const char *) theCString
  470. {
  471. const char *bytes;
  472. NSUInteger len, slen;
  473. if (!theCString)
  474. {
  475. return NO;
  476. }
  477. bytes = [self bytes];
  478. len = [self length];
  479. slen = strlen(theCString);
  480. if ( slen > len)
  481. {
  482. return NO;
  483. }
  484. if (!strncmp(bytes,theCString,slen))
  485. {
  486. return YES;
  487. }
  488. return NO;
  489. }
  490. //
  491. //
  492. //
  493. - (BOOL) hasCSuffix: (const char *) theCString
  494. {
  495. const char *bytes;
  496. NSUInteger len, slen;
  497. if (!theCString)
  498. {
  499. return NO;
  500. }
  501. bytes = [self bytes];
  502. len = [self length];
  503. slen = strlen(theCString);
  504. if (slen > len)
  505. {
  506. return NO;
  507. }
  508. if (!strncmp(&bytes[len-slen],theCString,slen))
  509. {
  510. return YES;
  511. }
  512. return NO;
  513. }
  514. //
  515. //
  516. //
  517. - (BOOL) hasCaseInsensitiveCPrefix: (const char *) theCString
  518. {
  519. const char *bytes;
  520. NSUInteger len, slen;
  521. if (!theCString)
  522. {
  523. return NO;
  524. }
  525. bytes = [self bytes];
  526. len = [self length];
  527. slen = strlen(theCString);
  528. if ( slen > len)
  529. {
  530. return NO;
  531. }
  532. if ( !strncasecmp(bytes,theCString,slen) )
  533. {
  534. return YES;
  535. }
  536. return NO;
  537. }
  538. //
  539. //
  540. //
  541. - (BOOL) hasCaseInsensitiveCSuffix: (const char *) theCString
  542. {
  543. const char *bytes;
  544. NSUInteger len, slen;
  545. if (!theCString)
  546. {
  547. return NO;
  548. }
  549. bytes = [self bytes];
  550. len = [self length];
  551. slen = strlen(theCString);
  552. if (slen > len)
  553. {
  554. return NO;
  555. }
  556. if (!strncasecmp(&bytes[len-slen],theCString,slen))
  557. {
  558. return YES;
  559. }
  560. return NO;
  561. }
  562. //
  563. //
  564. //
  565. - (NSComparisonResult) caseInsensitiveCCompare: (const char *) theCString
  566. {
  567. NSUInteger slen, len, clen;
  568. NSInteger i;
  569. const char *bytes;
  570. // Is this ok?
  571. if (!theCString)
  572. {
  573. return NSOrderedDescending;
  574. }
  575. bytes = [self bytes];
  576. len = [self length];
  577. slen = strlen(theCString);
  578. if (slen > len)
  579. {
  580. clen = len;
  581. }
  582. else
  583. {
  584. clen = slen;
  585. }
  586. i = strncasecmp(bytes,theCString,clen);
  587. if (i < 0)
  588. {
  589. return NSOrderedAscending;
  590. }
  591. if (i > 0)
  592. {
  593. return NSOrderedDescending;
  594. }
  595. if (slen == len)
  596. {
  597. return NSOrderedSame;
  598. }
  599. if (slen < len)
  600. {
  601. return NSOrderedAscending;
  602. }
  603. return NSOrderedDescending;
  604. }
  605. - (NSArray *) componentsSeparatedByCString: (const char *) theCString
  606. {
  607. NSMutableArray *aMutableArray;
  608. NSRange r1, r2;
  609. NSUInteger len;
  610. aMutableArray = [[NSMutableArray alloc] init];
  611. len = [self length];
  612. r1 = NSMakeRange(0,len);
  613. r2 = [self rangeOfCString: theCString
  614. options: 0
  615. range: r1];
  616. while (r2.length)
  617. {
  618. [aMutableArray addObject: [self subdataWithRange: NSMakeRange(r1.location, r2.location - r1.location)]];
  619. r1.location = r2.location + r2.length;
  620. r1.length = len - r1.location;
  621. r2 = [self rangeOfCString: theCString options: 0 range: r1];
  622. }
  623. [aMutableArray addObject: [self subdataWithRange: NSMakeRange(r1.location, len - r1.location)]];
  624. return AUTORELEASE(aMutableArray);
  625. }
  626. //
  627. //
  628. //
  629. - (NSString *) asciiString
  630. {
  631. return AUTORELEASE([[NSString alloc] initWithData: self encoding: NSASCIIStringEncoding]);
  632. }
  633. //
  634. //
  635. //
  636. - (NSString *) imapUtf7String
  637. {
  638. NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF7_IMAP);
  639. return AUTORELEASE([[NSString alloc] initWithData: self encoding: encoding]);
  640. }
  641. //
  642. //
  643. //
  644. - (const char *) cString
  645. {
  646. NSMutableData *aMutableData;
  647. aMutableData = [[NSMutableData alloc] init];
  648. AUTORELEASE_VOID(aMutableData);
  649. [aMutableData appendData: self];
  650. [aMutableData appendBytes: "\0" length: 1];
  651. return [aMutableData mutableBytes];
  652. }
  653. //
  654. //
  655. //
  656. - (unichar) characterAtIndex: (NSUInteger) theIndex
  657. {
  658. const char *bytes;
  659. NSUInteger i, len;
  660. len = [self length];
  661. if (len == 0 || theIndex >= len)
  662. {
  663. [[NSException exceptionWithName: NSRangeException
  664. reason: @"Index out of range."
  665. userInfo: nil] raise];
  666. return (unichar)0;
  667. }
  668. bytes = [self bytes];
  669. for (i = 0; i < theIndex; i++)
  670. {
  671. bytes++;
  672. }
  673. return (unichar)*bytes;
  674. }
  675. //
  676. //
  677. //
  678. - (NSData *) unwrap
  679. {
  680. NSData *result = self;
  681. if (self.length >= 2 &&
  682. [self characterAtIndex: 0] == '<' && [self characterAtIndex: self.length - 1] == '>') {
  683. NSRange rangeWithoutWrappers = NSMakeRange(1, self.length - 2);
  684. result = [self subdataWithRange:rangeWithoutWrappers];
  685. }
  686. return result;
  687. }
  688. //
  689. //
  690. //
  691. - (NSData *) unwrapWithLimit: (NSUInteger) theQuoteLimit
  692. {
  693. NSMutableData *aMutableData, *lines;
  694. NSData *aLine;
  695. NSUInteger i, len, quote_depth, line_quote_depth, line_start;
  696. BOOL is_flowed;
  697. len = [self length];
  698. aMutableData = [[NSMutableData alloc] init];//WithCapacity: len];
  699. lines = [[NSMutableData alloc] init];
  700. quote_depth = -1;
  701. // We analyse the string until the last character
  702. for (i = 0; i < len;)
  703. {
  704. // We analyse the quote depth of the current line
  705. if ([self characterAtIndex: i] == '>')
  706. {
  707. for (line_quote_depth = 0; i < len && [self characterAtIndex: i] == '>'; i++)
  708. {
  709. line_quote_depth++;
  710. }
  711. }
  712. else
  713. {
  714. line_quote_depth = 0;
  715. }
  716. // If the current quote depth is not defined, set it to quote depth of current line
  717. if (quote_depth == -1)
  718. {
  719. quote_depth = line_quote_depth;
  720. }
  721. // We verify if the line has been space-stuffed
  722. if (i < len && [self characterAtIndex: i] == ' ')
  723. {
  724. i++;
  725. }
  726. line_start = i;
  727. // We look for the next line break
  728. for (; i < len && [self characterAtIndex: i] != '\n'; i++);
  729. // We get the actual content of the current line
  730. aLine = [self subdataWithRange: NSMakeRange(line_start, i-line_start)];
  731. // We verify if the line ends with a soft break
  732. is_flowed = [aLine length] > 0 && [aLine characterAtIndex: [aLine length]-1] == ' ';
  733. // We must handle usenet signature as a special case
  734. if (is_flowed && [aLine caseInsensitiveCCompare: "-- "] == NSOrderedSame)
  735. {
  736. is_flowed = NO;
  737. }
  738. if (is_flowed && quote_depth == line_quote_depth)
  739. {
  740. // The current line is flowed;
  741. // we append it to the buffer without quote characters
  742. [lines appendData: aLine];
  743. }
  744. else if (is_flowed)
  745. {
  746. // The current line is flowed but has mis-matched quoting
  747. // We first append the previous paragraph to the buffer with the necessary quote characters
  748. if (quote_depth)
  749. {
  750. [lines replaceBytesInRange: NSMakeRange(0, [lines length])
  751. withBytes: [[lines quoteWithLevel: quote_depth wrappingLimit: theQuoteLimit] bytes]];
  752. }
  753. [aMutableData appendData: lines];
  754. [aMutableData appendCString: "\n"];
  755. // We initialize the current paragraph with the current line
  756. [lines replaceBytesInRange: NSMakeRange(0, [lines length]) withBytes: [aLine bytes]];
  757. // We set the paragraph depth with the current line depth
  758. quote_depth = line_quote_depth;
  759. }
  760. else if (!is_flowed && quote_depth == line_quote_depth)
  761. {
  762. // The line is fixed, we append it.
  763. [lines appendData: aLine];
  764. // We add the necessary quote characters in the paragraph
  765. if (quote_depth)
  766. {
  767. NSData *d;
  768. d = [lines quoteWithLevel: quote_depth wrappingLimit: theQuoteLimit];
  769. [lines replaceBytesInRange: NSMakeRange(0, [lines length])
  770. withBytes: [d bytes] length: [d length]];
  771. }
  772. // We append the paragraph (if any)
  773. if ([lines length])
  774. {
  775. [aMutableData appendData: lines];
  776. }
  777. [aMutableData appendCString: "\n"];
  778. // We empty the paragraph buffer
  779. [lines replaceBytesInRange: NSMakeRange(0,[lines length])
  780. withBytes: NULL
  781. length: 0];
  782. // We reset the paragraph depth
  783. quote_depth = -1;
  784. }
  785. else
  786. {
  787. // The line is fixed but has mis-matched quoting
  788. // We first append the previous paragraph (if any) to the buffer with the necessary quote characters
  789. if (quote_depth)
  790. {
  791. [lines replaceBytesInRange: NSMakeRange(0, [lines length])
  792. withBytes: [[lines quoteWithLevel: quote_depth wrappingLimit: theQuoteLimit] bytes]];
  793. }
  794. [aMutableData appendData: lines];
  795. [aMutableData appendCString: "\n"];
  796. // We append the fixed line to the buffer with the necessary quote characters
  797. if (line_quote_depth)
  798. {
  799. aLine = [aLine quoteWithLevel: line_quote_depth wrappingLimit: theQuoteLimit];
  800. }
  801. [aMutableData appendData: aLine];
  802. [aMutableData appendCString: "\n"];
  803. // We empty the paragraph buffer
  804. [lines replaceBytesInRange: NSMakeRange(0,[lines length])
  805. withBytes: NULL
  806. length: 0];
  807. // We reset the paragraph depth
  808. quote_depth = -1;
  809. }
  810. // The next iteration must starts after the line break
  811. i++;
  812. }
  813. // We must handle flowed lines that don't have a fixed line break at the end of the message
  814. if ([lines length])
  815. {
  816. if (quote_depth)
  817. {
  818. [lines replaceBytesInRange: NSMakeRange(0, [lines length])
  819. withBytes: [[lines quoteWithLevel: quote_depth wrappingLimit: theQuoteLimit] bytes]];
  820. }
  821. [aMutableData appendData: lines];
  822. [aMutableData appendCString: "\n"];
  823. }
  824. DESTROY(lines);
  825. return AUTORELEASE(aMutableData);
  826. }
  827. //
  828. //
  829. //
  830. - (NSData *) wrapWithLimit: (NSUInteger) theLimit
  831. {
  832. NSMutableData *aMutableData;
  833. NSData *aLine, *part;
  834. NSArray *lines;
  835. NSUInteger i, j, k, split;
  836. int depth;
  837. // We first verify if the string is valid
  838. if ([self length] == 0)
  839. {
  840. return [NSData data];
  841. }
  842. // We then verify if the limit is valid
  843. if (theLimit == 0 || theLimit > 998)
  844. {
  845. theLimit = 998;
  846. }
  847. // We initialize our local variables
  848. aMutableData = [[NSMutableData alloc] init];//WithCapacity: [self length]];
  849. lines = [self componentsSeparatedByCString: "\n"];
  850. // We analyse each line
  851. for (i = 0; i < [lines count]; i++)
  852. {
  853. aLine = [lines objectAtIndex: i];
  854. // We compute the quote depth
  855. for (depth = 0; depth < [aLine length] && [aLine characterAtIndex: depth] == '>'; depth++);
  856. j = depth;
  857. // We remove the leading whitespace if any
  858. if (depth && [aLine length] > j && [aLine characterAtIndex: j] == 32)
  859. {
  860. j++;
  861. }
  862. aLine = [aLine subdataFromIndex: j];
  863. // If the line is NOT the signature separator, we remove the trailing space(s)
  864. if ([aLine caseInsensitiveCCompare: "-- "] != NSOrderedSame)
  865. {
  866. for (j = [aLine length]; j > 0 && [aLine characterAtIndex: j-1] == 32; j--);
  867. if (depth && j < [aLine length])
  868. {
  869. // If line is quoted, we preserve a whitespace for the soft-break
  870. j++;
  871. }
  872. aLine = [aLine subdataToIndex: j];
  873. }
  874. // If the line is the signature separator or if the line length with the
  875. // quote characters and the space-stuffing is lower than the limit,
  876. // we directly append the line to the buffer
  877. if ([aLine caseInsensitiveCCompare: "-- "] == NSOrderedSame || depth+1+[aLine length] <= theLimit)
  878. {
  879. // We add the quote characters
  880. for (j = 0; j < depth; j++)
  881. {
  882. [aMutableData appendCString: ">"];
  883. }
  884. // We space-stuff the line if necessary. The conditions are:
  885. // - the line is quoted or
  886. // - the line starts with a quote character or
  887. // - the line starts with a whitespace or
  888. // - the line starts with the word From.
  889. if (depth ||
  890. ([aLine length] && ([aLine characterAtIndex: 0] == '>' || [aLine characterAtIndex: 0] == ' ' || [aLine hasCPrefix: "From"])))
  891. {
  892. [aMutableData appendCString: " "];
  893. }
  894. // We append the line to the buffer
  895. [aMutableData appendData: aLine];
  896. [aMutableData appendCString: "\n"];
  897. // We jump to the next line
  898. continue;
  899. }
  900. // We look for the right place to split the line
  901. for (j = 0; j < [aLine length];)
  902. {
  903. // We verify if the line after character j has a length lower than the limit
  904. if ([aLine length] - j + depth + 1 < theLimit)
  905. {
  906. split = [aLine length];
  907. }
  908. // No it hasn't
  909. else
  910. {
  911. split = j;
  912. // We search for the last whitespace before the limit
  913. for (k = j; k < [aLine length] && k - j + depth + 1 < theLimit; k++)
  914. {
  915. if ([aLine characterAtIndex: k] == 32)
  916. {
  917. split = k;
  918. }
  919. }
  920. /*
  921. No good spot; include the entire next word. This isn't really
  922. optimal, but the alternative is to split the word, and that
  923. would be horribly ugly. Also, it'd mean that deeply quoted
  924. text might appear with one letter on each row, which is even
  925. uglier and means that the receiver won't be able to
  926. reconstruct the text.
  927. A proper fix would be to have both parameters for a 'soft'
  928. line limit that we _try_ to break before, and a 'hard' line
  929. limit that specifies an actual hard limit of a protocol or
  930. something. In NNTP, the values would be 72 and 998
  931. respectively. This means that text quoted 70 levels (and yes,
  932. I have seen such posts) will appear with one unbroken word on
  933. each line (as long as the word is shorter than 928
  934. characters). This is still ugly, but:
  935. a. invalid (protocol-wise) lines will never be generated
  936. (unless something's quoted >998 levels)
  937. b. a MIME decoder that handles format=flowed will be able to
  938. reconstruct the text properly
  939. (Additionally, it might turn out to be useful to have a lower
  940. limit on wrapping length, eg. 20. If the effective line
  941. length is shorter than this, wrap to quote-depth+soft-limit
  942. (so eg. text quoted 60 times would be wrapped at 60+72
  943. characters instead of 72). This wouldn't make any difference
  944. on flowed capable MIME decoders, but might turn out to look
  945. better when viewed with non-flowed handling programs.
  946. Hopefully, such deeply quoted text won't be common enough to
  947. be worth the trouble, so people with non-flowed capable
  948. software will simply have to live with the ugly posts in
  949. those cases.)
  950. */
  951. if (split == j)
  952. {
  953. // No whitespace found before the limit;
  954. // continue farther until a whitespace or the last character of the line
  955. for (; k < [aLine length] && [aLine characterAtIndex: k] != 32; k++);
  956. split = k;
  957. }
  958. }
  959. // Since the line will be splitted, we must keep a whitespace for
  960. // the soft-line break
  961. if (split < [aLine length])
  962. {
  963. split++;
  964. }
  965. // Retrieve splitted part of line
  966. part = [aLine subdataWithRange: NSMakeRange(j, split - j)];
  967. // We add the quote characters
  968. for (k = 0; k < depth; k++)
  969. {
  970. [aMutableData appendCString: ">"];
  971. }
  972. // We space-stuff the line if necesary.
  973. if (depth ||
  974. ([part length] && ([part characterAtIndex: 0] == '>' || [part characterAtIndex: 0] == ' ' || [part hasCPrefix: "From"])))
  975. {
  976. [aMutableData appendCString: " "];
  977. }
  978. // Append line part to buffer
  979. [aMutableData appendData: part];
  980. [aMutableData appendCString: "\n"];
  981. // Next iteration continues where current split occured
  982. j = split;
  983. }
  984. }
  985. if (i > 0)
  986. {
  987. [aMutableData replaceBytesInRange: NSMakeRange([aMutableData length]-1, 1) withBytes: NULL length: 0];
  988. }
  989. return AUTORELEASE(aMutableData);
  990. }
  991. //
  992. //
  993. //
  994. - (NSData *) quoteWithLevel: (NSUInteger) theLevel
  995. wrappingLimit: (NSUInteger) theLimit
  996. {
  997. NSMutableData *aMutableData, *aQuotePrefix;
  998. NSData *aData, *aLine;
  999. NSArray *lines;
  1000. BOOL isQuoted;
  1001. int i;
  1002. // We verify if the wrapping limit is smaller then the quote level
  1003. if (theLevel > theLimit)
  1004. {
  1005. return [NSData data];
  1006. }
  1007. aMutableData = [[NSMutableData alloc] initWithCapacity: [self length]];
  1008. aQuotePrefix = [[NSMutableData alloc] initWithCapacity: theLevel];
  1009. // We wrap the string to the proper limit
  1010. aData = [self wrapWithLimit: (theLimit - theLevel)];
  1011. lines = [aData componentsSeparatedByCString: "\n"];
  1012. // We prepare the line prefix
  1013. for (i = 0; i < theLevel; i++)
  1014. {
  1015. [aQuotePrefix appendCString: ">"];
  1016. }
  1017. // We add the line prefix to each wrapped line
  1018. for (i = 0; i < [lines count]; i++)
  1019. {
  1020. aLine = [lines objectAtIndex: i];
  1021. isQuoted = ([aLine length] > 0 && [aLine characterAtIndex: 0] == '>');
  1022. [aMutableData appendData: aQuotePrefix];
  1023. if (!isQuoted)
  1024. {
  1025. [aMutableData appendCString: " "];
  1026. }
  1027. [aMutableData appendData: aLine];
  1028. [aMutableData appendCString: "\n"];
  1029. }
  1030. if (i > 0)
  1031. {
  1032. [aMutableData replaceBytesInRange: NSMakeRange([aMutableData length]-1, 1) withBytes: NULL length: 0];
  1033. }
  1034. RELEASE(aQuotePrefix);
  1035. return AUTORELEASE(aMutableData);
  1036. }
  1037. @end
  1038. //
  1039. //
  1040. //
  1041. @implementation NSMutableData (PantomimeExtensions)
  1042. - (void) appendCFormat: (NSString *) theFormat, ...
  1043. {
  1044. NSString *aString;
  1045. va_list args;
  1046. va_start(args, theFormat);
  1047. aString = [[NSString alloc] initWithFormat: theFormat arguments: args];
  1048. va_end(args);
  1049. // We allow lossy conversion to not lose any information / raise an exception
  1050. [self appendData: [aString dataUsingEncoding: NSASCIIStringEncoding allowLossyConversion: YES]];
  1051. RELEASE(aString);
  1052. }
  1053. //
  1054. //
  1055. //
  1056. - (void) appendCString: (const char *) theCString
  1057. {
  1058. [self appendBytes: theCString length: strlen(theCString)];
  1059. }
  1060. //
  1061. //
  1062. //
  1063. - (void) insertCString: (const char *) theCString
  1064. atIndex: (NSUInteger) theIndex
  1065. {
  1066. NSUInteger s_length, length;
  1067. if (!theCString)
  1068. {
  1069. return;
  1070. }
  1071. s_length = strlen(theCString);
  1072. if (s_length == 0)
  1073. {
  1074. return;
  1075. }
  1076. length = [self length];
  1077. // We insert at the beginning of the data
  1078. if (theIndex <= 0)
  1079. {
  1080. NSMutableData *data;
  1081. data = [NSMutableData dataWithBytes: theCString length: s_length];
  1082. [data appendData: self];
  1083. [self setData: data];
  1084. }
  1085. // We insert at the end of the data
  1086. else if (theIndex >= length)
  1087. {
  1088. [self appendCString: theCString];
  1089. }
  1090. // We insert somewhere in the middle
  1091. else
  1092. {
  1093. NSMutableData *data;
  1094. NSData *subData = [self subdataWithRange: NSMakeRange(0, theIndex)];
  1095. data = [NSMutableData dataWithBytes:subData.bytes length: theIndex];
  1096. [data appendCString: theCString];
  1097. [data appendData: [self subdataWithRange: NSMakeRange(theIndex, length - theIndex)]];
  1098. [self setData: data];
  1099. }
  1100. }
  1101. //
  1102. //
  1103. //
  1104. - (void) replaceCRLFWithLF
  1105. {
  1106. unsigned char *bytes, *bi, *bo;
  1107. NSUInteger delta, i,length;
  1108. bytes = [self mutableBytes];
  1109. length = [self length];
  1110. bi = bo = bytes;
  1111. for (i = delta = 0; i < length; i++, bi++)
  1112. {
  1113. if (i+1 < length && bi[0] == '\r' && bi[1] == '\n')
  1114. {
  1115. i++;
  1116. bi++;
  1117. delta++;
  1118. }
  1119. *bo = *bi;
  1120. bo++;
  1121. }
  1122. [self setLength: length-delta];
  1123. }
  1124. //
  1125. //
  1126. //
  1127. - (NSMutableData *) replaceLFWithCRLF
  1128. {
  1129. NSMutableData *aMutableData;
  1130. unsigned char *bytes, *bi, *bo;
  1131. NSUInteger delta, i, length;
  1132. bi = bytes = [self mutableBytes];
  1133. length = [self length];
  1134. delta = 0;
  1135. if (bi[0] == '\n')
  1136. {
  1137. delta++;
  1138. }
  1139. bi++;
  1140. for (i = 1; i < length; i++, bi++)
  1141. {
  1142. if ((bi[0] == '\n') && (bi[-1] != '\r'))
  1143. {
  1144. delta++;
  1145. }
  1146. }
  1147. bi = bytes;
  1148. aMutableData = [[NSMutableData alloc] initWithLength: (length+delta)];
  1149. bo = [aMutableData mutableBytes];
  1150. for (i = 0; i < length; i++, bi++, bo++)
  1151. {
  1152. if ((i+1 < length) && (bi[0] == '\r') && (bi[1] == '\n'))
  1153. {
  1154. *bo = *bi;
  1155. bo++;
  1156. bi++;
  1157. i++;
  1158. }
  1159. else if (*bi == '\n')
  1160. {
  1161. *bo = '\r';
  1162. bo++;
  1163. }
  1164. *bo = *bi;
  1165. }
  1166. return AUTORELEASE(aMutableData);
  1167. }
  1168. @end
  1169. //
  1170. // C functions
  1171. //
  1172. int getValue(char c)
  1173. {
  1174. if (c >= 'A' && c <= 'Z') return (c - 'A');
  1175. if (c >= 'a' && c <= 'z') return (c - 'a' + 26);
  1176. if (c >= '0' && c <= '9') return (c - '0' + 52);
  1177. if (c == '+') return 62;
  1178. if (c == '/') return 63;
  1179. if (c == '=') return 0;
  1180. return -1;
  1181. }
  1182. //
  1183. //
  1184. //
  1185. void nb64ChunkFor3Characters(char *buf, const char *inBuf, NSUInteger theLength)
  1186. {
  1187. if (theLength >= 3)
  1188. {
  1189. buf[0] = basis_64[inBuf[0]>>2 & 0x3F];
  1190. buf[1] = basis_64[(((inBuf[0] & 0x3)<< 4) | ((inBuf[1] & 0xF0) >> 4)) & 0x3F];
  1191. buf[2] = basis_64[(((inBuf[1] & 0xF) << 2) | ((inBuf[2] & 0xC0) >>6)) & 0x3F];
  1192. buf[3] = basis_64[inBuf[2] & 0x3F];
  1193. }
  1194. else if(theLength == 2)
  1195. {
  1196. buf[0] = basis_64[inBuf[0]>>2 & 0x3F];
  1197. buf[1] = basis_64[(((inBuf[0] & 0x3)<< 4) | ((inBuf[1] & 0xF0) >> 4)) & 0x3F];
  1198. buf[2] = basis_64[(((inBuf[1] & 0xF) << 2) | ((0 & 0xC0) >>6)) & 0x3F];
  1199. buf[3] = '=';
  1200. }
  1201. else
  1202. {
  1203. buf[0] = basis_64[inBuf[0]>>2 & 0x3F];
  1204. buf[1] = basis_64[(((inBuf[0] & 0x3)<< 4) | ((0 & 0xF0) >> 4)) & 0x3F];
  1205. buf[2] = '=';
  1206. buf[3] = '=';
  1207. }
  1208. }