From 21d3023a9b7b6aff68c1170e345951396b1c6cfd Mon Sep 17 00:00:00 2001
From: 单军华
Date: Tue, 31 Jul 2018 13:35:21 +0800
Subject: [PATCH] no message

---
 screendisplay/Pods/ASIHTTPRequest/Classes/ASIWebPageRequest/ASIWebPageRequest.m |  722 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 722 insertions(+), 0 deletions(-)

diff --git a/screendisplay/Pods/ASIHTTPRequest/Classes/ASIWebPageRequest/ASIWebPageRequest.m b/screendisplay/Pods/ASIHTTPRequest/Classes/ASIWebPageRequest/ASIWebPageRequest.m
new file mode 100755
index 0000000..eccac6a
--- /dev/null
+++ b/screendisplay/Pods/ASIHTTPRequest/Classes/ASIWebPageRequest/ASIWebPageRequest.m
@@ -0,0 +1,722 @@
+//
+//  ASIWebPageRequest.m
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
+//
+//  Created by Ben Copsey on 29/06/2010.
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
+//
+//  This is an EXPERIMENTAL class - use at your own risk!
+
+#import "ASIWebPageRequest.h"
+#import "ASINetworkQueue.h"
+#import <CommonCrypto/CommonHMAC.h>
+#import <libxml/HTMLparser.h>
+#import <libxml/xmlsave.h>
+#import <libxml/xpath.h>
+#import <libxml/xpathInternals.h>
+
+// An xPath query that controls the external resources ASIWebPageRequest will fetch
+// By default, it will fetch stylesheets, javascript files, images, frames, iframes, and html 5 video / audio
+static xmlChar *xpathExpr = (xmlChar *)"//link/@href|//a/@href|//script/@src|//img/@src|//frame/@src|//iframe/@src|//style|//*/@style|//source/@src|//video/@poster|//audio/@src";
+
+static NSLock *xmlParsingLock = nil;
+static NSMutableArray *requestsUsingXMLParser = nil;
+
+@interface ASIWebPageRequest ()
+- (void)readResourceURLs;
+- (void)updateResourceURLs;
+- (void)parseAsHTML;
+- (void)parseAsCSS;
+- (void)addURLToFetch:(NSString *)newURL;
++ (NSArray *)CSSURLsFromString:(NSString *)string;
+- (NSString *)relativePathTo:(NSString *)destinationPath fromPath:(NSString *)sourcePath;
+
+- (void)finishedFetchingExternalResources:(ASINetworkQueue *)queue;
+- (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest;
+- (void)externalResourceFetchFailed:(ASIHTTPRequest *)externalResourceRequest;
+
+@property (retain, nonatomic) ASINetworkQueue *externalResourceQueue;
+@property (retain, nonatomic) NSMutableDictionary *resourceList;
+@end
+
+@implementation ASIWebPageRequest
+
++ (void)initialize
+{
+	if (self == [ASIWebPageRequest class]) {
+		xmlParsingLock = [[NSLock alloc] init];
+		requestsUsingXMLParser = [[NSMutableArray alloc] init];
+	}
+}
+
+- (id)initWithURL:(NSURL *)newURL
+{
+	self = [super initWithURL:newURL];
+	[self setShouldIgnoreExternalResourceErrors:YES];
+	return self;
+}
+
+- (void)dealloc
+{
+	[externalResourceQueue cancelAllOperations];
+	[externalResourceQueue release];
+	[resourceList release];
+	[parentRequest release];
+	[super dealloc];
+}
+
+// This is a bit of a hack
+// The role of this method in normal ASIHTTPRequests is to tell the queue we are done with the request, and perform some cleanup
+// We override it to stop that happening, and instead do that work in the bottom of finishedFetchingExternalResources:
+- (void)markAsFinished
+{
+	if ([self error]) {
+		[super markAsFinished];
+	}
+}
+
+// This method is normally responsible for telling delegates we are done, but it happens to be the most convenient place to parse the responses
+// Again, we call the super implementation in finishedFetchingExternalResources:, or here if this download was not an HTML or CSS file
+- (void)requestFinished
+{
+	complete = NO;
+	if ([self mainRequest] || [self didUseCachedResponse]) {
+		[super requestFinished];
+		[super markAsFinished];
+		return;
+	}
+	webContentType = ASINotParsedWebContentType;
+	NSString *contentType = [[[self responseHeaders] objectForKey:@"Content-Type"] lowercaseString];
+	contentType = [[contentType componentsSeparatedByString:@";"] objectAtIndex:0];
+	if ([contentType isEqualToString:@"text/html"] || [contentType isEqualToString:@"text/xhtml"] || [contentType isEqualToString:@"text/xhtml+xml"] || [contentType isEqualToString:@"application/xhtml+xml"]) {
+		[self parseAsHTML];
+		return;
+	} else if ([contentType isEqualToString:@"text/css"]) {
+		[self parseAsCSS];
+		return;
+	}
+	[super requestFinished];
+	[super markAsFinished];
+}
+
+- (void)parseAsCSS
+{
+	webContentType = ASICSSWebContentType;
+
+	NSString *responseCSS = nil;
+	NSError *err = nil;
+	if ([self downloadDestinationPath]) {
+		responseCSS = [NSString stringWithContentsOfFile:[self downloadDestinationPath] encoding:[self responseEncoding] error:&err];
+	} else {
+		responseCSS = [self responseString];
+	}
+	if (err) {
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:100 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to read HTML string from response",NSLocalizedDescriptionKey,err,NSUnderlyingErrorKey,nil]]];
+		return;
+	} else if (!responseCSS) {
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:100 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to read HTML string from response",NSLocalizedDescriptionKey,nil]]];
+		return;
+	}
+	NSArray *urls = [[self class] CSSURLsFromString:responseCSS];
+
+	[self setResourceList:[NSMutableDictionary dictionary]];
+
+	for (NSString *theURL in urls) {
+		[self addURLToFetch:theURL];
+	}
+	if (![[self resourceList] count]) {
+		[super requestFinished];
+		[super markAsFinished];
+		return;
+	}
+
+	// Create a new request for every item in the queue
+	[[self externalResourceQueue] cancelAllOperations];
+	[self setExternalResourceQueue:[ASINetworkQueue queue]];
+	[[self externalResourceQueue] setDelegate:self];
+	[[self externalResourceQueue] setShouldCancelAllRequestsOnFailure:[self shouldIgnoreExternalResourceErrors]];
+	[[self externalResourceQueue] setShowAccurateProgress:[self showAccurateProgress]];
+	[[self externalResourceQueue] setQueueDidFinishSelector:@selector(finishedFetchingExternalResources:)];
+	[[self externalResourceQueue] setRequestDidFinishSelector:@selector(externalResourceFetchSucceeded:)];
+	[[self externalResourceQueue] setRequestDidFailSelector:@selector(externalResourceFetchFailed:)];
+	for (NSString *theURL in [[self resourceList] keyEnumerator]) {
+		ASIWebPageRequest *externalResourceRequest = [ASIWebPageRequest requestWithURL:[NSURL URLWithString:theURL relativeToURL:[self url]]];
+		[externalResourceRequest setRequestHeaders:[self requestHeaders]];
+		[externalResourceRequest setDownloadCache:[self downloadCache]];
+		[externalResourceRequest setCachePolicy:[self cachePolicy]];
+		[externalResourceRequest setCacheStoragePolicy:[self cacheStoragePolicy]];
+		[externalResourceRequest setUserInfo:[NSDictionary dictionaryWithObject:theURL forKey:@"Path"]];
+		[externalResourceRequest setParentRequest:self];
+		[externalResourceRequest setUrlReplacementMode:[self urlReplacementMode]];
+		[externalResourceRequest setShouldResetDownloadProgress:NO];
+		[externalResourceRequest setDelegate:self];
+		[externalResourceRequest setUploadProgressDelegate:self];
+		[externalResourceRequest setDownloadProgressDelegate:self];
+		if ([self downloadDestinationPath]) {
+			[externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]];
+		}
+		[[self externalResourceQueue] addOperation:externalResourceRequest];
+	}
+	[[self externalResourceQueue] go];
+}
+
+- (const char *)encodingName
+{
+	xmlCharEncoding encoding = XML_CHAR_ENCODING_NONE;
+	switch ([self responseEncoding])
+	{
+		case NSASCIIStringEncoding:
+			encoding = XML_CHAR_ENCODING_ASCII;
+			break;
+		case NSJapaneseEUCStringEncoding:
+			encoding = XML_CHAR_ENCODING_EUC_JP;
+			break;
+		case NSUTF8StringEncoding:
+			encoding = XML_CHAR_ENCODING_UTF8;
+			break;
+		case NSISOLatin1StringEncoding:
+			encoding = XML_CHAR_ENCODING_8859_1;
+			break;
+		case NSShiftJISStringEncoding:
+			encoding = XML_CHAR_ENCODING_SHIFT_JIS;
+			break;
+		case NSISOLatin2StringEncoding:
+			encoding = XML_CHAR_ENCODING_8859_2;
+			break;
+		case NSISO2022JPStringEncoding:
+			encoding = XML_CHAR_ENCODING_2022_JP;
+			break;
+		case NSUTF16BigEndianStringEncoding:
+			encoding = XML_CHAR_ENCODING_UTF16BE;
+			break;
+		case NSUTF16LittleEndianStringEncoding:
+			encoding = XML_CHAR_ENCODING_UTF16LE;
+			break;
+		case NSUTF32BigEndianStringEncoding:
+			encoding = XML_CHAR_ENCODING_UCS4BE;
+			break;
+		case NSUTF32LittleEndianStringEncoding:
+			encoding = XML_CHAR_ENCODING_UCS4LE;
+			break;
+		case NSNEXTSTEPStringEncoding:
+		case NSSymbolStringEncoding:
+		case NSNonLossyASCIIStringEncoding:
+		case NSUnicodeStringEncoding:
+		case NSMacOSRomanStringEncoding:
+		case NSUTF32StringEncoding:
+		default:
+			encoding = XML_CHAR_ENCODING_ERROR;
+			break;
+	}
+	return xmlGetCharEncodingName(encoding);
+}
+
+- (void)parseAsHTML
+{
+	webContentType = ASIHTMLWebContentType;
+
+	// Only allow parsing of a single document at a time
+	[xmlParsingLock lock];
+
+	if (![requestsUsingXMLParser count]) {
+		xmlInitParser();
+	}
+	[requestsUsingXMLParser addObject:self];
+
+
+    /* Load XML document */
+	if ([self downloadDestinationPath]) {
+		doc = htmlReadFile([[self downloadDestinationPath] cStringUsingEncoding:NSUTF8StringEncoding], [self encodingName], HTML_PARSE_NONET | HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
+	} else {
+		NSData *data = [self responseData];
+		doc = htmlReadMemory([data bytes], (int)[data length], "", [self encodingName], HTML_PARSE_NONET | HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
+	}
+    if (doc == NULL) {
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to parse reponse XML",NSLocalizedDescriptionKey,nil]]];
+		return;
+    }
+	
+	[self setResourceList:[NSMutableDictionary dictionary]];
+
+    // Populate the list of URLS to download
+    [self readResourceURLs];
+
+	if ([self error] || ![[self resourceList] count]) {
+		[requestsUsingXMLParser removeObject:self];
+		xmlFreeDoc(doc);
+		doc = NULL;
+	}
+
+	[xmlParsingLock unlock];
+
+	if ([self error]) {
+		return;
+	} else if (![[self resourceList] count]) {
+		[super requestFinished];
+		[super markAsFinished];
+		return;
+	}
+	
+	// Create a new request for every item in the queue
+	[[self externalResourceQueue] cancelAllOperations];
+	[self setExternalResourceQueue:[ASINetworkQueue queue]];
+	[[self externalResourceQueue] setDelegate:self];
+	[[self externalResourceQueue] setShouldCancelAllRequestsOnFailure:[self shouldIgnoreExternalResourceErrors]];
+	[[self externalResourceQueue] setShowAccurateProgress:[self showAccurateProgress]];
+	[[self externalResourceQueue] setQueueDidFinishSelector:@selector(finishedFetchingExternalResources:)];
+	[[self externalResourceQueue] setRequestDidFinishSelector:@selector(externalResourceFetchSucceeded:)];
+	[[self externalResourceQueue] setRequestDidFailSelector:@selector(externalResourceFetchFailed:)];
+	for (NSString *theURL in [[self resourceList] keyEnumerator]) {
+		ASIWebPageRequest *externalResourceRequest = [ASIWebPageRequest requestWithURL:[NSURL URLWithString:theURL relativeToURL:[self url]]];
+		[externalResourceRequest setRequestHeaders:[self requestHeaders]];
+		[externalResourceRequest setDownloadCache:[self downloadCache]];
+		[externalResourceRequest setCachePolicy:[self cachePolicy]];
+		[externalResourceRequest setCacheStoragePolicy:[self cacheStoragePolicy]];
+		[externalResourceRequest setUserInfo:[NSDictionary dictionaryWithObject:theURL forKey:@"Path"]];
+		[externalResourceRequest setParentRequest:self];
+		[externalResourceRequest setUrlReplacementMode:[self urlReplacementMode]];
+		[externalResourceRequest setShouldResetDownloadProgress:NO];
+		[externalResourceRequest setDelegate:self];
+		[externalResourceRequest setUploadProgressDelegate:self];
+		[externalResourceRequest setDownloadProgressDelegate:self];
+		if ([self downloadDestinationPath]) {
+			[externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]];
+		}
+		[[self externalResourceQueue] addOperation:externalResourceRequest];
+	}
+	[[self externalResourceQueue] go];
+}
+
+- (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest
+{
+	NSString *originalPath = [[externalResourceRequest userInfo] objectForKey:@"Path"];
+	NSMutableDictionary *requestResponse = [[self resourceList] objectForKey:originalPath];
+	NSString *contentType = [[externalResourceRequest responseHeaders] objectForKey:@"Content-Type"];
+	if (!contentType) {
+		contentType = @"application/octet-stream";
+	}
+	[requestResponse setObject:contentType forKey:@"ContentType"];
+	if ([self downloadDestinationPath]) {
+		[requestResponse setObject:[externalResourceRequest downloadDestinationPath] forKey:@"DataPath"];
+	} else {
+		NSData *data = [externalResourceRequest responseData];
+		if (data) {
+			[requestResponse setObject:data forKey:@"Data"];
+		}
+	}
+}
+
+- (void)externalResourceFetchFailed:(ASIHTTPRequest *)externalResourceRequest
+{
+	if ([[self externalResourceQueue] shouldCancelAllRequestsOnFailure]) {
+		[self failWithError:[externalResourceRequest error]];
+	}
+}
+
+- (void)finishedFetchingExternalResources:(ASINetworkQueue *)queue
+{
+	if ([self urlReplacementMode] != ASIDontModifyURLs) {
+		if (webContentType == ASICSSWebContentType) {
+			NSMutableString *parsedResponse;
+			NSError *err = nil;
+			if ([self downloadDestinationPath]) {
+				parsedResponse = [NSMutableString stringWithContentsOfFile:[self downloadDestinationPath] encoding:[self responseEncoding] error:&err];
+			} else {
+				parsedResponse = [[[self responseString] mutableCopy] autorelease];
+			}
+			if (err) {
+				[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to read response CSS from disk",NSLocalizedDescriptionKey,nil]]];
+				return;
+			}
+			if (![self error]) {
+				for (NSString *resource in [[self resourceList] keyEnumerator]) {
+					if ([parsedResponse rangeOfString:resource].location != NSNotFound) {
+						NSString *newURL = [self contentForExternalURL:resource];
+						if (newURL) {
+							[parsedResponse replaceOccurrencesOfString:resource withString:newURL options:0 range:NSMakeRange(0, [parsedResponse length])];
+						}
+					}
+				}
+			}
+			if ([self downloadDestinationPath]) {
+				[parsedResponse writeToFile:[self downloadDestinationPath] atomically:NO encoding:[self responseEncoding] error:&err];
+				if (err) {
+					[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to write response CSS to disk",NSLocalizedDescriptionKey,nil]]];
+					return;
+				}
+			} else {
+				[self setRawResponseData:(id)[parsedResponse dataUsingEncoding:[self responseEncoding]]];
+			}
+		} else {
+			[xmlParsingLock lock];
+
+			[self updateResourceURLs];
+
+			if (![self error]) {
+
+				// We'll use the xmlsave API so we can strip the xml declaration
+				xmlSaveCtxtPtr saveContext;
+
+				if ([self downloadDestinationPath]) {
+
+					// Truncate the file first
+					[[[[NSFileManager alloc] init] autorelease] createFileAtPath:[self downloadDestinationPath] contents:nil attributes:nil];
+
+					saveContext = xmlSaveToFd([[NSFileHandle fileHandleForWritingAtPath:[self downloadDestinationPath]] fileDescriptor],NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5
+					xmlSaveDoc(saveContext, doc);
+					xmlSaveClose(saveContext);
+
+				} else {
+	#if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED <= __MAC_10_5
+					// xmlSaveToBuffer() is not implemented in the 10.5 version of libxml
+					NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
+					[[[[NSFileManager alloc] init] autorelease] createFileAtPath:tempPath contents:nil attributes:nil];
+					saveContext = xmlSaveToFd([[NSFileHandle fileHandleForWritingAtPath:tempPath] fileDescriptor],NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5
+					xmlSaveDoc(saveContext, doc);
+					xmlSaveClose(saveContext);
+					[self setRawResponseData:[NSMutableData dataWithContentsOfFile:tempPath]];
+	#else
+					xmlBufferPtr buffer = xmlBufferCreate();
+					saveContext = xmlSaveToBuffer(buffer,NULL,2); // 2 == XML_SAVE_NO_DECL, this isn't declared on Mac OS 10.5
+					xmlSaveDoc(saveContext, doc);
+					xmlSaveClose(saveContext);
+					[self setRawResponseData:[[[NSMutableData alloc] initWithBytes:buffer->content length:buffer->use] autorelease]];
+					xmlBufferFree(buffer);
+	#endif
+				}
+
+				// Strip the content encoding if the original response was gzipped
+				if ([self isResponseCompressed]) {
+					NSMutableDictionary *headers = [[[self responseHeaders] mutableCopy] autorelease];
+					[headers removeObjectForKey:@"Content-Encoding"];
+					[self setResponseHeaders:headers];
+				}
+			}
+
+			xmlFreeDoc(doc);
+			doc = nil;
+
+			[requestsUsingXMLParser removeObject:self];
+			if (![requestsUsingXMLParser count]) {
+				xmlCleanupParser();
+			}
+			[xmlParsingLock unlock];
+		}
+	}
+	if (![self parentRequest]) {
+		[[self class] updateProgressIndicator:&downloadProgressDelegate withProgress:contentLength ofTotal:contentLength];
+	}
+
+	NSMutableDictionary *newHeaders = [[[self responseHeaders] mutableCopy] autorelease];
+	[newHeaders removeObjectForKey:@"Content-Encoding"];
+	[self setResponseHeaders:newHeaders];
+
+	// Write the parsed content back to the cache
+	if ([self urlReplacementMode] != ASIDontModifyURLs) {
+		[[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]];
+	}
+
+	[super requestFinished];
+	[super markAsFinished];
+}
+
+- (void)readResourceURLs
+{
+	// Create xpath evaluation context
+    xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+    if(xpathCtx == NULL) {
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to create new XPath context",NSLocalizedDescriptionKey,nil]]];
+		return;
+    }
+
+    // Evaluate xpath expression
+    xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
+    if(xpathObj == NULL) {
+        xmlXPathFreeContext(xpathCtx); 
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to evaluate XPath expression!",NSLocalizedDescriptionKey,nil]]];
+		return;
+    }
+	
+	// Now loop through our matches
+	xmlNodeSetPtr nodes = xpathObj->nodesetval;
+
+    int size = (nodes) ? nodes->nodeNr : 0;
+	int i;
+    for(i = size - 1; i >= 0; i--) {
+		assert(nodes->nodeTab[i]);
+		NSString *parentName  = [NSString stringWithCString:(char *)nodes->nodeTab[i]->parent->name encoding:[self responseEncoding]];
+		NSString *nodeName = [NSString stringWithCString:(char *)nodes->nodeTab[i]->name encoding:[self responseEncoding]];
+
+		xmlChar *nodeValue = xmlNodeGetContent(nodes->nodeTab[i]);
+		NSString *value = [NSString stringWithCString:(char *)nodeValue encoding:[self responseEncoding]];
+		xmlFree(nodeValue);
+
+		// Our xpath query matched all <link> elements, but we're only interested in stylesheets
+		// We do the work here rather than in the xPath query because the query is case-sensitive, and we want to match on 'stylesheet', 'StyleSHEEt' etc
+		if ([[parentName lowercaseString] isEqualToString:@"link"]) {
+			xmlChar *relAttribute = xmlGetNoNsProp(nodes->nodeTab[i]->parent,(xmlChar *)"rel");
+			if (relAttribute) {
+				NSString *rel = [NSString stringWithCString:(char *)relAttribute encoding:[self responseEncoding]];
+				xmlFree(relAttribute);
+				if ([[rel lowercaseString] isEqualToString:@"stylesheet"]) {
+					[self addURLToFetch:value];
+				}
+			}
+
+		// Parse the content of <style> tags and style attributes to find external image urls or external css files
+		} else if ([[nodeName lowercaseString] isEqualToString:@"style"]) {
+			NSArray *externalResources = [[self class] CSSURLsFromString:value];
+			for (NSString *theURL in externalResources) {
+				[self addURLToFetch:theURL];
+			}
+
+		// Parse the content of <source src=""> tags (HTML 5 audio + video)
+		// We explictly disable the download of files with .webm, .ogv and .ogg extensions, since it's highly likely they won't be useful to us
+		} else if ([[parentName lowercaseString] isEqualToString:@"source"] || [[parentName lowercaseString] isEqualToString:@"audio"]) {
+			NSString *fileExtension = [[value pathExtension] lowercaseString];
+			if (![fileExtension isEqualToString:@"ogg"] && ![fileExtension isEqualToString:@"ogv"] && ![fileExtension isEqualToString:@"webm"]) {
+				[self addURLToFetch:value];
+			}
+
+		// For all other elements matched by our xpath query (except hyperlinks), add the content as an external url to fetch
+		} else if (![[parentName lowercaseString] isEqualToString:@"a"]) {
+			[self addURLToFetch:value];
+		}
+		if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL) {
+			nodes->nodeTab[i] = NULL;
+		}
+    }
+	
+	xmlXPathFreeObject(xpathObj);
+    xmlXPathFreeContext(xpathCtx); 
+}
+
+- (void)addURLToFetch:(NSString *)newURL
+{
+	// Get rid of any surrounding whitespace
+	newURL = [newURL stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+	// Don't attempt to fetch data URIs
+	if ([newURL length] > 4) {
+		if (![[[newURL substringToIndex:5] lowercaseString] isEqualToString:@"data:"]) {
+			NSURL *theURL = [NSURL URLWithString:newURL relativeToURL:[self url]];
+			if (theURL) {
+				if (![[self resourceList] objectForKey:newURL]) {
+					[[self resourceList] setObject:[NSMutableDictionary dictionary] forKey:newURL];
+				}
+			}
+		}
+	}
+}
+
+
+- (void)updateResourceURLs
+{
+	// Create xpath evaluation context
+	xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+	if(xpathCtx == NULL) {
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to create new XPath context",NSLocalizedDescriptionKey,nil]]];
+		return;
+	}
+
+ 	// Evaluate xpath expression
+	xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
+	if(xpathObj == NULL) {
+		xmlXPathFreeContext(xpathCtx);
+		[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:101 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Error: unable to evaluate XPath expression!",NSLocalizedDescriptionKey,nil]]];
+		return;
+	}
+
+	// Loop through all the matches, replacing urls where nescessary
+	xmlNodeSetPtr nodes = xpathObj->nodesetval;
+	int size = (nodes) ? nodes->nodeNr : 0;
+	int i;
+	for(i = size - 1; i >= 0; i--) {
+		assert(nodes->nodeTab[i]);
+		NSString *parentName  = [NSString stringWithCString:(char *)nodes->nodeTab[i]->parent->name encoding:[self responseEncoding]];
+		NSString *nodeName  = [NSString stringWithCString:(char *)nodes->nodeTab[i]->name encoding:[self responseEncoding]];
+
+		xmlChar *nodeValue = xmlNodeGetContent(nodes->nodeTab[i]);
+		NSString *value = [NSString stringWithCString:(char *)nodeValue encoding:[self responseEncoding]];
+		xmlFree(nodeValue);
+
+		// Replace external urls in <style> tags or in style attributes
+		if ([[nodeName lowercaseString] isEqualToString:@"style"]) {
+			NSArray *externalResources = [[self class] CSSURLsFromString:value];
+			for (NSString *theURL in externalResources) {
+				if ([value rangeOfString:theURL].location != NSNotFound) {
+					NSString *newURL = [self contentForExternalURL:theURL];
+					if (newURL) {
+						value = [value stringByReplacingOccurrencesOfString:theURL withString:newURL];
+					}
+				}
+			}
+			xmlNodeSetContent(nodes->nodeTab[i], (xmlChar *)[value cStringUsingEncoding:[self responseEncoding]]);
+
+		// Replace relative hyperlinks with absolute ones, since we will need to set a local baseURL when loading this in a web view
+		} else if ([self urlReplacementMode] == ASIReplaceExternalResourcesWithLocalURLs && [[parentName lowercaseString] isEqualToString:@"a"]) {
+			NSString *newURL = [[NSURL URLWithString:value relativeToURL:[self url]] absoluteString];
+			if (newURL) {
+				xmlNodeSetContent(nodes->nodeTab[i], (xmlChar *)[newURL cStringUsingEncoding:[self responseEncoding]]);
+			}
+
+		// Replace all other external resource urls
+		} else {
+			NSString *newURL = [self contentForExternalURL:value];
+			if (newURL) {
+				xmlNodeSetContent(nodes->nodeTab[i], (xmlChar *)[newURL cStringUsingEncoding:[self responseEncoding]]);
+			}
+		}
+
+		if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL) {
+			nodes->nodeTab[i] = NULL;
+		}
+	}
+	xmlXPathFreeObject(xpathObj);
+	xmlXPathFreeContext(xpathCtx);
+}
+
+// The three methods below are responsible for forwarding delegate methods we want to handle to the parent request's approdiate delegate
+// Certain delegate methods are ignored (eg setProgress: / setDoubleValue: / setMaxValue:)
+- (BOOL)respondsToSelector:(SEL)selector
+{
+	if ([self parentRequest]) {
+		return [[self parentRequest] respondsToSelector:selector];
+	}
+	//Ok, now check for selectors we want to pass on to the delegate
+	if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
+		return [delegate respondsToSelector:selector];
+	} else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
+		return [downloadProgressDelegate respondsToSelector:selector];
+	} else if (selector == @selector(request:didSendBytes:)  || selector == @selector(request:incrementUploadSizeBy:)) {
+		return [uploadProgressDelegate respondsToSelector:selector];
+	}
+	return [super respondsToSelector:selector];
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
+{
+	if ([self parentRequest]) {
+		return [[self parentRequest] methodSignatureForSelector:selector];
+	}
+	if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
+		return [(id)delegate methodSignatureForSelector:selector];
+	} else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
+		return [(id)downloadProgressDelegate methodSignatureForSelector:selector];
+	} else if (selector == @selector(request:didSendBytes:)  || selector == @selector(request:incrementUploadSizeBy:)) {
+		return [(id)uploadProgressDelegate methodSignatureForSelector:selector];
+	}
+	return nil;
+}
+
+- (void)forwardInvocation:(NSInvocation *)anInvocation
+{
+	if ([self parentRequest]) {
+		return [[self parentRequest] forwardInvocation:anInvocation];
+	}
+	SEL selector = [anInvocation selector];
+	if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
+		[anInvocation invokeWithTarget:delegate];
+	} else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
+		[anInvocation invokeWithTarget:downloadProgressDelegate];
+	} else if (selector == @selector(request:didSendBytes:)  || selector == @selector(request:incrementUploadSizeBy:)) {
+		[anInvocation invokeWithTarget:uploadProgressDelegate];
+	}
+}
+
+// A quick and dirty way to build a list of external resource urls from a css string
++ (NSArray *)CSSURLsFromString:(NSString *)string
+{
+	NSMutableArray *urls = [NSMutableArray array];
+	NSScanner *scanner = [NSScanner scannerWithString:string];
+	[scanner setCaseSensitive:NO];
+	while (1) {
+		NSString *theURL = nil;
+		[scanner scanUpToString:@"url(" intoString:NULL];
+		[scanner scanString:@"url(" intoString:NULL];
+		[scanner scanUpToString:@")" intoString:&theURL];
+		if (!theURL) {
+			break;
+		}
+		// Remove any quotes or whitespace around the url
+		theURL = [theURL stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+		theURL = [theURL stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\"'"]];
+		theURL = [theURL stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+		[urls addObject:theURL];
+	}
+	return urls;
+}
+
+// Returns a relative file path from sourcePath to destinationPath (eg ../../foo/bar.txt)
+- (NSString *)relativePathTo:(NSString *)destinationPath fromPath:(NSString *)sourcePath
+{
+	NSArray *sourcePathComponents = [sourcePath pathComponents];
+	NSArray *destinationPathComponents = [destinationPath pathComponents];
+	NSUInteger i;
+	NSString *newPath = @"";
+	NSString *sourcePathComponent, *destinationPathComponent;
+	for (i=0; i<[sourcePathComponents count]; i++) {
+		sourcePathComponent = [sourcePathComponents objectAtIndex:i];
+		if ([destinationPathComponents count] > i) {
+			destinationPathComponent = [destinationPathComponents objectAtIndex:i];
+			if (![sourcePathComponent isEqualToString:destinationPathComponent]) {
+				NSUInteger i2;
+				for (i2=i+1; i2<[sourcePathComponents count]; i2++) {
+					newPath = [newPath stringByAppendingPathComponent:@".."];
+				}
+				newPath = [newPath stringByAppendingPathComponent:destinationPathComponent];
+				for (i2=i+1; i2<[destinationPathComponents count]; i2++) {
+					newPath = [newPath stringByAppendingPathComponent:[destinationPathComponents objectAtIndex:i2]];
+				}
+				break;
+			}
+		}
+	}
+	return newPath;
+}
+
+- (NSString *)contentForExternalURL:(NSString *)theURL
+{
+	if ([self urlReplacementMode] == ASIReplaceExternalResourcesWithLocalURLs) {
+		NSString *resourcePath = [[resourceList objectForKey:theURL] objectForKey:@"DataPath"];
+		return [self relativePathTo:resourcePath fromPath:[self downloadDestinationPath]];
+	}
+	NSData *data;
+	if ([[resourceList objectForKey:theURL] objectForKey:@"DataPath"]) {
+		data = [NSData dataWithContentsOfFile:[[resourceList objectForKey:theURL] objectForKey:@"DataPath"]];
+	} else {
+		data = [[resourceList objectForKey:theURL] objectForKey:@"Data"];
+	}
+	NSString *contentType = [[resourceList objectForKey:theURL] objectForKey:@"ContentType"];
+	if (data && contentType) {
+		NSString *dataURI = [NSString stringWithFormat:@"data:%@;base64,",contentType];
+		dataURI = [dataURI stringByAppendingString:[ASIHTTPRequest base64forData:data]];
+		return dataURI;
+	}
+	return nil;
+}
+
+- (NSString *)cachePathForRequest:(ASIWebPageRequest *)theRequest
+{
+	// If we're using a download cache (and its a good idea to do so when using ASIWebPageRequest), ask it for the location to store this file
+	// This ends up being quite efficient, as we download directly to the cache
+	if ([self downloadCache]) {
+		return [[self downloadCache] pathToStoreCachedResponseDataForRequest:theRequest];
+
+	// This is a fallback for when we don't have a download cache - we store the external resource in a file in the temporary directory
+	} else {
+		// Borrowed from: http://stackoverflow.com/questions/652300/using-md5-hash-on-a-string-in-cocoa
+		const char *cStr = [[[theRequest url] absoluteString] UTF8String];
+		unsigned char result[16];
+		CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
+		NSString *md5 = [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],result[8], result[9], result[10], result[11],result[12], result[13], result[14], result[15]]; 	
+		return [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[md5 stringByAppendingPathExtension:@"html"]];
+	}
+}
+
+
+@synthesize externalResourceQueue;
+@synthesize resourceList;
+@synthesize parentRequest;
+@synthesize urlReplacementMode;
+@synthesize shouldIgnoreExternalResourceErrors;
+@end

--
Gitblit v1.8.0