OSSP CVS Repository

ossp - ossp-pkg/lmtp2nntp/msg.c 1.2
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/lmtp2nntp/msg.c 1.2

#include <stdlib.h>
#include "msg.h"
#include "str.h"
#include "argz.h"
#include <stdio.h>

msg_t *msg_create(void)
{
    msg_t *msg;

    if ((msg = (msg_t *)malloc(sizeof(msg_t))) == NULL)
        return NULL;

    msg->mail_from = NULL;
    msg->azRcpt = NULL;
    msg->asRcpt = 0;
    msg->azNewsgroups = NULL;
    msg->asNewsgroups = 0;
    msg->azHeaders = NULL;
    msg->asHeaders = 0;
    msg->cpMsg = NULL;
    msg->cpHeaders = NULL;
    msg->cpBody = NULL;
    msg->cpMsgid = NULL;

    return msg;
}

void msg_destroy(msg_t *msg)
{
    if (msg == NULL)
        return;
                                             //FIXME what about non-graceful aborts?
    if (msg->mail_from != NULL)
        free(msg->mail_from);
    if (msg->azRcpt != NULL)
        free(msg->azRcpt);
    if (msg->azHeaders != NULL)
        free(msg->azHeaders);
    if (msg->azNewsgroups != NULL)
        free(msg->azNewsgroups);
    if (msg->cpMsg != NULL)
        free(msg->cpMsg);
    if (msg->cpHeaders != NULL)
        free(msg->cpHeaders);
    if (msg->cpBody != NULL)
        free(msg->cpBody);
    free(msg);
}

