aboutsummaryrefslogtreecommitdiff
path: root/lib/network
diff options
context:
space:
mode:
Diffstat (limited to 'lib/network')
-rw-r--r--lib/network/Makefile.am7
-rw-r--r--lib/network/Makefile.in0
-rw-r--r--lib/network/http_dyn.cpp66
-rw-r--r--lib/network/http_dyn.h34
-rw-r--r--lib/network/http_file.cpp228
-rw-r--r--lib/network/http_file.h37
-rw-r--r--lib/network/httpd.cpp631
-rw-r--r--lib/network/httpd.h123
-rw-r--r--lib/network/serversocket.cpp55
-rw-r--r--lib/network/serversocket.h18
-rw-r--r--lib/network/serversocket.lobin0 -> 191574 bytes
-rw-r--r--lib/network/socket.cpp294
-rw-r--r--lib/network/socket.h63
-rw-r--r--lib/network/socket.lobin0 -> 224253 bytes
-rw-r--r--lib/network/xmlrpc.cpp518
-rw-r--r--lib/network/xmlrpc.h81
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
new file mode 100644
index 00000000..8039034b
--- /dev/null
+++ b/lib/network/serversocket.lo
Binary files differ
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
new file mode 100644
index 00000000..1d809469
--- /dev/null
+++ b/lib/network/socket.lo
Binary files differ
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