/** **************************************************************************
 * response_headers_handler.c
 * 
 * Copyright 2008 Bryan Ischo <bryan@ischo.com>
 * 
 * This file is part of libs3.
 * 
 * libs3 is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, version 3 of the License.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of this library and its programs with the
 * OpenSSL library, and distribute linked combinations including the two.
 *
 * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License version 3
 * along with libs3, in a file named COPYING.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 ************************************************************************** **/

#include <ctype.h>
#include <string.h>
#include "response_headers_handler.h"


void response_headers_handler_initialize(ResponseHeadersHandler *handler)
{
    handler->responseProperties.requestId = 0;
    handler->responseProperties.requestId2 = 0;
    handler->responseProperties.contentType = 0;
    handler->responseProperties.contentLength = 0;
    handler->responseProperties.server = 0;
    handler->responseProperties.eTag = 0;
    handler->responseProperties.lastModified = -1;
    handler->responseProperties.metaDataCount = 0;
    handler->responseProperties.metaData = 0;
    handler->done = 0;
    string_multibuffer_initialize(handler->responsePropertyStrings);
    string_multibuffer_initialize(handler->responseMetaDataStrings);
}


void response_headers_handler_add(ResponseHeadersHandler *handler,
                                  char *header, int len)
{
    S3ResponseProperties *responseProperties = &(handler->responseProperties);
    char *end = &(header[len]);
    
    // Curl might call back the header function after the body has been
    // received, for 'chunked encoded' contents.  We don't handle this as of
    // yet, and it's not clear that it would ever be useful.
    if (handler->done) {
        return;
    }

    // If we've already filled up the response headers, ignore this data.
    // This sucks, but it shouldn't happen - S3 should not be sending back
    // really long headers.
    if (handler->responsePropertyStringsSize == 
        (sizeof(handler->responsePropertyStrings) - 1)) {
        return;
    }

    // It should not be possible to have a header line less than 3 long
    if (len < 3) {
        return;
    }

    // Skip whitespace at beginning of header; there never should be any,
    // but just to be safe
    while (is_blank(*header)) {
        header++;
    }

    // The header must end in \r\n, so skip back over it, and also over any
    // trailing whitespace
    end -= 3;
    while ((end > header) && is_blank(*end)) {
        end--;
    }
    if (!is_blank(*end)) {
        end++;
    }

    if (end == header) {
        // totally bogus
        return;
    }

    *end = 0;
    
    // Find the colon to split the header up
    char *c = header;
    while (*c && (*c != ':')) {
        c++;
    }
    
    int namelen = c - header;

    // Now walk c past the colon
    c++;
    // Now skip whitespace to the beginning of the value
    while (is_blank(*c)) {
        c++;
    }

    int valuelen = (end - c) + 1, fit;

    if (!strncmp(header, "x-amz-request-id", namelen)) {
        responseProperties->requestId = 
            string_multibuffer_current(handler->responsePropertyStrings);
        string_multibuffer_add(handler->responsePropertyStrings, c, 
                               valuelen, fit);
    }
    else if (!strncmp(header, "x-amz-id-2", namelen)) {
        responseProperties->requestId2 = 
            string_multibuffer_current(handler->responsePropertyStrings);
        string_multibuffer_add(handler->responsePropertyStrings, c, 
                               valuelen, fit);
    }
    else if (!strncmp(header, "Content-Type", namelen)) {
        responseProperties->contentType = 
            string_multibuffer_current(handler->responsePropertyStrings);
        string_multibuffer_add(handler->responsePropertyStrings, c, 
                               valuelen, fit);
    }
    else if (!strncmp(header, "Content-Length", namelen)) {
        handler->responseProperties.contentLength = 0;
        while (*c) {
            handler->responseProperties.contentLength *= 10;
            handler->responseProperties.contentLength += (*c++ - '0');
        }
    }
    else if (!strncmp(header, "Server", namelen)) {
        responseProperties->server = 
            string_multibuffer_current(handler->responsePropertyStrings);
        string_multibuffer_add(handler->responsePropertyStrings, c, 
                               valuelen, fit);
    }
    else if (!strncmp(header, "ETag", namelen)) {
        responseProperties->eTag = 
            string_multibuffer_current(handler->responsePropertyStrings);
        string_multibuffer_add(handler->responsePropertyStrings, c, 
                               valuelen, fit);
    }
    else if (!strncmp(header, S3_METADATA_HEADER_NAME_PREFIX, 
                      sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) {
        // Make sure there is room for another x-amz-meta header
        if (handler->responseProperties.metaDataCount ==
            sizeof(handler->responseMetaData)) {
            return;
        }
        // Copy the name in
        char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]);
        int metaNameLen = 
            (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1));
        char *copiedName = 
            string_multibuffer_current(handler->responseMetaDataStrings);
        string_multibuffer_add(handler->responseMetaDataStrings, metaName,
                               metaNameLen, fit);
        if (!fit) {
            return;
        }

        // Copy the value in
        char *copiedValue = 
            string_multibuffer_current(handler->responseMetaDataStrings);
        string_multibuffer_add(handler->responseMetaDataStrings,
                               c, valuelen, fit);
        if (!fit) {
            return;
        }

        if (!handler->responseProperties.metaDataCount) {
            handler->responseProperties.metaData = 
                handler->responseMetaData;
        }

        S3NameValue *metaHeader = 
            &(handler->responseMetaData
              [handler->responseProperties.metaDataCount++]);
        metaHeader->name = copiedName;
        metaHeader->value = copiedValue;
    }
}


void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl)
{
    // Now get the last modification time from curl, since it's easiest to let
    // curl parse it
    time_t lastModified;
    if (curl_easy_getinfo
        (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) {
        handler->responseProperties.lastModified = lastModified;
    }
    
    handler->done = 1;
}
