mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 08:26:27 +00:00
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:
parent
a504a2adde
commit
2dcfcebcda
3 changed files with 1152 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue