Integrated XMLRPC support.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@20843 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
CaS 2005-03-04 15:50:06 +00:00
parent a504a2adde
commit 2dcfcebcda
3 changed files with 1152 additions and 1 deletions

View file

@ -1,3 +1,8 @@
2005-03-04 Richard Frith-Macdonald <rfm@gnu.org>
* Source/Additions/GSXML.m: GSXMLRPC integrated.
* Headers/Additions/GSUstepBase/GSXML.h: GSXMLRPC integrated.
2005-03-03 Adam Fedor <fedor@gnu.org>
* Source/NSFileManager.m (-createFileAtPath:...): Define var at

View file

@ -1,6 +1,6 @@
/** Interface for XML parsing classes
Copyright (C) 2000 Free Software Foundation, Inc.
Copyright (C) 2000-2005 Free Software Foundation, Inc.
Written by: Michael Pakhantsov <mishel@berest.dp.ua> on behalf of
Brainstorm computer solutions.
@ -403,6 +403,240 @@
- (GSXMLDocument*) xsltTransform: (GSXMLDocument*)xsltStylesheet;
@end
#include <Foundation/NSURLHandle.h>
@class NSArray;
@class NSDictionary;
@class NSTimer;
@class GSXMLNode;
@class GSXMLRPC;
/**
* <p>The GSXMLRPC class provides methods for constructing and parsing
* XMLRPC method call and response documents ... so that calls may
* be constructed of standard objects.
* </p>
* <p>The correspondence between XMLRPC values and Objective-C objects
* is as follows -
* </p>
* <list>
* <item><strong>i4</strong> (or <em>int</em>) is an [NSNumber] other
* than a real/float or boolean.</item>
* <item><strong>boolean</strong> is an [NSNumber] created as a BOOL.</item>
* <item><strong>string</strong> is an [NSString] object.</item>
* <item><strong>double</strong> is an [NSNumber] created as a float or
* double.</item>
* <item><strong>dateTime.iso8601</strong> is an [NSDate] object.</item>
* <item><strong>base64</strong> is an [NSData] object.</item>
* <item><strong>array</strong> is an [NSArray] object.</item>
* <item><strong>struct</strong> is an [NSDictionary] object.</item>
* </list>
* <p>If you attempt to use any other type of object in the construction
* of an XMLRPC document, the [NSObject-description] method of that
* object will be used to create a striong, and the resulting object
* will be encoded as an XMLRPC <em>string</em> element.
* </p>
* <p>In particular, the names of members in a <em>struct</em> must be strings,
* so if you provide an [NSDictionary] object to represent a <em>struct</em>
* the keys of the dictionary will be converted to strings if necessary.
* </p>
* <p>The class also provides a method for making a synchronous XMLRPC
* method call (with timeout), or an asynchronous call in which the
* call completion is handled by a delegate.
* </p>
*/
@interface GSXMLRPC : NSObject <NSURLHandleClient>
{
@private
NSURLHandle *handle;
NSTimer *timer;
id result;
id delegate; // Not retained.
}
/**
* Given a method name and an array of parameters, this method constructs
* the XML document for the corresponding XMLRPC call and returns the
* document as a string.<br />
* The params array may be empty or nil if there are no parameters to be
* passed.<br />
* The method returns nil if passed an invalid method name (a method name
* may contain any of the ascii alphanumeric characters and underscore,
* fullstop, colon, or slash).<br />
* This method is used internally when sending an XMLRPC method call to
* a remote system, but you can also call it yourself.
*/
- (NSString*) buildMethodCall: (NSString*)method
params: (NSArray*)params;
/**
* Constructs an XML document for an XMLRPC fault response with the
* specified code and string. The resulting document is returned
* as a string.<br />
* This method is intended for use by applications acting as XMLRPC servers.
*/
- (NSString*) buildResponseWithFaultCode: (int)code andString: (NSString*)s;
/**
* Builds an XMLRPC response with the specified array of parameters and
* returns the document as a string.<br />
* The params array may be empty or nil if there are no parameters to be
* returned (an empty params element will be created).<br />
* This method is intended for use by applications acting as XMLRPC servers.
*/
- (NSString*) buildResponseWithParams: (NSArray*)params;
/**
* Returns the delegate previously set by the -setDelegate: method.<br />
* The delegate handles completion of asynchronous method calls to the
* URL specified when the receiver was initialised (if any).
*/
- (id) delegate;
/**
* Initialise the receiver to make XMLRPC calls to the specified URL.<br />
* This method just calls -initWithURL:certificate:privateKey:password:
* with nil arguments for the SSL credentials.
*/
- (id) initWithURL: (NSString*)url;
/** <init />
* Initialise the receiver to make XMLRPC calls to the specified url
* and (optionally) with the specified SSL parameters.<br />
* The url argument may be nil, in which case the receiver will be
* unable to make XMLRPC calls, but can be used to parse incoming
* requests and build responses.<br />
* If the SSL credentials are non-nil, connections to the remote server
* will be authenticated using the supplied certificate so that the
* remote system knows who is contacting it.
*/
- (id) initWithURL: (NSString*)url
certificate: (NSString*)cert
privateKey: (NSString*)pKey
password: (NSString*)pwd;
/**
* Calls -sendMethodCall:params:timeout: and waits for the response.<br />
* Returns the response parameters (an array),
* the response fault (a dictionary),
* or a failure reason (a string).
*/
- (id) makeMethodCall: (NSString*)method
params: (NSArray*)params
timeout: (int)seconds;
/**
* Parses XML data containing an XMLRPC method call.<br />
* Returns the name of the method call.<br />
* Empties, and then places the method parameters (if any)
* in the params argument.<br />
* NB. Any containers (arrays or dictionaries) in the parsed parameters
* will be mutable, so you can modify this data structure as you like.<br />
* Raises an exception if parsing fails.<br />
* This method is intended for the use of XMLRPC server applications.
*/
- (NSString*) parseMethod: (NSData*)request
params: (NSMutableArray*)params;
/**
* Parses XML data containing an XMLRPC method response.<br />
* Returns nil for succes, the fault dictionary on failure.<br />
* Places the response parameters (if any) in the params argument.<br />
* NB. Any containers (arrays or dictionaries) in the parsed parameters
* will be mutable, so you can modify this data structure as you like.<br />
* Raises an exception if parsing fails.<br />
* Used internally when making a method call to a remote server.
*/
- (NSDictionary*) parseResponse: (NSData*)response
params: (NSMutableArray*)params;
/**
* Returns the result of the last method call, or nil if there has been
* no method call or one is in progress.<br />
* The result may be one of -
* <list>
* <item>A mutable array ... the parameters of a success response.</item>
* <item>A dictionary ... containing a fault response.</item>
* <item>A string ... describing a low-level failure (eg. timeout).</item>
* </list>
* NB. Any containers (arrays or dictionaries) in the parsed parameters
* of a success response will be mutable, so you can modify this data
* structure as you like.
*/
- (id) result;
/**
* Send an asynchronous XMLRPC method call with the specified timeout.<br />
* A delegate should have been set to handle the result of this call,
* but if one was not set the state of the asynchronous call may be polled
* by calling the -result method, which will return nil as long as the
* call has not completed.<br />
* The call may be cancelled by calling the -timeout: method<br />
* This method returns YES if the call was started,
* NO if it could not be started
* (eg because another call is in progress or because of bad arguments).<br />
* NB. For the asynchronous operation to proceed, the current [NSRunLoop]
* must be run.
*/
- (BOOL) sendMethodCall: (NSString*)method
params: (NSArray*)params
timeout: (int)seconds;
/**
* Specify whether to perform mdebug trace on I/O
*/
- (void) setDebug: (BOOL)flag;
/**
* Sets the delegate object which will receive callbacks when an XMLRPC
* call completes.<br />
* NB. this delegate is <em>not</em> retained, and should be removed
* before it is deallocated (call -setDelegate: again with a nil argument
* to remove the delegate).
*/
- (void) setDelegate: (id)aDelegate;
/**
* Handles timeouts, passing information to delegate ... you don't need to
* call this method, but you <em>may</em> call it in order to cancel an
* asynchronous request as if it had timed out.
*/
- (void) timeout: (NSTimer*)t;
/** Allows GSXMLRPC to act as a client of NSURLHandle. Internal use only. */
- (void) URLHandle: (NSURLHandle*)sender
resourceDataDidBecomeAvailable: (NSData*)newData;
/** Allows GSXMLRPC to act as a client of NSURLHandle. Internal use only. */
- (void) URLHandle: (NSURLHandle*)sender
resourceDidFailLoadingWithReason: (NSString*)reason;
/** Allows GSXMLRPC to act as a client of NSURLHandle. Internal use only. */
- (void) URLHandleResourceDidBeginLoading: (NSURLHandle*)sender;
/** Allows GSXMLRPC to act as a client of NSURLHandle. Internal use only. */
- (void) URLHandleResourceDidCancelLoading: (NSURLHandle*)sender;
/** Allows GSXMLRPC to act as a client of NSURLHandle. Internal use only. */
- (void) URLHandleResourceDidFinishLoading: (NSURLHandle*)sender;
@end
/**
* Delegates should implement this method in order to be informed of
* the success or failure of an XMLRPC method call which was initiated
* by the -sendMethodCall:params:timeout: method.<br />
*/
@interface GSXMLRPC (Delegate)
/**
* Called by the sender when an XMLRPC method call completes (either success
* or failure).
* The delegate may then call the -result method to retrieve the result of
* the method call from the sender.
*/
- (void) completedXMLRPC: (GSXMLRPC*)sender;
@end
#endif /* STRICT_MACOS_X */
#endif /* STRICT_OPENSTEP */

