Select Git revision
SpiceClient.cpp
-
Adam Paquette authored
* Fix segfault from spiceclient * Updated changelog
Adam Paquette authored* Fix segfault from spiceclient * Updated changelog
SpiceClient.cpp 17.98 KiB
#include "SpiceClient.h"
#include <sstream>
#include <QDomElement>
#include <QFile>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include "Application.h"
#include "Blob.h"
#include "Constants.h"
#include "IException.h"
#include "IString.h"
#include "Pvl.h"
#include "Table.h"
#include "TextFile.h"
using namespace std;
using namespace Isis;
namespace Isis {
/**
* This initializes a SpiceClient. It forms the XML to send and
* starts in its own thread.
*
* @param url
* @param port
* @param cubeLabel
* @param ckSmithed
* @param ckRecon
* @param ckNadir
* @param ckPredicted
* @param spkSmithed
* @param spkRecon
* @param spkPredicted
* @param shape
* @param startPad
* @param endPad
*/
SpiceClient::SpiceClient(QString url, int port, Pvl &cubeLabel,
bool ckSmithed, bool ckRecon, bool ckNadir, bool ckPredicted,
bool spkSmithed, bool spkRecon, bool spkPredicted,
QString shape, double startPad, double endPad) : QThread() {
p_xml = NULL;
p_networkMgr = NULL;
p_request = NULL;
p_response = NULL;
p_rawResponse = NULL;
p_error = NULL;
QString raw;
p_xml = new QString();
raw = "<input_label>\n";
raw += " <isis_version>\n";
QString version = Application::Version();
QByteArray isisVersionRaw(version.toLatin1());
raw += QString(isisVersionRaw.toHex().constData()) + "\n";
raw += " </isis_version>\n";
raw += " <parameters>\n";
raw += " <cksmithed value='" + toString(ckSmithed) + "' />\n";
raw += " <ckrecon value='" + toString(ckRecon) + "' />\n";
raw += " <ckpredicted value='" + toString(ckPredicted) + "' />\n";
raw += " <cknadir value='" + toString(ckNadir) + "' />\n";
raw += " <spksmithed value='" + toString(spkSmithed) + "' />\n";
raw += " <spkrecon value='" + toString(spkRecon) + "' />\n";
raw += " <spkpredicted value='" + toString(spkPredicted) + "' />\n";
raw += " <shape value='" + shape + "' />\n";
raw += " <startpad time='" + toString(startPad) + "' />\n";
raw += " <endpad time='" + toString(endPad) + "' />\n";
raw += " </parameters>\n";
raw += " <label>\n";
stringstream str;
str << cubeLabel;
raw += QString(QByteArray(str.str().c_str()).toHex().constData()) + "\n";
raw += " </label>\n";
raw += "</input_label>";
*p_xml = QString(QByteArray(raw.toLatin1()).toHex().constData());
int contentLength = p_xml->length();
QString contentLengthStr = toString((BigInt)contentLength);
p_request = new QNetworkRequest();
p_request->setUrl(QUrl(url));
p_request->setRawHeader("User-Agent", "SpiceInit 1.0");
p_request->setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
moveToThread(this);
start();
}
/**
* This cleans up the spice client.
*
*/
SpiceClient::~SpiceClient() {
delete p_xml;
p_xml = NULL;
delete p_error;
p_error = NULL;
delete p_networkMgr;
p_networkMgr = NULL;
delete p_request;
p_request = NULL;
delete p_response;
p_response = NULL;
delete p_rawResponse;
p_rawResponse = NULL;
}
/**
* This POSTS to the spice server
*/
void SpiceClient::sendRequest() {
p_networkMgr = new QNetworkAccessManager();
connect(p_networkMgr, SIGNAL(finished(QNetworkReply *)),
this, SLOT(replyFinished(QNetworkReply *)));
connect(p_networkMgr, SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)),
this, SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *)));
connect(p_networkMgr, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
connect(p_networkMgr, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
this, SLOT(sslErrors(QNetworkReply *, const QList<QSslError> &)));
QByteArray data;
QUrl params;
QUrlQuery query;
query.addQueryItem("name", *p_xml);
params.setQuery(query);
data.append(params.toEncoded());
p_networkMgr->post(*p_request, data);
exec();
}
/**
* This is called when the server responds.
*
* @param reply
*/
void SpiceClient::replyFinished(QNetworkReply *reply) {
// Check for redirection
QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redirectionTarget.isNull()) {
QUrl redirectedUrl = redirectionTarget.toUrl();
// Update the request with the new URL and re-send
p_request->setUrl(redirectedUrl);
sendRequest();
return;
}
// No redirection, proceed with the original processing
p_rawResponse = new QString(QString(reply->readAll()));
// Decode the response
p_response = new QString();
try {
*p_response = QString(
QByteArray::fromHex(QByteArray(p_rawResponse->toLatin1())).constData());
// Make sure we can get the log out of it before continuing
applicationLog();
}
catch(IException &) {
p_error = new QString();
// Well, the XML is bad, maybe it's PVL
try {
Pvl pvlTest;
stringstream s;
s << *p_rawResponse;
s >> pvlTest;
PvlGroup &err = pvlTest.findGroup("Error", Pvl::Traverse);
*p_error = "The Spice Server was unable to initialize the cube.";
if (err.findKeyword("Message")[0] != "") {
*p_error += " The error reported was: ";
*p_error += err.findKeyword("Message")[0];
}
}
catch(IException &) {
if (reply->error() != QNetworkReply::NoError) {
*p_error = "An error occurred when talking to the server";
switch (reply->error()) {
case QNetworkReply::NoError:
break;
case QNetworkReply::ConnectionRefusedError:
*p_error += ". The server refused the connection";
break;
case QNetworkReply::RemoteHostClosedError:
*p_error += ". The server closed the connection";
break;
case QNetworkReply::HostNotFoundError:
*p_error += ". The server was not found";
break;
case QNetworkReply::TimeoutError:
*p_error += ". The connection timed out";
break;
case QNetworkReply::OperationCanceledError:
*p_error += ". We aborted the network operation";
break;
case QNetworkReply::SslHandshakeFailedError:
*p_error += ". Could not establish an encrypted connection";
break;
case QNetworkReply::TemporaryNetworkFailureError:
*p_error += ". There was a temporary network failure";
break;
case QNetworkReply::NetworkSessionFailedError:
*p_error += ". The connection was broken";
break;
case QNetworkReply::BackgroundRequestNotAllowedError:
*p_error += ". The background request is not allowed";
break;
case QNetworkReply::TooManyRedirectsError:
*p_error += ". The maximum limit of redirects was reached";
break;
case QNetworkReply::InsecureRedirectError:
*p_error += ". A redirect from https to http occurred";
break;
case QNetworkReply::ProxyConnectionRefusedError:
*p_error += ". The proxy server refused the connection";
break;
case QNetworkReply::ProxyConnectionClosedError:
*p_error += ". The proxy server closed the connection";
break;
case QNetworkReply::ProxyNotFoundError:
*p_error += ". The proxy server could not be found";
break;
case QNetworkReply::ProxyTimeoutError:
*p_error += ". The connection to the proxy server timed out";
break;
case QNetworkReply::ProxyAuthenticationRequiredError:
*p_error += ". The proxy server requires authentication";
break;
case QNetworkReply::ContentAccessDenied:
*p_error += ". Access to the remove content was denied (401)";
break;
case QNetworkReply::ContentOperationNotPermittedError:
*p_error += ". The operation requested on the server is not "
"permitted";
break;
case QNetworkReply::ContentNotFoundError:
*p_error += ". The spice server script was not found (404)";
break;
case QNetworkReply::AuthenticationRequiredError:
*p_error += ". The server requires authentication";
break;
case QNetworkReply::ContentReSendError:
*p_error += ". The server requests for you to try again";
break;
case QNetworkReply::ContentConflictError:
*p_error += ". There is a conflict with the current state of the resource";
break;
case QNetworkReply::ContentGoneError:
*p_error += ". The requested resource is no longer available";
break;
case QNetworkReply::InternalServerError:
*p_error += ". The server encountered an unexpected error";
break;
case QNetworkReply::OperationNotImplementedError:
*p_error += ". The server does not support the functionality required to "
"the request";
break;
case QNetworkReply::ServiceUnavailableError:
*p_error += ". The server is unable to handle the request at this time.";
break;
case QNetworkReply::ProtocolUnknownError:
*p_error += ". The attempted network protocol is unknown";
break;
case QNetworkReply::ProtocolInvalidOperationError:
*p_error += ". The network protocol did not support this "
"operation";
break;
case QNetworkReply::UnknownNetworkError:
*p_error += ". An unknown network-related error occurred";
break;
case QNetworkReply::UnknownProxyError:
*p_error += ". An unknown proxy-related error occurred";
break;
case QNetworkReply::UnknownContentError:
*p_error += ". An unknown content-related error occurred";
break;
case QNetworkReply::ProtocolFailure:
*p_error += ". A breakdown in the protocol was detected";
break;
case QNetworkReply::UnknownServerError:
*p_error += ". An unknown error related to the server occurred.";
break;
}
}
else {
// Well, we really don't know what this is.
*p_error = "The server sent an unrecognized response";
if (*p_rawResponse != "") {
*p_error += " [";
*p_error += *p_rawResponse;
*p_error += "]";
}
}
}
}
quit();
}
/**
* Called if the server requires a authentication
*/
void SpiceClient::authenticationRequired(QNetworkReply *, QAuthenticator *) {
if(!p_response) {
p_rawResponse = new QString();
p_response = new QString();
}
if (!p_error) {
p_error = new QString();
}
*p_error = "Server expects authentication which is not currently ";
*p_error += "supported";
quit();
}
/**
* Called if the server requires a authentication
*/
void SpiceClient::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *) {
if(!p_response) {
p_rawResponse = new QString();
p_response = new QString();
}
if (!p_error) {
p_error = new QString();
}
*p_error = "Server expects authentication which is not currently ";
*p_error += "supported";
quit();
}
/**
* @param reply
* @param err
*/
void SpiceClient::sslErrors(QNetworkReply *reply,
const QList<QSslError> & err) {
if(!p_response) {
p_rawResponse = new QString();
p_response = new QString();
}
if (!p_error) {
p_error = new QString();
}
*p_error = "Server expects authentication which is not currently ";
*p_error += "supported";
quit();
}
/**
* This returns the root of the returned XML, throws an
* appropriate error if the XML is wrong or missing.
*
* @return QDomElement
*/
QDomElement SpiceClient::rootXMLElement() {
if(!p_response || !p_rawResponse) {
QString error = "No server response available";
throw IException(IException::Io, error, _FILEINFO_);
}
QDomDocument document;
QString errorMsg;
int errorLine, errorCol;
if(!p_response->isEmpty() &&
document.setContent(QString(p_response->toLatin1()),
&errorMsg, &errorLine, &errorCol)) {
return document.firstChild().toElement();
}
else {
QString msg = "Unexpected response from spice server [";
msg += *p_rawResponse;
msg += "]";
throw IException(IException::Io, msg, _FILEINFO_);
}
}
/**
* This finds a tag (i.e. \<some_tag>) inside the current
* element.
*
* @param currentElement
* @param name
*
* @return QDomElement
*/
QDomElement SpiceClient::findTag(QDomElement currentElement, QString name) {
QString qtName = name;
for(QDomNode node = currentElement.firstChild();
!node .isNull();
node = node.nextSibling()) {
QDomElement element = node.toElement();
if(element.tagName() == qtName) {
return element;
}
}
QString msg = "Server response missing XML Tag [" + name + "]";
throw IException(IException::Io, msg, _FILEINFO_);
}
/**
* This returns the spiceinit'd PvlGroup from the server.
*
* @return PvlGroup
*/
PvlGroup SpiceClient::kernelsGroup() {
checkErrors();
QDomElement root = rootXMLElement();
QDomElement kernelsLabel = findTag(root, "kernels_label");
QString kernelsLabels = elementContents(kernelsLabel);
QString unencoded(QByteArray::fromHex(kernelsLabels.toLatin1()).constData());
stringstream pvlStream;
pvlStream << unencoded;
Pvl labels;
pvlStream >> labels;
return labels.findGroup("Kernels", Pvl::Traverse);
}
/**
* This returns the PvlGroup we should log to the console
*
* @return PvlGroup
*/
PvlGroup SpiceClient::applicationLog() {
checkErrors();
QDomElement root = rootXMLElement();
QDomElement logLabel = findTag(root, "application_log");
QString logLabels = elementContents(logLabel);
QString unencoded(QByteArray::fromHex(logLabels.toLatin1()).constData());
stringstream pvlStream;
pvlStream << unencoded;
Pvl labels;
pvlStream >> labels;
return labels.findGroup("Kernels", Pvl::Traverse);
}
/**
* This returns the contents of the current element as a string.
* \<element>
* Contents
* \</element>
*
* @param element
*
* @return QString
*/
QString SpiceClient::elementContents(QDomElement element) {
return element.firstChild().toText().data();
}
/**
* This returns the table given by the server. The ownership of the table is
* given to the caller.
*
* @return Table*
*/
Table *SpiceClient::pointingTable() {
return readTable("instrument_pointing", "InstrumentPointing");
}
/**
* This returns the table given by the server. The ownership of the table is
* given to the caller.
*
* @return Table*
*/
Table *SpiceClient::positionTable() {
return readTable("instrument_position", "InstrumentPosition");
}
/**
* This returns the table given by the server. The ownership of the table is
* given to the caller.
*
* @return Table*
*/
Table *SpiceClient::bodyRotationTable() {
return readTable("body_rotation", "BodyRotation");
}
/**
* This returns the table given by the server.
*/
Table *SpiceClient::sunPositionTable() {
return readTable("sun_position", "SunPosition");
}
/**
* This throws an exception if an error occurred
*/
void SpiceClient::checkErrors() {
if(p_error) {
throw IException(IException::Unknown, *p_error, _FILEINFO_);
}
}
/**
* This yields the current thread until the server response is
* received and initial (basic) processing is complete.
*/
void SpiceClient::blockUntilComplete() {
while(isRunning()) {
yieldCurrentThread();
}
}
PvlObject SpiceClient::naifKeywordsObject() {
checkErrors();
QDomElement root = rootXMLElement();
QDomElement kernelsLabel = findTag(root, "kernels_label");
QString kernelsLabels = elementContents(kernelsLabel);
QString unencoded(QByteArray::fromHex(kernelsLabels.toLatin1()).constData());
stringstream pvlStream;
pvlStream << unencoded;
Pvl labels;
pvlStream >> labels;
return labels.findObject("NaifKeywords");
}
/**
* Converts a boolean to "yes" or "no"
*
* @param boolVal
*
* @return QString
*/
QString SpiceClient::yesNo(bool boolVal) {
if(boolVal)
return "yes";
else
return "no";
}
Table *SpiceClient::readTable(QString xmlName, QString tableName) {
checkErrors();
QDomElement root = rootXMLElement();
QDomElement tablesTag = findTag(root, "tables");
QDomElement pointingTag = findTag(tablesTag, xmlName);
QString encodedString = elementContents(pointingTag);
QByteArray encodedArray;
encodedArray.append(encodedString.toUtf8());
QByteArray unencodedArray(QByteArray::fromHex(encodedArray));
stringstream tableStream;
tableStream.write(unencodedArray.data(), unencodedArray.size());
Pvl lab;
tableStream >> lab;
Blob tableBlob(tableName, "Table");
tableBlob.Read(lab, tableStream);
Table *table = new Table(tableBlob);
return table;
}
};