/*****************************************************************************/
/* Mime.h                                 Copyright (c) Ladislav Zezula 2021 */
/*---------------------------------------------------------------------------*/
/* MIME parsing functions for CascLib                                        */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 21.01.21  1.00  Lad  Created                                              */
/*****************************************************************************/

#ifndef __MIME_H__
#define __MIME_H__

//-----------------------------------------------------------------------------
// MIME constants

#define MAX_LENGTH_BOUNDARY 128

// Flags returned by CASC_MIME_HTTP::GetHttpReplyFlags()
#define HTTP_HEADER_COMPLETE    0x01            // HTML header is complete
#define HTTP_DATA_COMPLETE      0x02            // HTML data is complete

enum CASC_MIME_ENCODING
{
    MimeEncodingTextPlain,
    MimeEncodingBase64,
    MimeEncodingQuotedPrintable,
    MimeEncodingMax
};

enum CASC_PRESENCE
{
    FieldPresenceUnknown,
    FieldPresencePresent,
    FieldPresenceNotPresent
};

//-----------------------------------------------------------------------------
// Structure for caching parsed HTTP response information

struct CASC_MIME_RESPONSE
{
    CASC_MIME_RESPONSE()
    {
        header_offset = header_length = CASC_INVALID_SIZE_T;
        content_offset = content_length = CASC_INVALID_SIZE_T;
        http_code = CASC_INVALID_SIZE_T;
        clength_presence = http_presence = FieldPresenceUnknown;
        response_length = 0;
    }

    bool ParseResponse(const char * response, size_t length, bool final = false);

    size_t response_length;             // Previous length of the response
    size_t header_offset;               // Offset of the response header, usually 0
    size_t header_length;               // Length of the header, if known
    size_t content_offset;              // Offset of the content
    size_t content_length;              // Length of the content, if known
    size_t http_code;                   // HTTP code, if present
    CASC_PRESENCE clength_presence;     // State of the "content length" field
    CASC_PRESENCE http_presence;        // Presence of the "HTTP" field
};

//-----------------------------------------------------------------------------
// MIME blob class

struct CASC_MIME_BLOB
{
    CASC_MIME_BLOB(char * mime_ptr, char * mime_end);
    ~CASC_MIME_BLOB();

    char * GetNextLine();

    char * ptr;
    char * end;
};

//-----------------------------------------------------------------------------
// MIME class

class CASC_MIME_ELEMENT
{
    public:

    CASC_MIME_ELEMENT();
    ~CASC_MIME_ELEMENT();

    DWORD GiveAway(CASC_BLOB & target);

    DWORD LoadSingle(char * data, size_t data_length);
    DWORD Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr = NULL);

    CASC_MIME_ELEMENT * GetChild()  { return folder.pChild; }

#ifdef CASCLIB_DEBUG
    void Print(size_t nLevel, size_t nIndex);
#endif

    protected:

    CASC_MIME_ELEMENT * AllocateAndLoadElement(char * a_mime_data, char * a_mime_data_end, const char * boundary_begin);
    bool   ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding);
    bool   ExtractBoundary(const char * line);

    DWORD DecodeTextPlain(char * content_begin, char * content_end, CASC_BLOB & output);
    DWORD DecodeQuotedPrintable(char * content_begin, char * content_end, CASC_BLOB & output);
    DWORD DecodeBase64(char * content_begin, char * content_end, CASC_BLOB & output);

    struct
    {
        CASC_MIME_ELEMENT * pChild;     // Pointer to the first child
        CASC_MIME_ELEMENT * pNext;      // Pointer to the next-in-folder element
    } folder;

    CASC_BLOB data;
    char boundary[MAX_LENGTH_BOUNDARY];
};

class CASC_MIME
{
    public:

    CASC_MIME();
    ~CASC_MIME();

    DWORD GiveAway(CASC_BLOB & target);

    DWORD Load(char * data, CASC_MIME_RESPONSE & MimeResponse);

#ifdef CASCLIB_DEBUG
    void Print();
#endif

    protected:

    CASC_MIME_ELEMENT root;
};

#endif // __MIME_H__
