<commit_before>//
//  PackageResourceCache.m
//  SDKLauncher-iOS
//
//  Created by <NAME> on 3/8/13.
// Modified by <NAME>
//  Copyright (c) 2012-2013 The Readium Foundation.
//

#import "PackageResourceCache.h"
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonHMAC.h>
#import "RDPackageResource.h"

static NSString *m_basePath = nil;
static NSData *m_key = nil;

@interface PackageResourceCache()

- (NSString *)absolutePathForRelativePath:(NSString *)relativePath;
- (NSData *)sha1:(NSData *)data;

+ (void)trim: (int)maxFilesToKeep;

@end


@implementation PackageResourceCache


//
// Returns the cache path for the given package-relative path.
//
- (NSString *)absolutePathForRelativePath:(NSString *)relativePath {
	NSData *data = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
	NSString *fileName = hexStringFromData([self sha1:data]);
	NSString *ext = relativePath.pathExtension;

	if (ext != nil && ext.length > 0) {
		// Preserve the file extension since the consumer of this resource might care.
		fileName = [fileName stringByAppendingPathExtension:ext];
	}

	return [m_basePath stringByAppendingPathComponent:fileName];
}


//
// Adds the given resource to the cache as well as encrypts it.
//
- (void)addResource:(RDPackageResource *)resource {
	if (resource == nil) {
		NSLog(@"The resource is nil!");
		return;
	}

	NSString *relativePath = resource.relativePath;

	if (relativePath == nil || relativePath.length == 0) {
		NSLog(@"The relative path is missing!");
		return;
	}

    if (m_debugAssetStream)
    {
        NSLog(@"ADD RES: %@", relativePath);

        if (!m_skipCrypt)
        {
            NSLog(@"ENCRYPT");
        }

    }
    
	[PackageResourceCache trim: 3];

	NSString *path = [self absolutePathForRelativePath:relativePath];
	NSFileManager *fm = [NSFileManager defaultManager];
	[fm removeItemAtPath:path error:nil];
	[fm createDirectoryAtPath:m_basePath withIntermediateDirectories:YES attributes:nil
		error:nil];

	NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
	[stream open];

	size_t bufferSize = kSDKLauncherPackageResourceBufferSize + kCCBlockSizeAES128;
	UInt8 buffer[bufferSize];

	while (YES) {
		NSData *chunk = [resource createNextChunkByReading];

		if (chunk == nil) {
			break;
		}

        if (m_skipCrypt)
        {
            [stream write:(const uint8_t*)[chunk bytes] maxLength:[chunk length]];
        }
        else
        {
            size_t numBytesEncrypted = 0;

            CCCryptorStatus cryptStatus = CCCrypt(
                    kCCEncrypt,
                    kCCAlgorithmAES128,
                    kCCOptionPKCS7Padding,
                    m_key.bytes,
                    m_key.length,
                    NULL,
                    chunk.bytes,
                    chunk.length,
                    buffer,
                    bufferSize,
                    &numBytesEncrypted);

            if (cryptStatus == kCCSuccess) {
                [stream write:buffer maxLength:numBytesEncrypted];
            }
            else {
                NSLog(@"Encryption failed!");
            }
        }

        [chunk release];
	}

	[stream close];
}


