Tiny programs (C, C++, C#, ...)
File detail
Source code
/*
* file: nntp_client.cc - NNTP protocol layer encapsulation
* project: nntpbackup (ISA)
* By Kamil Dudka, xdudka00
* mailto: xdudka00@gmail.com
*/
#include "nntp_client.h"
#include <iostream>
const unsigned DEFAULT_LINE_LENGTH= 512; ///< Use this for performance tunning
const char *const NntpProtocol::szEndl= "\r\n"; ///< CR-LF
const char *const NntpProtocol::szEndText= "."; ///< "end of article" char
// Begin of TCP layer
TcpConnection::TcpConnection (const char *szAddr, unsigned uPort)
{
if (0!= netConnect (szAddr, uPort, &_sockfd))
throw ErrConnect();
if (NULL== (_netLine= netLineInit (_sockfd, DEFAULT_LINE_LENGTH))) {
netClose (_sockfd);
throw ErrConnect();
}
}
TcpConnection::~TcpConnection()
{
netLineDestroy (_netLine);
netClose (_sockfd);
}
TcpConnection &TcpConnection::operator<< (const char *szText)
{
if (0!= netPut (_netLine, szText))
throw ErrWrite();
return *this;
}
TcpConnection &TcpConnection::operator>> (const char **pszText)
{
if (0!= netLineGet (_netLine, pszText))
throw ErrRead();
return *this;
}
// Begin of NNTP protocol layer
NntpProtocol::NntpProtocol (TcpConnection &tcp):
_tcp (tcp)
{
this->readStatus ();
switch (getStatus ()) {
case 200: // server ready, posting allowed
case 201: // server ready, no posting allowed
break;
default: throw ErrConnect();
}
}
NntpProtocol::~NntpProtocol()
{
}
NntpProtocol::Cmd::~Cmd()
{
}
// GROUP nntp command
NntpProtocol::CmdGroup::CmdGroup (const char *szGroup)
{
_strCmd= "GROUP ";
_strCmd.append (szGroup);
}
void NntpProtocol::CmdGroup::handleStatus (int iStatus)
{
switch (iStatus) {
case 211: // group selected
break;
default: throw ErrCmd (iStatus);
}
}
// ARTICLE nntp command
NntpProtocol::CmdArticle::CmdArticle()
{
_strCmd= "ARTICLE";
}
void NntpProtocol::CmdArticle::handleStatus (int iStatus)
{
switch (iStatus) {
case 220: // article retrieved, head and body follow
break;
default: throw ErrCmd (iStatus);
}
}
// NEXT nntp command
NntpProtocol::CmdNext::CmdNext()
{
_strCmd= "NEXT";
}
void NntpProtocol::CmdNext::handleStatus (int iStatus)
{
switch (iStatus) {
case 223: // article retrieved
break;
default: throw ErrCmd (iStatus);
}
}
// QUIT nntp command
NntpProtocol::CmdQuit::CmdQuit()
{
_strCmd= "QUIT";
}
void NntpProtocol::CmdQuit::handleStatus (int)
{
// no status handling needed
}
// command sending engine
NntpProtocol &NntpProtocol::operator<< (Cmd *pCmd)
{
try {
_tcp << pCmd->_strCmd.c_str() << szEndl;
this->readStatus();
pCmd->handleStatus (getStatus ());
}
catch (...) {
delete pCmd;
throw;
}
delete pCmd;
return *this;
}
// read status response
void NntpProtocol::readStatus() {
_tcp >> &_szLine;
if (1!= sscanf (_szLine, "%i", &_iStatus))
throw ErrNoStatus();
}
// read data line
bool NntpProtocol::getLine (const char **pszLine)
{
*pszLine= NULL;
_tcp >> &_szLine;
if (0== strcmp (_szLine, szEndText))
// end of article
return false;
if ('.'== _szLine[0]) {
if ('.'== _szLine[1])
// double dot
*pszLine= _szLine +1;
else
throw ErrDot();
}
else
// normal data line
*pszLine= _szLine;
return true;
}
// simple nntp CLIENT
NntpClient::NntpClient (const char *szServer, unsigned uPort):
_tcp (szServer, uPort),
_nntp (_tcp),
_bEnd (false)
{
}
NntpClient::~NntpClient ()
{
#ifdef ARTICLE_PIPELINE // EXPERIMENTAL
const char *tmp;
if (_bEnd && 411!= _nntp.getStatus()) {
_tcp >> &tmp; // drop pipelined ARTICLE status
while (_nntp.getLine (&tmp)); // drop pipelined ARTICLE text
_tcp >> &tmp; // drop pipelined NEXT status
}
#endif
_nntp << new NntpProtocol::CmdQuit;
}
void NntpClient::groupSelect (const char *szGroup)
{
#ifdef ARTICLE_PIPELINE // EXPERIMENTAL
const char *tmp;
if (_bEnd && 411!= _nntp.getStatus()) {
_tcp >> &tmp; // drop pipelined ARTICLE status
while (_nntp.getLine (&tmp)); // drop pipelined ARTICLE text
_tcp >> &tmp; // drop pipelined NEXT status
}
#endif
_nntp << new NntpProtocol::CmdGroup (szGroup);
_bEnd=false;
#ifdef ARTICLE_PIPELINE // EXPERIMENTAL
_tcp << "ARTICLE\r\nNEXT\r\n"; // pipeline initiation
#endif
}
bool NntpClient::readNext (std::ostream &streamTo)
{
if (_bEnd)
// there is no next (saved state)
return false;
try {
// read article
_nntp << new NntpProtocol::CmdArticle;
const char *szLine;
while (_nntp.getLine (&szLine))
streamTo << szLine << std::endl;
// jump to next
_nntp << new NntpProtocol::CmdNext;
}
catch (NntpProtocol::ErrCmd err) {
switch (err._iStatus) {
case 420:
case 423:
case 430:
// no article found
_bEnd= true;
return false;
case 421:
// no next article found
_bEnd= true;
break;
default: throw;
}
}
return true;
}