Česky
Kamil Dudka

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

File detail

Name:Downloadclient.cc [Download]
Location: tiny > IPK > trunk
Size:5.7 KB
Last modification:2022-09-09 13:06

Source code

/*
 * file: client.cc - Client module containing main ()
 * project: BSD Sockets (IPK)
 * By Kamil Dudka, xdudka00
 * mailto: xdudka00@gmail.com
 */
 
#include <iostream>
#include <sstream>
#include <cstring>
 
#include "client.h"
#include "netio.h"
 
using std::string;
 
namespace {
	const char szClientName[] = "client/1.0";
 
	const unsigned DefLineLength = 256;
 
	struct ErrClient {
		string strErrMsg;
 
		ErrClient (const char *msg) { strErrMsg = msg; }
	};
}
 
/*
 * Connect to server using module netio
 */
Connection::Connection (Request &rq) {
	if (0!= netConnect (rq.getServer (), rq.getPort (), &socket))
		throw ErrHandled ();
 
	if (NULL== (pNL= netLineInit (socket, DefLineLength) ))
		throw ErrInit ();
}
 
/*
 * Disconnect from server
 */
Connection::~Connection () {
	netLineDestroy (pNL);
	netClose (socket);
}
 
/*
 * Send plain text to server
 */
Connection & Connection::operator<< (const char *szText) {
	netPut (pNL, szText);
	return *this;
}
 
/*
 * Read one line from server
 */
Connection & Connection::operator>> (string &str) {
	char *szText;
	netLineGet (pNL, &szText);
	str = szText;
 
	return *this;
}
 
 
// Set port number
void Request::setPort (char *str) {
	char tmp;
	if (1!=sscanf (str, "%u%c", &port, &tmp))
		throw ErrBadPort ();
}
 
// Set fields string and test fields integrity
void DataRequest::setFields (char *str) {
	const int length = strlen (str);
	if (0==length || length>4)
		// More than 4 fields
		throw ErrBadArg ();
 
	// Copy fields string
	fields = str;
 
	// Check fields integrity
	bool bName = false;
	bool bSurname = false;
	bool bLogin = false;
	bool bFaculty = false;
	for (int i=0; i<length; i++)
		switch (fields [i]) {
			case 'N': 
				duplCheck (bName);
				break;
			case 'S':
				duplCheck (bSurname);
				break;
			case 'L':
				duplCheck (bLogin);
				break;
			case 'F':
				duplCheck (bFaculty);
				break;
			default:
				throw ErrBadField ();
		}
}
 
// Read DataRequest from command-line
void DataRequest::readFromCmd (int argc, char *argv[]) {
	// Found arguments (nothing yet)
	bool bServer = false;
	bool bPort = false;
	bool bName = false;
	bool bSurname = false;
	bool bLogin = false;
	bool bFaculty = false;
	bool bFields = false;
 
	// Cycle read each arg from cmd-line
	for (int i=1; i<argc; i++) {
		char *arg = argv[i];
		if ('-'!= *arg)
			throw ErrBadArg ();
		arg++;
 
		switch (*arg) {
			case 'h':	// Server name
			case 'p':	// Port number
			case 'n':	// name
			case 's':	// surname
			case 'l':	// login
			case 'f':	// faculty
				if (++i >= argc)
					throw ErrArgMissing ();
		}
 
		// Read the second part of the arg
		switch (*arg) {
			case 'h':
				duplCheck (bServer);
				server = argv [i];
				break;
			case 'p':
				duplCheck (bPort);
				setPort (argv [i]);
				break;
			case 'n':
				duplCheck (bName);
				name = argv [i];
				break;
			case 's':
				duplCheck (bSurname);
				surname = argv [i];
				break;
			case 'l':
				duplCheck (bLogin);
				login = argv [i];
				break;
			case 'f':
				duplCheck (bFaculty);
				faculty = argv [i];
				break;
			case '-':
				if (0==strcmp (arg, "-help"))
					throw PrintHelp ();
				else
					throw ErrBadArg ();
			default:
				duplCheck (bFields);
				setFields (arg);
		}
	}
 
	if (!bServer || !bPort)
		// Incomplete cmd-line
		throw ErrArgMissing ();
 
	if (!bName && !bSurname && ! bLogin && !bFaculty)
		// Nothing to search
		throw ErrNothingToDisplay ();
 
	if (!bFields)
		// Nothing to display
		throw ErrNothingToDisplay ();
}
 
void DataRequest::sendRequest (Connection &conn) {
	// RQ Header with hostname
	conn << "RQ\n";
	conn << "Host: " << server << "\n";
 
	// Client
	conn << "Client: " << szClientName << "\n";
 
	// Request data
	conn << "Fields: " << fields << "\n";
 
	if (!name.empty ())
		conn << "Name: " << name << "\n";
 
	if (!surname.empty ())
		conn << "Surname: " << surname << "\n";
 
	if (!login.empty ())
		conn << "Login: " << login << "\n";
 
	if (!faculty.empty ())
		conn << "Faculty: " << faculty << "\n";
 
	// End header with \n
	conn << "\n";
}
 
// something as main()
void clientInvoke (int argc, char *argv[])
{
	try {
		// Read DataRequest from cmd-line
		DataRequest drq;
		drq.readFromCmd (argc, argv);
 
		// Initialize network layer
		Connection conn (drq);
 
		// Send request
		drq.sendRequest (conn);
 
		// Check status
		string strLine;
		conn >> strLine;
		if ("OK"!=strLine)
			throw ErrClient ("Request refused by server");
 
		// Check records field
		unsigned uCount = 0;
		while (conn >> strLine, !strLine.empty ()) {
			std::istringstream line (strLine);
			string str;
			line >> str;
			if ("Records:"==str)
				line >> uCount;
		}
 
		// Check if some records were found
		if (0==uCount)
			throw DataRequest::ErrNothingFound ();
 
		// Print records
		for (; uCount!=0 ; uCount--) {
			conn >> strLine;
			std::cout << strLine << std::endl;
		}
	}
	catch (DataRequest::ErrArgMissing) {
		throw ErrClient ("Missing argument");
	}
	catch (DataRequest::ErrBadArg) {
		throw ErrClient ("Invalid argument");
	}
	catch (DataRequest::ErrBadPort) {
		throw ErrClient ("Invalid port number");
	}
	catch (DataRequest::ErrBadField) {
		throw ErrClient ("Invalid argument");
	}
	catch (DataRequest::ErrNothingFound) {
		throw ErrClient ("Nothing found");
	}
	catch (DataRequest::ErrNothingToDisplay) {
		throw ErrClient ("Nothing to display");
	}
	catch (Connection::ErrInit) {
		throw ErrClient ("Could not initialize");
	}
}
 
int main (int argc, char *argv[]) {
	try {
		clientInvoke (argc, argv);
	}
	catch (ErrClient err) {
		std::cout << "client: error: " << err.strErrMsg << std::endl;
		return 1;
	}
	catch (Connection::ErrHandled) {
		return 1;
	}
	catch (DataRequest::PrintHelp) {
		std::cout << "Usage: client -h hostname -p port [-NSLF] [-n name] [-s surname] [-l login] [-f faculty]\n";
		return 0;
	}
}