/* $Cambridge: hermes/src/prayer/accountd/file.c,v 1.2 2008/09/16 09:59:54 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2002 */
/* See the file NOTICE for conditions of use and distribution. */

#include "accountd.h"

/* file_checkname() ******************************************************
 *
 * Run saniy check on path. Looking for /..
 *    s: Filename to check
 *
 * Returns: T if name passes sanity check.
 ************************************************************************/

static BOOL file_checkname(char *s)
{
    if (*s == '/')
        return (NIL);

    while (*s) {
        if ((s[0] == '.') && (s[1] == '.'))
            return (NIL);
        s++;
    }
    return (T);
}

/* file_get() ************************************************************
 *
 * Get named file from peer.
 *   config: Accountd configuration
 *   stream: write file to this stream
 *     line: Line following GET. Should be:
 *             canon encoded filename e.g: Hello%20World
 *
 * Returns status on stream:
 *   OK {size}  - File download 
 *   NO (Text)  - Couldn't download file (followed by reason)
 *  BAD (Text)  - Protocol error
 *
 ************************************************************************/

BOOL file_get(struct config * config, struct iostream * stream, char *line)
{
    char *name;
    FILE *file;
    struct stat sbuf;
    int c;

    if (!((name = string_get_token(&line)) && (name[0]))) {
        ioputs(stream, "BAD No filename provided" CRLF);
        ioflush(stream);
        return (T);
    }

    string_canon_decode(name);

    if (!file_checkname(name)) {
        ioputs(stream, "NO Invalid file name" CRLF);
        ioflush(stream);
        return (T);
    }

    if (stat(name, &sbuf)) {
        ioputs(stream, "NO No such file" CRLF);
        ioflush(stream);
        return (T);
    }

    if (!(sbuf.st_mode & S_IFREG)) {
        ioputs(stream, "NO Not a regular file" CRLF);
        ioflush(stream);
        return (T);
    }

    if ((file = fopen(name, "r")) == NIL) {
        ioputs(stream, "NO Couldn't open file" CRLF);
        ioflush(stream);
        return (T);
    }

    ioprintf(stream, "OK {%lu}" CRLF, (unsigned long) sbuf.st_size);

    while ((c = getc(file)) != EOF)
        ioputc(c, stream);

    ioputs(stream, "" CRLF);
    ioflush(stream);
    fclose(file);
    return (T);
}

/* file_put() ************************************************************
 *
 * Upload named file from peer.
 *   config: Accountd configuration
 *   stream: write file to this stream
 *     line: Line following PUT command. Should be:
 *              canon encoded filename e.g: Hello%20World
 *              Size of following literal followed by CRLF
 *
 * Returns status on stream:
 *   OK (Text)  - File upload succeeded
 *   NO (Text)  - Couldn't upload file (followed by reason)
 *  BAD (Text)  - Protocol error
 ************************************************************************/

BOOL file_put(struct config * config, struct iostream * stream, char *line)
{
    char *name, *size;
    FILE *file;
    int c, len;

    if (!((name = string_get_token(&line)) && (name[0]))) {
        ioputs(stream, "BAD No filename provided" CRLF);
        ioflush(stream);
        return (T);
    }

    string_canon_decode(name);

    if (!((size = string_get_token(&line)) && (size[0]))) {
        ioputs(stream, "BAD No file size provided" CRLF);
        ioflush(stream);
        return (T);
    }

    if (((len = strlen(size)) < 2) ||
        (size[0] != '{') || (size[len - 1] != '}')) {
        ioputs(stream, "BAD Invalid file size" CRLF);
        ioflush(stream);
        return (T);
    }
    /* Check that size[1] -> size[len-1] all digits? */
    size[len - 1] = '\0';       /* Probably not needed */

    len = atoi(size + 1);

    if (!file_checkname(name)) {
        /* Swallow unwanted text */
        while ((len > 0) && (c = iogetc(stream)))
            len--;

        ioputs(stream, "NO Invalid file name" CRLF);
        ioflush(stream);
        return (T);
    }

    if ((file = fopen(name, "w")) == NIL) {
        /* Swallow unwanted text */
        while ((len > 0) && (c = iogetc(stream)))
            len--;

        ioputs(stream, "NO Couldn't open file" CRLF);
        ioflush(stream);
        return (T);
    }

    while ((len > 0) && (c = iogetc(stream))) {
        putc(c, file);
        len--;
    }

    if (len > 0) {
        ioprintf(stream, "BAD %d bytes missing from input" CRLF, len);
        ioflush(stream);
        return (T);
    }

    ioputs(stream, "OK File uploaded" CRLF);
    ioflush(stream);
    fclose(file);
    return (T);
}