msg_rc_t msg_split(msg_t *msg)
{
    char        *cpName;
    char        *cpValue;
    char        *cpRem;
    char        *cp;

    /* INPUTS
     *
     * msg->cpMsg
     * must contain the wholly RFC822 formatted message with native
     * (unescaped) dots at the beginning of a line, the 'From ' envelope,
     * headers, double newline, body, '\0', no trailing dot;
     *
     * OUTPUTS
     *
     * msg->azHeaders, msg->asHeaders
     * contains the headers in argz format, one logical '\0'-terminated line
     * per header which might be wrapped into multiple '\n'-ended physical
     * lines. The "To:" and "Cc:" headers are silently. The "Newsgroups:" and
     * "Message-ID" headers are removed and their values are stored in
     * separate structures (see below).
     *
     * msg->cpBody
     * contains the unmodified body of the message, '\0'-terminated, no
     * trailing dot.
     *
     * msg->cpMsgid
     * contains the message id including surrounding angle brackets.
     *
     * msg->azNewsgroups, asNewsgroups
     * is a argz-type array of strings containing the Newsgroups based on the
     * header information.
     */

    /* split message into header and body */
    if (!str_parse(msg->cpMsg, "m/((?:.*?)\\n)\\n(.*)$/s", &msg->cpHeaders, &msg->cpBody))
        return MSG_ERR_SPLITSPLITBODY;

    /* replace envelope From w/o colon by X-F: pseudotag. This eliminates the
     * special case of having one header, which is really an embedded
     * envelope, not ending with a colon while all other do. After splitting
     * headers into name and value pairs this action is reversed.
     */
    if (strlen(msg->cpHeaders) < 4)
        return MSG_ERR_SPLITLEN;
    if (strncasecmp(msg->cpHeaders, "From", 4) != 0)
        return MSG_ERR_SPLITMISSINGFROM;
    memcpy(msg->cpHeaders, "X-F:", 4);

    /* unwrap header lines */
    //FIXME poor man's s///g simulator as current str library doesn't support //global substitution
    while (str_parse(msg->cpHeaders, "s/(.*?)\\n[ \\t]+(.*)/$1 $2/s", &cpRem)) {
        free(msg->cpHeaders);
        msg->cpHeaders = cpRem;
    }

    /* split header lines into names and values */
    //FIXME str enhancement requests and bugs to be fixed
    //FIXME - fix bug "not" [^...] working
    //FIXME - improve str_parse(foo, "...", &foo) should free foo() on it's own
    //FIXME - add "global" in s/search/replace/g (see above "unwrap hader lines")
    while (str_parse(msg->cpHeaders, "m/^([\\w-]+?:)[ \\t]*(.*?)[ \\t]*\\n(.*)/s", &cpName, &cpValue, &cpRem)) {
        free(msg->cpHeaders);
        msg->cpHeaders = cpRem;
        argz_add(&msg->azHeaders, &msg->asHeaders, cpName);
        argz_add(&msg->azHeaders, &msg->asHeaders, cpValue);
    }

    /* reverse the 'From ' to 'X-F: ' replacement */
    memcpy(msg->azHeaders, "From", 4); /* replace envelope X-F: pseudotag with From w/o colon */

    /* check for headers we care about and do whatever neccessary */
    msg->cpMsgid = NULL;
    msg->azNewsgroups = NULL;
    msg->asNewsgroups = 0;
    cp = msg->azHeaders;
    while (cp != NULL) {
        if (strcasecmp("From", cp) == 0) {
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  name  */
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  value */
            continue;
        }
        if (strcasecmp("To:", cp) == 0) {
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  name  */
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  value */
            continue;
        }
        if (strcasecmp("Cc:", cp) == 0) {
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  name  */
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  value */
            continue;
        }
        if (strcasecmp("Message-ID:", cp) == 0) {
            if (msg->cpMsgid != NULL)
                return MSG_ERR_SPLITIDMULTI;
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  name  */
            //fprintf(stderr, "DEBUG: Message-ID cp = ***%s***\n", cp);
            if ((cp == NULL) || (strlen(cp) == 0))                         /* get  value */
                return MSG_ERR_SPLITIDEMPTY;
            if ((msg->cpMsgid = strdup(cp)) == NULL)
                return MSG_ERR_MEM;
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  value */
            continue;
        }
        if (strcasecmp("Newsgroups:", cp) == 0) {
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  name  */
            //fprintf(stderr, "DEBUG: Newsgroups cp = ***%s***\n", cp);
            if (argz_add(&msg->azNewsgroups, &msg->asNewsgroups, cp) != 0) /* get  value */
                return MSG_ERR_MEM;
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);             /* del  value */
            continue;
        }
        if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL)  /* next value */
            break;
        if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL)  /* next name  */
            break;
    }
    if (msg->cpMsgid == NULL)
        return MSG_ERR_SPLITIDNONE;

    if (msg->azNewsgroups != NULL) {
        argz_stringify(msg->azNewsgroups, msg->asNewsgroups, ',');
        if (argz_create_sep(msg->azNewsgroups, ',', &msg->azNewsgroups, &msg->asNewsgroups) != 0)
            return MSG_ERR_MEM;
    }

    return MSG_OK;
}

