diff options
author | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-01-19 00:19:29 +0000 |
---|---|---|
committer | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-01-19 00:19:29 +0000 |
commit | f8247040e6231c4b3b5099ea3a526348b7941566 (patch) | |
tree | 0cc92ad6ebf6ae49a62f6e7ef8ec819121d63630 /trunk/main/minimime | |
parent | d88e56c61ce2042544c1a8a71c93b69ab2e6ffba (diff) |
Creating tag for the release of asterisk-1.6.0-beta1v1.6.0-beta1
git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.6.0-beta1@99163 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'trunk/main/minimime')
43 files changed, 13547 insertions, 0 deletions
diff --git a/trunk/main/minimime/Make.conf b/trunk/main/minimime/Make.conf new file mode 100644 index 000000000..de149932a --- /dev/null +++ b/trunk/main/minimime/Make.conf @@ -0,0 +1,7 @@ +CC=gcc +PREFIX=/usr +LIBNAME=libmmime.so.0.0 +HAVE_STRLCAT= +HAVE_STRLCPY= +INSTALL=/usr/bin/install +HAVE_DEBUG=1 diff --git a/trunk/main/minimime/Makefile b/trunk/main/minimime/Makefile new file mode 100644 index 000000000..555d17a04 --- /dev/null +++ b/trunk/main/minimime/Makefile @@ -0,0 +1,67 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Makefile for resource modules +# +# Copyright (C) 2007, Digium, Inc. +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps + +include $(ASTTOPDIR)/Makefile.moddir_rules + +LIBMMIME:=libmmime.a +MM_SRCS= \ + mimeparser.tab.c \ + mimeparser.yy.c \ + mm_init.c \ + mm_base64.c \ + mm_codecs.c \ + mm_contenttype.c \ + mm_context.c \ + mm_envelope.c \ + mm_error.c \ + mm_header.c \ + mm_mem.c \ + mm_mimepart.c \ + mm_mimeutil.c \ + mm_param.c \ + mm_parse.c \ + mm_util.c + +MM_OBJS:=$(MM_SRCS:%.c=%.o) +MM_HDRS:=mm.h mm_util.h + +# Use weaker error checking because we have some automatically generated +# files. However just mask out -Werror, because other warnings below: +# -Wundef -Wstrict-prototypes -Wmissing-declarations +# -Wmissing-prototypes +# may actually be important and spot out real bugs. +ASTCFLAGS:=$(filter-out -Werror,$(ASTCFLAGS)) + +ASTCFLAGS+=-std=c99 + +all: $(LIBMMIME) + +$(LIBMMIME): $(MM_OBJS) + $(ECHO_PREFIX) echo " [AR] $^ -> $@" + $(CMD_PREFIX) $(AR) cr $@ $^ + $(CMD_PREFIX) $(RANLIB) $@ + +mimeparser.yy.c: + flex -Pmimeparser_yy -omimeparser.yy.c mimeparser.l + +mimeparser.tab.c: + bison -d -pmimeparser_yy -omimeparser.tab.c mimeparser.y + +clean:: + rm -f $(LIBMMIME) *.o + +.PHONY: clean all + +ifneq ($(wildcard .*.d),) + include .*.d +endif diff --git a/trunk/main/minimime/mimeparser.h b/trunk/main/minimime/mimeparser.h new file mode 100644 index 000000000..f6bfbaad7 --- /dev/null +++ b/trunk/main/minimime/mimeparser.h @@ -0,0 +1,76 @@ +#ifndef _MIMEPARSER_H_INCLUDED +#define _MIMEPARSER_H_INCLUDED + +#include "mm.h" + +struct s_position +{ + size_t opaque_start; + size_t start; + size_t end; +}; + +struct lexer_state +{ + int header_state; + int lineno; + size_t current_pos; + int condition; + + int is_envelope; + + size_t message_len; + size_t buffer_length; + + /* temporary marker variables */ + size_t body_opaque_start; + size_t body_start; + size_t body_end; + size_t preamble_start; + size_t preamble_end; + size_t postamble_start; + size_t postamble_end; + + char *boundary_string; + char *endboundary_string; + const char *message_buffer; +}; + + +struct parser_state +{ + MM_CTX *ctx; + struct mm_mimepart *envelope; + struct mm_mimepart *temppart; + struct mm_mimepart *current_mimepart; + struct mm_content *ctype; + int parsemode; + int have_contenttype; + int debug; + int mime_parts; + struct lexer_state lstate; +}; + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +#include "mimeparser.tab.h" + +/** + * Prototypes for functions used by the parser routines + */ +int count_lines(char *); +int dprintf2(struct parser_state *, const char *, ...); +int mimeparser_yyparse(struct parser_state *, void *); +int mimeparser_yylex(YYSTYPE *, void *); +int mimeparser_yyerror(struct parser_state *, void *, const char *); +int mimeparser_yylex_init(yyscan_t* scanner); +int mimeparser_yylex_destroy(yyscan_t yyscanner); +void reset_lexer_state(void *yyscanner, struct parser_state *pstate); +int PARSER_initialize(struct parser_state *pstate, yyscan_t scanner); +void PARSER_setbuffer(const char *string, yyscan_t scanner); +void PARSER_setfp(FILE *fp, yyscan_t scanner); + +#endif /* ! _MIMEPARSER_H_INCLUDED */ diff --git a/trunk/main/minimime/mimeparser.l b/trunk/main/minimime/mimeparser.l new file mode 100644 index 000000000..19d42cf3a --- /dev/null +++ b/trunk/main/minimime/mimeparser.l @@ -0,0 +1,484 @@ +%{ +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * This is a lexer file for parsing MIME compatible messages. It is intended + * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still + * has quite a few problems: + * + * - The parsing could probably be done in a more elegant way + * - I don't know what performance impact REJECT has on the parser + */ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "mimeparser.h" +#include "mimeparser.tab.h" + +#define NAMEOF(v) #v +/* BC() is a debug wrapper for lex' BEGIN() macro */ +#define BC(x) do { \ + struct lexer_state *lstate = yyget_extra(yyscanner); \ + BEGIN(x); \ + lstate->condition = x; \ +} while(0); + +#define ZERO(x) memset(x, '\0', sizeof(x)) + +#define PREALLOC_BUFFER 100000 +#undef YY_BUF_SIZE +#define YY_BUF_SIZE 65536 + +enum header_states +{ + STATE_MAIL = 0, + STATE_CTYPE, + STATE_CDISP, + STATE_CENC, + STATE_MIME +}; + + + +%} + +%option reentrant +%option yylineno +%option bison-bridge + +%s headers +%s header +%s headervalue +%s tspecialvalue +%s comment +%s body +%s postamble +%s preamble +%s boundary +%s endboundary +%s endoffile + +STRING [a-zA-Z0-9\-\.\_] +TSPECIAL [a-zA-Z0-9)(<>@,;:/\-.=_\+'? ] +TSPECIAL_LITE [a-zA-Z0-9)(<>@,-._+'?\[\]] + +%% + +<INITIAL,headers>^[a-zA-Z]+[a-zA-Z0-9\-\_]* { + struct lexer_state *lstate = yyget_extra(yyscanner); + + yylval_param->string=strdup(yytext); + lstate->current_pos += yyleng; + BC(header); + + /* Depending on what header we are processing, we enter a different + * state and return a different value. + */ + if (!strcasecmp(yytext, "Content-Type")) { + lstate->header_state = STATE_CTYPE; + return CONTENTTYPE_HEADER; + } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) { + lstate->header_state = STATE_CENC; + return CONTENTENCODING_HEADER; + } else if (!strcasecmp(yytext, "Content-Disposition")) { + lstate->header_state = STATE_CDISP; + return CONTENTDISPOSITION_HEADER; + } else if (!strcasecmp(yytext, "MIME-Version")) { + lstate->header_state = STATE_MAIL; + return MIMEVERSION_HEADER; + } else { + lstate->header_state = STATE_MAIL; + return MAIL_HEADER; + } +} + +<INITIAL,headers>. { + struct lexer_state *lstate = yyget_extra(yyscanner); + /* dprintf2("Unknown header char: %c\n", *yytext); */ + lstate->current_pos += yyleng; + return ANY; +} + +<headers>^(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->lineno++; + + lstate->current_pos += yyleng; + + /* This marks the end of headers. Depending on whether we are in the + * envelope currently we need to parse either a body or the preamble + * now. + */ + if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) { + BC(body); + lstate->body_start = lstate->current_pos; + } else { + lstate->is_envelope = 0; + lstate->preamble_start = lstate->current_pos; + BC(preamble); + } + + return ENDOFHEADERS; +} + +<header>\: { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(headervalue); + lstate->current_pos += yyleng; + return COLON; +} + +<header>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(headers); + /* dprintf2("Invalid header, returning EOL\n"); */ + lstate->current_pos += yyleng; + return EOL; +} + +<headervalue>(\n|\r\n)[\ \t]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + +<headervalue>.+|(.+(\n|\r\n)[\ \t]+.+)+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) { + REJECT; + } + lstate->current_pos += yyleng; + while (*yytext && isspace(*yytext)) yytext++; + /* Do we actually have a header value? */ + if (*yytext == '\0') { + yylval_param->string = strdup(""); + } else { + yylval_param->string=strdup(yytext); + lstate->lineno += count_lines(yytext); + } + return WORD; +} + +<headervalue,tspecialvalue>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + /* marks the end of one header line */ + lstate->lineno++; + BC(headers); + lstate->current_pos += yyleng; + return EOL; +} + +<headervalue>;|;(\r\n|\n)[\ \t]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->lineno += count_lines(yytext); + lstate->current_pos += yyleng; + return SEMICOLON; +} + +<headervalue>\= { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; + return EQUAL; +} + +<headervalue>\" { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(tspecialvalue); + lstate->current_pos += yyleng; + return *yytext; +} + +<headervalue>{STRING}+|{TSPECIAL_LITE}+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + yylval_param->string=strdup(yytext); + lstate->lineno += count_lines(yytext); + lstate->current_pos += yyleng; + return WORD; +} + +<headervalue>[\ |\t]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + +<tspecialvalue>{TSPECIAL}+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->lineno += count_lines(yytext); + yylval_param->string=strdup(yytext); + lstate->current_pos += yyleng; + return TSPECIAL; +} + +<tspecialvalue>\" { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(headervalue); + lstate->current_pos += yyleng; + return *yytext; +} + +<body>^\-\-{TSPECIAL}+\-\- { + struct lexer_state *lstate = yyget_extra(yyscanner); + /** + * Make sure we only catch matching boundaries, and not other lines + * that begin and end with two dashes. If we have catched a valid + * end boundary, which actually ends a body, we save the current + * position, put the token back on the input stream and let the + * endboundary condition parse the actual token. + */ + if (lstate->endboundary_string != NULL) { + if (strcmp(lstate->endboundary_string, yytext)) { + /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */ + REJECT; + } else { + lstate->current_pos += yyleng; + /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */ + if (lstate->body_start) { + yylval_param->position.opaque_start = + lstate->body_opaque_start; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos - yyleng; + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + yyless(0); + BC(endboundary); + return BODY; + } + } + } else { + } + + REJECT; +} + +<body,preamble>^\-\-{TSPECIAL}+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + /** + * Make sure we only catch matching boundaries, and not other lines + * that begin with two dashes. + */ + if (lstate->boundary_string != NULL) { + if (strcmp(lstate->boundary_string, yytext)) { + /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/ + REJECT; + } else { + /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/ + if (lstate->body_start) { + yylval_param->position.opaque_start = lstate->body_opaque_start; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos; + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + yyless(0); + BC(boundary); + return BODY; + } else if (lstate->preamble_start) { + yylval_param->position.start = lstate->preamble_start; + yylval_param->position.end = lstate->current_pos; + lstate->preamble_start = lstate->preamble_end = 0; + yyless(0); + BC(boundary); + return PREAMBLE; + } else { + BC(boundary); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return(BOUNDARY); + } + } + } else { + } + + REJECT; +} + +<body>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; + lstate->lineno++; +} + +<body>\r { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; + /* dprintf2("stray CR in body...\n"); */ +} + +<body>[^\r\n]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + +<body><<EOF>> { + struct lexer_state *lstate = yyget_extra(yyscanner); + if (lstate->boundary_string == NULL && lstate->body_start) { + yylval_param->position.opaque_start = 0; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos; + lstate->body_start = 0; + return BODY; + } else if (lstate->body_start) { + return POSTAMBLE; + } + yyterminate(); +} + +<preamble,postamble>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + /* dprintf2("Preamble CR/LF at line %d\n", lineno); */ + lstate->lineno++; + lstate->current_pos += yyleng; +} + +<boundary>[^\r\n]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return BOUNDARY; +} + +<endboundary>[^\r\n]+ { + struct lexer_state *lstate = yyget_extra(yyscanner); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return ENDBOUNDARY; +} + +<boundary>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(headers); + lstate->lineno++; + lstate->current_pos += yyleng; + lstate->body_opaque_start = lstate->current_pos; + return EOL; +} + +<endboundary>(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + BC(postamble); + lstate->lineno++; + lstate->current_pos += yyleng; +} + +<preamble>. { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + + +<postamble>. { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + +(\r\n|\n) { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->lineno++; + lstate->current_pos += yyleng; + return EOL; +} + +. { + struct lexer_state *lstate = yyget_extra(yyscanner); + lstate->current_pos += yyleng; + return((int)*yytext); +} + + +%% + +void reset_lexer_state(void *yyscanner, struct parser_state *pstate) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + struct lexer_state *lstate = &(pstate->lstate); + + yyset_extra((void*)lstate, yyscanner); + BEGIN(0); + lstate->header_state = STATE_MAIL; + lstate->lineno = 0; + lstate->current_pos = 1; + lstate->condition = 0; + + lstate->is_envelope = 1; + + lstate->message_len = 0; + lstate->buffer_length = 0; + + /* temporary marker variables */ + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + lstate->preamble_start = 0; + lstate->preamble_end = 0; + lstate->postamble_start = 0; + lstate->postamble_end = 0; +} + +void +PARSER_setbuffer(const char *string, yyscan_t scanner) +{ + struct lexer_state *lstate = yyget_extra(scanner); + lstate->message_buffer = string; + yy_scan_string(string, scanner); +} + +void +PARSER_setfp(FILE *fp, yyscan_t scanner) +{ + /* looks like a bug in bison 2.2a -- the wrong code is generated for yyset_in !! */ + struct yyguts_t * yyg = (struct yyguts_t*) scanner; + yyg->yyin_r = fp; + + if (0) { + /* This is just to make a compiler warning go away */ + yyunput(0, NULL, scanner); + } +} + +/** + * Counts how many lines a given string represents in the message (in case of + * folded header values, for example, or a message body). + */ +int +count_lines(char *txt) +{ + char *o; + int line; + + line = 0; + + for (o = txt; *o != '\0'; o++) + if (*o == '\n') + line++; + + return line; +} diff --git a/trunk/main/minimime/mimeparser.tab.c b/trunk/main/minimime/mimeparser.tab.c new file mode 100644 index 000000000..ef9a4a3fd --- /dev/null +++ b/trunk/main/minimime/mimeparser.tab.c @@ -0,0 +1,2339 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program 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; either version 2, or (at your option) + any later version. + + This program 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 + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse mimeparser_yyparse +#define yylex mimeparser_yylex +#define yyerror mimeparser_yyerror +#define yylval mimeparser_yylval +#define yychar mimeparser_yychar +#define yydebug mimeparser_yydebug +#define yynerrs mimeparser_yynerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + ANY = 258, + COLON = 259, + DASH = 260, + DQUOTE = 261, + ENDOFHEADERS = 262, + EOL = 263, + EOM = 264, + EQUAL = 265, + MIMEVERSION_HEADER = 266, + SEMICOLON = 267, + CONTENTDISPOSITION_HEADER = 268, + CONTENTENCODING_HEADER = 269, + CONTENTTYPE_HEADER = 270, + MAIL_HEADER = 271, + HEADERVALUE = 272, + BOUNDARY = 273, + ENDBOUNDARY = 274, + CONTENTTYPE_VALUE = 275, + TSPECIAL = 276, + WORD = 277, + BODY = 278, + PREAMBLE = 279, + POSTAMBLE = 280 + }; +#endif +/* Tokens. */ +#define ANY 258 +#define COLON 259 +#define DASH 260 +#define DQUOTE 261 +#define ENDOFHEADERS 262 +#define EOL 263 +#define EOM 264 +#define EQUAL 265 +#define MIMEVERSION_HEADER 266 +#define SEMICOLON 267 +#define CONTENTDISPOSITION_HEADER 268 +#define CONTENTENCODING_HEADER 269 +#define CONTENTTYPE_HEADER 270 +#define MAIL_HEADER 271 +#define HEADERVALUE 272 +#define BOUNDARY 273 +#define ENDBOUNDARY 274 +#define CONTENTTYPE_VALUE 275 +#define TSPECIAL 276 +#define WORD 277 +#define BODY 278 +#define PREAMBLE 279 +#define POSTAMBLE 280 + + + + +/* Copy the first part of user declarations. */ +#line 1 "mimeparser.y" + +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * These are the grammatic definitions in yacc syntax to parse MIME conform + * messages. + * + * TODO: + * - honour parse flags passed to us (partly done) + * - parse Content-Disposition header (partly done) + * - parse Content-Encoding header + */ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "mimeparser.h" +#include "mm.h" +#include "mm_internal.h" + +int set_boundary(char *,struct parser_state *); +int mimeparser_yywrap(void); +void reset_environ(struct parser_state *pstate); +int PARSER_initialize(struct parser_state *pstate, void *yyscanner); + +static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *); +FILE *mimeparser_yyget_in (yyscan_t yyscanner ); + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 67 "mimeparser.y" +{ + int number; + char *string; + struct s_position position; +} +/* Line 187 of yacc.c. */ +#line 220 "mimeparser.tab.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 233 "mimeparser.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined(YYENABLE_NLS) +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 26 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 61 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 28 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 29 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 50 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 83 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 280 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 27, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 26, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 7, 8, 15, 18, 21, 23, + 25, 27, 28, 30, 31, 34, 36, 40, 42, 44, + 46, 48, 50, 52, 57, 61, 66, 72, 77, 83, + 85, 90, 95, 98, 101, 103, 107, 111, 114, 116, + 120, 123, 125, 129, 133, 135, 137, 141, 143, 146, + 148 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 29, 0, -1, 30, -1, 32, -1, -1, 33, 34, + 31, 36, 55, 35, -1, 33, 56, -1, 38, 33, + -1, 53, -1, 38, -1, 24, -1, -1, 25, -1, + -1, 36, 37, -1, 37, -1, 54, 33, 56, -1, + 39, -1, 40, -1, 41, -1, 43, -1, 44, -1, + 45, -1, 16, 4, 22, 8, -1, 16, 4, 8, + -1, 15, 4, 47, 8, -1, 15, 4, 47, 48, + 8, -1, 13, 4, 42, 8, -1, 13, 4, 42, + 49, 8, -1, 22, -1, 14, 4, 22, 8, -1, + 11, 4, 22, 8, -1, 46, 8, -1, 46, 3, + -1, 3, -1, 22, 26, 22, -1, 12, 50, 48, + -1, 12, 50, -1, 12, -1, 12, 51, 49, -1, + 12, 51, -1, 12, -1, 22, 10, 52, -1, 22, + 10, 52, -1, 22, -1, 21, -1, 27, 21, 27, + -1, 7, -1, 18, 8, -1, 19, -1, 23, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 112, 112, 114, 119, 118, 131, 139, 141, 165, + 169, 184, 188, 191, 195, 197, 201, 216, 218, 228, + 230, 232, 234, 248, 255, 274, 282, 292, 298, 306, + 329, 336, 343, 347, 349, 353, 362, 364, 366, 380, + 382, 384, 398, 429, 443, 449, 464, 472, 479, 498, + 517 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ANY", "COLON", "DASH", "DQUOTE", + "ENDOFHEADERS", "EOL", "EOM", "EQUAL", "MIMEVERSION_HEADER", "SEMICOLON", + "CONTENTDISPOSITION_HEADER", "CONTENTENCODING_HEADER", + "CONTENTTYPE_HEADER", "MAIL_HEADER", "HEADERVALUE", "BOUNDARY", + "ENDBOUNDARY", "CONTENTTYPE_VALUE", "TSPECIAL", "WORD", "BODY", + "PREAMBLE", "POSTAMBLE", "'/'", "'\"'", "$accept", "message", + "multipart_message", "@1", "singlepart_message", "headers", "preamble", + "postamble", "mimeparts", "mimepart", "header", "mail_header", + "contenttype_header", "contentdisposition_header", "content_disposition", + "contentencoding_header", "mimeversion_header", "invalid_header", "any", + "mimetype", "contenttype_parameters", "content_disposition_parameters", + "contenttype_parameter", "content_disposition_parameter", + "contenttype_parameter_value", "end_headers", "boundary", "endboundary", + "body", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 47, 34 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 28, 29, 29, 31, 30, 32, 33, 33, 33, + 34, 34, 35, 35, 36, 36, 37, 38, 38, 38, + 38, 38, 38, 39, 39, 40, 40, 41, 41, 42, + 43, 44, 45, 46, 46, 47, 48, 48, 48, 49, + 49, 49, 50, 51, 52, 52, 52, 53, 54, 55, + 56 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 0, 6, 2, 2, 1, 1, + 1, 0, 1, 0, 2, 1, 3, 1, 1, 1, + 1, 1, 1, 4, 3, 4, 5, 4, 5, 1, + 4, 4, 2, 2, 1, 3, 3, 2, 1, 3, + 2, 1, 3, 3, 1, 1, 3, 1, 2, 1, + 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 34, 47, 0, 0, 0, 0, 0, 0, 2, + 3, 11, 9, 17, 18, 19, 20, 21, 22, 0, + 8, 0, 0, 0, 0, 0, 1, 50, 10, 4, + 6, 7, 33, 32, 0, 29, 0, 0, 0, 0, + 24, 0, 0, 31, 27, 41, 0, 30, 0, 25, + 38, 0, 23, 0, 0, 15, 0, 0, 40, 28, + 35, 0, 37, 26, 48, 49, 14, 13, 0, 0, + 39, 0, 36, 12, 5, 16, 45, 44, 0, 43, + 42, 0, 46 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 8, 9, 42, 10, 11, 29, 74, 54, 55, + 12, 13, 14, 15, 36, 16, 17, 18, 19, 39, + 51, 46, 62, 58, 79, 20, 56, 67, 30 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -20 +static const yytype_int8 yypact[] = +{ + 3, -20, -20, 17, 21, 22, 23, 24, 5, -20, + -20, -11, 3, -20, -20, -20, -20, -20, -20, 1, + -20, 7, 8, 9, 10, -7, -20, -20, -20, -20, + -20, -20, -20, -20, 25, -20, -1, 26, 11, 12, + -20, 27, 18, -20, -20, 16, 31, -20, 19, -20, + 20, 32, -20, 35, 4, -20, 3, 36, 33, -20, + -20, 37, 38, -20, -20, -20, -20, 28, 29, -19, + -20, -19, -20, -20, -20, -20, -20, -20, 30, -20, + -20, 34, -20 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -20, -20, -20, -20, -20, -12, -20, -20, -20, -6, + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -13, -4, -20, -20, -16, -20, -20, -20, -10 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 31, 40, 76, 77, 32, 26, 1, 44, 78, 33, + 2, 45, 27, 28, 3, 41, 4, 5, 6, 7, + 49, 21, 53, 65, 50, 22, 23, 24, 25, 34, + 35, 37, 38, 43, 47, 52, 53, 48, 57, 59, + 63, 60, 61, 64, 68, 45, 69, 71, 66, 72, + 50, 81, 27, 73, 70, 80, 0, 0, 75, 0, + 0, 82 +}; + +static const yytype_int8 yycheck[] = +{ + 12, 8, 21, 22, 3, 0, 3, 8, 27, 8, + 7, 12, 23, 24, 11, 22, 13, 14, 15, 16, + 8, 4, 18, 19, 12, 4, 4, 4, 4, 22, + 22, 22, 22, 8, 8, 8, 18, 26, 22, 8, + 8, 22, 22, 8, 56, 12, 10, 10, 54, 62, + 12, 21, 23, 25, 58, 71, -1, -1, 68, -1, + -1, 27 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 7, 11, 13, 14, 15, 16, 29, 30, + 32, 33, 38, 39, 40, 41, 43, 44, 45, 46, + 53, 4, 4, 4, 4, 4, 0, 23, 24, 34, + 56, 33, 3, 8, 22, 22, 42, 22, 22, 47, + 8, 22, 31, 8, 8, 12, 49, 8, 26, 8, + 12, 48, 8, 18, 36, 37, 54, 22, 51, 8, + 22, 22, 50, 8, 8, 19, 37, 55, 33, 10, + 49, 10, 48, 25, 35, 56, 21, 22, 27, 52, + 52, 21, 27 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (pstate, yyscanner, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined(YYLTYPE_IS_TRIVIAL) +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, yyscanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, pstate, yyscanner); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + struct parser_state *pstate; + void *yyscanner; +#endif +{ + if (!yyvaluep) + return; + YYUSE (pstate); + YYUSE (yyscanner); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, pstate, yyscanner) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + struct parser_state *pstate; + void *yyscanner; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule, struct parser_state *pstate, void *yyscanner) +#else +static void +yy_reduce_print (yyvsp, yyrule, pstate, yyscanner) + YYSTYPE *yyvsp; + int yyrule; + struct parser_state *pstate; + void *yyscanner; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , pstate, yyscanner); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule, pstate, yyscanner); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, struct parser_state *pstate, void *yyscanner) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, pstate, yyscanner) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + struct parser_state *pstate; + void *yyscanner; +#endif +{ + YYUSE (yyvaluep); + YYUSE (pstate); + YYUSE (yyscanner); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (struct parser_state *pstate, void *yyscanner); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (struct parser_state *pstate, void *yyscanner) +#else +int +yyparse (pstate, yyscanner) + struct parser_state *pstate; + void *yyscanner; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 119 "mimeparser.y" + { + mm_context_attachpart(pstate->ctx, pstate->current_mimepart); + pstate->current_mimepart = mm_mimepart_new(); + pstate->have_contenttype = 0; + ;} + break; + + case 5: +#line 125 "mimeparser.y" + { + dprintf2(pstate,"This was a multipart message\n"); + ;} + break; + + case 6: +#line 132 "mimeparser.y" + { + dprintf2(pstate,"This was a single part message\n"); + mm_context_attachpart(pstate->ctx, pstate->current_mimepart); + ;} + break; + + case 8: +#line 142 "mimeparser.y" + { + /* If we did not find a Content-Type header for the current + * MIME part (or envelope), we create one and attach it. + * According to the RFC, a type of "text/plain" and a + * charset of "us-ascii" can be assumed. + */ + struct mm_content *ct; + struct mm_param *param; + + if (!pstate->have_contenttype) { + ct = mm_content_new(); + mm_content_settype(ct, "text/plain"); + + param = mm_param_new(); + param->name = xstrdup("charset"); + param->value = xstrdup("us-ascii"); + + mm_content_attachtypeparam(ct, param); + mm_mimepart_attachcontenttype(pstate->current_mimepart, ct); + } + pstate->have_contenttype = 0; + ;} + break; + + case 10: +#line 170 "mimeparser.y" + { + char *preamble; + size_t offset; + + if ((yyvsp[(1) - (1)].position).start != (yyvsp[(1) - (1)].position).end) { + preamble = PARSE_readmessagepart(0, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end, + &offset,yyscanner,pstate); + if (preamble == NULL) { + return(-1); + } + pstate->ctx->preamble = preamble; + dprintf2(pstate,"PREAMBLE:\n%s\n", preamble); + } + ;} + break; + + case 12: +#line 189 "mimeparser.y" + { + ;} + break; + + case 16: +#line 202 "mimeparser.y" + { + + if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) { + mm_errno = MM_ERROR_ERRNO; + return(-1); + } + + pstate->temppart = mm_mimepart_new(); + pstate->current_mimepart = pstate->temppart; + pstate->mime_parts++; + ;} + break; + + case 18: +#line 219 "mimeparser.y" + { + pstate->have_contenttype = 1; + if (mm_content_iscomposite(pstate->envelope->type)) { + pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART; + } else { + pstate->ctx->messagetype = MM_MSGTYPE_FLAT; + } + ;} + break; + + case 22: +#line 235 "mimeparser.y" + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid header encountered"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + ;} + break; + + case 23: +#line 249 "mimeparser.y" + { + struct mm_mimeheader *hdr; + hdr = mm_mimeheader_generate((yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].string)); + mm_mimepart_attachheader(pstate->current_mimepart, hdr); + ;} + break; + + case 24: +#line 256 "mimeparser.y" + { + struct mm_mimeheader *hdr; + + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid header encountered"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + + hdr = mm_mimeheader_generate((yyvsp[(1) - (3)].string), xstrdup("")); + mm_mimepart_attachheader(pstate->current_mimepart, hdr); + ;} + break; + + case 25: +#line 275 "mimeparser.y" + { + mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (4)].string)); + mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype); + dprintf2(pstate,"Content-Type -> %s\n", (yyvsp[(3) - (4)].string)); + pstate->ctype = mm_content_new(); + ;} + break; + + case 26: +#line 283 "mimeparser.y" + { + mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (5)].string)); + mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype); + dprintf2(pstate,"Content-Type (P) -> %s\n", (yyvsp[(3) - (5)].string)); + pstate->ctype = mm_content_new(); + ;} + break; + + case 27: +#line 293 "mimeparser.y" + { + dprintf2(pstate,"Content-Disposition -> %s\n", (yyvsp[(3) - (4)].string)); + pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (4)].string)); + ;} + break; + + case 28: +#line 299 "mimeparser.y" + { + dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", (yyvsp[(3) - (5)].string)); + pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (5)].string)); + ;} + break; + + case 29: +#line 307 "mimeparser.y" + { + /* + * According to RFC 2183, the content disposition value may + * only be "inline", "attachment" or an extension token. We + * catch invalid values here if we are not in loose parsing + * mode. + */ + if (strcasecmp((yyvsp[(1) - (1)].string), "inline") && strcasecmp((yyvsp[(1) - (1)].string), "attachment") + && strncasecmp((yyvsp[(1) - (1)].string), "X-", 2)) { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid content-disposition"); + return(-1); + } + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + (yyval.string) = (yyvsp[(1) - (1)].string); + ;} + break; + + case 30: +#line 330 "mimeparser.y" + { + dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", (yyvsp[(3) - (4)].string)); + ;} + break; + + case 31: +#line 337 "mimeparser.y" + { + dprintf2(pstate,"MIME-Version -> '%s'\n", (yyvsp[(3) - (4)].string)); + ;} + break; + + case 35: +#line 354 "mimeparser.y" + { + char type[255]; + snprintf(type, sizeof(type), "%s/%s", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string)); + (yyval.string) = type; + ;} + break; + + case 38: +#line 367 "mimeparser.y" + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid Content-Type header"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + ;} + break; + + case 41: +#line 385 "mimeparser.y" + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid Content-Disposition header"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + ;} + break; + + case 42: +#line 399 "mimeparser.y" + { + struct mm_param *param; + param = mm_param_new(); + + dprintf2(pstate,"Param: '%s', Value: '%s'\n", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string)); + + /* Catch an eventual boundary identifier */ + if (!strcasecmp((yyvsp[(1) - (3)].string), "boundary")) { + if (pstate->lstate.boundary_string == NULL) { + set_boundary((yyvsp[(3) - (3)].string),pstate); + } else { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("duplicate boundary " + "found"); + return -1; + } else { + /* TODO: attach MM_WARNING_DUPPARAM */ + } + } + } + + param->name = xstrdup((yyvsp[(1) - (3)].string)); + param->value = xstrdup((yyvsp[(3) - (3)].string)); + + mm_content_attachtypeparam(pstate->ctype, param); + ;} + break; + + case 43: +#line 430 "mimeparser.y" + { + struct mm_param *param; + param = mm_param_new(); + + param->name = xstrdup((yyvsp[(1) - (3)].string)); + param->value = xstrdup((yyvsp[(3) - (3)].string)); + + mm_content_attachdispositionparam(pstate->ctype, param); + + ;} + break; + + case 44: +#line 444 "mimeparser.y" + { + dprintf2(pstate,"contenttype_param_val: WORD=%s\n", (yyvsp[(1) - (1)].string)); + (yyval.string) = (yyvsp[(1) - (1)].string); + ;} + break; + + case 45: +#line 450 "mimeparser.y" + { + dprintf2(pstate,"contenttype_param_val: TSPECIAL\n"); + /* For broken MIME implementation */ + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("tspecial without quotes"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVAL */ + } + (yyval.string) = (yyvsp[(1) - (1)].string); + ;} + break; + + case 46: +#line 465 "mimeparser.y" + { + dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" ); + (yyval.string) = (yyvsp[(2) - (3)].string); + ;} + break; + + case 47: +#line 473 "mimeparser.y" + { + dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno); + ;} + break; + + case 48: +#line 480 "mimeparser.y" + { + if (pstate->lstate.boundary_string == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + if (strcmp(pstate->lstate.boundary_string, (yyvsp[(1) - (2)].string))) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid boundary: '%s' (%d)", (yyvsp[(1) - (2)].string), strlen((yyvsp[(1) - (2)].string))); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + dprintf2(pstate,"New MIME part... (%s)\n", (yyvsp[(1) - (2)].string)); + ;} + break; + + case 49: +#line 499 "mimeparser.y" + { + if (pstate->lstate.endboundary_string == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + if (strcmp(pstate->lstate.endboundary_string, (yyvsp[(1) - (1)].string))) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid end boundary: %s", (yyvsp[(1) - (1)].string)); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + dprintf2(pstate,"End of MIME message\n"); + ;} + break; + + case 50: +#line 518 "mimeparser.y" + { + char *body; + size_t offset; + + dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end, (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start); + + body = PARSE_readmessagepart((yyvsp[(1) - (1)].position).opaque_start, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end, + &offset,yyscanner,pstate); + + if (body == NULL) { + return(-1); + } + pstate->current_mimepart->opaque_body = body; + pstate->current_mimepart->body = body + offset; + pstate->current_mimepart->opaque_length = (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start - 2 + offset; + pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset; + ;} + break; + + +/* Line 1267 of yacc.c. */ +#line 1913 "mimeparser.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (pstate, yyscanner, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (pstate, yyscanner, yymsg); + } + else + { + yyerror (pstate, yyscanner, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, pstate, yyscanner); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, pstate, yyscanner); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (pstate, yyscanner, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, pstate, yyscanner); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, pstate, yyscanner); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 537 "mimeparser.y" + + +/* + * This function gets the specified part from the currently parsed message. + */ +static char * +PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end, + size_t *offset, yyscan_t yyscanner, struct parser_state *pstate) +{ + size_t body_size; + size_t current; + size_t start; + char *body; + + /* calculate start and offset markers for the opaque and + * header stripped body message. + */ + if (opaque_start > 0) { + /* Multipart message */ + if (real_start) { + if (real_start < opaque_start) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency (S:%d/O:%d)", + real_start, + opaque_start); + return(NULL); + } + start = opaque_start; + *offset = real_start - start; + /* Flat message */ + } else { + start = opaque_start; + *offset = 0; + } + } else { + start = real_start; + *offset = 0; + } + + /* The next three cases should NOT happen anytime */ + if (end <= start) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency,2"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + if (start < *offset) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + if (start < 0 || end < 0) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency,4"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + + /* XXX: do we want to enforce a maximum body size? make it a + * parser option? */ + + /* Read in the body message */ + body_size = end - start; + + if (body_size < 1) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("size of body cannot be < 1"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + + body = (char *)malloc(body_size + 1); + if (body == NULL) { + mm_errno = MM_ERROR_ERRNO; + return(NULL); + } + + /* Get the message body either from a stream or a memory + * buffer. + */ + if (mimeparser_yyget_in(yyscanner) != NULL) { + FILE *x = mimeparser_yyget_in(yyscanner); + current = ftell(x); + fseek(x, start - 1, SEEK_SET); + fread(body, body_size - 1, 1, x); + fseek(x, current, SEEK_SET); + } else if (pstate->lstate.message_buffer != NULL) { + strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size); + } + + return(body); + +} + +int +yyerror(struct parser_state *pstate, void *yyscanner, const char *str) +{ + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("%s", str); + mm_error_setlineno(pstate->lstate.lineno); + return -1; +} + +int +mimeparser_yywrap(void) +{ + return 1; +} + +/** + * Sets the boundary value for the current message + */ +int +set_boundary(char *str, struct parser_state *pstate) +{ + size_t blen; + + blen = strlen(str); + + pstate->lstate.boundary_string = (char *)malloc(blen + 3); + pstate->lstate.endboundary_string = (char *)malloc(blen + 5); + + if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) { + if (pstate->lstate.boundary_string != NULL) { + free(pstate->lstate.boundary_string); + } + if (pstate->lstate.endboundary_string != NULL) { + free(pstate->lstate.endboundary_string); + } + return -1; + } + + pstate->ctx->boundary = xstrdup(str); + + snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str); + snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str); + + return 0; +} + +/** + * Debug printf() + */ +int +dprintf2(struct parser_state *pstate, const char *fmt, ...) +{ + va_list ap; + char *msg; + if (pstate->debug == 0) return 1; + + va_start(ap, fmt); + vasprintf(&msg, fmt, ap); + va_end(ap); + + fprintf(stderr, "%s", msg); + free(msg); + + return 0; + +} + +void reset_environ(struct parser_state *pstate) +{ + pstate->lstate.lineno = 0; + pstate->lstate.boundary_string = NULL; + pstate->lstate.endboundary_string = NULL; + pstate->lstate.message_buffer = NULL; + pstate->mime_parts = 0; + pstate->debug = 0; + pstate->envelope = NULL; + pstate->temppart = NULL; + pstate->ctype = NULL; + pstate->current_mimepart = NULL; + + pstate->have_contenttype = 0; +} +/** + * Initializes the parser engine. + */ +int +PARSER_initialize(struct parser_state *pstate, void *yyscanner) +{ + void reset_lexer_state(void *yyscanner, struct parser_state *); +#if 0 + if (pstate->ctx != NULL) { + xfree(pstate->ctx); + pstate->ctx = NULL; + } + if (pstate->envelope != NULL) { + xfree(pstate->envelope); + pstate->envelope = NULL; + } + if (pstate->ctype != NULL) { + xfree(pstate->ctype); + pstate->ctype = NULL; + } +#endif + /* yydebug = 1; */ + reset_environ(pstate); + reset_lexer_state(yyscanner,pstate); + + pstate->envelope = mm_mimepart_new(); + pstate->current_mimepart = pstate->envelope; + pstate->ctype = mm_content_new(); + + pstate->have_contenttype = 0; + + return 1; +} + + + diff --git a/trunk/main/minimime/mimeparser.tab.h b/trunk/main/minimime/mimeparser.tab.h new file mode 100644 index 000000000..c19731fd2 --- /dev/null +++ b/trunk/main/minimime/mimeparser.tab.h @@ -0,0 +1,112 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program 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; either version 2, or (at your option) + any later version. + + This program 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 + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + ANY = 258, + COLON = 259, + DASH = 260, + DQUOTE = 261, + ENDOFHEADERS = 262, + EOL = 263, + EOM = 264, + EQUAL = 265, + MIMEVERSION_HEADER = 266, + SEMICOLON = 267, + CONTENTDISPOSITION_HEADER = 268, + CONTENTENCODING_HEADER = 269, + CONTENTTYPE_HEADER = 270, + MAIL_HEADER = 271, + HEADERVALUE = 272, + BOUNDARY = 273, + ENDBOUNDARY = 274, + CONTENTTYPE_VALUE = 275, + TSPECIAL = 276, + WORD = 277, + BODY = 278, + PREAMBLE = 279, + POSTAMBLE = 280 + }; +#endif +/* Tokens. */ +#define ANY 258 +#define COLON 259 +#define DASH 260 +#define DQUOTE 261 +#define ENDOFHEADERS 262 +#define EOL 263 +#define EOM 264 +#define EQUAL 265 +#define MIMEVERSION_HEADER 266 +#define SEMICOLON 267 +#define CONTENTDISPOSITION_HEADER 268 +#define CONTENTENCODING_HEADER 269 +#define CONTENTTYPE_HEADER 270 +#define MAIL_HEADER 271 +#define HEADERVALUE 272 +#define BOUNDARY 273 +#define ENDBOUNDARY 274 +#define CONTENTTYPE_VALUE 275 +#define TSPECIAL 276 +#define WORD 277 +#define BODY 278 +#define PREAMBLE 279 +#define POSTAMBLE 280 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 67 "mimeparser.y" +{ + int number; + char *string; + struct s_position position; +} +/* Line 1489 of yacc.c. */ +#line 105 "mimeparser.tab.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + diff --git a/trunk/main/minimime/mimeparser.y b/trunk/main/minimime/mimeparser.y new file mode 100644 index 000000000..7c32a290d --- /dev/null +++ b/trunk/main/minimime/mimeparser.y @@ -0,0 +1,748 @@ +%{ +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * These are the grammatic definitions in yacc syntax to parse MIME conform + * messages. + * + * TODO: + * - honour parse flags passed to us (partly done) + * - parse Content-Disposition header (partly done) + * - parse Content-Encoding header + */ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "mimeparser.h" +#include "mm.h" +#include "mm_internal.h" + +int set_boundary(char *,struct parser_state *); +int mimeparser_yywrap(void); +void reset_environ(struct parser_state *pstate); +int PARSER_initialize(struct parser_state *pstate, void *yyscanner); + +static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *); +FILE *mimeparser_yyget_in (yyscan_t yyscanner ); + +%} + +%pure-parser +%parse-param {struct parser_state *pstate} +%parse-param {void *yyscanner} +%lex-param {void *yyscanner} + +%union +{ + int number; + char *string; + struct s_position position; +} + +%token ANY +%token COLON +%token DASH +%token DQUOTE +%token ENDOFHEADERS +%token EOL +%token EOM +%token EQUAL +%token MIMEVERSION_HEADER +%token SEMICOLON + +%token <string> CONTENTDISPOSITION_HEADER +%token <string> CONTENTENCODING_HEADER +%token <string> CONTENTTYPE_HEADER +%token <string> MAIL_HEADER +%token <string> HEADERVALUE +%token <string> BOUNDARY +%token <string> ENDBOUNDARY +%token <string> CONTENTTYPE_VALUE +%token <string> TSPECIAL +%token <string> WORD + +%token <position> BODY +%token <position> PREAMBLE +%token <position> POSTAMBLE + +%type <string> content_disposition +%type <string> contenttype_parameter_value +%type <string> mimetype +%type <string> body + +%start message + +%% + +/* This is a parser for a MIME-conform message, which is in either single + * part or multi part format. + */ +message : + multipart_message + | + singlepart_message + ; + +multipart_message: + headers preamble + { + mm_context_attachpart(pstate->ctx, pstate->current_mimepart); + pstate->current_mimepart = mm_mimepart_new(); + pstate->have_contenttype = 0; + } + mimeparts endboundary postamble + { + dprintf2(pstate,"This was a multipart message\n"); + } + ; + +singlepart_message: + headers body + { + dprintf2(pstate,"This was a single part message\n"); + mm_context_attachpart(pstate->ctx, pstate->current_mimepart); + } + ; + +headers : + header headers + | + end_headers + { + /* If we did not find a Content-Type header for the current + * MIME part (or envelope), we create one and attach it. + * According to the RFC, a type of "text/plain" and a + * charset of "us-ascii" can be assumed. + */ + struct mm_content *ct; + struct mm_param *param; + + if (!pstate->have_contenttype) { + ct = mm_content_new(); + mm_content_settype(ct, "text/plain"); + + param = mm_param_new(); + param->name = xstrdup("charset"); + param->value = xstrdup("us-ascii"); + + mm_content_attachtypeparam(ct, param); + mm_mimepart_attachcontenttype(pstate->current_mimepart, ct); + } + pstate->have_contenttype = 0; + } + | + header + ; + +preamble: + PREAMBLE + { + char *preamble; + size_t offset; + + if ($1.start != $1.end) { + preamble = PARSE_readmessagepart(0, $1.start, $1.end, + &offset,yyscanner,pstate); + if (preamble == NULL) { + return(-1); + } + pstate->ctx->preamble = preamble; + dprintf2(pstate,"PREAMBLE:\n%s\n", preamble); + } + } + | + ; + +postamble: + POSTAMBLE + { + } + | + ; + +mimeparts: + mimeparts mimepart + | + mimepart + ; + +mimepart: + boundary headers body + { + + if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) { + mm_errno = MM_ERROR_ERRNO; + return(-1); + } + + pstate->temppart = mm_mimepart_new(); + pstate->current_mimepart = pstate->temppart; + pstate->mime_parts++; + } + ; + +header : + mail_header + | + contenttype_header + { + pstate->have_contenttype = 1; + if (mm_content_iscomposite(pstate->envelope->type)) { + pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART; + } else { + pstate->ctx->messagetype = MM_MSGTYPE_FLAT; + } + } + | + contentdisposition_header + | + contentencoding_header + | + mimeversion_header + | + invalid_header + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid header encountered"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + } + ; + +mail_header: + MAIL_HEADER COLON WORD EOL + { + struct mm_mimeheader *hdr; + hdr = mm_mimeheader_generate($1, $3); + mm_mimepart_attachheader(pstate->current_mimepart, hdr); + } + | + MAIL_HEADER COLON EOL + { + struct mm_mimeheader *hdr; + + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid header encountered"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + + hdr = mm_mimeheader_generate($1, xstrdup("")); + mm_mimepart_attachheader(pstate->current_mimepart, hdr); + } + ; + +contenttype_header: + CONTENTTYPE_HEADER COLON mimetype EOL + { + mm_content_settype(pstate->ctype, "%s", $3); + mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype); + dprintf2(pstate,"Content-Type -> %s\n", $3); + pstate->ctype = mm_content_new(); + } + | + CONTENTTYPE_HEADER COLON mimetype contenttype_parameters EOL + { + mm_content_settype(pstate->ctype, "%s", $3); + mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype); + dprintf2(pstate,"Content-Type (P) -> %s\n", $3); + pstate->ctype = mm_content_new(); + } + ; + +contentdisposition_header: + CONTENTDISPOSITION_HEADER COLON content_disposition EOL + { + dprintf2(pstate,"Content-Disposition -> %s\n", $3); + pstate->ctype->disposition_type = xstrdup($3); + } + | + CONTENTDISPOSITION_HEADER COLON content_disposition content_disposition_parameters EOL + { + dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", $3); + pstate->ctype->disposition_type = xstrdup($3); + } + ; + +content_disposition: + WORD + { + /* + * According to RFC 2183, the content disposition value may + * only be "inline", "attachment" or an extension token. We + * catch invalid values here if we are not in loose parsing + * mode. + */ + if (strcasecmp($1, "inline") && strcasecmp($1, "attachment") + && strncasecmp($1, "X-", 2)) { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid content-disposition"); + return(-1); + } + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + $$ = $1; + } + ; + +contentencoding_header: + CONTENTENCODING_HEADER COLON WORD EOL + { + dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", $3); + } + ; + +mimeversion_header: + MIMEVERSION_HEADER COLON WORD EOL + { + dprintf2(pstate,"MIME-Version -> '%s'\n", $3); + } + ; + +invalid_header: + any EOL + ; + +any: + any ANY + | + ANY + ; + +mimetype: + WORD '/' WORD + { + char type[255]; + snprintf(type, sizeof(type), "%s/%s", $1, $3); + $$ = type; + } + ; + +contenttype_parameters: + SEMICOLON contenttype_parameter contenttype_parameters + | + SEMICOLON contenttype_parameter + | + SEMICOLON + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid Content-Type header"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + } + ; + +content_disposition_parameters: + SEMICOLON content_disposition_parameter content_disposition_parameters + | + SEMICOLON content_disposition_parameter + | + SEMICOLON + { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("invalid Content-Disposition header"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVHDR */ + } + } + ; + +contenttype_parameter: + WORD EQUAL contenttype_parameter_value + { + struct mm_param *param; + param = mm_param_new(); + + dprintf2(pstate,"Param: '%s', Value: '%s'\n", $1, $3); + + /* Catch an eventual boundary identifier */ + if (!strcasecmp($1, "boundary")) { + if (pstate->lstate.boundary_string == NULL) { + set_boundary($3,pstate); + } else { + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("duplicate boundary " + "found"); + return -1; + } else { + /* TODO: attach MM_WARNING_DUPPARAM */ + } + } + } + + param->name = xstrdup($1); + param->value = xstrdup($3); + + mm_content_attachtypeparam(pstate->ctype, param); + } + ; + +content_disposition_parameter: + WORD EQUAL contenttype_parameter_value + { + struct mm_param *param; + param = mm_param_new(); + + param->name = xstrdup($1); + param->value = xstrdup($3); + + mm_content_attachdispositionparam(pstate->ctype, param); + + } + ; + +contenttype_parameter_value: + WORD + { + dprintf2(pstate,"contenttype_param_val: WORD=%s\n", $1); + $$ = $1; + } + | + TSPECIAL + { + dprintf2(pstate,"contenttype_param_val: TSPECIAL\n"); + /* For broken MIME implementation */ + if (pstate->parsemode != MM_PARSE_LOOSE) { + mm_errno = MM_ERROR_MIME; + mm_error_setmsg("tspecial without quotes"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } else { + /* TODO: attach MM_WARNING_INVAL */ + } + $$ = $1; + } + | + '"' TSPECIAL '"' + { + dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" ); + $$ = $2; + } + ; + +end_headers : + ENDOFHEADERS + { + dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno); + } + ; + +boundary : + BOUNDARY EOL + { + if (pstate->lstate.boundary_string == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + if (strcmp(pstate->lstate.boundary_string, $1)) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid boundary: '%s' (%d)", $1, strlen($1)); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + dprintf2(pstate,"New MIME part... (%s)\n", $1); + } + ; + +endboundary : + ENDBOUNDARY + { + if (pstate->lstate.endboundary_string == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency"); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + if (strcmp(pstate->lstate.endboundary_string, $1)) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("invalid end boundary: %s", $1); + mm_error_setlineno(pstate->lstate.lineno); + return(-1); + } + dprintf2(pstate,"End of MIME message\n"); + } + ; + +body: + BODY + { + char *body; + size_t offset; + + dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", $1.start, $1.end, $1.end - $1.start); + + body = PARSE_readmessagepart($1.opaque_start, $1.start, $1.end, + &offset,yyscanner,pstate); + + if (body == NULL) { + return(-1); + } + pstate->current_mimepart->opaque_body = body; + pstate->current_mimepart->body = body + offset; + pstate->current_mimepart->opaque_length = $1.end - $1.start - 2 + offset; + pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset; + } + ; + +%% + +/* + * This function gets the specified part from the currently parsed message. + */ +static char * +PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end, + size_t *offset, yyscan_t yyscanner, struct parser_state *pstate) +{ + size_t body_size; + size_t current; + size_t start; + char *body; + + /* calculate start and offset markers for the opaque and + * header stripped body message. + */ + if (opaque_start > 0) { + /* Multipart message */ + if (real_start) { + if (real_start < opaque_start) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency (S:%d/O:%d)", + real_start, + opaque_start); + return(NULL); + } + start = opaque_start; + *offset = real_start - start; + /* Flat message */ + } else { + start = opaque_start; + *offset = 0; + } + } else { + start = real_start; + *offset = 0; + } + + /* The next three cases should NOT happen anytime */ + if (end <= start) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency,2"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + if (start < *offset) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + if (start < 0 || end < 0) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("internal incosistency,4"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + + /* XXX: do we want to enforce a maximum body size? make it a + * parser option? */ + + /* Read in the body message */ + body_size = end - start; + + if (body_size < 1) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("size of body cannot be < 1"); + mm_error_setlineno(pstate->lstate.lineno); + return(NULL); + } + + body = (char *)malloc(body_size + 1); + if (body == NULL) { + mm_errno = MM_ERROR_ERRNO; + return(NULL); + } + + /* Get the message body either from a stream or a memory + * buffer. + */ + if (mimeparser_yyget_in(yyscanner) != NULL) { + FILE *x = mimeparser_yyget_in(yyscanner); + current = ftell(x); + fseek(x, start - 1, SEEK_SET); + fread(body, body_size - 1, 1, x); + fseek(x, current, SEEK_SET); + } else if (pstate->lstate.message_buffer != NULL) { + strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size); + } + + return(body); + +} + +int +yyerror(struct parser_state *pstate, void *yyscanner, const char *str) +{ + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("%s", str); + mm_error_setlineno(pstate->lstate.lineno); + return -1; +} + +int +mimeparser_yywrap(void) +{ + return 1; +} + +/** + * Sets the boundary value for the current message + */ +int +set_boundary(char *str, struct parser_state *pstate) +{ + size_t blen; + + blen = strlen(str); + + pstate->lstate.boundary_string = (char *)malloc(blen + 3); + pstate->lstate.endboundary_string = (char *)malloc(blen + 5); + + if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) { + if (pstate->lstate.boundary_string != NULL) { + free(pstate->lstate.boundary_string); + } + if (pstate->lstate.endboundary_string != NULL) { + free(pstate->lstate.endboundary_string); + } + return -1; + } + + pstate->ctx->boundary = xstrdup(str); + + snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str); + snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str); + + return 0; +} + +/** + * Debug printf() + */ +int +dprintf2(struct parser_state *pstate, const char *fmt, ...) +{ + va_list ap; + char *msg; + if (pstate->debug == 0) return 1; + + va_start(ap, fmt); + vasprintf(&msg, fmt, ap); + va_end(ap); + + fprintf(stderr, "%s", msg); + free(msg); + + return 0; + +} + +void reset_environ(struct parser_state *pstate) +{ + pstate->lstate.lineno = 0; + pstate->lstate.boundary_string = NULL; + pstate->lstate.endboundary_string = NULL; + pstate->lstate.message_buffer = NULL; + pstate->mime_parts = 0; + pstate->debug = 0; + pstate->envelope = NULL; + pstate->temppart = NULL; + pstate->ctype = NULL; + pstate->current_mimepart = NULL; + + pstate->have_contenttype = 0; +} +/** + * Initializes the parser engine. + */ +int +PARSER_initialize(struct parser_state *pstate, void *yyscanner) +{ + void reset_lexer_state(void *yyscanner, struct parser_state *); +#if 0 + if (pstate->ctx != NULL) { + xfree(pstate->ctx); + pstate->ctx = NULL; + } + if (pstate->envelope != NULL) { + xfree(pstate->envelope); + pstate->envelope = NULL; + } + if (pstate->ctype != NULL) { + xfree(pstate->ctype); + pstate->ctype = NULL; + } +#endif + /* yydebug = 1; */ + reset_environ(pstate); + reset_lexer_state(yyscanner,pstate); + + pstate->envelope = mm_mimepart_new(); + pstate->current_mimepart = pstate->envelope; + pstate->ctype = mm_content_new(); + + pstate->have_contenttype = 0; + + return 1; +} + + diff --git a/trunk/main/minimime/mimeparser.yy.c b/trunk/main/minimime/mimeparser.yy.c new file mode 100644 index 000000000..4cf454c7c --- /dev/null +++ b/trunk/main/minimime/mimeparser.yy.c @@ -0,0 +1,2627 @@ +#line 2 "mimeparser.yy.c" + +#line 4 "mimeparser.yy.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 33 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +int mimeparser_yylex_init (yyscan_t* scanner); + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE mimeparser_yyrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE mimeparser_yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + int yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via mimeparser_yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void mimeparser_yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void mimeparser_yypop_buffer_state (yyscan_t yyscanner ); + +static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner ); +static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner ); +static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER mimeparser_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE mimeparser_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); + +void *mimeparser_yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *mimeparser_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void mimeparser_yyfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer mimeparser_yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + mimeparser_yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + mimeparser_yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 30 +#define YY_END_OF_BUFFER 31 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_acclist[179] = + { 0, + 31, 2, 29, 30, 28, 30, 2, 29, 30, 1, + 2, 29, 30, 3, 28, 30, 2, 29, 30, 29, + 30, 5, 28, 30, 29, 30, 4, 29, 30, 7, + 29, 30, 7, 13, 29, 30, 8, 28, 30, 7, + 29, 30, 7, 11, 29, 30, 7, 12, 29, 30, + 7, 12, 29, 30, 7, 9, 29, 30, 7, 10, + 29, 30, 8, 28, 30, 29, 30, 14, 29, 30, + 15, 29, 30, 29, 30, 20, 29, 30, 18, 28, + 30, 19, 29, 30, 20, 29, 30, 27, 29, 30, + 21, 28, 30, 27, 29, 30, 26, 29, 30, 26, + + 29, 30, 26, 29, 30, 22, 29, 30, 24, 28, + 30, 29, 30, 23, 29, 30, 25, 28, 30, 29, + 30, 28, 1, 1, 3, 28, 5, 28, 7, 7, + 7, 13, 6, 8, 28, 7, 12, 7, 12, 7, + 8, 28, 14, 20, 18, 28, 20, 21, 28, 22, + 24, 28, 23, 25, 28, 6, 9, 17, 20, 17, + 7, 7, 6, 7, 7, 9, 17, 20, 7, 7, + 6, 7, 7, 9, 16, 17, 20, 7 + } ; + +static yyconst flex_int16_t yy_accept[112] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 5, 7, 10, 14, + 17, 20, 22, 25, 27, 30, 33, 37, 40, 43, + 47, 51, 55, 59, 63, 66, 68, 71, 74, 76, + 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, + 109, 112, 114, 117, 120, 122, 123, 124, 125, 127, + 129, 130, 130, 131, 133, 134, 136, 138, 140, 140, + 141, 143, 144, 145, 147, 148, 150, 150, 151, 153, + 154, 156, 156, 156, 157, 158, 158, 160, 161, 162, + + 163, 165, 167, 169, 170, 171, 173, 175, 178, 179, + 179 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 1, 6, 1, 1, 1, 1, 7, 7, + 7, 1, 7, 7, 8, 9, 10, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 12, 13, 7, + 14, 7, 7, 7, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 1, 16, 1, 11, 1, 15, 15, 15, 15, + + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 1, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[18] = + { 0, + 1, 1, 2, 3, 4, 1, 4, 5, 4, 4, + 5, 4, 4, 4, 5, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[128] = + { 0, + 0, 2, 4, 15, 28, 38, 50, 0, 67, 0, + 6, 8, 10, 82, 12, 17, 19, 88, 21, 23, + 25, 30, 32, 34, 228, 253, 253, 224, 208, 253, + 219, 253, 253, 165, 253, 40, 95, 43, 43, 84, + 110, 119, 90, 98, 253, 155, 0, 253, 153, 0, + 253, 148, 131, 253, 253, 132, 253, 128, 116, 0, + 253, 119, 0, 253, 108, 253, 0, 80, 253, 253, + 100, 103, 103, 0, 118, 127, 0, 0, 131, 106, + 253, 0, 0, 253, 136, 253, 0, 0, 253, 0, + 253, 138, 139, 143, 144, 145, 153, 0, 0, 155, + + 160, 161, 81, 112, 169, 162, 173, 41, 173, 253, + 178, 183, 188, 193, 198, 203, 208, 34, 213, 215, + 220, 225, 230, 235, 237, 242, 247 + } ; + +static yyconst flex_int16_t yy_def[128] = + { 0, + 111, 111, 111, 111, 112, 112, 110, 7, 110, 9, + 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, + 117, 117, 112, 112, 110, 110, 110, 110, 118, 110, + 110, 110, 110, 110, 110, 119, 119, 110, 119, 119, + 119, 41, 119, 119, 110, 110, 120, 110, 110, 121, + 110, 110, 121, 110, 110, 110, 110, 110, 110, 122, + 110, 110, 123, 110, 110, 110, 118, 118, 110, 110, + 119, 110, 119, 37, 110, 110, 41, 42, 110, 119, + 110, 120, 121, 110, 124, 110, 125, 122, 110, 123, + 110, 126, 110, 126, 126, 110, 124, 125, 127, 127, + + 127, 127, 97, 127, 127, 105, 105, 97, 127, 0, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110 + } ; + +static yyconst flex_int16_t yy_nxt[271] = + { 0, + 110, 110, 27, 28, 27, 28, 27, 28, 27, 49, + 27, 49, 51, 52, 55, 56, 29, 30, 31, 55, + 56, 55, 58, 61, 62, 61, 62, 64, 65, 29, + 33, 34, 64, 65, 27, 49, 27, 49, 67, 35, + 33, 34, 72, 73, 75, 76, 73, 75, 108, 35, + 36, 37, 38, 39, 37, 40, 41, 42, 42, 36, + 42, 36, 43, 44, 42, 41, 37, 32, 32, 45, + 46, 47, 48, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 32, 32, 51, 52, 72, 73, 108, 53, + 55, 58, 79, 80, 68, 59, 74, 72, 73, 74, + + 72, 73, 72, 73, 92, 93, 73, 92, 96, 73, + 91, 74, 72, 73, 72, 109, 77, 77, 77, 75, + 77, 89, 75, 87, 77, 77, 78, 78, 94, 78, + 86, 94, 95, 78, 86, 95, 83, 83, 85, 100, + 92, 83, 100, 92, 101, 102, 95, 101, 102, 95, + 84, 83, 83, 83, 83, 66, 105, 81, 83, 105, + 103, 106, 107, 106, 106, 107, 106, 70, 83, 83, + 105, 72, 109, 105, 107, 93, 109, 107, 26, 26, + 26, 26, 26, 32, 32, 32, 32, 32, 50, 50, + 50, 50, 50, 54, 54, 54, 54, 54, 57, 57, + + 57, 57, 57, 60, 60, 60, 60, 60, 63, 63, + 63, 63, 63, 71, 71, 71, 71, 71, 82, 82, + 83, 69, 68, 83, 83, 88, 66, 110, 88, 88, + 90, 110, 110, 90, 90, 97, 110, 110, 97, 97, + 98, 98, 99, 110, 99, 99, 99, 104, 110, 104, + 104, 104, 25, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110 + } ; + +static yyconst flex_int16_t yy_chk[271] = + { 0, + 0, 0, 1, 1, 2, 2, 3, 3, 11, 11, + 12, 12, 13, 13, 15, 15, 2, 4, 4, 16, + 16, 17, 17, 19, 19, 20, 20, 21, 21, 4, + 5, 5, 22, 22, 23, 23, 24, 24, 118, 5, + 6, 6, 36, 36, 38, 39, 39, 38, 108, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 14, 14, 40, 40, 103, 14, + 18, 18, 43, 43, 68, 18, 37, 37, 37, 37, + + 44, 44, 71, 71, 72, 73, 73, 72, 80, 80, + 65, 37, 41, 41, 104, 104, 41, 41, 41, 75, + 41, 62, 75, 59, 41, 41, 42, 42, 76, 42, + 58, 76, 79, 42, 56, 79, 85, 85, 53, 92, + 93, 85, 92, 93, 94, 95, 96, 94, 95, 96, + 52, 85, 85, 97, 97, 49, 100, 46, 97, 100, + 97, 101, 102, 106, 101, 102, 106, 34, 97, 97, + 105, 105, 105, 105, 107, 109, 109, 107, 111, 111, + 111, 111, 111, 112, 112, 112, 112, 112, 113, 113, + 113, 113, 113, 114, 114, 114, 114, 114, 115, 115, + + 115, 115, 115, 116, 116, 116, 116, 116, 117, 117, + 117, 117, 117, 119, 119, 119, 119, 119, 120, 120, + 121, 31, 29, 121, 121, 122, 28, 25, 122, 122, + 123, 0, 0, 123, 123, 124, 0, 0, 124, 124, + 125, 125, 126, 0, 126, 126, 126, 127, 0, 127, + 127, 127, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110 + } ; + +/* Table of booleans, true if rule could match eol. */ +static yyconst flex_int32_t yy_rule_can_match_eol[31] = + { 0, +0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, }; + +#define REJECT \ +{ \ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \ +yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \ +++yyg->yy_lp; \ +goto find_rule; \ +} + +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "mimeparser.l" +#line 2 "mimeparser.l" +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * This is a lexer file for parsing MIME compatible messages. It is intended + * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still + * has quite a few problems: + * + * - The parsing could probably be done in a more elegant way + * - I don't know what performance impact REJECT has on the parser + */ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "mimeparser.h" +#include "mimeparser.tab.h" + +#define NAMEOF(v) #v +/* BC() is a debug wrapper for lex' BEGIN() macro */ +#define BC(x) do { \ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); \ + BEGIN(x); \ + lstate->condition = x; \ +} while(0); + +#define ZERO(x) memset(x, '\0', sizeof(x)) + +#define PREALLOC_BUFFER 100000 +#undef YY_BUF_SIZE +#define YY_BUF_SIZE 65536 + +enum header_states +{ + STATE_MAIL = 0, + STATE_CTYPE, + STATE_CDISP, + STATE_CENC, + STATE_MIME +}; + + + + + + + + + + + + + + +#line 658 "mimeparser.yy.c" + +#define INITIAL 0 +#define headers 1 +#define header 2 +#define headervalue 3 +#define tspecialvalue 4 +#define comment 5 +#define body 6 +#define postamble 7 +#define preamble 8 +#define boundary 9 +#define endboundary 10 +#define endoffile 11 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + yy_state_type *yy_state_buf; + yy_state_type *yy_state_ptr; + char *yy_full_match; + int yy_lp; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int mimeparser_yylex_destroy (yyscan_t yyscanner ); + +int mimeparser_yyget_debug (yyscan_t yyscanner ); + +void mimeparser_yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner ); + +void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *mimeparser_yyget_in (yyscan_t yyscanner ); + +void mimeparser_yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *mimeparser_yyget_out (yyscan_t yyscanner ); + +void mimeparser_yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +int mimeparser_yyget_leng (yyscan_t yyscanner ); + +char *mimeparser_yyget_text (yyscan_t yyscanner ); + +int mimeparser_yyget_lineno (yyscan_t yyscanner ); + +int mimeparser_yyget_column (yyscan_t yyscanner); + +void mimeparser_yyset_lineno (int line_number ,yyscan_t yyscanner ); + +void mimeparser_yyset_column (int column_no , yyscan_t yyscanner); + +YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner ); + +void mimeparser_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int mimeparser_yywrap (yyscan_t yyscanner ); +#else +extern int mimeparser_yywrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int mimeparser_yylex \ + (YYSTYPE * yylval_param ,yyscan_t yyscanner); + +#define YY_DECL int mimeparser_yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 94 "mimeparser.l" + + +#line 909 "mimeparser.yy.c" + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + /* Create the reject buffer large enough to save one state per allowed character. */ + if ( ! yyg->yy_state_buf ) + yyg->yy_state_buf = (yy_state_type *)mimeparser_yyalloc(YY_STATE_BUF_SIZE ,yyscanner); + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + mimeparser_yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + mimeparser_yy_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); + + yyg->yy_state_ptr = yyg->yy_state_buf; + *yyg->yy_state_ptr++ = yy_current_state; + +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 111 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yyg->yy_state_ptr++ = yy_current_state; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 253 ); + +yy_find_action: + yy_current_state = *--yyg->yy_state_ptr; + yyg->yy_lp = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[yyg->yy_lp]; + { + yyg->yy_full_match = yy_cp; + break; + } + } + --yy_cp; + yy_current_state = *--yyg->yy_state_ptr; + yyg->yy_lp = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + int yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +#line 96 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + + yylval_param->string=strdup(yytext); + lstate->current_pos += yyleng; + BC(header); + + /* Depending on what header we are processing, we enter a different + * state and return a different value. + */ + if (!strcasecmp(yytext, "Content-Type")) { + lstate->header_state = STATE_CTYPE; + return CONTENTTYPE_HEADER; + } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) { + lstate->header_state = STATE_CENC; + return CONTENTENCODING_HEADER; + } else if (!strcasecmp(yytext, "Content-Disposition")) { + lstate->header_state = STATE_CDISP; + return CONTENTDISPOSITION_HEADER; + } else if (!strcasecmp(yytext, "MIME-Version")) { + lstate->header_state = STATE_MAIL; + return MIMEVERSION_HEADER; + } else { + lstate->header_state = STATE_MAIL; + return MAIL_HEADER; + } +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 124 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + /* dprintf2("Unknown header char: %c\n", *yytext); */ + lstate->current_pos += yyleng; + return ANY; +} + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 131 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->lineno++; + + lstate->current_pos += yyleng; + + /* This marks the end of headers. Depending on whether we are in the + * envelope currently we need to parse either a body or the preamble + * now. + */ + if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) { + BC(body); + lstate->body_start = lstate->current_pos; + } else { + lstate->is_envelope = 0; + lstate->preamble_start = lstate->current_pos; + BC(preamble); + } + + return ENDOFHEADERS; +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 153 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(headervalue); + lstate->current_pos += yyleng; + return COLON; +} + YY_BREAK +case 5: +/* rule 5 can match eol */ +YY_RULE_SETUP +#line 160 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(headers); + /* dprintf2("Invalid header, returning EOL\n"); */ + lstate->current_pos += yyleng; + return EOL; +} + YY_BREAK +case 6: +/* rule 6 can match eol */ +YY_RULE_SETUP +#line 168 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +#line 173 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) { + REJECT; + } + lstate->current_pos += yyleng; + while (*yytext && isspace(*yytext)) yytext++; + /* Do we actually have a header value? */ + if (*yytext == '\0') { + yylval_param->string = strdup(""); + } else { + yylval_param->string=strdup(yytext); + lstate->lineno += count_lines(yytext); + } + return WORD; +} + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 190 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + /* marks the end of one header line */ + lstate->lineno++; + BC(headers); + lstate->current_pos += yyleng; + return EOL; +} + YY_BREAK +case 9: +/* rule 9 can match eol */ +YY_RULE_SETUP +#line 199 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->lineno += count_lines(yytext); + lstate->current_pos += yyleng; + return SEMICOLON; +} + YY_BREAK +case 10: +YY_RULE_SETUP +#line 206 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; + return EQUAL; +} + YY_BREAK +case 11: +YY_RULE_SETUP +#line 212 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(tspecialvalue); + lstate->current_pos += yyleng; + return *yytext; +} + YY_BREAK +case 12: +YY_RULE_SETUP +#line 219 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + yylval_param->string=strdup(yytext); + lstate->lineno += count_lines(yytext); + lstate->current_pos += yyleng; + return WORD; +} + YY_BREAK +case 13: +YY_RULE_SETUP +#line 227 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + YY_BREAK +case 14: +YY_RULE_SETUP +#line 232 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->lineno += count_lines(yytext); + yylval_param->string=strdup(yytext); + lstate->current_pos += yyleng; + return TSPECIAL; +} + YY_BREAK +case 15: +YY_RULE_SETUP +#line 240 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(headervalue); + lstate->current_pos += yyleng; + return *yytext; +} + YY_BREAK +case 16: +YY_RULE_SETUP +#line 247 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + /** + * Make sure we only catch matching boundaries, and not other lines + * that begin and end with two dashes. If we have catched a valid + * end boundary, which actually ends a body, we save the current + * position, put the token back on the input stream and let the + * endboundary condition parse the actual token. + */ + if (lstate->endboundary_string != NULL) { + if (strcmp(lstate->endboundary_string, yytext)) { + /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */ + REJECT; + } else { + lstate->current_pos += yyleng; + /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */ + if (lstate->body_start) { + yylval_param->position.opaque_start = + lstate->body_opaque_start; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos - yyleng; + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + yyless(0); + BC(endboundary); + return BODY; + } + } + } else { + } + + REJECT; +} + YY_BREAK +case 17: +YY_RULE_SETUP +#line 282 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + /** + * Make sure we only catch matching boundaries, and not other lines + * that begin with two dashes. + */ + if (lstate->boundary_string != NULL) { + if (strcmp(lstate->boundary_string, yytext)) { + /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/ + REJECT; + } else { + /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/ + if (lstate->body_start) { + yylval_param->position.opaque_start = lstate->body_opaque_start; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos; + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + yyless(0); + BC(boundary); + return BODY; + } else if (lstate->preamble_start) { + yylval_param->position.start = lstate->preamble_start; + yylval_param->position.end = lstate->current_pos; + lstate->preamble_start = lstate->preamble_end = 0; + yyless(0); + BC(boundary); + return PREAMBLE; + } else { + BC(boundary); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return(BOUNDARY); + } + } + } else { + } + + REJECT; +} + YY_BREAK +case 18: +/* rule 18 can match eol */ +YY_RULE_SETUP +#line 324 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; + lstate->lineno++; +} + YY_BREAK +case 19: +YY_RULE_SETUP +#line 330 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; + /* dprintf2("stray CR in body...\n"); */ +} + YY_BREAK +case 20: +YY_RULE_SETUP +#line 336 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + YY_BREAK +case YY_STATE_EOF(body): +#line 341 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + if (lstate->boundary_string == NULL && lstate->body_start) { + yylval_param->position.opaque_start = 0; + yylval_param->position.start = lstate->body_start; + yylval_param->position.end = lstate->current_pos; + lstate->body_start = 0; + return BODY; + } else if (lstate->body_start) { + return POSTAMBLE; + } + yyterminate(); +} + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +#line 355 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + /* dprintf2("Preamble CR/LF at line %d\n", lineno); */ + lstate->lineno++; + lstate->current_pos += yyleng; +} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 362 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return BOUNDARY; +} + YY_BREAK +case 23: +YY_RULE_SETUP +#line 369 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + yylval_param->string = strdup(yytext); + lstate->current_pos += yyleng; + return ENDBOUNDARY; +} + YY_BREAK +case 24: +/* rule 24 can match eol */ +YY_RULE_SETUP +#line 376 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(headers); + lstate->lineno++; + lstate->current_pos += yyleng; + lstate->body_opaque_start = lstate->current_pos; + return EOL; +} + YY_BREAK +case 25: +/* rule 25 can match eol */ +YY_RULE_SETUP +#line 385 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + BC(postamble); + lstate->lineno++; + lstate->current_pos += yyleng; +} + YY_BREAK +case 26: +YY_RULE_SETUP +#line 392 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + YY_BREAK +case 27: +YY_RULE_SETUP +#line 398 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; +} + YY_BREAK +case 28: +/* rule 28 can match eol */ +YY_RULE_SETUP +#line 403 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->lineno++; + lstate->current_pos += yyleng; + return EOL; +} + YY_BREAK +case 29: +YY_RULE_SETUP +#line 410 "mimeparser.l" +{ + struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); + lstate->current_pos += yyleng; + return((int)*yytext); +} + YY_BREAK +case 30: +YY_RULE_SETUP +#line 417 "mimeparser.l" +ECHO; + YY_BREAK +#line 1438 "mimeparser.yy.c" + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(headers): + case YY_STATE_EOF(header): + case YY_STATE_EOF(headervalue): + case YY_STATE_EOF(tspecialvalue): + case YY_STATE_EOF(comment): + case YY_STATE_EOF(postamble): + case YY_STATE_EOF(preamble): + case YY_STATE_EOF(boundary): + case YY_STATE_EOF(endboundary): + case YY_STATE_EOF(endoffile): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * mimeparser_yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( mimeparser_yywrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of mimeparser_yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + mimeparser_yyrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); + + yyg->yy_state_ptr = yyg->yy_state_buf; + *yyg->yy_state_ptr++ = yy_current_state; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 111 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yyg->yy_state_ptr++ = yy_current_state; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + + register YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 111 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 110); + if ( ! yy_is_jam ) + *yyg->yy_state_ptr++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + if ( c == '\n' ){ + --yylineno; + } + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + mimeparser_yyrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( mimeparser_yywrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void mimeparser_yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + mimeparser_yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + mimeparser_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + mimeparser_yy_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * mimeparser_yypop_buffer_state(); + * mimeparser_yypush_buffer_state(new_buffer); + */ + mimeparser_yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + mimeparser_yy_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (mimeparser_yywrap()) processing, but the only time this flag + * is looked at is after mimeparser_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) mimeparser_yyalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + mimeparser_yy_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with mimeparser_yy_create_buffer() + * @param yyscanner The scanner object. + */ + void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + mimeparser_yyfree((void *) b->yy_ch_buf ,yyscanner ); + + mimeparser_yyfree((void *) b ,yyscanner ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a mimeparser_yyrestart() or at EOF. + */ + static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + mimeparser_yy_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then mimeparser_yy_init_buffer was _probably_ + * called from mimeparser_yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + mimeparser_yy_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + mimeparser_yyensure_buffer_stack(yyscanner); + + /* This block is copied from mimeparser_yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from mimeparser_yy_switch_to_buffer. */ + mimeparser_yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void mimeparser_yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + mimeparser_yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner) +{ + int num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE mimeparser_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + mimeparser_yy_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to mimeparser_yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * mimeparser_yy_scan_bytes() instead. + */ +YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return mimeparser_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to mimeparser_yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) mimeparser_yyalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = mimeparser_yy_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in mimeparser_yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int mimeparser_yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int mimeparser_yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *mimeparser_yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *mimeparser_yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int mimeparser_yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *mimeparser_yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void mimeparser_yyset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "mimeparser_yyset_lineno called with no buffer" , yyscanner); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void mimeparser_yyset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "mimeparser_yyset_column called with no buffer" , yyscanner); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see mimeparser_yy_switch_to_buffer + */ +void mimeparser_yyset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void mimeparser_yyset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int mimeparser_yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void mimeparser_yyset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void mimeparser_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* mimeparser_yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int mimeparser_yylex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) mimeparser_yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from mimeparser_yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + + yyg->yy_state_buf = 0; + yyg->yy_state_ptr = 0; + yyg->yy_full_match = 0; + yyg->yy_lp = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * mimeparser_yylex_init() + */ + return 0; +} + +/* mimeparser_yylex_destroy is for both reentrant and non-reentrant scanners. */ +int mimeparser_yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + mimeparser_yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + mimeparser_yyfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + mimeparser_yyfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + mimeparser_yyfree ( yyg->yy_state_buf , yyscanner); + yyg->yy_state_buf = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * mimeparser_yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + mimeparser_yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *mimeparser_yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *mimeparser_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void mimeparser_yyfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see mimeparser_yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 417 "mimeparser.l" + + + +void reset_lexer_state(void *yyscanner, struct parser_state *pstate) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + struct lexer_state *lstate = &(pstate->lstate); + + mimeparser_yyset_extra((void*)lstate,yyscanner); + BEGIN(0); + lstate->header_state = STATE_MAIL; + lstate->lineno = 0; + lstate->current_pos = 1; + lstate->condition = 0; + + lstate->is_envelope = 1; + + lstate->message_len = 0; + lstate->buffer_length = 0; + + /* temporary marker variables */ + lstate->body_opaque_start = 0; + lstate->body_start = 0; + lstate->body_end = 0; + lstate->preamble_start = 0; + lstate->preamble_end = 0; + lstate->postamble_start = 0; + lstate->postamble_end = 0; +} + +void +PARSER_setbuffer(const char *string, yyscan_t scanner) +{ + struct lexer_state *lstate = mimeparser_yyget_extra(scanner); + lstate->message_buffer = string; + mimeparser_yy_scan_string(string,scanner); +} + +void +PARSER_setfp(FILE *fp, yyscan_t scanner) +{ + /* looks like a bug in bison 2.2a -- the wrong code is generated for mimeparser_yyset_in !! */ + struct yyguts_t * yyg = (struct yyguts_t*) scanner; + yyg->yyin_r = fp; + + if (0) { + /* This is just to make a compiler warning go away */ + yyunput(0, NULL, scanner); + } +} + +/** + * Counts how many lines a given string represents in the message (in case of + * folded header values, for example, or a message body). + */ +int +count_lines(char *txt) +{ + char *o; + int line; + + line = 0; + + for (o = txt; *o != '\0'; o++) + if (*o == '\n') + line++; + + return line; +} + diff --git a/trunk/main/minimime/minimime.c b/trunk/main/minimime/minimime.c new file mode 100644 index 000000000..1b324585c --- /dev/null +++ b/trunk/main/minimime/minimime.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * MiniMIME test program + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> + +#include "mm.h" + +void +usage(void) +{ + fprintf(stderr, + "MiniMIME test suite\n" + "Usage: ./minimime [-m] <filename>\n\n" + " -m : use memory based scanning\n\n" + ); + exit(1); +} + +int +main(int argc, char **argv) +{ + MM_CTX *ctx; + struct mm_mimeheader *header, *lastheader; + struct mm_warning *lastwarning; + struct mm_mimepart *part; + struct mm_content *ct; + int parts, i; + struct stat st; + int fd; + char *buf; + int scan_mode = 0; + + lastheader = NULL; + + while ((i = getopt(argc, argv, "m")) != -1) { + switch(i) { + case 'm': + scan_mode = 1; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + } + +#ifdef __HAVE_LEAK_DETECTION + /* Initialize memory leak detection if compiled in */ + MM_leakd_init(); +#endif + + /* Initialize MiniMIME library */ + mm_library_init(); + + /* Register all default codecs (base64/qp) */ + mm_codec_registerdefaultcodecs(); + + do { + /* Create a new context */ + ctx = mm_context_new(); + + /* Parse a file into our context */ + if (scan_mode == 0) { + i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0); + } else { + if (stat(argv[0], &st) == -1) { + fprintf(stderr, "INFO: stat"); + } + + if ((fd = open(argv[0], O_RDONLY)) == -1) { + fdprintf(stderr, "INFO: open"); + } + + buf = (char *)malloc(st.st_size); + if (buf == NULL) { + fdprintf(stderr, "INFO: malloc"); + } + + if (read(fd, buf, st.st_size) != st.st_size) { + fdprintf(stderr, "INFO: read"); + } + + close(fd); + buf[st.st_size] = '\0'; + + i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0); + } + + if (i == -1 || mm_errno != MM_ERROR_NONE) { + printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno()); + exit(1); + } + + /* Get the number of MIME parts */ + parts = mm_context_countparts(ctx); + if (parts == 0) { + printf("ERROR: got zero MIME parts, huh\n"); + exit(1); + } else { + if (mm_context_iscomposite(ctx)) { + printf("Got %d MIME parts\n", parts - 1); + } else { + printf("Flat message (not multipart)\n"); + } + } + + /* Get the main MIME part */ + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + fprintf(stderr, "Could not get envelope part\n"); + exit(1); + } + + printf("Printing envelope headers:\n"); + /* Print all headers */ + if (mm_mimepart_headers_start(part, &lastheader) == -1) { + fprintf(stderr, "No headers in envelope\n"); + exit(1); + } + while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) { + printf("%s: %s\n", header->name, header->value); + } + + printf("%s\n", mm_content_tostring(part->type)); + printf("\n"); + + ct = part->type; + assert(ct != NULL); + + if (mm_context_iscomposite(ctx) == 0) { + printf("Printing body part for FLAT message:\n"); + part = mm_context_getpart(ctx, 0); + printf("%s", part->body); + } + + /* Loop through all MIME parts beginning with 1 */ + for (i = 1; i < mm_context_countparts(ctx); i++) { + char *decoded; + + printf("Printing headers for MIME part %d\n", i); + + /* Get the current MIME entity */ + part = mm_context_getpart(ctx, i); + if (part == NULL) { + fprintf(stderr, "Should have %d parts but " + "couldn't retrieve part %d", + mm_context_countparts(ctx), i); + exit(1); + } + + /* Print all headers */ + if (mm_mimepart_headers_start(part, &lastheader) == -1) { + printf("Ups no headers\n"); + } + while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) { + printf("%s: %s\n", header->name, header->value); + } + + printf("Part Type: %s\n", mm_content_tostring(part->type)); + + /* Print MIME part body */ + printf("\nPrinting message BODY:\n%s\n", (char *)part->opaque_body); + decoded = mm_mimepart_decode(part); + if (decoded != NULL) { + printf("DECODED:\n%s\n", decoded); + free(decoded); + } + } + + /* Print out all warnings that we might have received */ + if (mm_context_haswarnings(ctx) > 0) { + lastwarning = NULL; + fprintf(stderr, "WARNINGS:\n"); +#if 0 + while ((warning = mm_warning_next(ctx, &lastwarning)) + != NULL) { + fprintf(stderr, " -> %s\n", warning->message); + } +#endif + } + + printf("ENVELOPE:\n"); + + do { + char *env; + size_t env_len; + + mm_context_flatten(ctx, &env, &env_len, 0); + printf("%s", env); + + } while (0); + + mm_context_free(ctx); + ctx = NULL; + +#ifdef __HAVE_LEAK_DETECTION + MM_leakd_printallocated(); +#endif + + } while (0); + + return 0; +} diff --git a/trunk/main/minimime/mm.h b/trunk/main/minimime/mm.h new file mode 100644 index 000000000..449cd8f72 --- /dev/null +++ b/trunk/main/minimime/mm.h @@ -0,0 +1,367 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MM_H_INCLUDED +#define _MM_H_INCLUDED + +#include "asterisk.h" +#include "mm_queue.h" +#include "mm_mem.h" + +#define MM_MIME_LINELEN 998 +#define MM_BASE64_LINELEN 76 + +TAILQ_HEAD(mm_mimeheaders, mm_mimeheader); +TAILQ_HEAD(mm_mimeparts, mm_mimepart); +TAILQ_HEAD(mm_params, mm_param); +SLIST_HEAD(mm_codecs, mm_codec); +SLIST_HEAD(mm_warnings, mm_warning); + +/* + * Parser modes + */ +enum mm_parsemodes +{ + /** Parse loosely, accept some MIME quirks */ + MM_PARSE_LOOSE = 0, + /** Parse as strict as possible */ + MM_PARSE_STRICT +}; + +/* + * Available parser flags + */ +enum mm_parseflags +{ + MM_PARSE_NONE = (1L << 0), + MM_PARSE_STRIPCOMMENTS = (1L << 1) +}; + +/* + * Enumeration of MIME encodings + */ +enum mm_encoding +{ + MM_ENCODING_NONE = 0, + MM_ENCODING_BASE64, + MM_ENCODING_QUOTEDPRINTABLE, + MM_ENCODING_UNKNOWN +}; + +/* + * Message type + */ +enum mm_messagetype +{ + /** Flat message */ + MM_MSGTYPE_FLAT = 0, + /** Composite message */ + MM_MSGTYPE_MULTIPART +}; + +/* + * Enumeration of error categories + */ +enum mm_errors +{ + MM_ERROR_NONE = 0, + MM_ERROR_UNDEF, + MM_ERROR_ERRNO, + MM_ERROR_PARSE, + MM_ERROR_MIME, + MM_ERROR_CODEC, + MM_ERROR_PROGRAM +}; + +enum mm_warning_ids +{ + MM_WARN_NONE = 0, + MM_WARN_PARSE, + MM_WARN_MIME, + MM_WARN_CODEC +}; + +enum mm_addressfields { + MM_ADDR_TO = 0, + MM_ADDR_CC, + MM_ADDR_BCC, + MM_ADDR_FROM, + MM_ADDR_SENDER, + MM_ADDR_REPLY_TO +}; + +enum mm_flatten_flags { + MM_FLATTEN_NONE = 0, + MM_FLATTEN_SKIPENVELOPE = (1L << 1), + MM_FLATTEN_OPAQUE = (1L << 2), + MM_FLATTEN_NOPREAMBLE = (1L << 3) +}; + +/* + * More information about an error + */ +struct mm_error_data +{ + int error_id; + int error_where; + int lineno; + char error_msg[128]; +}; + +extern int mm_errno; +extern struct mm_error_data mm_error; + +enum mm_warning_code +{ + MM_WARNING_NONE = 0, + MM_WARNING_INVHDR, +}; + +/* + * A parser warning + */ +struct mm_warning +{ + enum mm_warning_code warning; + uint32_t lineno; + SLIST_ENTRY(mm_warning) next; +}; + +/* + * Representation of a MiniMIME codec object + */ +struct mm_codec +{ + enum mm_encoding id; + char *encoding; + + char *(*encoder)(char *, uint32_t); + char *(*decoder)(char *); + + SLIST_ENTRY(mm_codec) next; +}; + +/* + * Representation of a MIME Content-Type parameter + */ +struct mm_param +{ + char *name; + char *value; + + TAILQ_ENTRY(mm_param) next; +}; + +/* + * Representation of a mail or MIME header field + */ +struct mm_mimeheader +{ + char *name; + char *value; + + struct mm_params params; + + TAILQ_ENTRY(mm_mimeheader) next; +}; + +/* + * Representation of a MIME Content-Type object + */ +struct mm_content +{ + char *maintype; + char *subtype; + char *disposition_type; + + struct mm_params type_params; + struct mm_params disposition_params; + + char *encstring; + enum mm_encoding encoding; +}; + +/* + * Representation of a MIME part + */ +struct mm_mimepart +{ + struct mm_mimeheaders headers; + + size_t opaque_length; + char *opaque_body; + + size_t length; + char *body; + + struct mm_content *type; + + TAILQ_ENTRY(mm_mimepart) next; +}; + +/* + * Represantation of a MiniMIME context + */ +struct mm_context +{ + struct mm_mimeparts parts; + enum mm_messagetype messagetype; + struct mm_warnings warnings; + struct mm_codecs codecs; + char *boundary; + char *preamble; + size_t max_message_size; +}; + +typedef struct mm_context MM_CTX; +typedef struct mm_context mm_ctx_t; + +char *mm_unquote(const char *); +char *mm_uncomment(const char *); +char *mm_stripchars(char *, char *); +char *mm_addchars(char *, char *, uint16_t); +int mm_gendate(char **); +void mm_striptrailing(char **, const char *); +int mm_mimeutil_genboundary(char *, size_t, char **); + +int mm_library_init(void); +int mm_library_isinitialized(void); + +int mm_parse_mem(MM_CTX *, const char *, int, int); +int mm_parse_file(MM_CTX *, const char *, int, int); +int mm_parse_fileptr(MM_CTX *, FILE *, int, int); + +MM_CTX *mm_context_new(void); +void mm_context_free(MM_CTX *); +int mm_context_attachpart(MM_CTX *, struct mm_mimepart *); +int mm_context_deletepart(MM_CTX *, int, int); +int mm_context_countparts(MM_CTX *); +struct mm_mimepart *mm_context_getpart(MM_CTX *, int); +int mm_context_iscomposite(MM_CTX *); +int mm_context_haswarnings(MM_CTX *); +int mm_context_flatten(MM_CTX *, char **, size_t *, int); + +int mm_envelope_getheaders(MM_CTX *, char **, size_t *); +int mm_envelope_setheader(MM_CTX *, const char *, const char *, ...); + +struct mm_mimeheader *mm_mimeheader_new(void); +void mm_mimeheader_free(struct mm_mimeheader *); +struct mm_mimeheader *mm_mimeheader_generate(const char *, const char *); +int mm_mimeheader_uncomment(struct mm_mimeheader *); +int mm_mimeheader_uncommentbyname(struct mm_mimepart *, const char *); +int mm_mimeheader_uncommentall(struct mm_mimepart *); +int mm_mimeheader_tostring(struct mm_mimeheader *); +char *mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name); +int mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param); + +struct mm_mimepart *mm_mimepart_new(void); +void mm_mimepart_free(struct mm_mimepart *); +int mm_mimepart_attachheader(struct mm_mimepart *, struct mm_mimeheader *); +int mm_mimepart_countheaders(struct mm_mimepart *part); +int mm_mimepart_countheaderbyname(struct mm_mimepart *, const char *); +struct mm_mimeheader *mm_mimepart_getheaderbyname(struct mm_mimepart *, const char *, int); +const char *mm_mimepart_getheadervalue(struct mm_mimepart *, const char *, int); +int mm_mimepart_headers_start(struct mm_mimepart *, struct mm_mimeheader **); +struct mm_mimeheader *mm_mimepart_headers_next(struct mm_mimepart *, struct mm_mimeheader **); +char *mm_mimepart_decode(struct mm_mimepart *); +struct mm_content *mm_mimepart_getcontent(struct mm_mimepart *); +size_t mm_mimepart_getlength(struct mm_mimepart *); +char *mm_mimepart_getbody(struct mm_mimepart *, int); +void mm_mimepart_attachcontenttype(struct mm_mimepart *, struct mm_content *); +int mm_mimepart_setdefaultcontenttype(struct mm_mimepart *, int); +int mm_mimepart_flatten(struct mm_mimepart *, char **, size_t *, int); +struct mm_mimepart *mm_mimepart_fromfile(const char *); + +struct mm_content *mm_content_new(void); +void mm_content_free(struct mm_content *); +int mm_content_attachtypeparam(struct mm_content *, struct mm_param *); +int mm_content_attachdispositionparam(struct mm_content *, struct mm_param *); +struct mm_content *mm_content_parse(const char *, int); +char *mm_content_gettypeparambyname(struct mm_content *, const char *); +char *mm_content_getdispositionparambyname(struct mm_content *, const char *); +struct mm_param *mm_content_gettypeparamobjbyname(struct mm_content *, const char *); +struct mm_param *mm_content_getdispositionparamobjbyname(struct mm_content *, const char *); +int mm_content_setmaintype(struct mm_content *, char *, int); +int mm_content_setsubtype(struct mm_content *, char *, int); +int mm_content_settype(struct mm_content *, const char *, ...); +int mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy); +char *mm_content_getmaintype(struct mm_content *); +char *mm_content_getsubtype(struct mm_content *); +char *mm_content_gettype(struct mm_content *); +char *mm_content_getdispositiontype(struct mm_content *ct); +int mm_content_iscomposite(struct mm_content *); +int mm_content_isvalidencoding(const char *); +int mm_content_setencoding(struct mm_content *, const char *); +char *mm_content_typeparamstostring(struct mm_content *); +char *mm_content_dispositionparamstostring(struct mm_content *); +char *mm_content_tostring(struct mm_content *); + +struct mm_param *mm_param_new(void); +void mm_param_free(struct mm_param *); + +char *mm_flatten_mimepart(struct mm_mimepart *); +char *mm_flatten_context(MM_CTX *); + +int mm_codec_isregistered(const char *); +int mm_codec_hasdecoder(const char *); +int mm_codec_hasencoder(const char *); +int mm_codec_register(const char *, char *(*encoder)(char *, uint32_t), char *(*decoder)(char *)); +int mm_codec_unregister(const char *); +int mm_codec_unregisterall(void); +void mm_codec_registerdefaultcodecs(void); + +char *mm_base64_decode(char *); +char *mm_base64_encode(char *, uint32_t); + +void mm_error_init(void); +void mm_error_setmsg(const char *, ...); +void mm_error_setlineno(int lineno); +char *mm_error_string(void); +int mm_error_lineno(void); + +void mm_warning_add(MM_CTX *, int, const char *, ...); +struct mm_warning *mm_warning_next(MM_CTX *, struct mm_warning **); + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif /* ! HAVE_STRLCPY */ +#ifndef HAVE_STRLCAT +size_t strlcat(char *, const char *, size_t); +#endif /* ! HAVE_STRLCAT */ + +#define MM_ISINIT() do { \ + assert(mm_library_isinitialized() == 1); \ +} while (0); + +#endif /* ! _MM_H_INCLUDED */ diff --git a/trunk/main/minimime/mm_base64.c b/trunk/main/minimime/mm_base64.c new file mode 100644 index 000000000..ee6dc183b --- /dev/null +++ b/trunk/main/minimime/mm_base64.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2003 Jann Fischer <jfi@openbsd.de> + * All rights reserved. + * + * XXX: This piece of software is not nearly MIME compatible as it should be. + * + * This is based on third-party code, see the copyright notice below. + * + */ + +/* $Id$ */ + +/*********************************************************** + Copyright 1998 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Carnegie Mellon +University not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +******************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "mm_internal.h" + +#define XX 127 + +static int _mm_base64_decode(char *); +static char *_mm_base64_encode(char *, uint32_t); + +/* + * Tables for encoding/decoding base64 + */ +static const char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char index_64[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, + 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, + XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; +#define CHAR64(c) (index_64[(unsigned char)(c)]) + +/* + * mm_base64_decode() + * + * Decodes the data pointed to by 'data' from the BASE64 encoding to the data + * format it was encoded from. Returns a pointer to a string on success or + * NULL on error. The string returned needs to be freed by the caller at some + * later point. + * + */ +char * +mm_base64_decode(char *data) +{ + char *buf; + + assert(data != NULL); + + buf = mm_stripchars(data, "\r\n"); + assert(buf != NULL); + + _mm_base64_decode(buf); + assert(buf != NULL); + return(buf); +} + +/* + * mm_base64_encode() + * + * Encodes the data pointed to by 'data', which is of the length specified in + * 'len' to the BASE64 format. Returns a pointer to a string containing the + * BASE64 encoding, whose lines are broken at the MIME recommended linelength + * of 76 characters. If an error occured, returns NULL. The string returned + * needs to be freed by the caller at some later point. + * + */ +char * +mm_base64_encode(char *data, uint32_t len) { + char *buf; + char *ret; + + assert(data != NULL); + + buf = _mm_base64_encode(data, len); + assert(buf != NULL); + + ret = mm_addchars(buf, "\r\n", MM_BASE64_LINELEN); + xfree(buf); + assert(ret != NULL); + return ret; +} + +/* + * Decode in-place the base64 data in 'input'. Returns the length + * of the decoded data, or -1 if there was an error. + */ +static int +_mm_base64_decode(char *input) +{ + uint32_t len = 0; + unsigned char *output = (unsigned char *)input; + int c1, c2, c3, c4; + + while (*input) { + c1 = *input++; + if (CHAR64(c1) == XX) return -1; + c2 = *input++; + if (CHAR64(c2) == XX) return -1; + c3 = *input++; + if (c3 != '=' && CHAR64(c3) == XX) return -1; + c4 = *input++; + if (c4 != '=' && CHAR64(c4) == XX) return -1; + *output++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4); + ++len; + if (c3 == '=') break; + *output++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2); + ++len; + if (c4 == '=') break; + *output++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4); + ++len; + } + *output = 0; + + return len; +} + +/* + * Encode the given binary string of length 'len' and return Base64 + * in a char buffer. It allocates the space for buffer. + * caller must free the space. + */ +static char * +_mm_base64_encode(char *data, uint32_t len) +{ + char *buf; + uint32_t buflen; + int c1; + int c2; + int c3; + uint32_t maxbuf; + + buflen = 0; + +#ifdef RUBBISH + maxbuf = len*4/3 + 1; /* size after expantion */ +#endif + maxbuf = len*2 + 20; /* size after expantion */ + + buf = (char *)xmalloc(maxbuf); + + while (len && buflen < (maxbuf - 6)) { + + c1 = (unsigned char)*data++; + buf[buflen++] = basis_64[c1>>2]; + + if (--len == 0) c2 = 0; + else c2 = (unsigned char)*data++; + buf[buflen++] = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; + + if (len == 0) { + buf[buflen++] = '='; + buf[buflen++] = '='; + break; + } + + if (--len == 0) c3 = 0; + else c3 = (unsigned char)*data++; + + buf[buflen++] = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + if (len == 0) { + buf[buflen++] = '='; + + break; + } + + --len; + buf[buflen++] = basis_64[c3 & 0x3F]; + } + + buf[buflen]=0; + return buf; +} diff --git a/trunk/main/minimime/mm_codecs.c b/trunk/main/minimime/mm_codecs.c new file mode 100644 index 000000000..23f94aeba --- /dev/null +++ b/trunk/main/minimime/mm_codecs.c @@ -0,0 +1,250 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +extern struct mm_codecs codecs; + +/** @file mm_codecs.c + * + * This module contains functions to manipulate MiniMIME codecs + * + */ + +/** @defgroup codecs Manipulating MiniMIME codecs */ + +/** @{ + * @name Codec manipulation + */ + +/** + * Looks up whether a context has an decoder installed for a given encoding + * + * @param encoding The encoding specifier to look up + * @return 1 if a decoder is installed or 0 if not + * @ingroup codecs + */ +int +mm_codec_hasdecoder(const char *encoding) +{ + struct mm_codec *codec; + + assert(encoding != NULL); + + SLIST_FOREACH(codec, &codecs, next) { + assert(codec->encoding != NULL); + if (!strcasecmp(codec->encoding, encoding)) { + if (codec->decoder != NULL) + return 1; + else + return 0; + } + } + + return 0; +} + +/** + * Looks up whether a context has an encoder installed for a given encoding + * + * @param ctx A valid MIME context + * @param encoding The encoding specifier to look up + * @return 1 if an encoder is installed or 0 if not + * @ingroup codecs + */ +int +mm_codec_hasencoder(const char *encoding) +{ + struct mm_codec *codec; + + assert(encoding != NULL); + + SLIST_FOREACH(codec, &codecs, next) { + assert(codec->encoding != NULL); + if (!strcasecmp(codec->encoding, encoding)) { + if (codec->encoder != NULL) + return 1; + else + return 0; + } + } + + return 0; +} + +/** + * Looks up whether a codec for a given encoding is installed to a context + * + * @param encoding The encoding specifier to look up + * @return 1 if a codec was found or 0 if not + * @ingroup codecs + */ +int +mm_codec_isregistered(const char *encoding) +{ + struct mm_codec *codec; + + assert(encoding != NULL); + + SLIST_FOREACH(codec, &codecs, next) { + if (!strcasecmp(codec->encoding, encoding)) { + return 1; + } + } + + return 0; +} + +/** + * Registers a codec with the MiniMIME library + * + * @param encoding The encoding specifier for which to register the codec + * @param encoder The encoder function for this encoding + * @param decoder The decoder function for this encoding + * @return 1 if successfull or 0 if not + * @ingroup codecs + * + * This function registers a codec for a given MiniMIME context. The codec + * may provide an decoder, an encoder or both (but not none). If there is + * a codec already installed for this encoding, the function will puke. + */ +int +mm_codec_register(const char *encoding, + char *(*encoder)(char *data, uint32_t i), + char *(*decoder)(char *data)) +{ + struct mm_codec *codec; + + assert(encoding != NULL); + + assert(mm_codec_isregistered(encoding) != 1); + + codec = (struct mm_codec *)xmalloc(sizeof(struct mm_codec)); + + codec->encoding = xstrdup(encoding); + codec->encoder = encoder; + codec->decoder = decoder; + + if (SLIST_EMPTY(&codecs)) { + SLIST_INSERT_HEAD(&codecs, codec, next); + return 1; + } else { + struct mm_codec *lcodec, *tcodec; + tcodec = NULL; + SLIST_FOREACH(lcodec, &codecs, next) { + if (lcodec != NULL) + tcodec = lcodec; + } + assert(tcodec != NULL); + SLIST_INSERT_AFTER(tcodec, codec, next); + return 1; + } + + return 0; +} + +/** + * Unregisters a MiniMIME codec + * + * @param encoding The encoding specifier which to unregister + * @return 0 if unregistered successfully, or -1 if there was no such codec + * @ingroup codecs + */ +int +mm_codec_unregister(const char *encoding) +{ + struct mm_codec *codec; + + assert(encoding != NULL); + + SLIST_FOREACH(codec, &codecs, next) { + if (!strcasecmp(codec->encoding, encoding)) { + xfree(codec->encoding); + xfree(codec); + codec = NULL; + return 0; + } + } + + return -1; +} + +/** + * Unregisters all codecs within a context + * + * @param ctx A valid MiniMIME context + * @return 0 if all codecs were unregistered successfully or -1 if an error + * occured. + * @note Foobar + */ +int +mm_codec_unregisterall(void) +{ + struct mm_codec *codec; + + SLIST_FOREACH(codec, &codecs, next) { + if (mm_codec_unregister(codec->encoding) == -1) { + return -1; + } + } + + return 0; +} + +/** + * Registers the default codecs to a MiniMIME context + * + * This functions registers the codecs for the following encodings to a + * MiniMIME context: + * + * - Base64 + * - (TODO:) Quoted-Printable + */ +void +mm_codec_registerdefaultcodecs(void) +{ + mm_codec_register("base64", mm_base64_encode, mm_base64_decode); +} + + +/** @} */ diff --git a/trunk/main/minimime/mm_contenttype.c b/trunk/main/minimime/mm_contenttype.c new file mode 100644 index 000000000..27fbc0c12 --- /dev/null +++ b/trunk/main/minimime/mm_contenttype.c @@ -0,0 +1,759 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +/* This file is documented using Doxygen */ + +/** + * @file mm_contenttype.c + * + * This module contains functions for manipulating Content-Type objects. + */ + +/** @defgroup contenttype Accessing and manipulating Content-Type objects */ + +struct mm_encoding_mappings { + const char *idstring; + int type; +}; + +static struct mm_encoding_mappings mm_content_enctypes[] = { + { "Base64", MM_ENCODING_BASE64 }, + { "Quoted-Printable", MM_ENCODING_QUOTEDPRINTABLE }, + { NULL, - 1}, +}; + +static const char *mm_composite_maintypes[] = { + "multipart", + "message", + NULL, +}; + +static const char *mm_composite_encodings[] = { + "7bit", + "8bit", + "binary", + NULL, +}; + +/** @{ + * @name Functions for manipulating Content objects + */ + +/** + * Creates a new object to hold a Content representation. + * The allocated memory must later be freed using mm_content_free() + * + * @return An object representing a MIME Content-Type + * @see mm_content_free + * @ingroup contenttype + */ +struct mm_content * +mm_content_new(void) +{ + struct mm_content *ct; + + ct = (struct mm_content *)xmalloc(sizeof(struct mm_content)); + + ct->maintype = NULL; + ct->subtype = NULL; + + TAILQ_INIT(&ct->type_params); + TAILQ_INIT(&ct->disposition_params); + + ct->encoding = MM_ENCODING_NONE; + ct->encstring = NULL; + + return ct; +} + +/** + * Releases all memory associated with an Content object + * + * @param ct A Content-Type object + * @return Nothing + * @ingroup contenttype + */ +void +mm_content_free(struct mm_content *ct) +{ + struct mm_param *param; + + assert(ct != NULL); + + if (ct->maintype != NULL) { + xfree(ct->maintype); + ct->maintype = NULL; + } + if (ct->subtype != NULL) { + xfree(ct->subtype); + ct->subtype = NULL; + } + if (ct->encstring != NULL) { + xfree(ct->encstring); + ct->encstring = NULL; + } + + TAILQ_FOREACH(param, &ct->type_params, next) { + TAILQ_REMOVE(&ct->type_params, param, next); + mm_param_free(param); + } + TAILQ_FOREACH(param, &ct->disposition_params, next) { + TAILQ_REMOVE(&ct->disposition_params, param, next); + mm_param_free(param); + } + + xfree(ct); +} + +/** + * Attaches a content-type parameter to a Content object + * + * @param ct The target Content object + * @param param The Content-Type parameter which to attach + * @return 0 on success and -1 on failure + * @ingroup contenttype + */ +int +mm_content_attachtypeparam(struct mm_content *ct, struct mm_param *param) +{ + assert(ct != NULL); + assert(param != NULL); + + if (TAILQ_EMPTY(&ct->type_params)) { + TAILQ_INSERT_HEAD(&ct->type_params, param, next); + } else { + TAILQ_INSERT_TAIL(&ct->type_params, param, next); + } + + return 0; +} + + +/** + * Attaches a content-disposition parameter to a Content-Disposition object + * + * @param ct The target Content object + * @param param The Content-Type parameter which to attach + * @return 0 on success and -1 on failure + * @ingroup contenttype + */ +int +mm_content_attachdispositionparam(struct mm_content *ct, struct mm_param *param) +{ + assert(ct != NULL); + assert(param != NULL); + + if (TAILQ_EMPTY(&ct->disposition_params)) { + TAILQ_INSERT_HEAD(&ct->disposition_params, param, next); + } else { + TAILQ_INSERT_TAIL(&ct->disposition_params, param, next); + } + + return 0; +} + + +/** + * Gets a Content-Type parameter value from a Content object. + * + * @param ct the Content object + * @param name the name of the parameter to retrieve + * @return The value of the parameter on success or a NULL pointer on failure + * @ingroup contenttype + */ +char * +mm_content_gettypeparambyname(struct mm_content *ct, const char *name) +{ + struct mm_param *param; + + assert(ct != NULL); + + TAILQ_FOREACH(param, &ct->type_params, next) { + if (!strcasecmp(param->name, name)) { + return param->value; + } + } + + return NULL; +} + +/** + * Gets a Content-Disposition parameter value from a Content object. + * + * @param ct the Content object + * @param name the name of the parameter to retrieve + * @return The value of the parameter on success or a NULL pointer on failure + * @ingroup contenttype + */ +char * +mm_content_getdispositionparambyname(struct mm_content *ct, const char *name) +{ + struct mm_param *param; + + assert(ct != NULL); + + TAILQ_FOREACH(param, &ct->disposition_params, next) { + if (!strcasecmp(param->name, name)) { + return param->value; + } + } + + return NULL; +} + +struct mm_param * +mm_content_gettypeparamobjbyname(struct mm_content *ct, const char *name) +{ + struct mm_param *param; + + assert(ct != NULL); + + TAILQ_FOREACH(param, &ct->type_params, next) { + if (!strcasecmp(param->name, name)) { + return param; + } + } + + return NULL; +} + +struct mm_param * +mm_content_getdispositionparamobjbyname(struct mm_content *ct, const char *name) +{ + struct mm_param *param; + + assert(ct != NULL); + + TAILQ_FOREACH(param, &ct->disposition_params, next) { + if (!strcasecmp(param->name, name)) { + return param; + } + } + + return NULL; +} + +/** + * Sets the MIME main Content-Type for a MIME Content object + * + * @param ct The MIME Content object + * @param value The value which to set the main type to + * @param copy Whether to make a copy of the value (original value must be + * freed afterwards to prevent memory leaks). + */ +int +mm_content_setmaintype(struct mm_content *ct, char *value, int copy) +{ + assert(ct != NULL); + assert(value != NULL); + + if (copy) { + /** + * @bug The xfree() call could lead to undesirable results. + * Do we really need it? + */ + if (ct->maintype != NULL) { + xfree(ct->maintype); + } + ct->maintype = xstrdup(value); + } else { + ct->maintype = value; + } + + return 0; +} + +/** + * Retrieves the main MIME Content-Type stored in a Content object + * + * @param ct A valid Content object + * @returns A pointer to the string representing the main type + * @ingroup contenttype + */ +char * +mm_content_getmaintype(struct mm_content *ct) +{ + assert(ct != NULL); + assert(ct->maintype != NULL); + + return ct->maintype; +} + +/** + * Sets the MIME Content-Disposition type for a MIME Content object + * + * @param ct The MIME Content object + * @param value The value which to set the main type to + * @param copy Whether to make a copy of the value (original value must be + * freed afterwards to prevent memory leaks). + */ +int +mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy) +{ + assert(ct != NULL); + assert(value != NULL); + + if (copy) { + /** + * @bug The xfree() call could lead to undesirable results. + * Do we really need it? + */ + if (ct->disposition_type != NULL) { + xfree(ct->disposition_type); + } + ct->disposition_type = xstrdup(value); + } else { + ct->disposition_type = value; + } + + return 0; +} + +/** + * Retrieves the Content-Disposition MIME type stored in a Content object + * + * @param ct A valid Content-Type object + * @returns A pointer to the string representing the main type + * @ingroup contenttype + */ +char * +mm_content_getdispositiontype(struct mm_content *ct) +{ + assert(ct != NULL); + assert(ct->disposition_type != NULL); + + return ct->disposition_type; +} + +/** + * Retrieves the sub MIME Content-Type stored in a Content object + * + * @param ct A valid Content-Type object + * @return A pointer to the string holding the current sub MIME type + * @ingroup contenttype + */ +char * +mm_content_getsubtype(struct mm_content *ct) +{ + assert(ct != NULL); + assert(ct->subtype != NULL); + + return ct->subtype; +} + +/** + * Sets the MIME sub Content-Type for a MIME Content object + * + * @param ct The MIME Content-Type object + * @param value The value which to set the sub type to + * @param copy Whether to make a copy of the value (original value must be + * freed afterwards to prevent memory leaks). + */ +int +mm_content_setsubtype(struct mm_content *ct, char *value, int copy) +{ + assert(ct != NULL); + assert(value != NULL); + + if (copy) { + /** + * @bug The xfree() call could lead to undesirable results. + * Do we really need it? + */ + if (ct->subtype != NULL) { + xfree(ct->subtype); + } + ct->subtype = xstrdup(value); + } else { + ct->subtype = value; + } + + return 0; +} + +int +mm_content_settype(struct mm_content *ct, const char *fmt, ...) +{ + char *maint, *subt; + char buf[512], *parse; + va_list ap; + + mm_errno = MM_ERROR_NONE; + + va_start(ap, fmt); + /* Make sure no truncation occurs */ + if (vsnprintf(buf, sizeof buf, fmt, ap) > sizeof buf) { + mm_errno = MM_ERROR_ERRNO; + mm_error_setmsg("Input string too long"); + return -1; + } + va_end(ap); + + parse = buf; + maint = strsep(&parse, "/"); + if (maint == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("Invalid type specifier: %s", buf); + return -1; + } + ct->maintype = xstrdup(maint); + + subt = strsep(&parse, ""); + if (subt == NULL) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("Invalid type specifier: %s", buf); + return -1; + } + ct->subtype = xstrdup(subt); + + return 0; +} + +/** + * Checks whether the Content-Type represents a composite message or not + * + * @param ct A valid Content-Type object + * @returns 1 if the Content-Type object represents a composite message or + * 0 if not. + */ +int +mm_content_iscomposite(struct mm_content *ct) +{ + int i; + + for (i = 0; mm_composite_maintypes[i] != NULL; i++) { + if (!strcasecmp(ct->maintype, mm_composite_maintypes[i])) { + return 1; + } + } + + /* Not found */ + return 0; +} + +/** + * Verifies whether a string represents a valid encoding or not. + * + * @param encoding The string to verify + * @return 1 if the encoding string is valid or 0 if not + * + */ +int +mm_content_isvalidencoding(const char *encoding) +{ + int i; + + for (i = 0; mm_composite_encodings[i] != NULL; i++) { + if (!strcasecmp(encoding, mm_composite_encodings[i])) { + return 1; + } + } + + /* Not found */ + return 0; +} + +/** + * Set the encoding of a MIME entitity according to a mapping table + * + * @param ct A valid content type object + * @param encoding A string representing the content encoding + * @return 0 if successfull or -1 if not (i.e. unknown content encoding) + */ +int +mm_content_setencoding(struct mm_content *ct, const char *encoding) +{ + int i; + + assert(ct != NULL); + assert(encoding != NULL); + + for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) { + if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) { + ct->encoding = mm_content_enctypes[i].type; + ct->encstring = xstrdup(encoding); + return 0; + } + } + + /* If we didn't find a mapping, set the encoding to unknown */ + ct->encoding = MM_ENCODING_UNKNOWN; + ct->encstring = NULL; + return 1; +} + +/** + * Gets the numerical ID of a content encoding identifier + * + * @param ct A valid Content Type object + * @param encoding A string representing the content encoding identifier + * @return The numerical ID of the content encoding + */ +#if 0 +int +mm_content_getencoding(struct mm_content *ct, const char *encoding) +{ + int i; + + assert(ct != NULL); + + for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) { + if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) { + return mm_content_enctypes[i].type; + } + } + + /* Not found */ + return MM_ENCODING_UNKNOWN; +} +#endif + +/** + * Constructs a MIME conform string of Content-Type parameters. + * + * @param ct A valid Content Type object + * @return A pointer to a string representing the Content-Type parameters + * in MIME terminology, or NULL if either the Content-Type object + * is invalid, has no parameters or no memory could be allocated. + * + * This function constructs a MIME conform string including all the parameters + * associated with the given Content-Type object. It should NOT be used if + * you need an opaque copy of the current MIME part (e.g. for PGP purposes). + */ +char * +mm_content_typeparamstostring(struct mm_content *ct) +{ + size_t size, new_size; + struct mm_param *param; + char *param_string, *cur_param; + char *buf; + + size = 1; + param_string = NULL; + cur_param = NULL; + + param_string = (char *) xmalloc(size); + *param_string = '\0'; + + /* Concatenate all Content-Type parameters attached to the current + * Content-Type object to a single string. + */ + TAILQ_FOREACH(param, &ct->type_params, next) { + if (asprintf(&cur_param, "; %s=\"%s\"", param->name, + param->value) == -1) { + goto cleanup; + } + + new_size = size + strlen(cur_param) + 1; + + if (new_size < 0 || new_size > 1000) { + size = 0; + goto cleanup; + } + + buf = (char *) xrealloc(param_string, new_size); + if (buf == NULL) { + size = 0; + goto cleanup; + } + + param_string = buf; + size = new_size; + strlcat(param_string, cur_param, size); + + xfree(cur_param); + cur_param = NULL; + } + + return param_string; + +cleanup: + if (param_string != NULL) { + xfree(param_string); + param_string = NULL; + } + if (cur_param != NULL) { + xfree(cur_param); + cur_param = NULL; + } + return NULL; +} + +/** + * Constructs a MIME conformant string of Content-Disposition parameters. + * + * @param ct A valid Content object + * @return A pointer to a string representing the Content-Disposition parameters + * in MIME terminology, or NULL if either the Content object + * is invalid, has no Disposition parameters or no memory could be allocated. + * + * This function constructs a MIME conforming string including all the parameters + * associated with the given Content-Disposition object. It should NOT be used if + * you need an opaque copy of the current MIME part (e.g. for PGP purposes). + */ +char * +mm_content_dispositionparamstostring(struct mm_content *ct) +{ + size_t size, new_size; + struct mm_param *param; + char *param_string, *cur_param; + char *buf; + + size = 1; + param_string = NULL; + cur_param = NULL; + + param_string = (char *) xmalloc(size); + *param_string = '\0'; + + /* Concatenate all Content-Disposition parameters attached to the current + * Content object to a single string. + */ + TAILQ_FOREACH(param, &ct->disposition_params, next) { + if (asprintf(&cur_param, "; %s=\"%s\"", param->name, + param->value) == -1) { + goto cleanup; + } + + new_size = size + strlen(cur_param) + 1; + + if (new_size < 0 || new_size > 1000) { + size = 0; + goto cleanup; + } + + buf = (char *) xrealloc(param_string, new_size); + if (buf == NULL) { + size = 0; + goto cleanup; + } + + param_string = buf; + size = new_size; + strlcat(param_string, cur_param, size); + + xfree(cur_param); + cur_param = NULL; + } + + return param_string; + +cleanup: + if (param_string != NULL) { + xfree(param_string); + param_string = NULL; + } + if (cur_param != NULL) { + xfree(cur_param); + cur_param = NULL; + } + return NULL; +} + +/** + * Creates a Content-Type header according to the object given + * + * @param ct A valid Content-Type object + * + */ +char * +mm_content_tostring(struct mm_content *ct) +{ + char *paramstring; + char *buf; + char *headerstring; + size_t size; + + paramstring = NULL; + headerstring = NULL; + buf = NULL; + + if (ct == NULL) { + return NULL; + } + if (ct->maintype == NULL || ct->subtype == NULL) { + return NULL; + } + + size = strlen(ct->maintype) + strlen(ct->subtype) + 2; + headerstring = (char *)xmalloc(size); + snprintf(headerstring, size, "%s/%s", ct->maintype, ct->subtype); + + paramstring = mm_content_typeparamstostring(ct); + if (paramstring == NULL) { + goto cleanup; + } + + size += strlen(paramstring) + strlen("Content-Type: ") + 1; + buf = (char *)malloc(size); + if (buf == NULL) { + goto cleanup; + } + + snprintf(buf, size, "Content-Type: %s%s", headerstring, paramstring); + + xfree(headerstring); + xfree(paramstring); + + headerstring = NULL; + paramstring = NULL; + + return buf; + +cleanup: + if (paramstring != NULL) { + xfree(paramstring); + paramstring = NULL; + } + if (headerstring != NULL) { + xfree(headerstring); + headerstring = NULL; + } + if (buf != NULL) { + xfree(buf); + buf = NULL; + } + return NULL; +} + +/** @} */ diff --git a/trunk/main/minimime/mm_context.c b/trunk/main/minimime/mm_context.c new file mode 100644 index 000000000..d67c1e6b3 --- /dev/null +++ b/trunk/main/minimime/mm_context.c @@ -0,0 +1,614 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> + +#include "mm_internal.h" + +/** @file mm_context.c + * + * Modules for manipulating MiniMIME contexts + */ + +/** @defgroup context Accessing and manipulating MIME contexts + * + * Each message in MiniMIME is represented by a so called ``context''. A + * context holds all necessary information given about a MIME message, such + * as the envelope, all MIME parts etc. + */ + +/** @{ + * @name Manipulating MiniMIME contexts + */ + +/** + * Creates a new MiniMIME context object. + * + * @return a new MiniMIME context object + * @see mm_context_free + * + * This function creates a new MiniMIME context, which will hold a message. + * The memory needed is allocated dynamically and should later be free'd + * using mm_context_free(). + * + * Before a context can be created, the MiniMIME library needs to be + * initialized properly using mm_library_init(). + * + */ +MM_CTX * +mm_context_new(void) +{ + MM_CTX *ctx; + + MM_ISINIT(); + + ctx = (MM_CTX *)xmalloc(sizeof(MM_CTX)); + ctx->messagetype = MM_MSGTYPE_FLAT; /* This is the default */ + ctx->boundary = NULL; + ctx->preamble = xstrdup("This is a message in MIME format, generated " + "by MiniMIME 0.1"); + + TAILQ_INIT(&ctx->parts); + SLIST_INIT(&ctx->warnings); + + return ctx; +} + +/** + * Releases a MiniMIME context object + * + * @param ctx A valid MiniMIME context + * @see mm_context_new + * + * This function releases all memory associated with MiniMIME context object + * that was created using mm_context_new(). It will also release all memory + * used for the MIME parts attached, and their specific properties (such as + * Content-Type information, headers, and the body data). + */ +void +mm_context_free(MM_CTX *ctx) +{ + struct mm_mimepart *part; + struct mm_warning *warning, *nxt; + + assert(ctx != NULL); + + TAILQ_FOREACH(part, &ctx->parts, next) { + TAILQ_REMOVE(&ctx->parts, part, next); + mm_mimepart_free(part); + } + + if (ctx->boundary != NULL) { + xfree(ctx->boundary); + ctx->boundary = NULL; + } + + if (ctx->preamble != NULL) { + xfree(ctx->preamble); + ctx->preamble = NULL; + } + + for (warning = SLIST_FIRST(&ctx->warnings); + warning != SLIST_END(&ctx->warnings); + warning = nxt) { + nxt = SLIST_NEXT(warning, next); + SLIST_REMOVE(&ctx->warnings, warning, mm_warning, next); + xfree(warning); + warning = NULL; + } + + xfree(ctx); + ctx = NULL; +} + +/** + * Attaches a MIME part object to a MiniMIME context. + * + * @param ctx the MiniMIME context + * @param part the MIME part object to attach + * @return 0 on success or -1 on failure. Sets mm_errno on failure. + * + * This function attaches a MIME part to a context, appending it to the end + * of the message. + * + * The MIME part should be initialized before attaching it using + * mm_mimepart_new(). + */ +int +mm_context_attachpart(MM_CTX *ctx, struct mm_mimepart *part) +{ + assert(ctx != NULL); + assert(part != NULL); + + if (TAILQ_EMPTY(&ctx->parts)) { + TAILQ_INSERT_HEAD(&ctx->parts, part, next); + } else { + TAILQ_INSERT_TAIL(&ctx->parts, part, next); + } + + return 0; +} + +/** + * Attaches a MIME part object to a MiniMIME context at a given position + * + * @param ctx A valid MiniMIME context + * @param part The MIME part object to attach + * @param pos After which part to attach the object + * @return 0 on success or -1 if the given position is invalid + * @see mm_context_attachpart + * + * This function attaches a MIME part object after a given position in the + * specified context. If the position is invalid (out of range), the part + * will not get attached to the message and the function returns -1. If + * the index was in range, the MIME part will get attached after the MIME + * part at the given position, moving any possible following MIME parts one + * down the hierarchy. + */ +#if 0 +int +mm_context_attachpart_after(MM_CTX *ctx, struct mm_mimepart *part, int pos) +{ + struct mm_mimepart *p; + int where; + + where = 0; + p = NULL; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (where == pos) { + p = part; + } + } + + if (p == NULL) { + return(-1); + } + + TAILQ_INSERT_AFTER(&ctx->parts, p, part, next); + + return(0); +} +#endif + +/** + * Deletes a MIME part object from a MiniMIME context + * + * @param ctx A valid MiniMIME context object + * @param which The number of the MIME part object to delete + * @param freemem Whether to free the memory associated with the MIME part + * object + * @return 0 on success or -1 on failure. Sets mm_errno on failure. + * + * This function deletes a MIME part from a given context. The MIME part to + * delete is specified as numerical index by the parameter ``which''. If the + * parameter ``freemem'' is set to anything greater than 0, the memory that + * is associated will be free'd by using mm_mimepart_free(), otherwise the + * memory is left untouched (if you still have a pointer to the MIME part + * around). + */ +int +mm_context_deletepart(MM_CTX *ctx, int which, int freemem) +{ + struct mm_mimepart *part; + int cur; + + assert(ctx != NULL); + assert(which >= 0); + + cur = 0; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (cur == which) { + TAILQ_REMOVE(&ctx->parts, part, next); + if (freemem) + mm_mimepart_free(part); + return 0; + } + cur++; + } + + return -1; +} + +/** + * Counts the number of attached MIME part objects in a given MiniMIME context + * + * @param ctx The MiniMIME context + * @returns The number of attached MIME part objects + */ +int +mm_context_countparts(MM_CTX *ctx) +{ + int count; + struct mm_mimepart *part; + + assert(ctx != NULL); + + count = 0; + + if (TAILQ_EMPTY(&ctx->parts)) { + return 0; + } else { + TAILQ_FOREACH(part, &ctx->parts, next) { + count++; + } + } + + assert(count > -1); + + return count; +} + +/** + * Gets a specified MIME part object from a MimeMIME context + * + * @param ctx The MiniMIME context + * @param which The number of the MIME part object to retrieve + * @returns The requested MIME part object on success or a NULL pointer if + * there is no such part. + */ +struct mm_mimepart * +mm_context_getpart(MM_CTX *ctx, int which) +{ + struct mm_mimepart *part; + int cur; + + assert(ctx != NULL); + + cur = 0; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (cur == which) { + return part; + } + cur++; + } + + return NULL; +} + +/** + * Checks whether a given context represents a composite (multipart) message + * + * @param ctx A valid MiniMIME context object + * @return 1 if the context is a composite message or 0 if it's flat + * + */ +int +mm_context_iscomposite(MM_CTX *ctx) +{ + if (ctx->messagetype == MM_MSGTYPE_MULTIPART) { + return 1; + } else { + return 0; + } +} + +/** + * Checks whether there are any warnings associated with a given context + * + * @param ctx A valid MiniMIME context + * @return 1 if there are warnings associated with the context, otherwise 0 + */ +int +mm_context_haswarnings(MM_CTX *ctx) +{ + if (SLIST_EMPTY(&ctx->warnings)) { + return 0; + } else { + return 1; + } +} + +/** + * Generates a generic boundary string for a given context + * + * @param ctx A valid MiniMIME context + * @return 0 on success or -1 on failure + * + * This function generates a default boundary string for the given context. + * If there is already a boundary for the context, the memory will be free()'d. + */ +#if 0 +int +mm_context_generateboundary(MM_CTX *ctx) +{ + char *boundary; + struct mm_mimepart *part; + struct mm_param *param; + + if (mm_mimeutil_genboundary("++MiniMIME++", 20, &boundary) == -1) { + return(-1); + } + + if (ctx->boundary != NULL) { + xfree(ctx->boundary); + ctx->boundary = NULL; + } + + /* If we already have an envelope, make sure that we also justify the + * "boundary" parameter of the envelope. + */ + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + return(0); + } + if (part->type != NULL) { + param = mm_content_gettypeparamobjbyname(part->type, "boundary"); + if (param == NULL) { + param = mm_param_new(); + param->name = xstrdup("boundary"); + param->value = xstrdup(boundary); + mm_content_attachtypeparam(part->type, param); + } else { + if (param->value != NULL) { + xfree(param->value); + param->value = NULL; + } + param->value = xstrdup(boundary); + } + } + + ctx->boundary = boundary; + return(0); +} +#endif + +/** + * Sets a preamble for the given MiniMIME context + * + * @param ctx A valid MiniMIME context + * @param preamble The preamble to set + * @return 0 on success or -1 on failure + * + * This function sets the MIME preamble (the text between the end of envelope + * headers and the beginning of the first MIME part) for a given context + * object. If preamble is a NULL-pointer then the preamble will be deleted, + * and the currently associated memory will be free automagically. + */ +#if 0 +int +mm_context_setpreamble(MM_CTX *ctx, char *preamble) +{ + if (ctx == NULL) + return(-1); + + if (preamble == NULL) { + if (ctx->preamble != NULL) { + xfree(ctx->preamble); + } + ctx->preamble = NULL; + } else { + ctx->preamble = xstrdup(preamble); + } + return(0); +} +#endif + +#if 0 +char * +mm_context_getpreamble(MM_CTX *ctx) +{ + if (ctx == NULL) + return(NULL); + + return(ctx->preamble); +} +#endif + +/** + * Creates an ASCII message of the specified context + * + * @param ctx A valid MiniMIME context object + * @param flat Where to store the message + * @param flags Flags that affect the flattening process + * + * This function ``flattens'' a MiniMIME context, that is, it creates an ASCII + * represantation of the message the context contains. The flags can be a + * bitwise combination of the following constants: + * + * - MM_FLATTEN_OPAQUE : use opaque MIME parts when flattening + * - MM_FLATTEN_SKIPENVELOPE : do not flatten the envelope part + * + * Great care is taken to not produce invalid MIME output. + */ +#if 0 +int +mm_context_flatten(MM_CTX *ctx, char **flat, size_t *length, int flags) +{ + struct mm_mimepart *part; + char *message; + char *flatpart; + char *buf; + char *envelope_headers; + size_t message_size; + size_t tmp_size; + char envelope; + + mm_errno = MM_ERROR_NONE; + envelope = 1; + + message = NULL; + message_size = 0; + + if (ctx->boundary == NULL) { + if (mm_context_iscomposite(ctx)) { + mm_context_generateboundary(ctx); + } + } + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (envelope) { + if (flags & MM_FLATTEN_SKIPENVELOPE) { + envelope = 0; + if ((message = (char *) malloc(1)) == NULL) { + mm_errno = MM_ERROR_ERRNO; + goto cleanup; + } + *message = '\0'; + continue; + } + + if (part->type == NULL && mm_context_countparts(ctx) > 1) { + if (mm_mimepart_setdefaultcontenttype(part, 1) + == -1) { + goto cleanup; + } + if (mm_context_generateboundary(ctx) == -1) { + goto cleanup; + } + ctx->messagetype = MM_MSGTYPE_MULTIPART; + } + + if (mm_envelope_getheaders(ctx, &envelope_headers, + &tmp_size) == -1) { + return -1; + } + + message = envelope_headers; + message_size = tmp_size; + envelope = 0; + + if (ctx->preamble != NULL + && mm_context_iscomposite(ctx) + && !(flags & MM_FLATTEN_NOPREAMBLE)) { + tmp_size += strlen(ctx->preamble) + + (strlen("\r\n") * 2); + buf = (char *)xrealloc(message, tmp_size); + if (buf == NULL) { + goto cleanup; + } + message_size += tmp_size; + message = buf; + strlcat(message, "\r\n", message_size); + strlcat(message, ctx->preamble, message_size); + strlcat(message, "\r\n", message_size); + } + } else { + /* Enforce Content-Type if none exist */ + if (part->type == NULL) { + if (mm_mimepart_setdefaultcontenttype(part, 0) + == -1) { + goto cleanup; + } + } + + /* Append a boundary if necessary */ + if (ctx->boundary != NULL) { + tmp_size = strlen(ctx->boundary) + + (strlen("\r\n") * 2) + strlen("--"); + + if (tmp_size < 1) { + return(-1); + } + if (message_size + tmp_size < 1) { + return(-1); + } + + buf = (char *)xrealloc(message, message_size + + tmp_size); + if (buf == NULL) { + goto cleanup; + } + message_size += tmp_size; + message = buf; + strlcat(message, "\r\n", message_size); + strlcat(message, "--", message_size); + strlcat(message, ctx->boundary, message_size); + strlcat(message, "\r\n", message_size); + } + + if (mm_mimepart_flatten(part, &flatpart, &tmp_size, + (flags & MM_FLATTEN_OPAQUE)) == -1) { + goto cleanup; + } + + if (tmp_size < 1) { + goto cleanup; + } + + buf = (char *) xrealloc(message, message_size + + tmp_size); + if (buf == NULL) { + goto cleanup; + } + + message_size += tmp_size; + message = buf; + + strlcat(message, flatpart, message_size); + xfree(flatpart); + flatpart = NULL; + } + } + + /* Append end boundary */ + if (ctx->boundary != NULL && mm_context_iscomposite(ctx)) { + tmp_size = strlen(ctx->boundary) + (strlen("\r\n") * 2) + + (strlen("--") * 2); + buf = (char *)xrealloc(message, message_size + tmp_size); + if (buf == NULL) { + goto cleanup; + } + + message_size += tmp_size; + message = buf; + if (message[strlen(message)-1] != 13) + strlcat(message, "\r", message_size); + strlcat(message, "\n", message_size); + strlcat(message, "--", message_size); + strlcat(message, ctx->boundary, message_size); + strlcat(message, "--", message_size); + strlcat(message, "\r\n", message_size); + } + + *flat = message; + *length = message_size; + + return 0; + +cleanup: + if (message != NULL) { + xfree(message); + message = NULL; + } + return -1; +} +#endif + +/** @} */ diff --git a/trunk/main/minimime/mm_envelope.c b/trunk/main/minimime/mm_envelope.c new file mode 100644 index 000000000..7400d3de2 --- /dev/null +++ b/trunk/main/minimime/mm_envelope.c @@ -0,0 +1,271 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +/** @file mm_envelope.c + * + * This module contains functions for accessing a message's envelope. This + * are mainly wrapper functions for easy access. + */ + +/** @defgroup envelope Accessing and manipulating a message's envelope + */ + +/** @{ + * @name Accessing and manipulating a message's envelope + */ + +/** + * Gets an ASCII representation of all envelope headers + * + * @param ctx A valid MiniMIME context + * @param result Where to store the resulting ASCII headers + * @param length Where to store the length of the result + * @returns 0 on success or -1 on failure. + * @note Sets mm_errno on failure + * + * This is mainly a convinience function. It constructs an ASCII representation + * from all of the message's envelope headers and stores the result in headers. + * Memory is allocated dynamically, and the total length of the result is + * stored in length. This function takes care that the output is MIME conform, + * and folds long lines according to the MIME standard at position 78 of the + * string. It also nicely formats all MIME related header fields, such as + * the Content-Type header. + * + * Since the memory needed to store the result is allocated dynamically, one + * should take care of freeing it again when it's not needed anymore. If an + * error occurs, *result will be set to NULL, *length will be set to zero + * and mm_errno will be set to a reasonable value. + * + */ +int +mm_envelope_getheaders(MM_CTX *ctx, char **result, size_t *length) +{ + struct mm_mimepart *part; + struct mm_mimeheader *hdr; + char *buf, *hdrbuf; + size_t headers_length, tmp_length; + + headers_length = 1; + buf = NULL; + + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + return -1; + } + + /* Initialize our buffer */ + if ((buf = (char *)xmalloc(headers_length)) == NULL) { + mm_errno = MM_ERROR_ERRNO; + goto cleanup; + } + *buf = '\0'; + + /* Store each envelope header */ + TAILQ_FOREACH(hdr, &part->headers, next) { + tmp_length = strlen(hdr->name) + strlen(hdr->value) + + strlen(": \r\n"); + hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length); + if (hdrbuf == NULL) { + mm_errno = MM_ERROR_ERRNO; + goto cleanup; + } + + headers_length += tmp_length; + buf = hdrbuf; + + strlcat(buf, hdr->name, headers_length); + strlcat(buf, ": ", headers_length); + strlcat(buf, hdr->value, headers_length); + strlcat(buf, "\r\n", headers_length); + } + + /* Construct and store MIME headers */ + if (part->type != NULL) { + char *typebuf; + typebuf = mm_content_tostring(part->type); + if (typebuf == NULL) { + goto cleanup; + } + tmp_length = strlen(typebuf) + strlen("\r\n"); + + hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length); + if (hdrbuf == NULL) { + mm_errno = MM_ERROR_ERRNO; + goto cleanup; + } + + headers_length += tmp_length; + buf = hdrbuf; + + strlcat(buf, typebuf, headers_length); + strlcat(buf, "\r\n", headers_length); + } + + *result = buf; + *length = headers_length; + + return 0; + +cleanup: + if (buf != NULL) { + xfree(buf); + buf = NULL; + } + *result = NULL; + *length = 0; + return -1; +} + +/** + * Sets a header field in the envelope + * + * @param ctx A valid MiniMIME context + * @param name The name of the header field to set + * @param fmt A format string specifying the value of the header field + * @return 0 on success or -1 on failure + * + * This function generates a new MIME header and attaches it to the first + * MIME part (the envelope) found in the given context. If no part is + * attached already, the function will return an error. The function will + * store a copy of ``name'' as the header's name field, and dynamically + * allocate the memory needed to build the format string. + */ +int +mm_envelope_setheader(MM_CTX *ctx, const char *name, const char *fmt, ...) +{ + va_list ap; + char *buf; + struct mm_mimeheader *hdr; + struct mm_mimepart *part; + + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + return(-1); + } + + hdr = mm_mimeheader_new(); + if (hdr == NULL) { + return(-1); + } + + hdr->name = xstrdup(name); + + va_start(ap, fmt); + if (vasprintf(&buf, fmt, ap) == -1) { + goto cleanup; + } + va_end(ap); + + hdr->value = buf; + + if (mm_mimepart_attachheader(part, hdr) == -1) { + goto cleanup; + } + + return(0); + +cleanup: + if (hdr != NULL) { + if (hdr->name != NULL) { + xfree(hdr->name); + hdr->name = NULL; + } + if (hdr->value != NULL) { + xfree(hdr->value); + hdr->value = NULL; + } + } + return(-1); +} + +/** + * Gets the list of recipients for a MIME message + * + * @param ctx A valid MiniMIME context + * @param result Where to store the result + * @param length Where to store the length of the result + * @returns 0 on success or -1 on error + * @note Sets mm_errno on error + * + * This functions gets the list of recipients for a given MIME message. It + * does so by concatenating the "From" and "Cc" header fields, and storing + * the results in recipients. The memory needed to store the result is + * allocated dynamically, and the total length of the result is stored in + * length. + * + * One should take care to free() the result once it's not needed anymore. + */ +#if 0 +int +mm_envelope_getrecipients(MM_CTX *ctx, char **result, size_t *length) +{ + struct mm_mimepart *part; + struct mm_mimeheader *to, *cc; + size_t recipients_length = 0; + + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + return -1; + } + + to = mm_mimepart_getheaderbyname(part, "From", 0); + cc = mm_mimepart_getheaderbyname(part, "Cc", 0); + + if (to == NULL || cc == NULL) { + *result = NULL; + *length = 0; + return -1; + } + + if (to != NULL) { + recipients_length += strlen(to->value); + } + if (cc != NULL) { + recipients_length += strlen(cc->value); + } + + return 0; +} +#endif + +/** @} */ diff --git a/trunk/main/minimime/mm_error.c b/trunk/main/minimime/mm_error.c new file mode 100644 index 000000000..5b27bf724 --- /dev/null +++ b/trunk/main/minimime/mm_error.c @@ -0,0 +1,123 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "mm_internal.h" +#include "mm_util.h" + +/** @file mm_error.c + * + * This module contains functions for MiniMIME error information/manipulation + */ + +/** @defgroup error MiniMIME error functions */ + +/** + * Initializes the global error object + * + * @ingroup error + * + * This function initializes the global error object mm_error. This must be + * done when the library is initialized, and is automatically called from + * mm_init_library(). + */ +void +mm_error_init(void) +{ + mm_error.error_id = 0; + mm_error.error_where = 0; + mm_error.lineno = 0; + memset(&mm_error.error_msg, '\0', sizeof(mm_error.error_msg)); +} + +/** + * Sets a descriptive error message + * + * @param fmt The error message as format string + * @ingroup error + * + * This function is called from the various MiniMIME modules in case an + * error occured. Should never be called by the user. + */ +void +mm_error_setmsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(mm_error.error_msg, sizeof(mm_error.error_msg), fmt, ap); + va_end(ap); + +} + +void +mm_error_setlineno(int lineno) +{ + mm_error.lineno = lineno; +} + +/** + * Retrieves the current error message + * + * @return The currently set error message + * @ingroup error + * + * This function can be used to retrieve a descriptive error message for the + * current error, much like strerror() function of libc. When this function + * is called without an error being set, it returns the string "No error". + * The string returned does not need to be freed, since it is not dynamically + * allocated by the library. + */ +char * +mm_error_string(void) +{ + if (mm_errno != MM_ERROR_ERRNO && mm_error.error_msg[0] == '\0') { + return "No error"; + } else if (mm_errno == MM_ERROR_ERRNO) { + return strerror(errno); + } else { + return mm_error.error_msg; + } +} + +int +mm_error_lineno(void) +{ + return mm_error.lineno; +} diff --git a/trunk/main/minimime/mm_header.c b/trunk/main/minimime/mm_header.c new file mode 100644 index 000000000..827c19cac --- /dev/null +++ b/trunk/main/minimime/mm_header.c @@ -0,0 +1,213 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +/** @file mm_header.c + * + * This module contains functions for manipulating MIME headers + */ + +/** + * Creates a new MIME header object + * + * @return A new and initialized MIME header object + * @see mm_mimeheader_free + * + * This function creates and initializes a new MIME header object, which must + * later be freed using mm_mimeheader_free() + */ +struct mm_mimeheader * +mm_mimeheader_new(void) +{ + struct mm_mimeheader *header; + + header = (struct mm_mimeheader *)xmalloc(sizeof(struct mm_mimeheader)); + + header->name = NULL; + header->value = NULL; + TAILQ_INIT(&header->params); + + return header; +} + +/** + * Frees a MIME header object + * + * @param header The MIME header object which to free + */ +void +mm_mimeheader_free(struct mm_mimeheader *header) +{ + struct mm_param *param; + assert(header != NULL); + + if (header->name != NULL) { + xfree(header->name); + header->name = NULL; + } + if (header->value != NULL) { + xfree(header->value); + header->value = NULL; + } + + TAILQ_FOREACH(param, &header->params, next) { + TAILQ_REMOVE(&header->params, param, next); + mm_param_free(param); + } +xfree(header); + header = NULL; +} + +/** + * Creates a new MIME header, but does no checks whatsoever (create as-is) + */ +struct mm_mimeheader * +mm_mimeheader_generate(const char *name, const char *value) +{ + struct mm_mimeheader *header; + + header = mm_mimeheader_new(); + + header->name = xstrdup(name); + header->value = xstrdup(value); + + return header; +} + +/** + * Attaches a parameter to a MimeHeader object + * + * @param hdr The target MimeHeader object + * @param param The parameter to attach + * @return 0 on success and -1 on failure + * @ingroup mimeheader + */ +int +mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param) +{ + assert(hdr != NULL); + assert(param != NULL); + + if (TAILQ_EMPTY(&hdr->params)) { + TAILQ_INSERT_HEAD(&hdr->params, param, next); + } else { + TAILQ_INSERT_TAIL(&hdr->params, param, next); + } + + return 0; +} + + +/** + * Gets a parameter value from a MimeHeader object. + * + * @param hdr the MimeHeader object + * @param name the name of the parameter to retrieve + * @return The value of the parameter on success or a NULL pointer on failure + * @ingroup mimeheader + */ +char * +mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name) +{ + struct mm_param *param; + + assert(hdr != NULL); + + TAILQ_FOREACH(param, &hdr->params, next) { + if (!strcasecmp(param->name, name)) { + return param->value; + } + } + + return NULL; +} + +int +mm_mimeheader_uncomment(struct mm_mimeheader *header) +{ + char *new; + + assert(header != NULL); + assert(header->name != NULL); + assert(header->value != NULL); + + new = mm_uncomment(header->value); + if (new == NULL) + return -1; + + xfree(header->value); + header->value = new; + + return 0; +} + +int +mm_mimeheader_uncommentbyname(struct mm_mimepart *part, const char *name) +{ + struct mm_mimeheader *header; + + TAILQ_FOREACH(header, &part->headers, next) { + if (!strcasecmp(header->name, name)) { + return mm_mimeheader_uncomment(header); + } + } + + /* Not found */ + return -1; +} + +int +mm_mimeheader_uncommentall(struct mm_mimepart *part) +{ + struct mm_mimeheader *header; + int ret, r; + + ret = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + if ((r = mm_mimeheader_uncomment(header)) == -1) { + ret = -1; + } + } + + return ret; +} diff --git a/trunk/main/minimime/mm_init.c b/trunk/main/minimime/mm_init.c new file mode 100644 index 000000000..2ec37db6b --- /dev/null +++ b/trunk/main/minimime/mm_init.c @@ -0,0 +1,65 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "mm_internal.h" + +int mm_errno; +struct mm_error_data mm_error; +static int mm_initialized; +struct mm_codecs codecs; + +int +mm_library_init(void) +{ + assert(mm_initialized != 1); + + mm_errno = MM_ERROR_NONE; + mm_initialized = 1; + + SLIST_INIT(&codecs); + + mm_error_init(); + + return 0; +} + +int +mm_library_isinitialized(void) +{ + return mm_initialized; +} diff --git a/trunk/main/minimime/mm_internal.h b/trunk/main/minimime/mm_internal.h new file mode 100644 index 000000000..155591487 --- /dev/null +++ b/trunk/main/minimime/mm_internal.h @@ -0,0 +1,65 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @file mm_internal.h + * Data definitions for MiniMIME + */ +#ifndef _MM_INTERNAL_H_INCLUDED +#define _MM_INTERNAL_H_INCLUDED + +#include "mm.h" + +#define debugp(m, ...) do { \ + fprintf(stderr, "%s:%d:: ", __FILE__, __LINE__); \ + fprintf(stderr, m, ## __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ +} while (0); + +/** + * @{ + * @name Utility functions + */ +#ifndef __HAVE_LEAK_DETECTION +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +void xfree(void *); +char *xstrdup(const char *); +#endif + +char *xstrsep(char **, const char *); + +/* THIS FILE IS INTENTIONALLY LEFT BLANK */ + +#endif /* ! _MM_INTERNAL_H_INCLUDED */ diff --git a/trunk/main/minimime/mm_mem.c b/trunk/main/minimime/mm_mem.c new file mode 100644 index 000000000..6c915e564 --- /dev/null +++ b/trunk/main/minimime/mm_mem.c @@ -0,0 +1,170 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> + +#include "mm_internal.h" + +#ifdef __HAVE_LEAK_DETECTION +# include "mm_mem.h" + +static struct MM_chunks chunks; + +void * +MM_malloc(size_t size, char *filename, int line) +{ + struct MM_mem_chunk *chunk; + void *pointer; + + pointer = malloc(size); + if (pointer == NULL) + fdprintf(stderr, "INFO: malloc"); + + chunk = (struct MM_mem_chunk *)malloc(sizeof(struct MM_mem_chunk)); + if (chunk == NULL) + fdprintf(stderr, "INFO: malloc"); + + chunk->address = pointer; + chunk->size = size; + chunk->filename = filename; + chunk->line = line; + + TAILQ_INSERT_TAIL(&chunks, chunk, next); + + return pointer; +} + +char * +MM_strdup(const char *s, char *filename, int line) +{ + char *r; + + r = (char *)MM_malloc(strlen(s)+1, filename, line); + strlcpy(r, s, strlen(s) + 1); + if (strlen(r) != strlen(s)) { + debugp("%d:%d", strlen(s), strlen(r)); + } + return r; + +} + +void * +MM_realloc(void *p, size_t new_size, char *filename, int line) +{ + void *r; + void *a; + struct MM_mem_chunk *chunk; + struct MM_mem_chunk *last; + + a = p; + chunk = NULL; + last = NULL; + + assert(new_size > 0); + + TAILQ_FOREACH(chunk, &chunks, next) { + if (chunk->address == p) { + last = chunk; + } + } + + if (last == NULL) { + debugp("MM_realloc: did not find chunk at %p (%s:%d) " + ", creating new", p, filename, line); + return MM_malloc(new_size, filename, line); + } + + r = realloc(p, new_size); + if (r == NULL) + return NULL; + + last->address = r; + last->size = new_size; + last->filename = filename; + last->line = line; + + return r; +} + +void +MM_free(void *pointer, char *filename, int line, char *name) +{ + struct MM_mem_chunk *chunk, *nxt; + + for (chunk = TAILQ_FIRST(&chunks); chunk != TAILQ_END(&chunks); + chunk = nxt) { + nxt = TAILQ_NEXT(&chunks, next); + if (chunk->address == pointer) { + TAILQ_REMOVE(&chunks, chunk, next); + free(chunk->address); + free(chunk); + return; + } + } + + debugp("FREE: did not find storage %s (at %p), %s:%d", name, pointer, + filename, line); +} + +void +MM_leakd_flush(void) +{ + debugp("flushing memory informations"); + while (!TAILQ_EMPTY(&chunks)) + SLIST_REMOVE_HEAD(&chunks, next); +} + +void +MM_leakd_printallocated(void) +{ + struct MM_mem_chunk *chunk; + debugp("printing dynamic memory allocations"); + TAILQ_FOREACH(chunk, &chunks, next) { + debugp(" chunk: %p (alloc'ed at %s:%d, size %d)\n", + chunk->address, chunk->filename, chunk->line, chunk->size); + } +} + +void +MM_leakd_init(void) +{ + TAILQ_INIT(&chunks); +} + +#endif /* !__HAVE_LEAK_DETECTOR */ diff --git a/trunk/main/minimime/mm_mem.h b/trunk/main/minimime/mm_mem.h new file mode 100644 index 000000000..34840d033 --- /dev/null +++ b/trunk/main/minimime/mm_mem.h @@ -0,0 +1,32 @@ +#ifndef __MEM_H +#define __MEM_H + +#ifdef __HAVE_LEAK_DETECTION + +#define NAMEOF(v) #v +#define xmalloc(x) MM_malloc(x, __FILE__, __LINE__) +#define xfree(x) MM_free(x, __FILE__, __LINE__, NAMEOF(x)) +#define xstrdup(x) MM_strdup(x, __FILE__, __LINE__) +#define xrealloc(x, y) MM_realloc(x, y, __FILE__, __LINE__) + +TAILQ_HEAD(MM_chunks, MM_mem_chunk); + +struct MM_mem_chunk { + void *address; + const char *filename; + uint32_t line; + size_t size; + + TAILQ_ENTRY(MM_mem_chunk) next; +}; + +void *MM_malloc(size_t, char *, int); +void *MM_realloc(void *, size_t, char *, int); +void MM_free(void *, char *, int, char *); +char *MM_strdup(const char *, char *, int); +void MM_leakd_init(void); +void MM_leakd_printallocated(void); +void MM_leakd_flush(void); + +#endif /* __HAVE_LEAK_DETECTION */ +#endif /* ! HAVE_MEM_H */ diff --git a/trunk/main/minimime/mm_mimepart.c b/trunk/main/minimime/mm_mimepart.c new file mode 100644 index 000000000..631debae0 --- /dev/null +++ b/trunk/main/minimime/mm_mimepart.c @@ -0,0 +1,659 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" + +/** @file mm_mimepart.c + * + * This module contains functions for manipulating MIME header objects. + */ + +/** @defgroup mimepart Accessing and manipulating MIME parts + * + * MIME parts, also called entities, represent the structure of a MIME + * message. ``Normal'' internet messages have only a single part, and + * are called ``flat'' messages. Multipart messages have more then one + * part, and each MIME part can have it's own subset of headers. + * + * Provided here are functions to easily access all informations from + * a MIME part, including their specific headers and bodies. + */ + +/** @{ + * @name Creating and destroying MIME parts + */ + +/** + * Allocates memory for a new mm_mimepart structure and initializes it. + * + * @return A pointer to a struct of type mm_mimeheader or NULL on failure + * @see mm_mimepart_free + * @note The memory must be freed by using mm_mimepart_free() later on. + */ +struct mm_mimepart * +mm_mimepart_new(void) +{ + struct mm_mimepart *part; + + part = (struct mm_mimepart *)xmalloc(sizeof(struct mm_mimepart)); + + TAILQ_INIT(&part->headers); + + part->opaque_length = 0; + part->opaque_body = NULL; + + part->length = 0; + part->body = NULL; + + part->type = NULL; + + return part; +} + +/** + * Creates a MIME part from a file + * + * @param filename The name of the file to create the MIME part from + * @return A pointer to a new MIME part object + * + * This function creates a new MIME part object from a file. The object should + * be freed using mm_mimepart_free() later on. This function does NOT set the + * Content-Type and neither does any encoding work. + */ +struct mm_mimepart * +mm_mimepart_fromfile(const char *filename) +{ + int fd; + char *data; + size_t r; + struct stat st; + struct mm_mimepart *part; + + mm_errno = MM_ERROR_NONE; + + if ((fd = open(filename, O_RDONLY)) == -1) { + mm_errno = MM_ERROR_ERRNO; + return NULL; + } + + if ((stat(filename, &st)) == -1) { + mm_errno = MM_ERROR_ERRNO; + close(fd); + return NULL; + } + + data = xmalloc(st.st_size); + r = read(fd, data, st.st_size); + if (r != st.st_size) { + mm_errno = MM_ERROR_ERRNO; + close(fd); + return(NULL); + } + + data[r] = '\0'; + close(fd); + + part = mm_mimepart_new(); + part->length = r; + part->body = data; + + return part; +} + + +/** + * Frees all memory allocated by a mm_mimepart object. + * + * @param part A pointer to an allocated mm_mimepart object + * @see mm_mimepart_new + */ +void +mm_mimepart_free(struct mm_mimepart *part) +{ + struct mm_mimeheader *header; + + assert(part != NULL); + + TAILQ_FOREACH(header, &part->headers, next) { + mm_mimeheader_free(header); + TAILQ_REMOVE(&part->headers, header, next); + } + + if (part->opaque_body != NULL) { + xfree(part->opaque_body); + part->opaque_body = NULL; + part->body = NULL; + } else if (part->body != NULL) { + xfree(part->body); + part->body = NULL; + } + + if (part->type != NULL) { + mm_content_free(part->type); + part->type = NULL; + } + + xfree(part); + part = NULL; +} + +/** @} */ + +/** @{ + * @name Accessing the MIME part's mail header + */ + +/** + * Attaches a mm_mimeheader object to a MIME part + * + * @param part A valid MIME part object + * @param header A valid MIME header object + * @return 0 if successfull or -1 if the header could not be attached + */ +int +mm_mimepart_attachheader(struct mm_mimepart *part, struct mm_mimeheader *header) +{ + assert(part != NULL); + assert(header != NULL); + + if (TAILQ_EMPTY(&part->headers)) { + TAILQ_INSERT_HEAD(&part->headers, header, next); + } else { + TAILQ_INSERT_TAIL(&part->headers, header, next); + } + + return(0); +} + +/** + * Retrieves the number of MIME headers available in a MIME part + * + * @param part A valid MIME part object + * @return The number of MIME headers within the MIME part + */ +int +mm_mimepart_countheaders(struct mm_mimepart *part) +{ + int found; + struct mm_mimeheader *header; + + assert(part != NULL); + + found = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + found++; + } + + return found; +} + +/** + * Retrieves the number of MIME headers with a given name in a MIME part + * + * @param part A valid MIME part object + * @param name The name of the MIME header which to count for + * @return The number of MIME headers within the MIME part + */ +int +mm_mimepart_countheaderbyname(struct mm_mimepart *part, const char *name) +{ + int found; + struct mm_mimeheader *header; + + assert(part != NULL); + + found = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + if (strcasecmp(header->name, name) == 0) { + found++; + } + } + + return found; +} + +/** + * Get a MIME header object from a MIME part + * + * @param part A valid MIME part object + * @param name The name of the MIME header which to retrieve + * @param idx Which header field to get (in case of multiple headers of the + * same name). + * @return A pointer to the requested MIME header on success, or NULL if there + * either isn't a header with the requested name or idx is out of + * range. + */ +struct mm_mimeheader * +mm_mimepart_getheaderbyname(struct mm_mimepart *part, const char *name, int idx) +{ + struct mm_mimeheader *header; + int curidx; + + curidx = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + if (!strcasecmp(header->name, name)) { + if (curidx == idx) + return header; + else + curidx++; + } + } + + /* Not found */ + return NULL; +} + +/** + * Gets the value of a MIME header object + * + * @param part A valid MIME part object + * @param name The name of the header field to get the value from + * @param idx The index of the header field to get, in case there are multiple + * headers with the same name. + * @return A pointer to the requested value on success, or NULL if there either + * isn't a header with the requested name or idx is out of range. + * + */ +const char * +mm_mimepart_getheadervalue(struct mm_mimepart *part, const char *name, int idx) +{ + struct mm_mimeheader *header; + + header = mm_mimepart_getheaderbyname(part, name, idx); + if (header == NULL) + return NULL; + else + return header->value; +} + +/** + * Initializes a header loop for a given MIME part + * + * @param part A valid MIME part object + * @param id The address of a MIME header object (to allow reentrance) + * @return 0 on success or -1 on failure + * @see mm_mimepart_headers_next + * + * Looping through headers can be done in the following way: + * + * @code + * struct mm_mimeheader *header, *lheader; + * + * mm_mimepart_headers_start(part, &lheader); + * + * while ((header = mm_mimepart_headers_next(part, &lheader)) != NULL) { + * printf("%s: %s\n", header->name, header->value); + * } + * + * @endcode + * + * For convienience, the macro mm_mimepart_headers_foreach() can be used to + * loop through headers in a one-shot manner. + */ +int +mm_mimepart_headers_start(struct mm_mimepart *part, struct mm_mimeheader **id) +{ + assert(part != NULL); + + if (TAILQ_EMPTY(&part->headers)) { + return -1; + } + *id = NULL; + return 0; +} + +/** + * Returns the next MIME header of a given MIME part object + * + * @param part A valid MIME part object + * @param id A previously initialized MIME header object + * @return A pointer to the MIME header object or NULL if end of headers was + * reached. + * @see mm_mimepart_headers_start + */ +struct mm_mimeheader * +mm_mimepart_headers_next(struct mm_mimepart *part, struct mm_mimeheader **id) +{ + struct mm_mimeheader *header; + + assert(part != NULL); + + if (*id == NULL) { + header = TAILQ_FIRST(&part->headers); + } else { + header = TAILQ_NEXT(*id, next); + } + *id = header; + + return header; +} + +/** @} */ + +/** @{ + * @name Accessing and manipulating the MIME part's body + */ + +/** + * Gets the pointer to the MIME part's body data + * + * @param part A valid MIME part object + * @param opaque Whether to get the opaque part or not + * @return A pointer to the MIME part's body + * @see mm_mimepart_setbody + * + */ +char * +mm_mimepart_getbody(struct mm_mimepart *part, int opaque) +{ + assert(part != NULL); + + if (opaque) + return part->opaque_body; + else + return part->body; +} + +/** + * Sets the MIME part's body data + * + * @param part A valid MIME part object + * @param data A pointer to the data which to set + * @see mm_mimepart_getbody + * + * This functions sets the body data for a given MIME part. The string pointed + * to by data must be NUL-terminated. The data is copied into the MIME part's + * body, and thus, the memory pointed to by data can be freed after the + * operation. + */ +#if 0 +void +mm_mimepart_setbody(struct mm_mimepart *part, const char *data, int opaque) +{ + assert(part != NULL); + assert(data != NULL); + + if (opaque) { + part->opaque_body = xstrdup(data); + part->body = part->opaque_body; + } else { + part->body = xstrdup(data); + } + part->length = strlen(data); +} +#endif + +/** + * Gets the length of a given MIME part object + * + * @param part A valid MIME part object + * @returns The size of the part's body in byte. + * + * This function returns the total length of the given MIME part's body. The + * length does not include the headers of the MIME parts. If the function + * returns 0, no body part is set currently. + */ +size_t +mm_mimepart_getlength(struct mm_mimepart *part) +{ + assert(part != NULL); + + return part->length; +} + + +/** + * Decodes a MIME part according to it's encoding using MiniMIME codecs + * + * @param A valid MIME part object + * @return 0 if the MIME part could be successfully decoded or -1 if not + * @note Sets mm_errno on error + * + * This function decodes the body of a MIME part with a registered decoder + * according to it's Content-Transfer-Encoding header field. + */ +char * +mm_mimepart_decode(struct mm_mimepart *part) +{ + extern struct mm_codecs codecs; + struct mm_codec *codec; + void *decoded; + + assert(part != NULL); + assert(part->type != NULL); + + decoded = NULL; + + /* No encoding associated */ + if (part->type->encstring == NULL) + return NULL; + + /* Loop through codecs and find a suitable one */ + SLIST_FOREACH(codec, &codecs, next) { + if (!strcasecmp(part->type->encstring, codec->encoding)) { + decoded = codec->decoder((char *)part->body); + break; + } + } + + return decoded; +} + +/** + * Creates an ASCII representation of the given MIME part + * + * @param part A valid MIME part object + * @param result Where to store the result + * @param length Where to store the length of the result + * @param opaque Whether to use the opaque MIME part + * @returtn 0 on success or -1 on error. + * @see mm_context_flatten + * + * This function creates an ASCII representation of a given MIME part. It will + * dynamically allocate the memory needed and stores the result in the memory + * region pointed to by result. The length of the result will be stored in + * length. If opaque is set to 1, mm_mimepart_flatten will store an opaque + * version of the MIME part in result, which means no headers will be created + * or sanitized. This is particulary useful if the part is digitally signed by + * e.g. PGP, and the signature spans the header fields of the part in question. + * + */ +int +mm_mimepart_flatten(struct mm_mimepart *part, char **result, size_t *length, + int opaque) +{ + size_t part_length; + char *buf; + char *ct_hdr; + + *result = NULL; + *length = 0; + buf = NULL; + ct_hdr = NULL; + part_length = 0; + + if (opaque && part->opaque_body != NULL) { + part_length = strlen(part->opaque_body); + *result = xstrdup(part->opaque_body); + *length = part_length; + return(0); + } else { + if (part->type == NULL) { + return(-1); + } + + ct_hdr = mm_content_tostring(part->type); + if (ct_hdr == NULL) { + return(-1); + } + + part_length += strlen(ct_hdr) + 2; + part_length += strlen("\r\n") * 2; + part_length += strlen(part->body); + + if (part_length < 0) { + goto cleanup; + } + + buf = (char *) xmalloc(part_length); + if (buf == NULL) { + goto cleanup; + } + + snprintf(buf, part_length, + "%s\r\n\r\n%s\r\n", + ct_hdr, + part->body); + + xfree(ct_hdr); + ct_hdr = NULL; + + *result = buf; + *length = part_length; + } + + return(0); + +cleanup: + if (ct_hdr != NULL) { + xfree(ct_hdr); + ct_hdr = NULL; + } + if (buf != NULL) { + xfree(buf); + buf = NULL; + } + + *result = NULL; + *length = 0; + + return -1; +} + +/** + * Sets the default Content-Type for a given MIME part + * + * @param part A valid MIME part object + * @param part Whether the Content-Type should be for composite or not + * @return 0 on success or -1 on failure + * + * This function sets a default Content-Type according to RFC 2045 with a value + * of "text/plain; charset="us-ascii"". This function should only be used if + * the MIME part in question does not have a valid Content-Type specification. + */ +int +mm_mimepart_setdefaultcontenttype(struct mm_mimepart *part, int composite) +{ + struct mm_content *type; + struct mm_param *param; + + if (part == NULL) { + return(-1); + } + + if (part->type != NULL) { + mm_content_free(part->type); + part->type = NULL; + } + + type = mm_content_new(); + if (composite) { + type->maintype = xstrdup("multipart"); + type->subtype = xstrdup("mixed"); + } else { + type->maintype = xstrdup("text"); + type->subtype = xstrdup("plain"); + param = mm_param_new(); + param->name = xstrdup("charset"); + param->value = xstrdup("us-ascii"); + mm_content_attachtypeparam(type, param); + } + + mm_mimepart_attachcontenttype(part, type); + + return (0); +} + +/** @{ + * @name Accessing the MIME part's Content-Type information + */ + +/** + * Attaches a context type object to a MIME part + * + * @param part A valid MIME part object + * @param ct The content type object to attach + * @return Nothing + * + * This function attaches a Content-Type object to a MIME part. It does not + * care whether the Content-Type suites the actual content in the MIME part, + * so the programmer should take care of that. + */ +void +mm_mimepart_attachcontenttype(struct mm_mimepart *part, struct mm_content *ct) +{ + part->type = ct; +} + +/** + * Gets the Content-Type of a given MIME part object + * + * @param part A valid MIME part object + * @return The Content-Type object of the specified MIME part + * + * This function returns a pointer to the Content-Type object of the given + * MIME part. This pointer might be set to NULL, indicating that there is + * no Content-Type object for the given MIME part currently. + */ +struct mm_content * +mm_mimepart_getcontent(struct mm_mimepart *part) +{ + assert(part != NULL); + + return part->type; +} + +/** @} */ diff --git a/trunk/main/minimime/mm_mimeutil.c b/trunk/main/minimime/mm_mimeutil.c new file mode 100644 index 000000000..4a5cc35e9 --- /dev/null +++ b/trunk/main/minimime/mm_mimeutil.c @@ -0,0 +1,137 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2004 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <assert.h> + +#include "mm_internal.h" + +#define MM_DATE_LENGTH 50 + +static const char boundary_charset[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.="; + +/** @file mm_mimeutil.c + * + * This module contains various MIME related utility functions. + */ + +/** @defgroup mimeutil MIME related utility functions */ + +/** + * Generates an RFC 2822 conform date string + * + * @param timezone Whether to include timezone information + * @returns A pointer to the actual date string + * @note The pointer returned must be freed some time + * + * This function generates an RFC 2822 conform date string to use in message + * headers. It allocates memory to hold the string and returns a pointer to + * it. The generated date is in the format (example): + * + * Thu, 25 December 2003 16:35:22 +0100 (CET) + * + * This function dynamically allocates memory and returns a pointer to it. + * This memory should be released with free() once not needed anymore. + */ +#if 0 +int +mm_mimeutil_gendate(char **result) +{ + time_t curtime; + struct tm *curtm; + + if (result != NULL) { + curtime = time(NULL); + curtm = localtime(&curtime); + if ((*result = (char *) malloc(MM_DATE_LENGTH)) == NULL) { + return(-1); + } + return(strftime(*result, MM_DATE_LENGTH, + "%a, %d %b %G %T %z (%Z)", curtm)); + } else { + return(-1); + } +} +#endif + +int +mm_mimeutil_genboundary(char *prefix, size_t length, char **result) +{ + size_t total; + size_t preflen; + struct timeval curtm; + int i; + int pos; + + total = 0; + preflen = 0; + + if (result == NULL) { + return(-1); + } + *result = NULL; + + gettimeofday(&curtm, NULL); + srandom(curtm.tv_usec); + + if (prefix != NULL) { + total = strlen(prefix); + preflen = total; + } + + total += length; + + if ((*result = (char *) xmalloc(total + 1)) == NULL) { + mm_errno = MM_ERROR_ERRNO; + return(-1); + } + + *result = '\0'; + + if (prefix != NULL) { + strlcat(*result, prefix, total); + } + + for (i = 0; i < length - 1; i++) { + pos = random() % strlen(boundary_charset); + *result[i + preflen] = boundary_charset[pos]; + } + *result[total] = '\0'; + + return (0); +} diff --git a/trunk/main/minimime/mm_param.c b/trunk/main/minimime/mm_param.c new file mode 100644 index 000000000..ee4147bf5 --- /dev/null +++ b/trunk/main/minimime/mm_param.c @@ -0,0 +1,225 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +/** + * @file mm_param.c + * + * Functions to manipulate MIME parameters + */ + +/** @defgroup param Accessing and manipulating MIME parameters */ + +/** @{ + * + * @name Functions for manipulating MIME parameters + * + * MIME parameters are properties attached to certain MIME headers, such as + * Content-Type and Content-Disposition. MIME parameters have a textual + * representations as in <i>name=value</i>. They contain important information + * about the MIME structure of a message, such as the boundary string used, + * which charset was used to encode the message and so on. This module + * provides simple to use functions to query or set MIME parameters. + * + * Each MIME header may hold an arbitrary amount of such parameters, which + * are delimeted by each other with a semicolon. + */ + +/** + * Creates a new object to hold a MIME parameter. + * + * @return An object representing a MIME parameter + * @see mm_param_free + * @note The allocated memory must later be freed using mm_param_free() + */ +struct mm_param * +mm_param_new(void) +{ + struct mm_param *param; + + param = (struct mm_param *)xmalloc(sizeof(struct mm_param)); + + param->name = NULL; + param->value = NULL; + + return param; +} + +/** + * Releases all memory associated with a MIME parameter object. + * + * @param param A valid MIME parameter object to be freed + * @return Nothing + * @see mm_param_new + */ +void +mm_param_free(struct mm_param *param) +{ + assert(param != NULL); + + if (param->name != NULL) { + xfree(param->name); + param->name = NULL; + } + if (param->value != NULL) { + xfree(param->value); + param->value = NULL; + } + xfree(param); +} + +/** + * Generates a new Content-Type parameter with the given name and value + * + * @param name The name of the MIME parameter + * @param value The value of the MIME parameter + * @returns A new MIME parameter object + * @see mm_param_free + * @see mm_param_new + * + * This function generates a new MIME parameter, with the name + * and value given as the arguments. The needed memory for the operation + * is allocated dynamically. It stores a copy of name and value in the + * actual object, so the memory holding the arguments can safely be + * freed after successfull return of this function. + */ +#if 0 +struct mm_param * +mm_param_generate(const char *name, const char *value) +{ + struct mm_param *param; + + param = mm_param_new(); + + param->name = xstrdup(name); + param->value = xstrdup(value); + + return param; +} +#endif + +/** + * Sets the name of the given MIME parameter + * + * @param param A valid MIME parameter object + * @param name The new name of the parameter + * @param copy If set to > 0, copy the value stored in name + * @returns The address of the previous name for passing to free() + */ +#if 0 +char * +mm_param_setname(struct mm_param *param, const char *name, int copy) +{ + char *retadr; + assert(param != NULL); + + retadr = param->name; + + if (copy) + param->name = xstrdup(name); + else + param->name = (char *)name; + + return retadr; +} +#endif + +/** + * Sets the value of the given MIME parameter + * + * @param param A valid MIME parameter object + * @param name The new value for the parameter + * @param copy If set to > 0, copy the value stored in value + * @returns The address of the previous value for passing to free() + */ +#if 0 +char * +mm_param_setvalue(struct mm_param *param, const char *value, int copy) +{ + char *retadr; + assert(param != NULL); + + retadr = param->value; + + if (copy) + param->value = xstrdup(value); + else + param->value = (char *)value; + + return retadr; +} +#endif + +/** + * Gets the name of a MIME parameter object + * + * @param param A valid MIME parameter object + * @returns The name of the MIME parameter + */ +#if 0 +const char * +mm_param_getname(struct mm_param *param) +{ + assert(param != NULL); + return param->name; +} +#endif + +/** + * Gets the value of a MIME parameter object + * + * @param param A valid MIME parameter object + * @returns The value of the MIME parameter + */ +#if 0 +const char * +mm_param_getvalue(struct mm_param *param) +{ + assert(param != NULL); + return param->value; +} +#endif + +/** @} */ diff --git a/trunk/main/minimime/mm_parse.c b/trunk/main/minimime/mm_parse.c new file mode 100644 index 000000000..4223d99e0 --- /dev/null +++ b/trunk/main/minimime/mm_parse.c @@ -0,0 +1,168 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" +#include "mm_util.h" + +#include "mimeparser.h" +#include "mimeparser.tab.h" + +/** @file mm_parse.c + * + * Functions to parse MIME messages + */ + +/** + * Parses a NUL-terminated string into a MiniMIME context + * + * @param ctx A valid MiniMIME context object + * @param text The NUL-terminated string to parse + * @param parsemode The parsemode + * @param flags The flags to pass to the parser + * @returns 0 on success or -1 on failure + * @note Sets mm_errno if an error occurs + * + * This function parses a MIME message, stored in the memory region pointed to + * by text (must be NUL-terminated) according to the parseflags and stores the + * results in the MiniMIME context specified by ctx. + * + * The following modes can be used to specify how the message should be + * parsed: + * + * - MM_PARSE_STRICT: Do not tolerate MIME violations + * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible + * + * The context needs to be initialized before using mm_context_new() and may + * be freed using mm_context_free(). + */ +int +mm_parse_mem(MM_CTX *ctx, const char *text, int parsemode, int flags) +{ + void *yyscanner; + int res; + struct parser_state pstate; + + pstate.ctx = ctx; + pstate.parsemode = parsemode; + + mimeparser_yylex_init(&yyscanner); + PARSER_initialize(&pstate, yyscanner); + + PARSER_setbuffer(text, yyscanner); + PARSER_setfp(NULL, yyscanner); + + res = mimeparser_yyparse(&pstate,yyscanner); + mimeparser_yylex_destroy(yyscanner); + return res; +} + +/** + * Parses a file into a MiniMIME context + * + * @param ctx A valid MiniMIME context object + * @param filename The name of the file to parse + * @param parsemode The parsemode + * @param flags The flags to pass to the parser + * @returns 0 on success or -1 on failure + * @note Sets mm_errno if an error occurs + * + * This function parses a MIME message, stored in the filesystem according to + * the parseflags and stores the results in the MiniMIME context specified by + * ctx. + * + * The following modes can be used to specify how the message should be + * parsed: + * + * - MM_PARSE_STRICT: Do not tolerate MIME violations + * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible + * + * The context needs to be initialized before using mm_context_new() and may + * be freed using mm_context_free(). + */ +int +mm_parse_file(MM_CTX *ctx, const char *filename, int parsemode, int flags) +{ + FILE *fp; + int res; + void *yyscanner; + struct parser_state pstate; + + mimeparser_yylex_init(&yyscanner); + + if ((fp = fopen(filename, "r")) == NULL) { + mm_errno = MM_ERROR_ERRNO; + return -1; + } + + PARSER_setfp(fp,yyscanner); + PARSER_initialize(&pstate, yyscanner); + + pstate.ctx = ctx; + pstate.parsemode = parsemode; + + res = mimeparser_yyparse(&pstate,yyscanner); + mimeparser_yylex_destroy(yyscanner); + fclose(fp); + return res; +} + +int +mm_parse_fileptr(MM_CTX *ctx, FILE *f, int parsemode, int flags) +{ + int res; + void *yyscanner; + struct parser_state pstate; + + mimeparser_yylex_init(&yyscanner); + + PARSER_setfp(f, yyscanner); + PARSER_initialize(&pstate, yyscanner); + + pstate.ctx = ctx; + pstate.parsemode = parsemode; + + res = mimeparser_yyparse(&pstate,yyscanner); + mimeparser_yylex_destroy(yyscanner); + + return res; +} diff --git a/trunk/main/minimime/mm_queue.h b/trunk/main/minimime/mm_queue.h new file mode 100644 index 000000000..893e2fae2 --- /dev/null +++ b/trunk/main/minimime/mm_queue.h @@ -0,0 +1,508 @@ +/* $OpenBSD: queue.h,v 1.25 2004/04/08 16:08:21 henning Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != SLIST_END(head); \ + (varp) = &SLIST_NEXT((var), field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_NEXT(head, elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ + if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/trunk/main/minimime/mm_util.c b/trunk/main/minimime/mm_util.c new file mode 100644 index 000000000..90debcb6e --- /dev/null +++ b/trunk/main/minimime/mm_util.c @@ -0,0 +1,412 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <assert.h> + +#include "mm_internal.h" + +/** @file mm_util.c + * + * This module contains utility functions for the MiniMIME library + */ + +/** @defgroup util General purpose utility functions */ + +#ifndef __HAVE_LEAK_DETECTION +/** + * Allocates a block of memory + * + * @param size The size of the memory region to allocate + * @return A pointer to the allocated memory region + * @ingroup util + * + * xmalloc() calls abort() if either the size argument is negative or the + * requested memory amount could not be allocated via an assert() call. + */ +void * +xmalloc(size_t size) +{ + void *p; + + assert(size > 0); + p = malloc(size); + assert(p != NULL); + + return p; +} + +/** + * realloc() wrapper + * + * @param p Pointer to a memory region which should be reallocated + * @param size The new size of the memory region + * @return A pointer to the reallocated memory region + * @ingroup util + * + * xrealloc() is a wrapper around realloc() which calls abort() if either the + * size argument is negative or the requested memory amount could not be + * allocated. + */ +void * +xrealloc(void *p, size_t size) +{ + void *n; + + assert(size > 0); + n = realloc(p, size); + assert(n != NULL); + + return n; +} + +char * +xstrdup(const char *str) +{ + char *p; + + assert(str != NULL); + p = strdup(str); + assert(p != NULL); + + return p; +} + +void +xfree(void *p) +{ + assert(p != NULL); + free(p); + p = NULL; + assert(p == NULL); +} +#endif /* ! __HAVE_LEAK_DETECTION */ + +/** + * Unquotes a string + * + * @param string The quoted string to unquote + * @return A pointer to the unquoted string + * @ingroup util + * + * This function unquotes a string. That is, it returns a pointer to a newly + * allocated memory region in which the unquoted string is stored. Only + * leading and trailing double-qoutes are removed. The string needs to be + * freed when it is not needed anymore. + */ +char * +mm_unquote(const char *string) +{ + char *ret; + + if (string[0] != '\"' || string[strlen(string)-1] != '\"') + return xstrdup(string); + + ret = xstrdup(string + 1); + ret[strlen(ret)-1] = '\0'; + + return ret; +} + + +/** + * Removes MIME comments from a string + * + * @param string The string to uncomment + * @return A pointer to the uncommented string or NULL on error. Sets mm_errno. + * @ingroup util + * + * This function removes MIME comments from a string (included in parantheses). + * It returns a pointer to a newly allocated memory region in which the + * uncommented string is stored. The returned string needs to be freed when + * it's not used anymore. + */ +char * +mm_uncomment(const char *string) +{ + char *buf, *new, *orig, *token; + size_t new_size; + int found; + int open; + + assert(string != NULL); + + new_size = strlen(string) + 1; + new = NULL; + buf = NULL; + orig = NULL; + found = 0; + open = 0; + mm_errno = MM_ERROR_NONE; + + buf = xstrdup(string); + orig = buf; + + while (*buf != '\0') { + if (*buf == '(') { + open++; + new_size--; + found++; + } else if (*buf == ')') { + open--; + new_size--; + } else { + if (open) + new_size--; + } + buf++; + } + + if (open != 0) { + mm_errno = MM_ERROR_PARSE; + mm_error_setmsg("Uncommenting: parantheses are unbalanced"); + goto cleanup; + } + + if (!found) { + new = orig; + return orig; + } + + new = xmalloc(new_size + 1); + *new = '\0'; + buf = orig; + token = buf; + + /* Tokenize our string by parentheses, and copy the portions which are + * not commented to our destination. + */ + open = 0; + while (*buf != '\0') { + if (*buf == '(') { + if (!open) { + *buf = '\0'; + strlcat(new, token, new_size); + token = buf+1; + } + open++; + } + if (*buf == ')') { + open--; + token = buf + 1; + } + buf++; + } + + strlcat(new, token, new_size); + +cleanup: + if (orig != NULL) { + xfree(orig); + orig = NULL; + } + + if (mm_errno != MM_ERROR_NONE) { + if (new != NULL) { + xfree(new); + new = NULL; + } + return NULL; + } else { + return new; + } +} + +/** + * separate strings + * + * @param stringp A pointer to the string being splitted + * @param delim The delimeter string + * @ingroup util + * + * This function works similar to strsep(), with the difference that delim is + * treated as a whole. + */ +char * +xstrsep(char **stringp, const char *delim) +{ + char *p; + char *s; + char *r; + + if (*stringp == NULL || *stringp == '\0') + return NULL; + + p = *stringp; + + if ((s = strstr(p, delim)) == NULL) { + r = p; + while (*p != '\0') + p++; + *stringp = NULL; + return r; + } else { + r = p; + p += strlen(p) - strlen(s); + *p = '\0'; + *stringp = p + strlen(delim); + return r; + } +} + +/** + * Strips a given character set from a string + * + * @param input The string which to strip + * @param strip The character set to strip off + * @return A copy of the original string with all chars stripped + * @ingroup util + */ +char * +mm_stripchars(char *input, char *strip) +{ + char *output, *orig; + int i, j, chars; + + assert(input != NULL); + assert(strip != '\0'); + + chars = 0; + orig = input; + + while (*orig != '\0') { + for (i = 0; i < strlen(strip); i++) { + if (*orig == strip[i]) { + chars++; + break; + } + } + orig++; + } + + /* If we have not found any char in the input, return a dup of the orig + string */ + if (chars == 0) + return(xstrdup(input)); + + output = (char *)xmalloc(strlen(input) - chars); + orig = output; + + for (i = 0; i < strlen(input); i++) { + int stripc; + stripc = 0; + for (j = 0; j < strlen(strip); j++) { + if (input[i] == strip[j]) { + stripc = 1; + break; + } + } + if (stripc == 0) { + *output = input[i]; + output++; + } + } + + *output = '\0'; + + return(orig); +} + +/** + * Adds characters to a string at given positions + * + * @param input The string to which to add characters + * @param add The character string to add + * @param linelength The position where to add the character + * @return A copy of the string with characters added + * @ingroup util + * + * This function adds the characters add at each linelength positions and + * returns this new string. + */ +char * +mm_addchars(char *input, char *add, uint16_t linelength) +{ + uint32_t len; + uint32_t i; + uint32_t l; + uint32_t j; + uint16_t addcrlf; + char *output; + char *orig; + + len = strlen(input); + if (len <= linelength) + return(xstrdup(input)); + + addcrlf = len / linelength; + + output = (char *)xmalloc(len + (addcrlf * strlen(add))); + orig = output; + + for (i = 0, l = 0; i < len; i++, l++) { + if (l == linelength) { + for (j = 0; j < strlen(add); j++) { + *output = add[j]; + output++; + } + l = 0; + } + *output = input[i]; + output++; + } + + *output = '\0'; + output = orig; + + return(orig); +} + +void +mm_striptrailing(char **what, const char *charset) +{ + size_t eos, i, hit; + char *str; + + str = *what; + for (eos = strlen(str)-1; eos >= 0; eos--) { + hit = 0; + for (i = 0; i < strlen(charset); i++) { + if (str[eos] == charset[i]) { + str[eos] = '\0'; + hit = 1; + break; + } + } + if (!hit) + break; + } +} diff --git a/trunk/main/minimime/mm_util.h b/trunk/main/minimime/mm_util.h new file mode 100644 index 000000000..b7058657d --- /dev/null +++ b/trunk/main/minimime/mm_util.h @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __MM_UTIL_H +#define __MM_UTIL_H + +#define STRIP_TRAILING(str, charset) do { \ + size_t eos, i, hit; \ + for (eos = strlen(str); eos > 0; eos--) { \ + hit = 0; \ + for (i = 0; i <= strlen(charset); i++) { \ + if (str[eos] == charset[i]) {\ + str[eos] = '\0'; \ + hit = 1; \ + break; \ + } \ + } \ + if (!hit) \ + break; \ + } \ +} while (0); + +#endif /* ! __MM_UTIL_H */ diff --git a/trunk/main/minimime/mm_warnings.c b/trunk/main/minimime/mm_warnings.c new file mode 100644 index 000000000..c9b8a7daf --- /dev/null +++ b/trunk/main/minimime/mm_warnings.c @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" + +/** + * Attaches a warning message to a context + * + * @param ctx A valid MiniMIME context object + * @param type The type of the warning + * @param fmt The warning message as format string + */ +void +mm_warning_add(MM_CTX *ctx, int type, const char *fmt, ...) +{ + struct mm_warning *warning; + char buf[1024]; + va_list ap; + + assert(ctx != NULL); + + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + warning = (struct mm_warning *)xmalloc(sizeof(struct mm_warning)); + warning->message = xstrdup(buf); + warning->type = type; + + if (SLIST_EMPTY(&ctx->warnings)) { + SLIST_INSERT_HEAD(&ctx->warnings, warning, next); + } else { + struct mm_warning *last, *after; + + after = NULL; + + SLIST_FOREACH(last, &ctx->warnings, next) { + if (last != NULL) { + after = last; + } + } + + assert(after != NULL); + + SLIST_INSERT_AFTER(after, warning, next); + } +} + +struct mm_warning * +mm_warning_next(MM_CTX *ctx, struct mm_warning **last) +{ + struct mm_warning *warning; + + if (*last == NULL) { + warning = SLIST_FIRST(&ctx->warnings); + } else { + warning = SLIST_NEXT(*last, next); + } + + *last = warning; + return warning; +} diff --git a/trunk/main/minimime/strlcat.c b/trunk/main/minimime/strlcat.c new file mode 100644 index 000000000..2d2da0413 --- /dev/null +++ b/trunk/main/minimime/strlcat.c @@ -0,0 +1,70 @@ +/* $OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/trunk/main/minimime/strlcpy.c b/trunk/main/minimime/strlcpy.c new file mode 100644 index 000000000..94785a309 --- /dev/null +++ b/trunk/main/minimime/strlcpy.c @@ -0,0 +1,66 @@ +/* $OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/trunk/main/minimime/sys/mm_queue.h b/trunk/main/minimime/sys/mm_queue.h new file mode 100644 index 000000000..c85bb240c --- /dev/null +++ b/trunk/main/minimime/sys/mm_queue.h @@ -0,0 +1,503 @@ +/* $OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ + if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/trunk/main/minimime/test.sh b/trunk/main/minimime/test.sh new file mode 100755 index 000000000..1beca0b74 --- /dev/null +++ b/trunk/main/minimime/test.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# MiniMIME test cases + +[ ! -x ./tests/parse -o ! -x ./tests/create ] && { + echo "You need to compile the test suite first to accomplish tests" + exit 1 +} + +LD_LIBRARY_PATH=${PWD} +export LD_LIBRARY_PATH + +DIRECTORY=${1:-tests/messages} +FILES=${2:-"*"} + +TESTS=0 +F_ERRORS=0 +F_INVALID="" +M_ERRORS=0 +M_INVALID="" +for f in ${DIRECTORY}/${FILES}; do + if [ -f "${f}" ]; then + TESTS=$((TESTS + 2)) + echo -n "Running PARSER test for $f (file)... " + output=`./tests/parse $f 2>&1` + [ $? != 0 ] && { + echo "FAILED ($output)" + F_ERRORS=$((F_ERRORS + 1)) + F_INVALID="${F_INVALID} ${f} " + } || { + echo "PASSED" + } + echo -n "Running PARSER test for $f (memory)... " + output=`./tests/parse -m $f 2>&1` + [ $? != 0 ] && { + echo "FAILED ($output)" + M_ERRORS=$((M_ERRORS + 1)) + M_INVALID="${M_INVALID} ${f} " + } || { + echo "PASSED" + } + fi +done + +echo "Ran a total of ${TESTS} tests" + +if [ ${F_ERRORS} -gt 0 ]; then + echo "!! ${F_ERRORS} messages had errors in file based parsing" + echo "-> ${F_INVALID}" +fi +if [ ${M_ERRORS} -gt 0 ]; then + echo "!! ${F_ERRORS} messages had errors in memory based parsing" +fi + +unset LD_LIBRARY_PATH diff --git a/trunk/main/minimime/tests/Makefile b/trunk/main/minimime/tests/Makefile new file mode 100644 index 000000000..ae460c6b2 --- /dev/null +++ b/trunk/main/minimime/tests/Makefile @@ -0,0 +1,18 @@ +BINARIES=parse create +CFLAGS=-Wall -ggdb -g3 -I.. +LDFLAGS=-L.. +LIBS=-lmmime +CC=gcc + +all: parse create + +parse: parse.o + $(CC) -o parse parse.o $(LDFLAGS) $(LIBS) + +create: create.o + $(CC) -o create create.o $(LDFLAGS) $(LIBS) + +clean: + rm -f $(BINARIES) + rm -f *.o + rm -f *.core diff --git a/trunk/main/minimime/tests/create.c b/trunk/main/minimime/tests/create.c new file mode 100644 index 000000000..c881f17f7 --- /dev/null +++ b/trunk/main/minimime/tests/create.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * MiniMIME test program - create.c + * + * Creates a MIME message of the given MIME parts + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> + +#include "mm.h" + +const char *progname; + +void +usage(void) +{ + fprintf(stderr, + "MiniMIME test suite\n" + "USAGE: %s <part> [<part_2>[<part_N>[...]]]\n", + progname + ); +} + +void +print_error(void) +{ + fprintf(stderr, "ERROR: %s\n", mm_error_string()); +} + +int +main(int argc, char **argv) +{ + MM_CTX *ctx; + struct mm_mimepart *part; + char *data; + size_t length; + int i; + + progname = argv[0]; + + if (argc < 2) { + usage(); + exit(1); + } + + mm_library_init(); + + ctx = mm_context_new(); + + part = mm_mimepart_new(); + mm_context_attachpart(ctx, part); + mm_envelope_setheader(ctx, "From", "foo@bar.com"); + + for (i=1; i < argc; i++) { + part = mm_mimepart_fromfile(argv[i]); + if (part == NULL) { + print_error(); + exit(1); + } + mm_context_attachpart(ctx, part); + } + + if (mm_context_flatten(ctx, &data, &length, 0) == -1) { + print_error(); + exit(1); + } + + printf("%s", data); + + exit(0); +} diff --git a/trunk/main/minimime/tests/messages/test1.txt b/trunk/main/minimime/tests/messages/test1.txt new file mode 100644 index 000000000..2c24b1972 --- /dev/null +++ b/trunk/main/minimime/tests/messages/test1.txt @@ -0,0 +1,50 @@ +Return-Path: <rezine@hannover.ccc.de> +X-Original-To: test@mistrust.net +Delivered-To: rezine@hannover.ccc.de +Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (Client did not present a certificate) + by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2 + for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST) +Date: Sun, 24 Aug 2003 15:49:15 +0200 +From: Jann Fischer <rezine@hannover.ccc.de> +To: test@mistrust.net +Subject: Test +Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de> +Organization: Chaos Computer Club Hannover +X-Mailer: Who-Cares 5.23 +Mime-Version: 1.0 +Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; + boundary="=.2S1ZDSX8ir3lbt" + +--=.2S1ZDSX8ir3lbt +Content-Type: application/pgp-encrypted + +Version: 1 + +--=.2S1ZDSX8ir3lbt +Content-Type: application/octet-stream + +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.2.2 (OpenBSD) + +hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3 +2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm +5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE +AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6 +Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW +dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf +V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste +Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O +h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ +GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX +Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J +JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq +0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf +1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F +Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX +2tj4//Dyw3Lw/L5xGxYRP1Q= +=fSLd +-----END PGP MESSAGE----- + +--=.2S1ZDSX8ir3lbt-- diff --git a/trunk/main/minimime/tests/messages/test2.txt b/trunk/main/minimime/tests/messages/test2.txt new file mode 100644 index 000000000..2c24b1972 --- /dev/null +++ b/trunk/main/minimime/tests/messages/test2.txt @@ -0,0 +1,50 @@ +Return-Path: <rezine@hannover.ccc.de> +X-Original-To: test@mistrust.net +Delivered-To: rezine@hannover.ccc.de +Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (Client did not present a certificate) + by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2 + for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST) +Date: Sun, 24 Aug 2003 15:49:15 +0200 +From: Jann Fischer <rezine@hannover.ccc.de> +To: test@mistrust.net +Subject: Test +Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de> +Organization: Chaos Computer Club Hannover +X-Mailer: Who-Cares 5.23 +Mime-Version: 1.0 +Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; + boundary="=.2S1ZDSX8ir3lbt" + +--=.2S1ZDSX8ir3lbt +Content-Type: application/pgp-encrypted + +Version: 1 + +--=.2S1ZDSX8ir3lbt +Content-Type: application/octet-stream + +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.2.2 (OpenBSD) + +hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3 +2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm +5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE +AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6 +Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW +dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf +V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste +Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O +h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ +GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX +Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J +JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq +0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf +1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F +Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX +2tj4//Dyw3Lw/L5xGxYRP1Q= +=fSLd +-----END PGP MESSAGE----- + +--=.2S1ZDSX8ir3lbt-- diff --git a/trunk/main/minimime/tests/messages/test3.txt b/trunk/main/minimime/tests/messages/test3.txt new file mode 100644 index 000000000..082b6fb61 --- /dev/null +++ b/trunk/main/minimime/tests/messages/test3.txt @@ -0,0 +1,12 @@ +From: Jann Fischer <rezine@criminology.de> +To: cipherlist <cipherlist@mistrust.net> +Subject: Foobar +Date: blahblah +MIME-Version: 1.0 (MiniMIME) +Content-Type: multipart/mixed; boundary="abcd" + +--abcd +Content-Type: plain/text; + +This is a test :-> +--abcd-- diff --git a/trunk/main/minimime/tests/messages/test4.txt b/trunk/main/minimime/tests/messages/test4.txt new file mode 100644 index 000000000..a08246939 --- /dev/null +++ b/trunk/main/minimime/tests/messages/test4.txt @@ -0,0 +1,168 @@ +X-Envelope-From: <511-bounces@hannover.ccc.de> +X-Envelope-To: <rezine@criminology.de> +X-Delivery-Time: 1070263752 +Received: from gost.hannover.ccc.de (hannover.ccc.de [62.48.71.164]) + by mailin.webmailer.de (8.12.10/8.12.10) with ESMTP id hB17TAUR020052 + for <rezine@criminology.de>; Mon, 1 Dec 2003 08:29:10 +0100 (MET) +Received: from localhost.hannover.ccc.de (unknown [127.0.0.1]) + by gost.hannover.ccc.de (Postfix) with ESMTP + id 092C8BC81; Mon, 1 Dec 2003 08:29:23 +0100 (CET) +X-Original-To: 511@hannover.ccc.de +Delivered-To: 511@hannover.ccc.de +Received: from sbapp3 (unknown [211.157.36.9]) + by gost.hannover.ccc.de (Postfix) with ESMTP id 3F93ABC7C + for <511@hannover.ccc.de>; Mon, 1 Dec 2003 08:29:12 +0100 (CET) +From: "Vanessa Lintner" <reply@seekercenter.net> +To: 511@hannover.ccc.de +Date: Mon, 1 Dec 2003 15:30:57 +0800 +X-Priority: 3 +X-Library: Indy 8.0.25 +Message-Id: <20031201072912.3F93ABC7C@gost.hannover.ccc.de> +Subject: [CCC511] http://lists.hannover.ccc.de +X-BeenThere: 511@hannover.ccc.de +X-Mailman-Version: 2.1.2 +Precedence: list +Reply-To: Vanessa Lintner <vanessa@seekercenter.net>, + Oeffentliche Mailingliste des C3H <511@hannover.ccc.de> +List-Id: Oeffentliche Mailingliste des C3H <511.hannover.ccc.de> +List-Unsubscribe: <http://hannover.ccc.de/mailman/listinfo/511>, + <mailto:511-request@hannover.ccc.de?subject=unsubscribe> +List-Post: <mailto:511@hannover.ccc.de> +List-Help: <mailto:511-request@hannover.ccc.de?subject=help> +List-Subscribe: <http://hannover.ccc.de/mailman/listinfo/511>, + <mailto:511-request@hannover.ccc.de?subject=subscribe> +Content-Type: multipart/mixed; boundary="===============14807035762661644==" +Sender: 511-bounces@hannover.ccc.de +Errors-To: 511-bounces@hannover.ccc.de + +--===============14807035762661644== +Content-Type: text/html; + +<html> +<head> +<title></title> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> +<style type="text/css"> +.stbtm { + BACKGROUND-COLOR:#cecbde; BORDER-BOTTOM: #665b8e 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #665b8e 1px solid; BORDER-TOP: #ffffff 1px solid; COLOR: #000000; FONT-SIZE: 12pt; HEIGHT: 26px; WIDTH: 120px; clip: rect( )} +.stedit { + background-color:#484C68; white-space: nowrap; border: #000000; BORDER-BOTTOM: #ffffff 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid; FONT-SIZE: 10pt; color: #CCCCCC; font-weight: bold} + +</style> +</head> +<BODY leftMargin=0 onload="" topMargin=0 marginheight="0" marginwidth="0" bgcolor="#FFFFFF"> + <table border="0" cellspacing="0" cellpadding="0" width="580"> + <tr> + <td width="20" rowspan="2"> </td> + <td colspan="3"> + <table border="0" cellspacing="0" cellpadding="0" align="left" width="560"> + <tr> + <td width="330" height="307"> + <table width="330" border="0" cellspacing="0" cellpadding="0" background="http://www.imagespool.com/skbmp/letter_01.gif" height="307"> + <tr> + <td> + <p> <font face=Arial size=2> </font> <font face=Arial size=2><font face="Verdana, Arial, Helvetica, sans-serif" color="#000000">Hello,<br> + <br> + I have visited <a href='http://lists.hannover.ccc.de'>lists.hannover.ccc.de</a> + and noticed that your website is not listed on some search + engines. I am sure that through our service the number of + people who visit your website will definitely increase. + <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter</a> + is a unique technology that instantly submits your website + to over 500,000 search engines and directories -- a really + low-cost and effective way to advertise your site. For more + details please go to <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter.net</a>.<br> + <br> + Give your website maximum exposure today!<br> + Looking forward to hearing from you.<br> + <br> + </font></font> + <table border=0 width=100%> + <tr> + <td width=50%> <font face="Arial" color="#000000" size="2">Best + Regards,<br> + Vanessa Lintner<br> + Sales & Marketing <br> + <a target=_blank href="http://www.seekercenter.net/index.php">www.SeekerCenter.net</a></font> + <TD><td width=50%> + <div align="center" valign=middle> + <form target=_blank action=http://www.seekercenter.net method=POST> + <input type="submit" name="Submit" value="Signup Now!!!" class="stbtm"> + </form> + </div> + </TD> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td width="250" height="64" valign="middle"> + <table width="230" border="0" cellspacing="0" cellpadding="0"> + <tr> + <td colspan="3" height="2"></td> + </tr> + <tr> + <td colspan="3"><img src="http://report.imagespool.com/report_email.php?s=1&e=511@hannover.ccc.de" border=0 width=0 height=0> + <p><img src="http://www.imagespool.com/skbmp/letter_04.gif" height="12"></p> + </td> + </tr> + <tr> + <td colspan="3"><img src="http://www.imagespool.com/skbmp/letter_05.gif" height="127"><img src="http://ww2.imagespool.com/1/9/b/0r066.jpg" width="177" height="127"><img src="http://www.imagespool.com/skbmp/letter_07.gif" width="33" height="127"></td> + </tr> + <tr> + <td colspan="3" height="92" background="http://www.imagespool.com/skbmp/letter_08.gif" valign="bottom"> + <table width="230" border="0" cellspacing="0" cellpadding="0" height="92"> + <tr> + <td width="36" height="43"> </td> + <td width="157" height="43"> </td> + <td width="134" height="43"> </td> + </tr> + <tr> + <td width="36" height="2"> </td> + <td width="157" height="2"> </td> + <td width="134" height="2"> </td> + </tr> + </table> + </td> + </tr> + <tr> </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr> + <td colspan="3"> + <table width="560" border="0" cellspacing="0" cellpadding="1" bordercolor="0"> + <tr> + <td> </td> + </tr> + <tr> + <td bgcolor="#EFEFEF"><font face="Verdana, Arial, Helvetica, sans-serif" size="1">You + are receiving this email because you opted-in to receive special + offers through a partner website. If you feel that you received + this email in error or do not wish to receive additional special + offers, please enter your email address here and click the button + of "Remove Me": <a href="http://www.seekercenter.net/remove.php?email=511@hannover.ccc.de"> + <img src="http://www.imagespool.com/skbmp/removeme.gif" width="73" height="17" border="0"></a> + </font></td> + </tr> + </table> + </td> + </tr> + </table> +</body> +</html> + +--===============14807035762661644== +Content-Type: text/plain; charset="iso-8859-1" +MIME-Version: 1.0 +Content-Transfer-Encoding: quoted-printable +Content-Disposition: inline + +_______________________________________________ +511 mailing list +511@hannover.ccc.de +--===============14807035762661644==-- diff --git a/trunk/main/minimime/tests/messages/test5.txt b/trunk/main/minimime/tests/messages/test5.txt new file mode 100644 index 000000000..5e4cdb17d --- /dev/null +++ b/trunk/main/minimime/tests/messages/test5.txt @@ -0,0 +1,44 @@ +Return-Path: <rezine@criminology.de> +X-Original-To: rezine@mistrust.net +Delivered-To: rezine@hannover.ccc.de +Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (Client did not present a certificate) + by gost.hannover.ccc.de (Postfix) with ESMTP id 79E9BBC7C + for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:36 +0100 (CET) +Received: from thinktank.niedersachsen.de (localhost [127.0.0.1]) + by thinktank.niedersachsen.de (8.12.9/8.12.2) with SMTP id hBOCZBFU029588 + for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:11 +0100 (CET) +Date: Wed, 24 Dec 2003 13:35:11 +0100 +From: Jann Fischer <rezine@criminology.de> +To: rezine@mistrust.net +Subject: Test +Message-Id: <20031224133511.5f4b6d9b.rezine@criminology.de> +X-Mailer: Who Cares 5.23 +Mime-Version: 1.0 +Content-Type: multipart/mixed; + boundary="Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800" + +This is a multi-part message in MIME format. + +--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800 +Content-Type: text/plain; charset=US-ASCII +Content-Transfer-Encoding: 7bit + +Test + +-- +Be careful who you follow. +0x6D839821 | FA8C 3663 9906 D8C3 AC16 F7C4 66E0 F351 6D83 9821 + +--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800 +Content-Type: application/octet-stream; + name="bar.c" +Content-Disposition: attachment; + filename="bar.c" +Content-Transfer-Encoding: base64 + +I2luY2x1ZGUgPHN0ZGlvLmg+Cgp2b2lkCm1haW4oaW50IGFyZ2MsIGNoYXIgKiphcmd2KQp7CgkJ +cHJpbnRmKCIlc1xuIiwgYXJndlswXSk7Cn0K + +--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800-- diff --git a/trunk/main/minimime/tests/messages/test6.txt b/trunk/main/minimime/tests/messages/test6.txt new file mode 100644 index 000000000..fb4e7a14c --- /dev/null +++ b/trunk/main/minimime/tests/messages/test6.txt @@ -0,0 +1,12 @@ +From: Me +Date: Foobar +To: There +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="abcde" + +--abcde +Content-Type: text/plain + +Blah blah +Blah +--abcde-- diff --git a/trunk/main/minimime/tests/messages/test7.txt b/trunk/main/minimime/tests/messages/test7.txt new file mode 100644 index 000000000..1cda11e3f --- /dev/null +++ b/trunk/main/minimime/tests/messages/test7.txt @@ -0,0 +1,64 @@ +Return-Path: MAILER-DAEMON +Received: from chaos.verfassungsschutz.de (localhost [IPv6:::1]) + by chaos.verfassungsschutz.de (8.12.7/8.12.2) with ESMTP id h2EKV1oM031761 + for <jfi@chaos.verfassungsschutz.de>; Fri, 14 Mar 2003 21:31:18 +0100 (CET) +Received: from localhost (localhost) + by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BNU1vr029177; + Wed, 12 Mar 2003 00:35:01 +0100 (CET) +Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET) +From: Mail Delivery Subsystem <MAILER-DAEMON@chaos.verfassungsschutz.de> +Message-Id: <200303112335.h2BNU1vr029177@chaos.verfassungsschutz.de> +To: jfi@chaos.verfassungsschutz.de +MIME-Version: 1.0 +Content-Type: multipart/report; report-type=delivery-status; + boundary="h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de" +Subject: Warning: could not send message for past 4 hours +Auto-Submitted: auto-generated (warning-timeout) + +This is a MIME-encapsulated message + +--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de + + ********************************************** + ** THIS IS A WARNING MESSAGE ONLY ** + ** YOU DO NOT NEED TO RESEND YOUR MESSAGE ** + ********************************************** + +The original message was received at Tue, 11 Mar 2003 20:18:36 +0100 (CET) +from jfi@localhost + + ----- Transcript of session follows ----- +451 4.4.1 reply: read error from localhost +rezine@kommunism.us... Deferred: Connection timed out with localhost +Warning: message still undelivered after 4 hours +Will keep trying until message is 5 days old + +--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de +Content-Type: message/delivery-status + +Reporting-MTA: dns; chaos.verfassungsschutz.de +Arrival-Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET) + +Final-Recipient: RFC822; rezine@kommunism.us +Action: delayed +Status: 4.4.2 +Last-Attempt-Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET) +Will-Retry-Until: Sun, 16 Mar 2003 20:18:36 +0100 (CET) + +--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de +Content-Type: message/rfc822 + +Return-Path: <jfi> +Received: (from jfi@localhost) + by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BJIawm025679 + for rezine@kommunism.us; Tue, 11 Mar 2003 20:18:36 +0100 (CET) +Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET) +From: Jann Fischer <jfi> +Message-Id: <200303111918.h2BJIawm025679@chaos.verfassungsschutz.de> +To: rezine@kommunism.us +Subject: Test + +Test + +--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de-- + diff --git a/trunk/main/minimime/tests/parse.c b/trunk/main/minimime/tests/parse.c new file mode 100644 index 000000000..3d3bdf028 --- /dev/null +++ b/trunk/main/minimime/tests/parse.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004 Jann Fischer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * MiniMIME test program - parse.c + * + * Parses any given messages + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> + +#include "mm.h" + +const char *progname; + +void +usage(void) +{ + fprintf(stderr, + "MiniMIME test suite\n" + "Usage: %s [-m] <filename>\n\n" + " -m : use memory based scanning\n\n", + progname + ); + exit(1); +} + +int +main(int argc, char **argv) +{ + MM_CTX *ctx; + struct mm_mimeheader *header, *lastheader = NULL; + struct mm_mimepart *part; + struct mm_content *ct; + int parts, i; + struct stat st; + int fd; + char *buf; + int scan_mode = 0; + + progname = strdup(argv[0]); + + while ((i = getopt(argc, argv, "m")) != -1) { + switch(i) { + case 'm': + scan_mode = 1; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + } + +#ifdef __HAVE_LEAK_DETECTION + /* Initialize memory leak detection if compiled in */ + MM_leakd_init(); +#endif + + /* Initialize MiniMIME library */ + mm_library_init(); + + /* Register all default codecs (base64/qp) */ + mm_codec_registerdefaultcodecs(); + + do { + /* Create a new context */ + ctx = mm_context_new(); + + /* Parse a file into our context */ + if (scan_mode == 0) { + i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0); + } else { + if (stat(argv[0], &st) == -1) { + err(1, "stat"); + } + + if ((fd = open(argv[0], O_RDONLY)) == -1) { + err(1, "open"); + } + + buf = (char *)malloc(st.st_size); + if (buf == NULL) { + err(1, "malloc"); + } + + if (read(fd, buf, st.st_size) != st.st_size) { + err(1, "read"); + } + + close(fd); + buf[st.st_size] = '\0'; + + i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0); + } + + if (i == -1 || mm_errno != MM_ERROR_NONE) { + printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno()); + exit(1); + } + + /* Get the number of MIME parts */ + parts = mm_context_countparts(ctx); + if (parts == 0) { + printf("ERROR: got zero MIME parts, huh\n"); + exit(1); + } else { + if (mm_context_iscomposite(ctx)) { + printf("Got %d MIME parts\n", parts - 1); + } else { + printf("Flat message (not multipart)\n"); + } + } + + /* Get the main MIME part */ + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + fprintf(stderr, "Could not get envelope part\n"); + exit(1); + } + + printf("Printing envelope headers:\n"); + /* Print all headers */ + lastheader = NULL; + while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) + printf("%s: %s\n", header->name, header->value); + + printf("%s\n", mm_content_tostring(part->type)); + printf("\n"); + + ct = part->type; + assert(ct != NULL); + + if (mm_context_iscomposite(ctx) == 0) { + printf("Printing body part for FLAT message:\n"); + part = mm_context_getpart(ctx, 0); + printf("%s", part->body); + } + + /* Loop through all MIME parts beginning with 1 */ + for (i = 1; i < mm_context_countparts(ctx); i++) { + char *decoded; + + printf("Printing headers for MIME part %d\n", i); + + /* Get the current MIME entity */ + part = mm_context_getpart(ctx, i); + if (part == NULL) { + fprintf(stderr, "Should have %d parts but " + "couldn't retrieve part %d", + mm_context_countparts(ctx), i); + exit(1); + } + + /* Print all headers */ + lastheader = NULL; + while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) + printf("%s: %s\n", header->name, header->value); + + printf("%s\n", mm_content_tostring(part->type)); + + /* Print MIME part body */ + printf("\nPRINTING MESSAGE BODY (%d):\n%s\n", i, part->opaque_body); + decoded = mm_mimepart_decode(part); + if (decoded != NULL) { + printf("DECODED:\n%s\n", decoded); + free(decoded); + } + } + + printf("RECONSTRUCTED MESSAGE:\n"); + + do { + char *env; + size_t env_len; + + mm_context_flatten(ctx, &env, &env_len, 0); + printf("%s", env); + free(env); + + } while (0); + + mm_context_free(ctx); + ctx = NULL; + +#ifdef __HAVE_LEAK_DETECTION + MM_leakd_printallocated(); +#endif + + } while (0); + + return 0; +} |