server mode for Qt, missing tables
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 24 Dec 2010 17:44:30 +0000 (17:44 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 24 Dec 2010 17:44:30 +0000 (17:44 +0000)
git-svn-id: https://silmor.de/svn/softmagic/pack/trunk@667 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

35 files changed:
README
doc/index.html
doc/qtsrv.html [new file with mode: 0644]
doc/wolf-comm.html
examples/clock/clock.wolf
examples/clock/qtc/clock.cpp [new file with mode: 0644]
examples/clock/qtc/clock.h [new file with mode: 0644]
examples/clock/qtc/clock.pro [new file with mode: 0644]
examples/clock/qts/clock.cpp [new file with mode: 0644]
examples/clock/qts/clock.h [new file with mode: 0644]
examples/clock/qts/clockserv.pro [new file with mode: 0644]
phpbase/autoload.php
phpbase/exception.php
qtbase/include/WServer [new file with mode: 0644]
qtbase/include/WServerReply [new file with mode: 0644]
qtbase/include/WServerRequest [new file with mode: 0644]
qtbase/include/interface.h
qtbase/include/server.h [new file with mode: 0644]
qtbase/include/transaction.h
qtbase/src/interface.cpp
qtbase/src/server.cpp [new file with mode: 0644]
qtbase/src/server_p.h [new file with mode: 0644]
qtbase/src/transaction.cpp
qtbase/wbase.pro
woc/php/phpclass.cpp
woc/php/phpout.h
woc/php/phptrans.cpp
woc/proc/procclass.cpp
woc/proc/procclass.h
woc/proc/proctrans.cpp
woc/proc/proctrans.h
woc/qt/qtclass.cpp
woc/qt/qtout.cpp
woc/qt/qtstrans.cpp
woc/qt/qtstrans.h

diff --git a/README b/README
index 7b4d073..553f29b 100644 (file)
--- a/README
+++ b/README
@@ -7,7 +7,7 @@ version 0.5
 (c) Konrad Rosenbaum, 2009-2010
 
 Directories qtbase, woc:
-The CPP-source files are licensed under GPLv3 or at your option any newer version
+The C++-source files are licensed under GPLv3 or at your option any newer version
 of the license. See COPYING.GPL for details.
 
 Directory phpbase:
@@ -24,4 +24,4 @@ own terms.
 Documentation
 --------------
 
-See doc/index.html for documentation.
+See doc/index.html for documentation and build-instructions.
index 9765131..993bd58 100644 (file)
@@ -5,7 +5,7 @@
 
 <h2>Introduction</h2>
 
-PACK is a toolkit that speeds up the development of web based client-server-applications by providing wrappers for database storage and client-server communication. The main component is the Web Object Compiler (WOC) that translates XML based interface descriptions into the target language.<p>
+PACK is a toolkit that speeds up the development of database driven web based client-server-applications by providing wrappers for database storage and client-server communication. The main component is the Web Object Compiler (WOC) that translates XML based interface descriptions into the target language.<p>
 
 The following languages are currently supported:<br>
 <table frame="1" border="1">
@@ -16,11 +16,11 @@ The following languages are currently supported:<br>
 
 <tr><td><b>C++/Qt 4.x</b></td>
  <td>yes</td>
- <td>planned</td>
+ <td><i>planned</i></td>
 </tr>
 
 <tr><td><b>PHP 5.x</b></td>
- <td>planned</td>
+ <td><i>planned</i></td>
  <td>yes</td>
 </tr>
 
@@ -29,27 +29,52 @@ The following languages are currently supported:<br>
 </tr>
 
 <tr><td><b>XML Schema</b></td>
- <td colspan="2">planned for validation</td>
+ <td colspan="2"><i>planned/incomplete</i> for validation and as base for SOAP</td>
 </tr>
-</table>
+
+<tr><td><b>XML SOAP</b></td>
+ <td colspan="2"><i>planned/incomplete</i> generates WSDL and Schema files for the communication layer only</td>
+</tr>
+</table><p>
+
+
+<h2>Building PACK</h2>
+
+For compiling PACK you need a C++ compiler (eg. GCC) and Qt4.x. 
+The meta-compiler WOC requires at least Qt 4.4, the Qt binding itself requires at least Qt 4.7.
+For compiling and running generated code it all depends on your target:<br>
+<table frame="1" border="1">
+<tr><td>C++/Qt<td>A C++ compiler and at least Qt 4.7, modules: Core, Network, XML, optionally XMLpatterns</tr>
+<tr><td>PHP<td>PHP, at least version 5.2</tr>
+<tr><td>SOAP<td>any compatible toolkit that can handle WSDL files</tr>
+</table><p>
+
+Compiling the meta-compiler is relatively easy: just enter the woc directory and call qmake and make (or open the woc.pro file in QtCreator and compile from there). The same approach works for PACK's Qt base library. The PHP library of PACK does not need to be compiled, just include it in your project.
 
 <h2>Concepts</h2>
 
-In a classic three-tier architecture (see below) there are two interface layers: data and communication.
-The Web Object Compiler helps implementing these three-tier architecture interfaces.<p>
+In a classic three-tier architecture (see below) there are two interface layers between the actual tiers: data and communication.
+The Web Object Compiler helps implementing these interfaces between layers.<p>
 
 <table frame="1" border="1">
-<tr><td>Layer 3<td>Client Presentation Layer</tr>
-<tr><td rowspan="3"><i>Communication Interface</i><td><i><b>Server Abstraction Layer</b></i></tr>
- <tr><td><i>Network Protocol (eg. HTTP)</i></tr>
- <tr><td><i><b>Communication Abstraction Layer</b></i></tr>
+<tr><td>Layer 3<td>Client, Presentation</tr>
+<tr><td><i>Communication Interface</i><td><i><b>Communication Abstraction Layer</b></i></tr>
 <tr><td>Layer 2<td>Server Business Logic</tr>
-<tr><td rowspan="2"><i>Data Interface</i><td><i><b>Database Abstraction Layer</b></i></tr>
- <tr><td><i>DB-Protocol (eg. LibMySQL)</i></tr>
-<tr><td>Layer 1<td>Database (eg. MySQL)</tr>
+<tr><td><i>Data Interface</i><td><i><b>Database Abstraction Layer</b></i></tr>
+<tr><td>Layer 1<td>Database (eg. MySQL, PostgreSQL)</tr>
 </table>
 <i>Three Tier Architecture</i><p>
 
+PACK currently supports MySQL and PostgreSQL as databases, but it is relatively easy to port to new SQL-based database systems. Non-SQL relational database systems may be usable, but take much more effort to create an interface driver.<p>
+
+The structure of the database is written up as XML statements in PACK's WOLF files - from this PACK generates the code necessary to create the database tables and to execute queries.<p>
+
+The Data Interface provides wrapper classes around tables which are more convenient to use than plain SQL and will also automatically use the correct SQL dialect for queries.<p>
+
+The business logic needs to be written by the user, but using PACK's facilities it is often very easy to do.<p>
+
+The Communication Interface generated by PACK contains two types of structures: communication classes and transactions. Transactions are operations that the client can execute (remotely) in the server. Each transaction has a unique name, input data and output data. This data can use primitive built-in types or project specific communication classes. These classes define structures of primitive types and other communication classes that enclose the data that is transferred between client and server. The classes also wrap the code necessary to serialize and unserialize themselves during transmission. In addition it is possible to write direct translations between database tables and communication classes to assist in some of the typical translations often performed in the business logic.
+
 <h2>The Language</h2>
 
 <ul>
@@ -68,8 +93,10 @@ The Web Object Compiler helps implementing these three-tier architecture interfa
  <li><a href="phpsrv.html">PHP Server Binding<ul>
   <li><a href="phpbase/index.html">Source Docu</li>
  </ul></li>
- <li><a href="qtcli.html">Qt 4 Client Binding<ul>
-  <li><a href="qtbase/index.html">Source Docu</li>
+ <li>Qt Side:<ul>
+  <li><a href="qtcli.html">Qt 4 Client Binding</a></li>
+  <li><a href="qtsrv.html">Qt 4 Server Binding</a></li>
+  <li><a href="qtbase/index.html">Source Docu</a></li>
  </ul></li>
 </ul>
 
diff --git a/doc/qtsrv.html b/doc/qtsrv.html
new file mode 100644 (file)
index 0000000..6804703
--- /dev/null
@@ -0,0 +1,58 @@
+<html>
+<title>Qt Server Binding</title>
+<body>
+
+<h1>PACK Qt Server Binding</h1>
+
+<h2>Setting up the Server</h2>
+
+The Qt Server Binding understands the SCGI protocol to talk to a web server as a server side content provider. Alternatively a simple cgi2scgi gateway can be used to bridge the web server's CGI interface to the Qt binding.<p>
+
+<h3>The Qt Server</h3>
+
+The Qt binding is a separate server with the actual web server acting as a proxy to it.<p>
+
+An instance of the class WServer must be set up to face the web server, the generated instance of WInterface is registered at one or several instances of WServer. WServer can be set up to use a listening TCP or local socket - although the specification for SCGI specifies TCP only. If you use TCP it is recommended to bind to localhost (IPv4: 127.0.0.1, IPv6: ::1).<p>
+
+It is also recommended to let the Qt program run as a service, one possibility is to use the QtService solution to implement this.
+
+<h3>Setup with Apache</h3>
+
+Apache is listed here as an example of a setup, the SCGI protocol is also supported by lighttpd and IIS.<p>
+
+For Apache there are two possibilities for setup: using SCGI and CGI. For SCGI you can use mod_proxy_scgi or mod_scgi, this example is for mod_proxy_scgi:
+<pre>
+ProxyPass /localpath scgi://127.0.0.1:4000
+</pre>
+In this case the Qt server has to be set up using a TCP socket, since Apache implements SCGI to the letter.<p>
+
+Using CGI (mod_cgi or mod_cgid) you need to plug a CGi-to-SCGI bridge between Apache and the Qt binding - one such bridge is the C-program in the cgi2scgi directory, it takes environment variables or arguments to configure its target. First it needs to be linked into Apaches paths:
+<pre>
+ScriptAlias /packcgi /usr/local/pack/cgi2scgi/cgi2scgi
+</pre>
+In this case the script is linked directly to the path <tt>/packcgi</tt> - another possibility is to link the path that the cgi2scgi binary is in or to use a wrapper script. If the binary is used directly you have to use mod_env to hand over the configuration:
+<pre>
+&lt;Directory "/usr/local/pack/cgi2scgi">
+  Options ExecCGI
+  Order allow,deny
+  Allow from all
+  SetEnv SCGI_FILE /tmp/scgisocket
+  #SetEnv SCGI_HOST 127.0.0.1
+  #SetEnv SCGI_PORT 4000
+&lt;/Directory>
+</pre>
+You have the choice of using SCGI_FILE for an AF_UNIX local socket or SCGI_HOST and SCGI_PORT for a TCP socket. If you use a wrapper script you can use arguments instead - one argument for an AF_UNIX socket, two for TCP:
+<pre>
+#!/bin/sh
+exec /usr/local/pack/cgi2scgi/cgi2scgi localhost 4000
+</pre>
+
+
+<h2>Binding to the Base</h2>
+
+<h2>Communication Classes</h2>
+
+<h2>Transactions</h2>
+
+
+</html>
\ No newline at end of file
index f5f8417..6b0ca2e 100644 (file)
@@ -11,6 +11,8 @@ An example class can be seen here:
 
 <pre>
 &lt;Class name="Ticket">
+  &lt;Base lang="php/client" class="MyBaseClass"/>
+  
   &lt;Enum name="TicketState" refColumn="ticket:status"/>
   &lt;Property name="ticketid" type="astring" id="yes"/>
   &lt;Property name="eventid" type="int"/>
@@ -40,9 +42,12 @@ Class attributes:
 <table frame="1" border="1">
 <tr><td><b>Attribute</b></td><td><b>Description</b></td></tr>
 <tr><td>name</td><td>the name of the class</td></tr>
-<tr><td>abstract</td><td>(optional) marks the class abstract</td></tr>
+<tr><td>abstract</td><td>(optional) marks the class abstract in all generated language targets</td></tr>
+<tr><td>base</td><td>(optional) marks this class derived from the class given as base, the base class must already exist, this attribute overrides the use of the Base tag</td></tr>
 </table><p>
 
+The Base tag can be used to derive the class from a class different from WObject, although that class must be derived from WObject. If you want to derive from a class defined in the WOLF file, use the base attribute instead.
+
 <h3>Properties and Types</h3>
 
 The Enum tag defines a local enumeration. In most cases they will simply reference an enumeration that has already been defined in a database table with a refColumn attribute (syntax: <tt><i>table</i>:<i>column</i></tt>), but it may also locally define its values by using the same Value tags used for database enums.<p>
@@ -62,7 +67,7 @@ Property types:
 <tr><td>int</td><td>an integer - it is at least a 32bit signed type; it is transported as attribute</td></tr>
 <tr><td>astring</td><td>a string that can be safely stored in a XML attribute</td></tr>
 <tr><td>string</td><td>a string that is transported as element and can reach any length</td></tr>
-<tr><td><i>EnumName</i></td>the name of an enum defined in the same class is possible, local storage is as integer, transport is as string equalling the symbolic name of the value in an attribute</td></tr>
+<tr><td><i>EnumName</i></td><td>the name of an enum defined in the same class is possible, local storage is as integer, transport is as string equalling the symbolic name of the value in an attribute</td></tr>
 <tr><td><i>ClassName</i></td><td>the name of a defined class makes the property an instance of that class; they are transported as sub-elements of this class</td></tr>
 <tr><td>List:*</td><td>prefixing the type with "List:" makes the property a list of that type - any of the above types can be made into lists; on transport list values are always serialized as elements</td></tr>
 </table><p>
index 6594ed4..977a115 100644 (file)
@@ -14,6 +14,7 @@
        
        <!-- class -->
        <Class name="Time">
+               <Abstract lang="php/server"/>
                <Doc>Transport class for clock.</Doc>
                <Property name="year" type="int"/>
                <Property name="month" type="int"/>
                <Property name="minute" type="int"/>
                <Property name="second" type="int"/>
                <Property name="dayOfWeek" type="astring"/>
-               <Property name="monthName" type="string"/>
+               <Property name="monthName" type="astring"/>
        </Class>
        
        <!-- transaction -->
        <Transaction name="GetTime" mode="open">
                <Input/>
-               <Call lang="php" method="Session::login($this);"/>
+               <Call lang="php" method="Time::getTimeCall($this);"/>
+               <Call lang="qt" method="getTimeCall(this);" include="clock.h"/>
                <Output>
                        <Var name="time" type="Time"/>
                </Output>
        </Transaction>
+       <Transaction name="GetTimeOffsetUtc" mode="open">
+               <Input>
+                       <Var name="minutesFromUtc" type="int"/>
+               </Input>
+               <Call lang="qt" method="getTimeCall2(this);" include="clock.h"/>
+               <Output>
+                       <Var name="time" type="Time"/>
+               </Output>
+       </Transaction>
+       <Transaction name="ExitServer" mode="open">
+               <Input>
+                       <Var name="magicWord" type="string"/>
+               </Input>
+               <Call lang="qt" method="shutdownServer(this);" include="clock.h"/>
+               <Output>
+                       <Var name="shuttingdown" type="bool"/>
+               </Output>
+       </Transaction>
+       <Transaction name="AuthTest" mode="auth">
+               <Call lang="qt" method='sethello((QString)"Hello World");' />
+               <Output>
+                       <Var name="hello" type="string"/>
+               </Output>
+       </Transaction>
+       <Transaction name="ACLTest" mode="checked">
+               <Call lang="qt" method='sethello((QString)"Hello World");' />
+               <Output>
+                       <Var name="hello" type="string"/>
+               </Output>
+       </Transaction>
 </Wolf>
\ No newline at end of file
diff --git a/examples/clock/qtc/clock.cpp b/examples/clock/qtc/clock.cpp
new file mode 100644 (file)
index 0000000..e48c6f2
--- /dev/null
@@ -0,0 +1,106 @@
+#include "clock.h"
+#include <CIncludeAll>
+#include <QApplication>
+#include <QDateTime>
+#include <QDebug>
+#include <QFormLayout>
+#include <QInputDialog>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QSpinBox>
+#include <WInterface>
+
+///the URL of the server
+QString url;
+///global interface to the server
+CInterface ifc;
+
+ClockDisplay::ClockDisplay()
+{
+       QFormLayout*fl;
+       setLayout(fl=new QFormLayout);
+       fl->addRow("Server URL:",new QLabel(url));
+       QPushButton*p;
+       fl->addRow("Query:",p=new QPushButton("Get Server Local Time"));
+       connect(p,SIGNAL(clicked()),this,SLOT(askTime()));
+       fl->addRow("Offset from Utc (min):",offs=new QSpinBox);
+       offs->setRange(-14*60,14*60);
+       fl->addRow("Query:",p=new QPushButton("Get UTC/offset Time"));
+       connect(p,SIGNAL(clicked()),this,SLOT(askOffsetTime()));
+       fl->addRow("Time of Query:",qtime=new QLabel);
+       fl->addRow("Server Reported Time:",stime=new QLabel);
+       fl->addRow("Shut Down",p=new QPushButton("Attempt to shut down Server"));
+       connect(p,SIGNAL(clicked()),this,SLOT(shutdownServer()));
+}
+
+void ClockDisplay::askTime()
+{
+       CTGetTime gt=ifc.queryGetTime();
+       displayTime(gt.gettime(),gt.hasError());
+}
+
+void ClockDisplay::askOffsetTime()
+{
+       CTGetTimeOffsetUtc gt=ifc.queryGetTimeOffsetUtc(offs->value());
+       displayTime(gt.gettime(),gt.hasError());
+}
+
+void ClockDisplay::shutdownServer()
+{
+       //ask for the magic word, needed for shutting down
+       QString s=QInputDialog::getText(this,"Magic Word","Please enter the magic word ('please'):");
+       if(s=="")return;
+       //query the server
+       CTExitServer es=ifc.queryExitServer(s);
+       //display result
+       if(es.hasError() || es.getshuttingdown().isNull() || es.getshuttingdown().value()==false)
+               QMessageBox::warning(this,"Error","Unable to shut down server.");
+       else
+               QMessageBox::information(this,"Success","The server is shutting down.");
+}
+
+
+void ClockDisplay::displayTime(const COTime& tm,bool hasError)
+{
+       //display current local time for comparison
+       qtime->setText(QDateTime::currentDateTime().toString());
+       //was there an error?
+       if(hasError){
+               stime->setText("#ERROR");
+               return;
+       }
+       //display server time
+       QString tx=QString("%1-%2-%3 %4:%5:%6")
+               .arg((int)tm.year(),4,10,(QChar)'0')
+               .arg((int)tm.month(),2,10,(QChar)'0')
+               .arg((int)tm.day(),2,10,(QChar)'0')
+               .arg((int)tm.hour(),2,10,(QChar)'0')
+               .arg((int)tm.minute(),2,10,(QChar)'0')
+               .arg((int)tm.second(),2,10,(QChar)'0') ;
+       stime->setText(tx);
+
+}
+
+
+int main(int argc,char**argv)
+{
+       //central application object
+       QApplication app(argc,argv);
+       //parse arguments, we expect the URL...
+       QStringList args=app.arguments();
+       if(args.size()<2){
+               qDebug()<<"Usage:"<<args[0].toAscii().data()<<"url...";
+               return 1;
+       }
+       url=args[1];
+       //initialize the interface, set URL and debug mode
+       ifc.setUrl(url);
+       ifc.setLogLevel(WInterface::LogDetailed);
+       //init display
+       ClockDisplay cd;
+       cd.show();
+       //enter event loop
+       return app.exec();
+}
diff --git a/examples/clock/qtc/clock.h b/examples/clock/qtc/clock.h
new file mode 100644 (file)
index 0000000..e20cd76
--- /dev/null
@@ -0,0 +1,26 @@
+#include <QDialog>
+
+class QSpinBox;
+class COTime;
+class QLineEdit;
+class QLabel;
+///The display class for the clock client.
+class ClockDisplay:public QDialog
+{
+       Q_OBJECT
+       public:
+               ///instantiates the display
+               ClockDisplay();
+       public slots:
+               ///ask for the server time
+               void askTime();
+               ///ask for server time as a time with specific offset from UTC
+               void askOffsetTime();
+               ///attempt to shut the server down
+               void shutdownServer();
+               ///helper: show the time we got from the server
+               void displayTime(const COTime&,bool);
+       private:
+               QLabel*qtime,*stime;
+               QSpinBox*offs;
+};
diff --git a/examples/clock/qtc/clock.pro b/examples/clock/qtc/clock.pro
new file mode 100644 (file)
index 0000000..cc5d2de
--- /dev/null
@@ -0,0 +1,15 @@
+TEMPLATE = app
+TARGET = clock
+CONFIG += debug link_prl
+QT += xml network
+LIBS += -L../../../qtbase -lqwbase
+INCLUDEPATH += ../../../qtbase/include
+
+OBJECTS_DIR = .ctmp
+MOC_DIR = .ctmp
+RCC_DIR = .ctmp
+
+SOURCES += clock.cpp
+HEADERS += clock.h
+
+include(wob/wob.pri)
\ No newline at end of file
diff --git a/examples/clock/qts/clock.cpp b/examples/clock/qts/clock.cpp
new file mode 100644 (file)
index 0000000..8eac26b
--- /dev/null
@@ -0,0 +1,137 @@
+#include <CIncludeAll>
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QDebug>
+#include <QStringList>
+#include <QTimer>
+#include <WServer>
+#include "clock.h"
+
+
+///The document used if all else fails...
+static const QString rootdoc=
+"<h1>Clock Example</h1>\n"
+"Point your client to subdirectory .../clock<p>\n"
+"Go to .../debug for the debugging reflector...\n";
+
+///global web machine interface
+CInterface ifc;
+
+
+///helper function for handlers below, converts QDataTime to COTime
+static COTime toCOTime(const QDateTime&now)
+{
+       COTime tm;
+       tm.setyear(now.date().year());
+       tm.setmonth(now.date().month());
+       tm.setmonthName(now.date().toString("MMMM"));
+       tm.setday(now.date().day());
+       tm.setdayOfWeek(now.date().toString("dddd"));
+       tm.sethour(now.time().hour());
+       tm.setminute(now.time().minute());
+       tm.setsecond(now.time().second());
+       return tm;
+}
+
+///implements the GetTime transaction
+void getTimeCall(CTGetTime* trans)
+{
+       QDateTime now=QDateTime::currentDateTime();
+       trans->settime(toCOTime(now));
+}
+
+///implements the GetTimeOffsetUtc transaction
+void getTimeCall2(CTGetTimeOffsetUtc* trans)
+{
+       QDateTime now=QDateTime::currentDateTime().toUTC();
+       now.addSecs(trans->getminutesFromUtc()*60);
+       trans->settime(toCOTime(now));
+}
+
+///owner object for all servers, when it is deleted all servers will shut down
+QObject*owner=0;
+///handler for the ShutdownServer transaction
+void shutdownServer(CTExitServer* trans)
+{
+       //check whether the user said "please"
+       if(trans->getmagicWord().trimmed().toLower()==QString("please")){
+               trans->setshuttingdown(true);
+               if(owner!=0)
+                       QTimer::singleShot(1,owner,SLOT(deleteLater()));
+               QTimer::singleShot(100,QCoreApplication::instance(),SLOT(quit()));
+       }else{
+               trans->setshuttingdown(false);
+       }
+}
+
+///main function
+int main(int argc,char**argv)
+{
+       //central application object
+       QCoreApplication app(argc,argv);
+       owner=new QObject;
+       //parse command line arguments
+       QStringList args=app.arguments();
+       bool haveserv=false;
+       for(int i=0;i<args.size();i++){
+               QStringList sl=args[i].split("=");
+               if(sl.size()!=2)continue;
+               WServer*server=0;
+               //TCP server
+               if(sl[0]=="-net"){
+                       sl=sl[1].split(",");
+                       //default is localhost
+                       QHostAddress addr("127.0.0.1");
+                       //get the host
+                       if(sl.size()>1){
+                               addr=QHostAddress(sl[0]);
+                               if(addr.isNull()){
+                                       qDebug()<<"Invalid IP address"<<sl[0]<<"- aborting.";
+                                       return 1;
+                               }
+                       }
+                       bool ok;
+                       //get the port
+                       int port=sl.last().toInt(&ok);
+                       if(!ok){
+                               qDebug()<<"not a port:"<<sl.last()<<"- aborting.";
+                               return 1;
+                       }
+                       if(port<0||port>65535){
+                               qDebug()<<"illegal port"<<port<<"- aborting.";
+                               return 1;
+                       }
+                       qDebug()<<"initializing network SCGI host"<<addr.toString()<<"on port"<<port;
+                       server=new WServer(addr,port,owner);
+               }else
+               //AF_UNIX server
+               if(sl[0]=="-local"){
+                       qDebug()<<"initializing local SCGI server"<<sl[1];
+                       server=new WServer(sl[1],owner);
+               }else continue;
+               //do we have a server?
+               if(server!=0){
+                       //if so: check whether it was successfully initialized
+                       if(!server->isActive()){
+                               delete server;
+                               qDebug()<<"unable to initialize server";
+                               continue;
+                       }
+                       //remember there is one
+                       haveserv=true;
+                       //register /debug URL - simple reflector
+                       server->enableDebugUrl();
+                       //register fall back document to everything below / that does not have something else
+                       server->registerStatic("/",rootdoc);
+                       //register actual server side interface to /clock
+                       server->registerInterface("/clock",&ifc);
+               }
+       }
+       //check there is a working server
+       if(!haveserv){
+               qDebug()<<"Use -net=IPaddr,port, -net=port, or -local=file to configure the SCGI server.";
+               return 1;
+       }
+       //enter event loop and wait for requests...
+       return app.exec();
+}
\ No newline at end of file
diff --git a/examples/clock/qts/clock.h b/examples/clock/qts/clock.h
new file mode 100644 (file)
index 0000000..a6651d2
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef CLOCK_H
+#define CLOCK_H
+
+//This file declares the handlers for transactions.
+//They are implemented in clock.cpp.
+
+class CTGetTime;
+class CTGetTimeOffsetUtc;
+class CTExitServer;
+void getTimeCall(CTGetTime*);
+void getTimeCall2(CTGetTimeOffsetUtc*);
+void shutdownServer(CTExitServer*);
+
+#endif
diff --git a/examples/clock/qts/clockserv.pro b/examples/clock/qts/clockserv.pro
new file mode 100644 (file)
index 0000000..d001c11
--- /dev/null
@@ -0,0 +1,16 @@
+TEMPLATE = app
+TARGET = clockserv
+QT += network xml
+QT -= gui
+CONFIG += debug link_prl
+LIBS += -L../../../qtbase -lqwbase
+INCLUDEPATH += ../../../qtbase/include
+
+OBJECTS_DIR = .ctmp
+MOC_DIR = .ctmp
+RCC_DIR = .ctmp
+
+HEADERS += clock.h
+SOURCES += clock.cpp
+
+include(wob/wob.pri)
\ No newline at end of file
index 9717817..d2165af 100644 (file)
@@ -29,6 +29,8 @@ wob_autoclass("WobSchemaBase",$d."/schema.php");
 wob_autoclass("WobTransactionBase",$d."/transaction.php");
 wob_autoclass("WobXmlException",$d."/exception.php");
 wob_autoclass("WobTransactionError",$d."/exception.php");
+wob_autoclass("WobWobTransactionError",$d."/exception.php");
+wob_autoclass("WobSoapTransactionError",$d."/exception.php");
 wob_autoclass("WObject",$d."/object.php");
 //load the linguist dummies, since we use them quite often
 include_once($d."/tr.php");
index ca0d834..32ec2a5 100644 (file)
 
 class WobXmlException extends Exception {};
 
-/**general Wob Error - thrown whenever a transaction fails for any reason*/
-class WobTransactionError extends Exception
+/**general Transaction Error - children of this class are thrown whenever a transaction fails for any reason*/
+class TransactionError extends Exception
 {
        protected $etype="_system";
        protected $estr="";
        protected $trans=null;
        protected $mode=WobTransactionBase::WobEncoding;
        
-       public function __construct(WobTransactionBase $trans,$errorString,$errorType="_system")
+       public function __construct($errorString,$errorType="_system")
        {
                parent::__construct($errorString);
                $this->etype=$errorType;
                $this->estr=$errorString;
-               $this->trans=$trans;
-               if($trans!==null){
-                       $this->mode=$trans->messageEncoding();
-               }
        }
        
        /**used by machine oriented transaction to print the proper XML representation of the error*/
@@ -46,7 +42,11 @@ class WobTransactionError extends Exception
        {
                header("X-WobResponse-Status: Error");
                $xml=new DOMDocument;
-               $root=$xml->createElementNS($this->trans->xmlPackNamespace(),"WobError",$this->estr);
+               if($this->trans===null)
+                       $ns="ns:wob-error";
+               else
+                       $ns=$this->trans->xmlPackNamespace();
+               $root=$xml->createElementNS($ns,"WobError",$this->estr);
                $root->setAttribute("type",$this->etype);
                $xml->appendChild($root);
                print($xml->saveXml());
@@ -60,22 +60,35 @@ class WobTransactionError extends Exception
        
 };
 
+/**general Wob Error - thrown whenever a transaction fails for any reason and the transaction is already known*/
+class WobTransactionError extends TransactionError
+{
+       public function __construct(WobTransactionBase $trans,$errorString,$errorType="_system")
+       {
+               parent::__construct($errorString,$errorType);
+               $this->trans=$trans;
+               if($trans!==null){
+                       $this->mode=$trans->messageEncoding();
+               }
+       }
+};
+
 /**specific Wob Error - thrown where we know we use Wob protocol*/
-class WobWobTransactionError extends WobTransactionError
+class WobWobTransactionError extends TransactionError
 {
        public function __construct($errorString,$errorType="_system")
        {
-               parent::__construct(null,$errorString,$errorType);
+               parent::__construct($errorString,$errorType);
                $this->mode=WobTransactionBase::WobEncoding;
        }
 };
 
 /**specific Wob Error - thrown where we know we use SOAP protocol*/
-class WobSoapTransactionError extends WobTransactionError
+class WobSoapTransactionError extends TransactionError
 {
        public function __construct($errorString,$errorType="_system")
        {
-               parent::__construct(null,$errorString,$errorType);
+               parent::__construct($errorString,$errorType);
                $this->mode=WobTransactionBase::Soap12Encoding;
        }
 };
diff --git a/qtbase/include/WServer b/qtbase/include/WServer
new file mode 100644 (file)
index 0000000..bce425e
--- /dev/null
@@ -0,0 +1 @@
+#include "server.h"
diff --git a/qtbase/include/WServerReply b/qtbase/include/WServerReply
new file mode 100644 (file)
index 0000000..bce425e
--- /dev/null
@@ -0,0 +1 @@
+#include "server.h"
diff --git a/qtbase/include/WServerRequest b/qtbase/include/WServerRequest
new file mode 100644 (file)
index 0000000..bce425e
--- /dev/null
@@ -0,0 +1 @@
+#include "server.h"
index 04dc8b4..5d7d70c 100644 (file)
@@ -21,6 +21,8 @@
 class QNetworkAccessManager;
 class QNetworkRequest;
 class QNetworkReply;
+class WServerRequest;
+class WServerReply;
 
 class WInterface:public QObject
 {
@@ -45,6 +47,10 @@ class WInterface:public QObject
                ///called by WTransaction to post a request
                QNetworkReply* post(const QNetworkRequest&request,const QByteArray&data);
                
+               friend class WServer;
+               ///called by WServer to execute a transaction
+               virtual WServerReply execute(const WServerRequest&);
+               
        public:
                virtual ~WInterface();
                /**overwrite if you need additional headers (eg. session-ids) for certain transactions (transaction name is handed in as string argument); per default returns empty map*/
diff --git a/qtbase/include/server.h b/qtbase/include/server.h
new file mode 100644 (file)
index 0000000..2719e88
--- /dev/null
@@ -0,0 +1,118 @@
+//
+// C++ Interface: WInterface
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOB_SERVER_H
+#define WOB_SERVER_H
+
+#include <QObject>
+#include <QHostAddress>
+#include <QMap>
+#include <QMetaType>
+
+class QTcpServer;
+class QLocalServer;
+class WInterface;
+
+class WServerRequest
+{
+       public:
+               WServerRequest();
+               WServerRequest(const WServerRequest&);
+               
+               WServerRequest& operator=(const WServerRequest&);
+               
+               bool hasHeader(const QString&)const;
+               bool hasCgiHeader(const QString&)const;
+               QByteArray header(const QString&)const;
+               QByteArray cgiHeader(const QString&)const;
+               
+               QByteArray bodyData()const;
+               
+               bool isValid()const;
+               
+               QByteArray dump()const;
+               
+               QString pathInfo()const;
+               
+       protected:
+               WServerRequest(const QByteArray&);
+               void setBody(const QByteArray&);
+               friend class WServer;
+               friend class WServerReceiver;
+               
+       private:
+               QMap<QByteArray,QByteArray>mhead;
+               QByteArray mbody;
+};
+Q_DECLARE_METATYPE(WServerRequest);
+
+class WServerReply
+{
+       public:
+               WServerReply();
+               WServerReply(const WServerReply&);
+               WServerReply& operator=(const WServerReply&);
+               
+               bool setStatus(short code,QString str);
+               bool setHeader(const QString&,const QString&);
+               void setBody(const QString&);
+               void setBody(const QByteArray&);
+               
+       protected:
+               friend class WServer;
+               QByteArray toWireFormat()const;
+       private:
+               short mStatCode;
+               QByteArray mStatStr,mBody;
+               QMap<QString,QPair<QString,QString> >mHead;
+};
+
+/** Represents an SCGI server as a proxy to server side interfaces.
+*/
+class WServer:public QObject
+{
+       Q_OBJECT
+       public:
+               WServer(const QString&,QObject*parent=0);
+               WServer(const QHostAddress&,unsigned short,QObject*parent=0);
+               virtual ~WServer();
+               
+               bool isActive()const{return tcpserv!=0 || locserv!=0;}
+               
+               int receiveTimeout()const{return mrecvtimeout;}
+       public slots:
+               void registerInterface(const QString&,WInterface*);
+               void registerStatic(const QString&,const QString&);
+               void unregisterPath(const QString&);
+               void register404(const QString&);
+               void register400(const QString&);
+               void setReceiveTimeout(int);
+               void restrictSourceHosts(const QPair<QHostAddress,int>&);
+               void enableDebugUrl(bool enable=true);
+               
+       private slots:
+               void newConnection();
+               void handleConnection(QIODevice*);
+               void handleRequest(WServerRequest,QIODevice*);
+       private:
+               void handleRequestHelper(WServerRequest,QIODevice*);
+               QTcpServer*tcpserv;
+               QLocalServer*locserv;
+               QMap<QString,WInterface*>iface;
+               QMap<QString,QString>statcontent;
+               QString err404,err400;
+               int mrecvtimeout;
+               QPair<QHostAddress,int>sourcehost;
+               bool mdebugenabled;
+};
+
+#endif
index 2457d6e..658ec14 100644 (file)
@@ -121,9 +121,12 @@ class WTransaction:public WHelper
                /**internal: execute a query on the web asynchronously*/
                virtual void startQuery(QString,QByteArray);
                
+               /**internal: encode an error, used by server*/
+               virtual QByteArray encodeError()const;
+               
        protected slots:
                /**internal: collect query data*/
-               virtual void endQuery()=0;
+               virtual void endQuery();
        signals:
                /** this signal is raised when the transaction is finished successfully or with an error, data or an error information is available*/
                void finished();
index ba825da..5e1fc7f 100644 (file)
@@ -10,7 +10,8 @@
 //
 //
 
-#include "WInterface"
+#include <WInterface>
+#include <WServer>
 
 // #include <QHttp>
 #include <QMutex>
@@ -81,3 +82,12 @@ void WInterface::setProxy(QString proxyhost,unsigned short proxyport,QString pro
        m_proxypass=proxypassword;
        m_netman->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy,proxyhost,proxyport,proxyuser,proxypassword));
 }
+
+WServerReply WInterface::execute(const WServerRequest&)
+{
+       WServerReply rp;
+       rp.setStatus(500,"Not implemented");
+       rp.setHeader("Content-Type","text/html; charset=utf-8");
+       rp.setBody((QString)"<h1>Sorry</h1>Not implemented.");
+       return rp;
+}
\ No newline at end of file
diff --git a/qtbase/src/server.cpp b/qtbase/src/server.cpp
new file mode 100644 (file)
index 0000000..424ceb0
--- /dev/null
@@ -0,0 +1,442 @@
+//
+// C++ Implementation: WInterface
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "server.h"
+#include "server_p.h"
+#include <WInterface>
+
+#include <QDebug>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QStringList>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QTimer>
+
+static int metaid=qRegisterMetaType<WServerRequest>();
+
+#define ERR404 "<h1>Path does not exist</h1>\nSorry, the path you requested does not exist.\n"
+#define ERR400 "<h1>Invalid Request</h1>\nSorry, the request was invalid.\n"
+
+WServer::WServer(const QString&path,QObject*parent)
+:QObject(parent)
+{
+       mrecvtimeout=30000;
+       tcpserv=0;
+       mdebugenabled=false;
+       locserv=new QLocalServer(this);
+       if(!locserv->listen(path)){
+               qDebug()<<"Error creating server socket on path"<<path;
+               delete locserv;
+               locserv=0;
+               return;
+       }
+       connect(locserv,SIGNAL(newConnection()),this,SLOT(newConnection()));
+       err404=ERR404;
+       err400=ERR400;
+}
+
+WServer::WServer(const QHostAddress&host,unsigned short port,QObject*parent)
+:QObject(parent)
+{
+       mrecvtimeout=30000;
+       locserv=0;
+       mdebugenabled=false;
+       tcpserv=new QTcpServer(this);
+       if(!tcpserv->listen(host,port)){
+               qDebug()<<"Unable to create server socket for host"<<host.toString()<<"port"<<port;
+               delete tcpserv;
+               tcpserv=0;
+               return;
+       }
+       connect(tcpserv,SIGNAL(newConnection()),this,SLOT(newConnection()));
+       err404=ERR404;
+       err400=ERR400;
+}
+WServer::~WServer()
+{
+}
+
+void WServer::enableDebugUrl(bool enable){mdebugenabled=enable;}
+
+static inline QString normalizepath(const QString &p)
+{
+       QStringList src=QString(p).replace("\\","/").split("/",QString::SkipEmptyParts);
+       QStringList tgt;
+       for(int i=0;i<src.size();i++){
+               if(src[i]==".")continue;
+               if(src[i]=="..")tgt.removeLast();
+               else tgt<<QUrl::toPercentEncoding(src[i]);
+       }
+       QString ret;
+       for(int i=0;i<tgt.size();i++){
+               ret+="/";
+               ret+=tgt[i];
+       }
+       return ret;
+}
+
+void WServer::registerInterface(const QString&p,WInterface*ifc)
+{
+       QString path=normalizepath(p);
+       iface.remove(path);
+       statcontent.remove(path);
+       if(ifc!=0)
+               iface.insert(path,ifc);
+}
+
+void WServer::registerStatic(const QString&p,const QString&c)
+{
+       QString path=normalizepath(p);
+       iface.remove(path);
+       statcontent.remove(path);
+       statcontent.insert(path,c);
+}
+
+void WServer::unregisterPath(const QString&p)
+{
+       QString path=normalizepath(p);
+       iface.remove(path);
+       statcontent.remove(path);
+}
+
+void WServer::register404(const QString&e){err404=e;}
+void WServer::register400(const QString&e){err400=e;}
+
+void WServer::newConnection()
+{
+       //get connection
+       if(tcpserv!=0){
+               while(tcpserv->hasPendingConnections()){
+                       QTcpSocket*sock=tcpserv->nextPendingConnection();
+                       qDebug()<<"new TCP connection from"<<sock->peerAddress()<<"port"<<sock->peerPort();
+                       if(sourcehost.second>0){
+                               if(!sock->peerAddress().isInSubnet(sourcehost)){
+                                       sock->close();
+                                       sock->deleteLater();
+                                       qDebug()<<"dropping TCP connection";
+                                       continue;
+                               }
+                       }
+                       handleConnection(sock);
+               }
+       }
+       if(locserv!=0){
+               while(locserv->hasPendingConnections()){
+                       qDebug()<<"new Local connection";
+                       handleConnection(locserv->nextPendingConnection());
+               }
+       }
+}
+
+void WServer::handleConnection(QIODevice*conn)
+{
+       new WServerReceiver(this,conn);
+}
+
+static inline bool pathMatch(QString parent,QString subpath)
+{
+       int ps=parent.size();
+       int ss=subpath.size();
+       if(ss<ps)return false;
+       if(ss==ps)return subpath==parent;
+       return subpath.left(ps)==parent && subpath[ps]=='/';
+}
+
+static bool qstringlonger(const QString&s1,const QString&s2)
+{
+       return s1.size()>s2.size();
+}
+
+void WServer::handleRequestHelper(WServerRequest rq,QIODevice*sock)
+{
+       //reject invalid requests outright
+       if(!rq.isValid()){
+               qDebug()<<"rejecting invalid request with code 400";
+               sock->write(QByteArray("Status: 400 Invalid Request\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")+err400.toUtf8());
+               return;
+       }
+       QString path=rq.pathInfo();
+       //check for debug mode
+       if(mdebugenabled && pathMatch("/debug",path)){
+               qDebug()<<"debug: dumping request data as response";
+               sock->write(QByteArray("Status: 200 Ok\r\nContent-Type: text/plain\r\n\r\n"));
+               sock->write(rq.dump());
+               return;
+       }
+       //find a match
+       QStringList k=statcontent.keys();
+       k<<iface.keys();
+       qSort(k.begin(),k.end(),qstringlonger);
+       for(int i=0;i<k.size();i++)
+               if(pathMatch(k[i],path)){
+                       //find a static match
+                       if(statcontent.contains(k[i])){
+                               qDebug()<<"returning static content";
+                               sock->write(QByteArray("Status: 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"));
+                               sock->write(statcontent[k[i]].toUtf8());
+                               return;
+                       }else
+                       //find dynamic match
+                       if(iface.contains(k[i])){
+                               qDebug()<<"returning dynamic content";
+                               WServerReply rp=iface[k[i]]->execute(rq);
+                               sock->write(rp.toWireFormat());
+                               return;
+                       }
+               }
+       //no match, send a 404
+       qDebug()<<"no match found, sending 404";
+       sock->write(QByteArray("Status: 404 File not found\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")+err404.toUtf8());
+}
+void WServer::handleRequest(WServerRequest rq,QIODevice*sock)
+{
+       handleRequestHelper(rq,sock);
+       //flush buffers
+       QTcpSocket *tc=qobject_cast<QTcpSocket*>(sock);
+       if(tc!=0)tc->flush();
+       QLocalSocket*ls=qobject_cast<QLocalSocket*>(sock);
+       if(ls!=0)ls->flush();
+       //close it
+       connect(sock,SIGNAL(disconnected()),sock,SLOT(deleteLater()));
+       sock->close();
+}
+
+void WServer::restrictSourceHosts(const QPair<QHostAddress,int>&addr)
+{
+       sourcehost=addr;
+}
+
+void WServer::setReceiveTimeout(int r)
+{
+       if(r<=0)return;
+       mrecvtimeout=r;
+}
+
+WServerRequest::WServerRequest(){}
+WServerRequest::WServerRequest(const WServerRequest&r)
+{
+       mbody=r.mbody;
+       mhead=r.mhead;
+}
+WServerRequest::WServerRequest(const  QByteArray&b)
+{
+       QList<QByteArray>ba=b.split((char)0);
+       for(int i=1;i<ba.size();i+=2)
+               mhead.insert(ba[i-1],ba[i]);
+}
+
+WServerRequest& WServerRequest::operator=(const WServerRequest&r)
+{
+       mbody=r.mbody;
+       mhead=r.mhead;
+       return *this;
+}
+
+static inline QString headerVar(QString h)
+{
+       return h.toUpper().replace("-","_");
+}
+
+bool WServerRequest::hasCgiHeader(const QString&h)const
+{
+       return mhead.contains(headerVar(h).toAscii());
+}
+
+bool WServerRequest::hasHeader(const QString&h)const
+{
+       return hasCgiHeader("HTTP_"+h);
+}
+
+QByteArray WServerRequest::header(const QString&h)const
+{
+       return cgiHeader("HTTP_"+h);
+}
+
+QByteArray WServerRequest::cgiHeader(const QString&h)const
+{
+       return mhead.value(headerVar(h).toAscii());
+}
+QString WServerRequest::pathInfo()const
+{
+       QString p=QString::fromAscii(cgiHeader("PATH_INFO"));
+       if(p=="")return "/";
+       else return p;
+}
+QByteArray WServerRequest::bodyData()const
+{
+       return mbody;
+}
+
+void WServerRequest::setBody(const QByteArray&b){mbody=b;}
+
+bool WServerRequest::isValid()const
+{
+       return hasCgiHeader("SCGI");
+}
+
+QByteArray WServerRequest::dump()const
+{
+       QByteArray ret,hn;
+       foreach(hn,mhead.keys()){
+               ret+=hn+": "+mhead[hn]+"\r\n";
+       }
+       ret+="\r\n"+mbody;
+       return ret;
+}
+
+WServerReply::WServerReply()
+{
+       mStatCode=500;
+       mStatStr="Internal Error";
+}
+WServerReply::WServerReply(const WServerReply&r)
+{
+       operator=(r);
+}
+WServerReply& WServerReply::operator=(const WServerReply&r)
+{
+       mStatCode=r.mStatCode;
+       mStatStr=r.mStatStr;
+       mBody=r.mBody;
+       mHead=r.mHead;
+       return *this;
+}
+bool WServerReply::setStatus(short code,QString str)
+{
+       //check the return code
+       if(code<100 || code>=1000)return false;
+       //check the string
+       if(str.contains('\r')||str.contains('\n'))return false;
+       if(str.size()>500)return false;
+       //assign
+       mStatCode=code;
+       mStatStr=str.toAscii();
+       return true;
+}
+
+static QRegExp headerreg("[A-Za-z0-9-_]+");
+
+bool WServerReply::setHeader(const QString&h,const QString&c)
+{
+       //check header
+       if(!headerreg.exactMatch(h))return false;
+       //check content
+       if(c.contains('\r')||c.contains('\n'))return false;
+       //check length
+       if((h.size()+c.size()+2)>1024)return false;
+       //check for reserved headers
+       QString hd=h.toUpper().replace("-","_");
+       if(hd=="STATUS" || hd=="CONTENT_LENGTH")return false;
+       //set
+       mHead.insert(hd,QPair<QString,QString>(h,c));
+       return true;
+}
+void WServerReply::setBody(const QString&b)
+{
+       setBody(b.toUtf8());
+}
+void WServerReply::setBody(const QByteArray&b)
+{
+       mBody=b;
+       mHead.insert("CONTENT_LENGTH", QPair<QString,QString>("Content-Length", QByteArray::number(b.size())));
+}
+
+QByteArray WServerReply::toWireFormat()const
+{
+       QByteArray ret;
+       //top header
+       ret="Status: "+QByteArray::number(mStatCode)+" "+mStatStr+"\r\n";
+       QStringList hl=mHead.keys();
+       for(int i=0;i<hl.size();i++)
+               ret+=mHead[hl[i]].first.toAscii()+": "+mHead[hl[i]].second.toAscii()+"\r\n";
+       //body
+       ret+="\r\n"+mBody;
+       //return it
+       return ret;
+}
+
+WServerReceiver::WServerReceiver(WServer*s,QIODevice*d)
+       :QObject(s)
+{
+       //init
+       server=s;
+       sock=d;
+       mode=StartMode;
+       //set up for reading/closing
+       connect(d,SIGNAL(readyRead()),this,SLOT(receiveMore()));
+       connect(d,SIGNAL(disconnected()),this,SLOT(deleteLater()));
+       if(qobject_cast<QLocalSocket*>(d)!=0)
+               connect(d,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(deleteLater()));
+       else
+               connect(d,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(deleteLater()));
+       //set up time out
+       QTimer::singleShot(s->receiveTimeout(),this,SLOT(deleteLater()));
+       //set up for handling
+       connect(this,SIGNAL(readyForProcess(WServerRequest,QIODevice*)), s,SLOT(handleRequest(WServerRequest,QIODevice*)), Qt::QueuedConnection);
+       //read initial data
+       receiveMore();
+}
+
+WServerReceiver::~WServerReceiver()
+{
+       if(sock!=0)sock->deleteLater();
+}
+
+void WServerReceiver::receiveMore()
+{
+       //read
+       qint64 ba=sock->bytesAvailable();
+       if(ba<=0)return;
+       qDebug()<<"reading next"<<ba<<"bytes";
+       buffer+=sock->read(ba);
+       //interpret
+       if(mode==StartMode){
+               if(buffer.contains(':')){
+                       int p=buffer.indexOf(':');
+                       explen=buffer.left(p).toInt();
+                       buffer=buffer.mid(p+1);
+                       mode=HeaderMode;
+                       qDebug()<<"proceeding to header of size"<<explen;
+               }
+       }
+       if(mode==HeaderMode){
+               if(buffer.size()>=explen){
+                       QByteArray head=buffer.left(explen);
+                       buffer=buffer.mid(explen);
+                       request=WServerRequest(head);
+                       if(request.hasCgiHeader("Content-Length"))
+                               explen=request.cgiHeader("Content-Length").toInt();
+                       else
+                               explen=0;
+                       if(explen>0)
+                               mode=BodyMode;
+                       else
+                               mode=Completed;
+                       qDebug()<<"header complete next is"<<explen;
+               }
+       }
+       if(mode==BodyMode){
+               if(buffer.size()>explen){//this is not an off-by-one, see below
+                       request.setBody(buffer.mid(1,explen));
+                       qDebug()<<"body completed";
+                       mode=Completed;
+               }
+       }
+       //check for completion
+       if(mode==Completed){
+               qDebug()<<"request completed, now handling it";
+               emit readyForProcess(request,sock);
+               sock=0;
+               deleteLater();
+       }
+}
diff --git a/qtbase/src/server_p.h b/qtbase/src/server_p.h
new file mode 100644 (file)
index 0000000..7c8e4ea
--- /dev/null
@@ -0,0 +1,40 @@
+//
+// C++ Interface: wtransaction
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOLF_SERVER_PH
+#define WOLF_SERVER_PH
+
+#include "server.h"
+
+class WServerReceiver:public QObject
+{
+       Q_OBJECT
+       public:
+               WServerReceiver(WServer*,QIODevice*);
+               virtual ~WServerReceiver();
+       public slots:
+               void receiveMore();
+               
+       signals:
+               void readyForProcess(WServerRequest,QIODevice*);
+               
+       private:
+               enum Mode{StartMode,HeaderMode,BodyMode,Completed};
+               Mode mode;
+               int explen;
+               QByteArray head,body,buffer;
+               WServer*server;
+               QIODevice*sock;
+               WServerRequest request;
+};
+
+#endif
index 0c9a10f..d482a73 100644 (file)
@@ -17,6 +17,8 @@
 #include <QCoreApplication>
 #include <QDateTime>
 #include <QDebug>
+#include <QDomDocument>
+#include <QDomElement>
 #include <QEventLoop>
 #include <QNetworkAccessManager>
 #include <QNetworkReply>
@@ -138,6 +140,9 @@ void WTransaction_Private::startQuery(QString hreq,QByteArray data)
 
 }
 
