00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "nntp.h"
00012
00013 #include <sys/stat.h>
00014 #include <stdlib.h>
00015 #include <stdio.h>
00016
00017 #include <QDir>
00018 #include <QHash>
00019 #include <QRegExp>
00020
00021 #include <kcomponentdata.h>
00022 #include <kdebug.h>
00023 #include <kglobal.h>
00024 #include <klocale.h>
00025
00026 #include <kio/ioslave_defaults.h>
00027
00028 #define DBG_AREA 7114
00029 #define DBG kDebug(DBG_AREA)
00030
00031 #undef ERR
00032 #define ERR kError(DBG_AREA)
00033
00034 using namespace KIO;
00035
00036 extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); }
00037
00038 int kdemain(int argc, char **argv) {
00039
00040 KComponentData componentData("kio_nntp");
00041 if (argc != 4) {
00042 fprintf(stderr, "Usage: kio_nntp protocol domain-socket1 domain-socket2\n");
00043 exit(-1);
00044 }
00045
00046 NNTPProtocol *slave;
00047
00048
00049 if (strcasecmp(argv[1], "nntps") == 0) {
00050 slave = new NNTPProtocol(argv[2], argv[3], true);
00051 } else {
00052 slave = new NNTPProtocol(argv[2], argv[3], false);
00053 }
00054
00055 slave->dispatchLoop();
00056 delete slave;
00057
00058 return 0;
00059 }
00060
00061
00062
00063 NNTPProtocol::NNTPProtocol ( const QByteArray & pool, const QByteArray & app, bool isSSL )
00064 : TCPSlaveBase((isSSL ? "nntps" : "nntp"), pool, app, isSSL ),
00065 isAuthenticated( false )
00066 {
00067 DBG << "=============> NNTPProtocol::NNTPProtocol";
00068
00069 readBufferLen = 0;
00070 m_defaultPort = isSSL ? DEFAULT_NNTPS_PORT : DEFAULT_NNTP_PORT;
00071 m_port = m_defaultPort;
00072 }
00073
00074 NNTPProtocol::~NNTPProtocol() {
00075 DBG << "<============= NNTPProtocol::~NNTPProtocol";
00076
00077
00078 nntp_close();
00079 }
00080
00081 void NNTPProtocol::setHost ( const QString & host, quint16 port, const QString & user,
00082 const QString & pass )
00083 {
00084 DBG << ( ! user.isEmpty() ? (user+'@') : QString(""))
00085 << host << ":" << ( ( port == 0 ) ? m_defaultPort : port );
00086
00087 if ( isConnected() && (mHost != host || m_port != port ||
00088 mUser != user || mPass != pass) )
00089 nntp_close();
00090
00091 mHost = host;
00092 m_port = ( ( port == 0 ) ? m_defaultPort : port );
00093 mUser = user;
00094 mPass = pass;
00095 }
00096
00097 void NNTPProtocol::get( const KUrl& url )
00098 {
00099 DBG << url.prettyUrl();
00100 QString path = QDir::cleanPath(url.path());
00101
00102
00103 if ( path.startsWith( '/' ) )
00104 path.remove( 0, 1 );
00105 int pos = path.indexOf( '/' );
00106 QString group;
00107 QString msg_id;
00108 if ( pos > 0 ) {
00109 group = path.left( pos );
00110 msg_id = path.mid( pos + 1 );
00111 }
00112
00113 if ( group.isEmpty() || msg_id.isEmpty() ) {
00114 error(ERR_DOES_NOT_EXIST,path);
00115 return;
00116 }
00117
00118 int res_code;
00119 DBG << "group:" << group << "msg:" << msg_id;
00120
00121 if ( !nntp_open() )
00122 return;
00123
00124
00125 if ( mCurrentGroup != group && !group.isEmpty() ) {
00126 infoMessage( i18n("Selecting group %1...", group ) );
00127 res_code = sendCommand( "GROUP " + group );
00128 if ( res_code == 411 ){
00129 error( ERR_DOES_NOT_EXIST, path );
00130 mCurrentGroup.clear();
00131 return;
00132 } else if ( res_code != 211 ) {
00133 unexpected_response( res_code, "GROUP" );
00134 mCurrentGroup.clear();
00135 return;
00136 }
00137 mCurrentGroup = group;
00138 }
00139
00140
00141 infoMessage( i18n("Downloading article...") );
00142 res_code = sendCommand( "ARTICLE " + msg_id );
00143 if ( res_code == 423 || res_code == 430 ) {
00144 error( ERR_DOES_NOT_EXIST, path );
00145 return;
00146 } else if (res_code != 220) {
00147 unexpected_response(res_code,"ARTICLE");
00148 return;
00149 }
00150
00151
00152 char tmp[MAX_PACKET_LEN];
00153 while ( true ) {
00154 if ( !waitForResponse( readTimeout() ) ) {
00155 error( ERR_SERVER_TIMEOUT, mHost );
00156 nntp_close();
00157 return;
00158 }
00159 int len = readLine( tmp, MAX_PACKET_LEN );
00160 const char* buffer = tmp;
00161 if ( len <= 0 )
00162 break;
00163 if ( len == 3 && tmp[0] == '.' && tmp[1] == '\r' && tmp[2] == '\n')
00164 break;
00165 if ( len > 1 && tmp[0] == '.' && tmp[1] == '.' ) {
00166 ++buffer;
00167 --len;
00168 }
00169 data( QByteArray::fromRawData( buffer, len ) );
00170 }
00171
00172
00173 data(QByteArray());
00174
00175
00176 finished();
00177 }
00178
00179 void NNTPProtocol::put( const KUrl &, int , KIO::JobFlags )
00180 {
00181 if ( !nntp_open() )
00182 return;
00183 if ( post_article() )
00184 finished();
00185 }
00186
00187 void NNTPProtocol::special(const QByteArray& data) {
00188
00189 int cmd;
00190 QDataStream stream(data);
00191
00192 if ( !nntp_open() )
00193 return;
00194
00195 stream >> cmd;
00196 if (cmd == 1) {
00197 if (post_article()) finished();
00198 } else {
00199 error(ERR_UNSUPPORTED_ACTION,i18n("Invalid special command %1", cmd));
00200 }
00201 }
00202
00203 bool NNTPProtocol::post_article() {
00204 DBG;
00205
00206
00207 infoMessage( i18n("Sending article...") );
00208 int res_code = sendCommand( "POST" );
00209 if (res_code == 440) {
00210 error(ERR_WRITE_ACCESS_DENIED, mHost);
00211 return false;
00212 } else if (res_code != 340) {
00213 unexpected_response(res_code,"POST");
00214 return false;
00215 }
00216
00217
00218 int result;
00219 bool last_chunk_had_line_ending = true;
00220 do {
00221 QByteArray buffer;
00222 dataReq();
00223 result = readData( buffer );
00224 DBG << "receiving data:" << buffer;
00225
00226 if ( result > 0 ) {
00227
00228 int pos = 0;
00229 if ( last_chunk_had_line_ending && buffer[0] == '.' ) {
00230 buffer.insert( 0, '.' );
00231 pos += 2;
00232 }
00233 last_chunk_had_line_ending = ( buffer.endsWith( "\r\n" ) );
00234 while ( (pos = buffer.indexOf( "\r\n.", pos )) > 0) {
00235 buffer.insert( pos + 2, '.' );
00236 pos += 4;
00237 }
00238
00239
00240 write( buffer, buffer.length() );
00241 DBG << "writing:" << buffer;
00242 }
00243 } while ( result > 0 );
00244
00245
00246 if (result<0) {
00247 ERR << "error while getting article data for posting";
00248 nntp_close();
00249 return false;
00250 }
00251
00252
00253 write( "\r\n.\r\n", 5 );
00254
00255
00256 res_code = evalResponse( readBuffer, readBufferLen );
00257 if (res_code == 441) {
00258 error(ERR_COULD_NOT_WRITE, mHost);
00259 return false;
00260 } else if (res_code != 240) {
00261 unexpected_response(res_code,"POST");
00262 return false;
00263 }
00264
00265 return true;
00266 }
00267
00268
00269 void NNTPProtocol::stat( const KUrl& url ) {
00270 DBG << url.prettyUrl();
00271 UDSEntry entry;
00272 QString path = QDir::cleanPath(url.path());
00273 QRegExp regGroup = QRegExp("^\\/?[a-z0-9\\.\\-_]+\\/?$",Qt::CaseInsensitive);
00274 QRegExp regMsgId = QRegExp("^\\/?[a-z0-9\\.\\-_]+\\/<\\S+>$", Qt::CaseInsensitive);
00275 int pos;
00276 QString group;
00277 QString msg_id;
00278
00279
00280 if (path.isEmpty() || path == "/") {
00281 DBG << "root";
00282 fillUDSEntry( entry, QString(), 0, false, ( S_IWUSR | S_IWGRP | S_IWOTH ) );
00283
00284
00285 } else if (regGroup.indexIn(path) == 0) {
00286 if ( path.startsWith( '/' ) ) path.remove(0,1);
00287 if ((pos = path.indexOf('/')) > 0) group = path.left(pos);
00288 else group = path;
00289 DBG << "group:" << group;
00290
00291
00292 fillUDSEntry( entry, group, 0, false, ( S_IWUSR | S_IWGRP | S_IWOTH ) );
00293
00294
00295 } else if (regMsgId.indexIn(path) == 0) {
00296 pos = path.indexOf('<');
00297 group = path.left(pos);
00298 msg_id = KUrl::fromPercentEncoding( path.right(path.length()-pos).toLatin1() );
00299 if ( group.startsWith( '/' ) )
00300 group.remove( 0, 1 );
00301 if ((pos = group.indexOf('/')) > 0) group = group.left(pos);
00302 DBG << "group:" << group << "msg:" << msg_id;
00303 fillUDSEntry( entry, msg_id, 0, true );
00304
00305
00306 } else {
00307 error(ERR_DOES_NOT_EXIST,path);
00308 return;
00309 }
00310
00311 statEntry(entry);
00312 finished();
00313 }
00314
00315 void NNTPProtocol::listDir( const KUrl& url ) {
00316 DBG << url.prettyUrl();
00317 if ( !nntp_open() )
00318 return;
00319
00320 QString path = QDir::cleanPath(url.path());
00321
00322 if (path.isEmpty())
00323 {
00324 KUrl newURL(url);
00325 newURL.setPath("/");
00326 DBG << "redirecting to" << newURL.prettyUrl();
00327 redirection(newURL);
00328 finished();
00329 return;
00330 }
00331 else if ( path == "/" ) {
00332 fetchGroups( url.queryItem( "since" ), url.queryItem( "desc" ) == "true" );
00333 finished();
00334 } else {
00335
00336 int pos;
00337 QString group;
00338 if ( path.startsWith( '/' ) )
00339 path.remove( 0, 1 );
00340 if ((pos = path.indexOf('/')) > 0)
00341 group = path.left(pos);
00342 else
00343 group = path;
00344 QString first = url.queryItem( "first" );
00345 QString max = url.queryItem( "max" );
00346 if ( fetchGroup( group, first.toULong(), max.toULong() ) )
00347 finished();
00348 }
00349 }
00350
00351 void NNTPProtocol::fetchGroups( const QString &since, bool desc )
00352 {
00353 int expected;
00354 int res;
00355 if ( since.isEmpty() ) {
00356
00357 infoMessage( i18n("Downloading group list...") );
00358 res = sendCommand( "LIST" );
00359 expected = 215;
00360 } else {
00361
00362 infoMessage( i18n("Looking for new groups...") );
00363 res = sendCommand( "NEWGROUPS " + since );
00364 expected = 231;
00365 }
00366 if ( res != expected ) {
00367 unexpected_response( res, "LIST" );
00368 return;
00369 }
00370
00371
00372 QByteArray line;
00373 QString group;
00374 int pos, pos2;
00375 long msg_cnt;
00376 long access;
00377 UDSEntry entry;
00378 QHash<QString, UDSEntry> entryMap;
00379
00380
00381 while ( true ) {
00382 if ( ! waitForResponse( readTimeout() ) ) {
00383 error( ERR_SERVER_TIMEOUT, mHost );
00384 nntp_close();
00385 return;
00386 }
00387 readBufferLen = readLine ( readBuffer, MAX_PACKET_LEN );
00388 line = QByteArray( readBuffer, readBufferLen );
00389 if ( line == ".\r\n" )
00390 break;
00391
00392
00393 if ((pos = line.indexOf(' ')) > 0) {
00394
00395 group = line.left(pos);
00396
00397
00398 line.remove(0,pos+1);
00399 long last = 0;
00400 access = 0;
00401 if (((pos = line.indexOf(' ')) > 0 || (pos = line.indexOf('\t')) > 0) &&
00402 ((pos2 = line.indexOf(' ',pos+1)) > 0 || (pos2 = line.indexOf('\t',pos+1)) > 0)) {
00403 last = line.left(pos).toLongLong();
00404 long first = line.mid(pos+1,pos2-pos-1).toLongLong();
00405 msg_cnt = abs(last-first+1);
00406
00407 switch ( line[pos2 + 1] ) {
00408 case 'n': access = 0; break;
00409 case 'm': access = S_IWUSR | S_IWGRP; break;
00410 case 'y': access = S_IWUSR | S_IWGRP | S_IWOTH; break;
00411 }
00412 } else {
00413 msg_cnt = 0;
00414 }
00415
00416 entry.clear();
00417 fillUDSEntry( entry, group, msg_cnt, false, access );
00418 if ( !desc )
00419 listEntry( entry, false );
00420 else
00421 entryMap.insert( group, entry );
00422 }
00423 }
00424
00425
00426 QHash<QString, UDSEntry>::Iterator it = entryMap.begin();
00427 if ( desc ) {
00428 infoMessage( i18n("Downloading group descriptions...") );
00429 totalSize( entryMap.size() );
00430 }
00431 while ( desc ) {
00432
00433 if ( since.isEmpty() )
00434 res = sendCommand( "LIST NEWSGROUPS" );
00435 else {
00436
00437 if ( it == entryMap.end() )
00438 break;
00439 res = sendCommand( "LIST NEWSGROUPS " + it.key() );
00440 ++it;
00441 if( res == 503 ) {
00442
00443 continue;
00444 }
00445 }
00446 if ( res != 215 ) {
00447
00448 break;
00449 }
00450
00451
00452 while ( true ) {
00453 if ( ! waitForResponse( readTimeout() ) ) {
00454 error( ERR_SERVER_TIMEOUT, mHost );
00455 nntp_close();
00456 return;
00457 }
00458 readBufferLen = readLine ( readBuffer, MAX_PACKET_LEN );
00459 line = QByteArray( readBuffer, readBufferLen );
00460 if ( line == ".\r\n" )
00461 break;
00462
00463
00464 int pos = line.indexOf( ' ' );
00465 pos = pos < 0 ? line.indexOf( '\t' ) : qMin( pos, line.indexOf( '\t' ) );
00466 group = line.left( pos );
00467 QString groupDesc = line.right( line.length() - pos ).trimmed();
00468
00469 if ( entryMap.contains( group ) ) {
00470 entry = entryMap.take( group );
00471 entry.insert( KIO::UDSEntry::UDS_EXTRA, groupDesc );
00472 listEntry( entry, false );
00473 }
00474 }
00475
00476 if ( since.isEmpty() )
00477 break;
00478 }
00479
00480 for ( QHash<QString, UDSEntry>::Iterator it = entryMap.begin(); it != entryMap.end(); ++it )
00481 listEntry( it.value(), false );
00482
00483 entry.clear();
00484 listEntry( entry, true );
00485 }
00486
00487 bool NNTPProtocol::fetchGroup( QString &group, unsigned long first, unsigned long max ) {
00488 int res_code;
00489 QString resp_line;
00490
00491
00492 infoMessage( i18n("Selecting group %1...", group ) );
00493 res_code = sendCommand( "GROUP " + group );
00494 if ( res_code == 411 ) {
00495 error( ERR_DOES_NOT_EXIST, group );
00496 mCurrentGroup.clear();
00497 return false;
00498 } else if ( res_code != 211 ) {
00499 unexpected_response( res_code, "GROUP" );
00500 mCurrentGroup.clear();
00501 return false;
00502 }
00503 mCurrentGroup = group;
00504
00505
00506
00507 unsigned long firstSerNum, lastSerNum;
00508 resp_line = QString::fromLatin1( readBuffer );
00509 QRegExp re ( "211\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
00510 if ( re.indexIn( resp_line ) != -1 ) {
00511 firstSerNum = re.cap( 2 ).toLong();
00512 lastSerNum = re.cap( 3 ).toLong();
00513 } else {
00514 error( ERR_INTERNAL, i18n("Could not extract message serial numbers from server response:\n%1",
00515 resp_line ) );
00516 return false;
00517 }
00518
00519 if (firstSerNum == 0)
00520 return true;
00521 first = qMax( first, firstSerNum );
00522 if ( lastSerNum < first ) {
00523
00524
00525 return true;
00526 }
00527 if ( max > 0 && lastSerNum - first > max )
00528 first = lastSerNum - max + 1;
00529
00530 DBG << "Starting from serial number: " << first << " of " << firstSerNum << " - " << lastSerNum;
00531 setMetaData( "FirstSerialNumber", QString::number( firstSerNum ) );
00532 setMetaData( "LastSerialNumber", QString::number( lastSerNum ) );
00533
00534 infoMessage( i18n("Downloading new headers...") );
00535 totalSize( lastSerNum - first );
00536 bool notSupported = true;
00537 if ( fetchGroupXOVER( first, notSupported ) )
00538 return true;
00539 else if ( notSupported )
00540 return fetchGroupRFC977( first );
00541 return false;
00542 }
00543
00544
00545 bool NNTPProtocol::fetchGroupRFC977( unsigned long first )
00546 {
00547 UDSEntry entry;
00548
00549
00550 int res_code = sendCommand( "STAT " + QString::number( first ) );
00551 QString resp_line = readBuffer;
00552 if (res_code != 223) {
00553 unexpected_response(res_code,"STAT");
00554 return false;
00555 }
00556
00557
00558 QString msg_id;
00559 int pos, pos2;
00560 if ((pos = resp_line.indexOf('<')) > 0 && (pos2 = resp_line.indexOf('>',pos+1))) {
00561 msg_id = resp_line.mid(pos,pos2-pos+1);
00562 fillUDSEntry( entry, msg_id, 0, true );
00563 listEntry( entry, false );
00564 } else {
00565 error(ERR_INTERNAL,i18n("Could not extract first message id from server response:\n%1",
00566 resp_line));
00567 return false;
00568 }
00569
00570
00571 while (true) {
00572 res_code = sendCommand("NEXT");
00573 if (res_code == 421) {
00574
00575 entry.clear();
00576 listEntry( entry, true );
00577 return true;
00578 } else if (res_code != 223) {
00579 unexpected_response(res_code,"NEXT");
00580 return false;
00581 }
00582
00583
00584 resp_line = readBuffer;
00585 if ((pos = resp_line.indexOf('<')) > 0 && (pos2 = resp_line.indexOf('>',pos+1))) {
00586 msg_id = resp_line.mid(pos,pos2-pos+1);
00587 entry.clear();
00588 fillUDSEntry( entry, msg_id, 0, true );
00589 listEntry( entry, false );
00590 } else {
00591 error(ERR_INTERNAL,i18n("Could not extract message id from server response:\n%1",
00592 resp_line));
00593 return false;
00594 }
00595 }
00596 return true;
00597 }
00598
00599
00600 bool NNTPProtocol::fetchGroupXOVER( unsigned long first, bool ¬Supported )
00601 {
00602 notSupported = false;
00603
00604 QString line;
00605 QStringList headers;
00606
00607 int res = sendCommand( "LIST OVERVIEW.FMT" );
00608 if ( res == 215 ) {
00609 while ( true ) {
00610 if ( ! waitForResponse( readTimeout() ) ) {
00611 error( ERR_SERVER_TIMEOUT, mHost );
00612 nntp_close();
00613 return false;
00614 }
00615 readBufferLen = readLine ( readBuffer, MAX_PACKET_LEN );
00616 line = QString::fromLatin1( readBuffer, readBufferLen );
00617 if ( line == ".\r\n" )
00618 break;
00619 headers << line.trimmed();
00620 DBG << "OVERVIEW.FMT:" << line.trimmed();
00621 }
00622 } else {
00623
00624 headers << "Subject:" << "From:" << "Date:" << "Message-ID:"
00625 << "References:" << "Bytes:" << "Lines:";
00626 }
00627
00628 res = sendCommand( "XOVER " + QString::number( first ) + '-' );
00629 if ( res == 420 )
00630 return true;
00631 if ( res == 500 )
00632 notSupported = true;
00633 if ( res != 224 ) {
00634 unexpected_response( res, "XOVER" );
00635 return false;
00636 }
00637
00638 long msgSize;
00639 QString name;
00640 UDSEntry entry;
00641 int udsType;
00642
00643 QStringList fields;
00644 while ( true ) {
00645 if ( ! waitForResponse( readTimeout() ) ) {
00646 error( ERR_SERVER_TIMEOUT, mHost );
00647 nntp_close();
00648 return false;
00649 }
00650 readBufferLen = readLine ( readBuffer, MAX_PACKET_LEN );
00651 line = QString::fromLatin1( readBuffer, readBufferLen );
00652 if ( line == ".\r\n" ) {
00653 entry.clear();
00654 listEntry( entry, true );
00655 return true;
00656 }
00657
00658 fields = line.split( '\t', QString::KeepEmptyParts);
00659 msgSize = 0;
00660 entry.clear();
00661 udsType = KIO::UDSEntry::UDS_EXTRA;
00662 QStringList::ConstIterator it = headers.constBegin();
00663 QStringList::ConstIterator it2 = fields.constBegin();
00664
00665 name = (*it2);
00666 ++it2;
00667 for ( ; it != headers.constEnd() && it2 != fields.constEnd(); ++it, ++it2 ) {
00668 if ( (*it) == "Bytes:" ) {
00669 msgSize = (*it2).toLong();
00670 continue;
00671 }
00672 QString atomStr;
00673 if ( (*it).endsWith( QLatin1String( "full" ) ) )
00674 if ( (*it2).trimmed().isEmpty() )
00675 atomStr = (*it).left( (*it).indexOf( ':' ) + 1 );
00676 else
00677 atomStr = (*it2).trimmed();
00678 else
00679 atomStr = (*it) + ' ' + (*it2).trimmed();
00680 entry.insert( udsType++, atomStr );
00681 if ( udsType >= KIO::UDSEntry::UDS_EXTRA_END )
00682 break;
00683 }
00684 fillUDSEntry( entry, name, msgSize, true );
00685 listEntry( entry, false );
00686 }
00687 return true;
00688 }
00689
00690
00691 void NNTPProtocol::fillUDSEntry( UDSEntry& entry, const QString& name, long size,
00692 bool is_article, long access ) {
00693
00694 long posting=0;
00695
00696
00697 entry.insert(KIO::UDSEntry::UDS_NAME, name);
00698
00699
00700 entry.insert(KIO::UDSEntry::UDS_SIZE, size);
00701
00702
00703 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, is_article? S_IFREG : S_IFDIR);
00704
00705
00706 posting = postingAllowed? access : 0;
00707 long long accessVal = (is_article)? (S_IRUSR | S_IRGRP | S_IROTH) :
00708 (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | posting);
00709 entry.insert(KIO::UDSEntry::UDS_ACCESS, accessVal);
00710
00711 entry.insert(KIO::UDSEntry::UDS_USER, mUser.isEmpty() ? QString::fromLatin1("root") : mUser);
00712
00713
00714
00715
00716
00717
00718 if (is_article) {
00719 entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("message/news") );
00720 }
00721 }
00722
00723 void NNTPProtocol::nntp_close () {
00724 if ( isConnected() ) {
00725 write( "QUIT\r\n", 6 );
00726 disconnectFromHost();
00727 isAuthenticated = false;
00728 }
00729 mCurrentGroup.clear();
00730 }
00731
00732 bool NNTPProtocol::nntp_open()
00733 {
00734
00735 if ( isConnected() ) {
00736 DBG << "reusing old connection";
00737 return true;
00738 }
00739
00740 DBG << " nntp_open -- creating a new connection to" << mHost << ":" << m_port;
00741
00742 infoMessage( i18n("Connecting to server...") );
00743 if ( connectToHost( (isAutoSsl() ? "nntps" : "nntp"), mHost, m_port ) )
00744 {
00745 DBG << " nntp_open -- connection is open";
00746
00747
00748 int res_code = evalResponse( readBuffer, readBufferLen );
00749
00750
00751
00752
00753
00754 if ( ! ( res_code == 200 || res_code == 201 ) )
00755 {
00756 unexpected_response(res_code,"CONNECT");
00757 return false;
00758 }
00759
00760 DBG << " nntp_open -- greating was read res_code :" << res_code;
00761
00762 res_code = sendCommand("MODE READER");
00763
00764
00765 if ( !(res_code == 200 || res_code == 201) ) {
00766 unexpected_response( res_code, "MODE READER" );
00767 return false;
00768 }
00769
00770
00771 postingAllowed = (res_code == 200);
00772
00773
00774 if ( metaData("tls") == "on" ) {
00775 if ( sendCommand( "STARTTLS" ) != 382 ) {
00776 error( ERR_COULD_NOT_CONNECT, i18n("This server does not support TLS") );
00777 return false;
00778 }
00779 if ( !startSsl() ) {
00780 error( ERR_COULD_NOT_CONNECT, i18n("TLS negotiation failed") );
00781 return false;
00782 }
00783 }
00784
00785
00786 authenticate();
00787
00788 return true;
00789 }
00790
00791 return false;
00792 }
00793
00794 int NNTPProtocol::sendCommand( const QString &cmd )
00795 {
00796 int res_code = 0;
00797
00798 if ( !nntp_open() ) {
00799 ERR << "NOT CONNECTED, cannot send cmd" << cmd;
00800 return 0;
00801 }
00802
00803 DBG << "cmd:" << cmd;
00804
00805 write( cmd.toLatin1(), cmd.length() );
00806
00807 if ( !cmd.endsWith( QLatin1String( "\r\n" ) ) )
00808 write( "\r\n", 2 );
00809 res_code = evalResponse( readBuffer, readBufferLen );
00810
00811
00812 if (res_code == 480) {
00813 DBG << "auth needed, sending user info";
00814
00815 if ( mUser.isEmpty() || mPass.isEmpty() ) {
00816 KIO::AuthInfo authInfo;
00817 authInfo.username = mUser;
00818 authInfo.password = mPass;
00819 if ( openPasswordDialog( authInfo ) ) {
00820 mUser = authInfo.username;
00821 mPass = authInfo.password;
00822 }
00823 }
00824 if ( mUser.isEmpty() || mPass.isEmpty() )
00825 return res_code;
00826
00827 res_code = authenticate();
00828 if (res_code != 281) {
00829
00830 return res_code;
00831 }
00832
00833
00834 write( cmd.toLatin1(), cmd.length() );
00835 if ( !cmd.endsWith( QLatin1String( "\r\n" ) ) )
00836 write( "\r\n", 2 );
00837 res_code = evalResponse( readBuffer, readBufferLen );
00838 }
00839
00840 return res_code;
00841 }
00842
00843 int NNTPProtocol::authenticate()
00844 {
00845 int res_code = 0;
00846
00847 if( isAuthenticated ) {
00848
00849 return 281;
00850 }
00851
00852 if( mUser.isEmpty() || mPass.isEmpty() ) {
00853 return 281;
00854 }
00855
00856
00857 write( "AUTHINFO USER ", 14 );
00858 write( mUser.toLatin1(), mUser.length() );
00859 write( "\r\n", 2 );
00860 res_code = evalResponse( readBuffer, readBufferLen );
00861
00862 if( res_code == 281 ) {
00863
00864 return res_code;
00865 }
00866 if (res_code != 381) {
00867
00868 return res_code;
00869 }
00870
00871
00872 write( "AUTHINFO PASS ", 14 );
00873 write( mPass.toLatin1(), mPass.length() );
00874 write( "\r\n", 2 );
00875 res_code = evalResponse( readBuffer, readBufferLen );
00876
00877 if( res_code == 281 ) {
00878 isAuthenticated = true;
00879 }
00880
00881 return res_code;
00882 }
00883
00884 void NNTPProtocol::unexpected_response( int res_code, const QString &command )
00885 {
00886 ERR << "Unexpected response to" << command << "command: (" << res_code << ")"
00887 << readBuffer;
00888
00889
00890 switch ( res_code ) {
00891 case 205:
00892
00893 case 400:
00894 error( ERR_INTERNAL_SERVER,
00895 i18n( "The server %1 could not handle your request.\n"
00896 "Please try again now, or later if the problem persists.", mHost ) );
00897 break;
00898 case 480:
00899 error( ERR_COULD_NOT_LOGIN,
00900 i18n( "You need to authenticate to access the requested resource." ) );
00901 case 481:
00902 error( ERR_COULD_NOT_LOGIN,
00903 i18n( "The supplied login and/or password are incorrect." ) );
00904 break;
00905 case 502:
00906 error( ERR_ACCESS_DENIED, mHost );
00907 break;
00908 default:
00909 error( ERR_INTERNAL, i18n( "Unexpected server response to %1 command:\n%2", command, readBuffer ) );
00910 }
00911
00912 nntp_close();
00913 }
00914
00915 int NNTPProtocol::evalResponse ( char *data, ssize_t &len )
00916 {
00917 if ( !waitForResponse( responseTimeout() ) ) {
00918 error( ERR_SERVER_TIMEOUT , mHost );
00919 nntp_close();
00920 return -1;
00921 }
00922 len = readLine( data, MAX_PACKET_LEN );
00923
00924 if ( len < 3 )
00925 return -1;
00926
00927
00928 int respCode = ( ( data[0] - 48 ) * 100 ) + ( ( data[1] - 48 ) * 10 ) + ( ( data[2] - 48 ) );
00929
00930 DBG << "got:" << respCode;
00931
00932 return respCode;
00933 }
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987