#include <CoreServices/../Frameworks/OSServices.framework/Headers/WSMethodInvocation.h>
#include <CoreServices/../Frameworks/CFNetwork.framework/Headers/CFHTTPMessage.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <stdio.h>
#include "dotMacXmlRpc.h"
#include "dotMacTpMutils.h"
#if XML_DEBUG
#define xmlDebug(args...) printf(args)
#else
#define xmlDebug(args...)
#endif
#if DICTIONARY_DEBUG
#define RESPONSE_DICTIONARY_DEBUG 0
#else
#define RESPONSE_DICTIONARY_DEBUG 0
#endif
static UInt32 getHTTPStatusCodeFromWSInvokationResponse(CFDictionaryRef response)
{
CFHTTPMessageRef message =
(CFHTTPMessageRef)CFDictionaryGetValue(response, kWSHTTPResponseMessage);
UInt32 responseCode;
if (message != NULL) {
responseCode = CFHTTPMessageGetResponseStatusCode(message);
message = NULL;
} else {
xmlDebug("getHTTPStatusCode: no HTTP status\n");
responseCode = 500;
}
return responseCode;
}
static Boolean addAuthenticationToWSInvokation(
WSMethodInvocationRef wsRef,
CFStringRef username,
CFStringRef password,
CFDictionaryRef response)
{
CFHTTPMessageRef message =
(CFHTTPMessageRef)CFDictionaryGetValue(response, kWSHTTPResponseMessage);
CFHTTPMessageRef outgoingMessage = NULL;
if (message != NULL) {
CFURLRef theURL = CFHTTPMessageCopyRequestURL(message);
if (theURL != NULL) {
outgoingMessage = CFHTTPMessageCreateRequest(NULL, CFSTR("POST"),
theURL, kCFHTTPVersion1_1);
CFRelease(theURL);
}
}
Boolean successful = FALSE;
if ((message != NULL) && (outgoingMessage != NULL)) {
successful = CFHTTPMessageAddAuthentication(outgoingMessage, message,
username, password, kCFHTTPAuthenticationSchemeDigest, FALSE);
}
if (successful) {
WSMethodInvocationSetProperty(wsRef, CFSTR("/kWSHTTPMessage"), outgoingMessage);
}
if (outgoingMessage) {
CFRelease(outgoingMessage);
}
return successful;
}
OSStatus performAuthenticatedXMLRPC(
CFURLRef theURL,
CFStringRef theMethod,
CFDictionaryRef argDict,
CFArrayRef argOrder,
CFStringRef userName,
CFStringRef password,
CFDictionaryRef *resultDict, uint32_t *httpErrStatus) {
CFDictionaryRef result = NULL;
bool done = false;
OSStatus ortn = -1; *httpErrStatus = 0;
WSMethodInvocationRef wsRef = WSMethodInvocationCreate(theURL, theMethod,
kWSXMLRPCProtocol);
WSMethodInvocationSetParameters(wsRef, argDict, argOrder);
for(unsigned attempt=0; attempt<2; attempt++) {
xmlDebug("***************************************************************************\n");
xmlDebug("performAuthenticatedXMLRPC: WSMethodInvocationInvoke (attempt %u)\n", attempt);
xmlDebug("***************************************************************************\n");
CFDictionaryRef response = WSMethodInvocationInvoke(wsRef);
if (wsRef) {
CFRelease(wsRef);
wsRef = NULL;
}
#if XML_DEBUG
logCFstr("performAuthenticatedXMLRPC: userName:", userName);
#endif
if (WSMethodResultIsFault(response)) {
CFStringRef errorMsg = (CFStringRef)CFDictionaryGetValue(
response, kWSFaultString);
if (errorMsg != NULL) {
#if XML_DEBUG
logCFstr("performAuthenticatedXMLRPC: errorMsg", errorMsg);
#endif
UInt32 HTTPResponseCode =
getHTTPStatusCodeFromWSInvokationResponse(response);
xmlDebug("performAuthenticatedXMLRPC: HTTP status %lu\n", HTTPResponseCode);
if ((HTTPResponseCode == 401) && (attempt == 0)) {
wsRef = WSMethodInvocationCreate(theURL, theMethod, kWSXMLRPCProtocol);
WSMethodInvocationSetParameters(wsRef, argDict, argOrder);
Boolean successful = addAuthenticationToWSInvokation(wsRef,
userName, password, response);
if (!successful) {
xmlDebug("performAuthenticatedXMLRPC: unable to add authentication\n");
ortn = ioErr;
done = TRUE;
}
xmlDebug("performAuthenticatedXMLRPC: retrying with auth\n");
} else {
xmlDebug("performAuthenticatedXMLRPC: fatal RPC error\n");
*httpErrStatus = HTTPResponseCode;
ortn = dotMacHttpStatToOs(HTTPResponseCode);
done = TRUE;
}
}
else {
xmlDebug("performAuthenticatedXMLRPC: fault with no fault string!\n");
ortn = ioErr;
done = TRUE;
}
}
else {
xmlDebug("performAuthenticatedXMLRPC: success\n");
result = CFDictionaryCreateCopy(NULL,
(CFDictionaryRef)CFDictionaryGetValue(
response, kWSMethodInvocationResult));
ortn = noErr;
done = true;
}
if((response != NULL) ) {
#if RESPONSE_DICTIONARY_DEBUG
dumpDictionary("XMLRPC response", response);
#endif
CFRelease(response);
}
if(done) {
break;
}
}
if(result != NULL) {
*resultDict = result;
}
return ortn;
}