Drobné programy (C, C++, C#, ...)
Detail souboru
Zdrojový kód
/*
* 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>
#include <fstream>
namespace {
const char *szAppName= "nntbackup";
inline void errMsg (const char *szMsg) { std::cerr << szAppName << ": error: " << szMsg << "\n"; }
inline void errMsg (std::string &msg) { std::cerr << szAppName << ": error: " << msg << "\n"; }
}
/**
* This class parse command-line arguments. It uses direct writing
* to environment data thrught pointers. Be careful.
*/
class ArgParser {
public:
struct Err {}; ///< General error exception
struct ErrHelpNeeded: Err {}; ///< User request to print help
struct ErrCustom: Err { ///< Custom-text exception
std::string _errMsg; ///< Error message text
ErrCustom (const char *szMsg): _errMsg (szMsg) {}; ///< Exception constructor
};
/**
* Class constructor. Parameters are the same as main() function parameters.
* \param argc Main's argc parameter
* \param argv Main's argv parameter
*/
ArgParser (int argc, char *argv[]);
const char *server() const { return _szServer; } ///< \return Return server address (URL or IP).
unsigned port() const { return _uPort; } ///< \return Return port number. Return 0 if not present.
bool nextGroup (const char **pszGroup); ///< \return Return pointer to next group name.
private:
char *_szServer;
unsigned _uPort;
char **_pszCurrent;
};
/**
* NntpClient wrapper. It adds some features to NntpClient class.
*/
class NntpBackup: private NntpClient {
public:
struct Err {};
struct ErrFileOpen: Err { ///< Error opening file for writing
std::string _fileName; ///< Errorneous file name
ErrFileOpen (std::string fileName): _fileName (fileName) {}
};
/**
* \param szServer Server address (URL or IP).
* \param uPort Port number. DEF_NNTP_PORT is used if uPort is zero.
*/
NntpBackup (const char *szServer, unsigned uPort):
NntpClient (szServer, uPort ? uPort : NntpClient::DEF_NNTP_PORT)
{
}
/**
* Concatenate all articles in group szGroup and save text to file szGroup.
* \param szGroup Group name and also file name to save to.
*/
void groupBackup (const char *szGroup);
};
/**
* Application's main() function.
* \param argc Number of command-line arguments.
* \param argv Array of command-line argumnets text strings.
* \return Return zero if there is no error.
*/
int main (int argc, char *argv[]) try {
ArgParser args (argc, argv);
NntpBackup nntp (args.server(), args.port());
bool bAllOk= true;
const char *szGroup;
while (args.nextGroup (&szGroup)) {
try {
nntp.groupBackup (szGroup);
}
catch (NntpProtocol::ErrDot) {
errMsg ("Article data error");
bAllOk= false;
}
catch (NntpProtocol::ErrCmd err) {
switch (err._iStatus) {
case 411: {
// no such news group
std::string msg ("News group \'");
msg.append (szGroup);
msg.append ("\' not found on server");
errMsg (msg);
break;
}
default: throw;
}
bAllOk= false;
}
catch (NntpBackup::ErrFileOpen err) {
std::string msg("Could not open \'");
msg.append (err._fileName);
msg.append ("\' for writing");
errMsg (msg);
bAllOk= false;
}
} // while
return bAllOk ? 0 : -1;
}
catch (ArgParser::ErrHelpNeeded) {
std::cout <<
"nntpbackup <host>[:<port>] <group_1> [...<group_n>]\n"
" host (obligatory) is an address of NNTP server (IP or name)\n"
" port (optional) is port number, on which server listen, default is standrad NNTP port (119)\n"
" group_1...group_n (obligatory) are names of news groups, which should be backed up. "
"At least one news group has to be placed.\n";
return 0;
}
catch (ArgParser::ErrCustom err) {
errMsg (err._errMsg);
return -1;
}
catch (TcpConnection::ErrConnect) {
errMsg ("Could not connect to remote server");
return -1;
}
catch (TcpConnection::Err) {
errMsg ("Connection error (TCP layer)");
return -1;
}
catch (NntpProtocol::ErrConnect) {
errMsg ("Connection refused by server (NNTP layer)");
return -1;
}
catch (NntpProtocol::Err) {
errMsg ("Comunication error (NNTP layer)");
return -1;
}
ArgParser::ArgParser (int argc, char *argv[]):
_uPort (0)
{
if (argc <2 || 0==strcmp ("--help", argv [1]))
throw ErrHelpNeeded();
if (argc <3)
throw ErrCustom ("Missing argument");
_szServer= argv [1];
_pszCurrent= argv+2;
std::string srvString (_szServer);
size_t iColon= srvString.find (':');
if (std::string::npos!= iColon) {
_szServer [iColon]= '\0';
char tmp;
if (1!= sscanf (_szServer+iColon+1, "%u%c", &_uPort, &tmp))
throw ErrCustom ("Bad port number format");
}
}
bool ArgParser::nextGroup (const char **pszGroup)
{
*pszGroup= *_pszCurrent;
_pszCurrent++;
return (*pszGroup);
}
void NntpBackup::groupBackup (const char *szGroup)
{
// Select group on server
groupSelect (szGroup);
/**
* Opened file wrapper. This should automatically close opened file on an exception.
*/
class FileWrapper {
std::ofstream _file;
public:
FileWrapper (const char *szFileName):
_file (szFileName, std::ios::out)
{
if (!_file)
throw ErrFileOpen(szFileName);
}
~FileWrapper()
{
_file.close();
}
operator std::ostream&()
{
if (!_file)
throw Err();
return _file;
}
} file (szGroup);
// Save all articles in group to file
while (readNext (file))
static_cast<std::ostream &>(file) << std::endl;
}