src/qt3support/network/q3networkprotocol.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qt3support/network/q3networkprotocol.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1209 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3networkprotocol.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "q3localfs.h"
+#include "q3urloperator.h"
+#include "qtimer.h"
+#include "qmap.h"
+#include "q3ptrqueue.h"
+#include "q3valuelist.h"
+#include "qurlinfo.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3NETWORKPROTOCOL_DEBUG
+#define NETWORK_OP_DELAY 1000
+
+extern Q_COMPAT_EXPORT Q3NetworkProtocolDict *q3networkProtocolRegister;
+
+Q3NetworkProtocolDict *q3networkProtocolRegister = 0;
+
+class Q3NetworkProtocolPrivate
+{
+public:
+    Q3NetworkProtocolPrivate( Q3NetworkProtocol *p )
+    {
+	url = 0;
+	opInProgress = 0;
+	opStartTimer = new QTimer( p );
+	removeTimer = new QTimer( p );
+	operationQueue.setAutoDelete( false );
+	autoDelete = false;
+	removeInterval = 10000;
+	oldOps.setAutoDelete( false );
+    }
+
+    ~Q3NetworkProtocolPrivate()
+    {
+	removeTimer->stop();
+	if ( opInProgress ) {
+	    if ( opInProgress == operationQueue.head() )
+		operationQueue.dequeue();
+	    opInProgress->free();
+	}
+	while ( operationQueue.head() ) {
+	    operationQueue.head()->free();
+	    operationQueue.dequeue();
+	}
+	while ( oldOps.first() ) {
+	    oldOps.first()->free();
+	    oldOps.removeFirst();
+	}
+	delete opStartTimer;
+    }
+
+    Q3UrlOperator *url;
+    Q3PtrQueue< Q3NetworkOperation > operationQueue;
+    Q3NetworkOperation *opInProgress;
+    QTimer *opStartTimer, *removeTimer;
+    int removeInterval;
+    bool autoDelete;
+    Q3PtrList< Q3NetworkOperation > oldOps;
+};
+
+/*!
+    \class Q3NetworkProtocol
+    \brief The Q3NetworkProtocol class provides a common API for network protocols.
+
+    \compat
+
+    This is a base class which should be used for network protocols
+    implementations that can then be used in Qt (e.g. in the file
+    dialog) together with the Q3UrlOperator.
+
+    The easiest way to implement a new network protocol is to
+    reimplement the operation*() methods, e.g. operationGet(), etc.
+    Only the supported operations should be reimplemented. To specify
+    which operations are supported, also reimplement
+    supportedOperations() and return an int that is OR'd together
+    using the supported operations from the \l
+    Q3NetworkProtocol::Operation enum.
+
+    When you implement a network protocol this way, it is important to
+    emit the correct signals. Also, always emit the finished() signal
+    when an operation is done (on success \e and on failure). Qt
+    relies on correctly emitted finished() signals.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::newChildren( const Q3ValueList<QUrlInfo> &i, Q3NetworkOperation *op )
+
+    This signal is emitted after listChildren() was called and new
+    children (files) have been read from the list of files. \a i holds
+    the information about the new children. \a op is the pointer to
+    the operation object which contains all the information about the
+    operation, including the state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+
+    When implementing your own network protocol and reading children,
+    you usually don't read one child at once, but rather a list of
+    them. That's why this signal takes a list of QUrlInfo objects. If
+    you prefer to read just one child at a time you can use the
+    convenience signal newChild(), which takes a single QUrlInfo
+    object.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::newChild( const QUrlInfo &i, Q3NetworkOperation *op )
+
+    This signal is emitted if a new child (file) has been read.
+    Q3NetworkProtocol automatically connects it to a slot which creates
+    a list of QUrlInfo objects (with just one QUrlInfo \a i) and emits
+    the newChildren() signal with this list. \a op is the pointer to
+    the operation object which contains all the information about the
+    operation that has finished, including the state, etc.
+
+    This is just a convenience signal useful for implementing your own
+    network protocol. In all other cases connect to the newChildren()
+    signal with its list of QUrlInfo objects.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::finished( Q3NetworkOperation *op )
+
+    This signal is emitted when an operation finishes. This signal is
+    always emitted, for both success and failure. \a op is the pointer
+    to the operation object which contains all the information about
+    the operation, including the state, etc. Check the state and error
+    code of the operation object to determine whether or not the
+    operation was successful.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::start( Q3NetworkOperation *op )
+
+    Some operations (such as listChildren()) emit this signal when
+    they start processing the operation. \a op is the pointer to the
+    operation object which contains all the information about the
+    operation, including the state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::createdDirectory( const QUrlInfo &i, Q3NetworkOperation *op )
+
+    This signal is emitted when mkdir() has been successful and the
+    directory has been created. \a i holds the information about the
+    new directory. \a op is the pointer to the operation object which
+    contains all the information about the operation, including the
+    state, etc. Using op->arg( 0 ), you can get the file name of the
+    new directory.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::removed( Q3NetworkOperation *op )
+
+    This signal is emitted when remove() has been succesiisful and the
+    file has been removed. \a op holds the file name of the removed
+    file in the first argument, accessible with op->arg( 0 ). \a op is
+    the pointer to the operation object which contains all the
+    information about the operation, including the state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::itemChanged( Q3NetworkOperation *op )
+
+    This signal is emitted whenever a file which is a child of this
+    URL has been changed, e.g. by successfully calling rename(). \a op
+    holds the original and the new file names in the first and second
+    arguments, accessible with op->arg( 0 ) and op->arg( 1 )
+    respectively. \a op is the pointer to the operation object which
+    contains all the information about the operation, including the
+    state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::data( const QByteArray &data,
+    Q3NetworkOperation *op )
+
+    This signal is emitted when new \a data has been received after
+    calling get() or put(). \a op holds the name of the file from
+    which data is retrieved or uploaded in its first argument, and the
+    (raw) data in its second argument. You can get them with
+    op->arg( 0 ) and op->rawArg( 1 ). \a op is the pointer to the
+    operation object, which contains all the information about the
+    operation, including the state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator (which is used by the network
+    protocol) emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *op )
+
+    This signal is emitted during the transfer of data (using put() or
+    get()). \a bytesDone is how many bytes of \a bytesTotal have been
+    transferred. \a bytesTotal may be -1, which means that the total
+    number of bytes is not known. \a op is the pointer to the
+    operation object which contains all the information about the
+    operation, including the state, etc.
+
+    When a protocol emits this signal, Q3NetworkProtocol is smart
+    enough to let the Q3UrlOperator, which is used by the network
+    protocol, emit its corresponding signal.
+*/
+
+/*!
+    \fn void Q3NetworkProtocol::connectionStateChanged( int state, const QString &data )
+
+    This signal is emitted whenever the state of the connection of the
+    network protocol is changed. \a state describes the new state,
+    which is one of, \c ConHostFound, \c ConConnected or \c ConClosed.
+    \a data is a message text.
+*/
+
+/*!
+    \enum Q3NetworkProtocol::State
+
+    This enum contains the state that a Q3NetworkOperation can have.
+
+    \value StWaiting  The operation is in the Q3NetworkProtocol's queue
+    waiting to be prcessed.
+
+    \value StInProgress  The operation is being processed.
+
+    \value StDone  The operation has been processed successfully.
+
+    \value StFailed  The operation has been processed but an error occurred.
+
+    \value StStopped  The operation has been processed but has been
+    stopped before it finished, and is waiting to be processed.
+
+*/
+
+/*!
+    \enum Q3NetworkProtocol::Operation
+
+    This enum lists the possible operations that a network protocol
+    can support. supportedOperations() returns an int of these that is
+    OR'd together. Also, the type() of a Q3NetworkOperation is always
+    one of these values.
+
+    \value OpListChildren  List the children of a URL, e.g. of a directory.
+    \value OpMkDir  Create a directory.
+    \value OpRemove  Remove a child (e.g. a file).
+    \value OpRename  Rename a child (e.g. a file).
+    \value OpGet  Get data from a location.
+    \value OpPut  Put data to a location.
+    \omitvalue OpMkdir
+*/
+
+/*!
+    \enum Q3NetworkProtocol::ConnectionState
+
+    When the connection state of a network protocol changes it emits
+    the signal connectionStateChanged(). The first argument is one of
+    the following values:
+
+    \value ConHostFound  Host has been found.
+    \value ConConnected  Connection to the host has been established.
+    \value ConClosed  Connection has been closed.
+*/
+
+/*!
+    \enum Q3NetworkProtocol::Error
+
+    When an operation fails (finishes unsuccessfully), the
+    Q3NetworkOperation of the operation returns an error code which has
+    one of the following values:
+
+    \value NoError  No error occurred.
+
+    \value ErrValid  The URL you are operating on is not valid.
+
+    \value ErrUnknownProtocol  There is no protocol implementation
+    available for the protocol of the URL you are operating on (e.g.
+    if the protocol is http and no http implementation has been
+    registered).
+
+    \value ErrUnsupported  The operation is not supported by the
+    protocol.
+
+    \value ErrParse  The URL could not be parsed correctly.
+
+    \value ErrLoginIncorrect  You needed to login but the username
+    or password is wrong.
+
+    \value ErrHostNotFound  The specified host (in the URL) couldn't
+    be found.
+
+    \value ErrListChildren  An error occurred while listing the
+    children (files).
+
+    \value ErrMkDir  An error occurred when creating a directory.
+
+    \value ErrRemove  An error occurred when removing a child (file).
+
+    \value ErrRename   An error occurred when renaming a child (file).
+
+    \value ErrGet  An error occurred while getting (retrieving) data.
+
+    \value ErrPut  An error occurred while putting (uploading) data.
+
+    \value ErrFileNotExisting  A file which is needed by the operation
+    doesn't exist.
+
+    \value ErrPermissionDenied  Permission for doing the operation has
+    been denied.
+    \omitvalue ErrMkdir
+    \omitvalue ErrListChlidren
+
+    You should also use these error codes when implementing custom
+    network protocols. If this is not possible, you can define your own
+    error codes by using integer values that don't conflict with any
+    of these values.
+*/
+
+/*!
+    Constructor of the network protocol base class. Does some
+    initialization and connecting of signals and slots.
+*/
+
+Q3NetworkProtocol::Q3NetworkProtocol()
+    : QObject()
+{
+    d = new Q3NetworkProtocolPrivate( this );
+
+    connect( d->opStartTimer, SIGNAL(timeout()),
+	     this, SLOT(startOps()) );
+    connect( d->removeTimer, SIGNAL(timeout()),
+	     this, SLOT(removeMe()) );
+
+    if ( url() ) {
+	connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+		 url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+		 url(), SIGNAL(finished(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(start(Q3NetworkOperation*)),
+		 url(), SIGNAL(start(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		 url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		 url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+	connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+		 url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+		 url(), SIGNAL(removed(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+		 url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+		 url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(connectionStateChanged(int,QString)),
+		 url(), SIGNAL(connectionStateChanged(int,QString)) );
+    }
+
+    connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+	     this, SLOT(processNextOperation(Q3NetworkOperation*)) );
+    connect( this, SIGNAL(newChild(QUrlInfo,Q3NetworkOperation*)),
+	     this, SLOT(emitNewChildren(QUrlInfo,Q3NetworkOperation*)) );
+
+}
+
+/*!
+    Destructor.
+*/
+
+Q3NetworkProtocol::~Q3NetworkProtocol()
+{
+    delete d;
+}
+
+/*!
+    Sets the Q3UrlOperator, on which the protocol works, to \a u.
+
+    \sa Q3UrlOperator
+*/
+
+void Q3NetworkProtocol::setUrl( Q3UrlOperator *u )
+{
+    if ( url() ) {
+	disconnect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+		    url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(finished(Q3NetworkOperation*)),
+		    url(), SIGNAL(finished(Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(start(Q3NetworkOperation*)),
+		    url(), SIGNAL(start(Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		    url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		    url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+	disconnect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+		    url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(removed(Q3NetworkOperation*)),
+		    url(), SIGNAL(removed(Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+		    url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+		    url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+	disconnect( this, SIGNAL(connectionStateChanged(int,QString)),
+		    url(), SIGNAL(connectionStateChanged(int,QString)) );
+    }
+
+
+    // ### if autoDelete is true, we should delete the Q3UrlOperator (something
+    // like below; but that is not possible since it would delete this, too).
+    //if ( d->autoDelete && (d->url!=u) ) {
+    //    delete d->url; // destructor deletes the network protocol
+    //}
+    d->url = u;
+
+    if ( url() ) {
+	connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+		 url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+		 url(), SIGNAL(finished(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(start(Q3NetworkOperation*)),
+		 url(), SIGNAL(start(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		 url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+		 url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+	connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+		 url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+		 url(), SIGNAL(removed(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+		 url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+	connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+		 url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+	connect( this, SIGNAL(connectionStateChanged(int,QString)),
+		 url(), SIGNAL(connectionStateChanged(int,QString)) );
+    }
+
+    if ( !d->opInProgress && !d->operationQueue.isEmpty() )
+	d->opStartTimer->start( 0, true );
+}
+
+/*!
+    For processing operations the network protocol base class calls
+    this method quite often. This should be reimplemented by new
+    network protocols. It should return true if the connection is OK
+    (open); otherwise it should return false. If the connection is not
+    open the protocol should open it.
+
+    If the connection can't be opened (e.g. because you already tried
+    but the host couldn't be found), set the state of \a op to
+    Q3NetworkProtocol::StFailed and emit the finished() signal with
+    this Q3NetworkOperation as argument.
+
+    \a op is the operation that needs an open connection.
+*/
+
+bool Q3NetworkProtocol::checkConnection( Q3NetworkOperation * )
+{
+    return true;
+}
+
+/*!
+    Returns an int that is OR'd together using the enum values of
+    \l{Q3NetworkProtocol::Operation}, which describes which operations
+    are supported by the network protocol. Should be reimplemented by
+    new network protocols.
+*/
+
+int Q3NetworkProtocol::supportedOperations() const
+{
+    return 0;
+}
+
+/*!
+    Adds the operation \a op to the operation queue. The operation
+    will be processed as soon as possible. This method returns
+    immediately.
+*/
+
+void Q3NetworkProtocol::addOperation( Q3NetworkOperation *op )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+    qDebug( "Q3NetworkOperation: addOperation: %p %d", op, op->operation() );
+#endif
+    d->operationQueue.enqueue( op );
+    if ( !d->opInProgress )
+	d->opStartTimer->start( 0, true );
+}
+
+/*!
+    Static method to register a network protocol for Qt. For example,
+    if you have an implementation of NNTP (called Nntp) which is
+    derived from Q3NetworkProtocol, call:
+    \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 0
+    after which your implementation is registered for future nntp
+    operations.
+
+    The name of the protocol is given in \a protocol and a pointer to
+    the protocol factory is given in \a protocolFactory.
+*/
+
+void Q3NetworkProtocol::registerNetworkProtocol( const QString &protocol,
+						Q3NetworkProtocolFactoryBase *protocolFactory )
+{
+    if ( !q3networkProtocolRegister ) {
+	q3networkProtocolRegister = new Q3NetworkProtocolDict;
+	Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+    }
+
+    q3networkProtocolRegister->insert( protocol, protocolFactory );
+}
+
+/*!
+    Static method to get a new instance of the network protocol \a
+    protocol. For example, if you need to do some FTP operations, do
+    the following:
+    \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 1
+    This returns a pointer to a new instance of an ftp implementation
+    or null if no protocol for ftp was registered. The ownership of
+    the pointer is transferred to you, so you must delete it if you
+    don't need it anymore.
+
+    Normally you should not work directly with network protocols, so
+    you will not need to call this method yourself. Instead, use
+    Q3UrlOperator, which makes working with network protocols much more
+    convenient.
+
+    \sa Q3UrlOperator
+*/
+
+Q3NetworkProtocol *Q3NetworkProtocol::getNetworkProtocol( const QString &protocol )
+{
+    if ( !q3networkProtocolRegister ) {
+	q3networkProtocolRegister = new Q3NetworkProtocolDict;
+	Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+    }
+
+    if ( protocol.isNull() )
+	return 0;
+
+    Q3NetworkProtocolFactoryBase *factory = q3networkProtocolRegister->find( protocol );
+    if ( factory )
+	return factory->createObject();
+
+    return 0;
+}
+
+/*!
+    Returns true if the only protocol registered is for working on the
+    local filesystem; returns false if other network protocols are
+    also registered.
+*/
+
+bool Q3NetworkProtocol::hasOnlyLocalFileSystem()
+{
+    if ( !q3networkProtocolRegister )
+	return false;
+
+    Q3DictIterator< Q3NetworkProtocolFactoryBase > it( *q3networkProtocolRegister );
+    for ( ; it.current(); ++it )
+	if ( it.currentKey() != QLatin1String("file") )
+	    return false;
+    return true;
+}
+
+/*!
+  \internal
+  Starts processing network operations.
+*/
+
+void Q3NetworkProtocol::startOps()
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+    qDebug( "Q3NetworkOperation: start processing operations" );
+#endif
+    processNextOperation( 0 );
+}
+
+/*!
+  \internal
+  Processes the operation \a op. It calls the
+  corresponding operation[something]( Q3NetworkOperation * )
+  methods.
+*/
+
+void Q3NetworkProtocol::processOperation( Q3NetworkOperation *op )
+{
+    if ( !op )
+	return;
+
+    switch ( op->operation() ) {
+    case OpListChildren:
+	operationListChildren( op );
+	break;
+    case OpMkDir:
+	operationMkDir( op );
+	break;
+    case OpRemove:
+	operationRemove( op );
+	break;
+    case OpRename:
+	operationRename( op );
+	break;
+    case OpGet:
+	operationGet( op );
+	break;
+    case OpPut:
+	operationPut( op );
+	break;
+    }
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports listing children (files);
+    this method should then process this Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationListChildren( Q3NetworkOperation * )
+{
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports making directories; this
+    method should then process this Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationMkDir( Q3NetworkOperation * )
+{
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports removing children (files);
+    this method should then process this Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationRemove( Q3NetworkOperation * )
+{
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports renaming children (files);
+    this method should then process this Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationRename( Q3NetworkOperation * )
+{
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports getting data; this method
+    should then process the Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationGet( Q3NetworkOperation * )
+{
+}
+
+/*!
+    When implementing a new network protocol, this method should be
+    reimplemented if the protocol supports putting (uploading) data;
+    this method should then process the Q3NetworkOperation.
+
+    \a op is the pointer to the operation object which contains all
+    the information on the operation that has finished, including the
+    state, etc.
+*/
+
+void Q3NetworkProtocol::operationPut( Q3NetworkOperation * )
+{
+}
+
+/*! \internal
+*/
+
+void Q3NetworkProtocol::operationPutChunk( Q3NetworkOperation * )
+{
+}
+
+/*!
+  \internal
+  Handles operations. Deletes the previous operation object and
+  tries to process the next operation. It also checks the connection state
+  and only processes the next operation, if the connection of the protocol
+  is open. Otherwise it waits until the protocol opens the connection.
+*/
+
+void Q3NetworkProtocol::processNextOperation( Q3NetworkOperation *old )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+    qDebug( "Q3NetworkOperation: process next operation, old: %p", old );
+#endif
+    d->removeTimer->stop();
+
+    if ( old )
+	d->oldOps.append( old );
+    if ( d->opInProgress && d->opInProgress!=old )
+	d->oldOps.append( d->opInProgress );
+
+    if ( d->operationQueue.isEmpty() ) {
+	d->opInProgress = 0;
+	if ( d->autoDelete )
+	    d->removeTimer->start( d->removeInterval, true );
+	return;
+    }
+
+    Q3NetworkOperation *op = d->operationQueue.head();
+
+    d->opInProgress = op;
+
+    if ( !checkConnection( op ) ) {
+	if ( op->state() != Q3NetworkProtocol::StFailed ) {
+	    d->opStartTimer->start( 0, true );
+	} else {
+	    d->operationQueue.dequeue();
+	    clearOperationQueue();
+	    emit finished( op );
+	}
+
+	return;
+    }
+
+    d->opInProgress = op;
+    d->operationQueue.dequeue();
+    processOperation( op );
+}
+
+/*!
+    Returns the Q3UrlOperator on which the protocol works.
+*/
+
+Q3UrlOperator *Q3NetworkProtocol::url() const
+{
+    return d->url;
+}
+
+/*!
+    Returns the operation, which is being processed, or 0 of no
+    operation is being processed at the moment.
+*/
+
+Q3NetworkOperation *Q3NetworkProtocol::operationInProgress() const
+{
+    return d->opInProgress;
+}
+
+/*!
+    Clears the operation queue.
+*/
+
+void Q3NetworkProtocol::clearOperationQueue()
+{
+    d->operationQueue.dequeue();
+    d->operationQueue.setAutoDelete( true );
+    d->operationQueue.clear();
+}
+
+/*!
+    Stops the current operation that is being processed and clears all
+    waiting operations.
+*/
+
+void Q3NetworkProtocol::stop()
+{
+    Q3NetworkOperation *op = d->opInProgress;
+    clearOperationQueue();
+    if ( op ) {
+	op->setState( StStopped );
+	op->setProtocolDetail( tr( "Operation stopped by the user" ) );
+	emit finished( op );
+	setUrl( 0 );
+	op->free();
+    }
+}
+
+/*!
+    Because it's sometimes hard to take care of removing network
+    protocol instances, Q3NetworkProtocol provides an auto-delete
+    mechanism. If you set \a b to true, the network protocol instance
+    is removed after it has been inactive for \a i milliseconds (i.e.
+    \a i milliseconds after the last operation has been processed).
+    If you set \a b to false the auto-delete mechanism is switched
+    off.
+
+    If you switch on auto-delete, the Q3NetworkProtocol also deletes
+    its Q3UrlOperator.
+*/
+
+void Q3NetworkProtocol::setAutoDelete( bool b, int i )
+{
+    d->autoDelete = b;
+    d->removeInterval = i;
+}
+
+/*!
+    Returns true if auto-deleting is enabled; otherwise returns false.
+
+    \sa Q3NetworkProtocol::setAutoDelete()
+*/
+
+bool Q3NetworkProtocol::autoDelete() const
+{
+    return d->autoDelete;
+}
+
+/*!
+  \internal
+*/
+
+void Q3NetworkProtocol::removeMe()
+{
+    if ( d->autoDelete ) {
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+	qDebug( "Q3NetworkOperation:  autodelete of Q3NetworkProtocol %p", this );
+#endif
+	delete d->url; // destructor deletes the network protocol
+    }
+}
+
+void Q3NetworkProtocol::emitNewChildren( const QUrlInfo &i, Q3NetworkOperation *op )
+{
+    Q3ValueList<QUrlInfo> lst;
+    lst << i;
+    emit newChildren( lst, op );
+}
+
+class Q3NetworkOperationPrivate
+{
+public:
+    Q3NetworkProtocol::Operation operation;
+    Q3NetworkProtocol::State state;
+    QMap<int, QString> args;
+    QMap<int, QByteArray> rawArgs;
+    QString protocolDetail;
+    int errorCode;
+    QTimer *deleteTimer;
+};
+
+/*!
+    \class Q3NetworkOperation
+
+    \brief The Q3NetworkOperation class provides common operations for network protocols.
+
+    \compat
+
+    An object is created to describe the operation and the current
+    state for each operation that a network protocol should process.
+
+    \sa Q3NetworkProtocol
+*/
+
+/*!
+    Constructs a network operation object. \a operation is the type of
+    the operation, and \a arg0, \a arg1 and \a arg2 are the first
+    three arguments of the operation. The state is initialized to
+    Q3NetworkProtocol::StWaiting.
+
+    \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+				      const QString &arg0, const QString &arg1,
+				      const QString &arg2 )
+{
+    d = new Q3NetworkOperationPrivate;
+    d->deleteTimer = new QTimer( this );
+    connect( d->deleteTimer, SIGNAL(timeout()),
+	     this, SLOT(deleteMe()) );
+    d->operation = operation;
+    d->state = Q3NetworkProtocol::StWaiting;
+    d->args[ 0 ] = arg0;
+    d->args[ 1 ] = arg1;
+    d->args[ 2 ] = arg2;
+    d->rawArgs[ 0 ] = QByteArray( 0 );
+    d->rawArgs[ 1 ] = QByteArray( 0 );
+    d->rawArgs[ 2 ] = QByteArray( 0 );
+    d->protocolDetail.clear();
+    d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+    Constructs a network operation object. \a operation is the type of
+    the operation, and \a arg0, \a arg1 and \a arg2 are the first
+    three raw data arguments of the operation. The state is
+    initialized to Q3NetworkProtocol::StWaiting.
+
+    \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+				      const QByteArray &arg0, const QByteArray &arg1,
+				      const QByteArray &arg2 )
+{
+    d = new Q3NetworkOperationPrivate;
+    d->deleteTimer = new QTimer( this );
+    connect( d->deleteTimer, SIGNAL(timeout()),
+	     this, SLOT(deleteMe()) );
+    d->operation = operation;
+    d->state = Q3NetworkProtocol::StWaiting;
+    d->args[ 0 ].clear();
+    d->args[ 1 ].clear();
+    d->args[ 2 ].clear();
+    d->rawArgs[ 0 ] = arg0;
+    d->rawArgs[ 1 ] = arg1;
+    d->rawArgs[ 2 ] = arg2;
+    d->protocolDetail.clear();
+    d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+    Destructor.
+*/
+
+Q3NetworkOperation::~Q3NetworkOperation()
+{
+    qDeleteInEventHandler(d->deleteTimer);
+    delete d;
+}
+
+/*!
+    Sets the \a state of the operation object. This should be done by
+    the network protocol during processing; at the end it should be
+    set to Q3NetworkProtocol::StDone or Q3NetworkProtocol::StFailed,
+    depending on success or failure.
+
+    \sa Q3NetworkProtocol::State
+*/
+
+void Q3NetworkOperation::setState( Q3NetworkProtocol::State state )
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    d->state = state;
+}
+
+/*!
+    If the operation failed, the error message can be specified as \a
+    detail.
+*/
+
+void Q3NetworkOperation::setProtocolDetail( const QString &detail )
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    d->protocolDetail = detail;
+}
+
+/*!
+    Sets the error code to \a ec.
+
+    If the operation failed, the protocol should set an error code to
+    describe the error in more detail. If possible, one of the error
+    codes defined in Q3NetworkProtocol should be used.
+
+    \sa setProtocolDetail() Q3NetworkProtocol::Error
+*/
+
+void Q3NetworkOperation::setErrorCode( int ec )
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    d->errorCode = ec;
+}
+
+/*!
+    Sets the network operation's \a{num}-th argument to \a arg.
+*/
+
+void Q3NetworkOperation::setArg( int num, const QString &arg )
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    d->args[ num ] = arg;
+}
+
+/*!
+    Sets the network operation's \a{num}-th raw data argument to \a arg.
+*/
+
+void Q3NetworkOperation::setRawArg( int num, const QByteArray &arg )
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    d->rawArgs[ num ] = arg;
+}
+
+/*!
+    Returns the type of the operation.
+*/
+
+Q3NetworkProtocol::Operation Q3NetworkOperation::operation() const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->operation;
+}
+
+/*!
+    Returns the state of the operation. You can determine whether an
+    operation is still waiting to be processed, is being processed,
+    has been processed successfully, or failed.
+*/
+
+Q3NetworkProtocol::State Q3NetworkOperation::state() const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->state;
+}
+
+/*!
+    Returns the operation's \a{num}-th argument. If this argument was
+    not already set, an empty string is returned.
+*/
+
+QString Q3NetworkOperation::arg( int num ) const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->args[ num ];
+}
+
+/*!
+    Returns the operation's \a{num}-th raw data argument. If this
+    argument was not already set, an empty bytearray is returned.
+*/
+
+QByteArray Q3NetworkOperation::rawArg( int num ) const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->rawArgs[ num ];
+}
+
+/*!
+    Returns a detailed error message for the last error. This must
+    have been set using setProtocolDetail().
+*/
+
+QString Q3NetworkOperation::protocolDetail() const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->protocolDetail;
+}
+
+/*!
+    Returns the error code for the last error that occurred.
+*/
+
+int Q3NetworkOperation::errorCode() const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->errorCode;
+}
+
+/*!
+  \internal
+*/
+
+QByteArray& Q3NetworkOperation::raw( int num ) const
+{
+    if ( d->deleteTimer->isActive() ) {
+	d->deleteTimer->stop();
+	d->deleteTimer->start( NETWORK_OP_DELAY );
+    }
+    return d->rawArgs[ num ];
+}
+
+/*!
+    Sets this object to delete itself when it hasn't been used for one
+    second.
+
+    Because Q3NetworkOperation pointers are passed around a lot the
+    Q3NetworkProtocol generally does not have enough knowledge to
+    delete these at the correct time. If a Q3NetworkProtocol doesn't
+    need an operation any more it will call this function instead.
+
+    Note: you should never need to call the method yourself.
+*/
+
+void Q3NetworkOperation::free()
+{
+    d->deleteTimer->start( NETWORK_OP_DELAY );
+}
+
+/*!
+  \internal
+  Internal slot for auto-deletion.
+*/
+
+void Q3NetworkOperation::deleteMe()
+{
+    delete this;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_q3networkprotocol.cpp"
+
+#endif