Commit [ac1652] Maximize Restore History

Merge branch 'port-to-qtnetwork-4.4'

Leon Bottou Leon Bottou 2012-03-04

added src/qdjviewauthdialog.ui
changed src/djview.pro
changed src/djview_cs.ts
changed src/djview_de.ts
changed src/djview_fr.ts
changed src/djview_ru.ts
changed src/djview_uk.ts
changed src/djview_zh_cn.ts
changed src/djview_zh_tw.ts
changed src/qdjview.cpp
changed src/qdjview.h
changed src/qdjviewdialogs.cpp
changed src/qdjviewdialogs.h
changed README
copied src/qdjvuhttp.cpp -> src/qdjvunet.cpp
copied src/qdjvuhttp.h -> src/qdjvunet.h
src/qdjviewauthdialog.ui Diff Switch to side-by-side view
Loading...
src/djview.pro Diff Switch to side-by-side view
Loading...
src/djview_cs.ts Diff Switch to side-by-side view
Loading...
src/djview_de.ts Diff Switch to side-by-side view
Loading...
src/djview_fr.ts Diff Switch to side-by-side view
Loading...
src/djview_ru.ts Diff Switch to side-by-side view
Loading...
src/djview_uk.ts Diff Switch to side-by-side view
Loading...
src/djview_zh_cn.ts Diff Switch to side-by-side view
Loading...
src/djview_zh_tw.ts Diff Switch to side-by-side view
Loading...
src/qdjview.cpp Diff Switch to side-by-side view
Loading...
src/qdjview.h Diff Switch to side-by-side view
Loading...
src/qdjviewdialogs.cpp Diff Switch to side-by-side view
Loading...
src/qdjviewdialogs.h Diff Switch to side-by-side view
Loading...
README Diff Switch to side-by-side view
Loading...
src/qdjvuhttp.cpp to src/qdjvunet.cpp
--- a/src/qdjvuhttp.cpp
+++ b/src/qdjvunet.cpp
@@ -19,268 +19,426 @@
 # include "config.h"
 #endif
 
-#include "qdjvuhttp.h"
+#include "qdjvunet.h"
+#include "qdjviewprefs.h"
 #include <libdjvu/ddjvuapi.h>
 
+#include <QCoreApplication>
 #include <QDebug>
-
-
-/*! \class QDjVuHttpDocument
-  \brief Represents DjVu documents accessible via HTTP.
-  This class is derived from \a QDjVuDocument
-  and reimplements method \a newstream to handle 
-  documents available throught HTTP requests. */
+#include <QList>
+#include <QMap>
+#include <QSet>
+#include <QPointer>
+
+#if QT_VERSION >= 0x40400
+
+#include <QAuthenticator>
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QSslError>
+#include <QSslSocket>
+
+class QDjVuNetDocument::Private : public QObject
+{
+  Q_OBJECT
+
+public:
+  Private(QDjVuNetDocument *q);
+  ~Private();
+
+  QDjVuNetDocument *q;
+  QMap<QNetworkReply*, int> reqid;
+  QMap<QNetworkReply*, bool> reqok;
+  QDjVuContext *ctx;
+  QUrl url;
+  bool cache;
+
+public slots:
+  void readyRead();
+  void finished();
+  void error(QNetworkReply::NetworkError code);
+  void sslErrors(const QList<QSslError>&);
+  void authenticationRequired (QNetworkReply *reply, QAuthenticator *auth);
+  void proxyAuthenticationRequired (const QNetworkProxy &proxy, QAuthenticator *auth);
+
+private:
+  void doRead(QNetworkReply *reply, int streamid);
+  void doError(QNetworkReply *reply, int streamid);
+  bool doAuth(QString why, QAuthenticator *auth);
+};
+
+QDjVuNetDocument::Private::Private(QDjVuNetDocument *q)
+  : QObject(q), 
+    q(q) 
+{
+  QNetworkAccessManager *mgr = manager();
+  connect(mgr, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+          this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)) );
+  connect(mgr, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&,QAuthenticator*)),
+          this, SLOT(proxyAuthenticationRequired(const QNetworkProxy&,QAuthenticator*)) );
+}
+
+QDjVuNetDocument::Private::~Private()
+{
+  QMap<QNetworkReply*,int>::iterator it;
+  for(it = reqid.begin(); it != reqid.end(); ++it)
+    {
+      QNetworkReply *reply = it.key();
+      int streamid = it.value();
+      if (streamid >= 0)
+        ddjvu_stream_close(*q, streamid, true);
+      reply->abort();
+      reply->deleteLater();
+    }
+  reqid.clear();
+  reqok.clear();
+}
 
 void