View file

@ -4308,3 +4308,915 @@ static BOOL warned = NO; if (warned == NO) { warned = YES; NSLog(@"WARNING, use
}
@end
#include <Foundation/NSArray.h>
#include <Foundation/NSCalendarDate.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSException.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSString.h>
#include <Foundation/NSTimeZone.h>
#include <Foundation/NSTimer.h>
#include <Foundation/NSURL.h>
#include <Foundation/NSURLHandle.h>
#include <Foundation/NSValue.h>
#include <GNUstepBase/GSMime.h>
#include <GNUstepBase/GSXML.h>
/*
* Categories on other classes which are required for XMLRPC
*/
@interface NSArray (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSData (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSDate (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSDictionary (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSObject (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSNumber (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
@interface NSString (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent;
@end
/*
* A little code to handle indentation.
*/
static NSString *indentations[] = {
@" ",
@" ",
@" ",
@"\t",
@"\t ",
@"\t ",
@"\t ",
@"\t\t",
@"\t\t ",
@"\t\t ",
@"\t\t ",
@"\t\t\t",
@"\t\t\t ",
@"\t\t\t ",
@"\t\t\t ",
@"\t\t\t\t"
};
static void indentation(unsigned level, NSMutableString *str)
{
if (level > 0)
{
if (level >= sizeof(indentations)/sizeof(*indentations))
{
level = sizeof(indentations)/sizeof(*indentations) - 1;
}
[str appendString: indentations[level]];
}
}
/*
* Implementation of categories to output objects for XMLRPC
*/
@implementation NSArray (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
unsigned i;
unsigned c = [self count];
indentation(indent++, str);
[str appendString: @"<array>\n"];
indentation(indent++, str);
[str appendString: @"<data>\n"];
for (i = 0; i < c; i++)
{
id value = [self objectAtIndex: i];
indentation(indent++, str);
[str appendString: @"<value>\n"];
[value appendToXMLRPC: str indent: indent];
[str appendString: @"\n"];
indentation(--indent, str);
[str appendString: @"</value>\n"];
}
indentation(--indent, str);
[str appendString: @"</data>\n"];
indentation(--indent, str);
[str appendString: @"</array>"];
}
@end
@implementation NSData (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
NSData *d;
NSString *s;
d = [GSMimeDocument encodeBase64: self];
s = [[NSString alloc] initWithData: d encoding: NSASCIIStringEncoding];
[str appendString: @"<base64>"];
[str appendString: s];
[str appendString: @"</base64>"];
RELEASE(s);
}
@end
@implementation NSDate (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
static NSTimeZone *z = nil;
NSString *s;
if (z == nil)
{
s = RETAIN([NSTimeZone timeZoneForSecondsFromGMT: 0]);
}
s = [self descriptionWithCalendarFormat: @"%Y%m%dT%H:%M:%S"
timeZone: z
locale: nil];
[str appendString: @"<dateTime.iso8601>"];
[str appendString: s];
[str appendString: @"</dateTime.iso8601>"];
}
@end
@implementation NSDictionary (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
NSEnumerator *kEnum = [self keyEnumerator];
NSString *key;
indentation(indent++, str);
[str appendString: @"<struct>\n"];
while ((key = [kEnum nextObject]))
{
id value = [self objectForKey: key];
indentation(indent++, str);
[str appendString: @"<member>\n"];
indentation(indent, str);
[str appendString: @"<name>"];
[str appendString: [[key description] stringByEscapingXML]];
[str appendString: @"</name>\n"];
indentation(indent++, str);
[str appendString: @"<value>\n"];
[value appendToXMLRPC: str indent: indent--];
[str appendString: @"\n"];
indentation(indent--, str);
[str appendString: @"</value>\n"];
indentation(indent, str);
[str appendString: @"</member>\n"];
}
indentation(--indent, str);
[str appendString: @"</struct>"];
}
@end
@implementation NSNumber (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
const char *t = [self objCType];
indentation(indent, str);
if (strchr("cCsSiIlL", *t) != 0)
{
long i = [self longValue];
if ((i == 0 || i == 1) && (*t == 'c' || *t == 'C'))
{
if (i == 0)
{
[str appendString: @"<boolean>0</boolean>"];
}
else
{
[str appendString: @"<boolean>1</boolean>"];
}
}
else
{
[str appendFormat: @"<i4>%d</i4>", i];
}
}
else
{
[str appendFormat: @"<double>%f</double>", [self doubleValue]];
}
}
@end
@implementation NSObject (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
[[self description] appendToXMLRPC: str indent: indent];
}
@end
@implementation NSString (GSXMLRPC)
- (void) appendToXMLRPC: (NSMutableString*)str indent: (unsigned)indent
{
indentation(indent, str);
[str appendFormat: @"<string>%@</string>", [self stringByEscapingXML]];
}
@end
/*
* Convert incoming XMLRPC value to a normal Objective-C object.
*/
@interface GSXMLRPC (Private)
- (id) _parseValue: (GSXMLNode*)node;
@end
@implementation GSXMLRPC (Private)
- (id) _parseValue: (GSXMLNode*)node
{
NSString *name = [node name];
NSString *str;
if ([name isEqualToString: @"value"])
{
GSXMLNode *type = [node firstChildElement];
/*
* A value with no type element is just a string.
*/
if (type == nil)
{
name = @"string";
}
else
{
node = type;
name = [node name];
}
}
if ([name length] == 0)
{
return nil;
}
if ([name isEqualToString: @"i4"] || [name isEqualToString: @"int"])
{
str = [node content];
if (str == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"missing %@ value", name];
}
return [NSNumber numberWithInt: [str intValue]];
}
if ([name isEqualToString: @"string"])
{
str = [node content];
if (str == nil)
{
str = @"";
}
return str;
}
if ([name isEqualToString: @"boolean"])
{
char c;
str = [node content];
if (str == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"missing %@ value", name];
}
c = [str intValue];
return [NSNumber numberWithBool: c == 0 ? NO : YES];
}
if ([name isEqualToString: @"double"])
{
str = [node content];
if (str == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"missing %@ value", name];
}
return [NSNumber numberWithDouble: [str doubleValue]];
}
if ([name isEqualToString: @"data"])
{
NSData *d;
str = [node content];
if (str == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"missing %@ value", name];
}
d = [str dataUsingEncoding: NSASCIIStringEncoding];
return [GSMimeDocument decodeBase64: d];
}
if ([name isEqualToString: @"date"])
{
str = [node content];
if (str == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"missing %@ value", name];
}
return [NSCalendarDate dateWithString: str
calendarFormat: @"%Y%m%dT%H:%M:%S"
locale: nil];
}
if ([name isEqualToString: @"array"])
{
NSMutableArray *arr = [NSMutableArray array];
node = [node firstChildElement];
while (node != nil && [[node name] isEqualToString: @"data"] == NO)
{
node = [node nextElement];
}
if ([[node name] isEqualToString: @"data"] == YES)
{
node = [node firstChildElement];
while (node != nil)
{
if ([[node name] isEqualToString: @"value"] == YES)
{
id v;
v = [self _parseValue: node];
if (v != nil)
{
[arr addObject: v];
}
}
node = [node nextElement];
}
}
return arr;
}
if ([name isEqualToString: @"struct"])
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
node = [node firstChildElement];
while (node != nil)
{
if ([[node name] isEqualToString: @"member"] == YES)
{
GSXMLNode *member = [node firstChildElement];
NSString *key = nil;
id val = nil;
while (member != nil)
{
if ([[member name] isEqualToString: @"name"] == YES)
{
key = [member content];
}
else if ([[member name] isEqualToString: @"value"] == YES)
{
val = [self _parseValue: member];
}
if (key != nil && val != nil)
{
[dict setObject: val forKey: key];
break;
}
member = [member nextElement];
}
}
node = [node nextElement];
}
return dict;
}
[NSException raise: NSInvalidArgumentException
format: @"Unknown value type: %@", name];
return nil;
}
@end
/*
* And now, the actual GSXMLRPC class.
*/
@implementation GSXMLRPC
- (NSString*) buildMethodCall: (NSString*)method
params: (NSArray*)params
{
NSMutableString *str = [NSMutableString stringWithCapacity: 1024];
unsigned c = [params count];
unsigned i;
if ([method length] == 0)
{
return nil;
}
else
{
static NSCharacterSet *illegal = nil;
NSRange r;
if (illegal == nil)
{
NSMutableCharacterSet *tmp = [NSMutableCharacterSet new];
[tmp addCharactersInRange: NSMakeRange('0', 10)];
[tmp addCharactersInRange: NSMakeRange('a', 26)];
[tmp addCharactersInRange: NSMakeRange('A', 26)];
[tmp addCharactersInString: @"_.:/"];
[tmp invert];
illegal = [tmp copy];
RELEASE(tmp);
}
r = [method rangeOfCharacterFromSet: illegal];
if (r.length > 0)
{
return nil; // Bad method name.
}
}
[str appendString: @"<?xml version=\"1.0\"?>\n"];
[str appendString: @"<methodCall>\n"];
[str appendFormat: @" <methodName>%@</methodName>\n",
[method stringByEscapingXML]];
if (c > 0)
{
[str appendString: @" <params>\n"];
for (i = 0; i < c; i++)
{
[str appendString: @" <param>\n"];
[str appendString: @" <value>\n"];
[[params objectAtIndex: i] appendToXMLRPC: str indent: 3];
[str appendString: @"\n </value>\n"];
[str appendString: @" </param>\n"];
}
[str appendString: @" </params>\n"];
}
[str appendString: @"</methodCall>\n"];
return str;
}
- (NSString*) buildResponseWithFaultCode: (int)code andString: (NSString*)s
{
NSMutableString *str = [NSMutableString stringWithCapacity: 1024];
NSDictionary *fault;
fault = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: code], @"faultCode",
[s stringByEscapingXML], @"faultString",
nil];
[str appendString: @"<?xml version=\"1.0\"?>\n"];
[str appendString: @"<methodResponse>\n"];
[str appendString: @" <fault>\n"];
[str appendString: @" <value>\n"];
[fault appendToXMLRPC: str indent: 3];
[str appendString: @"\n </value>\n"];
[str appendString: @" </fault>\n"];
[str appendString: @"</methodResponse>\n"];
return str;
}
- (NSString*) buildResponseWithParams: (NSArray*)params
{
NSMutableString *str = [NSMutableString stringWithCapacity: 1024];
unsigned c = [params count];
unsigned i;
[str appendString: @"<?xml version=\"1.0\"?>\n"];
[str appendString: @"<methodResponse>\n"];
[str appendString: @" <params>\n"];
for (i = 0; i < c; i++)
{
[str appendString: @" <param>\n"];
[str appendString: @" <value>\n"];
[[params objectAtIndex: i] appendToXMLRPC: str indent: 3];
[str appendString: @"\n </value>\n"];
[str appendString: @" </param>\n"];
}
[str appendString: @" </params>\n"];
[str appendString: @"</methodResponse>\n"];
return str;
}
- (void) dealloc
{
if (timer != nil)
{
[self timeout: nil]; // Treat as immediate timeout.
}
[handle removeClient: self];
DESTROY(result);
DESTROY(handle);
[super dealloc];
}
- (id) delegate
{
return delegate;
}
- (id) initWithURL: (NSString*)url
{
return [self initWithURL: url certificate: nil privateKey: nil password: nil];
}
- (id) initWithURL: (NSString*)url
certificate: (NSString*)cert
privateKey: (NSString*)pKey
password: (NSString*)pwd
{
if (url != nil)
{
NS_DURING
{
NSURL *u = [NSURL URLWithString: url];
handle = RETAIN([u URLHandleUsingCache: NO]);
if (cert != nil && pKey != nil && pwd != nil)
{
[handle writeProperty: cert
forKey: GSHTTPPropertyCertificateFileKey];
[handle writeProperty: pKey forKey: GSHTTPPropertyKeyFileKey];
[handle writeProperty: pwd forKey: GSHTTPPropertyPasswordKey];
}
[handle addClient: self];
}
NS_HANDLER
{
DESTROY(self);
}
NS_ENDHANDLER
}
return self;
}
- (id) init
{
return [self initWithURL: nil certificate: nil privateKey: nil password: nil];
}
- (id) makeMethodCall: (NSString*)method
params: (NSArray*)params
timeout: (int)seconds
{
NS_DURING
{
if ([self sendMethodCall: method params: params timeout: seconds] == YES)
{
NSDate *when = AUTORELEASE(RETAIN([timer fireDate]));
while (timer != nil)
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
}
}
}
NS_HANDLER
{
ASSIGN(result, [localException description]);
}
NS_ENDHANDLER
return result;
}
- (NSString*) parseMethod: (NSData*)request
params: (NSMutableArray*)params
{
GSXPathContext *ctx = nil;
GSXPathNodeSet *ns = nil;
NSString *method;
[params removeAllObjects];
NS_DURING
{
GSXMLParser *parser = [GSXMLParser parserWithData: request];
GSXMLDocument *doc = nil;
[parser substituteEntities: YES];
[parser parse];
doc = [parser document];
ctx = AUTORELEASE([[GSXPathContext alloc] initWithDocument: doc]);
}
NS_HANDLER
{
ctx = nil;
}
NS_ENDHANDLER
if (ctx == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Bad Request: parse failed"];
}
ns = (GSXPathNodeSet*)[ctx evaluateExpression: @"//methodCall/methodName"];
if ([ns count] != 1)
{
[NSException raise: NSInvalidArgumentException
format: @"Badly formatted methodCall"];
}
method = [[ns nodeAtIndex: 0] content];
ns = (GSXPathNodeSet*)[ctx evaluateExpression:
@"//methodCall/params/param/value"];
NS_DURING
{
int i;
for (i = 0; i < [ns count]; i++)
{
GSXMLNode *node = [ns nodeAtIndex: i];
if ([[node name] isEqualToString: @"value"]
&& [node firstChildElement] != nil)
{
id value = [self _parseValue: [node firstChildElement]];
if (value != nil)
{
[params addObject: value];
}
}
}
}
NS_HANDLER
{
[params removeAllObjects];
[localException raise];
}
NS_ENDHANDLER
return method;
}
- (NSDictionary*) parseResponse: (NSData*) response
params: (NSMutableArray*)params
{
GSXPathContext *ctx = nil;
GSXPathNodeSet *ns = nil;
id fault = nil;
[params removeAllObjects];
NS_DURING
{
GSXMLParser *parser = [GSXMLParser parserWithData: response];
GSXMLDocument *doc = nil;
[parser substituteEntities: YES];
[parser parse];
doc = [parser document];
ctx = AUTORELEASE([[GSXPathContext alloc] initWithDocument: doc]);
}
NS_HANDLER
{
ctx = nil;
}
NS_ENDHANDLER
if (ctx == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Bad Response: parse failed"];
}
ns = (GSXPathNodeSet*)[ctx evaluateExpression:
@"//methodResponse/params/param/value"];
NS_DURING
{
int i;
if ([ns count] > 0)
{
for (i = 0; i < [ns count]; i++)
{
GSXMLNode *node = [ns nodeAtIndex: i];
if ([[node name] isEqualToString: @"value"]
&& [node firstChildElement] != nil)
{
id value = [self _parseValue: [node firstChildElement]];
if (value != nil)
{
[params addObject: value];
}
}
}
}
else
{
ns = (GSXPathNodeSet*)[ctx evaluateExpression:
@"//methodResponse/fault/value/struct"];
if ([ns count] > 0)
{
fault = [self _parseValue: [ns nodeAtIndex: 0]];
}
}
}
NS_HANDLER
{
[params removeAllObjects];
[localException raise];
}
NS_ENDHANDLER
return fault;
}
- (id) result
{
if (timer == nil)
{
return result;
}
else
{
return nil;
}
}
- (BOOL) sendMethodCall: (NSString*)method
params: (NSArray*)params
timeout: (int)seconds
{
NSString *xml;
NSData *data;
ASSIGN(result, @"unable to send");
if (handle == nil)
{
return NO; // Not initialised to send.
}
if (timer != nil)
{
return NO; // Send already in progress.
}
xml = [self buildMethodCall: method params: params];
if (xml == nil)
{
return NO;
}
data = [xml dataUsingEncoding: NSUTF8StringEncoding];
timer = [NSTimer scheduledTimerWithTimeInterval: seconds
target: self
selector: @selector(timeout:)
userInfo: nil
repeats: NO];
[handle writeProperty: @"POST" forKey: GSHTTPPropertyMethodKey];
[handle writeProperty: @"GSXMLRPC/1.0.0" forKey: @"User-Agent"];
[handle writeProperty: @"text/xml" forKey: @"Content-Type"];
[handle writeData: data];
[handle loadInBackground];
return YES;
}
- (void) setDebug: (BOOL)flag
{
if ([handle respondsToSelector: _cmd] == YES)
{
[(id)handle setDebug: flag];
}
}
- (void) setDelegate: (id)aDelegate
{
delegate = aDelegate;
}
- (void) timeout: (NSTimer*)t
{
[timer invalidate];
timer = nil;
[handle cancelLoadInBackground];
}
- (void) URLHandle: (NSURLHandle*)sender
resourceDataDidBecomeAvailable: (NSData*)newData
{
// Not interesting
}
- (void) URLHandle: (NSURLHandle*)sender
resourceDidFailLoadingWithReason: (NSString*)reason
{
ASSIGN(result, reason);
[timer invalidate];
timer = nil;
if ([delegate respondsToSelector: @selector(completedXMLRPC:)])
{
[delegate completedXMLRPC: self];
}
}
- (void) URLHandleResourceDidBeginLoading: (NSURLHandle*)sender
{
// Not interesting
}
- (void) URLHandleResourceDidCancelLoading: (NSURLHandle*)sender
{
ASSIGN(result, @"timeout");
[timer invalidate];
timer = nil;
if ([delegate respondsToSelector: @selector(completedXMLRPC:)])
{
[delegate completedXMLRPC: self];
}
}
- (void) URLHandleResourceDidFinishLoading: (NSURLHandle*)sender
{
NSMutableArray *params = [NSMutableArray array];
id fault = nil;
int code;
code = [[handle propertyForKey: NSHTTPPropertyStatusCodeKey] intValue];
if (code == 200)
{
NSData *response = [handle availableResourceData];
NS_DURING
{
fault = [self parseResponse: response params: params];
}
NS_HANDLER
{
fault = [localException reason];
}
NS_ENDHANDLER
}
else
{
fault = [NSString stringWithFormat: @"HTTP status %03d", code];
}
if (fault == nil)
{
ASSIGN(result, params);
}
else
{
ASSIGN(result, fault);
}
[timer invalidate];
timer = nil;
if ([delegate respondsToSelector: @selector(completedXMLRPC:)])
{
[delegate completedXMLRPC: self];
}
}
@end
@implementation GSXMLRPC (Delegate)
- (void) completedXMLRPC: (GSXMLRPC*)sender
{
}
@end