+//actually implemented in child classes
+void WTransaction::endQuery(){}
+//called from child classes
 void WTransaction_Private::endQuery()
 {
        //sanity check
@@ -265,6 +270,16 @@ bool WTransaction::hasError()const{return d->m_stage==Error;}
 QString WTransaction::errorType()const{return d->m_errtype;}
 QString WTransaction::interface()const{return d->m_iface;}
 
+QByteArray WTransaction::encodeError()const
+{
+       QDomDocument doc;
+       //TODO: add namespace
+       QDomElement root=doc.createElement("WobError");
+       root.setAttribute("type",errorType());
+       root.appendChild(doc.createTextNode(errorString()));
+       doc.appendChild(root);
+       return doc.toByteArray();
+}
 
 /*****************************************************************************/
 
index 5903c99..ee6ce31 100644 (file)
@@ -15,13 +15,16 @@ HEADERS += \
        include/object.h \
        include/transaction.h \
        include/transaction_p.h \
-       include/interface.h
+       include/interface.h \
+       include/server.h \
+       src/server_p.h
 
 SOURCES += \
        src/object.cpp \
        src/helper.cpp \
        src/transaction.cpp \
-       src/interface.cpp
+       src/interface.cpp \
+       src/server.cpp
 
 INCLUDEPATH += include src
 DEPENDPATH += include src
\ No newline at end of file
index ecc05bf..1a9f0a1 100644 (file)
@@ -46,7 +46,9 @@ void WocPHPClass::newClass(const WocClass&cls)
        
        ////
        //generate code
-       QString code="/* TRANSLATOR "+cna+" */\nclass "+cna+" extends "+cls.serverBaseClass()+"{\n\n";
+       QString code="/* TRANSLATOR "+cna+" */\nclass "+cna+" extends "+
+               cls.baseClass(m_lang,m_parent->classPrefix())+
+               "{\n\n";
        tf.write(code.toAscii());
        
        //property declaration and constructor
index e9275d3..261bd60 100644 (file)
@@ -59,6 +59,8 @@ class WocPHPOut:public WocOutput
                WocPHPTable*ptable;
                WocPHPTransaction*ptrans;
                
+               ///helper: returns the class prefix
+               QString classPrefix(){return "WO";}
                /**helper: return the PHP-class-name of a WocClass*/
                QString className(const WocClass&c){return "WO"+c.name();}
                /**helper: return the PHP-class-name of a WocClass plus Abstract if it is abstract*/
index eadc11c..b925907 100644 (file)
@@ -20,7 +20,7 @@
 
 static const QByteArray TRANSACTCLASS("class WobTransaction extends WobTransactionBase\n{\n");
 static const QByteArray TRANSACTSTART("  static public function handle(){\n    try{switch(self::getTransactionName()){\n");
-static const QByteArray TRANSACTEND("\tdefault:WobTransactionBase::noSuchTransaction();break;\n    }}catch(WobTransactionError $er){$er->printXml();}\n  }\n");
+static const QByteArray TRANSACTEND("\tdefault:WobTransactionBase::noSuchTransaction();break;\n    }}catch(TransactionError $er){$er->printXml();}\n  }\n");
 
 WocPHPTransaction::WocPHPTransaction(WocPHPOut*p ): QObject(p)
 {
index b50edc7..cc8cd90 100644 (file)
@@ -51,10 +51,25 @@ WocClass::WocClass(const QDomElement&cls)
                return;
        }
        qDebug("Info: parsing class %s",m_name.toAscii().data());
-       m_sbase=cls.attribute("serverbase","WObject");
-       m_cbase=cls.attribute("clientbase","WObject");
+       //scan inheritance
+       if(cls.hasAttribute("base"))
+               m_base.insert("wolf",cls.attribute("base"));
+       QList<QDomElement> nl=elementsByTagName(cls,"Base");
+       for(int i=0;i<nl.size();i++){
+               if(!nl[i].hasAttribute("lang") || !nl[i].hasAttribute("class")){
+                       m_valid=false;
+                       return;
+               }
+               QString lang=nl[i].attribute("lang");
+               QString cls=nl[i].attribute("class");
+               if(!lang.contains("/")){
+                       m_base.insert(lang+"/client",cls);
+                       m_base.insert(lang+"/server",cls);
+               }else
+                       m_base.insert(lang,cls);
+       }
        //scan properties
-       QList<QDomElement> nl=elementsByTagName(cls,"Property");
+       nl=elementsByTagName(cls,"Property");
        for(int i=0;i<nl.size();i++){
                QDomElement el=nl.at(i).toElement();
                if(el.isNull())continue;
@@ -334,3 +349,13 @@ QString WocClass::mapMethod(QString table,QString property,QString lang)const
                }
        return "";
 }
