This repository has been archived on 2021-08-17. You can view files and clone it, but cannot push or open issues or pull requests.
unix/http/client.c

149 lines
4.3 KiB
C

/**
* @file client.c
* @author Ivaylo Ivanov 11777707
* @date 03.11.2018
*
* @brief Client program module.
*
* The client module takes a URL as input, connects to the corresponding server on the corresponding
* port(`80` by default) and requests the file specified in the URL.
* The content of that file is written to `stdout`, to a file or to a directory.
*
* SYNOPSIS
* client [-p PORT] [ -o FILE | -d DIR ] URL
*
* EXAMPLE
* client http://ivayloivanov.eu/en/
*
**/
#define _GNU_SOURCE ///< in order for optarg to be defined
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "shared/http.h"
static struct addrinfo* get_host_info(const char *hostname, char *port);
int main(int argc, char *argv[]) {
check_opts_number(argc);
char *port = "80";
char *ofile = NULL;
char *dir = NULL;
char *url = NULL;
int c;
/// Process the options
while((c = getopt(argc, argv, "p:o:d:h")) != -1) {
switch(c) {
case 'p':
port = optarg;
break;
case 'o':
ofile = optarg;
break;
case 'd':
dir = optarg;
break;
case 'h':
printf("Command usage:\n");
print_usage();
exit(EXIT_SUCCESS);
break;
case '?':
print_usage();
exit(EXIT_FAILURE);
default:
abort();
}
}
/// Check for the case that both output file and directory are set
if(ofile != NULL && dir != NULL) {
printf("Either output file or directory is specified. You cannot use both.\n");
print_usage();
exit(EXIT_FAILURE);
}
/// Check if the required argument is there
if(argv[optind] == NULL) {
fprintf(stderr, "Mandatory argument 'url' missing.\n");
print_usage();
exit(EXIT_FAILURE);
} else {
url = argv[optind];
}
const char *full_url = strstr(url, "http://") + 7; //< the full url starts after http://
const char *path = strstr(full_url, "/");
const char *hostname = "google.com"; //< TODO: Change this later
struct addrinfo* res = get_host_info(hostname, port); //< Check if the address is resolvable
freeaddrinfo(res); //< Free the addrinfo struct
}
static void print_usage(void) {
printf("client [-p PORT] [ -o FILE | -d DIR ] URL\n");
}
/**
* @brief Function to check the number of arguments
* @details Checks the number of arguments supplied to the command line.
* If the arguments are less than the required or more than possible, it exits
* @param argc - Number of arguments obtained from the command line
* @return none
*
**/
static void check_opts_number(int argc) {
if(argc <= 1) {
fprintf(stderr, "At least one argument expected.\n");
print_usage();
exit(EXIT_FAILURE);
} else if(argc > 6) {
fprintf(stderr, "Too many arguments supplied.\n");
print_usage();
exit(EXIT_FAILURE);
}
}
/**
* @brief Function to check if the address is resolvable by the client
* @details Checks if the address is resolvable by the client.
* If the address is resolvable, it writes the result to res
* If the address is not resolvable, it exits with an error
* @param
* hostname - The address to be resolved
* port - The port to try
* @return res - The result of getaddrinfo()
*
**/
static struct addrinfo* get_host_info(const char *hostname, char *port) {
struct addrinfo hints; //< Configuration for the client
hints.ai_family = AF_INET; //< Use IPv4
hints.ai_socktype = SOCK_STREAM; //< Stream socket
hints.ai_flags = 0; //< Do not use any flags
hints.ai_protocol = IPPROTO_TCP; //< Use only TCP
struct addrinfo* res = NULL;
int err = getaddrinfo(hostname, port, &hints, &res);
if(err != 0) {
(void) fprintf(stderr, "ERROR: %s\n", gai_strerror(err));
exit(EXIT_FAILURE); //< Exit if there is an error
}
if(res == NULL) {
(void) fprintf(stderr, "ERROR: Could not resolve address %s\n", hostname);
exit(EXIT_FAILURE); //< Exit if the address is not resolved
}
return res;
}