PRIVATE char *intlmime_decode_qp(char *in)
{
    int i = 0, length;
    char token[3];
    char *out, *dest = 0;

    out = dest = (char *)XP_ALLOC(strlen(in)+1);
    if (dest == NULL)
        return NULL;
    memset(out, 0, strlen(in)+1);
    length = strlen(in);
    while (length > 0 || i != 0)
    {
        while (i < 3 && length > 0)
        {
            token [i++] = *in;
            in++;
            length--;
        }

        if (i < 3)
        {
          /* Didn't get enough for a complete token.
             If it might be a token, unread it.
             Otherwise, just dump it.
             */
            strncpy (out, token, i);
            break;
        }
        i = 0;

        if (token [0] == '=')
        {
            unsigned char c = 0;
            if (token[1] >= '0' && token[1] <= '9')
                c = token[1] - '0';
            else if (token[1] >= 'A' && token[1] <= 'F')
                c = token[1] - ('A' - 10);
            else if (token[1] >= 'a' && token[1] <= 'f')
                c = token[1] - ('a' - 10);
            else if (token[1] == CR || token[1] == LF)
            {
                /* =\n means ignore the newline. */
                if (token[1] == CR && token[2] == LF)
                    ;       /* swallow all three chars */
                else
                {
                    in--;   /* put the third char back */
                    length++;
                }
                continue;
            }
            else
            {
                /* = followed by something other than hex or newline -
                 pass it through unaltered, I guess.  (But, if
                 this bogus token happened to occur over a buffer
                 boundary, we can't do this, since we don't have
                 space for it.  Oh well.  Forget it.)  */
                if (in > out) *out++ = token[0];
                if (in > out) *out++ = token[1];
                if (in > out) *out++ = token[2];
                continue;
            }

            /* Second hex digit */
            c = (c << 4);
            if (token[2] >= '0' && token[2] <= '9')
                c += token[2] - '0';
            else if (token[2] >= 'A' && token[2] <= 'F')
                c += token[2] - ('A' - 10);
            else if (token[2] >= 'a' && token[2] <= 'f')
                c += token[2] - ('a' - 10);
            else
            {
                /* We got =xy where "x" was hex and "y" was not, so
                 treat that as a literal "=", x, and y.  (But, if
                 this bogus token happened to occur over a buffer
                 boundary, we can't do this, since we don't have
                 space for it.  Oh well.  Forget it.) */
                if (in > out) *out++ = token[0];
                if (in > out) *out++ = token[1];
                if (in > out) *out++ = token[2];
                continue;
            }

            *out++ = (char) c;
        }
        else
        {
            *out++ = token [0];

            token[0] = token[1];
            token[1] = token[2];
            i = 2;
        }
    }
    /* take care of special underscore case */
    for (out = dest; *out; out++)
        if (*out == '_')    *out = ' ';
    return dest;
}