-QDjVuHttpDocument::init(void)
-{
-  for (int i=0; i<connections.size(); i++)
-    {
-      connections[i].http = new QHttp(this);
-      connections[i].reqid = 0;
-      connections[i].streamid = -1;
-      QHttp *http = connections[i].http;
-      connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
-              this, SLOT(response(const QHttpResponseHeader&)) );
-      connect(http, SIGNAL(readyRead(const QHttpResponseHeader&)), 
-              this, SLOT(read(void)) );
-      connect(http, SIGNAL(requestFinished(int,bool)), 
-              this, SLOT(finished(int,bool)) );
-    }
-}
-
-
-/*! Construct a \a QDjVuHttpDocument object that can perform
-    up to \a nConnections simultaneous HTTP connections. 
-    See \a QDjVuDocument::QDjVuDocument for the explanation
-    of the other two arguments. */
-
-QDjVuHttpDocument::QDjVuHttpDocument(int nConnections, bool autoDelete, 
-                                     QObject *parent)
-  : QDjVuDocument(autoDelete, parent), 
-    connections(nConnections), 
-    ctx(0)
-{
-  init();
-}
-
-
-/*! \overload */
-
-QDjVuHttpDocument::QDjVuHttpDocument(bool autoDelete, QObject *parent)
-  : QDjVuDocument(autoDelete, parent), 
-    connections(2), 
-    ctx(0)
-{
-  init();
-}
-
-
-/*! \overload */
-
-QDjVuHttpDocument::QDjVuHttpDocument(QObject *parent)
-  : QDjVuDocument(parent), 
-    connections(2), 
-    ctx(0)
-{
-  init();
-}
-
-
-
-/*! Destructor.
-  Destroying the http document stops all streams. */
-
-QDjVuHttpDocument::~QDjVuHttpDocument()
-{
-  for (int i=0; i<connections.size(); i++)
-    if (connections[i].streamid >= 0)
-      ddjvu_stream_close(*this, connections[i].streamid, true);
-  foreach(Req req, requests)
-    if (req.streamid >= 0)
-      ddjvu_stream_close(*this, req.streamid, true);
-}
-
-
-
-/*! Specify an HTTP proxy. */
-
-void 
-QDjVuHttpDocument::setProxy(QString host, int port, QString us, QString pw)
-{
-  for (int i=0; i<connections.size(); i++)
-    connections[i].http->setProxy(host,port,us,pw);
-}
-
-
-/*! Associates the \a QDjVuDocument object with
-    with the \a QDjVuContext object \ctx in order
-    to decode the DjVu data located at URL \a url.
-    Only HTTP and FILE urls are recognized. */
-
-bool 
-QDjVuHttpDocument::setUrl(QDjVuContext *ctx, QUrl url, bool cache)
-{
-  bool okay = false;
-  if (url.isValid())
-    {
-      okay = true;
-      QString scheme = url.scheme().toLower();
-      if (scheme == "http")
-        QDjVuDocument::setUrl(ctx, url, cache);
-#if QT_VERSION >= 0x40300
-      else if (scheme == "https")
-        QDjVuDocument::setUrl(ctx, url, cache);
-#endif
-      else if (scheme == "file" && url.host().isEmpty())
-        QDjVuDocument::setFileName(ctx, url.toLocalFile(), cache);
-      else
-        emit error(tr("Unsupported url scheme '%1:'.").arg(scheme), 
-                   __FILE__, __LINE__ );
-      if (! isValid())
-        okay = false;
-    }
-  if (okay)
-    {
-      this->url = url;
-      this->ctx = ctx;
-      this->cache = cache;
-      return true;
-    }
-  qWarning("QDjVuHttpDocument::setUrl: unrecognized url");
-  return false;
-}
-
-
-void 
-QDjVuHttpDocument::newstream(int streamid, QString, QUrl url)
-{
-  Req req;
-  req.streamid = streamid;
-  req.url = url;
-  // Note: this is a stack, not a queue, because
-  // the last request is likely to block viewing
-  // the pages that the user is trying to view now.
-  requests.append(req);
-  schedule();
-}
-
-
-void 
-QDjVuHttpDocument::schedule(void)
-{
-  while (requests.size() > 0)
-    {
-      // search free connection
-      int c;
-      for (c=0; c<connections.size(); c++)
-        if (connections[c].reqid == 0)
-          break;
-      if (c >= connections.size())
-        return;
-      // initiate http transaction
-      Conn& conn = connections[c];
-      Req req = requests.last();
-      requests.removeLast();
-      if (req.url.port() > 0)
-        conn.http->setHost(req.url.host(), req.url.port());
-      else
-        conn.http->setHost(req.url.host());
-      if (! req.url.userName().isEmpty())
-        conn.http->setUser(req.url.userName(), req.url.password());
-      conn.streamid = req.streamid;
-      QString g = req.url.toEncoded(QUrl::RemoveAuthority|QUrl::RemoveScheme);
-      conn.reqid = conn.http->get(g);
-      QString m = tr("Requesting '%1'").arg(req.url.toString());
-      emit info(m);
-    }
-}
-
-
-void
-QDjVuHttpDocument::response(const QHttpResponseHeader &resp)
-{
-  int status = resp.statusCode();
-  for (int c=0; c<connections.size(); c++)
-    if ( connections[c].http == sender() )
-      {
-        Conn& conn = connections[c];
-        // HTTP redirect
-        if (status == 301 || status == 302 || status == 303 || status == 307 )
-          if (resp.hasKey("location"))
+QDjVuNetDocument::Private::doRead(QNetworkReply *reply, int streamid)
+{
+  QByteArray b = reply->readAll();
+  if (streamid >= 0 && b.size() > 0)
+    {
+      if (! reqok.value(reply, false))
+        {
+          int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+          QUrl location = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+          QByteArray type = reply->header(QNetworkRequest::ContentTypeHeader).toByteArray();
+          // check redirection
+          if (location.isValid())
             {
-              QUrl nurl = url.resolved(QUrl(resp.value("location")));
-              // TODO: detect loops
-              if (conn.streamid > 0 || status == 307)
+              reqid[reply] = -1;
+              QUrl nurl = reply->url().resolved(location);
+              if (streamid > 0 || status == 307)
                 { 
-                  newstream(conn.streamid, QString(), nurl);
-                  conn.streamid = -1;
+                  q->newstream(streamid, QString(), nurl);
                   return;
                 }
               // Permanent redirect on main stream changes the base url.
-              ddjvu_stream_close(*this, conn.streamid, false);
-              conn.streamid = -1;
-              setUrl(ctx, nurl, cache);
-              if (isValid())
+              ddjvu_stream_close(*q, streamid, false);
+              q->setUrl(ctx, nurl, cache);
+              if (q->isValid())
                 return;
             }
-        // HTTP and Content-Type errors
-        QString msg;
-        QString type = resp.contentType();
-        if (type.startsWith("text/"))
-          {
-            msg = tr("Received %1 data while retrieving %2.", 
-                     "%1 is a mime type").arg(type);
-            emit error(msg.arg(url.toString()), __FILE__, __LINE__);
-          }
-        if (status != 200 && status != 203)
-          {
-            msg = tr("Received http status %1 while retrieving %2.",
-                     "%1 is an http status code").arg(status);
-            if (conn.streamid >= 0)
-              ddjvu_stream_close(*this, conn.streamid, false);
-            conn.streamid = -1;
-            emit error(msg.arg(url.toString()), __FILE__, __LINE__);
-          }
-        return;
-      }
-}
-
-void 
-QDjVuHttpDocument::read(void)
-{
-  for (int c=0; c<connections.size(); c++)
-    if ( connections[c].http == sender() )
-      {
-        Conn& conn = connections[c];
-        QByteArray b = conn.http->readAll();
-        if (conn.streamid >= 0 && b.size() > 0)
-          ddjvu_stream_write(*this, conn.streamid, b.data(), b.size());
-      }
-}
-
-void 
-QDjVuHttpDocument::finished(int id, bool err)
-{
-  read();
-  for (int c=0; c<connections.size(); c++)
-    if (connections[c].reqid == id)
-      {
-        Conn& conn = connections[c];
-        if (err)
-          {
-            QString msg = tr("%1 while retrieving '%2'.")
-              .arg(conn.http->errorString())
-              .arg(url.toString());
-            emit error(msg , __FILE__, __LINE__);
-          }
-        if (conn.streamid >= 0)
-          ddjvu_stream_close(*this, conn.streamid, false);
-        conn.reqid = 0;
-        conn.streamid = -1;
-      }
-  schedule();
-}
+          // check status code
+          if (status != 200 && status != 203 && status != 0)
+            {
+              QString msg = tr("Received http status %1 while retrieving %2.",
+                               "%1 is an http status code")
+                .arg(status)
+                .arg(reply->url().toString());
+              emit q->error(msg, __FILE__, __LINE__);
+              return;
+            }
+          // check content type
+          if (type.startsWith("text/"))
+            {
+              QString msg = tr("Received <%1> data while retrieving %2.", 
+                               "%1 is a mime type")
+                .arg(QString::fromLatin1(type))
+                .arg(reply->url().toString());
+              emit q->error(msg, __FILE__, __LINE__);
+            }
+          reqok[reply] = true;
+        }
+      // process data
+      ddjvu_stream_write(*q, streamid, b.data(), b.size());
+    }
+}
+
+void
+QDjVuNetDocument::Private::doError(QNetworkReply *reply, int streamid)
+{
+  QNetworkReply::NetworkError code = reply->error();
+  if (streamid >= 0 && code != QNetworkReply::NoError)
+    {
+      QString msg = tr("%1 while retrieving '%2'.")
+        .arg(reply->errorString())
+        .arg(reply->url().toString());
+      emit q->error(msg , __FILE__, __LINE__);
+      ddjvu_stream_close(*q, streamid, false);
+      reqid[reply] = -1;
+    }
+}
+
+void 
+QDjVuNetDocument::Private::readyRead()
+{
+  QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+  if (reply) 
+    {
+      int streamid = reqid.value(reply, -1);
+      if (streamid >= 0)
+        doRead(reply, streamid);
+    }
+}
+ 
+void 
+QDjVuNetDocument::Private::finished()
+{
+  QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+  if (reply)
+    {
+      int streamid = reqid.value(reply, -1);
+      if (streamid >= 0)
+        {
+          if (reply->bytesAvailable() > 0)
+            doRead(reply, streamid);
+          if (reply->error() != QNetworkReply::NoError)
+            doError(reply, streamid);
+          else
+            ddjvu_stream_close(*q, streamid, false);
+        }
+    }
+  reqid.remove(reply);
+  reqok.remove(reply);
+  reply->deleteLater();
+}
+
+void 
+QDjVuNetDocument::Private::error(QNetworkReply::NetworkError)
+{
+  QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+  if (reply) 
+    {
+      int streamid = reqid.value(reply, -1);
+      if (streamid >= 0)
+        doError(reply, streamid);
+    }
+}
+
+void 
+QDjVuNetDocument::Private::sslErrors(const QList<QSslError>&)
+{
+  QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+  if (reply)
+    {
+      static QSet<QString> sslWhiteList;
+      QString host = reply->url().host();
+      bool okay = sslWhiteList.contains(host);
+      if (! okay)
+        {
+          QString why = tr("Cannot validate the certificate for server %1.").arg(host);
+          emit q->sslWhiteList(why, okay);
+          if (okay)
+            sslWhiteList += host;
+        }
+      if (okay)
+        reply->ignoreSslErrors();
+    }
+}
+
+bool
+QDjVuNetDocument::Private::doAuth(QString why, QAuthenticator *auth)
+{
+  QString user = auth->user();
+  QString pass = QString::null;
+  q->emit authRequired(why, user, pass);
+  if (pass.isNull())
+    return false;
+  auth->setUser(user);
+  auth->setPassword(pass);
+  return true;
+}
+
+void
+QDjVuNetDocument::Private::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
+{
+  QString host = reply->url().host();
+  QString why = tr("Authentication required for %1 (%2).").arg(auth->realm()).arg(host);
+  if (! doAuth(why, auth))
+    reply->abort();
+}
+
+void
+QDjVuNetDocument::Private::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
+{
+  QString why = tr("Authentication required for proxy %1.").arg(proxy.hostName());
+  doAuth(why, auth);
+}
+
+
+/*! \class QDjVuNetDocument
+  \brief Represents DjVu documents accessible via the network This class is
+  derived from \a QDjVuDocument and reimplements method \a newstream to handle
+  documents available throught network requests. */
+
+
+QDjVuNetDocument::~QDjVuNetDocument()
+{
+  delete p;
+}
+
+
+/*! Construct a \a QDjVuNetDocument object.
+    See \a QDjVuDocument::QDjVuDocument for the other two arguments. */
+
+QDjVuNetDocument::QDjVuNetDocument(bool autoDelete, QObject *parent)
+  : QDjVuDocument(autoDelete, parent), 
+    p(new QDjVuNetDocument::Private(this))
+{
+}
+
+/*! \overload */
+
+QDjVuNetDocument::QDjVuNetDocument(QObject *parent)
+  : QDjVuDocument(parent), 
+    p(new QDjVuNetDocument::Private(this))
+{
+}
+
+/*! Returns the \a QNetworkAccessManager used to reach the network. */
+
+QNetworkAccessManager* 
+QDjVuNetDocument::manager()
+{
+  static QPointer<QNetworkAccessManager> mgr;
+  QObject *app = QCoreApplication::instance();
+  if (! mgr)
+    mgr = new QNetworkAccessManager(app);
+  return mgr;
+}
+
+/*! Sets the application proxy using the host, port, user, and password
+    specified by url \a proxyUrl.  The proxy type depends on the url scheme.
+    Recognized schemes are \a "http", \a "ftp", and \a "socks5".  */
+
+void 
+QDjVuNetDocument::setProxy(QUrl proxyUrl)
+{
+  QNetworkProxy proxy;
+  QString scheme = proxyUrl.scheme();
+  if (scheme == "http")
+    proxy.setType(QNetworkProxy::HttpCachingProxy);
+  if (scheme == "ftp")
+    proxy.setType(QNetworkProxy::FtpCachingProxy);
+  else if (scheme == "socks5")
+    proxy.setType(QNetworkProxy::Socks5Proxy);
+  proxy.setHostName(proxyUrl.host());
+  proxy.setPort(proxyUrl.port());
+  proxy.setUser(proxyUrl.userName());
+  proxy.setPassword(proxyUrl.password());
+  QNetworkProxy::setApplicationProxy(proxy);
+}
+
+
+/*! Associates the \a QDjVuDocument object with
+    with the \a QDjVuContext object \ctx in order
+    to decode the DjVu data located at URL \a url.  */
+
+bool 
+QDjVuNetDocument::setUrl(QDjVuContext *ctx, QUrl url, bool cache)
+{
+  if (url.isValid())
+    {
+      if (url.scheme() == "file" && url.host().isEmpty())
+        QDjVuDocument::setFileName(ctx, url.toLocalFile(), cache);
+      else
+        QDjVuDocument::setUrl(ctx, url, cache);
+    }
+  if (isValid())
+    {
+      p->url = url;
+      p->ctx = ctx;
+      p->cache = cache;
+      return true;
+    }
+  return false;
+}
+
+
+/* Perform a request. */
+
+void 
+QDjVuNetDocument::newstream(int streamid, QString, QUrl url)
+{
+  QNetworkRequest request(url);
+  QString agent = "Djview/" + QDjViewPrefs::versionString();
+  request.setRawHeader("User-Agent", agent.toAscii());
+  QNetworkReply *reply = manager()->get(request);
+  connect(reply, SIGNAL(readyRead()), 
+          p, SLOT(readyRead()) );
+  connect(reply, SIGNAL(finished()),
+          p, SLOT(finished()) );
+  connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)),
+          p, SLOT(sslErrors(const QList<QSslError>&)) );
+  connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), 
+          p, SLOT(error(QNetworkReply::NetworkError)) );
+  emit info(tr("Requesting '%1'").arg(url.toString()));
+  p->reqid[reply] = streamid;
+  p->reqok[reply] = false;
+}
+
+
+/*! \fn 
+  void QDjVuNetDocument::authRequired(QString why, QString &user, QString &pass)
+  This signal is emitted when a username and password is required.
+  String \a why contains a suitable description of the purpose of the username and password.
+  Simply set \a user and \a pass and the credentials will be remembered.
+ */
+
+
+/*! \fn QDjVuNetDocument::sslWhiteList(QString host, bool &okay)
+  This signal is emitted when there are recoverable errors on a ssl connection
+  such as an inability to validate the certificate. String \a why contains
+  a description of the problem. Setting \a okay to true allows
+  the connection to proceed for the current session. */
+
+
+// ----------------------------------------
+// MOC
+
+#include "qdjvunet.moc"
+
+
+#else // QT_VERSION < 0x40400
+
+// ----------------------------------------
+// STUBS QT < 4.4.0
+
+
+class QDjVuNetDocument::Private : public QObject
+{
+  Q_OBJECT
+public:
+  Private() : QObject() { }
+};
+
+QDjVuNetDocument::~QDjVuNetDocument() 
+{ }
+
+QDjVuNetDocument::QDjVuNetDocument(bool autoDelete, QObject *parent)
+  : QDjVuDocument(autoDelete, parent) 
+{ }
+
+QDjVuNetDocument::QDjVuNetDocument(QObject *parent)
+  : QDjVuDocument(parent) 
+{ }
+
+bool 
+QDjVuNetDocument::setUrl(QDjVuContext *ctx, QUrl url, bool cache)
+{
+  if (url.isValid() && url.scheme() == "file" && url.host().isEmpty())
+    return setFileName(ctx, url.toLocalFile(), cache);
+  return false;
+}
+
+QNetworkAccessManager* 
+QDjVuNetDocument::manager()
+{ return 0; }
+
+void 
+QDjVuNetDocument::setProxy(QUrl)
+{ }
+
+void 
+QDjVuNetDocument::newstream(int, QString, QUrl)
+{ }
+
+#endif
 
 
 
