summaryrefslogblamecommitdiffstats
path: root/03_exercise/cli/client.c
blob: dbec3eb083c1caaf940cd731469d5361953d9b9f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                  
                   

                   
                   

                  


                       
                   
                    
 


                        

                     


                  

                                 
                
          

                               
































                                                                          
                   





                           

                       



                                                        
                          




                                                                                               





                       




                             




                                                              
         

     





                                                                                       












                                  
                                          









































































                                                                        

 
                                 
                   
              
                                                              
                                               
                           
     

                                
 
 
                                         
                
                       


             

                                                           

                         


                                                                            







                       
                            



                                                         
 



                                                
                                     


                              

                  



                                                 
 
                                              
                                   


                                                      
 



                                                                       
                                
 
              


                                                                         

     


                      
                   

                      

 
                                         









                                              
                            













                                                             
                                                                                      




                                        
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdbool.h>

#define PORT 9000
#define HOST "127.0.0.1"

#define BUF_SIZE 2048

#define BREAK    1
#define CONTINUE 2

char *get_current_dir_name(void);

int server_sock;
pid_t pid;

/* Signal Handler for SIGINT */
void sigintHandler(int sig_num);

static inline void die(char const *msg);

int write_all(int target_sock, char *buffer, size_t size);

void send_file(char *path);

int parse(char *buffer, ssize_t length);

void read_stdin();

_Noreturn void read_server_sock();

