1 | /****************************************************************************
|
---|
2 | ** $Id: nntp.cpp 2 2005-11-16 15:49:26Z dmik $
|
---|
3 | **
|
---|
4 | ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
|
---|
5 | **
|
---|
6 | ** This file is part of an example program for Qt. This example
|
---|
7 | ** program may be used, distributed and modified without limitation.
|
---|
8 | **
|
---|
9 | *****************************************************************************/
|
---|
10 |
|
---|
11 | #include "nntp.h"
|
---|
12 | #include <qurlinfo.h>
|
---|
13 | #include <stdlib.h>
|
---|
14 | #include <qurloperator.h>
|
---|
15 | #include <qstringlist.h>
|
---|
16 | #include <qregexp.h>
|
---|
17 |
|
---|
18 | Nntp::Nntp()
|
---|
19 | : QNetworkProtocol(), connectionReady( FALSE ),
|
---|
20 | readGroups( FALSE ), readArticle( FALSE )
|
---|
21 | {
|
---|
22 | // create the command socket and connect to its signals
|
---|
23 | commandSocket = new QSocket( this );
|
---|
24 | connect( commandSocket, SIGNAL( hostFound() ),
|
---|
25 | this, SLOT( hostFound() ) );
|
---|
26 | connect( commandSocket, SIGNAL( connected() ),
|
---|
27 | this, SLOT( connected() ) );
|
---|
28 | connect( commandSocket, SIGNAL( connectionClosed() ),
|
---|
29 | this, SLOT( closed() ) );
|
---|
30 | connect( commandSocket, SIGNAL( readyRead() ),
|
---|
31 | this, SLOT( readyRead() ) );
|
---|
32 | connect( commandSocket, SIGNAL( error( int ) ),
|
---|
33 | this, SLOT( error( int ) ) );
|
---|
34 | }
|
---|
35 |
|
---|
36 | Nntp::~Nntp()
|
---|
37 | {
|
---|
38 | close();
|
---|
39 | delete commandSocket;
|
---|
40 | }
|
---|
41 |
|
---|
42 | void Nntp::operationListChildren( QNetworkOperation * )
|
---|
43 | {
|
---|
44 | // create a command
|
---|
45 | QString path = url()->path(), cmd;
|
---|
46 | if ( path.isEmpty() || path == "/" ) {
|
---|
47 | // if the path is empty or we are in the root dir,
|
---|
48 | // we want to read the list of available newsgroups
|
---|
49 | cmd = "list newsgroups\r\n";
|
---|
50 | } else if ( url()->isDir() ) {
|
---|
51 | // if the path is a directory (in our case a news group)
|
---|
52 | // we want to list the articles of this group
|
---|
53 | path = path.replace( "/", "" );
|
---|
54 | cmd = "listgroup " + path + "\r\n";
|
---|
55 | } else
|
---|
56 | return;
|
---|
57 |
|
---|
58 | // write the command to the socket
|
---|
59 | commandSocket->writeBlock( cmd.latin1(), cmd.length() );
|
---|
60 | readGroups = TRUE;
|
---|
61 | }
|
---|
62 |
|
---|
63 | void Nntp::operationGet( QNetworkOperation *op )
|
---|
64 | {
|
---|
65 | // get the dirPath of the URL (this is our news group)
|
---|
66 | // and the filename (which is the article we want to read)
|
---|
67 | QUrl u( op->arg( 0 ) );
|
---|
68 | QString dirPath = u.dirPath(), file = u.fileName();
|
---|
69 | dirPath = dirPath.replace( "/", "" );
|
---|
70 |
|
---|
71 | // go to the group in which the article is
|
---|
72 | QString cmd;
|
---|
73 | cmd = "group " + dirPath + "\r\n";
|
---|
74 | commandSocket->writeBlock( cmd.latin1(), cmd.length() );
|
---|
75 |
|
---|
76 | // read the head of the article
|
---|
77 | cmd = "article " + file + "\r\n";
|
---|
78 | commandSocket->writeBlock( cmd.latin1(), cmd.length() );
|
---|
79 | readArticle = TRUE;
|
---|
80 | }
|
---|
81 |
|
---|
82 | bool Nntp::checkConnection( QNetworkOperation * )
|
---|
83 | {
|
---|
84 | // we are connected, return TRUE
|
---|
85 | if ( commandSocket->isOpen() && connectionReady )
|
---|
86 | return TRUE;
|
---|
87 |
|
---|
88 | // seems that there is no chance to connect
|
---|
89 | if ( commandSocket->isOpen() )
|
---|
90 | return FALSE;
|
---|
91 |
|
---|
92 | // don't call connectToHost() if we are already trying to connect
|
---|
93 | if ( commandSocket->state() == QSocket::Connecting )
|
---|
94 | return FALSE;
|
---|
95 |
|
---|
96 | // start connecting
|
---|
97 | connectionReady = FALSE;
|
---|
98 | commandSocket->connectToHost( url()->host(),
|
---|
99 | url()->port() != -1 ? url()->port() : 119 );
|
---|
100 | return FALSE;
|
---|
101 | }
|
---|
102 |
|
---|
103 | void Nntp::close()
|
---|
104 | {
|
---|
105 | // close the command socket
|
---|
106 | if ( commandSocket->isOpen() ) {
|
---|
107 | commandSocket->writeBlock( "quit\r\n", strlen( "quit\r\n" ) );
|
---|
108 | commandSocket->close();
|
---|
109 | }
|
---|
110 | }
|
---|
111 |
|
---|
112 | int Nntp::supportedOperations() const
|
---|
113 | {
|
---|
114 | // we only support listing children and getting data
|
---|
115 | return OpListChildren | OpGet;
|
---|
116 | }
|
---|
117 |
|
---|
118 | void Nntp::hostFound()
|
---|
119 | {
|
---|
120 | if ( url() )
|
---|
121 | emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) );
|
---|
122 | else
|
---|
123 | emit connectionStateChanged( ConHostFound, tr( "Host found" ) );
|
---|
124 | }
|
---|
125 |
|
---|
126 | void Nntp::connected()
|
---|
127 | {
|
---|
128 | if ( url() )
|
---|
129 | emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) );
|
---|
130 | else
|
---|
131 | emit connectionStateChanged( ConConnected, tr( "Connected to host" ) );
|
---|
132 | }
|
---|
133 |
|
---|
134 | void Nntp::closed()
|
---|
135 | {
|
---|
136 | if ( url() )
|
---|
137 | emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) );
|
---|
138 | else
|
---|
139 | emit connectionStateChanged( ConClosed, tr( "Connection closed" ) );
|
---|
140 | }
|
---|
141 |
|
---|
142 | void Nntp::readyRead()
|
---|
143 | {
|
---|
144 | // new data arrived on the command socket
|
---|
145 |
|
---|
146 | // of we should read the list of available groups, let's do so
|
---|
147 | if ( readGroups ) {
|
---|
148 | parseGroups();
|
---|
149 | return;
|
---|
150 | }
|
---|
151 |
|
---|
152 | // of we should read an article, let's do so
|
---|
153 | if ( readArticle ) {
|
---|
154 | parseArticle();
|
---|
155 | return;
|
---|
156 | }
|
---|
157 |
|
---|
158 | // read the new data from the socket
|
---|
159 | QCString s;
|
---|
160 | s.resize( commandSocket->bytesAvailable() );
|
---|
161 | commandSocket->readBlock( s.data(), commandSocket->bytesAvailable() );
|
---|
162 |
|
---|
163 | if ( !url() )
|
---|
164 | return;
|
---|
165 |
|
---|
166 | // of the code of the server response was 200, we know that the
|
---|
167 | // server is ready to get commands from us now
|
---|
168 | if ( s.left( 3 ) == "200" )
|
---|
169 | connectionReady = TRUE;
|
---|
170 | }
|
---|
171 |
|
---|
172 | void Nntp::parseGroups()
|
---|
173 | {
|
---|
174 | if ( !commandSocket->canReadLine() )
|
---|
175 | return;
|
---|
176 |
|
---|
177 | // read one line after the other
|
---|
178 | while ( commandSocket->canReadLine() ) {
|
---|
179 | QString s = commandSocket->readLine();
|
---|
180 |
|
---|
181 | // if the line starts with a dot, all groups or articles have been listed,
|
---|
182 | // so we finished processing the listChildren() command
|
---|
183 | if ( s[ 0 ] == '.' ) {
|
---|
184 | readGroups = FALSE;
|
---|
185 | operationInProgress()->setState( StDone );
|
---|
186 | emit finished( operationInProgress() );
|
---|
187 | return;
|
---|
188 | }
|
---|
189 |
|
---|
190 | // if the code of the server response is 215 or 211
|
---|
191 | // the next line will be the first group or article (depending on what we read).
|
---|
192 | // So let others know that we start reading now...
|
---|
193 | if ( s.left( 3 ) == "215" || s.left( 3 ) == "211" ) {
|
---|
194 | operationInProgress()->setState( StInProgress );
|
---|
195 | emit start( operationInProgress() );
|
---|
196 | continue;
|
---|
197 | }
|
---|
198 |
|
---|
199 | // parse the line and create a QUrlInfo object
|
---|
200 | // which describes the child (group or article)
|
---|
201 | bool tab = s.find( '\t' ) != -1;
|
---|
202 | QString group = s.mid( 0, s.find( tab ? '\t' : ' ' ) );
|
---|
203 | QUrlInfo inf;
|
---|
204 | inf.setName( group );
|
---|
205 | QString path = url()->path();
|
---|
206 | inf.setDir( path.isEmpty() || path == "/" );
|
---|
207 | inf.setSymLink( FALSE );
|
---|
208 | inf.setFile( !inf.isDir() );
|
---|
209 | inf.setWritable( FALSE );
|
---|
210 | inf.setReadable( TRUE );
|
---|
211 |
|
---|
212 | // let others know about our new child
|
---|
213 | emit newChild( inf, operationInProgress() );
|
---|
214 | }
|
---|
215 |
|
---|
216 | }
|
---|
217 |
|
---|
218 | void Nntp::parseArticle()
|
---|
219 | {
|
---|
220 | if ( !commandSocket->canReadLine() )
|
---|
221 | return;
|
---|
222 |
|
---|
223 | // read an article one line after the other
|
---|
224 | while ( commandSocket->canReadLine() ) {
|
---|
225 | QString s = commandSocket->readLine();
|
---|
226 |
|
---|
227 | // if the line starts with a dot, we finished reading something
|
---|
228 | if ( s[ 0 ] == '.' ) {
|
---|
229 | readArticle = FALSE;
|
---|
230 | operationInProgress()->setState( StDone );
|
---|
231 | emit finished( operationInProgress() );
|
---|
232 | return;
|
---|
233 | }
|
---|
234 |
|
---|
235 | if ( s.right( 1 ) == "\n" )
|
---|
236 | s.remove( s.length() - 1, 1 );
|
---|
237 |
|
---|
238 | // emit the new data of the article which we read
|
---|
239 | emit data( QCString( s.ascii() ), operationInProgress() );
|
---|
240 | }
|
---|
241 | }
|
---|
242 |
|
---|
243 | void Nntp::error( int code )
|
---|
244 | {
|
---|
245 | if ( code == QSocket::ErrHostNotFound ||
|
---|
246 | code == QSocket::ErrConnectionRefused ) {
|
---|
247 | // this signal is called if connecting to the server failed
|
---|
248 | if ( operationInProgress() ) {
|
---|
249 | QString msg = tr( "Host not found or couldn't connect to: \n" + url()->host() );
|
---|
250 | operationInProgress()->setState( StFailed );
|
---|
251 | operationInProgress()->setProtocolDetail( msg );
|
---|
252 | operationInProgress()->setErrorCode( (int)ErrHostNotFound );
|
---|
253 | clearOperationQueue();
|
---|
254 | emit finished( operationInProgress() );
|
---|
255 | }
|
---|
256 | }
|
---|
257 | }
|
---|