Merged revisions 60603 via svnmerge from
authorRussell Bryant <russell@russellbryant.com>
Fri, 6 Apr 2007 21:16:38 +0000 (21:16 +0000)
committerRussell Bryant <russell@russellbryant.com>
Fri, 6 Apr 2007 21:16:38 +0000 (21:16 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r60603 | russell | 2007-04-06 15:58:43 -0500 (Fri, 06 Apr 2007) | 13 lines

To be able to achieve the things that we would like to achieve with the
Asterisk GUI project, we need a fully functional HTTP interface with access
to the Asterisk manager interface.  One of the things that was intended to be
a part of this system, but was never actually implemented, was the ability for
the GUI to be able to upload files to Asterisk.  So, this commit adds this in
the most minimally invasive way that we could come up with.

A lot of work on minimime was done by Steve Murphy.  He fixed a lot of bugs in
the parser, and updated it to be thread-safe.  The ability to check
permissions of active manager sessions was added by Dwayne Hubbard.  Then,
hacking this all together and do doing the modifications necessary to the HTTP
interface was done by me.

........

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@60604 65c4cc65-6c06-0410-ace0-fbb531ad65f3

139 files changed:
Makefile.moddir_rules
configs/http.conf.sample
configure
configure.ac
include/asterisk/autoconfig.h.in
include/asterisk/compat.h
include/asterisk/manager.h
main/Makefile
main/http.c
main/manager.c
main/minimime/.cvsignore [new file with mode: 0644]
main/minimime/Doxyfile [new file with mode: 0644]
main/minimime/Make.conf [new file with mode: 0644]
main/minimime/Makefile [new file with mode: 0644]
main/minimime/mimeparser.h [new file with mode: 0644]
main/minimime/mimeparser.l [new file with mode: 0644]
main/minimime/mimeparser.tab.c [new file with mode: 0644]
main/minimime/mimeparser.tab.h [new file with mode: 0644]
main/minimime/mimeparser.y [new file with mode: 0644]
main/minimime/mimeparser.yy.c [new file with mode: 0644]
main/minimime/minimime.c [new file with mode: 0644]
main/minimime/mm-docs/html/bug.html [new file with mode: 0644]
main/minimime/mm-docs/html/doxygen.css [new file with mode: 0644]
main/minimime/mm-docs/html/files.html [new file with mode: 0644]
main/minimime/mm-docs/html/globals.html [new file with mode: 0644]
main/minimime/mm-docs/html/globals_func.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__codecs.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__contenttype.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__context.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__envelope.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__error.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__mimepart.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__mimeutil.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__param.html [new file with mode: 0644]
main/minimime/mm-docs/html/group__util.html [new file with mode: 0644]
main/minimime/mm-docs/html/index.html [new file with mode: 0644]
main/minimime/mm-docs/html/mimeparser_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mimeparser_8tab_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__codecs_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__contenttype_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__context_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__envelope_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__error_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__header_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__internal_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__internal_8h.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__mem_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__mimepart_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__mimeutil_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__param_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__parse_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__queue_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__util_8c.html [new file with mode: 0644]
main/minimime/mm-docs/html/mm__util_8h-source.html [new file with mode: 0644]
main/minimime/mm-docs/html/modules.html [new file with mode: 0644]
main/minimime/mm-docs/html/pages.html [new file with mode: 0644]
main/minimime/mm-docs/html/tabs.css [new file with mode: 0644]
main/minimime/mm-docs/latex/Makefile [new file with mode: 0644]
main/minimime/mm-docs/latex/bug.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/doxygen.sty [new file with mode: 0644]
main/minimime/mm-docs/latex/files.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__codecs.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__contenttype.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__context.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__envelope.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__error.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__mimepart.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__mimeutil.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__param.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/group__util.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__codecs_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__contenttype_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__context_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__envelope_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__error_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__header_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__internal_8h.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__mimepart_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__mimeutil_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__param_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__parse_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/mm__util_8c.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/modules.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/pages.tex [new file with mode: 0644]
main/minimime/mm-docs/latex/refman.tex [new file with mode: 0644]
main/minimime/mm-docs/refman.pdf [new file with mode: 0644]
main/minimime/mm.h [new file with mode: 0644]
main/minimime/mm_base64.c [new file with mode: 0644]
main/minimime/mm_codecs.c [new file with mode: 0644]
main/minimime/mm_contenttype.c [new file with mode: 0644]
main/minimime/mm_context.c [new file with mode: 0644]
main/minimime/mm_envelope.c [new file with mode: 0644]
main/minimime/mm_error.c [new file with mode: 0644]
main/minimime/mm_header.c [new file with mode: 0644]
main/minimime/mm_init.c [new file with mode: 0644]
main/minimime/mm_internal.h [new file with mode: 0644]
main/minimime/mm_mem.c [new file with mode: 0644]
main/minimime/mm_mem.h [new file with mode: 0644]
main/minimime/mm_mimepart.c [new file with mode: 0644]
main/minimime/mm_mimeutil.c [new file with mode: 0644]
main/minimime/mm_param.c [new file with mode: 0644]
main/minimime/mm_parse.c [new file with mode: 0644]
main/minimime/mm_queue.h [new file with mode: 0644]
main/minimime/mm_util.c [new file with mode: 0644]
main/minimime/mm_util.h [new file with mode: 0644]
main/minimime/mm_warnings.c [new file with mode: 0644]
main/minimime/mytest_files/ast_postdata [new file with mode: 0644]
main/minimime/mytest_files/ast_postdata2 [new file with mode: 0644]
main/minimime/mytest_files/ast_postdata3.gz [new file with mode: 0644]
main/minimime/mytest_files/mytest.c [new file with mode: 0644]
main/minimime/strlcat.c [new file with mode: 0644]
main/minimime/strlcpy.c [new file with mode: 0644]
main/minimime/sys/CVS/Entries [new file with mode: 0644]
main/minimime/sys/CVS/Repository [new file with mode: 0644]
main/minimime/sys/CVS/Root [new file with mode: 0644]
main/minimime/sys/mm_queue.h [new file with mode: 0644]
main/minimime/test.sh [new file with mode: 0755]
main/minimime/test/CVS/Entries [new file with mode: 0644]
main/minimime/test/CVS/Repository [new file with mode: 0644]
main/minimime/test/CVS/Root [new file with mode: 0644]
main/minimime/tests/CVS/Entries [new file with mode: 0644]
main/minimime/tests/CVS/Entries.Log [new file with mode: 0644]
main/minimime/tests/CVS/Repository [new file with mode: 0644]
main/minimime/tests/CVS/Root [new file with mode: 0644]
main/minimime/tests/Makefile [new file with mode: 0644]
main/minimime/tests/create.c [new file with mode: 0644]
main/minimime/tests/messages/CVS/Entries [new file with mode: 0644]
main/minimime/tests/messages/CVS/Repository [new file with mode: 0644]
main/minimime/tests/messages/CVS/Root [new file with mode: 0644]
main/minimime/tests/messages/test1.txt [new file with mode: 0644]
main/minimime/tests/messages/test2.txt [new file with mode: 0644]
main/minimime/tests/messages/test3.txt [new file with mode: 0644]
main/minimime/tests/messages/test4.txt [new file with mode: 0644]
main/minimime/tests/messages/test5.txt [new file with mode: 0644]
main/minimime/tests/messages/test6.txt [new file with mode: 0644]
main/minimime/tests/messages/test7.txt [new file with mode: 0644]
main/minimime/tests/parse.c [new file with mode: 0644]
main/strcompat.c

index d2dafe3..1c78ebd 100644 (file)
@@ -12,7 +12,9 @@
 #
 
 ifneq ($(findstring MALLOC_DEBUG,$(MENUSELECT_CFLAGS)),)
+ ifeq ($(findstring astmm.h,$(ASTCFLAGS)),)
   ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/astmm.h
+ endif
 endif
 
 ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
index 19f96ca..ebd09e7 100644 (file)
@@ -48,3 +48,16 @@ bindaddr=127.0.0.1
 ; To produce a certificate you can e.g. use openssl
 ;      openssl req -new -x509 -days 365 -nodes -out /tmp/foo.pem -keyout /tmp/foo.pem
 ;
+;prefix=asterisk
+
+; The post_mappings section maps URLs to real paths on the filesystem.  If a
+; POST is done from within an authenticated manager session to one of the
+; configured POST mappings, then any files in the POST will be placed in the
+; configured directory.
+;
+;[post_mappings]
+;
+; In this example, if the prefix option is set to "asterisk", then using the
+; POST URL: /asterisk/uploads will put files in /var/lib/asterisk/uploads/.
+;uploads = /var/lib/asterisk/uploads/
+;
index 00dcbd3..108c6e4 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 58948 .
+# From configure.ac Revision: 59203 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.60.
 #
@@ -16007,7 +16007,9 @@ done
 
 
 
-for ac_func in asprintf atexit bzero dup2 endpwent floor ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap pow putenv re_comp regcomp rint select setenv socket sqrt strcasecmp strcasestr strchr strcspn strdup strerror strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf
+
+
+for ac_func in asprintf atexit bzero dup2 endpwent floor ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap pow putenv re_comp regcomp rint select setenv socket sqrt strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf
 do
 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
 { echo "$as_me:$LINENO: checking for $ac_func" >&5
index ddc70ed..fe23ade 100644 (file)
@@ -275,7 +275,7 @@ AC_FUNC_STRNLEN
 AC_FUNC_STRTOD
 AC_FUNC_UTIME_NULL
 AC_FUNC_VPRINTF
-AC_CHECK_FUNCS([asprintf atexit bzero dup2 endpwent floor ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap pow putenv re_comp regcomp rint select setenv socket sqrt strcasecmp strcasestr strchr strcspn strdup strerror strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf])
+AC_CHECK_FUNCS([asprintf atexit bzero dup2 endpwent floor ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap pow putenv re_comp regcomp rint select setenv socket sqrt strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf])
 
 # https support (in main/http.c) uses funopen on BSD systems,
 # fopencookie on linux
index e450ba9..ef6a76a 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
 /* Define to 1 if you have the `strncasecmp' function. */
 #undef HAVE_STRNCASECMP
 
index e02d3c7..838e24e 100644 (file)
@@ -61,6 +61,14 @@ int unsetenv(const char *name);
 int vasprintf(char **strp, const char *fmt, va_list ap);
 #endif
 
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
 #ifdef SOLARIS
 #define __BEGIN_DECLS
 #define __END_DECLS
index 1cf03b5..8efcfba 100644 (file)
@@ -134,6 +134,22 @@ int ast_manager_register2(
 */
 int ast_manager_unregister( char *action );
 
+/*! 
+ * \brief Verify a session's read permissions against a permission mask.  
+ * \param ident session identity
+ * \param perm permission mask to verify
+ * \returns 1 if the session has the permission mask capabilities, otherwise 0
+ */
+int astman_verify_session_readpermissions(unsigned long ident, int perm);
+
+/*!
+ * \brief Verify a session's write permissions against a permission mask.  
+ * \param ident session identity
+ * \param perm permission mask to verify
+ * \returns 1 if the session has the permission mask capabilities, otherwise 0
+ */
+int astman_verify_session_writepermissions(unsigned long ident, int perm);
+
 /*! External routines may send asterisk manager events this way */
 /*!    \param category Event category, matches manager authorization
        \param event    Event name
index 9e0f6f2..f552fab 100644 (file)
@@ -133,7 +133,10 @@ else
   H323LDLIBS=
 endif
 
-asterisk: $(OBJS) editline/libedit.a db1-ast/libdb1.a $(AST_EMBED_LDSCRIPTS)
+minimime/libmmime.a:
+       @cd minimime && $(MAKE) libmmime.a
+
+asterisk: $(OBJS) editline/libedit.a db1-ast/libdb1.a minimime/libmmime.a $(AST_EMBED_LDSCRIPTS)
        @$(CC) -c -o buildinfo.o $(ASTCFLAGS) buildinfo.c
        $(ECHO_PREFIX) echo "   [LD] $^ -> $@"
        $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS)
@@ -145,3 +148,6 @@ clean::
        @if [ -f editline/Makefile ]; then $(MAKE) -C editline distclean ; fi
        @$(MAKE) -C db1-ast clean
        @$(MAKE) -C stdtime clean
+       @$(MAKE) -C minimime clean
+
+.PHONY: minimime/libmmime.a
index 4fbe9bd..0dee18f 100644 (file)
@@ -21,8 +21,9 @@
  * \brief http server for AMI access
  *
  * \author Mark Spencer <markster@digium.com>
- * This program implements a tiny http server supporting the "get" method
- * only and was inspired by micro-httpd by Jef Poskanzer 
+ *
+ * This program implements a tiny http server
+ * and was inspired by micro-httpd by Jef Poskanzer 
  * 
  * \ref AstHTTP - AMI over the http protocol
  */
@@ -47,6 +48,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <fcntl.h>
 #include <pthread.h>
 
+#include "minimime/mm.h"
+
 #include "asterisk/cli.h"
 #include "asterisk/http.h"
 #include "asterisk/utils.h"
@@ -55,6 +58,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/config.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/version.h"
+#include "asterisk/manager.h"
 
 #define MAX_PREFIX 80
 #define DEFAULT_PREFIX "/asterisk"
@@ -93,6 +97,14 @@ static struct server_args https_desc = {
 
 static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri);     /*!< list of supported handlers */
 
+struct ast_http_post_mapping {
+       AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
+       char *from;
+       char *to;
+};
+
+static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
+
 /* all valid URIs must be prepended by the string in prefix. */
 static char prefix[MAX_PREFIX];
 static int enablestatic;
@@ -329,6 +341,225 @@ void ast_http_uri_unlink(struct ast_http_uri *urih)
        AST_RWLIST_UNLOCK(&uris);
 }
 
+/*! \note This assumes that the post_mappings list is locked */
+static struct ast_http_post_mapping *find_post_mapping(const char *uri)
+{
+       struct ast_http_post_mapping *post_map;
+
+       if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
+               ast_log(LOG_DEBUG, "URI %s does not have prefix %s\n", uri, prefix);
+               return NULL;
+       }
+
+       uri += strlen(prefix);
+       if (*uri == '/')
+               uri++;
+       
+       AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
+               if (!strcmp(uri, post_map->from))
+                       return post_map;
+       }
+
+       return NULL;
+}
+
+static int get_filename(struct mm_mimepart *part, char *fn, size_t fn_len)
+{
+       const char *filename;
+
+       filename = mm_content_getdispositionparambyname(part->type, "filename");
+
+       if (ast_strlen_zero(filename))
+               return -1;
+
+       ast_copy_string(fn, filename, fn_len);
+
+       return 0;
+}
+
+static void post_raw(struct mm_mimepart *part, const char *post_dir, const char *fn)
+{
+       char filename[PATH_MAX];
+       FILE *f;
+       const char *body;
+       size_t body_len;
+
+       snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
+
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Posting raw data to %s\n", filename);
+
+       if (!(f = fopen(filename, "w"))) {
+               ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
+               return;
+       }
+
+       if (!(body = mm_mimepart_getbody(part, 0))) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Couldn't get the mimepart body\n");
+               fclose(f);
+               return;
+       }
+       body_len = mm_mimepart_getlength(part);
+
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Body length is %ld\n", body_len);
+
+       fwrite(body, 1, body_len, f);
+
+       fclose(f);
+}
+
+static struct ast_str *handle_post(struct server_instance *ser, char *uri, 
+       int *status, char **title, int *contentlength, struct ast_variable *headers,
+       struct ast_variable *cookies)
+{
+       char buf;
+       FILE *f;
+       size_t res;
+       struct ast_variable *var;
+       int content_len = 0;
+       MM_CTX *ctx;
+       int mm_res, i;
+       struct ast_http_post_mapping *post_map;
+       const char *post_dir;
+       unsigned long ident = 0;
+
+       for (var = cookies; var; var = var->next) {
+               if (strcasecmp(var->name, "mansession_id"))
+                       continue;
+
+               if (sscanf(var->value, "%lx", &ident) != 1) {
+                       *status = 400;
+                       *title = ast_strdup("Bad Request");
+                       return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+               }
+
+               if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
+                       *status = 401;
+                       *title = ast_strdup("Unauthorized");
+                       return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+               }
+
+               break;
+       }
+       if (!var) {
+               *status = 401;
+               *title = ast_strdup("Unauthorized");
+               return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+       }
+
+       if (!(f = tmpfile()))
+               return NULL;
+
+       for (var = headers; var; var = var->next) {
+               if (!strcasecmp(var->name, "Content-Length")) {
+                       if ((sscanf(var->value, "%u", &content_len)) != 1) {
+                               ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
+                               fclose(f);
+                               return NULL;
+                       }
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Got a Content-Length of %d\n", content_len);
+               } else if (!strcasecmp(var->name, "Content-Type"))
+                       fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
+       }
+
+       while ((res = fread(&buf, 1, 1, ser->f))) {
+               fwrite(&buf, 1, 1, f);
+               content_len--;
+               if (!content_len)
+                       break;
+       }
+
+       if (fseek(f, SEEK_SET, 0)) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Failed to seek temp file back to beginning.\n");
+               fclose(f);
+               return NULL;
+       }
+
+       AST_RWLIST_RDLOCK(&post_mappings);
+       if (!(post_map = find_post_mapping(uri))) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "%s is not a valid URI for POST\n", uri);
+               AST_RWLIST_UNLOCK(&post_mappings);
+               fclose(f);
+               *status = 404;
+               *title = ast_strdup("Not Found");
+               return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+       }
+       post_dir = ast_strdupa(post_map->to);
+       post_map = NULL;
+       AST_RWLIST_UNLOCK(&post_mappings);
+
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Going to post files to dir %s\n", post_dir);
+
+       if (!(ctx = mm_context_new())) {
+               fclose(f);
+               return NULL;
+       }
+
+       mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
+       fclose(f);
+       if (mm_res == -1) {
+               ast_log(LOG_ERROR, "Error parsing MIME data\n");
+               mm_context_free(ctx);
+               *status = 400;
+               *title = ast_strdup("Bad Request");
+               return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+       }
+
+       mm_res = mm_context_countparts(ctx);
+       if (!mm_res) {
+               ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
+               mm_context_free(ctx);
+               *status = 400;
+               *title = ast_strdup("Bad Request");
+               return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+       }
+
+       if (option_debug) {
+               if (mm_context_iscomposite(ctx))
+                       ast_log(LOG_DEBUG, "Found %d MIME parts\n", mm_res - 1);
+               else
+                       ast_log(LOG_DEBUG, "We have a flat (not multi-part) message\n");
+       }
+
+       for (i = 1; i < mm_res; i++) {
+               struct mm_mimepart *part;
+               char fn[PATH_MAX];
+
+               if (!(part = mm_context_getpart(ctx, i))) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Failed to get mime part num %d\n", i);
+                       continue;
+               }
+
+               if (get_filename(part, fn, sizeof(fn))) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Failed to retrieve a filename for part num %d\n", i);
+                       continue;
+               }
+       
+               if (!part->type) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "This part has no content struct?\n");
+                       continue;
+               }
+
+               /* XXX This assumes the MIME part body is not encoded! */
+               post_raw(part, post_dir, fn);
+       }
+
+       mm_context_free(ctx);
+
+       *status = 200;
+       *title = ast_strdup("OK");
+       return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
+}
+
 static struct ast_str *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
 {
        char *c;
@@ -520,7 +751,7 @@ static void *httpd_helper_thread(void *data)
        char buf[4096];
        char cookie[4096];
        struct server_instance *ser = data;
-       struct ast_variable *var, *prev=NULL, *vars=NULL;
+       struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
        char *uri, *title=NULL;
        int status = 200, contentlength = 0;
        struct ast_str *out = NULL;
@@ -549,8 +780,23 @@ static void *httpd_helper_thread(void *data)
                ast_trim_blanks(cookie);
                if (ast_strlen_zero(cookie))
                        break;
-               if (strncasecmp(cookie, "Cookie: ", 8))
+               if (strncasecmp(cookie, "Cookie: ", 8)) {
+                       char *name, *value;
+
+                       value = ast_strdupa(cookie);
+                       name = strsep(&value, ":");
+                       if (!value)
+                               continue;
+                       value = ast_skip_blanks(value);
+                       if (ast_strlen_zero(value))
+                               continue;
+                       var = ast_variable_new(name, value);
+                       if (!var)
+                               continue;
+                       var->next = headers;
+                       headers = var;
                        continue;
+               }
 
                /* TODO - The cookie parsing code below seems to work   
                   in IE6 and FireFox 1.5.  However, it is not entirely 
@@ -596,6 +842,8 @@ static void *httpd_helper_thread(void *data)
 
        if (!*uri)
                out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
+       else if (!strcasecmp(buf, "post")) 
+               out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
        else if (strcasecmp(buf, "get")) 
                out = ast_http_error(501, "Not Implemented", NULL,
                        "Attempt to use unimplemented / unsupported method");
@@ -851,6 +1099,47 @@ static void add_redirect(const char *value)
        AST_RWLIST_UNLOCK(&uri_redirects);
 }
 
+static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
+{
+       if (post_map->from)
+               free(post_map->from);
+       if (post_map->to)
+               free(post_map->to);
+       free(post_map);
+}
+
+static void destroy_post_mappings(void)
+{
+       struct ast_http_post_mapping *post_map;
+
+       AST_RWLIST_WRLOCK(&post_mappings);
+       while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
+               destroy_post_mapping(post_map);
+       AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static void add_post_mapping(const char *from, const char *to)
+{
+       struct ast_http_post_mapping *post_map;
+
+       if (!(post_map = ast_calloc(1, sizeof(*post_map))))
+               return;
+
+       if (!(post_map->from = ast_strdup(from))) {
+               destroy_post_mapping(post_map);
+               return;
+       }
+
+       if (!(post_map->to = ast_strdup(to))) {
+               destroy_post_mapping(post_map);
+               return;
+       }
+
+       AST_RWLIST_WRLOCK(&post_mappings);
+       AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
+       AST_RWLIST_UNLOCK(&post_mappings);
+}
+
 static int __ast_http_load(int reload)
 {
        struct ast_config *cfg;
@@ -869,6 +1158,7 @@ static int __ast_http_load(int reload)
 
        memset(&https_desc.sin, 0, sizeof(https_desc.sin));
        https_desc.sin.sin_port = htons(8089);
+
        strcpy(newprefix, DEFAULT_PREFIX);
 
        http_tls_cfg.enabled = 0;
@@ -884,6 +1174,8 @@ static int __ast_http_load(int reload)
                free(redirect);
        AST_RWLIST_UNLOCK(&uri_redirects);
 
+       destroy_post_mappings();
+
        cfg = ast_config_load("http.conf");
        if (cfg) {
                v = ast_variable_browse(cfg, "general");
@@ -931,6 +1223,10 @@ static int __ast_http_load(int reload)
                                ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
                        }
                }
+
+               for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
+                       add_post_mapping(v->name, v->value);
+
                ast_config_destroy(cfg);
        }
        if (!have_sslbindaddr)
@@ -943,6 +1239,7 @@ static int __ast_http_load(int reload)
        server_start(&http_desc);
        if (ssl_setup(https_desc.tls_cfg))
                server_start(&https_desc);
+
        return 0;
 }
 
@@ -950,6 +1247,7 @@ static int handle_show_http(int fd, int argc, char *argv[])
 {
        struct ast_http_uri *urih;
        struct http_uri_redirect *redirect;
+       struct ast_http_post_mapping *post_map;
 
        if (argc != 3)
                return RESULT_SHOWUSAGE;
@@ -986,6 +1284,14 @@ static int handle_show_http(int fd, int argc, char *argv[])
                ast_cli(fd, "  None.\n");
        AST_RWLIST_UNLOCK(&uri_redirects);
 
+
+       ast_cli(fd, "\nPOST mappings:\n");
+       AST_RWLIST_RDLOCK(&post_mappings);
+       AST_LIST_TRAVERSE(&post_mappings, post_map, entry)
+               ast_cli(fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
+       ast_cli(fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
+       AST_RWLIST_UNLOCK(&post_mappings);
+
        return RESULT_SUCCESS;
 }
 
@@ -1006,8 +1312,12 @@ static struct ast_cli_entry cli_http[] = {
 
 int ast_http_init(void)
 {
+       mm_library_init();
+       mm_codec_registerdefaultcodecs();
+
        ast_http_uri_link(&statusuri);
        ast_http_uri_link(&staticuri);
        ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
+
        return __ast_http_load(0);
 }
index 116b77a..891df4c 100644 (file)
@@ -2592,6 +2592,44 @@ static struct mansession *find_session(unsigned long ident)
        return s;
 }
 
+int astman_verify_session_readpermissions(unsigned long ident, int perm)
+{
+       int result = 0;
+       struct mansession *s;
+
+       AST_LIST_LOCK(&sessions);
+       AST_LIST_TRAVERSE(&sessions, s, list) {
+               ast_mutex_lock(&s->__lock);
+               if ((s->managerid == ident) && (s->readperm & perm)) {
+                       result = 1;
+                       ast_mutex_unlock(&s->__lock);
+                       break;
+               }
+               ast_mutex_unlock(&s->__lock);
+       }
+       AST_LIST_UNLOCK(&sessions);
+       return result;
+}
+
+int astman_verify_session_writepermissions(unsigned long ident, int perm)
+{
+       int result = 0;
+       struct mansession *s;
+
+       AST_LIST_LOCK(&sessions);
+       AST_LIST_TRAVERSE(&sessions, s, list) {
+               ast_mutex_lock(&s->__lock);
+               if ((s->managerid == ident) && (s->writeperm & perm)) {
+                       result = 1;
+                       ast_mutex_unlock(&s->__lock);
+                       break;
+               }
+               ast_mutex_unlock(&s->__lock);
+       }
+       AST_LIST_UNLOCK(&sessions);
+       return result;
+}
+
 /*
  * convert to xml with various conversion:
  * mode & 1    -> lowercase;
diff --git a/main/minimime/.cvsignore b/main/minimime/.cvsignore
new file mode 100644 (file)
index 0000000..f5e1bf8
--- /dev/null
@@ -0,0 +1,4 @@
+minimime
+*.so.*
+*.o
+*.swp
diff --git a/main/minimime/Doxyfile b/main/minimime/Doxyfile
new file mode 100644 (file)
index 0000000..5ad5e11
--- /dev/null
@@ -0,0 +1,1098 @@
+# Doxyfile 1.3.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = MiniMIME
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = mm-docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is used 
+# as the annotated text. Otherwise, the brief description is used as-is. If left 
+# blank, the following values are used ("$name" is automatically replaced with the 
+# name of the entity): "The $name class" "The $name widget" "The $name file" 
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = 
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/main/minimime/Make.conf b/main/minimime/Make.conf
new file mode 100644 (file)
index 0000000..de14993
--- /dev/null
@@ -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/main/minimime/Makefile b/main/minimime/Makefile
new file mode 100644 (file)
index 0000000..4351afd
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# 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
+
+ASTCFLAGS:=$(filter-out -Werror -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes,$(ASTCFLAGS))
+
+all: $(LIBMMIME)
+
+$(LIBMMIME): $(MM_OBJS)
+       $(ECHO_PREFIX) echo "   [AR] $^ -> $@"
+       $(CMD_PREFIX) $(AR) cr $@ $^
+       $(CMD_PREFIX) $(RANLIB) $@
+
+#mimeparser.yy.c: mimeparser.l
+#      flex -Pmimeparser_yy -omimeparser.yy.c mimeparser.l
+
+#mimeparser.tab.c: mimeparser.y
+#      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/main/minimime/mimeparser.h b/main/minimime/mimeparser.h
new file mode 100644 (file)
index 0000000..3d8da81
--- /dev/null
@@ -0,0 +1,67 @@
+#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;
+       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;
+};
+
+
+#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 *);
+
+
+#endif /* ! _MIMEPARSER_H_INCLUDED */
diff --git a/main/minimime/mimeparser.l b/main/minimime/mimeparser.l
new file mode 100644 (file)
index 0000000..d45295f
--- /dev/null
@@ -0,0 +1,483 @@
+%{
+/*
+ * 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\-\_]* {
+       yylval_param->string=strdup(yytext); 
+       struct lexer_state *lstate = yyget_extra(yyscanner);
+       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(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 yyscanner)
+{
+       /* looks like a bug in bison 2.2a -- the wrong code is generated for yyset_in !! */
+       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+       yyg->yyin_r = fp;
+       
+       if (0) {
+               /* This is just to make a compiler warning go away */
+               yyunput(0, NULL, yyscanner);
+       }
+}
+
+/**
+ * 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/main/minimime/mimeparser.tab.c b/main/minimime/mimeparser.tab.c
new file mode 100644 (file)
index 0000000..509fa4d
--- /dev/null
@@ -0,0 +1,2341 @@
+/* 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);
+
+typedef void *yyscan_t;
+
+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 69 "mimeparser.y"
+{
+       int number;
+       char *string;
+       struct s_position position;
+}
+/* Line 193 of yacc.c.  */
+#line 222 "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 235 "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 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,   114,   114,   116,   121,   120,   133,   141,   143,   167,
+     171,   186,   190,   193,   197,   199,   203,   218,   220,   230,
+     232,   234,   236,   250,   257,   276,   284,   294,   300,   308,
+     331,   338,   345,   349,   351,   355,   364,   366,   368,   382,
+     384,   386,   400,   431,   445,   451,   466,   474,   481,   500,
+     519
+};
+#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 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
+
+\f
+
+#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 */
+\f
+
+/*-----------------------------------------------.
+| 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;
+    }
+}
+\f
+
+/* 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 121 "mimeparser.y"
+    { 
+               mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+               pstate->current_mimepart = mm_mimepart_new();
+               pstate->have_contenttype = 0;
+       }
+    break;
+
+  case 5:
+#line 127 "mimeparser.y"
+    {
+               dprintf2(pstate,"This was a multipart message\n");
+       }
+    break;
+
+  case 6:
+#line 134 "mimeparser.y"
+    {
+               dprintf2(pstate,"This was a single part message\n");
+               mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+       }
+    break;
+
+  case 8:
+#line 144 "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 172 "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 191 "mimeparser.y"
+    {
+       }
+    break;
+
+  case 16:
+#line 204 "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 221 "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 237 "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 251 "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 258 "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 277 "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 285 "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 295 "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 301 "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 309 "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 332 "mimeparser.y"
+    {
+               dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", (yyvsp[(3) - (4)].string));
+       }
+    break;
+
+  case 31:
+#line 339 "mimeparser.y"
+    {
+               dprintf2(pstate,"MIME-Version -> '%s'\n", (yyvsp[(3) - (4)].string));
+       }
+    break;
+
+  case 35:
+#line 356 "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 369 "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 387 "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 401 "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 432 "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 446 "mimeparser.y"
+    {
+               dprintf2(pstate,"contenttype_param_val: WORD=%s\n", (yyvsp[(1) - (1)].string));
+               (yyval.string) = (yyvsp[(1) - (1)].string);
+       }
+    break;
+
+  case 45:
+#line 452 "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 467 "mimeparser.y"
+    {
+               dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" );
+               (yyval.string) = (yyvsp[(2) - (3)].string);
+       }
+    break;
+
+  case 47:
+#line 475 "mimeparser.y"
+    {
+               dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno);
+       }
+    break;
+
+  case 48:
+#line 482 "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 501 "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 520 "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 1915 "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 539 "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/main/minimime/mimeparser.tab.h b/main/minimime/mimeparser.tab.h
new file mode 100644 (file)
index 0000000..6cf3f22
--- /dev/null
@@ -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 69 "mimeparser.y"
+{
+       int number;
+       char *string;
+       struct s_position position;
+}
+/* Line 1529 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/main/minimime/mimeparser.y b/main/minimime/mimeparser.y
new file mode 100644 (file)
index 0000000..18f409f
--- /dev/null
@@ -0,0 +1,750 @@
+%{
+/*
+ * 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);
+
+typedef void *yyscan_t;
+
+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/main/minimime/mimeparser.yy.c b/main/minimime/mimeparser.yy.c
new file mode 100644 (file)
index 0000000..40d6bd9
--- /dev/null
@@ -0,0 +1,2622 @@
+#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 );
+
+void mimeparser_yyset_lineno (int line_number ,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"
+{
+       yylval_param->string=strdup(yytext); 
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       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 123 "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 130 "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 152 "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 159 "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 167 "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 172 "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 189 "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 198 "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 205 "mimeparser.l"
+{
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       lstate->current_pos += yyleng;
+       return EQUAL;
+}
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 211 "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 218 "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 226 "mimeparser.l"
+{
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       lstate->current_pos += yyleng;
+}      
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 231 "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 239 "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 246 "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 281 "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 323 "mimeparser.l"
+{
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       lstate->current_pos += yyleng;
+       lstate->lineno++;
+}
+       YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 329 "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 335 "mimeparser.l"
+{
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       lstate->current_pos += yyleng;
+}
+       YY_BREAK
+case YY_STATE_EOF(body):
+#line 340 "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 354 "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 361 "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 368 "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 375 "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 384 "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 391 "mimeparser.l"
+{
+       struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+       lstate->current_pos += yyleng;
+}
+       YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 397 "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 402 "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 409 "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 416 "mimeparser.l"
+ECHO;
+       YY_BREAK
+#line 1437 "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, 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 416 "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(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 yyscanner)
+{
+       /* looks like a bug in bison 2.2a -- the wrong code is generated for mimeparser_yyset_in !! */
+       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+       yyg->yyin_r = fp;
+       
+       if (0) {
+               /* This is just to make a compiler warning go away */
+               yyunput(0, NULL, yyscanner);
+       }
+}
+
+/**
+ * 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/main/minimime/minimime.c b/main/minimime/minimime.c
new file mode 100644 (file)
index 0000000..f96b62f
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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.