msg_rc_t msg_join(msg_t *msg)
{
    char        *cp;
    char        *cpRem;
    char       **aHeaders;
    int          i;
    char        *cpCut;
    char        *cpWrap;
    char        *cpNew;
    char         c;
    int          n;

    /* verify asNewsgroups */
    if (msg->azNewsgroups == NULL)
        return MSG_ERR_JOINGROUPNONE;
    argz_stringify(msg->azNewsgroups, msg->asNewsgroups, ',');
    //fprintf(stderr, "DEBUG: join consolidated azNewsgroups = ***%s***\n", msg->azNewsgroups);
    if (strlen(msg->azNewsgroups) == 0)
        return MSG_ERR_JOINGROUPEMPTY;
    argz_add(&msg->azHeaders, &msg->asHeaders, "Newsgroups:");
    argz_add(&msg->azHeaders, &msg->asHeaders, msg->azNewsgroups);

    /* verify Message-ID */
    if (msg->cpMsgid == NULL)
        return MSG_ERR_JOINIDNONE;
    if (strlen(msg->cpMsgid) == 0)
        return MSG_ERR_JOINIDEMPTY;
    argz_add(&msg->azHeaders, &msg->asHeaders, "Message-ID:");
    argz_add(&msg->azHeaders, &msg->asHeaders, msg->cpMsgid);

    /* merge name/value pairs into single string */
    argz_add(&msg->azHeaders, &msg->asHeaders, ""); /* append empty string */
    if ((aHeaders = (char **)malloc((argz_count(msg->azHeaders,
        msg->asHeaders) + 1) * sizeof(char *))) == NULL)
        exit(1); //FIXME
    argz_extract(msg->azHeaders, msg->asHeaders, aHeaders);
    i=0;
    while(1) {
        if ((cp = aHeaders[++i]) == NULL)
            break;
        *(cp-1) = ' ';
        if ((cp = aHeaders[++i]) == NULL)
            break;
        // *(cp-1) = '\n';
    }

    /* fold headers */
//FIXME where to place this defines best
#define WRAPAT 120
#define WRAPUSING "\n    "
    cp = NULL;
    while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) {
        if (strlen(cp) >= WRAPAT) {
            cpRem = cp;
            cpWrap = NULL;
            while (strlen(cpRem) >= WRAPAT) {
                for (i = WRAPAT; i >= 1 && (cpRem[i] != ' ') && (cpRem[i] != '\t'); i--);
                if (i == 0)
                    i = WRAPAT; /* sorry, hard cut at non-whitespace */
                if (i < WRAPAT)
                    i++; /* we don't care about the whitespace itself */
                cpCut = str_dup(cpRem, i);
                //FIXME 1.) continue searching downwards skipping all whitespaces and 2.) as we know the length replace str_dup/ strcat/ free with strncat only
                if (cpWrap == NULL) {
                    if ((cpWrap = (char *)malloc(strlen(cpCut)+strlen(WRAPUSING)+1)) == NULL)
                        exit(1); //FIXME
                    *cpWrap = '\0';
                }
                else {
                    if ((cpWrap = (char *)realloc(cpWrap, strlen(cpWrap)+strlen(cpCut)+strlen(WRAPUSING)+1)) == NULL)
                        exit(1); //FIXME
                    strcat(cpWrap, WRAPUSING);
                }
                strcat(cpWrap, cpCut);
                free(cpCut);
                cpRem += i;
            }
            for (i = 0; i < strlen(cpRem) && ((cpRem[i] == ' ') || (cpRem[i] == '\t')); i++);
            cpRem += i;
            if (strlen(cpRem) > 0) {
                if ((cpWrap = (char *)realloc(cpWrap, strlen(cpWrap)+strlen(cpRem)+strlen(WRAPUSING)+1)) == NULL)
                    exit(1); //FIXME
                strcat(cpWrap, WRAPUSING);
                strcat(cpWrap, cpRem);
            }
            argz_delete(&msg->azHeaders, &msg->asHeaders, cp);
            argz_insert(&msg->azHeaders, &msg->asHeaders, cp, cpWrap);
            free(cpWrap);
        }
    }

    argz_stringify(msg->azHeaders, msg->asHeaders, '\n');
    msg->cpHeaders = msg->azHeaders;

    if (argz_create_sep(msg->cpBody, '\n', &msg->azBody, &msg->asBody) != 0)
        return MSG_ERR_MEM;

    /* escape dots at the beginning of each line */
    cp = NULL;
    while ((cp =argz_next(msg->azBody, msg->asBody, cp)) != NULL) {
        if (*cp == '.') {
            if ((cpNew = malloc(strlen(cp) + 1)) == NULL)
                return MSG_ERR_MEM;
            *cpNew = '.';
            cpNew++;
            *cpNew = '\0';
            strcat(cpNew, cp);
            argz_delete(&msg->azBody, &msg->asBody, cp);
            argz_insert(&msg->azBody, &msg->asBody, cp, cpNew);
        }
    }

    argz_stringify(msg->azBody, msg->asBody, '\n');
    msg->cpBody = msg->azBody;

    /********************************************************************
     * header + CRLF + body + '.' + CRLF + '\0', replacing NL with CRLF *
     ********************************************************************/

    n = 0;
    /* count size of headers, reserve space for NL to CRLF conversion */
    for (i = 0; ((c = msg->cpHeaders[i]) != '\0'); i++) {
        if (c == '\n')
            n++;
        n++;
    }
    /* if headers don't end with NL, reserve space for CRLF */
    if (i >= 0 && msg->cpHeaders[i - 1] != '\n')
        n+=2;
    /* reserve space for CRLF between headers and body */
    n+=2;
    /* count size of body, reserve space for NL to CRLF conversion */
    for (i = 0; ((c = msg->cpBody[i]) != '\0'); i++) {
        if (c == '\n')
            n++;
        n++;
    }
    /* if body doesn't end with NL, reserve space for CRLF */
    if (i >= 0 && msg->cpBody[i - 1] != '\n')
        n+=2;
    /* reserve space for terminating '.'-CRLF-NUL at the end of the message */
    n+=4;

    if ((msg->cpMsg = (char *)malloc(n)) == NULL)
        return MSG_ERR_MEM;

    n = 0;
    /* copy headers, do NL to CRLF conversion */
    for (i = 0; ((c = msg->cpHeaders[i]) != '\0'); i++) {
        if (c == '\n')
            msg->cpMsg[n++] = '\r';
        msg->cpMsg[n++] = c;
    }
    /* if headers don't end with NL, append CRLF */
    if (i >= 0 && msg->cpHeaders[i - 1] != '\n') {
        msg->cpMsg[n++] = '\r';
        msg->cpMsg[n++] = '\n';
    }
    /* add CRLF between headers and body */
    msg->cpMsg[n++] = '\r';
    msg->cpMsg[n++] = '\n';
    /* copy body, do NL to CRLF conversion */
    for (i = 0; ((c = msg->cpBody[i]) != '\0'); i++) {
        if (c == '\n')
            msg->cpMsg[n++] = '\r';
        msg->cpMsg[n++] = c;
    }
    /* if body doesn't end with NL, append CRLF */
    if (i >= 0 && msg->cpBody[i - 1] != '\n') {
        msg->cpMsg[n++] = '\r';
        msg->cpMsg[n++] = '\n';
    }
    /* add terminating '.'-CRLF-NUL at the end of the message */
    msg->cpMsg[n++] =  '.';
    msg->cpMsg[n++] = '\r';
    msg->cpMsg[n++] = '\n';
    msg->cpMsg[n]   = '\0';

    //fprintf(stderr, "DEBUG: Message = ***%s***\n", msg->cpMsg);

    return MSG_OK;
}