src/qdjvuhttp.h to src/qdjvunet.h
--- a/src/qdjvuhttp.h
+++ b/src/qdjvunet.h
@@ -15,8 +15,8 @@
 //C- GNU General Public License for more details.
 //C-  ------------------------------------------------------------------
 
-#ifndef QDJVUHTTP_H
-#define QDJVUHTTP_H
+#ifndef QDJVUNET_H
+#define QDJVUNET_H
 
 #if AUTOCONF
 # include "config.h"
@@ -24,50 +24,28 @@
 
 #include <qdjvu.h>
 
-#include <QList>
-#include <QVector>
-#include <QHttp>
+class QAuthenticator;
+class QNetworkAccessManager;
 
-
-class QDjVuHttpDocument : public QDjVuDocument
+class QDjVuNetDocument : public QDjVuDocument
 {
   Q_OBJECT
-
 public:
-  ~QDjVuHttpDocument();
-  QDjVuHttpDocument(int nConnections=2, bool autoDelete=false, 
-                    QObject *parent=0);
-  QDjVuHttpDocument(bool autoDelete, QObject *parent=0);
-  QDjVuHttpDocument(QObject *parent);
-  void setProxy(QString host, int port=8080, 
-                QString user="", QString pass="");
+  ~QDjVuNetDocument();
+  QDjVuNetDocument(bool autoDelete=false, QObject *parent=0);
+  QDjVuNetDocument(QObject *parent);
   bool setUrl(QDjVuContext *ctx, QUrl url, bool cache=true);
-  
+  static QNetworkAccessManager* manager();
+  static void setProxy(QUrl proxyurl);
 protected:
   virtual void newstream(int streamid, QString name, QUrl url);
-  
+signals:
+  void authRequired(QString why, QString &user, QString &pass);
+  void sslWhiteList(QString why, bool &okay);
 private:
-  struct Req  { int streamid; QUrl url; };
-  struct Conn { QHttp *http; int reqid; int streamid; };
-  QList<Req>    requests;
-  QVector<Conn> connections;
-  QDjVuContext *ctx;
-  QUrl          url;
-  bool          cache;
-  void schedule(void);
-  void init(void);
-  
-private slots:
-  void response(const QHttpResponseHeader &resp);
-  void read(void);
-  void finished(int id, bool error);
+  class Private;
+  Private *p;
 };
-
-
-
-
-
-
 
 #endif