//
// Returns the content length of the resource at the given relative path.  Since the content
// is encrypted, we need to do some math as well as decrypt the final chunk to discover the
// true content length.
//
- (int)contentLengthAtRelativePath:(NSString *)relativePath {
	int contentLength = 0;

	if (relativePath == nil || relativePath.length == 0) {
		NSLog(@"The relative path is missing!");
		return 0;
	}

	NSString *path = [self absolutePathForRelativePath:relativePath];
	NSFileManager *fm = [NSFileManager defaultManager];
	NSDictionary *attributes = [fm attributesOfItemAtPath:path error:nil];

	if (attributes != nil) {
		int fileSize = attributes.fileSize;

		if (fileSize == 0) {
			return 0;
		}

        if (m_skipCrypt)
        {
            return fileSize;
        }

		int bufferSize = kSDKLauncherPackageResourceBufferSize + kCCBlockSizeAES128;
		int chunkCount = (fileSize - 1) / bufferSize;
		contentLength = chunkCount * kSDKLauncherPackageResourceBufferSize;

		@autoreleasepool {
			NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
            [handle seekToFileOffset:chunkCount * bufferSize];
			NSData *data = [handle readDataToEndOfFile];

			if (data == nil || data.length == 0) {
				NSLog(@"Could not read the last chunk of data!");
				contentLength = 0;
			}
			else {
                UInt8 buffer[bufferSize];
                size_t numBytesDecrypted = 0;

                CCCryptorStatus cryptStatus = CCCrypt(<commit_msg>minor<commit_after>//
//  PackageResourceCache.m
//  SDKLauncher-iOS
//
//  Created by <NAME> on 3/8/13.
// Modified by <NAME>
//  Copyright (c) 2012-2013 The Readium Foundation.
//

#import "PackageResourceCache.h"
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonHMAC.h>
#import "RDPackageResource.h"

static NSString *m_basePath = nil;
static NSData *m_key = nil;

@interface PackageResourceCache()

- (NSString *)absolutePathForRelativePath:(NSString *)relativePath;
- (NSData *)sha1:(NSData *)data;

+ (void)trim: (int)maxFilesToKeep;

@end


@implementation PackageResourceCache


//
// Returns the cache path for the given package-relative path.
//
- (NSString *)absolutePathForRelativePath:(NSString *)relativePath {
	NSData *data = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
	NSString *fileName = hexStringFromData([self sha1:data]);
	NSString *ext = relativePath.pathExtension;

	if (ext != nil && ext.length > 0) {
		// Preserve the file extension since the consumer of this resource might care.
		fileName = [fileName stringByAppendingPathExtension:ext];
	}

	return [m_basePath stringByAppendingPathComponent:fileName];
}


//
// Adds the given resource to the cache as well as encrypts it.
//
- (void)addResource:(RDPackageResource *)resource {
	if (resource == nil) {
		NSLog(@"The resource is nil!");
		return;
	}

	NSString *relativePath = resource.relativePath;

	if (relativePath == nil || relativePath.length == 0) {
		NSLog(@"The relative path is missing!");
		return;
	}

    if (m_debugAssetStream)
    {
        NSLog(@"ADD RES: %@", relativePath);

        if (!m_skipCrypt)
        {
            NSLog(@"ENCRYPT");
        }

    }
    
	[PackageResourceCache trim: 3];

	NSString *path = [self absolutePathForRelativePath:relativePath];
	NSFileManager *fm = [NSFileManager defaultManager];
	[fm removeItemAtPath:path error:nil];
	[fm createDirectoryAtPath:m_basePath withIntermediateDirectories:YES attributes:nil
		error:nil];

	NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
	[stream open];

	size_t bufferSize = kSDKLauncherPackageResourceBufferSize + kCCBlockSizeAES128;
	UInt8 buffer[bufferSize];

	while (YES) {
		NSData *chunk = [resource createNextChunkByReading];

		if (chunk == nil) {
			break;
		}

        if (m_skipCrypt)
        {
            [stream write:(const uint8_t*)[chunk bytes] maxLength:[chunk length]];
        }
        else
        {
            size_t numBytesEncrypted = 0;

            CCCryptorStatus cryptStatus = CCCrypt(
                    kCCEncrypt,
                    kCCAlgorithmAES128,
                    kCCOptionPKCS7Padding,
                    m_key.bytes,
                    m_key.length,
                    NULL,
                    chunk.bytes,
                    chunk.length,
                    buffer,
                    bufferSize,
                    &numBytesEncrypted);

            if (cryptStatus == kCCSuccess) {
                [stream write:buffer maxLength:numBytesEncrypted];
            }
            else {
                NSLog(@"Encryption failed!");
            }
        }

        [chunk release];
	}

	[stream close];
}


//
// Returns the content length of the resource at the given relative path.  Since the content
// is encrypted, we need to do some math as well as decrypt the final chunk to discover the
// true content length.
//
- (int)contentLengthAtRelativePath:(NSString *)relativePath {
	int contentLength = 0;

	if (relativePath == nil || relativePath.length == 0) {
		NSLog(@"The relative path is missing!");
		return 0;
	}

	NSString *path = [self absolutePathForRelativePath:relativePath];
	NSFileManager *fm = [NSFileManager defaultManager];
	NSDictionary *attributes = [fm attributesOfItemAtPath:path error:nil];

	if (attributes != nil) {
		int fileSize = attributes.fileSize;

		if (fileSize == 0) {
			return 0;
		}

        if (m_skipCrypt)
        {
            return fileSize;
        }

        if (m_debugAssetStream)
        {
            NSLog(@"DECRYPT for content length");
        }

		int bufferSize = kSDKLauncherPackageResourceBufferSize + kCCBlockSizeAES128;
		int chunkCount = (fileSize - 1) / bufferSize;
		contentLength = chunkCount * kSDKLauncherPackageResourceBufferSize;

		@autoreleasepool {
			NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
            [handle seekToFileOffset:chunkCount * bufferSize];
			NSData *data = [handle readDataToEndOfFile];

			if (data == nil || data.length == 0) {
				NSLog(@"Could not read the last chunk of data!");
				contentLength = 0;
			}
			else {
                UInt8 buffer[bufferSize];
                size_t numBytesDecrypted = 0;

                CCCryptorStatus cryptStatus = CCCrypt(
{'fraction_non_alphanumeric': 0.10381422717242829, 'fraction_numerical': 0.00861616055479668, 'mean_word_length': 2.795055821371611, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6594411', 'n_tokens_mistral': 3051, 'n_tokens_neox': 2720, 'n_words': 701}