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