/*      flop.c
 *
 * Flop
 *
 * Copyright 1997 Petteri Kangaslampi
*/

#if defined(__WINDOWS__) || defined(__NT__) || defined(_MSC_VER)
#   define __WIN32__
#endif

#ifdef __WIN32__
#   define WIN32_LEAN_AND_MEAN
#   include <windows.h>
#   include <winsock.h>
#else
#   include <unistd.h>
#   include <sys/types.h>
#   include <sys/socket.h>
#   include <netinet/in.h>
#   include <arpa/inet.h>
#   include <netdb.h>
#   include <fcntl.h>
#endif

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

/* WARNING! Assumes 32-bit integers! */

#define BUFFERSIZE 65536

#ifndef __WIN32__
typedef int SOCKET;
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#define closesocket close
#endif

SOCKET          sock;

FILE            *f = NULL;
char            *buffer;



void Error(char *msg)
{
    fprintf(stderr, "flop: %s\n", msg);

    if ( f != NULL )
        fclose(f);

#ifdef __WIN32__
    WSACleanup();
#endif

    exit(EXIT_FAILURE);
}


int KoolSend(SOCKET s, unsigned char *buf, int len, int flags)
{
    int         sent;

    while ( len )
    {
        sent = send(s, buf, len, flags);

        if ( (sent == 0) || (sent == SOCKET_ERROR) )
            return 0;

        buf += sent;
        len -= sent;
    }

    return 1;
}



int KoolRecv(SOCKET s, unsigned char *buf, int len, int flags)
{
    int         recvd;

    while ( len )
    {
        recvd = recv(s, buf, len, flags);

        if ( (recvd == 0) || (recvd == SOCKET_ERROR) )
            return 0;

        buf += recvd;
        len -= recvd;
    }

    return 1;
}



void Send(char *fileName, char *hostname, int port)
{
    struct sockaddr_in serv_addr;
    struct hostent *hostptr;
    unsigned    fileLength;
    unsigned    netLength;
    unsigned char nlen;
    unsigned    fileLeft, doNow;

    if ( strlen(fileName) > 255 )
        Error("File name too long");

    /* Open the file: */
    if ( (f = fopen(fileName, "rb")) == NULL )
        Error("Unable to open file");

    /* Get file size: */
    fseek(f, 0, SEEK_END);
    fileLength = (unsigned) ftell(f);
    fseek(f, 0, SEEK_SET);

    printf("Connecting...\n");

    if( (hostptr = gethostbyname( hostname )) == 0 )
        Error("DNS lookup failure");

    if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
        Error("Unable to open socket");

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr = *(struct in_addr*)(hostptr->h_addr);
    serv_addr.sin_port = htons(port);
    if ( connect(sock, (struct sockaddr*) &serv_addr,
        sizeof(serv_addr)) != 0 )
        Error("connect() failed");

    printf("Connected!\n");

    /* Send file length: */
    netLength = htonl(fileLength);
    if ( !KoolSend(sock, (unsigned char*) &netLength, 4, 0) )
        Error("send() failed");

    /* Send name length: */
    nlen = strlen(fileName);
    if ( !KoolSend(sock, &nlen, 1, 0) )
        Error("send() failed");

    /* Send name: */
    if ( !KoolSend(sock, fileName, nlen, 0) )
        Error("send() failed");

    /* Send file: */
    fileLeft = fileLength;
    while ( fileLeft )
    {
        printf("%s: %-10u\r", fileName, fileLeft);

        if ( fileLeft > BUFFERSIZE )
            doNow = BUFFERSIZE;
        else
            doNow = fileLeft;

        fread(buffer, doNow, 1, f);
        if ( !KoolSend(sock, buffer, doNow, 0) )
            Error("send() failed");
        fileLeft -= doNow;
    }

    printf("%s: %-10u\r", fileName, fileLeft);

    fclose(f);
    closesocket(sock);
}



void Receive(int port)
{
    struct sockaddr_in addr;
    unsigned    fileLength;
    unsigned    netLength;
    unsigned char nlen;
    char        *fileName;
    unsigned    fileLeft, doNow;
    SOCKET      listenSock;
    int         addrlen;

    printf("Waiting for connection...\n");

    if ( (listenSock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
        Error("Unable to open socket");

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    if ( bind(listenSock, (struct sockaddr*) &addr, sizeof(addr)) != 0 )
        Error("bind() failed");

    if ( listen(listenSock, 1) )
        Error("listen() failed");

    addrlen = sizeof(addr);
    if ( (sock = accept(listenSock, (struct sockaddr*) &addr, &addrlen))
        == INVALID_SOCKET )
        Error("accept() failed");

    printf("Connected!\n");

    /* Get file length: */
    if ( !KoolRecv(sock, (unsigned char*) &netLength, 4, 0) )
        Error("recv() failed");
    fileLength = ntohl(netLength);

    /* Get name length: */
    if ( !KoolRecv(sock, &nlen, 1, 0) )
        Error("recv() failed");

    if ( (fileName = malloc(((unsigned)nlen + 1))) == NULL )
        Error("Out of memory");

    /* Get name: */
    if ( !KoolRecv(sock, fileName, nlen, 0) )
        Error("recv() failed");
    fileName[nlen] = 0;

    /* Open da file: */
    if ( (f = fopen(fileName, "wb")) == NULL )
        Error("Unable to open output file");

    /* Get file: */
    fileLeft = fileLength;
    while ( fileLeft )
    {
        printf("%s: %-10u\r", fileName, fileLeft);

        if ( fileLeft > BUFFERSIZE )
            doNow = BUFFERSIZE;
        else
            doNow = fileLeft;

        if ( !KoolRecv(sock, buffer, doNow, 0) )
            Error("recv() failed");
        fwrite(buffer, doNow, 1, f);

        fileLeft -= doNow;
    }

    printf("%s: %-10u\r", fileName, fileLeft);

    fclose(f);
    closesocket(sock);
}



int main(int argc, char *argv[])
{
    int         port;

#ifdef __WIN32__
    WSADATA     wsad;
#endif
    
    setbuf(stdout, NULL);

#ifdef __WIN32__
    WSAStartup( 0x0101, &wsad );
#endif

    if ( (argc < 3) || ((argv[1][0] != 's') && (argv[1][0] != 'r')) )
    {
        fputs("Usage:  flop r port\n        flop s file host port\n", stderr);
        return 1;
    }

    if ( (buffer = malloc(BUFFERSIZE)) == NULL )
        Error("Out of memory");

    if ( argv[1][0] == 's' )
    {
        if ( argc != 5 )
        {
            fputs("Usage:  flop r port\n        flop s file host port\n",
                stderr);
            return 1;
        }

        if ( (port = atoi(argv[4])) == 0 )
            Error("Invalid port");

        Send(argv[2], argv[3], port);
    }
    else
    {
        if ( argc != 3 )
        {
            fputs("Usage:  flop r port\nflop s file host port\n", stderr);
            return 1;
        }

        if ( (port = atoi(argv[2])) == 0 )
            Error("Invalid port");

        Receive(port);
    }

#ifdef __WIN32__
    WSACleanup();
#endif

    free(buffer);

    printf("\n");

    return 0;
}
