503 lines
16 KiB
C++
503 lines
16 KiB
C++
|
/***************************************************************************
|
||
|
* 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;
|
||
|
}
|