/**
 * Do the two texts share a Substring which is at least half the length of
 * the longer text?
 * This speedup can produce non-minimal diffs.
 * @param text1 First CFStringRef.
 * @param text2 Second CFStringRef.
 * @param diffTimeout Time limit for diff.
 * @return Five element String array, containing the prefix of text1, the
 *     suffix of text1, the prefix of text2, the suffix of text2 and the
 *     common middle.   Or NULL if there was no match.
 */
CFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2, const float diffTimeout) {
  if (diffTimeout <= 0) {
    // Don't risk returning a non-optimal diff if we have unlimited time.
    return NULL;
  }
  CFStringRef longtext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text1 : text2;
  CFStringRef shorttext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text2 : text1;
  if (CFStringGetLength(longtext) < 4 || CFStringGetLength(shorttext) * 2 < CFStringGetLength(longtext)) {
    return NULL;  // Pointless.
  }

  // First check if the second quarter is the seed for a half-match.
  CFArrayRef hm1 = diff_halfMatchICreate(longtext, shorttext,
                       (CFStringGetLength(longtext) + 3) / 4);
  // Check again based on the third quarter.
  CFArrayRef hm2 = diff_halfMatchICreate(longtext, shorttext,
                       (CFStringGetLength(longtext) + 1) / 2);
  CFArrayRef hm;
  if (hm1 == NULL && hm2 == NULL) {
    return NULL;
  } else if (hm2 == NULL) {
    hm = CFRetain(hm1);
  } else if (hm1 == NULL) {
    hm = CFRetain(hm2);
  } else {
    // Both matched.  Select the longest.
    hm = CFStringGetLength(CFArrayGetValueAtIndex(hm1, 4)) > CFStringGetLength(CFArrayGetValueAtIndex(hm2, 4)) ? CFRetain(hm1) : CFRetain(hm2);
  }

  if (hm1 != NULL) {
    CFRelease(hm1);
  }
  if (hm2 != NULL) {
    CFRelease(hm2);
  }

  // A half-match was found, sort out the return data.
  if (CFStringGetLength(text1) > CFStringGetLength(text2)) {
    return hm;
    //return new CFStringRef[]{hm[0], hm[1], hm[2], hm[3], hm[4]};
  } else {
    //    { hm[0], hm[1], hm[2], hm[3], hm[4] }
    // => { hm[2], hm[3], hm[0], hm[1], hm[4] }

    CFMutableArrayRef hm_mutable = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(hm), hm);

    CFRelease(hm);

    CFArrayExchangeValuesAtIndices(hm_mutable, 0, 2);
    CFArrayExchangeValuesAtIndices(hm_mutable, 1, 3);
    return hm_mutable;
  }
}
/*
 * Obtain an array of the certificates in a timestamp response. Elements of the
 * returned array are SecCertificateRefs. The caller must CFRelease the returned
 * array. This timestamp is an authenticated timestamp provided by
 * a timestamping authority.
 *
 * Returns errSecParam if the CMS message was not signed or if signerIndex
 * is greater than the number of signers of the message minus one. It returns
 * errSecItemNotFound if no certificates were found.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called. 
 */
OSStatus CMSDecoderCopySignerTimestampCertificates(
                                                   CMSDecoderRef		cmsDecoder,
                                                   size_t				signerIndex,            /* usually 0 */
                                                   CFArrayRef          *certificateRefs)       /* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg = NULL;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    CFIndex tsn = 0;
    bool good = false;
    
    require(cmsDecoder && certificateRefs, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit);
    numContentInfos = SecCmsMessageContentLevelCount(cmsg);
    for (int dex = 0; !signedData && dex < numContentInfos; dex++)
    {
        SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
        SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
        if (tag == SEC_OID_PKCS7_SIGNED_DATA)
            if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
                if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
                {
                    CFArrayRef certList = SecCmsSignerInfoGetTimestampCertList(signerInfo);
                    require_action(certList, xit, status = errSecItemNotFound);
                    CFMutableArrayRef certs = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(certList), certList);
                    
                    if(certs){
                        //reorder certificates:
                        tsn = CFArrayGetCount(certs);
                        good = tsn > 0 && Security::CodeSigning::isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(certs, tsn-1)));
                        
                        if ( good == false )
                        {
                            //change TS certificate ordering.
                            for (CFIndex n = 0; n < tsn; n++)
                            {
                                if (SecCertificateRef tsRoot = SecCertificateRef(CFArrayGetValueAtIndex(certs, n)))
                                    if ((good = Security::CodeSigning::isAppleCA(tsRoot))) {
                                        CFArrayExchangeValuesAtIndices(certs, n, tsn-1);
                                        break;
                                    }
                            }
                        }
                        
                        *certificateRefs = CFArrayCreateCopy(kCFAllocatorDefault, certs);
                        CFRelease(certs);
                        status = errSecSuccess;
                    }
                    break;
                }
    }
    
    
    xit:
        return status;
    }
CF_INLINE void __CFZSort(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) {
    CFIndex cnt = range.length;
    while (1 < cnt) {
	for (CFIndex idx = range.location; idx < range.location + cnt - 1; idx++) {
            const void *a = CFArrayGetValueAtIndex(array, idx);
            const void *b = CFArrayGetValueAtIndex(array, idx + 1);
            if ((CFComparisonResult)(INVOKE_CALLBACK3(comparator, b, a, context)) < 0) {
                CFArrayExchangeValuesAtIndices(array, idx, idx + 1);
            }
	}
	cnt--;
    }
}