Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions |

# A simple NNTP implementation

This example shows how to implement your own QNetworkProtocol. The protocol that was chosen for this example is NTTP. Please note that this implementation is very simple since it is designed to be an example. It should not be used as a real NNTP implemention.

Header file (nntp.h):

    /****************************************************************************
    ** $Id: qt/nntp.h   3.3.7   edited Aug 31 2005 $
    **
    ** Copyright (C) 1992-2005 Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #ifndef NNTP_H
    #define NNTP_H

    #include <qsocket.h>
    #include <qnetworkprotocol.h>

    class Nntp : public QNetworkProtocol
    {
        Q_OBJECT

    public:
        Nntp();
        virtual ~Nntp();
        virtual int supportedOperations() const;

    protected:
        virtual void operationListChildren( QNetworkOperation *op );
        virtual void operationGet( QNetworkOperation *op );

        QSocket *commandSocket;
        bool connectionReady;
        bool readGroups;
        bool readArticle;

    private:
        bool checkConnection( QNetworkOperation *op );
        void close();
        void parseGroups();
        void parseArticle();

    protected slots:
        void hostFound();
        void connected();
        void closed();
        void readyRead();
        void error( int );

    };

    #endif

Implementation (nntp.cpp):

    /****************************************************************************
    ** $Id: qt/nntp.cpp   3.3.7   edited Aug 31 2005 $
    **
    ** Copyright (C) 1992-2005 Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #include "nntp.h"
    #include <qurlinfo.h>
    #include <stdlib.h>
    #include <qurloperator.h>
    #include <qstringlist.h>
    #include <qregexp.h>

    Nntp::Nntp()
        : QNetworkProtocol(), connectionReady( FALSE ),
          readGroups( FALSE ), readArticle( FALSE )
    {
        // create the command socket and connect to its signals
        commandSocket = new QSocket( this );
        connect( commandSocket, SIGNAL( hostFound() ),
                 this, SLOT( hostFound() ) );
        connect( commandSocket, SIGNAL( connected() ),
                 this, SLOT( connected() ) );
        connect( commandSocket, SIGNAL( connectionClosed() ),
                 this, SLOT( closed() ) );
        connect( commandSocket, SIGNAL( readyRead() ),
                 this, SLOT( readyRead() ) );
        connect( commandSocket, SIGNAL( error( int ) ),
                 this, SLOT( error( int ) ) );
    }

    Nntp::~Nntp()
    {
        close();
        delete commandSocket;
    }

    void Nntp::operationListChildren( QNetworkOperation * )
    {
        // create a command
        QString path = url()->path(), cmd;
        if ( path.isEmpty() || path == "/" ) {
            // if the path is empty or we are in the root dir,
            // we want to read the list of available newsgroups
            cmd = "list newsgroups\r\n";
        } else if ( url()->isDir() ) {
            // if the path is a directory (in our case a news group)
            // we want to list the articles of this group
            path = path.replace( "/", "" );
            cmd = "listgroup " + path + "\r\n";
        } else
            return;

        // write the command to the socket
        commandSocket->writeBlock( cmd.latin1(), cmd.length() );
        readGroups = TRUE;
    }

    void Nntp::operationGet( QNetworkOperation *op )
    {
        // get the dirPath of the URL (this is our news group)
        // and the filename (which is the article we want to read)
        QUrl u( op->arg( 0 ) );
        QString dirPath = u.dirPath(), file = u.fileName();
        dirPath = dirPath.replace( "/", "" );

        // go to the group in which the article is
        QString cmd;
        cmd = "group " + dirPath + "\r\n";
        commandSocket->writeBlock( cmd.latin1(), cmd.length() );

        // read the head of the article
        cmd = "article " + file + "\r\n";
        commandSocket->writeBlock( cmd.latin1(), cmd.length() );
        readArticle = TRUE;
    }

    bool Nntp::checkConnection( QNetworkOperation * )
    {
        // we are connected, return TRUE
        if ( commandSocket->isOpen() && connectionReady )
            return TRUE;

        // seems that there is no chance to connect
        if ( commandSocket->isOpen() )
            return FALSE;

        // don't call connectToHost() if we are already trying to connect
        if ( commandSocket->state() == QSocket::Connecting )
            return FALSE;

        // start connecting
        connectionReady = FALSE;
        commandSocket->connectToHost( url()->host(),
                                      url()->port() != -1 ? url()->port() : 119 );
        return FALSE;
    }

    void Nntp::close()
    {
        // close the command socket
        if ( commandSocket->isOpen() ) {
            commandSocket->writeBlock( "quit\r\n", strlen( "quit\r\n" ) );
            commandSocket->close();
        }
    }

    int Nntp::supportedOperations() const
    {
        // we only support listing children and getting data
        return OpListChildren | OpGet;
    }

    void Nntp::hostFound()
    {
        if ( url() )
            emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) );
        else
            emit connectionStateChanged( ConHostFound, tr( "Host found" ) );
    }

    void Nntp::connected()
    {
        if ( url() )
            emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) );
        else
            emit connectionStateChanged( ConConnected, tr( "Connected to host" ) );
    }

    void Nntp::closed()
    {
        if ( url() )
            emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) );
        else
            emit connectionStateChanged( ConClosed, tr( "Connection closed" ) );
    }

    void Nntp::readyRead()
    {
        // new data arrived on the command socket

        // of we should read the list of available groups, let's do so
        if ( readGroups ) {
            parseGroups();
            return;
        }

        // of we should read an article, let's do so
        if ( readArticle ) {
            parseArticle();
            return;
        }

        // read the new data from the socket
        QCString s;
        s.resize( commandSocket->bytesAvailable() + 1 );
        commandSocket->readBlock( s.data(), commandSocket->bytesAvailable() );

        if ( !url() )
            return;

        // of the code of the server response was 200, we know that the
        // server is ready to get commands from us now
        if ( s.left( 3 ) == "200" )
            connectionReady = TRUE;
    }

    void Nntp::parseGroups()
    {
        if ( !commandSocket->canReadLine() )
            return;

        // read one line after the other
        while ( commandSocket->canReadLine() ) {
            QString s = commandSocket->readLine();

            // if the  line starts with a dot, all groups or articles have been listed,
            // so we finished processing the listChildren() command
            if ( s[ 0 ] == '.' ) {
                readGroups = FALSE;
                operationInProgress()->setState( StDone );
                emit finished( operationInProgress() );
                return;
            }

            // if the code of the server response is 215 or 211
            // the next line will be the first group or article (depending on what we read).
            // So let others know that we start reading now...
            if ( s.left( 3 ) == "215" || s.left( 3 ) == "211" ) {
                operationInProgress()->setState( StInProgress );
                emit start( operationInProgress() );
                continue;
            }

            // parse the line and create a QUrlInfo object
            // which describes the child (group or article)
            bool tab = s.find( '\t' ) != -1;
            QString group = s.mid( 0, s.find( tab ? '\t' : ' ' ) );
            QUrlInfo inf;
            inf.setName( group );
            QString path = url()->path();
            inf.setDir( path.isEmpty() || path == "/" );
            inf.setSymLink( FALSE );
            inf.setFile( !inf.isDir() );
            inf.setWritable( FALSE );
            inf.setReadable( TRUE );

            // let others know about our new child
            emit newChild( inf, operationInProgress() );
        }

    }

    void Nntp::parseArticle()
    {
        if ( !commandSocket->canReadLine() )
            return;

        // read an article one line after the other
        while ( commandSocket->canReadLine() ) {
            QString s = commandSocket->readLine();

            // if the  line starts with a dot, we finished reading something
            if ( s[ 0 ] == '.' ) {
                readArticle = FALSE;
                operationInProgress()->setState( StDone );
                emit finished( operationInProgress() );
                return;
            }

            if ( s.right( 1 ) == "\n" )
                s.remove( s.length() - 1, 1 );

            // emit the new data of the article which we read
            emit data( QCString( s.ascii() ), operationInProgress() );
        }
    }

    void Nntp::error( int code )
    {
        if ( code == QSocket::ErrHostNotFound ||
             code == QSocket::ErrConnectionRefused ) {
            // this signal is called if connecting to the server failed
            if ( operationInProgress() ) {
                QString msg = tr( "Host not found or couldn't connect to: \n" + url()->host() );
                operationInProgress()->setState( StFailed );
                operationInProgress()->setProtocolDetail( msg );
                operationInProgress()->setErrorCode( (int)ErrHostNotFound );
                clearOperationQueue();
                emit finished( operationInProgress() );
            }
        }
    }

See also Network Examples.