int main() {
    setvbuf(stdout, NULL, _IONBF, 0);

    struct sockaddr_in addr = {
            .sin_family = AF_INET,
            .sin_port = htons(PORT),
            .sin_addr.s_addr = inet_addr(HOST)
    };

    if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        die("Could not open socket");

    if (connect(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        die("Could not connect to socket");

    signal(SIGINT, &sigintHandler);

    pid = fork();

    if (pid == 0) {
        read_stdin();
    } else {
        read_server_sock();
    }

    close(server_sock);
    kill(pid, SIGKILL);

    return 0;
}

void receive_file(int recv_sock, char *path, int size) {
    char buffer[BUF_SIZE];
    int  file_fd;
    int  received;

    if ((file_fd = open(path, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) == -1) { perror("Open"); }

    if (size == 0) {
        // just touch
        close(file_fd);
        return;
    }

    received = 0;

    /* Read all data */
    while (received < size) {
        int nread;
        if ((nread = read(recv_sock, buffer, BUF_SIZE)) > 0) {
            if (write_all(file_fd, buffer, nread) != nread) {
                break;
            }
            received += nread;
        }
    }

    if (received == size) {
        printf("successfully got remote file \"%s/%s\"", get_current_dir_name(), path);
        printf("\n");
    } else {
        printf("error getting remote file \"%s/%s\"", get_current_dir_name(), path);
        printf("\n");
    }

    close(file_fd);
}

bool check_get(char *buffer) {
    // "<<!bytecount!name"
    char cmd[4];

    memcpy(cmd, buffer, 3);
    cmd[3] = 0;

    if (strcmp(cmd, "<<!") == 0) {
        //printf("[c]: \"%s\"\n", buffer);
        int    offset    = 3;
        int    i         = offset;
        char   rest[BUF_SIZE - 3];
        size_t file_size;
        for (; i < BUF_SIZE; ++i) {
            if (buffer[i] == '!')
                break;
            rest[i - offset] = buffer[i];
        }
        rest[i - offset] = 0;
        file_size = atoi(rest);

        offset           = ++i;
        for (; i < BUF_SIZE; ++i) {
            if (buffer[i] == '!')
                break;
            rest[i - offset] = buffer[i];
        }
        rest[i - offset] = 0;
        char path[strlen(rest) + 1];
        memcpy(path, rest, strlen(rest));
        path[strlen(rest)]     = 0;
        path[strlen(rest) - 1] = 0;

        receive_file(server_sock, path, file_size);

        return true;
    }
    return false;
}

void read_server_sock() {
    char server_buffer[BUF_SIZE];

    while (1) {
        ssize_t length;
        if ((length = read(server_sock, server_buffer, BUF_SIZE)) > 0) {
            server_buffer[length] = '\0';

            // get issued, as it seems
            if (check_get(server_buffer)) {
                printf("$> ");
                fflush(stdout);
                continue;
            }

            printf("%s", server_buffer);
            if (length == 1 && server_buffer[length - 1] == '\n') {
                printf("$> ");
                fflush(stdout);
            }
        }
    }
}

void read_stdin() {
    char stdin_buffer[BUF_SIZE];

    bool done = false;
    while (!done) {
        ssize_t length;
        memset(stdin_buffer, 0, BUF_SIZE);
        if ((length = read(STDIN_FILENO, stdin_buffer, BUF_SIZE)) > 0) {
            switch (parse(stdin_buffer, length)) {
                case CONTINUE:
                    continue;
                case BREAK:
                    done = true;
                    break;
                default:
                    break;
            }
        }
    }
}

void sigintHandler(int sig_num) {
    (void) sig_num;
    errno = 0;
    if (fcntl(server_sock, F_GETFD) != -1 || errno != EBADF) {
        (void) write(server_sock, "exit\n", 6);
        close(server_sock);
    }
    errno = 0;
    die("Terminating client\n");
}

static inline void die(char const *msg) {
    perror(msg);
    kill(pid, SIGKILL);
    exit(-1);
}

int write_all(int target_sock, char *buffer, size_t size) {
    int sent = 0;

    while (sent < size) {
        int nwrite;
        if ((nwrite = write(target_sock, buffer + sent, size - sent)) < 0) {
            die("Write");
        }

        sent += nwrite;
    }

    return sent;
}

void send_file(char *path) {
    printf("Trying to send \"%s\" to remote...\n", path);
    char buffer[BUF_SIZE];
    memset(buffer, 0, BUF_SIZE);
    int tmp_fd, file_fd;

    if ((tmp_fd = open(path, O_RDONLY)) == -1) {
        perror("Open");
        return;
    }
    FILE *file = fdopen(tmp_fd, "r");
    fseek(file, 0L, SEEK_END);
    long int sz = ftell(file);
    fseek(file, 0L, SEEK_SET);
    fclose(file);
    close(tmp_fd);
    if ((file_fd = open(path, O_RDONLY)) == -1) {
        perror("Open");
        return;
    }

    int length = snprintf(NULL, 0, "%ld", sz);
    sprintf(buffer, "<<!%ld!", sz);
    sprintf(&(buffer[3 + length + 1]), "%s!\n", path);

    printf("[c] \"%s\"\n", buffer);

    if (write(server_sock, buffer, strlen(buffer)) != strlen(buffer)) {
        perror("write header");
        return;
    }
    memset(buffer, 0, BUF_SIZE);

    errno = 0;
    size_t count;
    while ((count = read(file_fd, buffer, sizeof(buffer))) > 0) {
        printf("Sent %i bytes\n", write_all(server_sock, buffer, count));
    }

    if (errno)
        perror("wad");

    close(file_fd);

    printf("done.\n");
}

int parse(char *buffer, ssize_t length) {
    char cmd[4];
    char path[BUF_SIZE - 3];
    memcpy(cmd, buffer, 3);
    cmd[3] = 0;
    memcpy(path, buffer + 4, BUF_SIZE - 4);
    path[strlen(path) - 1] = 0;
    if (strcmp(cmd, "put") == 0) {
        if (strlen(path) == 0) {
            fprintf(stderr, "path missing\n");
        } else {
            send_file(path);
        }
        return CONTINUE;
    }

    buffer[length] = '\0';

    if (strspn(buffer, " \n\t") == strlen(buffer)) {
        // skip empty lines - empty being just spaces or tabs
        return CONTINUE;
    }

    if (write(server_sock, buffer, strlen(buffer)) < 0)
        die("Could not send message");

    // TODO: exit should only close the connection (cleanly), NOT terminate the server
    if (strcmp(buffer, "exit\n") == 0) {
        return BREAK;
    }

    return 0;
}