home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmime / mimeunty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  13.4 KB  |  490 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* mimeunty.h --- definition of the MimeUntypedText class (see mimei.h)
  20.    Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
  21.  */
  22.  
  23. #include "mimeunty.h"
  24.  
  25. #define MIME_SUPERCLASS mimeContainerClass
  26. MimeDefClass(MimeUntypedText, MimeUntypedTextClass,
  27.              mimeUntypedTextClass, &MIME_SUPERCLASS);
  28.  
  29. static int MimeUntypedText_initialize (MimeObject *);
  30. static void MimeUntypedText_finalize (MimeObject *);
  31. static int MimeUntypedText_parse_begin (MimeObject *);
  32. static int MimeUntypedText_parse_line (char *, int32, MimeObject *);
  33.  
  34. static int MimeUntypedText_open_subpart (MimeObject *obj,
  35.                                          MimeUntypedTextSubpartType ttype,
  36.                                          const char *type,
  37.                                          const char *enc,
  38.                                          const char *name,
  39.                                          const char *desc);
  40. static int MimeUntypedText_close_subpart (MimeObject *obj);
  41.  
  42. static XP_Bool MimeUntypedText_uu_begin_line_p(const char *line, int32 length,
  43.                                                MimeDisplayOptions *opt,
  44.                                                char **type_ret,
  45.                                                char **name_ret);
  46. static XP_Bool MimeUntypedText_uu_end_line_p(const char *line, int32 length);
  47.  
  48. static XP_Bool MimeUntypedText_binhex_begin_line_p(const char *line,
  49.                                                    int32 length,
  50.                                                    MimeDisplayOptions *opt);
  51. static XP_Bool MimeUntypedText_binhex_end_line_p(const char *line,
  52.                                                  int32 length);
  53.  
  54. static int
  55. MimeUntypedTextClassInitialize(MimeUntypedTextClass *class)
  56. {
  57.   MimeObjectClass *oclass = (MimeObjectClass *) class;
  58.   XP_ASSERT(!oclass->class_initialized);
  59.   oclass->initialize  = MimeUntypedText_initialize;
  60.   oclass->finalize    = MimeUntypedText_finalize;
  61.   oclass->parse_begin = MimeUntypedText_parse_begin;
  62.   oclass->parse_line  = MimeUntypedText_parse_line;
  63.   return 0;
  64. }
  65.  
  66.  
  67. static int
  68. MimeUntypedText_initialize (MimeObject *object)
  69. {
  70.   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
  71. }
  72.  
  73. static void
  74. MimeUntypedText_finalize (MimeObject *object)
  75. {
  76.   MimeUntypedText *uty = (MimeUntypedText *) object;
  77.  
  78.   if (uty->open_hdrs)
  79.     {
  80.       /* Oops, those shouldn't still be here... */
  81.       MimeHeaders_free(uty->open_hdrs);
  82.       uty->open_hdrs = 0;
  83.     }
  84.  
  85.   /* What about the open_subpart?  We're gonna have to assume that it
  86.      is also on the MimeContainer->children list, and will get cleaned
  87.      up by that class. */
  88.  
  89.   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
  90. }
  91.  
  92. static int
  93. MimeUntypedText_parse_begin (MimeObject *obj)
  94. {
  95.   return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
  96. }
  97.  
  98. static int
  99. MimeUntypedText_parse_line (char *line, int32 length, MimeObject *obj)
  100. {
  101.   MimeUntypedText *uty = (MimeUntypedText *) obj;
  102.   int status = 0;
  103.   char *name = 0, *type = 0;
  104.   XP_Bool begin_line_p = FALSE;
  105.  
  106.   XP_ASSERT(line && *line);
  107.   if (!line || !*line) return -1;
  108.  
  109.   /* If we're supposed to write this object, but aren't supposed to convert
  110.      it to HTML, simply pass it through unaltered. */
  111.   if (obj->output_p &&
  112.       obj->options &&
  113.       !obj->options->write_html_p &&
  114.       obj->options->output_fn)
  115.     return MimeObject_write(obj, line, length, TRUE);
  116.  
  117.  
  118.   /* Open a new sub-part if this line demands it.
  119.    */
  120.   if (line[0] == 'b' &&
  121.       MimeUntypedText_uu_begin_line_p(line, length, obj->options,
  122.                                       &type, &name))
  123.     {
  124.       /* Close the old part and open a new one. */
  125.       status = MimeUntypedText_open_subpart (obj,
  126.                                              MimeUntypedTextSubpartTypeUUE,
  127.                                              type, ENCODING_UUENCODE,
  128.                                              name, NULL);
  129.       FREEIF(name);
  130.       FREEIF(type);
  131.       if (status < 0) return status;
  132.       begin_line_p = TRUE;
  133.     }
  134.  
  135.   else if (line[0] == '(' && line[1] == 'T' &&
  136.            MimeUntypedText_binhex_begin_line_p(line, length, obj->options))
  137.     {
  138.       /* Close the old part and open a new one. */
  139.       status = MimeUntypedText_open_subpart (obj,
  140.                                              MimeUntypedTextSubpartTypeBinhex,
  141.                                              APPLICATION_BINHEX, NULL,
  142.                                              NULL, NULL);
  143.       if (status < 0) return status;
  144.       begin_line_p = TRUE;
  145.     }
  146.  
  147.   /* Open a text/plain sub-part if there is no sub-part open.
  148.    */
  149.   if (!uty->open_subpart)
  150.     {
  151.       XP_ASSERT(!begin_line_p);
  152.       status = MimeUntypedText_open_subpart (obj,
  153.                                              MimeUntypedTextSubpartTypeText,
  154.                                              TEXT_PLAIN, NULL, NULL, NULL);
  155.       XP_ASSERT(uty->open_subpart);
  156.       if (!uty->open_subpart) return -1;
  157.       if (status < 0) return status;
  158.     }
  159.  
  160.   /* Hand this line to the currently-open sub-part.
  161.    */
  162.   status = uty->open_subpart->class->parse_buffer(line, length,
  163.                                                   uty->open_subpart);
  164.   if (status < 0) return status;
  165.  
  166.   /* Close this sub-part if this line demands it.
  167.    */
  168.   if (begin_line_p)
  169.     ;
  170.   else if (line[0] == 'e' &&
  171.            uty->type == MimeUntypedTextSubpartTypeUUE &&
  172.            MimeUntypedText_uu_end_line_p(line, length))
  173.     {
  174.       status = MimeUntypedText_close_subpart (obj);
  175.       if (status < 0) return status;
  176.       XP_ASSERT(!uty->open_subpart);
  177.     }
  178.   else if (uty->type == MimeUntypedTextSubpartTypeBinhex &&
  179.            MimeUntypedText_binhex_end_line_p(line, length))
  180.     {
  181.       status = MimeUntypedText_close_subpart (obj);
  182.       if (status < 0) return status;
  183.       XP_ASSERT(!uty->open_subpart);
  184.     }
  185.  
  186.   return 0;
  187. }
  188.  
  189.  
  190. static int
  191. MimeUntypedText_close_subpart (MimeObject *obj)
  192. {
  193.   MimeUntypedText *uty = (MimeUntypedText *) obj;
  194.   int status;
  195.  
  196.   if (uty->open_subpart)
  197.     {
  198.       status = uty->open_subpart->class->parse_eof(uty->open_subpart, FALSE);
  199.       uty->open_subpart = 0;
  200.  
  201.       XP_ASSERT(uty->open_hdrs);
  202.       if (uty->open_hdrs)
  203.         {
  204.           MimeHeaders_free(uty->open_hdrs);
  205.           uty->open_hdrs = 0;
  206.         }
  207.       uty->type = MimeUntypedTextSubpartTypeText;
  208.       if (status < 0) return status;
  209.  
  210.       /* Never put out a separator between sub-parts of UntypedText.
  211.          (This bypasses the rule that text/plain subparts always
  212.          have separators before and after them.)
  213.          */
  214.       if (obj->options && obj->options->state)
  215.         obj->options->state->separator_suppressed_p = TRUE;
  216.     }
  217.  
  218.   XP_ASSERT(!uty->open_hdrs);
  219.   return 0;
  220. }
  221.  
  222. static int
  223. MimeUntypedText_open_subpart (MimeObject *obj,
  224.                               MimeUntypedTextSubpartType ttype,
  225.                               const char *type,
  226.                               const char *enc,
  227.                               const char *name,
  228.                               const char *desc)
  229. {
  230.   MimeUntypedText *uty = (MimeUntypedText *) obj;
  231.   int status = 0;
  232.   char *h = 0;
  233.  
  234.   if (!type || !*type || !strcasecomp(type, UNKNOWN_CONTENT_TYPE))
  235.     type = APPLICATION_OCTET_STREAM;
  236.   if (enc && !*enc)
  237.     enc = 0;
  238.   if (desc && !*desc)
  239.     desc = 0;
  240.   if (name && !*name)
  241.     name = 0;
  242.  
  243.   if (uty->open_subpart)
  244.     {
  245.       status = MimeUntypedText_close_subpart (obj);
  246.       if (status < 0) return status;
  247.     }
  248.   XP_ASSERT(!uty->open_subpart);
  249.   XP_ASSERT(!uty->open_hdrs);
  250.  
  251.   /* To make one of these implicitly-typed sub-objects, we make up a fake
  252.      header block, containing only the minimum number of MIME headers needed.
  253.      We could do most of this (Type and Encoding) by making a null header
  254.      block, and simply setting obj->content_type and obj->encoding; but making
  255.      a fake header block is better for two reasons: first, it means that
  256.      something will actually be displayed when in `Show All Headers' mode;
  257.      and second, it's the only way to communicate the filename parameter,
  258.      aside from adding a new slot to MimeObject (which is something to be
  259.      avoided when possible.)
  260.    */
  261.  
  262.   uty->open_hdrs = MimeHeaders_new();
  263.   if (!uty->open_hdrs) return MK_OUT_OF_MEMORY;
  264.  
  265.   h = (char *) XP_ALLOC(XP_STRLEN(type) +
  266.                         (enc ? XP_STRLEN(enc) : 0) +
  267.                         (desc ? XP_STRLEN(desc) : 0) +
  268.                         (name ? XP_STRLEN(name) : 0) +
  269.                         100);
  270.   if (!h) return MK_OUT_OF_MEMORY;
  271.  
  272.   XP_STRCPY(h, HEADER_CONTENT_TYPE ": ");
  273.   XP_STRCAT(h, type);
  274.   XP_STRCAT(h, LINEBREAK);
  275.   status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
  276.   if (status < 0) goto FAIL;
  277.  
  278.   if (enc)
  279.     {
  280.       XP_STRCPY(h, HEADER_CONTENT_TRANSFER_ENCODING ": ");
  281.       XP_STRCAT(h, enc);
  282.       XP_STRCAT(h, LINEBREAK);
  283.       status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
  284.       if (status < 0) goto FAIL;
  285.     }
  286.  
  287.   if (desc)
  288.     {
  289.       XP_STRCPY(h, HEADER_CONTENT_DESCRIPTION ": ");
  290.       XP_STRCAT(h, desc);
  291.       XP_STRCAT(h, LINEBREAK);
  292.       status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
  293.       if (status < 0) goto FAIL;
  294.     }
  295.   if (name)
  296.     {
  297.       XP_STRCPY(h, HEADER_CONTENT_DISPOSITION ": inline; filename=\"");
  298.       XP_STRCAT(h, name);
  299.       XP_STRCAT(h, "\"" LINEBREAK);
  300.       status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
  301.       if (status < 0) goto FAIL;
  302.     }
  303.  
  304.   /* push out a blank line. */
  305.   XP_STRCPY(h, LINEBREAK);
  306.   status = MimeHeaders_parse_line(h, XP_STRLEN(h), uty->open_hdrs);
  307.   if (status < 0) goto FAIL;
  308.  
  309.  
  310.   /* Create a child... */
  311.   {
  312.     XP_Bool horrid_kludge = (obj->options && obj->options->state &&
  313.                              obj->options->state->first_part_written_p);
  314.     if (horrid_kludge)
  315.       obj->options->state->first_part_written_p = FALSE;
  316.  
  317.     uty->open_subpart = mime_create(type, uty->open_hdrs, obj->options);
  318.  
  319.     if (horrid_kludge)
  320.       obj->options->state->first_part_written_p = TRUE;
  321.  
  322.     if (!uty->open_subpart)
  323.       {
  324.         status = MK_OUT_OF_MEMORY;
  325.         goto FAIL;
  326.       }
  327.   }
  328.  
  329.   /* Add it to the list... */
  330.   status = ((MimeContainerClass *) obj->class)->add_child(obj,
  331.                                                           uty->open_subpart);
  332.   if (status < 0)
  333.     {
  334.       mime_free(uty->open_subpart);
  335.       uty->open_subpart = 0;
  336.       goto FAIL;
  337.     }
  338.  
  339.   /* And start its parser going. */
  340.   status = uty->open_subpart->class->parse_begin(uty->open_subpart);
  341.   if (status < 0)
  342.     {
  343.       /* MimeContainer->finalize will take care of shutting it down now. */
  344.       uty->open_subpart = 0;
  345.       goto FAIL;
  346.     }
  347.  
  348.   uty->type = ttype;
  349.  
  350.  FAIL:
  351.   FREEIF(h);
  352.  
  353.   if (status < 0 && uty->open_hdrs)
  354.     {
  355.       MimeHeaders_free(uty->open_hdrs);
  356.       uty->open_hdrs = 0;
  357.     }
  358.  
  359.   return status;
  360. }
  361.  
  362. static XP_Bool
  363. MimeUntypedText_uu_begin_line_p(const char *line, int32 length,
  364.                                 MimeDisplayOptions *opt,
  365.                                 char **type_ret, char **name_ret)
  366. {
  367.   const char *s;
  368.   char *name = 0;
  369.   char *type = 0;
  370.  
  371.   if (type_ret) *type_ret = 0;
  372.   if (name_ret) *name_ret = 0;
  373.  
  374.   if (XP_STRNCMP (line, "begin ", 6)) return FALSE;
  375.   /* ...then three or four octal digits. */
  376.   s = line + 6;
  377.   if (*s < '0' || *s > '7') return FALSE;
  378.   s++;
  379.   if (*s < '0' || *s > '7') return FALSE;
  380.   s++;
  381.   if (*s < '0' || *s > '7') return FALSE;
  382.   s++;
  383.   if (*s == ' ')
  384.     s++;
  385.   else
  386.     {
  387.       if (*s < '0' || *s > '7') return FALSE;
  388.       s++;
  389.       if (*s != ' ') return FALSE;
  390.     }
  391.  
  392.   while (XP_IS_SPACE(*s))
  393.     s++;
  394.  
  395.   name = (char *) XP_ALLOC(((line+length)-s) + 1);
  396.   if (!name) return FALSE; /* grr... */
  397.   XP_MEMCPY(name, s, (line+length)-s);
  398.   name[(line+length)-s] = 0;
  399.  
  400.   /* take off newline. */
  401.   if (name[XP_STRLEN(name)-1] == LF) name[XP_STRLEN(name)-1] = 0;
  402.   if (name[XP_STRLEN(name)-1] == CR) name[XP_STRLEN(name)-1] = 0;
  403.  
  404.   /* Now try and figure out a type.
  405.    */
  406.   if (opt && opt->file_type_fn)
  407.     type = opt->file_type_fn(name, opt->stream_closure);
  408.   else
  409.     type = 0;
  410.  
  411.   if (name_ret)
  412.     *name_ret = name;
  413.   else
  414.     FREEIF(name);
  415.  
  416.   if (type_ret)
  417.     *type_ret = type;
  418.   else
  419.     FREEIF(type);
  420.  
  421.   return TRUE;
  422. }
  423.  
  424. static XP_Bool
  425. MimeUntypedText_uu_end_line_p(const char *line, int32 length)
  426. {
  427. #if 0
  428.   /* A strictly conforming uuencode end line. */
  429.   return (line[0] == 'e' &&
  430.           line[1] == 'n' &&
  431.           line[2] == 'd' &&
  432.           (line[3] == 0 || XP_IS_SPACE(line[3])));
  433. #else
  434.   /* ...but, why don't we accept any line that begins with the three
  435.      letters "END" in any case: I've seen lots of partial messages
  436.      that look like
  437.  
  438.         BEGIN----- Cut Here-----
  439.         begin 644 foo.gif
  440.         Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  441.         Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  442.         Mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  443.         END------- Cut Here-----
  444.  
  445.      so let's be lenient here.  (This is only for the untyped-text-plain
  446.      case -- the uudecode parser itself is strict.)
  447.    */
  448.   return (line[0] == ' ' ||
  449.           line[0] == '\t' ||
  450.           ((line[0] == 'e' || line[0] == 'E') &&
  451.            (line[1] == 'n' || line[1] == 'N') &&
  452.            (line[2] == 'd' || line[2] == 'D')));
  453. #endif
  454. }
  455.  
  456.  
  457. #define BINHEX_MAGIC "(This file must be converted with BinHex 4.0)"
  458. #define BINHEX_MAGIC_LEN 45
  459.  
  460. static XP_Bool
  461. MimeUntypedText_binhex_begin_line_p(const char *line, int32 length,
  462.                                     MimeDisplayOptions *opt)
  463. {
  464.   if (length <= BINHEX_MAGIC_LEN)
  465.     return FALSE;
  466.  
  467.   while(length > 0 && XP_IS_SPACE(line[length-1]))
  468.     length--;
  469.  
  470.   if (length != BINHEX_MAGIC_LEN)
  471.     return FALSE;
  472.  
  473.   if (!XP_STRNCMP(line, BINHEX_MAGIC, BINHEX_MAGIC_LEN))
  474.     return TRUE;
  475.   else
  476.     return FALSE;
  477. }
  478.  
  479. static XP_Bool
  480. MimeUntypedText_binhex_end_line_p(const char *line, int32 length)
  481. {
  482.   if (length > 0 && line[length-1] == LF) length--;
  483.   if (length > 0 && line[length-1] == CR) length--;
  484.  
  485.   if (length != 0 && length != 64)
  486.     return TRUE;
  487.   else
  488.     return FALSE;
  489. }
  490.