📄 msg_mime.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@ingroup msg_mime * @CFILE msg_mime.c * * MIME-related headers and MIME multipart bodies for SIP/HTTP/RTSP. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Tue Jun 13 02:57:51 2000 ppessi * * */#include "config.h"#define _GNU_SOURCE 1#include <stddef.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <errno.h>#include <assert.h>#include <sofia-sip/su_alloc.h>#include "msg_internal.h"#include "sofia-sip/msg.h"#include "sofia-sip/msg_mime.h"#include <sofia-sip/su_uniqueid.h>#include <sofia-sip/su_errno.h>#if !HAVE_MEMMEMvoid *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);#endifsize_t memspn(const void *mem, size_t memlen, const void *accept, size_t acceptlen);size_t memcspn(const void *mem, size_t memlen, const void *reject, size_t rejectlen);/** Protocol version of MIME */char const msg_mime_version_1_0[] = "MIME/1.0";#include <sofia-sip/msg_parser.h>#include <sofia-sip/msg_mime_protos.h>/** Define a header class for headers without any extra data to copy */#define MSG_HEADER_CLASS_G(c, l, s, kind) \ MSG_HEADER_CLASS(msg_, c, l, s, g_common, kind, msg_generic, msg_generic)#define msg_generic_update NULL/** Define a header class for a msg_list_t kind of header */#define MSG_HEADER_CLASS_LIST(c, l, s, kind) \ MSG_HEADER_CLASS(msg_, c, l, s, k_items, kind, msg_list, msg_list)#define msg_list_update NULL/* ====================================================================== *//** Calculate length of line ending (0, 1 or 2). @internal */#define CRLF_TEST(b) ((b)[0] == '\r' ? ((b)[1] == '\n') + 1 : (b)[0] =='\n')/**@ingroup msg_mime * @defgroup msg_multipart MIME Multipart Body * * Representing MIME multipart bodies and their manipulation. * * The #msg_multipart_t is an object for storing MIME multipart message * bodies. It includes message components used for framing and identifying * message parts. Its syntax is defined in @RFC2046 as follows: * * @code * * multipart-body := [preamble CRLF] * dash-boundary transport-padding CRLF * body-part *encapsulation * close-delimiter transport-padding * [CRLF epilogue] * * preamble := discard-text * * discard-text := *(*text CRLF) * ; May be ignored or discarded. * * dash-boundary := "--" boundary * ; boundary taken from the value of boundary parameter * ; of the Content-Type field. * * boundary := 0*69<bchars> bcharsnospace * * bchars := bcharsnospace / " " * * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / * "+" / "_" / "," / "-" / "." / * "/" / ":" / "=" / "?" * * transport-padding := *LWSP-char * ; Composers MUST NOT generate non-zero length * ; transport padding, but receivers MUST be able to * ; handle padding added by message transports. * * body-part := <"message" as defined in @RFC822, with all header fields * optional, not starting with the specified dash-boundary, * and with the delimiter not occurring anywhere in the body * part. Note that the semantics of a part differ from the * semantics of a message, as described in the text.> * * encapsulation := delimiter transport-padding CRLF * body-part * * close-delimiter := delimiter "--" * * delimiter := CRLF dash-boundary * * epilogue := discard-text * * @endcode * * @par Parsing a Multipart Message * * When a message body contains a multipart entity (in other words, it has a * MIME media type of "multipart"), the application can split the multipart * entity into body parts * * The parsing is relatively simple, the application just gives a memory * home object, a Content-Type header object and message body object as an * argument to msg_multipart_parse() function: * @code * if (sip->sip_content_type && * strncasecmp(sip->sip_content_type, "multipart/", 10) == 0) { * msg_multipart_t *mp; * * if (sip->sip_multipart) * mp = sip->sip_multipart; * else * mp = msg_multipart_parse(msg_home(msg), * sip->sip_content_type, * (sip_payload_t *)sip->sip_payload); * * if (mp) * ... processing multipart ... * else * ... error handling ... * } * @endcode * * The resulting list of msg_multipart_t structures contain the parts of the * multipart entity, each part represented by a separate #msg_multipart_t * structure. Please note that in order to make error recovery possible, the * parsing is not recursive - if multipart contains another multipart, the * application is responsible for scanning for it and parsing it. * * @par Constructing a Multipart Message * * Constructing a multipart body is a bit more hairy. The application needs * a message object (#msg_t), which is used to buffer the encoding of * multipart components. * * As an example, let us create a "multipart/mixed" multipart entity with a * HTML and GIF contents, and convert it into a #sip_payload_t structure: * @code * msg_t *msg = msg_create(sip_default_mclass, 0); * su_home_t *home = msg_home(msg); * sip_t *sip = sip_object(msg); * sip_content_type_t *c; * msg_multipart_t *mp = NULL; * msg_header_t *h = NULL; * char *b; * size_t len, offset; * * mp = msg_multipart_create(home, "text/html;level=3", html, strlen(html)); * mp->mp_next = msg_multipart_create(home, "image/gif", gif, giflen); * * c = sip_content_type_make(home, "multipart/mixed"); * * // Add delimiters to multipart, and boundary parameter to content-type * if (msg_multipart_complete(home, c, mp) < 0) * return -1; // Error * * // Combine multipart components into the chain * h = NULL; * if (msg_multipart_serialize(&h, mp) < 0) * return -1; // Error * * // Encode all multipart components * len = msg_multipart_prepare(msg, mp, 0); * if (len < 0) * return -1; // Error * * pl = sip_payload_create(home, NULL, len); * * // Copy each element from multipart to pl_data * b = pl->pl_data; * for (offset = 0, h = mp; offset < len; h = h->sh_succ) { * memcpy(b + offset, h->sh_data, h->sh_len); * offset += h->sh_len; * } * @endcode * *//**Create a part for MIME multipart entity. * * The function msg_multipart_create() allocates a new #msg_multipart_t * object from memory home @a home. If @a content_type is non-NULL, it makes * a #msg_content_type_t header object and adds the header to the * #msg_multipart_t object. If @a dlen is nonzero, it allocates a * msg_payload_t structure of @a dlen bytes for the payload of the newly * created #msg_multipart_t object. If @a data is non-NULL, it copies the @a * dlen bytes of of data to the payload of the newly created * #msg_multipart_t object. * * @return A pointer to the newly created #msg_multipart_t object, or NULL * upon an error. */msg_multipart_t *msg_multipart_create(su_home_t *home, char const *content_type, void const *data, isize_t dlen){ msg_multipart_t *mp; mp = (msg_multipart_t *)msg_header_alloc(home, msg_multipart_class, 0); if (mp) { if (content_type) mp->mp_content_type = msg_content_type_make(home, content_type); if (dlen) mp->mp_payload = msg_payload_create(home, data, dlen); if ((!mp->mp_content_type && content_type) || (!mp->mp_payload && dlen)) { su_free(home, mp->mp_content_type); su_free(home, mp->mp_payload); su_free(home, mp); mp = NULL; } } return mp;}/** Convert boundary parameter to a search string. */static char *msg_multipart_boundary(su_home_t *home, char const *b){ char *boundary; if (!b || !(boundary = su_alloc(home, 2 + 2 + strlen(b) + 2 + 1))) return NULL; strcpy(boundary, CR LF "--"); if (b[0] == '"') /* " See http://bugzilla.gnome.org/show_bug.cgi?id=134216 */ msg_unquote(boundary + 4, b); else strcpy(boundary + 4, b); strcat(boundary + 4, CR LF); return boundary;}/** Boundary chars. */static char const bchars[] = "'()+_,-./:=?""0123456789""ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz"" ";#define bchars_len (sizeof(bchars) - 1)/** Search for a suitable boundary from MIME. */static char *msg_multipart_search_boundary(su_home_t *home, char const *p, size_t len){ size_t m; unsigned crlf; char const *end = p + len; char *boundary; if (len < 2) return NULL; /* Boundary looks like LF -- string SP* [CR] LF */ if (memcmp("--", p, 2) == 0) { /* We can be at boundary beginning, there is no CR LF */ m = 2 + memspn(p + 2, len - 2, bchars, bchars_len); if (m + 2 >= len) return NULL; crlf = p[m] == '\r' ? 1 + (p[m + 1] == '\n') : (p[m] == '\n'); while (p[m - 1] == ' ' || p[m - 1] == '\t') m--; if (m > 2 && crlf) { boundary = su_alloc(home, 2 + m + 2 + 1); if (boundary) { memcpy(boundary, CR LF, 2); memcpy(boundary + 2, p, m); strcpy(boundary + m + 2, CR LF); } return boundary; } } /* Look for LF -- */ for (;(p = memmem(p, end - p, LF "--", 3)); p += 3) { len = end - p; m = 3 + memspn(p + 3, len - 3, bchars, bchars_len); if (m + 2 >= len) return NULL; crlf = p[m] == '\r' ? 1 + (p[m + 1] == '\n') : (p[m] == '\n'); while (p[m - 1] == ' ' || p[m - 1] == '\t') m--; m--; if (m > 2 && crlf) { boundary = su_alloc(home, 2 + m + 2 + 1); if (boundary) { memcpy(boundary, CR LF, 2); memcpy(boundary + 2, p + 1, m); strcpy(boundary + 2 + m, CR LF); } return boundary; } } return NULL;}/** Parse a MIME multipart. * * The function msg_multipart_parse() parses a MIME multipart message. The * common syntax of multiparts is described in @RFC2046 (section 7). * * @param[in,out] home home for allocating structures * @param[in] c content-type header for multipart * @param[in] pl payload structure for multipart * * After parsing, the @a pl will contain the plain-text preamble (if any). * * @note If no @b Content-Type header is given, the msg_multipart_parse() * tries to look for a suitable boundary. Currently, it takes first * boundary-looking string and uses that, so it can be fooled with, for * instance, signature @c "--Pekka". */msg_multipart_t *msg_multipart_parse(su_home_t *home, msg_content_type_t const *c, msg_payload_t *pl){ msg_multipart_t *mp = NULL, *all = NULL, **mmp = &all; /* Dummy msg object */ msg_t msg[1] = {{{ SU_HOME_INIT(msg) }}}; size_t len, m, blen; char *boundary, *p, *next, save; char const *b, *end; msg_param_t param; p = pl->pl_data; len = pl->pl_len; end = p + len; su_home_init(msg_home(msg)); msg->m_class = msg_multipart_mclass; msg->m_tail = &msg->m_chain; /* Get boundary from Content-Type */ if (c && (param = msg_header_find_param(c->c_common, "boundary="))) boundary = msg_multipart_boundary(msg_home(msg), param); else boundary = msg_multipart_search_boundary(msg_home(msg), p, len); if (!boundary) return NULL; m = strlen(boundary) - 2, blen = m - 1; /* Find first delimiter */ if (memcmp(boundary + 2, p, m - 2) == 0) b = p, p = p + m - 2, len -= m - 2; else if ((p = memmem(p, len, boundary + 1, m - 1))) { if (p != pl->pl_data && p[-1] == '\r') b = --p, p = p + m, len -= m; else b = p, p = p + m - 1, len -= m - 1; } else { su_home_deinit(msg_home(msg)); return NULL; } /* Split multipart into parts */ for (;;) { while (p[0] == ' ') p++; p += p[0] == '\r' ? 1 + (p[1] == '\n') : (p[0] == '\n'); len = end - p; if (len < blen) break; next = memmem(p, len, boundary + 1, m = blen); if (!next) break; /* error */ if (next != p && next[-1] == '\r') next--, m++; mp = (msg_multipart_t *)msg_header_alloc(msg_home(msg), msg_multipart_class, 0); if (mp == NULL) break; /* error */ *mmp = mp; mmp = &mp->mp_next; /* Put delimiter transport-padding CRLF here */ mp->mp_common->h_data = b; mp->mp_common->h_len = p - b; /* .. and body-part here */ mp->mp_data = p; mp->mp_len = next - p; if (next[m] == '-' && next[m + 1] == '-') { /* We found close-delimiter */ assert(mp); if (!mp) break; /* error */ mp->mp_close_delim = (msg_payload_t *) msg_header_alloc(msg_home(msg), msg_payload_class, 0); if (!mp->mp_close_delim) break; /* error */ /* Include also transport-padding and epilogue in the close-delimiter */ mp->mp_close_delim->pl_data = next; mp->mp_close_delim->pl_len = p + len - next; break; } b = next; p = next + m; } if (!mp || !mp->mp_close_delim) { su_home_deinit(msg_home(msg)); /* Delimiter error */ return NULL; } /* Parse each part */ for (mp = all; mp; mp = mp->mp_next) { msg->m_object = (msg_pub_t *)mp; p = mp->mp_data; next = p + mp->mp_len; if (msg->m_tail) mp->mp_common->h_prev = msg->m_tail, *msg->m_tail = (msg_header_t *)mp; msg->m_chain = (msg_header_t *)mp; msg->m_tail = &mp->mp_common->h_succ; save = *next; *next = '\0'; /* NUL-terminate this part */ for (len = next - p; len > 0; len -= m, p += m) { if (IS_CRLF(p[0])) { m = msg_extract_separator(msg, (msg_pub_t*)mp, p, len, 1); assert(m > 0); p += m; len -= m; if (len > 0) { m = msg_extract_payload(msg, (msg_pub_t*)mp, NULL, len, p, len, 1); assert(m > 0); assert(len == m); } break; } m = msg_extract_header(msg, (msg_pub_t*)mp, p, len, 1); if (m <= 0) { assert(m > 0); /* Xyzzy */ } } *next = save; /* XXX - Should we leave the payload NUL-terminated? */ } /* Postprocess */ blen = strlen(boundary); for (mp = all; mp; mp = mp->mp_next) { mp->mp_data = boundary; mp->mp_len = blen; assert(mp->mp_payload || mp->mp_separator); if (mp->mp_close_delim) { msg_header_t **tail; if (mp->mp_payload) tail = &mp->mp_payload->pl_common->h_succ; else tail = &mp->mp_separator->sep_common->h_succ; assert(msg->m_chain == (msg_header_t *)mp); assert(*tail == NULL); mp->mp_close_delim->pl_common->h_prev = tail; *tail = (msg_header_t *)mp->mp_close_delim; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -