Česky
Kamil Dudka

Tiny programs (C, C++, C#, ...)

File detail

Name:Downloadnntp_client.cc [Download]
Location: tiny > ISA > nntpbackup
Size:4.8 KB
Last modification:2022-09-09 13:06

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;
}