+
+QString WocClass::baseClass(QString lang, QString prefix) const
+{
+       if(m_base.contains("wolf"))
+               return prefix+m_base["wolf"];
+       if(m_base.contains(lang))
+               return m_base[lang];
+       return "WObject";
+}
+
index 6d7b063..89acb42 100644 (file)
@@ -19,6 +19,8 @@
 #include <QPair>
 #include <QStringList>
 
+class QDomElement;
+
 /**helper structure to store enums in classes and tables*/
 struct WocEnum {
        QString name,doc;
@@ -39,10 +41,10 @@ class WocClass
                
                /**returns the class name*/
                QString name()const{return m_name;}
-               /**returns the name of the class it is derived from (on the server side) - default: WObject*/
-               QString serverBaseClass()const{return m_sbase;}
-               /**returns the parent class of the class on client side- default: WObject*/
-               QString clientBaseClass()const{return m_cbase;}
+               /**returns the name of the class it is derived from - default: WObject
+               \param lang the language for which to return the base class
+               \param prefix the class prefix in the target language */
+               QString baseClass(QString lang,QString prefix)const;
                
                /**returns true of it has a property with this name*/
                bool hasProperty(QString)const;
@@ -112,9 +114,10 @@ class WocClass
                //cabstract: conditional abstract - just for one or two languages; abstract overrides cabstract!
                bool m_valid,m_abstract;
                QStringList m_cabstract;
-               //name: class name
-               //base: name of parent class (s=server, c=client)
-               QString m_name,m_sbase,m_cbase;
+               //class name without prefix
+               QString m_name;
+               //name of parent class ("wolf" => the "base" attributes' content)
+               QMap<QString,QString>m_base;
                //property info
                struct s_prop{
                        QString name,type;
index 6486a34..2eba1dd 100644 (file)
@@ -96,10 +96,11 @@ WocTransaction::WocTransaction(const QDomElement&root)
                        return;
                }
                QString mt=el.attribute("method");
-               m_call.insert(nm,mt);
+               QString inc=el.attribute("include");
+               m_call.insert(nm,QPair<QString,QString>(mt,inc));
                if(!nm.contains("/")){
-                       m_call.insert(nm+"/client",mt);
-                       m_call.insert(nm+"/server",mt);
+                       m_call.insert(nm+"/client",QPair<QString,QString>(mt,inc));
+                       m_call.insert(nm+"/server",QPair<QString,QString>(mt,inc));
                }
        }
        //output
