We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / web / qxtwebcore.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtWeb  module of the Qt eXTension library
6 **
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
9 ** IBM.
10 **
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
15 **
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
20 **
21 ** <http://libqxt.org>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 #include "qxtwebcore.h"
25 #include "qxtabstractwebconnector.h"
26 #include "qxtwebcore_p.h"
27
28 #include <QTimer>
29 #include <QUrl>
30 #include "qxtwebcontroller.h"
31 #include <QCoreApplication>
32 #include <QTcpSocket>
33 #include <QVariant>
34 #include <QtDebug>
35 #include <QUrl>
36 #include <ctime>
37
38 /*!
39         \class QxtWebCore QxtWebCore
40         \ingroup QxtWeb
41         \brief qxtweb application core class. communicates, delegates, does all of the magic ;)
42
43
44         QxtWebCore is the base class of your web application.
45         it listens to the scgi protocoll
46
47         construct one webcore object in the main function of your application.
48         you must contruct it AFTER QCoreApplication and BEFORe any controllers.
49
50         \code
51         int main(int argc,char ** argv)
52                 {
53                 QCoreApplication  app(argc,argv);
54                 QxtWebCore core();
55                 core.listen(8080);
56                 QxtWebController controller("root");
57                 app.exec();
58                 }
59         \endcode
60 */
61
62 /*!
63         \fn static QxtWebCore* instance();
64         singleton accessor
65         \fn static void send(QByteArray);
66         Send data to the client. Use this rarely, but use it always when sending binary data such as images. \n
67         normal text/html comunication should be done using the controllers echo() function \n
68         note that after you called send the first time you cannot modify the header anymore \n
69         sending may be ignored by the transport when there is no client currently handled
70         \fn static QIODevice * socket();
71         direct access to a iodevice for writing binary data. \n
72         You shouldn't use that unless it's absolutly nessesary
73         \fn static QxtError parseString(QByteArray str, post_t & POST);
74         much like phps parse_string \n
75         \fn static QByteArray readContent(int maxsize=5000);
76         reads the content from the current socket if any has sent. \n
77         returns an empty QByteArray on any error.  \n
78         the content is cut at maxsize and not read from the socket.  \n
79         FIXME:\warning: this function is BLOCKING.  while content is read from the client, no other requests can be handled.
80         FIXME:\warning: due to paranoid timeouts this might not work for slow clients
81  */
82
83
84 static QxtWebCore * singleton_m=0;
85
86 //-----------------------interface----------------------------
87 QxtWebCore::QxtWebCore(QxtAbstractWebConnector * pt):QObject()
88 {
89     if (singleton_m)
90         qFatal("you're trying to construct QxtWebCore twice!");
91     qRegisterMetaType<server_t>("server_t");
92     qRegisterMetaTypeStreamOperators<server_t>("server_t");
93
94     singleton_m=this;
95     QXT_INIT_PRIVATE(QxtWebCore);
96     qxt_d().connector=pt;
97     connect(pt,SIGNAL(aboutToClose()),this,SIGNAL(aboutToClose()));
98     connect(pt,SIGNAL(incomming(server_t)),&qxt_d(),SLOT(incomming(server_t)));
99 }
100
101 QxtWebCore::~QxtWebCore()
102 {
103     singleton_m=0;
104 }
105
106
107 void QxtWebCore::send(QString a)
108 {
109     instance()->qxt_d().send(a);
110 }
111 void QxtWebCore::header(QString a,QString b)
112 {
113     instance()->qxt_d().header(a,b);
114 }
115
116 server_t &  QxtWebCore::SERVER()
117 {
118     return instance()->qxt_d().currentservert;
119 }
120
121 QIODevice * QxtWebCore::socket()
122 {
123     return instance()->qxt_d().connector->socket();
124 }
125
126 int QxtWebCore::start (quint16 port ,const QHostAddress & address )
127 {
128     return instance()->qxt_d().connector->start(port,address);
129 }
130
131 void QxtWebCore::redirect(QString location,int code)
132 {
133     instance()->qxt_d().redirect(location,code);
134 }
135
136 QxtWebCore * QxtWebCore::instance()
137 {
138     if (!singleton_m)
139         qFatal("no QxtWebCore constructed");
140     return singleton_m;
141 }
142 void QxtWebCore::setCodec ( QTextCodec * codec )
143 {
144     instance()->qxt_d().decoder=codec->makeDecoder();
145     instance()->qxt_d().encoder=codec->makeEncoder();
146 }
147
148 void QxtWebCore::close()
149 {
150     instance()->qxt_d().close();
151 }
152 void QxtWebCore::sendHeader()
153 {
154     instance()->qxt_d().sendheader();
155
156 }
157
158 //-----------------------implementation----------------------------
159
160
161
162
163 QxtWebCorePrivate::QxtWebCorePrivate(QObject *parent):QObject(parent),QxtPrivate<QxtWebCore>()
164 {
165     connector=0;
166     decoder=0;
167     encoder=0;
168 }
169
170 void QxtWebCorePrivate::send(QString str)
171 {
172     sendheader();
173
174     if (encoder)
175         connector->socket()->write(encoder->fromUnicode (str));
176     else
177         connector->socket()->write(str.toUtf8());
178
179 }
180 void QxtWebCorePrivate::close()
181 {
182     sendheader();
183     connector->close();
184 }
185
186 void QxtWebCorePrivate::sendheader()
187 {
188     if (!header_sent)
189     {
190         header_sent=true;
191         connector->sendHeader(answer);
192     }
193 }
194 void QxtWebCorePrivate::header(QString k,QString v)
195 {
196     if (header_sent)
197         qWarning("headers already sent");
198     if (encoder)
199         answer[encoder->fromUnicode (k)]=encoder->fromUnicode (v);
200     else
201         answer[k.toUtf8()]=v.toUtf8();
202
203 }
204 void QxtWebCorePrivate::redirect(QString l,int code)
205 {
206     QByteArray loc =QUrl(l).toEncoded ();
207
208     if (loc.isEmpty())
209         loc="/";
210     QxtWebCore::header("Status",QString::number(code).toUtf8());
211     QxtWebCore::header("Location",loc);
212     send(QString("<a href=\""+loc+"\">"+loc+"</a>"));
213 }
214
215
216
217
218
219
220 void QxtWebCorePrivate::incomming(server_t  SERVER)
221 {
222     header_sent=false;
223     answer.clear();
224     qDebug("%i, %s -> %s",(int)time(NULL),SERVER["HTTP_HOST"].constData(),SERVER["REQUEST_URI"].constData());
225
226
227     currentservert=SERVER;
228
229     emit(qxt_p().request());
230
231     ///--------------find controller ------------------
232     QByteArray path="404";
233     QList<QByteArray> requestsplit = SERVER["REQUEST_URI"].split('/');
234     if (requestsplit.count()>1)
235     {
236         path=requestsplit.at(1);
237         if (path.trimmed().isEmpty())path="root";
238     }
239     else if (requestsplit.count()>0)
240         path="root";
241
242     ///--------------controller------------------
243
244     QxtWebController * controller =qFindChild<QxtWebController *> (QCoreApplication::instance(), path );
245     if (!controller)
246     {
247         header("Status","404");
248         send("<h1>404 Controller ");
249         send(path);
250         send(" not found</h1>");
251         close();
252         qDebug("404 controller '%s' not found",path.constData());
253         return;
254     }
255
256     int i=controller->invoke(SERVER);
257     if (i!=0 && i!=2)
258     {
259         header("Status","404");
260         send("<h1>");
261         send(QString::number(i));
262         send("</h1>Sorry,, that didn't work as expected. You might want to contact this systems administrator.");
263     }
264     if (i!=2) ///FIXME temporary solution for keepalive
265         close();
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280 //-----------------------helper----------------------------
281
282 QByteArray QxtWebCore::content(int maxsize)
283 {
284     return instance()->qxt_d().connector->content(maxsize);
285 }
286
287
288 QxtError QxtWebCore::parseString(QByteArray content_in, post_t & POST)
289 {
290     QList<QByteArray> posts = content_in.split('&');
291     QByteArray post;
292     foreach(post,posts)
293     {
294         QList<QByteArray> b =post.split('=');
295         if (b.count()!=2)continue;
296         POST[QUrl::fromPercentEncoding  ( b[0].replace("+","%20"))]=QUrl::fromPercentEncoding  ( b[1].replace("+","%20") );
297     }
298     QXT_DROP_OK
299 }
300
301
302
303
304