diff options
Diffstat (limited to 'lib/network')
| -rw-r--r-- | lib/network/Makefile.am | 7 | ||||
| -rw-r--r-- | lib/network/Makefile.in | 0 | ||||
| -rw-r--r-- | lib/network/http_dyn.cpp | 66 | ||||
| -rw-r--r-- | lib/network/http_dyn.h | 34 | ||||
| -rw-r--r-- | lib/network/http_file.cpp | 228 | ||||
| -rw-r--r-- | lib/network/http_file.h | 37 | ||||
| -rw-r--r-- | lib/network/httpd.cpp | 631 | ||||
| -rw-r--r-- | lib/network/httpd.h | 123 | ||||
| -rw-r--r-- | lib/network/serversocket.cpp | 55 | ||||
| -rw-r--r-- | lib/network/serversocket.h | 18 | ||||
| -rw-r--r-- | lib/network/serversocket.lo | bin | 0 -> 191574 bytes | |||
| -rw-r--r-- | lib/network/socket.cpp | 294 | ||||
| -rw-r--r-- | lib/network/socket.h | 63 | ||||
| -rw-r--r-- | lib/network/socket.lo | bin | 0 -> 224253 bytes | |||
| -rw-r--r-- | lib/network/xmlrpc.cpp | 518 | ||||
| -rw-r--r-- | lib/network/xmlrpc.h | 81 |
16 files changed, 2155 insertions, 0 deletions
diff --git a/lib/network/Makefile.am b/lib/network/Makefile.am new file mode 100644 index 00000000..cfdc79b1 --- /dev/null +++ b/lib/network/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_network.a + +libenigma_network_a_SOURCES = \ + http_dyn.cpp http_file.cpp httpd.cpp serversocket.cpp socket.cpp xmlrpc.cpp diff --git a/lib/network/Makefile.in b/lib/network/Makefile.in new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/lib/network/Makefile.in diff --git a/lib/network/http_dyn.cpp b/lib/network/http_dyn.cpp new file mode 100644 index 00000000..ea47019b --- /dev/null +++ b/lib/network/http_dyn.cpp @@ -0,0 +1,66 @@ +#include <lib/network/http_dyn.h> + +eHTTPDyn::eHTTPDyn(eHTTPConnection *c, eString result): eHTTPDataSource(c), result(result) +{ + wptr=0; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + c->local_header["Content-Length"]=std::string(buffer); + if (c->code == -1) + { + c->code=200; + c->code_descr="OK"; + } +} + +eHTTPDyn::~eHTTPDyn() +{ +} + +int eHTTPDyn::doWrite(int hm) +{ + int tw=size-wptr; + if (tw>hm) + tw=hm; + if (tw<=0) + return -1; + connection->writeBlock(result.c_str()+wptr, tw); + wptr+=tw; + return (size > wptr) ? 1 : -1; +} + +eHTTPDynPathResolver::eHTTPDynPathResolver() +{ + dyn.setAutoDelete(true); +} + +void eHTTPDynPathResolver::addDyn(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection*)) +{ + dyn.push_back(new eHTTPDynEntry(request, path, function)); +} + +eHTTPDataSource *eHTTPDynPathResolver::getDataSource(eString request, eString path, eHTTPConnection *conn) +{ + eString p, opt; + if (path.find('?')!=eString::npos) + { + p=path.left(path.find('?')); + opt=path.mid(path.find('?')+1); + } else + { + p=path; + opt=""; + } + for (ePtrList<eHTTPDynEntry>::iterator i(dyn); i != dyn.end(); ++i) + if ((i->path==p) && (i->request==request)) + { + conn->code=-1; + eString s=i->function(request, path, opt, conn); + + if (s) + return new eHTTPDyn(conn, s); + + return new eHTTPError(conn, 500); + } + return 0; +} diff --git a/lib/network/http_dyn.h b/lib/network/http_dyn.h new file mode 100644 index 00000000..6fb8b9b1 --- /dev/null +++ b/lib/network/http_dyn.h @@ -0,0 +1,34 @@ +#ifndef __http_dyn_h_ +#define __http_dyn_h_ +#include <string> +#include <lib/network/httpd.h> + +class eHTTPDyn: public eHTTPDataSource +{ + eString result; + int wptr, size; +public: + eHTTPDyn(eHTTPConnection *c, eString result); + ~eHTTPDyn(); + int doWrite(int); +}; + +class eHTTPDynPathResolver: public eHTTPPathResolver +{ + struct eHTTPDynEntry + { + eString request, path; + eString (*function)(eString request, eString path, eString opt, eHTTPConnection *content); + + eHTTPDynEntry(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection *)): request(request), path(path), function(function) + { + } + }; + ePtrList<eHTTPDynEntry> dyn; +public: + void addDyn(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection *conn)); + eHTTPDynPathResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); +}; + +#endif diff --git a/lib/network/http_file.cpp b/lib/network/http_file.cpp new file mode 100644 index 00000000..89918569 --- /dev/null +++ b/lib/network/http_file.cpp @@ -0,0 +1,228 @@ +#include <lib/network/http_file.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string> +#include <shadow.h> +#include <pwd.h> + +eHTTPFile::eHTTPFile(eHTTPConnection *c, int _fd, int method, const char *mime): eHTTPDataSource(c), method(method) +{ + fd=_fd; + if (method == methodGET) + { + c->local_header["Content-Type"]=std::string(mime); + size=lseek(fd, 0, SEEK_END); + char asize[10]; + snprintf(asize, 10, "%d", size); + lseek(fd, 0, SEEK_SET); + c->local_header["Content-Length"]=std::string(asize); + } + connection->code_descr="OK"; + connection->code=200; +} + +int eHTTPFile::doWrite(int bytes) +{ + if (method == methodGET) + { + char buff[bytes]; + if (!size) + return -1; + int len=bytes; + if (len>size) + len=size; + len=read(fd, buff, len); + if (len<=0) + return -1; + size-=connection->writeBlock(buff, len); + if (!size) + return -1; + else + return 1; + } else + return -1; +} + +void eHTTPFile::haveData(void *data, int len) +{ + if (method != methodPUT) + return; + ::write(fd, data, len); +} + +eHTTPFile::~eHTTPFile() +{ + close(fd); +} + +eHTTPFilePathResolver::eHTTPFilePathResolver() +{ + translate.setAutoDelete(true); +} + + +static char _base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +static int unbase64(eString &dst, const eString string) +{ + dst=""; + char c[4]; + int pos=0; + unsigned int i=0; + + while (1) + { + if (i == string.size()) + break; + char *ch=strchr(_base64, string[i++]); + if (!ch) + { + i++; + continue; + } + c[pos++]=ch-_base64; + if (pos == 4) + { + char d[3]; + d[0]=c[0]<<2; + d[0]|=c[1]>>4; + d[1]=c[1]<<4; + d[1]|=c[2]>>2; + d[2]=c[2]<<6; + d[2]|=c[3]; + + dst+=d[0]; + if (c[2] != 64) + dst+=d[1]; + if (c[3] != 64) + dst+=d[2]; + pos=0; + } + } + return pos; +} + +int CheckUnixPassword(const char *user, const char *pass) +{ + passwd *pwd=getpwnam(user); + if (!pwd) + return -1; + char *cpwd=pwd->pw_passwd; + if (pwd && (!strcmp(pwd->pw_passwd, "x"))) + { + spwd *sp=getspnam(user); + if (!sp) // no shadow password defined. + return -1; + cpwd=sp->sp_pwdp; + } + if (!cpwd) + return -1; + if ((*cpwd=='!')||(*cpwd=='*')) // disabled user + return -2; + char *cres=crypt(pass, cpwd); + return !!strcmp(cres, cpwd); +} + +static int checkAuth(const eString cauth) +{ + eString auth; + if (cauth.left(6) != "Basic ") + return -1; + if (unbase64(auth, cauth.mid(6))) + return -1; + eString username=auth.left(auth.find(":")); + eString password=auth.mid(auth.find(":")+1); + if (CheckUnixPassword(username.c_str(), password.c_str())) + return -1; + return 0; +} + +eHTTPDataSource *eHTTPFilePathResolver::getDataSource(eString request, eString path, eHTTPConnection *conn) +{ + int method; + eDebug("request = %s, path = %s", request.c_str(), path.c_str()); + if (request == "GET") + method=eHTTPFile::methodGET; + else if (request == "PUT") + method=eHTTPFile::methodPUT; + else + return new eHTTPError(conn, 405); // method not allowed + if (path.find("../")!=eString::npos) // evil hax0r + return new eHTTPError(conn, 403); + if (path[0] != '/') // prepend '/' + path.insert(0,"/"); + if (path[path.length()-1]=='/') + path+="index.html"; + + eHTTPDataSource *data=0; + for (ePtrList<eHTTPFilePath>::iterator i(translate); i != translate.end(); ++i) + { + if (i->root==path.left(i->root.length())) + { + eString newpath=i->path+path.mid(i->root.length()); + if (newpath.find('?')) + newpath=newpath.left(newpath.find('?')); + eDebug("translated %s to %s", path.c_str(), newpath.c_str()); + + if (i->authorized & ((method==eHTTPFile::methodGET)?1:2)) + { + std::map<std::string, std::string>::iterator i=conn->remote_header.find("Authorization"); + if ((i == conn->remote_header.end()) || checkAuth(i->second)) + { + conn->local_header["WWW-Authenticate"]="Basic realm=\"dreambox\""; + return new eHTTPError(conn, 401); // auth req'ed + } + } + + int fd=open(newpath.c_str(), (method==eHTTPFile::methodGET)?O_RDONLY:(O_WRONLY|O_CREAT|O_TRUNC), 0644); + + if (fd==-1) + { + switch (errno) + { + case ENOENT: + data=new eHTTPError(conn, 404); + break; + case EACCES: + data=new eHTTPError(conn, 403); + break; + default: + data=new eHTTPError(conn, 403); // k.a. + break; + } + break; + } + + eString ext=path.mid(path.rfind('.')); + const char *mime="text/unknown"; + if ((ext==".html") || (ext==".htm")) + mime="text/html"; + else if ((ext==".jpeg") || (ext==".jpg")) + mime="image/jpeg"; + else if (ext==".gif") + mime="image/gif"; + else if (ext==".css") + mime="text/css"; + else if (ext==".png") + mime="image/png"; + else if (ext==".xml") + mime="text/xml"; + else if (ext==".xsl") + mime="text/xsl"; + + data=new eHTTPFile(conn, fd, method, mime); + break; + } + } + return data; +} + +void eHTTPFilePathResolver::addTranslation(eString path, eString root, int authorized) +{ + if (path[path.length()-1]!='/') + path+='/'; + if (root[root.length()-1]!='/') + root+='/'; + translate.push_back(new eHTTPFilePath(path, root, authorized)); +} diff --git a/lib/network/http_file.h b/lib/network/http_file.h new file mode 100644 index 00000000..88bffc60 --- /dev/null +++ b/lib/network/http_file.h @@ -0,0 +1,37 @@ +#ifndef __http_file_h +#define __http_file_h + +#include "httpd.h" + +class eHTTPFile: public eHTTPDataSource +{ + int fd, size; + const char *mime; + int method; +public: + enum { methodGET, methodPUT }; + eHTTPFile(eHTTPConnection *c, int fd, int method, const char *mime); + ~eHTTPFile(); + int doWrite(int); + void haveData(void *data, int len); +}; + +class eHTTPFilePathResolver: public eHTTPPathResolver +{ + struct eHTTPFilePath + { + eString path; + eString root; + int authorized; // must be authorized (1 means read, 2 write) + eHTTPFilePath(eString path, eString root, int authorized): path(path), root(root), authorized(authorized) + { + } + }; + ePtrList<eHTTPFilePath> translate; +public: + eHTTPFilePathResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); + void addTranslation(eString path, eString root, int auth); +}; + +#endif diff --git a/lib/network/httpd.cpp b/lib/network/httpd.cpp new file mode 100644 index 00000000..b8ed1a9d --- /dev/null +++ b/lib/network/httpd.cpp @@ -0,0 +1,631 @@ +// #define DEBUG_HTTPD +#include <lib/network/httpd.h> + +#include <sys/socket.h> +#include <error.h> +#include <errno.h> +#include <time.h> +#include <ctype.h> + +eHTTPDataSource::eHTTPDataSource(eHTTPConnection *c): connection(c) +{ +} + +eHTTPDataSource::~eHTTPDataSource() +{ +} + +void eHTTPDataSource::haveData(void *data, int len) +{ +} + +int eHTTPDataSource::doWrite(int) +{ + return 0; +} + +eHTTPError::eHTTPError(eHTTPConnection *c, int errcode): eHTTPDataSource(c), errcode(errcode) +{ + eString error="unknown error"; + switch (errcode) + { + case 400: error="Bad Request"; break; + case 401: error="Unauthorized"; break; + case 403: error="Forbidden"; break; + case 404: error="Not found"; break; + case 405: error="Method not allowed"; break; + case 500: error="Internal server error"; break; + } + connection->code_descr=error; + connection->code=errcode; + + connection->local_header["Content-Type"]=std::string("text/html"); +} + +int eHTTPError::doWrite(int w) +{ + eString html; + html+="<html><head><title>Error "+eString().setNum(connection->code)+"</title></head>"+ + "<body><h1>Error "+eString().setNum(errcode)+": "+connection->code_descr+"</h1></body></html>\n"; + connection->writeBlock(html.c_str(), html.length()); + return -1; +} + +eHTTPConnection::eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent): eSocket(socket, issocket, parent->ml), parent(parent), persistent(persistent) +{ +#ifdef DEBUG_HTTPD + eDebug("eHTTPConnection"); +#endif + CONNECT(this->readyRead_ , eHTTPConnection::readData); + CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten); + CONNECT(this->error_ , eHTTPConnection::gotError); + CONNECT(this->connectionClosed_ , eHTTPConnection::destruct); + CONNECT(this->hangup , eHTTPConnection::gotHangup); + + buffersize=128*1024; + localstate=stateWait; + remotestate=stateRequest; + data=0; +} + +void eHTTPConnection::destruct() +{ + gotHangup(); + delete this; +} + +eHTTPConnection::eHTTPConnection(eMainloop *ml): eSocket(ml), parent(0), persistent(0) +{ + CONNECT(this->readyRead_ , eHTTPConnection::readData); + CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten); + CONNECT(this->error_ , eHTTPConnection::gotError); + CONNECT(this->connected_ , eHTTPConnection::hostConnected); + CONNECT(this->connectionClosed_ , eHTTPConnection::destruct); + + localstate=stateWait; + remotestate=stateWait; + + buffersize=64*1024; + data=0; +} + +void eHTTPConnection::hostConnected() +{ + processLocalState(); +} + +void eHTTPConnection::start() +{ + if (localstate==stateWait) + { + localstate=stateRequest; + processLocalState(); + } +} + +void eHTTPConnection::gotHangup() +{ + if (data && remotestate == stateData) + data->haveData(0, 0); + if (data) + { + delete data; + data=0; + } + transferDone(0); + + localstate=stateWait; + remotestate=stateRequest; + + remote_header.clear(); + local_header.clear(); +} + +eHTTPConnection *eHTTPConnection::doRequest(const char *uri, eMainloop *ml, int *error) +{ + if (error) + *error=0; + + char *defaultproto="http"; + std::string proto, host, path; + int port=80; + + int state=0; // 0 proto, 1 host, 2 port 3 path + + while (*uri) + { + switch (state) + { + case 0: + if (!strncmp(uri, "://", 3)) + { + state=1; + uri+=3; + } else if ((*uri=='/') || (*uri==':')) + { + host=proto; + state=1; + proto=defaultproto; + } else + proto.push_back(*uri++); + break; + case 1: + if (*uri=='/') + state=3; + else if (*uri==':') + { + state=2; + port=0; + uri++; + } else + host.push_back(*uri++); + break; + case 2: + if (*uri=='/') + state=3; + else + { + if (!isdigit(*uri)) + { + port=-1; + state=3; + } else + { + port*=10; + port+=*uri++-'0'; + } + } + break; + case 3: + path.push_back(*uri++); + } + } + + if (state==0) + { + path=proto; + proto=defaultproto; + } + +#ifdef DEBUG_HTTPD + eDebug("proto: '%s', host '%s', path '%s', port '%d'", proto.c_str(), host.c_str(), path.c_str(), port); +#endif + + if (!host.size()) + { + eDebug("no host given"); + if (error) + *error=ENOENT; + return 0; + } + + if (strcmp(proto.c_str(), "http")) + { + eDebug("invalid protocol (%s)", proto.c_str()); + if (error) + *error=EINVAL; + return 0; + } + + if (port == -1) + { + eDebug("invalid port"); + if (error) + *error=EINVAL; + return 0; + } + + if (!path.size()) + path="/"; + + eHTTPConnection *c=new eHTTPConnection(ml); + c->request="GET"; + c->requestpath=path.c_str(); + c->httpversion="HTTP/1.0"; + c->local_header["Host"]=host; + if ((*error=c->connectToHost(host, port))) // already deleted by error + return 0; + return c; +} + +void eHTTPConnection::readData() +{ + processRemoteState(); +} + +void eHTTPConnection::bytesWritten(int) +{ + processLocalState(); +} + +int eHTTPConnection::processLocalState() +{ + switch (state()) + { + case Connection: + break; + default: + return 0; + } + int done=0; + while (!done) + { +#ifdef DEBUG_HTTPD + eDebug("processing local state %d", localstate); +#endif + switch (localstate) + { + case stateWait: +#ifdef DEBUG_HTTPD + eDebug("local wait"); +#endif + done=1; + break; + case stateRequest: + { +#ifdef DEBUG_HTTPD + eDebug("local request"); +#endif + eString req=request+" "+requestpath+" "+httpversion+"\r\n"; + writeBlock(req.c_str(), req.length()); + localstate=stateHeader; + remotestate=stateResponse; + break; + } + case stateResponse: + { +#ifdef DEBUG_HTTPD + eDebug("local Response"); +#endif + writeString( (httpversion + " " + eString().setNum(code)+" " + code_descr + "\r\n").c_str() ); + localstate=stateHeader; + local_header["Connection"]="close"; + break; + } + case stateHeader: +#ifdef DEBUG_HTTPD + eDebug("local header"); +#endif + for (std::map<std::string,std::string>::iterator cur=local_header.begin(); cur!=local_header.end(); ++cur) + { + writeString(cur->first.c_str()); + writeString(": "); + writeString(cur->second.c_str()); + writeString("\r\n"); + } + writeString("\r\n"); + if (request=="HEAD") + localstate=stateDone; + else + localstate=stateData; + break; + case stateData: +#ifdef DEBUG_HTTPD + eDebug("local data"); +#endif + if (data) + { + int btw=buffersize-bytesToWrite(); + if (btw>0) + { + if (data->doWrite(btw)<0) + { + localstate=stateDone; + } else + done=1; + } else + done=1; + } else + done=1; // wait for remote response + break; + case stateDone: +#if 0 + // move to stateClose + if (remote_header.find("Connection") != remote_header.end()) + { + eString &connection=remote_header["Connection"]; + if (connection == "keep-alive") + localstate=stateWait; + else + localstate=stateClose; + } +#endif +#ifdef DEBUG_HTTPD + eDebug("locate state done"); +#endif + if (!persistent) + localstate=stateClose; + else + localstate=stateWait; + break; + case stateClose: +#ifdef DEBUG_HTTPD + eDebug("closedown"); +#endif + if (persistent) + { + if (data) + delete data; + data=0; + localstate=stateWait; + } else + close(); // bye, bye, remote + return 1; + } + } +#ifdef DEBUG_HTTPD + eDebug("end local"); +#endif + return 0; +} + +int eHTTPConnection::processRemoteState() +{ + int abort=0, done=0; +#ifdef DEBUG_HTTPD + eDebug("%d bytes avail", bytesAvailable()); +#endif + while (((!done) || bytesAvailable()) && !abort) + { + switch (remotestate) + { + case stateWait: + { + int i=0; +#ifdef DEBUG_HTTPD + eDebug("remote stateWait"); +#endif + char buffer[1024]; + while (bytesAvailable()) { + i=readBlock(buffer, 1024); + } + done=1; + break; + } + case stateRequest: + { +#ifdef DEBUG_HTTPD + eDebug("stateRequest"); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } + + int del[2]; + del[0]=line.find(" "); + del[1]=line.find(" ", del[0]+1); + if (del[0]==-1) + { + if (data) + delete data; + eDebug("request buggy"); + httpversion="HTTP/1.0"; + data=new eHTTPError(this, 400); + done=0; + localstate=stateResponse; + remotestate=stateDone; + if (processLocalState()) + return -1; + break; + } + request=line.left(del[0]); + requestpath=line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)); + if (del[1]!=-1) + { + is09=0; + httpversion=line.mid(del[1]+1); + } else + is09=1; + + if (is09 || (httpversion.left(7) != "HTTP/1.") || httpversion.size()!=8) + { + remotestate=stateData; + done=0; + httpversion="HTTP/1.0"; + content_length_remaining=content_length_remaining=0; + data=new eHTTPError(this, 400); // bad request - not supporting version 0.9 yet + } else + remotestate=stateHeader; + break; + } + case stateResponse: + { +#ifdef DEBUG_HTTPD + eDebug("state response.."); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } +#ifdef DEBUG_HTTPD + eDebug("line: %s", line.c_str()); +#endif + int del[2]; + del[0]=line.find(" "); + del[1]=line.find(" ", del[0]+1); + if (del[0]==-1) + code=-1; + else + { + httpversion=line.left(del[0]); + code=atoi(line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)).c_str()); + if (del[1] != -1) + code_descr=line.mid(del[1]+1); + else + code_descr=""; + } + + remotestate=stateHeader; + break; + } + case stateHeader: + { +#ifdef DEBUG_HTTPD + eDebug("remote stateHeader"); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } + if (!line.length()) + { + content_length=0; + content_length_remaining=-1; + if (remote_header.count("Content-Length")) + { + content_length=atoi(remote_header["Content-Length"].c_str()); + content_length_remaining=content_length; + } + + if (parent) + { + for (ePtrList<eHTTPPathResolver>::iterator i(parent->resolver); i != parent->resolver.end(); ++i) + { + if ((data=i->getDataSource(request, requestpath, this))) + break; + } + localstate=stateResponse; // can be overridden by dataSource + } else + data=createDataSource(this); + + if (!data) + { + if (data) + delete data; + data=new eHTTPError(this, 404); + } + + if (content_length || // if content-length is set, we have content + remote_header.count("Content-Type") || // content-type - the same + (localstate != stateResponse)) // however, if we are NOT in response-state, so we are NOT server, there's ALWAYS more data to come. (exception: http/1.1 persistent) + remotestate=stateData; + else + { + data->haveData(0, 0); + remotestate=stateDone; + } + if (processLocalState()) + return -1; + } else + { + int del=line.find(":"); + eString name=line.left(del), value=line.mid(del+1); + if (value[0]==' ') + value=value.mid(1); + remote_header[std::string(name)]=std::string(value); + } + done=1; + break; + } + case stateData: + { +#ifdef DEBUG_HTTPD + eDebug("remote stateData"); +#endif + ASSERT(data); + char buffer[16284]; + int len; + while (bytesAvailable()) + { + int tr=sizeof(buffer); + if (content_length_remaining != -1) + if (tr>content_length_remaining) + tr=content_length_remaining; + len=readBlock(buffer, tr); + data->haveData(buffer, len); + if (content_length_remaining != -1) + content_length_remaining-=len; + if (!content_length_remaining) + { + data->haveData(0, 0); + remotestate=stateDone; + break; + } + } + done=1; + if (processLocalState()) + return -1; + break; + } + case stateDone: + remotestate=stateClose; + break; + case stateClose: +// if (!persistent) + remotestate=stateWait; +// else +// remotestate=stateRequest; + abort=1; + break; + default: + eDebug("HTTP: invalid state %d", remotestate); + done=1; + } + } +#ifdef DEBUG_HTTPD + eDebug("end remote"); +#endif + return 0; +} + +void eHTTPConnection::writeString(const char *data) +{ + writeBlock(data, strlen(data)); +} + +int eHTTPConnection::getLine(eString &line) +{ + if (!canReadLine()) + return 0; + + line = readLine(); + line.erase(line.length()-1); + + if (line[(line.length()-1)] == '\r') + line.erase(line.length()-1); + + return 1; +} + +void eHTTPConnection::gotError(int err) +{ + if (data) + { + delete data; + data=0; + } + transferDone(err); + delete this; +} + +eHTTPD::eHTTPD(int port, eMainloop *ml): eServerSocket(port, ml), ml(ml) +{ + if (!ok()) + eDebug("[NET] httpd server FAILED on port %d", port); + else + eDebug("[NET] httpd server started on port %d", port); + resolver.setAutoDelete(true); +} + +eHTTPConnection::~eHTTPConnection() +{ + if ((!persistent) && (state()!=Idle)) + eWarning("~eHTTPConnection, status still %d", state()); + if (data) + delete data; +} + +void eHTTPD::newConnection(int socket) +{ + new eHTTPConnection(socket, 1, this); +} diff --git a/lib/network/httpd.h b/lib/network/httpd.h new file mode 100644 index 00000000..791d49c8 --- /dev/null +++ b/lib/network/httpd.h @@ -0,0 +1,123 @@ +#ifndef __httpd_h +#define __httpd_h + +#include <asm/types.h> +#include <map> + +#include <lib/base/eptrlist.h> +#include <lib/base/ebase.h> +#include <lib/base/estring.h> +#include <lib/base/eerror.h> +#include <lib/network/socket.h> +#include <lib/network/serversocket.h> + +class eHTTPConnection; +class eHTTPDataSource; +class eHTTPD; + +class eHTTPPathResolver +{ +public: + virtual ~eHTTPPathResolver() {}; + virtual eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn)=0; +}; + +class eHTTPDataSource +{ +protected: + eHTTPConnection *connection; +public: + eHTTPDataSource(eHTTPConnection *c); + virtual ~eHTTPDataSource(); + virtual void haveData(void *data, int len); + virtual int doWrite(int bytes); // number of written bytes, -1 for "no more" +}; + +class eHTTPError: public eHTTPDataSource +{ + int errcode; +public: + eHTTPError(eHTTPConnection *c, int errcode); + ~eHTTPError() { } + void haveData(); + int doWrite(int bytes); +}; + +class eHTTPConnection: public eSocket +{ + void doError(int error); + + int getLine(eString &line); + + int processLocalState(); + int processRemoteState(); + void writeString(const char *data); + + eHTTPDataSource *data; + eHTTPD *parent; + + int buffersize; +private: + void readData(); + void gotError(int); + void bytesWritten(int); + void hostConnected(); + void destruct(); +public: + Signal1<void,int> transferDone; + Signal1<eHTTPDataSource*,eHTTPConnection*> createDataSource; + enum + { + /* + + < GET / HTTP/1.0 + < If-modified-since: bla + < + < Data + > 200 OK HTTP/1.0 + > Content-Type: text/html + > + > Data + */ + + stateWait, stateRequest, stateResponse, stateHeader, stateData, stateDone, stateClose + }; + int localstate, remotestate; + int persistent; + + eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent=0); + eHTTPConnection(eMainloop *ml); // ready to do "connectToHost" + static eHTTPConnection *doRequest(const char *uri, eMainloop *ml, int *error=0); + void start(); + void gotHangup(); + ~eHTTPConnection(); + + // stateRequest + eString request, requestpath, httpversion; + int is09; + + // stateResponse + + int code; + eString code_descr; + + std::map<std::string,std::string> remote_header, local_header; + + // stateData + int content_length, content_length_remaining; +}; + +class eHTTPD: public eServerSocket +{ + friend class eHTTPConnection; + ePtrList<eHTTPPathResolver> resolver; + eMainloop *ml; +public: + eHTTPD(int port, eMainloop *ml); + void newConnection(int socket); + + void addResolver(eHTTPPathResolver *r) { resolver.push_back(r); } + void removeResolver(eHTTPPathResolver *r) { resolver.remove(r); } +}; + +#endif diff --git a/lib/network/serversocket.cpp b/lib/network/serversocket.cpp new file mode 100644 index 00000000..fe2737eb --- /dev/null +++ b/lib/network/serversocket.cpp @@ -0,0 +1,55 @@ +#include <lib/network/serversocket.h> + +bool eServerSocket::ok() +{ + return okflag; +} + +void eServerSocket::notifier(int) +{ + int clientfd, clientlen; + struct sockaddr_in client_addr; + + eDebug("[SERVERSOCKET] incoming connection!"); + + clientlen=sizeof(client_addr); + clientfd=accept(getDescriptor(), + (struct sockaddr *) &client_addr, + (socklen_t*)&clientlen); + if(clientfd<0) + eDebug("[SERVERSOCKET] error on accept()"); + + newConnection(clientfd); +} + +eServerSocket::eServerSocket(int port, eMainloop *ml): eSocket(ml) +{ + struct sockaddr_in serv_addr; + + serv_addr.sin_family=AF_INET; + serv_addr.sin_addr.s_addr=INADDR_ANY; + serv_addr.sin_port=htons(port); + + okflag=1; + int val=1; + + setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &val, 4); + + if(bind(getDescriptor(), + (struct sockaddr *) &serv_addr, + sizeof(serv_addr))<0) + { + eDebug("[SERVERSOCKET] ERROR on bind() (%m)"); + okflag=0; + } + listen(getDescriptor(), 0); + + rsn->setRequested(eSocketNotifier::Read); +} + +eServerSocket::~eServerSocket() +{ +#if 0 + eDebug("[SERVERSOCKET] destructed"); +#endif +} diff --git a/lib/network/serversocket.h b/lib/network/serversocket.h new file mode 100644 index 00000000..6bd59f11 --- /dev/null +++ b/lib/network/serversocket.h @@ -0,0 +1,18 @@ +#ifndef __serversocket_h +#define __serversocket_h + +#include "socket.h" + +class eServerSocket: public eSocket +{ + void notifier(int handle); + int okflag; +protected: + virtual void newConnection(int socket)=0; +public: + eServerSocket(int port, eMainloop *ml); + virtual ~eServerSocket(); + bool ok(); +}; + +#endif /* __serversocket_h */ diff --git a/lib/network/serversocket.lo b/lib/network/serversocket.lo Binary files differnew file mode 100644 index 00000000..8039034b --- /dev/null +++ b/lib/network/serversocket.lo diff --git a/lib/network/socket.cpp b/lib/network/socket.cpp new file mode 100644 index 00000000..9de0ca0a --- /dev/null +++ b/lib/network/socket.cpp @@ -0,0 +1,294 @@ +#include <sys/ioctl.h> +#include <asm/ioctls.h> +#include <unistd.h> +#include <errno.h> + +#include <lib/network/socket.h> + +void eSocket::close() +{ + if (writebuffer.empty()) + { + int wasconnected=(mystate==Connection) || (mystate==Closing); + delete rsn; + rsn=0; + ::close(socketdesc); + socketdesc=-1; + mystate=Idle; + if (wasconnected) + connectionClosed_(); + } else + { + mystate=Closing; + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + } +} + +void eSocket::enableRead() +{ + if (rsn) + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Read); +} + +void eSocket::disableRead() +{ + if (rsn) + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Read); +} + +void eSocket::inject(const char *data, int len) +{ + readbuffer.write(data, len); + if (mystate == Connection) + readyRead_(); +} + +eString eSocket::readLine() +{ + int size=readbuffer.searchchr('\n'); + if (size == -1) + return eString(); + size++; // ich will auch das \n + char buffer[size+1]; + buffer[size]=0; + readbuffer.read(buffer, size); + return eString(buffer); +} + +bool eSocket::canReadLine() +{ + return readbuffer.searchchr('\n') != -1; +} + +int eSocket::bytesAvailable() +{ + return readbuffer.size(); +} + +int eSocket::readBlock(char *data, unsigned int maxlen) +{ + return readbuffer.read(data, maxlen); +} + +int eSocket::bytesToWrite() +{ + return writebuffer.size(); +} + +int eSocket::state() +{ + return mystate; +} + +int eSocket::setSocket(int s, int iss, eMainloop *ml) +{ + socketdesc=s; + issocket=iss; + fcntl(socketdesc, F_SETFL, O_NONBLOCK); + last_break = 0xFFFFFFFF; + + if (rsn) + delete rsn; + rsn=new eSocketNotifier(ml, getDescriptor(), + eSocketNotifier::Read|eSocketNotifier::Hungup); + CONNECT(rsn->activated, eSocket::notifier); + return 0; +} + +void eSocket::notifier(int what) +{ + if ((what & eSocketNotifier::Read) && (mystate == Connection)) + { + int bytesavail=256; + if (issocket) + if (ioctl(getDescriptor(), FIONREAD, &bytesavail)<0) + eDebug("FIONREAD failed.\n"); + + { + if (issocket) + { + if (!bytesavail) // does the REMOTE END has closed the connection? (no Hungup here!) + { + writebuffer.clear(); + close(); + return; + } + } else // when operating on terminals, check for break + { + // where is this struct defined? + struct async_icount { + unsigned long cts, dsr, rng, dcd, tx, rx; + unsigned long frame, parity, overrun, brk; + unsigned long buf_overrun; + } icount; + + if (!ioctl(getDescriptor(), TIOCGICOUNT, &icount)) + { + if (last_break == 0xFFFFFFFF) + last_break = icount.brk; + else if (last_break != icount.brk) + { + last_break = icount.brk; + readbuffer.fromfile(getDescriptor(), bytesavail); + readbuffer.clear(); + writebuffer.clear(); + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + write(getDescriptor(), "BREAK!", 6); + hangup(); + return; + } + } + } + int r; + if ((r=readbuffer.fromfile(getDescriptor(), bytesavail)) != bytesavail) + if (issocket) + eDebug("fromfile failed!"); + readyRead_(); + } + } else if (what & eSocketNotifier::Write) + { + if ((mystate == Connection) || (mystate == Closing)) + { + if (!writebuffer.empty()) + { + bytesWritten_(writebuffer.tofile(getDescriptor(), 65536)); + if (writebuffer.empty()) + { + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + if (mystate == Closing) + { + close(); // warning, we might get destroyed after close. + return; + } + } + } else + eDebug("got ready to write, but nothin in buffer. strange."); + if (mystate == Closing) + close(); + } else if (mystate == Connecting) + { + mystate=Connection; + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + + int res; + socklen_t size=sizeof(res); + ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size); + if (!res) + connected_(); + else + { + close(); + error_(res); + } + } + } else if (what & eSocketNotifier::Hungup) + { + if (mystate == Connection) + { + writebuffer.clear(); + close(); + } else if (mystate == Connecting) + { + int res; + socklen_t size=sizeof(res); + ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size); + close(); + error_(res); + } + } +} + +int eSocket::writeBlock(const char *data, unsigned int len) +{ + int w=len; + if (issocket && writebuffer.empty()) + { + int tw=::send(getDescriptor(), data, len, MSG_NOSIGNAL); + if ((tw < 0) && (errno != EWOULDBLOCK)) + eDebug("write: %m"); + + if (tw < 0) + tw = 0; + data+=tw; + len-=tw; + } + if (len) + writebuffer.write(data, len); + + if (!writebuffer.empty()) + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + return w; +} + +int eSocket::getDescriptor() +{ + return socketdesc; +} + +int eSocket::connectToHost(eString hostname, int port) +{ + struct hostent *server; + int res; + + if(!socketdesc){ + error_(errno); + return(-1); + } + server=gethostbyname(hostname.c_str()); + if(server==NULL) + { + eDebug("can't resolve %s", hostname.c_str()); + error_(errno); + return(-2); + } + bzero( (char*)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family=AF_INET; + bcopy( (char*)server->h_addr, + (char*)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port=htons(port); + res=::connect(socketdesc, (const sockaddr*)&serv_addr, sizeof(serv_addr)); + if ((res < 0) && (errno != EINPROGRESS)) + { + eDebug("can't connect to host: %s", hostname.c_str()); + close(); + error_(errno); + return(-3); + } + if (res < 0) // EINPROGRESS + { + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + mystate=Connecting; + } else + { + mystate=Connection; + connected_(); + } + return(0); +} + +eSocket::eSocket(eMainloop *ml): readbuffer(32768), writebuffer(32768), rsn(0) +{ + int s=socket(AF_INET, SOCK_STREAM, 0); +#if 0 + eDebug("[SOCKET]: initalized socket %d", socketdesc); +#endif + mystate=Idle; + setSocket(s, 1, ml); +} + +eSocket::eSocket(int socket, int issocket, eMainloop *ml): readbuffer(32768), writebuffer(32768), rsn(0) +{ + setSocket(socket, issocket, ml); + mystate=Connection; +} + +eSocket::~eSocket() +{ + if (rsn) + delete rsn; + if(socketdesc>=0) + { + ::close(socketdesc); + } +} diff --git a/lib/network/socket.h b/lib/network/socket.h new file mode 100644 index 00000000..edf9b904 --- /dev/null +++ b/lib/network/socket.h @@ -0,0 +1,63 @@ +#ifndef __socket_h +#define __socket_h + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fcntl.h> + +#include <lib/base/ebase.h> +#include <lib/base/eerror.h> +#include <lib/base/estring.h> +#include <libsig_comp.h> +#include <lib/base/buffer.h> + +class eSocket: public Object +{ + int mystate; + int issocket; + unsigned int last_break; +private: + int socketdesc; + eIOBuffer readbuffer; + eIOBuffer writebuffer; + int writebusy; + sockaddr_in serv_addr; +protected: + eSocketNotifier *rsn; + virtual void notifier(int); +public: + eSocket(eMainloop *ml); + eSocket(int socket, int issocket, eMainloop *ml); + ~eSocket(); + int connectToHost(eString hostname, int port); + int getDescriptor(); + int writeBlock(const char *data, unsigned int len); + int setSocket(int socketfd, int issocket, eMainloop *ml); + int bytesToWrite(); + int readBlock(char *data, unsigned int maxlen); + int bytesAvailable(); + bool canReadLine(); + eString readLine(); + void close(); + // flow control: start/stop data delivery into read buffer. + void enableRead(); + void disableRead(); + + void inject(const char *data, int len); + + enum State { Idle, HostLookup, Connecting, + Listening, Connection, Closing }; + int state(); + + Signal0<void> connectionClosed_; + Signal0<void> connected_; + Signal0<void> readyRead_; + Signal0<void> hangup; + Signal1<void,int> bytesWritten_; + Signal1<void,int> error_; +}; + +#endif /* __socket_h */ diff --git a/lib/network/socket.lo b/lib/network/socket.lo Binary files differnew file mode 100644 index 00000000..1d809469 --- /dev/null +++ b/lib/network/socket.lo diff --git a/lib/network/xmlrpc.cpp b/lib/network/xmlrpc.cpp new file mode 100644 index 00000000..a53b50a8 --- /dev/null +++ b/lib/network/xmlrpc.cpp @@ -0,0 +1,518 @@ +#ifndef DISABLE_NETWORK + +#include <lib/network/xmlrpc.h> + + +static std::map<eString, int (*)(std::vector<eXMLRPCVariant>&, ePtrList<eXMLRPCVariant>&)> rpcproc; + +void eXMLRPCVariant::zero() +{ + _struct=0; + _array=0; + _i4=0; + _boolean=0; + _string=0; + _double=0; +// _datetime=0; +// _base64=0; +} + +eXMLRPCVariant::eXMLRPCVariant(std::map<eString,eXMLRPCVariant*> *__struct) +{ + zero(); + _struct=__struct; +} + +eXMLRPCVariant::eXMLRPCVariant(std::vector<eXMLRPCVariant> *__array) +{ + zero(); + _array=__array; +} + +eXMLRPCVariant::eXMLRPCVariant(__s32 *__i4) +{ + zero(); + _i4=__i4; +} + +eXMLRPCVariant::eXMLRPCVariant(bool *__boolean) +{ + zero(); + _boolean=__boolean; +} + +eXMLRPCVariant::eXMLRPCVariant(eString *__string) +{ + zero(); + _string=__string; +} + +eXMLRPCVariant::eXMLRPCVariant(double *__double) +{ + zero(); + _double=__double; +} + +/*eXMLRPCVariant::eXMLRPCVariant(QDateTime *__datetime) +{ + zero(); + _datetime=__datetime; +} */ + +/*eXMLRPCVariant::eXMLRPCVariant(QByteArray *__base64) +{ + zero(); + _base64=__base64; +} */ + +eXMLRPCVariant::eXMLRPCVariant(const eXMLRPCVariant &c) +{ + zero(); + if (c._i4) + _i4=new int(*c._i4); + if (c._boolean) + _boolean=new bool(*c._boolean); + if (c._string) + _string=new eString(*c._string); + if (c._double) + _double=new double(*c._double); + // datetime, base64 + if (c._struct) + { + _struct=new std::map<eString,eXMLRPCVariant*>; + for (std::map<eString,eXMLRPCVariant*>::iterator b(c._struct->begin()); b != c._struct->end(); ++b) + _struct->insert(std::pair<eString,eXMLRPCVariant*>(b->first, new eXMLRPCVariant(*b->second))); + } + if (c._array) + _array = new std::vector<eXMLRPCVariant>(*c._array); +} + +eXMLRPCVariant::~eXMLRPCVariant() +{ + if (_struct) + { + for (std::map<eString,eXMLRPCVariant*>::iterator i(_struct->begin()); i != _struct->end(); ++i) + delete i->second; + + delete _struct; + } + if (_array) + delete _array; + if (_i4) + delete _i4; + if (_boolean) + delete _boolean; + if (_string) + delete _string; + if (_double) + delete _string; +/* if (_datetime) + delete _datetime;*/ +/* if (_base64) + delete _base64;*/ +} + +std::map<eString,eXMLRPCVariant*> *eXMLRPCVariant::getStruct() +{ + return _struct; +} + +std::vector<eXMLRPCVariant> *eXMLRPCVariant::getArray() +{ + return _array; +} + +__s32 *eXMLRPCVariant::getI4() +{ + return _i4; +} + +bool *eXMLRPCVariant::getBoolean() +{ + return _boolean; +} + +eString *eXMLRPCVariant::getString() +{ + return _string; +} + +double *eXMLRPCVariant::getDouble() +{ + return _double; +} + +/*QDateTime *eXMLRPCVariant::getDatetime() +{ + return _datetime; +} */ + +/*QByteArray *eXMLRPCVariant::getBase64() +{ + return _base64; +} */ + +void eXMLRPCVariant::toXML(eString &result) +{ + if (getArray()) + { + static eString s1("<value><array><data>"); + result+=s1; + for (unsigned int i=0; i<getArray()->size(); i++) + { + static eString s(" "); + result+=s; + (*getArray())[i].toXML(result); + static eString s1("\n"); + result+=s1; + } + static eString s2("</data></array></value>\n"); + result+=s2; + } else if (getStruct()) + { + static eString s1("<value><struct>"); + result+=s1; + for (std::map<eString,eXMLRPCVariant*>::iterator i(_struct->begin()); i != _struct->end(); ++i) + { + static eString s1(" <member><name>"); + result+=s1; + result+=i->first; + static eString s2("</name>"); + result+=s2; + i->second->toXML(result); + static eString s3("</member>\n"); + result+=s3; + } + static eString s2("</struct></value>\n"); + result+=s2; + } else if (getI4()) + { + static eString s1("<value><i4>"); + result+=s1; + result+=eString().setNum(*getI4()); + static eString s2("</i4></value>"); + result+=s2; + } else if (getBoolean()) + { + static eString s0("<value><boolean>0</boolean></value>"); + static eString s1("<value><boolean>1</boolean></value>"); + result+=(*getBoolean())?s1:s0; + } else if (getString()) + { + static eString s1("<value><string>"); + static eString s2("</string></value>"); + result+=s1; + result+=*getString(); + result+=s2; + } else if (getDouble()) + { + result+=eString().sprintf("<value><double>%lf</double></value>", *getDouble()); + } else + eFatal("couldn't append"); +} + +static eXMLRPCVariant *fromXML(XMLTreeNode *n) +{ + if (strcmp(n->GetType(), "value")) + return 0; + n=n->GetChild(); + const char *data=n->GetData(); + if (!data) + data=""; + if ((!strcmp(n->GetType(), "i4")) || (!strcmp(n->GetType(), "int"))) + return new eXMLRPCVariant(new int(atoi(data))); + else if (!strcmp(n->GetType(), "boolean")) + return new eXMLRPCVariant(new bool(atoi(data))); + else if (!strcmp(n->GetType(), "string")) + return new eXMLRPCVariant(new eString(data)); + else if (!strcmp(n->GetType(), "double")) + return new eXMLRPCVariant(new double(atof(data))); + else if (!strcmp(n->GetType(), "struct")) { + std::map<eString,eXMLRPCVariant*> *s=new std::map<eString,eXMLRPCVariant*>; + for (n=n->GetChild(); n; n=n->GetNext()) + { + if (strcmp(data, "member")) + { + delete s; + return 0; + } + eString name=0; + eXMLRPCVariant *value; + for (XMLTreeNode *v=n->GetChild(); v; v=v->GetNext()) + { + if (!strcmp(v->GetType(), "name")) + name=eString(v->GetData()); + else if (!strcmp(v->GetType(), "value")) + value=fromXML(v); + } + if ((!value) || (!name)) + { + delete s; + return 0; + } + s->INSERT(name,value); + } + return new eXMLRPCVariant(s); + } else if (!strcmp(n->GetType(), "array")) + { + ePtrList<eXMLRPCVariant> l; + l.setAutoDelete(true); + n=n->GetChild(); + if (strcmp(data, "data")) + return 0; + for (n=n->GetChild(); n; n=n->GetNext()) + if (!strcmp(n->GetType(), "value")) + { + eXMLRPCVariant *value=fromXML(n); + if (!value) + return 0; + l.push_back(value); + } + + return new eXMLRPCVariant( l.getVector() ); + } + eDebug("couldn't convert %s", n->GetType()); + return 0; +} + +eXMLRPCResponse::eXMLRPCResponse(eHTTPConnection *c): + eHTTPDataSource(c), parser("ISO-8859-1") +{ + // size etc. setzen aber erst NACH data-phase + connection->localstate=eHTTPConnection::stateWait; +} + +eXMLRPCResponse::~eXMLRPCResponse() +{ +} + +int eXMLRPCResponse::doCall() +{ + eDebug("doing call"); + result=""; + // get method name + eString methodName=0; + + if (connection->remote_header["Content-Type"]!="text/xml") + { + eDebug("remote header failure (%s != text/xml)", (connection->remote_header["Content-Type"]).c_str()); + return -3; + } + + XMLTreeNode *methodCall=parser.RootNode(); + if (!methodCall) + { + eDebug("empty xml"); + return -1; + } + if (strcmp(methodCall->GetType(), "methodCall")) + { + eDebug("no methodCall found"); + return -2; + } + + ePtrList<eXMLRPCVariant> params; + params.setAutoDelete(true); + + for (XMLTreeNode *c=methodCall->GetChild(); c; c=c->GetNext()) + { + if (!strcmp(c->GetType(), "methodName")) + methodName=eString(c->GetData()); + else if (!strcmp(c->GetType(), "params")) + { + for (XMLTreeNode *p=c->GetChild(); p; p=p->GetNext()) + if (!strcmp(p->GetType(), "param")) + params.push_back(fromXML(p->GetChild())); + } else + { + eDebug("unknown stuff found"); + return 0; + } + } + + if (!methodName) + { + eDebug("no methodName found!"); + return -3; + } + + eDebug("methodName: %s", methodName.c_str() ); + + result="<?xml version=\"1.0\"?>\n" + "<methodResponse>"; + + ePtrList<eXMLRPCVariant> ret; + ret.setAutoDelete(true); + + int (*proc)(std::vector<eXMLRPCVariant>&, ePtrList<eXMLRPCVariant> &)=rpcproc[methodName]; + int fault; + + std::vector<eXMLRPCVariant>* v = params.getVector(); + + if (!proc) + { + fault=1; + xmlrpc_fault(ret, -1, "called method not present"); + } else + fault=proc( *v , ret); + + delete v; + + eDebug("converting to text..."); + + if (fault) + { + result+="<fault>\n"; + ret.current()->toXML(result); + result+="</fault>\n"; + } else + { + result+="<params>\n"; + for (ePtrList<eXMLRPCVariant>::iterator i(ret); i != ret.end(); ++i) + { + result+="<param>"; + i->toXML(result); + result+="</param>"; + } + result+="</params>"; + } + result+="</methodResponse>"; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + wptr=0; + connection->local_header["Content-Type"]="text/xml"; + connection->local_header["Content-Length"]=buffer; + connection->code=200; + connection->code_descr="OK"; + connection->localstate=eHTTPConnection::stateResponse; + return 0; +} + +int eXMLRPCResponse::doWrite(int hm) +{ + int tw=size-wptr; + if (tw>hm) + tw=hm; + if (tw<=0) + return -1; + connection->writeBlock(result.c_str()+wptr, tw); + wptr+=tw; + return size > wptr ? 1 : -1; +} + +void eXMLRPCResponse::haveData(void *data, int len) +{ + if (result) + return; + int err=0; + + if (!parser.Parse((char*)data, len, !len)) + { + char temp[len+1]; + temp[len]=0; + memcpy(temp, data, len); + eDebug("%s: %s", temp, parser.ErrorString(parser.GetErrorCode())); + err=1; + } + + if ((!err) && (!len)) + err=doCall(); + + if (err) + { + eDebug("schade: %d", err); + connection->code=400; + connection->code_descr="Bad request"; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + wptr=0; + connection->local_header["Content-Type"]="text/html"; + connection->local_header["Content-Length"]=buffer; + result.sprintf("XMLRPC error %d\n", err); + connection->localstate=eHTTPConnection::stateResponse; + } +} + +void xmlrpc_initialize(eHTTPD *httpd) +{ + httpd->addResolver(new eHTTPXMLRPCResolver); +} + +void xmlrpc_addMethod(eString methodName, int (*proc)(std::vector<eXMLRPCVariant>&, ePtrList<eXMLRPCVariant>&)) +{ + rpcproc[methodName]=proc; +} + +void xmlrpc_fault(ePtrList<eXMLRPCVariant> &res, int faultCode, eString faultString) +{ + std::map<eString,eXMLRPCVariant*> *s=new std::map<eString,eXMLRPCVariant*>; + s->INSERT("faultCode", new eXMLRPCVariant(new __s32(faultCode))); + s->INSERT("faultString", new eXMLRPCVariant(new eString(faultString))); + res.push_back(new eXMLRPCVariant(s)); +} + +int xmlrpc_checkArgs(eString args, std::vector<eXMLRPCVariant> &parm, ePtrList<eXMLRPCVariant> &res) +{ + if (parm.size() != args.length()) + { + xmlrpc_fault(res, -500, eString().sprintf("parameter count mismatch (found %d, expected %d)", parm.size(), args.length())); + return 1; + } + + for (unsigned int i=0; i<args.length(); i++) + { + switch (args[i]) + { + case 'i': + if (parm[i].getI4()) + continue; + break; + case 'b': + if (parm[i].getBoolean()) + continue; + break; + case 's': + if (parm[i].getString()) + continue; + break; + case 'd': + if (parm[i].getDouble()) + continue; + break; +/* case 't': + if (parm[i].getDatetime()) + continue; + break; + case '6': + if (parm[i].getBase64()) + continue; + break;*/ + case '$': + if (parm[i].getStruct()) + continue; + break; + case 'a': + if (parm[i].getArray()) + continue; + break; + } + xmlrpc_fault(res, -501, eString().sprintf("parameter type mismatch, expected %c as #%d", args[i], i)); + return 1; + } + return 0; +} + +eHTTPXMLRPCResolver::eHTTPXMLRPCResolver() +{ +} + +eHTTPDataSource *eHTTPXMLRPCResolver::getDataSource(eString request, eString path, eHTTPConnection *conn) +{ + if ((path=="/RPC2") && (request=="POST")) + return new eXMLRPCResponse(conn); + if ((path=="/SID2") && (request=="POST")) + return new eXMLRPCResponse(conn); + return 0; +} + +#endif //DISABLE_NETWORK diff --git a/lib/network/xmlrpc.h b/lib/network/xmlrpc.h new file mode 100644 index 00000000..727e1e0e --- /dev/null +++ b/lib/network/xmlrpc.h @@ -0,0 +1,81 @@ +#ifndef DISABLE_NETWORK + +#ifndef __xmlrpc_h_ +#define __xmlrpc_h_ + +#include <asm/types.h> +#include <map> +#include <vector> +#include <xmltree.h> + +#include <lib/base/estring.h> +#include <lib/base/eptrlist.h> +#include <lib/network/httpd.h> + +#define INSERT(KEY,VALUE) insert(std::pair<eString, eXMLRPCVariant*>(KEY,VALUE)) + +class eXMLRPCVariant +{ + std::map<eString,eXMLRPCVariant*> *_struct; + std::vector<eXMLRPCVariant> *_array; + __s32 *_i4; + bool *_boolean; + eString *_string; + double *_double; +// QDateTime *_datetime; +// QByteArray *_base64; + void zero(); +public: + eXMLRPCVariant(std::map<eString,eXMLRPCVariant*> *_struct); + eXMLRPCVariant(std::vector<eXMLRPCVariant> *_array); + eXMLRPCVariant(__s32 *_i4); + eXMLRPCVariant(bool *_boolean); + eXMLRPCVariant(eString *_string); + eXMLRPCVariant(double *_double); +// eXMLRPCVariant(QDateTime *_datetime); +// eXMLRPCVariant(QByteArray *_base64); + eXMLRPCVariant(const eXMLRPCVariant &c); + ~eXMLRPCVariant(); + + std::map<eString,eXMLRPCVariant*> *getStruct(); + std::vector<eXMLRPCVariant> *getArray(); + __s32 *getI4(); + bool *getBoolean(); + eString *getString(); + double *getDouble(); +// QDateTime *getDatetime(); +// QByteArray *getBase64(); + + void toXML(eString &); +}; + +class eXMLRPCResponse: public eHTTPDataSource +{ + XMLTreeParser parser; + eString result; + int size; + int wptr; + int doCall(); +public: + eXMLRPCResponse(eHTTPConnection *c); + ~eXMLRPCResponse(); + + int doWrite(int); + void haveData(void *data, int len); +}; + +void xmlrpc_initialize(eHTTPD *httpd); +void xmlrpc_addMethod(eString methodName, int (*)(std::vector<eXMLRPCVariant>&, ePtrList<eXMLRPCVariant>&)); +void xmlrpc_fault(ePtrList<eXMLRPCVariant> &res, int faultCode, eString faultString); +int xmlrpc_checkArgs(eString args, std::vector<eXMLRPCVariant>&, ePtrList<eXMLRPCVariant> &res); + +class eHTTPXMLRPCResolver: public eHTTPPathResolver +{ +public: + eHTTPXMLRPCResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); +}; + +#endif + +#endif //DISABLE_NETWORK |