index eeb6035..e4f888f 100644 (file)
@@ -48,7 +48,9 @@ class WocTransaction
                /**returns whether a specific language binding exists for a call*/
                bool hasCall(QString c)const{return m_call.contains(c);}
                /**returns the called function*/
-               QString callFunction(QString c)const{return m_call[c];}
+               QString callFunction(QString c)const{return m_call[c].first;}
+               /**returns the include file of the called function*/
+               QString callInclude(QString c)const{return m_call[c].second.trimmed();}
                
                /**authentication mode*/
                enum AuthMode {
@@ -112,7 +114,7 @@ class WocTransaction
                QString m_name;
                bool m_valid,m_update;
                AuthMode m_mode;
-               QMap<QString,QString> m_call;
+               QMap<QString,QPair<QString,QString> > m_call;
                QList<QPair<QString,QString> >m_input,m_output;
                QStringList m_privileges;
                //docu
index 42a07a8..a1de708 100644 (file)
@@ -54,14 +54,14 @@ void WocQtClass::newClass(const WocClass&cls)
        QString hcd;
        QString scd;
        //includes
-       hcd="#include \""+cls.clientBaseClass()+"\"\n#include <QCoreApplication>\n\n";
+       hcd="#include \""+cls.baseClass(m_lang,m_prefix)+"\"\n#include <QCoreApplication>\n\n";
        QStringList k=cls.propertyNames();
        for(int i=0;i<k.size();i++)
                if(cls.propertyIsObject(k[i]))
                        hcd+="#include \""+m_prefix+"O"+cls.propertyPlainType(k[i])+"\"\n";
 
        //class declaration
-       hcd+="class "+cna+":public "+cls.clientBaseClass()+"\n{\n  Q_OBJECT\n";
+       hcd+="class "+cna+":public "+cls.baseClass(m_lang,m_prefix)+"\n{\n  Q_OBJECT\n";
        hdr.write(hcd.toAscii());
        
        //enums
@@ -101,6 +101,7 @@ void WocQtClass::newClass(const WocClass&cls)
 void WocQtClass::classScripting(const WocClass& cls, MFile& hdr, MFile& src, QString cna,QString cn)
 {
        Q_UNUSED(cna);Q_UNUSED(hdr);Q_UNUSED(cls);
+       if(!m_parent->doGenerateScripting())return;
        //converters
        QString hcd,scd;
        if(cna==cn){
index c053184..75c0582 100644 (file)
@@ -89,7 +89,6 @@ WocQtOut::WocQtOut(QDomElement&el)
                return;
        }
        m_hdr.write(QByteArray(HDRSTART).replace("%",m_prefix.toAscii()+"INCLUDE_H"));
-       addFile(ifaceClassName().toAscii());
 }
 
 WocQtOut::~WocQtOut(){}
@@ -106,6 +105,8 @@ void WocQtOut::finalize()
                m_scriptcode+="}\n\n";
                m_ifacecpp.write(m_scriptcode.toAscii());
        }
