xmlrpcjob.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "xmlrpcjob.h"
00021 #include "xmlrpcjob.moc"
00022
00023 #include "util.h"
00024
00025 #include <kdebug.h>
00026 #include <kcodecs.h>
00027 #include <klocale.h>
00028 #include <kurl.h>
00029 #include <kio/job.h>
00030
00031 #include <QDomElement>
00032 #include <QVariant>
00033 #include <QDateTime>
00034
00035 class Blokkal::Io::XmlRpcJob::Private {
00036 public:
00037 Private( void ) :
00038 responseBuffer( QByteArray() ),
00039 response( QVariant() ),
00040 responseType( ResponseError ){}
00041
00042 QByteArray responseBuffer;
00043 QVariant response;
00044 ResponseType responseType;
00045 };
00046
00047 Blokkal::Io::XmlRpcJob::XmlRpcJob( const KUrl & url,
00048 const QString & methodName,
00049 const QList< QVariant > & params,
00050 unsigned int workArounds,
00051 bool showProgressInfo ) :
00052 Job(),
00053 d( new Private() )
00054 {
00055 QByteArray postData;
00056
00057
00058 QDomDocument callDocument;
00059 callDocument.appendChild( callDocument.createProcessingInstruction( "xml", "version=\"1.0\"" ) );
00060
00061 QDomElement methodElement = callDocument.createElement( "methodCall" );
00062 callDocument.appendChild( methodElement );
00063
00064 QDomElement currentElement = callDocument.createElement( "methodName" );
00065 currentElement.appendChild( callDocument.createTextNode( methodName ) );
00066 methodElement.appendChild( currentElement );
00067
00068 QDomElement paramsElement = callDocument.createElement( "params" );
00069 methodElement.appendChild( paramsElement );
00070 for( QList< QVariant >::ConstIterator it = params.begin();
00071 it != params.end();
00072 ++it )
00073 {
00074 QDomElement paramElement = callDocument.createElement( "param" );
00075 paramsElement.appendChild( paramElement );
00076
00077 encodeData( paramElement, *it, workArounds );
00078 }
00079
00080 QTextStream stream( &postData, QIODevice::WriteOnly );
00081 callDocument.save( stream, 3 );
00082
00083
00084
00085 KIO::TransferJob * job = KIO::http_post( KUrl( url ),
00086 postData,
00087 showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo );
00088
00089 job->addMetaData( "UserAgent", "Blokkal/IO XML-RPC Job" );
00090 job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
00091 job->addMetaData( "ConnectTimeout", "300" );
00092
00093 connect( job,
00094 SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00095 SLOT( slotDataArrived( KIO::Job *, const QByteArray & ) ) );
00096 addSubjob( job );
00097 }
00098
00099 Blokkal::Io::XmlRpcJob::~XmlRpcJob( void )
00100 {
00101 delete d;
00102 }
00103
00104 void Blokkal::Io::XmlRpcJob::slotDataArrived( KIO::Job * job, const QByteArray & data )
00105 {
00106 if( subjobs().isEmpty() || job != subjobs().first() ) {
00107 return;
00108 }
00109
00110 d->responseBuffer.append( data );
00111 }
00112
00113 void Blokkal::Io::XmlRpcJob::jobFinished( void )
00114 {
00115
00116 if( error() ) {
00117 d->responseType = ResponseError;
00118 return;
00119 }
00120
00121 QString errorText;
00122 QDomDocument responseDocument;
00123 if( responseDocument.setContent( d->responseBuffer, &errorText ) ) {
00124 QDomNode rootNode = responseDocument.firstChild();
00125 for( ; !rootNode.isNull(); rootNode = rootNode.nextSibling() ) {
00126 if( rootNode.isElement() ) {
00127 break;
00128 }
00129 }
00130
00131 if( rootNode.firstChild().nodeName() == "params" ) {
00132
00133 QDomNode valueNode = rootNode.firstChild().firstChild().firstChild();
00134 d->response = decodeData( valueNode.toElement() );
00135 d->responseType = ResponseSuccess;
00136 }
00137 else if( rootNode.firstChild().nodeName() == "fault" ) {
00138
00139 setError( -1 );
00140 d->response = decodeData( rootNode.firstChild().firstChild().toElement() );
00141 if( d->response.type() == QVariant::Map ) {
00142 setErrorText( ( ( d->response.toMap() )["faultString"] ).toString() );
00143 }
00144 d->responseType = ResponseFault;
00145 }
00146 else {
00147 setError( -1 );
00148 setErrorText( i18n( "Could not parse response." ) );
00149 d->responseType = ResponseError;
00150 }
00151 }
00152 else {
00153 setError( -1 );
00154 setErrorText( errorText );
00155 d->responseType = ResponseError;
00156 }
00157
00158 }
00159
00160 const QVariant & Blokkal::Io::XmlRpcJob::response( void ) const
00161 {
00162 return d->response;
00163 }
00164
00165 Blokkal::Io::XmlRpcJob::ResponseType Blokkal::Io::XmlRpcJob::responseType( void ) const
00166 {
00167 return d->responseType;
00168 }
00169
00170 void Blokkal::Io::XmlRpcJob::encodeData( QDomElement & containerElement, QVariant data, unsigned int workArounds )
00171 {
00172 QDomDocument callDocument = containerElement.ownerDocument();
00173 QDomElement valueElement = callDocument.createElement( "value" );
00174 containerElement.appendChild( valueElement );
00175 QDomElement currentElement;
00176 switch ( data.type() ) {
00177 case QVariant::Int:
00178 currentElement = callDocument.createElement( "int" );
00179 valueElement.appendChild( currentElement );
00180 currentElement.appendChild( callDocument.createTextNode( data.toString() ) );
00181 break;
00182 case QVariant::Bool:
00183 currentElement = callDocument.createElement( "boolean" );
00184 valueElement.appendChild( currentElement );
00185 currentElement.appendChild( callDocument.createTextNode( data.toBool() ? "1" : "0" ) );
00186 break;
00187 case QVariant::String:
00188 currentElement = callDocument.createElement( "string" );
00189 valueElement.appendChild( currentElement );
00190 currentElement.appendChild( callDocument.createTextNode( data.toString() ) );
00191 break;
00192 case QVariant::Double:
00193 currentElement = callDocument.createElement( "double" );
00194 valueElement.appendChild( currentElement );
00195 currentElement.appendChild( callDocument.createTextNode( data.toString() ) );
00196 break;
00197 case QVariant::DateTime:
00198 currentElement = callDocument.createElement( "dateTime.iso8601" );
00199 valueElement.appendChild( currentElement );
00200 if( workArounds & WordPressDateWorkAround ) {
00201 currentElement.appendChild( callDocument.createTextNode( data.toDateTime().toString( Qt::ISODate ).remove( '-' ) ) );
00202 }
00203 else {
00204 currentElement.appendChild( callDocument.createTextNode( data.toDateTime().toString( Qt::ISODate ) ) );
00205 }
00206 break;
00207 case QVariant::ByteArray:
00208 currentElement = callDocument.createElement( "base64" );
00209 valueElement.appendChild( currentElement );
00210 currentElement.appendChild( callDocument.createTextNode( KCodecs::base64Encode( data.toByteArray() ) ) );
00211 break;
00212 case QVariant::Map:
00213 {
00214 currentElement = callDocument.createElement( "struct" );
00215 valueElement.appendChild( currentElement );
00216 const QMap<QString, QVariant> mapData = data.toMap();
00217 for( QMap<QString, QVariant>::ConstIterator it = mapData.begin();
00218 it != mapData.end();
00219 ++it )
00220 {
00221 QDomElement memberElement = callDocument.createElement( "member" );
00222 currentElement.appendChild( memberElement );
00223 QDomElement nameElement = callDocument.createElement( "name" );
00224 memberElement.appendChild( nameElement );
00225 nameElement.appendChild( callDocument.createTextNode( it.key() ) );
00226 encodeData( memberElement, it.value(), workArounds );
00227 }
00228 }
00229 break;
00230 case QVariant::List:
00231 case QVariant::StringList:
00232 {
00233 currentElement = callDocument.createElement( "array" );
00234 valueElement.appendChild( currentElement );
00235 QDomElement dataElement = callDocument.createElement( "data" );
00236 currentElement.appendChild( dataElement );
00237 const QList<QVariant> arrayData = data.toList();
00238 for( QList<QVariant>::ConstIterator it = arrayData.begin();
00239 it != arrayData.end();
00240 ++it )
00241 {
00242 encodeData( dataElement, *it, workArounds );
00243 }
00244 }
00245 break;
00246 default:
00247 kError() << "encountered invalid parameter type: " << data.type() << endl;
00248 break;
00249 }
00250 }
00251
00252 QVariant Blokkal::Io::XmlRpcJob::decodeData( const QDomElement & valueElement )
00253 {
00254 Q_ASSERT( valueElement.nodeName() == "value" );
00255
00256 QDomElement typeElement = valueElement.firstChild().toElement();
00257 QString type = typeElement.nodeName();
00258 if( type == "int" || type == "i4" ) {
00259 return QVariant( typeElement.text().toInt() );
00260 }
00261
00262 if ( type == "boolean" ) {
00263 if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) {
00264 return QVariant( TRUE );
00265 }
00266 else {
00267 return QVariant( FALSE );
00268 }
00269 }
00270
00271 if( type == "double" ) {
00272 return QVariant( typeElement.text().toDouble() );
00273 }
00274
00275 if ( type == "dateTime.iso8601" ) {
00276 if( typeElement.text().contains( '-' ) ) {
00277 return QVariant( QDateTime::fromString( typeElement.text(), Qt::ISODate ) );
00278 }
00279 else {
00280 return QVariant( QDateTime::fromString( typeElement.text().insert( 6, '-' ).insert( 4, '-' ), Qt::ISODate ) );
00281 }
00282 }
00283
00284 if ( type == "base64" ) {
00285 return QVariant( KCodecs::base64Decode( typeElement.text().toLatin1() ) );
00286 }
00287
00288 if( type == "struct" ) {
00289 QMap<QString, QVariant> map;
00290 QDomNode memberNode = typeElement.firstChild();
00291 while ( !memberNode.isNull() ) {
00292 const QString name = memberNode.namedItem( "name" ).toElement().text();
00293 const QVariant value = decodeData( memberNode.namedItem( "value" ).toElement() );
00294 map[ name ] = value;
00295 memberNode = memberNode.nextSibling();
00296 }
00297 return QVariant( map );
00298 }
00299
00300 if ( type == "array" ) {
00301 QList<QVariant> list;
00302 QDomNode valueNode = typeElement.firstChild().firstChild();
00303 while ( !valueNode.isNull() ) {
00304 list.append( decodeData( valueNode.toElement() ));
00305 valueNode = valueNode.nextSibling();
00306 }
00307 return QVariant( list );
00308 }
00309
00310
00311
00312
00313 if( type == "string" ||
00314 !valueElement.firstChild().toText().isNull() )
00315 {
00316 return QVariant( Util::decodeUtf8Data( valueElement.text().toUtf8() ) );
00317 }
00318
00319
00320 kError() << "encountered invalid type: " << type << endl;
00321 kError() << "returning invalid QVariant" << endl;
00322 return QVariant();
00323 }