char *msg_error(msg_t *msg, msg_rc_t rc)
{
    char *str;
                                              str = "MSG: no description";
    if      (rc == MSG_OK                   ) str = "MSG: no error";
    else if (rc == MSG_ERR_MEM              ) str = "MSG: memory";
    else if (rc == MSG_ERR_SPLITSPLITBODY   ) str = "MSG: split into header and body failed";
    else if (rc == MSG_ERR_SPLITLEN         ) str = "MSG: header is too short";
    else if (rc == MSG_ERR_SPLITMISSINGFROM ) str = "MSG: header is missing 'From ' envelope";
    else if (rc == MSG_ERR_SPLITIDNONE      ) str = "MSG: header is missing 'Message-ID'";
    else if (rc == MSG_ERR_SPLITIDEMPTY     ) str = "MSG: header has empty 'Message-ID'";
    else if (rc == MSG_ERR_SPLITIDMULTI     ) str = "MSG: header has multiple 'Message-ID's";
    else if (rc == MSG_ERR_JOINGROUPNONE    ) str = "MSG: join with no 'Newsgroup'";
    else if (rc == MSG_ERR_JOINGROUPEMPTY   ) str = "MSG: join with empty 'Newsgroup'";
    else if (rc == MSG_ERR_JOINIDNONE       ) str = "MSG: join with no 'Message-ID'";
    else if (rc == MSG_ERR_JOINIDEMPTY      ) str = "MSG: join with empty 'Message-ID'";
    return str;
}


CVSTrac 2.0.1