+       //add interface class
+       addFile(ifaceClassName().toAscii());
        //finish sources
        m_ifacecpp.write(SRCEND);
        m_ifacecpp.close();
index 3051cb2..5894893 100644 (file)
@@ -20,5 +20,477 @@ WocQtServerTransaction::WocQtServerTransaction(WocQtOut*p)
 {
 }
 WocQtServerTransaction::~WocQtServerTransaction(){}
-void WocQtServerTransaction::finalize(){}
-void WocQtServerTransaction::newTransaction(const WocTransaction&){}
+
+void WocQtServerTransaction::finalize()
+{
+       trnList();
+}
+
+
+
+struct QtSTrans{
+       QString cn;//class name
+       QString cnp;//private class name
+       QString defparm;//default interface name
+       //code store
+       QString hcd;//header: class decl
+       QString hdi;//header includes
+       QString pcd;//private class decl
+       QString scd;//cpp source
+       QString sri;//cpp includes
+       //in/out params
+       QStringList in,out;
+       //transaction itself
+       const WocTransaction&trn;
+       QtSTrans(const WocTransaction&t,QString cn_,QString cnp_):trn(t){
+               in=trn.inputNames();
+               out=trn.outputNames();
+               cn=cn_;cnp=cnp_;
+       }
+};
+
+void WocQtServerTransaction::newTransaction(const WocTransaction&trn)
+{
+       QString cn=m_prefix+"T"+trn.name();
+       QString cnp=cn+"_Private";
+       addFile(cn);
+       MFile hdr(m_basedir+"/"+m_subdir+"/src"+cn+".h");
+       MFile src(m_basedir+"/"+m_subdir+"/src"+cn+".cpp");
+       if(!hdr.open(QIODevice::WriteOnly|QIODevice::Truncate) ||
+          !src.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create class files for transaction %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //basics
+       QtSTrans ct(trn,cn,cnp);
+       //lead in
+       hdr.write(QByteArray(HDRSTART).replace("%",cn.toAscii()));
+       src.write(QByteArray(SRCSTART).replace("%",cn.toAscii()));
+       
+       //start constructing code
+       
+       //include section
+       genInclude(ct);
+       //start of class
+       ct.hcd+="\nclass "+cnp+";\n";
+       ct.hcd+="\nclass "+cn+":public "+m_transbase+"\n{\n  Q_OBJECT\n";
+       ct.pcd+="\nclass "+cnp+":public WTransaction_PrivateBase\n{\n";
+       
+       //create properties
+       genProperties(ct);
+       
+       //create constructors
+       ct.defparm="=\""+WocProcessor::instance()->projectName()+"\"";
+       genTors(ct);
+       
+       //query method implemented
+       genQuery(ct);
+
+       //create getters
+       genGetters(ct);
+       genSetters(ct);
+       
+       //create scripting
+       genScripting(ct);
+       
+       //button class up
+       ct.hcd+="};\n\n";
+       ct.pcd+="};\n\n";
+       
+       //make meta object...
+       ct.hcd+="Q_DECLARE_METATYPE("+cn+")\n";
+       ct.scd+="static int mymetatypeid=qRegisterMetaType<"+cn+">();\n";
+       
+       //write code
+       hdr.write(ct.hdi.toAscii());
+       hdr.write(ct.hcd.toAscii());
+       src.write(ct.sri.toAscii());
+       src.write(ct.pcd.toAscii());
+       src.write(ct.scd.toAscii());
+       
+       //lead out
+       hdr.write(QByteArray(HDREND).replace("%",cn.toAscii()));
+       src.write(QByteArray(SRCEND).replace("%",cn.toAscii()));
+}
+
+void WocQtServerTransaction::genInclude(QtSTrans&ct)
+{
+       ct.hdi+="#include \""+m_transbase+"\"\n";
+       ct.sri+="#include \"WTransaction_Private\"\n";
+       ct.sri+="#include \""+m_parent->ifaceClassName()+"\"\n";
+       ct.sri+="#include <QCoreApplication>\n\n";
+       for(int i=0;i<ct.in.size();i++){
+               QString tp=qtobjtype(ct.trn,ct.in[i],WocQtOut::In);
+               if(tp!="")ct.hdi+="#include <"+tp+">\n";
+       }
+       for(int i=0;i<ct.out.size();i++){
+               QString tp=qtobjtype(ct.trn,ct.out[i],WocQtOut::Out);
+               if(tp!="")ct.hdi+="#include <"+tp+">\n";
+       }
+}
+
+void WocQtServerTransaction::genProperties(QtSTrans&ct)
+{
+       ct.hcd+="  private:\n\t"+ct.cnp+"*p;\n\tfriend class "+ct.cnp+";\n";
+       ct.pcd+="  protected:\n\tfriend class "+ct.cn+";\n";
+       ct.pcd+="\t"+ct.cnp+"("+ct.cn+"*parent){parent->p=this;}\n";
+       ct.pcd+="\tvoid attach("+ct.cn+"*parent){parent->p=this;WTransaction_PrivateBase::attach();}\n";
+       ct.pcd+="\tvoid detach("+ct.cn+"*parent){parent->p=0;WTransaction_PrivateBase::detach();}\n";
+       for(int i=0;i<ct.in.size();i++){
+               ct.pcd+="\t"+qttype(ct.trn,ct.in[i],WocQtOut::In)+"in_"+ct.in[i]+";\n";
+       }
+       for(int i=0;i<ct.out.size();i++)
+               ct.pcd+="\t"+qttype(ct.trn,ct.out[i],WocQtOut::Out)+"out_"+ct.out[i]+";\n";
+       ct.pcd+="\tWServerRequest request;\n";
+}
+
+void WocQtServerTransaction::genTors(QtSTrans&ct)
+{
+       //define parametric constructor
+       ct.hcd+="  protected:\n";
+       ct.hcd+="\tfriend class "+m_parent->ifaceClassName()+";\n";
+       ct.hcd+="\t"+ct.cn+"(QString,const WServerRequest&);\n";
+       //parametric constructor implementation
+       ct.scd+=ct.cn+"::"+ct.cn+"(QString iface,const WServerRequest&request)\n";
+       ct.scd+="\t:"+m_transbase+"(iface)\n{\n";
+       ct.scd+="\tnew "+ct.cnp+"(this);\n";
+       ct.scd+="\tp->request=request;\n";
+       ct.scd+="\tdecodeData(request.bodyData());\n";
+       ct.scd+="}\n\n";
+       //decl default constructor
+       ct.hcd+="  public:\n";
+       ct.hcd+="\t"+ct.cn+"();\n";
+       ct.scd+=ct.cn+"::"+ct.cn+"()\n{\n\tnew "+ct.cnp+"(this);\n}\n\n";
+       //decl copy constructor
+       ct.hcd+="\t"+ct.cn+"(const "+ct.cn+"&);\n";
+       //copy constructor implementation
+       ct.scd+=ct.cn+"::"+ct.cn+"(const "+ct.cn+"&t)\n\t:"+m_transbase+"(t)\n{\n";
+       ct.scd+="\tt.p->attach(this);\n";
+       ct.scd+="}\n\n";
+       //decl copy operator
+       ct.hcd+="\t"+ct.cn+"& operator=(const "+ct.cn+"&);\n";
+       //copy operator implemented
+       ct.scd+=ct.cn+"& "+ct.cn+"::operator=(const "+ct.cn+"&t)\n{\n";
+       ct.scd+="\t"+m_transbase+"::operator=(t);\n";
+       ct.scd+="\tp->detach(this);t.p->attach(this);\n";
+       ct.scd+="\treturn *this;\n}\n\n";
+       
+       //destructor
+       ct.hcd+="\t~"+ct.cn+"();\n";
+       ct.scd+=ct.cn+"::~"+ct.cn+"()\n{\n\tp->detach(this);\n}\n\n";
+}
+
+void WocQtServerTransaction::genQuery(QtSTrans& ct)
+{
+       //method decl
+       ct.hcd+="  private:\n";
+       ct.hcd+="\tQByteArray encodeData();\n";
+       ct.hcd+="\tvoid decodeData(QByteArray);\n";
+       //encode input
+       ct.scd+="QByteArray "+ct.cn+"::encodeData()\n{\n";
+       ct.scd+=trnOutput(ct.trn);
+       ct.scd+="}\n";
+       //decode output
+       ct.scd+="void "+ct.cn+"::decodeData(QByteArray rba)\n{\n";
+       ct.scd+=trnInput(ct.trn);
+       ct.scd+="}\n";
+       //execution method
+       ct.hdi+="class WServerRequest;class WServerReply;\n";
+       ct.sri+="#include<WServerRequest>\n#include<WServerReply>\n";
+       ct.hcd+="  protected:\n\tvoid executeLocal();\n";
+       ct.hcd+="  public:\n\tWServerReply execute();\n";
+       ct.scd+="WServerReply "+ct.cn+"::execute()\n{\n\texecuteLocal();\n";
+       //encode reply
+       ct.scd+="\tWServerReply reply;\n";
+       ct.scd+="\treply.setStatus(200,\"Ok\");\n";
+       ct.scd+="\tif(d->m_stage!=Error){\n";
+       ct.scd+="\t\treply.setHeader(\"X-WobResponse-Status\",\"Ok\");\n";
+       ct.scd+="\t\treply.setBody(encodeData());\n";
+       ct.scd+="\t}else{\n";
+       ct.scd+="\t\treply.setHeader(\"X-WobResponse-Status\",\"Error\");\n";
+       ct.scd+="\t\treply.setBody(encodeError());\n\t}\n";
+       ct.scd+="\treturn reply;\n}\n";
+       //local part of executor
+       ct.scd+="void "+ct.cn+"::executeLocal()\n{\n";
+       //add auth check
+       if(ct.trn.authMode()!=WocTransaction::Open){
+               ct.scd+="\t"+m_parent->ifaceClassName()+" *iface="+m_parent->ifaceClassName()+"::instance();\n";
+               ct.scd+="\tif(iface==0){\n";
+               ct.scd+="\t\td->m_stage=Error;d->m_errtype=\"_iface\";d->m_errstr=\"Interface not found.\";\n";
+               ct.scd+="\t\treturn;\n\t}\n";
+               ct.scd+="\tif(!iface->isAuthenticated(p->request)){\n";
+               ct.scd+="\t\td->m_stage=Error;d->m_errtype=\"_auth\";d->m_errstr=\"Not authenticated.\";\n";
+               ct.scd+="\t\treturn;\n\t}\n";
+               if(ct.trn.authMode()==WocTransaction::Checked){
+                       ct.scd+="\tif(!iface->hasRight(p->request,"+m_parent->ifaceClassName()+"::R"+ct.trn.name()+")){\n";
+                       ct.scd+="\t\td->m_stage=Error;d->m_errtype=\"_auth\";d->m_errstr=\"User does not have access right.\";\n";
+                       ct.scd+="\t\treturn;\n\t}\n";
+               }
+       }
+       //execute
+       ct.scd+="\t"+ct.trn.callFunction("qt/server")+"\n";
+       QString inc=ct.trn.callInclude("qt/server");
+       if(inc!="")
+               ct.sri+="#include \""+inc+"\"\n";
+       ct.scd+="}\n";
+       //set error method
+       ct.hcd+="\tvoid setError(QString type,QString errorText);\n";
+       ct.scd+="void "+ct.cn+"::setError(QString type,QString errorText)\n{\n";
+       ct.scd+="\td->m_stage=Error;d->m_errtype=type;d->m_errstr=errorText;\n";
+       ct.scd+="}\n";
+}
+
+void WocQtServerTransaction::genGetters(QtSTrans& ct)
+{
+       ct.hcd+="  public:\n";
+       for(int i=0;i<ct.in.size();i++){
+               QString tp=qttype(ct.trn,ct.in[i],WocQtOut::In);
+               ct.hcd+="\tQ_SLOT "+tp+" get"+ct.in[i]+"();\n";
+               ct.scd+=tp+" "+ct.cn+"::get"+ct.in[i]+"(){return p->in_"+ct.in[i]+";}\n";
+       }
+}
+void WocQtServerTransaction::genSetters(QtSTrans& ct)
+{
+       for(int i=0;i<ct.out.size();i++){
+               QString tp=qttype(ct.trn,ct.out[i],WocQtOut::Out);
+               ct.hcd+="\tQ_SLOT void set"+ct.out[i]+"(const "+tp+"&);\n";
+               ct.scd+="void "+ct.cn+"::set"+ct.out[i]+"(const "+tp+"&obj){p->out_"+ct.out[i]+"=obj;}\n";
+       }
+}
+
+void WocQtServerTransaction::genScripting(QtSTrans& ct)
+{
+       if(!m_parent->doGenerateScripting())return;
+       //converters between transaction and script values
+       ct.hdi+="class QScriptValue;\n";
+       ct.hdi+="class QScriptEngine;\n";
+       ct.sri+="#include <QScriptValue>\n";
+       ct.sri+="#include <QScriptEngine>\n";
+       ct.hcd+="\tstatic QScriptValue toScriptValue(QScriptEngine*,const "+ct.cn+"&);\n";
+       ct.hcd+="\tstatic void fromScriptValue(const QScriptValue&,"+ct.cn+"&);\n";
+       ct.scd+="QScriptValue "+ct.cn+"::toScriptValue(QScriptEngine*engine,const "+ct.cn+"&obj)\n{\n";
+       ct.scd+="\treturn engine->newQObject(new "+ct.cn+"(obj),QScriptEngine::ScriptOwnership);\n}\n";
+       ct.scd+="void "+ct.cn+"::fromScriptValue(const QScriptValue&val,"+ct.cn+"&obj)\n{\n";
+       ct.scd+="\t"+ct.cn+"*xobj=qobject_cast<"+ct.cn+"*>(val.toQObject());\n";
+       ct.scd+="\tif(xobj!=0 && &obj!=xobj)obj = *xobj;\n}\n";
+       //add to engine
+       m_parent->addScriptInit(
+        "\t{QScriptValue mo=engine->newQMetaObject(&"+ct.cn+"::staticMetaObject);\n"
+        "\tgval.setProperty(\""+ct.cn+"\",mo);\n"
+        "\tqScriptRegisterMetaType(engine,"+ct.cn+"::toScriptValue,"+ct.cn+"::fromScriptValue);}\n"
+       );
+}
+
+QString WocQtServerTransaction::trnOutput(const WocTransaction&trn)
+{
+       QString code;
+       code+="\tWTransaction::LogWrap log(this,\""+trn.name()+"\");\n";
+       code+="\tQDomDocument doc;QDomElement root=doc.createElement(\"WobResponse-"+trn.name()+"\");\n";
+       code+="\tQDomElement tmp;\n";
+       code+="\t/*start of output encoding*/\n";
+       QStringList sl=trn.outputNames();
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.outputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\troot.setAttribute(\""+sl[i]+"\",p->out_"+sl[i]+".value()";
+                       if(trn.isBoolType(t))
+                               code+="?\"true\":\"false\"";
+                       code+=");\n";
+               }else{
+                       if(trn.isListType(t)){
+                               QString pt=trn.plainType(t);
+                               code+="\tfor(int i=0;i<p->out_"+sl[i]+".size();i++){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttmp=p->out_"+sl[i]+"[i].toXml(doc,\""+sl[i]+"\");\n";
+                               }else{
+                                       code+="\t\ttmp=doc.createElement(\""+sl[i]+"\");\n";
+                                       code+="\t\ttmp.appendChild(doc.createTextNode(";
+                                       if(trn.isIntType(t))
+                                               code+="QString::number(p->out_"+sl[i]+"[i])";
+                                       else
+                                       if(trn.isBoolType(t))
+                                               code+="p->out_"+sl[i]+"[i]?\"true\":\"false\"";
+                                       else
+                                       if(trn.isBlobType(t))
+                                               code+="p->out_"+sl[i]+".toBase64()";
+                                       else
+                                               code+="p->out_"+sl[i]+"[i]";
+                                       code+="));\n";
+                               }
+                               code+="\t\troot.appendChild(tmp);\n";
+                               code+="\t}\n";
+                       }else{
+                               if(trn.isObjectType(t)){
+                                       code+="\troot.appendChild(p->out_"+sl[i]+".value().toXml(doc,\""+sl[i]+"\"));\n";
+                               }else{
+                                       code+="\ttmp=doc.createElement(\""+sl[i]+"\");\n";
+                                       code+="\ttmp.appendChild(doc.createTextNode(";
+                                       if(trn.isIntType(t))
+                                               code+="QString::number(p->out_"+sl[i]+".value())";
+                                       else
+                                       if(trn.isBlobType(t))
+                                               code+="p->out_"+sl[i]+".value().toBase64()";
+                                       else
+                                               code+="p->out_"+sl[i]+".value()";
+                                       code+="));\n\troot.appendChild(tmp);\n";
+                               }
+                       }
+               }
+       }
+       code+="\t/*end of output encoding*/\n";
+       code+="\tdoc.appendChild(root);\n";
+       code+="\treturn doc.toByteArray();\n";
+       return code;
+}
+
+QString WocQtServerTransaction::trnInput(const WocTransaction&trn)
+{
+       QStringList sl=trn.inputNames();
+       QString code;
+       code+="\tWTransaction::LogWrap log(this,\""+trn.name()+"\");\n";
+       code+="\t/*start of input decoding*/\n";
+       //basic XML parsing
+       code+="\tif(rba.isEmpty()){\n";
+       code+="\t\td->m_stage=Error;d->m_errtype=\"_iface\";\n";
+       code+="\t\td->m_errstr=QCoreApplication::translate(\"WobTransaction\",\"XML request parser error: empty request.\");\n";
+       code+="\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n";
+       code+="\tQDomDocument doc;\n";
+       code+="\tQString emsg;int eln,ecl;\n";
+       code+="\tif(!doc.setContent(rba,&emsg,&eln,&ecl)){\n";
+       code+="\t\td->m_stage=Error;d->m_errtype=\"_iface\";\n";
+       code+="\t\td->m_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML request parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n";
+       code+="\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n";
+       code+="\tQDomElement root=doc.documentElement();\n";
+       //parse parameters
+       code+="\tQList<QDomElement> nl;\n";
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.inputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\tp->in_"+sl[i]+"=";
+                       if(trn.isBoolType(t))code+="str2bool(";
+                       code+="root.attribute(\""+sl[i]+"\")";
+                       if(trn.isIntType(t))code+=".toInt()";else
+                       if(trn.isBoolType(t))code+=")";
+                       code+=";\n";
+               }else{
+                       code+="\tnl=elementsByTagName(root,\""+sl[i]+"\");\n";
+                       if(trn.isListType(t)){
+                               code+="\tfor(int i=0;i<nl.size();i++){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttry{p->in_"+sl[i]+".append("+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(i).toElement()));}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n";
+                               }else if(trn.isIntType(t)){
+                                       code+="\t\tp->in_"+sl[i]+".append(nl.at(i).toElement().text().toInt());\n";
+                               }else if(trn.isBoolType(t)){
+                                       code+="\t\tp->in_"+sl[i]+".append(str2bool(nl.at(i).toElement().text()));\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\tp->in_"+sl[i]+".append(QByteArray::fromBase64(nl.at(i).toElement().text().toAscii()));\n";
+                               }else{//can only be string
+                                       code+="\t\tp->in_"+sl[i]+".append(nl.at(i).toElement().text());\n";
+                               }
+                               code+="\t}\n";
+                       }else{
+                               code+="\tif(nl.size()>0){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttry{p->in_"+sl[i]+"="+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(0).toElement());}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\tp->in_"+sl[i]+"=QByteArray::fromBase64(nl.at(0).toElement().text().toAscii());\n";
+                               }else{//can only be string
+                                       code+="\t\tp->in_"+sl[i]+"=nl.at(0).toElement().text();\n";
+                               }
+                               code+="\t}\n";
+                       }
+               }
+       }
+       code+="\t/*end of input*/\n";
+       return code;
+}
+
+void WocQtServerTransaction::trnList()
+{
+       QString code;
+       //header
+       code+="  Q_ENUMS(Right)\n";
+       code+="  enum Right {\n    NoRight";
+       QStringList r=WocProcessor::instance()->transactionNames();
+       QStringList p=WocProcessor::instance()->privilegeNames();
+       QStringList pp=p;
+       for(int i=0;i<r.size();i++)
+               code+=",\n    R"+r[i];
+       for(int i=0;i<p.size();i++)
+               code+=",\n    P"+pp[i].replace(':',"_");
+       code+="\n  };\n";
+       code+="  typedef QList<Right> RightList;\n";
+       code+="  Q_INVOKABLE static QString rightToString(Right);\n";
+       code+="  Q_INVOKABLE static QString rightToLocalString(Right);\n";
+       code+="  Q_INVOKABLE static Right stringToRight(QString);\n";
+       code+="  Q_INVOKABLE static QStringList allKnownRightsString();\n";
+       code+="  Q_INVOKABLE static "+m_parent->ifaceClassName()+"::RightList allKnownRights();\n";
+       code+="  protected: WServerReply execute(const WServerRequest&);\n";
+
+       //implement authenticator
+       code+="public:\n";
+       code+="  ///override to implement authentication checks\n";
+       code+="  virtual bool isAuthenticated(const WServerRequest&){return false;}\n";
+       code+="  ///override to implement the check for access rights\n";
+       code+="  virtual bool hasRight(const WServerRequest&,Right){return false;}\n";
+
+       //write header
+       m_iface.write(code.toAscii());code.clear();
+       
+       //register types
+       code+="static int righttypeid=";
+       code+="qRegisterMetaType<"+m_parent->ifaceClassName()+"::RightList>()+";
+       code+="qRegisterMetaType<QList<"+m_parent->ifaceClassName()+"::RightList> >();\n";
+       m_parent->addPostIface("Q_DECLARE_METATYPE("+m_parent->ifaceClassName()+"::RightList)\n");
+       m_parent->addPostIface("Q_DECLARE_METATYPE(QList<"+ m_parent->ifaceClassName()+ "::RightList>)\n");
+       m_parent->addScriptInit("\tqScriptRegisterSequenceMetaType<QList<RightList> >(engine);\n");
+       
+       //implement rights
+       code+="QString "+m_parent->ifaceClassName()+"::rightToString(Right r)\n{\n\tswitch(r){\n";
+       for(int i=0;i<r.size();i++)
+               code+="\t\tcase R"+r[i]+":return \""+r[i]+"\";\n";
+       for(int i=0;i<p.size();i++)
+               code+="\t\tcase P"+pp[i]+":return \""+p[i]+"\";\n";
+       code+="\t\tdefault:return \"\";\n\t}\n}\n";
+       code+="QString "+m_parent->ifaceClassName()+"::rightToLocalString(Right r)\n{\n\tswitch(r){\n";
+       for(int i=0;i<r.size();i++)
+               code+="\t\tcase R"+r[i]+":return tr(\""+r[i]+"\");\n";
+       for(int i=0;i<p.size();i++)
+               code+="\t\tcase P"+pp[i]+":return tr(\""+p[i]+"\");\n";
+       code+="\t\tdefault:return \"\";\n\t}\n}\n";
+       code+=m_parent->ifaceClassName()+"::Right "+m_prefix+"Interface::stringToRight(QString s)\n{\n";
+       for(int i=0;i<r.size();i++)
+               code+="\tif(s==\""+r[i]+"\")return R"+r[i]+";else\n";
+       for(int i=0;i<p.size();i++)
+               code+="\tif(s==\""+p[i]+"\")return P"+pp[i]+";else\n";
+       code+="\treturn NoRight;\n}\n";
+       code+="QList<"+m_parent->ifaceClassName()+"::Right> "+m_parent->ifaceClassName()+"::allKnownRights()\n{\n";
+       code+="\tQList<Right> ret;ret";
+       for(int i=0;i<r.size();i++)
+               code+="<<R"+r[i];
+       for(int i=0;i<p.size();i++)
+               code+="<<P"+pp[i];
+       code+=";\n\treturn ret;\n}\n";
+       code+="QStringList "+m_parent->ifaceClassName()+"::allKnownRightsString()\n{\n";
+       code+="\tQStringList ret;ret";
+       for(int i=0;i<r.size();i++)
+               code+="<<\""+r[i]+"\"";
+       for(int i=0;i<p.size();i++)
+               code+="<<\""+p[i]+"\"";
+       code+=";\n\treturn ret;\n}\n";
+       
+       //implement handler
+       code+="#include <WServerReply>\n#include <WServerRequest>\n";
+       code+="WServerReply "+m_parent->ifaceClassName()+"::execute(const WServerRequest&request)\n";
+       code+="{\n\tQString rq=request.header(\"X-WobRequest\");\n";
+       for(int i=0;i<r.size();i++)
+               code+="\tif(rq==\""+r[i]+"\")return "+m_prefix+"T"+r[i]+"(name(),request).execute();\n";
+       code+="\tWServerReply rp;rp.setStatus(200,\"Ok\");\n";
+       code+="\trp.setHeader(\"X-WobResponse-Status\",\"Error\");\n";
+       code+="\trp.setBody(QByteArray(\"<WobError type=\\\"_iface\\\">Unknown Transaction</WobError>\"));\n";
+       code+="\treturn rp;\n}\n";
+       
+       //write to interface CPP
+       m_ifacecpp.write(code.toAscii());
+}
index 5bfd58d..c7c2ae1 100644 (file)
@@ -15,6 +15,8 @@
 
 #include "qtout.h"
 
+class QtSTrans;
+
 class WocQtServerTransaction:public WocQtTransaction
 {
        public:
@@ -23,7 +25,26 @@ class WocQtServerTransaction:public WocQtTransaction
                virtual void finalize();
                virtual void newTransaction(const WocTransaction&);
        private:
-               WocQtOut*m_parent;
+               /**helper generates the transaction input encoding*/
+               QString trnInput(const WocTransaction&);
+               /**helper generates the transaction output decoding*/
+               QString trnOutput(const WocTransaction&);
+               /**helper generates enums and strings for all transactions*/
+               void trnList();
+               ///helper: generate include section
+               void genInclude(QtSTrans&);
+               ///helper: generate properties
+               void genProperties(QtSTrans&);
+               ///helper: generate con- and de-structors
+               void genTors(QtSTrans&);
+               ///helper: generate query methods
+               void genQuery(QtSTrans&);
+               ///helper: generate getter methods
+               void genGetters(QtSTrans&);
+               ///helper: generate setter methods
+               void genSetters(QtSTrans&);
+               ///helper: generate QtScript glue code
+               void genScripting(QtSTrans&);
 };
 
 #endif