diff options
| -rw-r--r-- | ChangeLog | 10 | ||||
| -rw-r--r-- | dbus/dbus-message-builder.c | 710 | ||||
| -rw-r--r-- | dbus/dbus-message-builder.h | 1 | ||||
| -rw-r--r-- | dbus/dbus-message.c | 7 | ||||
| -rw-r--r-- | dbus/dbus-string.c | 35 | ||||
| -rw-r--r-- | dbus/dbus-string.h | 3 | ||||
| -rw-r--r-- | test/data/valid-messages/simplest.message | 6 | 
7 files changed, 769 insertions, 3 deletions
| @@ -1,5 +1,15 @@  2003-01-26  Havoc Pennington  <hp@pobox.com> +	* dbus/dbus-message-builder.c: implement, completely untested. + +	* test/data/*: add data to be used in testing.  +	".message" files are our simple loadable text format. +	".message-raw" will be binary dumps of messages. + +	* dbus/dbus-string.c (_dbus_string_starts_with_c_str): new + +2003-01-26  Havoc Pennington  <hp@pobox.com> +  	* dbus/dbus-sysdeps.c (_dbus_file_get_contents): new function  	* dbus/dbus-errors.c (dbus_result_to_string): add diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c index e91fa391..d290ecd4 100644 --- a/dbus/dbus-message-builder.c +++ b/dbus/dbus-message-builder.c @@ -20,8 +20,14 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   *   */ +#include <config.h> +  #ifdef DBUS_BUILD_TESTS +  #include "dbus-message-builder.h" +#include "dbus-hash.h" +#include "dbus-internals.h" +#include "dbus-marshal.h"  /**   * @defgroup DBusMessageBuilder code for loading test message data @@ -34,18 +40,274 @@   * @{   */ +static dbus_bool_t +pop_line (DBusString *source, +          DBusString *dest) +{ +  int eol; +   +  _dbus_string_set_length (dest, 0); +   +  eol = 0; +  if (_dbus_string_find (source, 0, "\n", &eol)) +    eol += 1; /* include newline */ +  else +    eol = _dbus_string_get_length (source); + +  if (eol == 0) +    { +      _dbus_verbose ("no more data in file\n"); +      return FALSE; +    } +   +  if (!_dbus_string_move_len (source, 0, eol, +                              dest, 0)) +    { +      _dbus_warn ("failed to pop line\n"); +      return FALSE; +    } + +  /* dump the newline */ +  _dbus_string_set_length (dest, +                           _dbus_string_get_length (dest) - 1); +   +  return TRUE; +} + +static void +strip_command_name (DBusString *str) +{ +  int i; +   +  i = 0; +  if (_dbus_string_find_blank (str, 0, &i)) +    _dbus_string_skip_blank (str, i, &i); + +  _dbus_string_delete (str, 0, i); +} + +static void +strip_leading_space (DBusString *str) +{ +  int i; +   +  i = 0; +  _dbus_string_skip_blank (str, 0, &i); + +  if (i > 0) +    _dbus_string_delete (str, 0, i); +} + +typedef struct +{ +  DBusString name; +  int length; /**< length to write */ +  int offset; /**< where to write it into the data */ +  int endian; /**< endianness to write with */ +} SavedLength; + +static void +free_saved_length (void *data) +{ +  SavedLength *sl = data; + +  _dbus_string_free (&sl->name); +  dbus_free (sl); +} + +static SavedLength* +ensure_saved_length (DBusHashTable    *hash, +                     const DBusString *name) +{ +  SavedLength *sl; +  const char *s; + +  _dbus_string_get_const_data (name, &s); + +  sl = _dbus_hash_table_lookup_string (hash, s); +  if (sl != NULL) +    return sl; +   +  sl = dbus_new0 (SavedLength, 1); + +  if (!_dbus_string_init (&sl->name, _DBUS_INT_MAX)) +    { +      dbus_free (sl); +      return NULL; +    } + +  if (!_dbus_string_copy (name, 0, &sl->name, 0)) +    goto failed; + +  _dbus_string_get_const_data (&sl->name, &s); + +  if (!_dbus_hash_table_insert_string (hash, (char*)s, sl)) +    goto failed; + +  sl->length = -1; +  sl->offset = -1; +  sl->endian = -1; +   +  return sl; +   + failed: +  free_saved_length (sl); +  return NULL; +} + +static dbus_bool_t +save_length (DBusHashTable    *hash, +             const DBusString *name, +             int               length) +{ +  SavedLength *sl; + +  sl = ensure_saved_length (hash, name); + +  if (sl == NULL) +    return FALSE; +  else if (sl->length >= 0) +    { +      _dbus_warn ("Same SAVE_LENGTH given twice\n"); +      return FALSE; +    } +  else +    sl->length = length; + +  return TRUE; +} + +static dbus_bool_t +save_offset (DBusHashTable    *hash, +             const DBusString *name, +             int               offset, +             int               endian) +{ +  SavedLength *sl; + +  sl = ensure_saved_length (hash, name); + +  if (sl == NULL) +    return FALSE; +  else if (sl->offset >= 0) +    { +      _dbus_warn ("Same LENGTH given twice\n"); +      return FALSE; +    } +  else +    { +      sl->offset = offset; +      sl->endian = endian; +    } + +  return TRUE; +} + +/** Saves the segment to delete in order to unalign the next item */ +#define SAVE_FOR_UNALIGN(str, boundary)                                 \ +  int align_pad_start = _dbus_string_get_length (str);                  \ +  int align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, (boundary)) + +/** Deletes the alignment padding */ +#define PERFORM_UNALIGN(str)                                    \ +  if (unalign)                                                  \ +    {                                                           \ +      _dbus_string_delete ((str), align_pad_start,              \ +                           align_pad_end - align_pad_start);    \ +      unalign = FALSE;                                          \ +    } + + +static dbus_bool_t +append_quoted_string (DBusString       *dest, +                      const DBusString *quoted) +{ +  dbus_bool_t in_quotes = FALSE; +  int i; + +  i = 0; +  while (i < _dbus_string_get_length (quoted)) +    { +      unsigned char b; + +      b = _dbus_string_get_byte (quoted, i); +       +      if (in_quotes) +        { +          if (b == '\'') +            in_quotes = FALSE; +          else +            { +              if (!_dbus_string_append_byte (dest, b)) +                return FALSE; +            } +        } +      else +        { +          if (b == '\'') +            in_quotes = TRUE; +          else if (b == ' ' || b == '\n' || b == '\t') +            break; /* end on whitespace if not quoted */ +          else +            { +              if (!_dbus_string_append_byte (dest, b)) +                return FALSE; +            } +        } +       +      ++i; +    } + +  if (!_dbus_string_append_byte (dest, '\0')) +    return FALSE; +  return TRUE; +} + +static dbus_bool_t +append_saved_length (DBusString       *dest, +                     DBusHashTable    *length_hash, +                     const DBusString *name, +                     int               offset, +                     int               endian) +{ +  if (!save_offset (length_hash, name, +                    offset, endian)) +    { +      _dbus_warn ("failed to save offset to LENGTH\n"); +      return FALSE; +    } +   +  if (!_dbus_marshal_int32 (dest, endian, +                            -1)) +    { +      _dbus_warn ("failed to append a length\n"); +      return FALSE; +    } + +  return TRUE; +} +  /**   * Reads the given filename, which should be in "message description   * language" (look at some examples), and builds up the message data   * from it.  The message data may be invalid, or valid.   * + * The parser isn't very strict, it's just a hack for test programs. + *    * The file format is:   * @code + *   VALID_HEADER normal header; byte order, padding, header len, body len, serial + *   BIG_ENDIAN switch to big endian + *   LITTLE_ENDIAN switch to little endian + *   OPPOSITE_ENDIAN switch to opposite endian   *   ALIGN <N> aligns to the given value   *   UNALIGN skips alignment for the next marshal - *   BYTE <N> inserts the given integer in [0,255] + *   BYTE <N> inserts the given integer in [0,255] or char in 'a' format   *   SAVE_LENGTH <name> records the current length under the given name   *   LENGTH <name> inserts the saved length of the same name + *   CHOP <N> chops last N bytes off the data + *   FIELD_NAME <abcd> inserts 4-byte field name + *   TYPE <typename> inserts a typecode byte    * @endcode   *    * Following commands insert aligned data unless @@ -54,8 +316,11 @@   *   INT32 <N> marshals an INT32   *   UINT32 <N> marshals a UINT32   *   DOUBLE <N> marshals a double - *   STRING "Foo" marshals a string + *   STRING 'Foo' marshals a string   * @endcode + * + * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 } + * and so forth.   *    * @param dest the string to append the message data to   * @param filename the filename to load @@ -65,8 +330,447 @@ dbus_bool_t  _dbus_message_data_load (DBusString       *dest,                           const DBusString *filename)  { -  /* FIXME implement */ +  DBusString file; +  DBusResultCode result; +  DBusString line; +  dbus_bool_t retval; +  int line_no; +  dbus_bool_t unalign; +  DBusHashTable *length_hash; +  int endian; +  DBusHashIter iter; +   +  retval = FALSE; +  length_hash = NULL; +   +  if (!_dbus_string_init (&file, _DBUS_INT_MAX)) +    return FALSE; + +  if (!_dbus_string_init (&line, _DBUS_INT_MAX)) +    { +      _dbus_string_free (&file); +      return FALSE; +    } +   +  if ((result = _dbus_file_get_contents (&file, filename)) != DBUS_RESULT_SUCCESS) +    { +      const char *s; +      _dbus_string_get_const_data (filename, &s); +      _dbus_warn ("Getting contents of %s failed: %s\n", +                     s, dbus_result_to_string (result)); +                      +      goto out; +    } + +  length_hash = _dbus_hash_table_new (DBUS_HASH_STRING, +                                      NULL, +                                      free_saved_length); +  if (length_hash == NULL) +    goto out; +   +  endian = DBUS_COMPILER_BYTE_ORDER; +  unalign = FALSE; +  line_no = 0; + next_iteration: +  while (pop_line (&file, &line)) +    { +      dbus_bool_t just_set_unalign; + +      just_set_unalign = FALSE; +      line_no += 1; + +      strip_leading_space (&line); +       +      if (_dbus_string_starts_with_c_str (&line, +                                          "#")) +        { +          /* Ignore this comment */ +          goto next_iteration; +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "VALID_HEADER")) +        { +          int i; +          DBusString name; +           +          if (!_dbus_string_append_byte (dest, endian)) +            { +              _dbus_warn ("could not append endianness\n"); +              goto parse_failed; +            } + +          i = 0; +          while (i < 3) +            { +              if (!_dbus_string_append_byte (dest, '\0')) +                { +                  _dbus_warn ("could not append nul pad\n"); +                  goto parse_failed; +                } +              ++i; +            } + +          _dbus_string_init_const (&name, "Header"); +          if (!append_saved_length (dest, length_hash, +                                    &name, _dbus_string_get_length (dest), +                                    endian)) +            goto parse_failed; + +          _dbus_string_init_const (&name, "Body"); +          if (!append_saved_length (dest, length_hash, +                                    &name, _dbus_string_get_length (dest), +                                    endian)) +            goto parse_failed; +           +          /* client serial */ +          if (!_dbus_marshal_int32 (dest, endian, 1)) +            { +              _dbus_warn ("couldn't append client serial\n"); +              goto parse_failed; +            } +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "BIG_ENDIAN")) +        { +          endian = DBUS_BIG_ENDIAN; +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "LITTLE_ENDIAN")) +        { +          endian = DBUS_LITTLE_ENDIAN; +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "OPPOSITE_ENDIAN")) +        { +          if (endian == DBUS_BIG_ENDIAN) +            endian = DBUS_LITTLE_ENDIAN; +          else +            endian = DBUS_BIG_ENDIAN; +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "ALIGN")) +        { +          long val; + +          strip_command_name (&line); + +          if (!_dbus_string_parse_int (&line, 0, &val, NULL)) +            goto parse_failed; + +          if (val > 16) +            { +              _dbus_warn ("Aligning to %ld boundary is crack\n", +                             val); +              goto parse_failed; +            } +           +          if (!_dbus_string_align_length (dest, val)) +            goto parse_failed; +        } +      else if (_dbus_string_starts_with_c_str (&line, "UNALIGN")) +        { +          unalign = TRUE; +          just_set_unalign = TRUE; +        } +      else if (_dbus_string_starts_with_c_str (&line, "CHOP")) +        { +          long val; + +          /* FIXME if you CHOP the offset for a LENGTH +           * command, we segfault. +           */ +           +          strip_command_name (&line); + +          if (!_dbus_string_parse_int (&line, 0, &val, NULL)) +            goto parse_failed; + +          if (val > _dbus_string_get_length (dest)) +            { +              _dbus_warn ("Trying to chop %ld bytes but we only have %d\n", +                          val, +                          _dbus_string_get_length (dest)); +              goto parse_failed; +            } +           +          _dbus_string_shorten (dest, val); +        } +      else if (_dbus_string_starts_with_c_str (&line, "BYTE")) +        { +          unsigned char the_byte; +           +          strip_command_name (&line); + +          if (_dbus_string_equal_c_str (&line, "'\\''")) +            the_byte = '\''; +          else if (_dbus_string_get_byte (&line, 0) == '\'' && +                   _dbus_string_get_length (&line) >= 3 && +                   _dbus_string_get_byte (&line, 2) == '\'') +            the_byte = _dbus_string_get_byte (&line, 1); +          else +            { +              long val; +              if (!_dbus_string_parse_int (&line, 0, &val, NULL)) +                goto parse_failed; +              if (val > 255) +                { +                  _dbus_warn ("A byte must be in range 0-255 not %ld\n", +                                 val); +                  goto parse_failed; +                } +              the_byte = (unsigned char) val; +            } + +          _dbus_string_append_byte (dest, the_byte); +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "SAVE_LENGTH")) +        { +          strip_command_name (&line); + +          if (!save_length (length_hash, &line, +                            _dbus_string_get_length (dest))) +            { +              _dbus_warn ("failed to save length\n"); +              goto parse_failed; +            } +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "LENGTH")) +        { +          SAVE_FOR_UNALIGN (dest, 4); +           +          strip_command_name (&line); + +          if (!append_saved_length (dest, length_hash, +                                    &line, +                                    unalign ? align_pad_start : align_pad_end, +                                    endian)) +            { +              _dbus_warn ("failed to add LENGTH\n"); +              goto parse_failed; +            } + +          PERFORM_UNALIGN (dest); +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "FIELD_NAME")) +        { +          strip_command_name (&line); + +          if (_dbus_string_get_length (&line) != 4) +            { +              const char *s; +              _dbus_string_get_const_data (&line, &s); +              _dbus_warn ("Field name must be four characters not \"%s\"\n", +                             s); +              goto parse_failed; +            } + +          if (unalign) +            unalign = FALSE; +          else +            _dbus_string_align_length (dest, 4); +           +          if (!_dbus_string_copy (&line, 0, dest, +                                  _dbus_string_get_length (dest))) +            goto parse_failed; +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "TYPE")) +        { +          int code; +           +          strip_command_name (&line);           + +          if (_dbus_string_starts_with_c_str (&line, "INVALID")) +            code = DBUS_TYPE_INVALID; +          else if (_dbus_string_starts_with_c_str (&line, "NIL")) +            code = DBUS_TYPE_NIL; +          else if (_dbus_string_starts_with_c_str (&line, "INT32")) +            code = DBUS_TYPE_INT32; +          else if (_dbus_string_starts_with_c_str (&line, "UINT32")) +            code = DBUS_TYPE_UINT32; +          else if (_dbus_string_starts_with_c_str (&line, "DOUBLE")) +            code = DBUS_TYPE_DOUBLE; +          else if (_dbus_string_starts_with_c_str (&line, "STRING")) +            code = DBUS_TYPE_STRING; +          else if (_dbus_string_starts_with_c_str (&line, "INT32_ARRAY")) +            code = DBUS_TYPE_INT32_ARRAY; +          else if (_dbus_string_starts_with_c_str (&line, "UINT32_ARRAY")) +            code = DBUS_TYPE_UINT32_ARRAY; +          else if (_dbus_string_starts_with_c_str (&line, "DOUBLE_ARRAY")) +            code = DBUS_TYPE_DOUBLE_ARRAY; +          else if (_dbus_string_starts_with_c_str (&line, "BYTE_ARRAY")) +            code = DBUS_TYPE_BYTE_ARRAY; +          else if (_dbus_string_starts_with_c_str (&line, "STRING_ARRAY")) +            code = DBUS_TYPE_STRING_ARRAY; +          else +            { +              const char *s; +              _dbus_string_get_const_data (&line, &s); +              _dbus_warn ("%s is not a valid type name\n", s); +              goto parse_failed; +            } + +          if (!_dbus_string_append_byte (dest, code)) +            { +              _dbus_warn ("could not append typecode byte\n"); +              goto parse_failed; +            } +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "INT32")) +        { +          SAVE_FOR_UNALIGN (dest, 4); +          long val; +           +          strip_command_name (&line); + +          if (!_dbus_string_parse_int (&line, 0, &val, NULL)) +            goto parse_failed; +           +          if (!_dbus_marshal_int32 (dest, endian, +                                    val)) +            { +              _dbus_warn ("failed to append INT32\n"); +              goto parse_failed; +            } + +          PERFORM_UNALIGN (dest); +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "UINT32")) +        { +          SAVE_FOR_UNALIGN (dest, 4); +          long val; +           +          strip_command_name (&line); + +          /* FIXME should have _dbus_string_parse_uint32 */ +          if (!_dbus_string_parse_int (&line, 0, &val, NULL)) +            goto parse_failed; +           +          if (!_dbus_marshal_uint32 (dest, endian, +                                     val)) +            { +              _dbus_warn ("failed to append UINT32\n"); +              goto parse_failed; +            } + +          PERFORM_UNALIGN (dest); +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "DOUBLE")) +        { +          SAVE_FOR_UNALIGN (dest, 8); +          double val; +           +          strip_command_name (&line); + +          if (!_dbus_string_parse_double (&line, 0, &val, NULL)) +            goto parse_failed; +           +          if (!_dbus_marshal_double (dest, endian, +                                     val)) +            { +              _dbus_warn ("failed to append DOUBLE\n"); +              goto parse_failed; +            } + +          PERFORM_UNALIGN (dest); +        } +      else if (_dbus_string_starts_with_c_str (&line, +                                               "STRING")) +        { +          SAVE_FOR_UNALIGN (dest, 4); +          int size_offset; +          int old_len; +           +          strip_command_name (&line); + +          size_offset = _dbus_string_get_length (dest); +          size_offset = _DBUS_ALIGN_VALUE (size_offset, 4); +          if (!_dbus_marshal_uint32 (dest, endian, 0)) +            { +              _dbus_warn ("Failed to append string size\n"); +              goto parse_failed; +            } + +          old_len = _dbus_string_get_length (dest); +          if (!append_quoted_string (dest, &line)) +            { +              _dbus_warn ("Failed to append quoted string\n"); +              goto parse_failed; +            } + +          _dbus_marshal_set_uint32 (dest, endian, size_offset, +                                    /* subtract 1 for nul */ +                                    _dbus_string_get_length (dest) - old_len - 1); +           +          PERFORM_UNALIGN (dest); +        } +      else +        goto parse_failed; +       +      if (!just_set_unalign && unalign) +        { +          _dbus_warn ("UNALIGN prior to something that isn't aligned\n"); +          goto parse_failed; +        } + +      goto next_iteration; /* skip parse_failed */ +       +    parse_failed: +      { +        const char *s; +        _dbus_string_get_const_data (&line, &s); +        _dbus_warn ("couldn't process line %d \"%s\"\n", +                    line_no, s); +        goto out; +      } +    } + +  _dbus_hash_iter_init (length_hash, &iter); +  while (_dbus_hash_iter_next (&iter)) +    { +      SavedLength *sl = _dbus_hash_iter_get_value (&iter); +      const char *s; + +      _dbus_string_get_const_data (&sl->name, &s); +       +      if (sl->length < 0) +        { +          _dbus_warn ("Used LENGTH %s but never did SAVE_LENGTH\n", +                      s); +          goto out; +        } +      else if (sl->offset < 0) +        { +          _dbus_warn ("Did SAVE_LENGTH %s but never used LENGTH\n", +                      s); +          goto out; +        } +      else +        { +          _dbus_verbose ("Filling in length %s endian = %d offset = %d length = %d\n", +                         s, sl->endian, sl->offset, sl->length); +          _dbus_marshal_set_int32 (dest, +                                   sl->endian, +                                   sl->offset, +                                   sl->length); +        } +    } +   +  retval = TRUE; +   + out: +  if (length_hash != NULL) +    _dbus_hash_table_unref (length_hash); +  _dbus_string_free (&file); +  _dbus_string_free (&line); +  return retval;  }  /** @} */ diff --git a/dbus/dbus-message-builder.h b/dbus/dbus-message-builder.h index 098b0796..00a8e956 100644 --- a/dbus/dbus-message-builder.h +++ b/dbus/dbus-message-builder.h @@ -28,6 +28,7 @@  #include <dbus/dbus-memory.h>  #include <dbus/dbus-types.h> +#include <dbus/dbus-string.h>  DBUS_BEGIN_DECLS; diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index dfebb8ee..dae219a8 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -1470,6 +1470,13 @@ dbus_message_set_sender (DBusMessage  *message,      }  } +/** + * Gets the service which originated this message, + * or #NULL if unknown or inapplicable. + * + * @param message the message + * @returns the service name or #NULL + */  const char*  dbus_message_get_sender (DBusMessage *message)  { diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 0e9a94d2..ac84cda8 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -1369,6 +1369,41 @@ _dbus_string_equal_c_str (const DBusString *a,    return TRUE;  } +/** + * Checks whether a string starts with the given C string. + * + * @param a the string + * @param c_str the C string + * @returns #TRUE if string starts with it + */ +dbus_bool_t +_dbus_string_starts_with_c_str (const DBusString *a, +                                const char       *c_str) +{ +  const unsigned char *ap; +  const unsigned char *bp; +  const unsigned char *a_end; +  const DBusRealString *real_a = (const DBusRealString*) a; +  DBUS_GENERIC_STRING_PREAMBLE (real_a); + +  ap = real_a->str; +  bp = (const unsigned char*) c_str; +  a_end = real_a->str + real_a->len; +  while (ap != a_end && *bp) +    { +      if (*ap != *bp) +        return FALSE; +       +      ++ap; +      ++bp; +    } + +  if (*bp == '\0') +    return TRUE; +  else +    return FALSE; +} +  static const signed char base64_table[] = {    /* 0 */ 'A',    /* 1 */ 'B', diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index c5564e9c..a0cc1ee0 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -163,6 +163,9 @@ dbus_bool_t _dbus_string_equal        (const DBusString *a,  dbus_bool_t _dbus_string_equal_c_str  (const DBusString *a,                                         const char       *c_str); +dbus_bool_t _dbus_string_starts_with_c_str  (const DBusString *a, +                                             const char       *c_str); +  dbus_bool_t _dbus_string_base64_encode (const DBusString *source,                                          int               start,                                          DBusString       *dest, diff --git a/test/data/valid-messages/simplest.message b/test/data/valid-messages/simplest.message new file mode 100644 index 00000000..949aa852 --- /dev/null +++ b/test/data/valid-messages/simplest.message @@ -0,0 +1,6 @@ +## simplest possible valid message + +## this does a LENGTH Header and LENGTH Body +VALID_HEADER +SET_LENGTH Header +SET_LENGTH Body | 
