hs.hsadmin/hsacppcli/hsadminc/source/transaction.cpp

503 lines
16 KiB
C++
Raw Permalink Normal View History

/***************************************************************************
* Copyright (C) 2005 by Christof Donat *
* cdonat@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "transaction.h"
#include "xmlparser.h"
#include "httpclient.h"
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/format.hpp>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
using boost::lexical_cast;
using boost::bad_lexical_cast;
using namespace xmlParser;
Transaction::Transaction(vector<string> options) :
abstractcommandlineparser::CmdLineParser<commandline::Parameters,commandline::parsedParameters>(options),
m_nextAttributeValue(""),
m_globalFault(false),
m_currentCall(0) {
if(!this->m_parseSuccessful) return;
vector<shared_ptr<commandline::callParameter> >& calls = m_parsed->m_call;
for( vector<shared_ptr<commandline::callParameter> >::iterator c = calls.begin(); c != calls.end(); c++ ) {
shared_ptr<commandline::callParameter> call = *c;
vector<shared_ptr<commandline::orderParameter> >::iterator orderhere = call->m_order.end();
if( call->m_globalOrderIndex >= 0 ) orderhere = call->m_order.begin()+call->m_globalOrderIndex;
call->m_order.insert( orderhere, m_parsed->m_globalOrder.begin(), m_parsed->m_globalOrder.end() );
call->m_where.insert( call->m_where.end(), m_parsed->m_only.begin(), m_parsed->m_only.end() );
call->m_set.insert( call->m_set.end(), m_parsed->m_setall.begin(), m_parsed->m_setall.end() );
call->m_unset.insert( call->m_unset.end(), m_parsed->m_unsetall.begin(), m_parsed->m_unsetall.end() );
if( m_parsed->m_ignoreerrors ) call->m_ignoreerror = true;
}
};
void Transaction::operator()(shared_ptr<ConfigFileParser> cfgfile) {
class {
public:
void post(string &url) {
GenericHttpClient * client = createHttpClient(url);
m_response = client->post(m_request);
};
void execCall(Transaction *t, string &username, string &url, shared_ptr<ConfigFileParser> cfgfile) {
m_request = t->getParsed()->toXML(username,cfgfile);
Logger::log(Logger::XML,"--XML request--\n"+m_request+"\n---");
this->post(url);
ResponseParser parse;
if( !parse(m_response, t) ) Logger::log(Logger::XML,"parse error");
Logger::log(Logger::XML,"response parsed:");
if(Logger::XML && Logger::levels[Logger::level]) logNode(t->m_docelem,0);
}
void logNode(Node* node, int ind) {
Text *text;
Element *elm;
string indent = "";
for( int j = 0; j < ind; j++ ) indent += " ";
string attributes;
switch(node->m_type) {
case ELEMENT:
elm = (Element*)node;
for(map<string,string>::iterator i = elm->m_attributes.begin(); i != elm->m_attributes.end(); i++)
attributes += " "+(i->first)+"=\""+(i->second)+"\"";
Logger::log(Logger::XML,indent+"<"+elm->m_name+attributes+">");
for( vector<Node*>::iterator i = elm->m_nodes.begin(); i != elm->m_nodes.end(); i++ )
logNode(*i,ind+1);
Logger::log(Logger::XML,indent+"</"+elm->m_name+">");
break;
case TEXT:
text = (Text*)node;
Logger::log(Logger::XML,indent+text->m_content);
break;
}
}
private:
int connectToHost(const string hostname, const int port) const {
int sock;
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
if( (server_addr.sin_addr.s_addr = inet_addr(hostname.c_str())) == INADDR_NONE ) {
struct hostent* hp = gethostbyname(hostname.c_str());
if( hp == 0 ) {
string msg = Logger::getMessageFormatString(Logger::CantResolveHostname);
boost::format fmt(msg);
fmt % hostname;
Logger::log(Logger::FATAL,fmt.str());
exit(1);
}
bcopy(hp->h_addr, (char*) &server_addr.sin_addr, hp->h_length);
}
server_addr.sin_port = htons(port);
if( (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) {
Logger::log(Logger::FATAL,Logger::getMessageFormatString(Logger::CantOpenSocket));
exit(1);
}
if( connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0 ) {
string msg = Logger::getMessageFormatString(Logger::CantConnetcToHost);
boost::format fmt(msg);
fmt % hostname % port;
Logger::log(Logger::FATAL,fmt.str());
close(sock);
exit(1);
}
return sock;
}
string m_request;
string m_response;
} XMLRPCrequest;
string username = "";
ConfigFileParser::config cfg = cfgfile->getConfig(username);
string url = cfg.server;
username = this->getUser();
cfg = cfgfile->getConfig(username);
if( cfg.server != "" ) url = cfg.server;
Logger::log(Logger::DEBUG,"now I know which Config to use");
XMLRPCrequest.execCall(this,username,url,cfgfile);
}
// XML Parser stuff
Element* Transaction::stepin(int count,Element* value) {
if( count<= 0 ) return value;
for( ; count > 0; count-- )
if(value->m_nodes[0]->m_type == xmlParser::ELEMENT ) value = (Element*)value->m_nodes[0];
return value;
}
string Transaction::getContent(Element* value) {
while( value->m_nodes.size() > 0 && value->m_nodes[0]->m_type == xmlParser::ELEMENT ) value = (Element*)value->m_nodes[0];
if( value->m_nodes.size() > 0 ) {
Text* txt = (Text*)value->m_nodes[0];
if( value->m_name == "boolean") {
int b = boost::lexical_cast<int>(replaceEntities(txt->m_content));
return (b == 0)?"false":"true";
} else if( value->m_name == "dateTime.iso8601") {
string datestring = replaceEntities(txt->m_content);
string year = datestring.substr(0,4);
string month = datestring.substr(4,2);
string day = datestring.substr(6,2);
string hour = datestring.substr(9,2);
string minute= datestring.substr(12,2);
string second= datestring.substr(15,2);
return day+"."+month+"."+year+" "+hour+":"+minute+":"+second;
} else if( value->m_name == "base64") {
string encoded = replaceEntities(txt->m_content);
string decoded;
char next = '\0';
int shift = 0;
while( encoded.size() ) {
int d = encoded[0];
encoded = encoded.substr(1,encoded.size()-1);
if ( d >= 'A' && d <= 'Z') d -= 'A';
else if ( d >= 'a' && d <= 'z') d = (d-'a')+26;
else if ( d >= '0' && d <= '9') d = (d-'0')+52;
else if ( d == '+') d = 62;
else if ( d == '/') d = 63;
else continue;
next += d << shift;
if( !shift ) { decoded += (char)(next&255); next = next >> 8; }
shift = (shift-2)%8;
}
if( shift ) decoded += next;
return decoded;
} else {
return replaceEntities(txt->m_content);
}
}
return string("");
}
void Transaction::handleElem(Element * elem) {
if( m_tagstack.size() == 0 ) {
m_docelem = elem;
return;
}
Element *array, *data;
int tab = 8;
switch( m_tagstack.size() ) {
case 6: // fill next call
if(elem->m_name != "value" ) break;
array = (Element*)(elem->m_nodes[0]);
if( array->m_name == "struct" ) {
map<string,string> errorstruct;
if(array->m_nodes.size() != 0) for(vector<Node*>::iterator n = array->m_nodes.begin(); n != array->m_nodes.end(); n++) {
Element* member = (Element*)(*n);
if(member->m_nodes.size() >= 2) {
Element *name, *value;
name = (Element*)member->m_nodes[0];
if( name->m_name == "value" ) {
value = name;
name = (Element*)member->m_nodes[1];
} else
value = (Element*)member->m_nodes[1];
while( name->m_nodes.size() > 0 && name->m_nodes[0]->m_type == xmlParser::ELEMENT ) name = (Element*)name->m_nodes[0];
while( value->m_nodes.size() > 0 && value->m_nodes[0]->m_type == xmlParser::ELEMENT ) value = (Element*)value->m_nodes[0];
if( value->m_nodes.size() > 0 && name->m_nodes.size() > 0 ) {
string namtxt = getContent(name);
string valtxt = getContent(value);
errorstruct[namtxt] = valtxt;
}
}
}
string msg = Logger::getMessageFormatString(Logger::ServerErrorCode);
int errnum = boost::lexical_cast<int>(errorstruct["errorcode"]);
string errorstring = Logger::getErrnoMessage(errnum);
if( errorstring != "" ) errorstruct["errorstring"] = errorstring;
boost::format fmt(msg);
fmt % errorstruct["errorcode"] % errorstruct["errorstring"];
Logger::log(Logger::ERROR,fmt.str());
break;
}
data = (Element*)(array->m_nodes[0]);
if(data->m_nodes.size() != 0) {
vector<string> headlines;
int tabpos = 0;
vector<int> tabstops;
Element* headlinesdata = stepin(3,data);
if( m_parsed->m_call[m_currentCall]->m_display == "" ) {
if(headlinesdata->m_nodes.size() != 0) for(vector<Node*>::iterator in = headlinesdata->m_nodes.begin(); in != headlinesdata->m_nodes.end(); in++) {
string output = "";
if((*in)->m_type == xmlParser::ELEMENT ) {
output = getContent((Element*)(*in));
} else {
Text* txt = (Text*)(*in);
output = replaceEntities(txt->m_content);
}
std::cout << output;
tabstops.push_back(tabpos);
tabpos += output.length();
if( in+1 != headlinesdata->m_nodes.end() )do {
std::cout << " ";
tabpos++;
} while( tabpos % tab );
}
std::cout << "\n";
for(;tabpos;tabpos--) std::cout << "-";
std::cout << "\n";
} else {
if(headlinesdata->m_nodes.size() != 0) for(vector<Node*>::iterator in = headlinesdata->m_nodes.begin(); in != headlinesdata->m_nodes.end(); in++) {
string headline = getContent((Element*)(*in));
if( headline != "" ) headlines.push_back( headline );
}
}
Element* contentvalue;
if(data->m_nodes[1]->m_type == xmlParser::ELEMENT ) contentvalue = (Element*)data->m_nodes[1];
else contentvalue = data;
Element* contentdata = stepin(2,contentvalue);
if(contentdata->m_nodes.size() != 0) for(vector<Node*>::iterator n = contentdata->m_nodes.begin(); n != contentdata->m_nodes.end(); n++) {
if( m_parsed->m_call[m_currentCall]->m_display == "" ) {
tabpos = 0;
int i = 0;
Element* innerdata = stepin(2,(Element*)(*n));
if(innerdata->m_nodes.size() != 0) for(vector<Node*>::iterator in = innerdata->m_nodes.begin(); in != innerdata->m_nodes.end(); in++) {
string oputput = getContent((Element*)(*in));
while( tabpos < tabstops[i] ) {
std::cout << " "; tabpos++;
}
std::cout << oputput;
tabpos += oputput.length();
i++;
}
std::cout << "\n";
} else {
map<string,string> values;
int i = 0;
Element* innerdata = stepin(2,(Element*)(*n));
if(innerdata->m_nodes.size() != 0) for(vector<Node*>::iterator in = innerdata->m_nodes.begin(); in != innerdata->m_nodes.end(); in++) {
string oputput = getContent((Element*)(*in));
if( oputput != "" ) {
values[headlines[i]] = oputput;
i++;
}
}
std::cout << m_parsed->m_call[m_currentCall]->evalDisplay(values);
}
}
}
m_currentCall++;
break;
default:
break;
}
m_tagstack.top().m_nodes.push_back(elem);
}
void Transaction::handleGlobalFaultElem(xmlParser::Element * elem) {
if( m_tagstack.size() == 0 ) {
m_docelem = elem;
return;
}
Text *txt;
switch( m_tagstack.size() ) {
case 5:
if(elem->m_name == "value" )
Logger::log(Logger::FATAL,getContent(elem));
break;
default:
break;
}
m_tagstack.top().m_nodes.push_back(elem);
}
string Transaction::replaceEntities(const string &input) {
string rval;
for( string::const_iterator i = input.begin(); i != input.end(); i++ ) {
if( *i != '&' ) rval += *i;
else {
string tmp;
int j;
for( j = 1; *(i+j) != ';' && i+j != input.end(); j++ ) tmp += *(i+j);
if( *(i+j) != ';' ) { rval += "&"; continue; }
if( tmp == "amp" ) {
rval += "&";
i+=j;
} else if( tmp == "gt" ) {
rval += ">";
i+=j;
} else if( tmp == "lt" ) {
rval += "<";
i+=j;
} else if( tmp == "quot" ) {
rval += "\"";
i+=j;
} else if( tmp == "apos" ) {
rval += "'";
i+=j;
} else if( tmp[0] == '#' ) {
if( tmp[1] != 'x' ) {
wchar_t value[2];
value[0] = lexical_cast<int>(tmp.substr(1));
value[1] = 0;
char u[255];
bzero(u,255);
wcstombs(u,value,254);
rval += u;
i+=j;
} else {
wchar_t value[2];
value[0] = 0;
value[1] = 0;
if ( tmp[2] >= '0' && tmp[2] <= '9' ) value[0] = tmp[2]-'0';
else if( tmp[2] >= 'a' && tmp[2] <= 'f' ) value[0] = tmp[2]-'a';
else if( tmp[2] >= 'A' && tmp[2] <= 'F' ) value[0] = tmp[2]-'A';
else { rval += "&"; continue;}
value[0] = value[0] << 4;
if ( tmp[3] >= '0' && tmp[3] <= '9' ) value[0] += tmp[3]-'0';
else if( tmp[3] >= 'a' && tmp[3] <= 'f' ) value[0] += tmp[3]-'a';
else if( tmp[3] >= 'A' && tmp[3] <= 'F' ) value[0] += tmp[3]-'A';
else { rval += "&"; continue; }
char u[255];
bzero(u,255);
wcstombs(u,value,254);
rval += u;
i+=j;
}
} else {
rval += "&";
}
}
}
return rval;
}
bool Transaction::operator()(int type, string content) {
string tmp;
Element elem, currentelem;
Text text;
switch( type ) {
case responseParserHook::ELEMENT:
break;
case responseParserHook::ATTRIBUTE:
m_nextAttributeList[content] = m_nextAttributeValue;
m_nextAttributeValue = "";
break;
case responseParserHook::TEXT:
if( m_tagstack.size() ) {
m_tagstack.top().m_nodes.push_back(new Text(content));
} else return false;
break;
case responseParserHook::CDATA:
if( m_tagstack.size() ) {
m_tagstack.top().m_nodes.push_back(new Text(content));
} else return false;
break;
case responseParserHook::PROCESSING_INSTRUCTION:
if( m_nextAttributeList["version"] != "1.0" || content != "xml" ) return false;
tmp = "";
for(map<string,string>::iterator i = m_nextAttributeList.begin(); i != m_nextAttributeList.end(); i++)
tmp += " "+(i->first)+"="+(i->second);
m_nextAttributeList.clear();
break;
case responseParserHook::COMMENT:
break;
case responseParserHook::DOCUMENT:
break;
case responseParserHook::START_ELEMENT:
if(m_tagstack.size() == 1 && content == "fault") m_globalFault = true;
elem = Element(content,m_nextAttributeList,vector<Node*>());
m_nextAttributeList.clear();
m_tagstack.push(elem);
break;
case responseParserHook::END_ELEMENT:
currentelem = m_tagstack.top();
m_tagstack.pop();
if( currentelem.m_name != content ) {
return false;
}
if( !m_globalFault ) handleElem(new Element(currentelem));
else handleGlobalFaultElem(new Element(currentelem));
break;
case responseParserHook::EMPTY_ELEMENT:
if(m_tagstack.size() == 1 && content == "fault") m_globalFault = true;
if( !m_globalFault ) handleElem(new Element(content,m_nextAttributeList,vector<Node*>()));
else handleGlobalFaultElem(new Element(content,m_nextAttributeList,vector<Node*>()));
m_nextAttributeList.clear();
break;
case responseParserHook::ATTRIBUTE_VALUE:
m_nextAttributeValue = content;
break;
default:
string msg = Logger::getMessageFormatString(Logger::UnknownCallbackInXMLParser);
boost::format fmt(msg);
fmt % type % content;
Logger::log(Logger::FATAL,fmt.str());
break;
}
return true;
}