Blame src/liboggz/oggz_comments.c

Packit a38265
/*
Packit a38265
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
Packit a38265
   Organisation (CSIRO) Australia
Packit a38265
Packit a38265
   Redistribution and use in source and binary forms, with or without
Packit a38265
   modification, are permitted provided that the following conditions
Packit a38265
   are met:
Packit a38265
Packit a38265
   - Redistributions of source code must retain the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer.
Packit a38265
Packit a38265
   - Redistributions in binary form must reproduce the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer in the
Packit a38265
   documentation and/or other materials provided with the distribution.
Packit a38265
Packit a38265
   - Neither the name of CSIRO Australia nor the names of its
Packit a38265
   contributors may be used to endorse or promote products derived from
Packit a38265
   this software without specific prior written permission.
Packit a38265
Packit a38265
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit a38265
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit a38265
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
Packit a38265
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
Packit a38265
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit a38265
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit a38265
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit a38265
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit a38265
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit a38265
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit a38265
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit a38265
*/
Packit a38265
Packit a38265
#include "config.h"
Packit a38265
Packit a38265
#include <stdio.h>
Packit a38265
#include <stdlib.h>
Packit a38265
#include <string.h>
Packit a38265
#include <limits.h> /* ULONG_MAX */
Packit a38265
#ifndef WIN32
Packit a38265
#include <strings.h>
Packit a38265
#endif
Packit a38265
Packit a38265
#include <assert.h>
Packit a38265
Packit a38265
#include "oggz_private.h"
Packit a38265
#include "oggz_vector.h"
Packit a38265
Packit a38265
#include "oggz/oggz_stream.h"
Packit a38265
Packit a38265
/*#define DEBUG*/
Packit a38265
Packit a38265
#ifdef WIN32                                                                   
Packit a38265
#define strcasecmp _stricmp
Packit a38265
#endif
Packit a38265
Packit a38265
/* Ensure comment vector length can be expressed in 32 bits
Packit a38265
 * including space for the trailing NUL */
Packit a38265
#define MAX_COMMENT_LENGTH 0xFFFFFFFE
Packit a38265
#define oggz_comment_clamp(c) MIN((c),MAX_COMMENT_LENGTH)
Packit a38265
Packit a38265
static size_t
Packit a38265
oggz_comment_len (const char * s)
Packit a38265
{
Packit a38265
  size_t len;
Packit a38265
Packit a38265
  if (s == NULL) return 0;
Packit a38265
Packit a38265
  len = strlen (s);
Packit a38265
  return oggz_comment_clamp(len);
Packit a38265
}
Packit a38265
Packit a38265
static char *
Packit a38265
oggz_strdup (const char * s)
Packit a38265
{
Packit a38265
  char * ret;
Packit a38265
  if (s == NULL) return NULL;
Packit a38265
  ret = oggz_malloc (oggz_comment_len(s) + 1);
Packit a38265
  if (ret == NULL) return NULL;
Packit a38265
Packit a38265
  return strcpy (ret, s);
Packit a38265
}
Packit a38265
Packit a38265
static char *
Packit a38265
oggz_strdup_len (const char * s, size_t len)
Packit a38265
{
Packit a38265
  char * ret;
Packit a38265
  if (s == NULL) return NULL;
Packit a38265
  if (len == 0) return NULL;
Packit a38265
  len = oggz_comment_clamp(len);
Packit a38265
  ret = oggz_malloc (len + 1);
Packit a38265
  if (!ret) return NULL;
Packit a38265
  if (strncpy (ret, s, len) == NULL) {
Packit a38265
    oggz_free (ret);
Packit a38265
    return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  ret[len] = '\0';
Packit a38265
  return ret;
Packit a38265
}
Packit a38265
Packit a38265
static char *
Packit a38265
oggz_index_len (const char * s, char c, int len)
Packit a38265
{
Packit a38265
  int i;
Packit a38265
Packit a38265
  for (i = 0; *s && i < len; i++, s++) {
Packit a38265
    if (*s == c) return (char *)s;
Packit a38265
  }
Packit a38265
Packit a38265
  return NULL;
Packit a38265
}
Packit a38265
Packit a38265
/*
Packit a38265
 Comments will be stored in the Vorbis style.
Packit a38265
 It is describled in the "Structure" section of
Packit a38265
    http://www.xiph.org/ogg/vorbis/doc/v-comment.html
Packit a38265
Packit a38265
The comment header is decoded as follows:
Packit a38265
  1) [vendor_length] = read an unsigned integer of 32 bits
Packit a38265
  2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
Packit a38265
  3) [user_comment_list_length] = read an unsigned integer of 32 bits
Packit a38265
  4) iterate [user_comment_list_length] times {
Packit a38265
     5) [length] = read an unsigned integer of 32 bits
Packit a38265
     6) this iteration's user comment = read a UTF-8 vector as [length] octets
Packit a38265
     }
Packit a38265
  7) [framing_bit] = read a single bit as boolean
Packit a38265
  8) if ( [framing_bit]  unset or end of packet ) then ERROR
Packit a38265
  9) done.
Packit a38265
Packit a38265
  If you have troubles, please write to ymnk@jcraft.com.
Packit a38265
 */
Packit a38265
Packit a38265
#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
Packit a38265
                           ((buf[base+2]<<16)&0xff0000)| \
Packit a38265
                           ((buf[base+1]<<8)&0xff00)| \
Packit a38265
  	           	    (buf[base]&0xff))
Packit a38265
#define writeint(buf, base, val) buf[base+3]=(char)(((val)>>24)&0xff); \
Packit a38265
                                 buf[base+2]=(char)(((val)>>16)&0xff); \
Packit a38265
                                 buf[base+1]=(char)(((val)>>8)&0xff); \
Packit a38265
                                 buf[base]=(char)((val)&0xff);
Packit a38265
Packit a38265
/* The FLAC header will encode the length of the comments in
Packit a38265
   24bit BE format.
Packit a38265
*/
Packit a38265
#define writeint24be(buf, base, val) buf[base]=(char)(((val)>>16)&0xff); \
Packit a38265
                                     buf[base+1]=(char)(((val)>>8)&0xff); \
Packit a38265
                                     buf[base+2]=(char)((val)&0xff);
Packit a38265
Packit a38265
static int
Packit a38265
oggz_comment_validate_byname (const char * name)
Packit a38265
{
Packit a38265
  const char * c;
Packit a38265
Packit a38265
  if (!name) return 0;
Packit a38265
Packit a38265
  for (c = name; *c; c++) {
Packit a38265
    if (*c < 0x20 || *c > 0x7D || *c == 0x3D) {
Packit a38265
#ifdef DEBUG
Packit a38265
      printf ("XXX char %c in %s invalid\n", *c, name);
Packit a38265
#endif
Packit a38265
      return 0;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  /* XXX: we really should validate value as UTF-8 here, but ... */
Packit a38265
Packit a38265
  return 1;
Packit a38265
}
Packit a38265
Packit a38265
static OggzComment *
Packit a38265
oggz_comment_new (const char * name, const char * value)
Packit a38265
{
Packit a38265
  OggzComment * comment;
Packit a38265
Packit a38265
  if (!oggz_comment_validate_byname (name)) return NULL;
Packit a38265
  /* Ensures that name != NULL and contains only valid characters */
Packit a38265
Packit a38265
  comment = oggz_malloc (sizeof (OggzComment));
Packit a38265
  if (comment == NULL) return NULL;
Packit a38265
Packit a38265
  comment->name = oggz_strdup (name);
Packit a38265
  if (comment->name == NULL) {
Packit a38265
    oggz_free (comment);
Packit a38265
    return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  if (value) {
Packit a38265
    comment->value = oggz_strdup (value);
Packit a38265
    if (comment->value == NULL) {
Packit a38265
      oggz_free (comment->name);
Packit a38265
      oggz_free (comment);
Packit a38265
      return NULL;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    comment->value = NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  return comment;
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
oggz_comment_free (OggzComment * comment)
Packit a38265
{
Packit a38265
  if (!comment) return;
Packit a38265
  if (comment->name) oggz_free (comment->name);
Packit a38265
  if (comment->value) oggz_free (comment->value);
Packit a38265
  oggz_free (comment);
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
oggz_comment_cmp (const OggzComment * comment1, const OggzComment * comment2)
Packit a38265
{
Packit a38265
  if (comment1 == comment2) return 1;
Packit a38265
  if (!comment1 || !comment2) return 0;
Packit a38265
Packit a38265
  if (strcasecmp (comment1->name, comment2->name)) return 0;
Packit a38265
  if (strcmp (comment1->value, comment2->value)) return 0;
Packit a38265
Packit a38265
  return 1;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
_oggz_comment_set_vendor (OGGZ * oggz, long serialno,
Packit a38265
			  const char * vendor_string)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
Packit a38265
Packit a38265
  if (stream->vendor) oggz_free (stream->vendor);
Packit a38265
Packit a38265
  if ((stream->vendor = oggz_strdup (vendor_string)) == NULL)
Packit a38265
    return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
/* Public API */
Packit a38265
Packit a38265
const char *
Packit a38265
oggz_comment_get_vendor (OGGZ * oggz, long serialno)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
Packit a38265
  if (oggz == NULL) return NULL;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return NULL;
Packit a38265
Packit a38265
  return stream->vendor;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comment_set_vendor (OGGZ * oggz, long serialno, const char * vendor_string)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    stream = oggz_add_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
  if (oggz->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
Packit a38265
      return _oggz_comment_set_vendor (oggz, serialno, vendor_string);
Packit a38265
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
Packit a38265
const OggzComment *
Packit a38265
oggz_comment_first (OGGZ * oggz, long serialno)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
Packit a38265
  if (oggz == NULL) return NULL;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return NULL;
Packit a38265
Packit a38265
  return oggz_vector_nth_p (stream->comments, 0);
Packit a38265
}
Packit a38265
Packit a38265
const OggzComment *
Packit a38265
oggz_comment_first_byname (OGGZ * oggz, long serialno, char * name)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * comment;
Packit a38265
  int i;
Packit a38265
Packit a38265
  if (oggz == NULL) return NULL;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return NULL;
Packit a38265
Packit a38265
  if (name == NULL) return oggz_vector_nth_p (stream->comments, 0);
Packit a38265
Packit a38265
  if (!oggz_comment_validate_byname (name))
Packit a38265
    return NULL;
Packit a38265
Packit a38265
  for (i = 0; i < oggz_vector_size (stream->comments); i++) {
Packit a38265
    comment = (OggzComment *) oggz_vector_nth_p (stream->comments, i);
Packit a38265
    if (comment->name && !strcasecmp (name, comment->name))
Packit a38265
      return comment;
Packit a38265
  }
Packit a38265
Packit a38265
  return NULL;
Packit a38265
}
Packit a38265
Packit a38265
const OggzComment *
Packit a38265
oggz_comment_next (OGGZ * oggz, long serialno, const OggzComment * comment)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  int i;
Packit a38265
Packit a38265
  if (oggz == NULL || comment == NULL) return NULL;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return NULL;
Packit a38265
Packit a38265
  i = oggz_vector_find_index_p (stream->comments, comment);
Packit a38265
Packit a38265
  return oggz_vector_nth_p (stream->comments, i+1);
Packit a38265
}
Packit a38265
Packit a38265
const OggzComment *
Packit a38265
oggz_comment_next_byname (OGGZ * oggz, long serialno,
Packit a38265
                          const OggzComment * comment)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * v_comment;
Packit a38265
  int i;
Packit a38265
Packit a38265
  if (oggz == NULL || comment == NULL) return NULL;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return NULL;
Packit a38265
Packit a38265
  i = oggz_vector_find_index_p (stream->comments, comment);
Packit a38265
Packit a38265
  for (i++; i < oggz_vector_size (stream->comments); i++) {
Packit a38265
    v_comment = (OggzComment *) oggz_vector_nth_p (stream->comments, i);
Packit a38265
    if (v_comment->name && !strcasecmp (comment->name, v_comment->name))
Packit a38265
      return v_comment;
Packit a38265
  }
Packit a38265
Packit a38265
  return NULL;
Packit a38265
}
Packit a38265
Packit a38265
static OggzComment *
Packit a38265
_oggz_comment_add_byname (oggz_stream_t * stream, const char * name, const char * value)
Packit a38265
{
Packit a38265
  OggzComment * comment, * new_comment;
Packit a38265
  int i;
Packit a38265
Packit a38265
  /* Check that the same name=value pair is not already present */
Packit a38265
  for (i = 0; i < oggz_vector_size (stream->comments); i++) {
Packit a38265
    comment = (OggzComment *) oggz_vector_nth_p (stream->comments, i);
Packit a38265
    if (comment->name && !strcasecmp (name, comment->name)) {
Packit a38265
      if (comment->value == NULL) {
Packit a38265
        if (value == NULL) return comment;
Packit a38265
      } else if ((value && !strcmp (value, comment->value)) || 
Packit a38265
                 (value == NULL && comment->value == NULL)) {
Packit a38265
        return comment;
Packit a38265
      }
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  /* Allocate new comment and insert it */
Packit a38265
  if ((new_comment = oggz_comment_new (name, value)) == NULL)
Packit a38265
    return NULL;
Packit a38265
Packit a38265
  return oggz_vector_insert_p (stream->comments, new_comment);
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comment_add (OGGZ * oggz, long serialno, const OggzComment * comment)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * new_comment;
Packit a38265
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    stream = oggz_add_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
  if (oggz->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
Packit a38265
      if (!oggz_comment_validate_byname (comment->name))
Packit a38265
        return OGGZ_ERR_COMMENT_INVALID;
Packit a38265
Packit a38265
      if (_oggz_comment_add_byname (stream, comment->name, comment->value) == NULL)
Packit a38265
        return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
      return 0;
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comment_add_byname (OGGZ * oggz, long serialno,
Packit a38265
                         const char * name, const char * value)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * new_comment;
Packit a38265
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    stream = oggz_add_stream (oggz, serialno);
Packit a38265
  if (stream == NULL)
Packit a38265
    return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
  if (oggz->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
Packit a38265
      if (!oggz_comment_validate_byname (name))
Packit a38265
        return OGGZ_ERR_COMMENT_INVALID;
Packit a38265
Packit a38265
      if (_oggz_comment_add_byname (stream, name, value) == NULL)
Packit a38265
        return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
      return 0;
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comment_remove (OGGZ * oggz, long serialno, OggzComment * comment)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * v_comment;
Packit a38265
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
Packit a38265
Packit a38265
  if (oggz->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
      v_comment = oggz_vector_find_p (stream->comments, comment);
Packit a38265
Packit a38265
      if (v_comment == NULL) return 0;
Packit a38265
Packit a38265
      oggz_vector_remove_p (stream->comments, v_comment);
Packit a38265
      oggz_comment_free (v_comment);
Packit a38265
Packit a38265
      return 1;
Packit a38265
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comment_remove_byname (OGGZ * oggz, long serialno, char * name)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  OggzComment * comment;
Packit a38265
  int i, ret = 0;
Packit a38265
Packit a38265
  if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
Packit a38265
Packit a38265
  if (oggz->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
      for (i = 0; i < oggz_vector_size (stream->comments); i++) {
Packit a38265
        comment = (OggzComment *) oggz_vector_nth_p (stream->comments, i);
Packit a38265
        if (!strcasecmp (name, comment->name)) {
Packit a38265
          oggz_comment_remove (oggz, serialno, comment);
Packit a38265
          i--;
Packit a38265
          ret++;
Packit a38265
        }
Packit a38265
      }
Packit a38265
      return ret;
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comments_copy (OGGZ * src, long src_serialno,
Packit a38265
                    OGGZ * dest, long dest_serialno)
Packit a38265
{
Packit a38265
  const OggzComment * comment;
Packit a38265
Packit a38265
  if (src == NULL || dest == NULL) return OGGZ_ERR_BAD_OGGZ;
Packit a38265
Packit a38265
  if (dest->flags & OGGZ_WRITE) {
Packit a38265
    if (OGGZ_CONFIG_WRITE) {
Packit a38265
      oggz_comment_set_vendor (dest, dest_serialno,
Packit a38265
                               oggz_comment_get_vendor (src, src_serialno));
Packit a38265
Packit a38265
      for (comment = oggz_comment_first (src, src_serialno); comment;
Packit a38265
           comment = oggz_comment_next (src, src_serialno, comment)) {
Packit a38265
        oggz_comment_add (dest, dest_serialno, comment);
Packit a38265
      }
Packit a38265
    } else {
Packit a38265
      return OGGZ_ERR_DISABLED;
Packit a38265
    }
Packit a38265
  } else {
Packit a38265
    return OGGZ_ERR_INVALID;
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
/* Internal API */
Packit a38265
int
Packit a38265
oggz_comments_init (oggz_stream_t * stream)
Packit a38265
{
Packit a38265
  stream->vendor = NULL;
Packit a38265
  stream->comments = oggz_vector_new ();
Packit a38265
  if (stream->comments == NULL) return -1;
Packit a38265
Packit a38265
  oggz_vector_set_cmp (stream->comments, (OggzCmpFunc) oggz_comment_cmp, NULL);
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comments_free (oggz_stream_t * stream)
Packit a38265
{
Packit a38265
  oggz_vector_foreach (stream->comments, (OggzFunc)oggz_comment_free);
Packit a38265
  oggz_vector_delete (stream->comments);
Packit a38265
  stream->comments = NULL;
Packit a38265
Packit a38265
  if (stream->vendor) oggz_free (stream->vendor);
Packit a38265
  stream->vendor = NULL;
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
oggz_comments_decode (OGGZ * oggz, long serialno,
Packit a38265
                      unsigned char * comments, long length)
Packit a38265
{
Packit a38265
   oggz_stream_t * stream;
Packit a38265
   char *c= (char *)comments;
Packit a38265
   int i, nb_fields, n;
Packit a38265
   size_t len;
Packit a38265
   char *end;
Packit a38265
   char * name, * value, * nvalue = NULL;
Packit a38265
   OggzComment * comment;
Packit a38265
Packit a38265
   if (length<8)
Packit a38265
      return -1;
Packit a38265
Packit a38265
   end = c+length;
Packit a38265
   len=readint(c, 0);
Packit a38265
Packit a38265
   c+=4;
Packit a38265
   if (len>(size_t)(end-c)) return -1;
Packit a38265
Packit a38265
   stream = oggz_get_stream (oggz, serialno);
Packit a38265
   if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
Packit a38265
Packit a38265
   /* Vendor */
Packit a38265
   if (len > 0) {
Packit a38265
     if ((nvalue = oggz_strdup_len (c, len)) == NULL)
Packit a38265
       return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
     if (_oggz_comment_set_vendor (oggz, serialno, nvalue) == OGGZ_ERR_OUT_OF_MEMORY) {
Packit a38265
       oggz_free (nvalue);
Packit a38265
       return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
     }
Packit a38265
Packit a38265
     oggz_free (nvalue);
Packit a38265
   }
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
   fwrite(c, 1, len, stderr); fputc ('\n', stderr);
Packit a38265
#endif
Packit a38265
   c+=len;
Packit a38265
Packit a38265
   if (c+4>end) return -1;
Packit a38265
Packit a38265
   /* This value gets checked effectively by the 'for' condition
Packit a38265
      and the checks within the loop for c running off the end.  */
Packit a38265
   nb_fields=readint(c, 0);
Packit a38265
   c+=4;
Packit a38265
   for (i=0;i
Packit a38265
      if (c+4>end) return -1;
Packit a38265
Packit a38265
      len=readint(c, 0);
Packit a38265
Packit a38265
      c+=4;
Packit a38265
      if (len>(size_t)(end-c)) return -1;
Packit a38265
Packit a38265
      n = 0;
Packit a38265
      name = c;
Packit a38265
      value = oggz_index_len (c, '=', len);
Packit a38265
      if (value) {
Packit a38265
         *value = '\0';
Packit a38265
         value++;
Packit a38265
         n = c+len - value;
Packit a38265
      }
Packit a38265
Packit a38265
      if (n != 0) {
Packit a38265
         if ((nvalue = oggz_strdup_len (value, n)) == NULL)
Packit a38265
           return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
         printf ("oggz_comments_decode: [%d] %s -> %s (length %d)\n",
Packit a38265
         i, name, nvalue, n);
Packit a38265
#endif
Packit a38265
         if (_oggz_comment_add_byname (stream, name, nvalue) == NULL) {
Packit a38265
           oggz_free (nvalue);
Packit a38265
           return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
	 }
Packit a38265
Packit a38265
         oggz_free (nvalue);
Packit a38265
      } else {
Packit a38265
        /* For the case of a comment which is not in key=value form,
Packit a38265
         * duplicate exactly the length of the comment, as it is
Packit a38265
         * not NUL-terminated. In the case of the last comment of the
Packit a38265
         * packet, it will be followed immediately by a framing bit.
Packit a38265
         */
Packit a38265
         if ((nvalue = oggz_strdup_len (name, len)) == NULL)
Packit a38265
           return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
         printf ("oggz_comments_decode: [%d] %s (no value) (length %d)\n",
Packit a38265
         i, nvalue, len);
Packit a38265
#endif
Packit a38265
Packit a38265
         if (_oggz_comment_add_byname (stream, nvalue, NULL) == NULL) {
Packit a38265
           oggz_free (nvalue);
Packit a38265
           return OGGZ_ERR_OUT_OF_MEMORY;
Packit a38265
	 }
Packit a38265
Packit a38265
         oggz_free (nvalue);
Packit a38265
      }
Packit a38265
Packit a38265
      c+=len;
Packit a38265
   }
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
   printf ("oggz_comments_decode: done\n");
Packit a38265
#endif
Packit a38265
Packit a38265
   return 0;
Packit a38265
}
Packit a38265
Packit a38265
/*
Packit a38265
 * Pre-condition: at least one of accum, delta are non-zero,
Packit a38265
 * ie. don't call accum_length (0, 0);
Packit a38265
 * \retval 0 Failure: integer overflow
Packit a38265
 */
Packit a38265
static unsigned long
Packit a38265
accum_length (unsigned long * accum, unsigned long delta)
Packit a38265
{
Packit a38265
  /* Pre-condition: don't call accum_length (0, 0) */
Packit a38265
  assert (*accum != 0 || delta != 0);
Packit a38265
Packit a38265
  /* Check for integer overflow */
Packit a38265
  if (delta > ULONG_MAX - (*accum))
Packit a38265
    return 0;
Packit a38265
Packit a38265
  *accum += delta;
Packit a38265
Packit a38265
  return *accum;
Packit a38265
}
Packit a38265
Packit a38265
long
Packit a38265
oggz_comments_encode (OGGZ * oggz, long serialno,
Packit a38265
                      unsigned char * buf, long length)
Packit a38265
{
Packit a38265
  oggz_stream_t * stream;
Packit a38265
  char * c = (char *)buf;
Packit a38265
  const OggzComment * comment;
Packit a38265
  int nb_fields = 0, vendor_length = 0;
Packit a38265
  unsigned long actual_length = 0, remaining = length, field_length;
Packit a38265
Packit a38265
  /* Deal with sign of length first */
Packit a38265
  if (length < 0) return 0;
Packit a38265
Packit a38265
  stream = oggz_get_stream (oggz, serialno);
Packit a38265
  if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO;
Packit a38265
Packit a38265
  /* Vendor string */
Packit a38265
  if (stream->vendor)
Packit a38265
    vendor_length = oggz_comment_len (stream->vendor);
Packit a38265
  if (accum_length (&actual_length, 4 + vendor_length) == 0)
Packit a38265
    return 0;
Packit a38265
#ifdef DEBUG
Packit a38265
  printf ("oggz_comments_encode: vendor = %s\n", stream->vendor);
Packit a38265
#endif
Packit a38265
Packit a38265
  /* user comment list length */
Packit a38265
  if (accum_length (&actual_length, 4) == 0)
Packit a38265
    return 0;
Packit a38265
Packit a38265
Packit a38265
  for (comment = oggz_comment_first (oggz, serialno); comment;
Packit a38265
       comment = oggz_comment_next (oggz, serialno, comment)) {
Packit a38265
    /* [size]"name" */
Packit a38265
    if (accum_length (&actual_length, 4 + oggz_comment_len (comment->name)) == 0)
Packit a38265
      return 0;
Packit a38265
    if (comment->value) {
Packit a38265
      /* "=value" */
Packit a38265
      if (accum_length (&actual_length, 1 + oggz_comment_len (comment->value)) == 0)
Packit a38265
        return 0;
Packit a38265
    }
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
    printf ("oggz_comments_encode: %s = %s\n",
Packit a38265
	    comment->name, comment->value);
Packit a38265
#endif
Packit a38265
Packit a38265
    nb_fields++;
Packit a38265
  }
Packit a38265
Packit a38265
  /* framing bit */
Packit a38265
  if (accum_length (&actual_length, 1) == 0)
Packit a38265
    return 0;
Packit a38265
Packit a38265
  /* NB. actual_length is not modified from here onwards */
Packit a38265
Packit a38265
  if (buf == NULL) return actual_length;
Packit a38265
Packit a38265
  remaining -= 4;
Packit a38265
  if (remaining <= 0) return actual_length;
Packit a38265
  writeint (c, 0, vendor_length);
Packit a38265
  c += 4;
Packit a38265
Packit a38265
  if (stream->vendor) {
Packit a38265
    field_length = oggz_comment_len (stream->vendor);
Packit a38265
    memcpy (c, stream->vendor, MIN (field_length, remaining));
Packit a38265
    c += field_length; remaining -= field_length;
Packit a38265
    if (remaining <= 0) return actual_length;
Packit a38265
  }
Packit a38265
Packit a38265
  remaining -= 4;
Packit a38265
  if (remaining <= 0) return actual_length;
Packit a38265
  writeint (c, 0, nb_fields);
Packit a38265
  c += 4;
Packit a38265
Packit a38265
  for (comment = oggz_comment_first (oggz, serialno); comment;
Packit a38265
       comment = oggz_comment_next (oggz, serialno, comment)) {
Packit a38265
Packit a38265
    field_length = oggz_comment_len (comment->name);     /* [size]"name" */
Packit a38265
    if (comment->value)
Packit a38265
      field_length += 1 + oggz_comment_len (comment->value); /* "=value" */
Packit a38265
Packit a38265
    remaining -= 4;
Packit a38265
    if (remaining <= 0) return actual_length;
Packit a38265
    writeint (c, 0, field_length);
Packit a38265
    c += 4;
Packit a38265
Packit a38265
    field_length = oggz_comment_len (comment->name);
Packit a38265
    memcpy (c, comment->name, MIN (field_length, remaining));
Packit a38265
    c += field_length; remaining -= field_length;
Packit a38265
    if (remaining <= 0) return actual_length;
Packit a38265
Packit a38265
    if (comment->value) {
Packit a38265
      remaining --;
Packit a38265
      if (remaining <= 0) return actual_length;
Packit a38265
      *c = '=';
Packit a38265
      c++;
Packit a38265
Packit a38265
      field_length = oggz_comment_len (comment->value);
Packit a38265
      memcpy (c, comment->value, MIN (field_length, remaining));
Packit a38265
      c += field_length; remaining -= field_length;
Packit a38265
      if (remaining <= 0) return actual_length;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  if (remaining <= 0) return actual_length;
Packit a38265
  *c = 0x01;
Packit a38265
Packit a38265
  return actual_length;
Packit a38265
}
Packit a38265
Packit a38265
/* NB. Public use of this function is deprecated; the simpler
Packit a38265
 * oggz_comments_generate() automatically determines the packet_type */
Packit a38265
ogg_packet *
Packit a38265
oggz_comment_generate(OGGZ * oggz, long serialno,
Packit a38265
		      OggzStreamContent packet_type,
Packit a38265
		      int FLAC_final_metadata_block)
Packit a38265
{
Packit a38265
  ogg_packet *c_packet;
Packit a38265
Packit a38265
  unsigned char *buffer;
Packit a38265
  unsigned const char *preamble;
Packit a38265
  long preamble_length, comment_length, buf_size;
Packit a38265
Packit a38265
  /* Some types use preambles in the comment packet. FLAC is notable;
Packit a38265
     n9-32 should contain the length of the comment data as 24bit unsigned
Packit a38265
     BE, and the first octet should be ORed with 0x80 if this is the last
Packit a38265
     (only) metadata block. Any user doing FLAC has to know how to do the
Packit a38265
     encapsulation anyway. */
Packit a38265
  const unsigned char preamble_vorbis[7] =
Packit a38265
    {0x03, 0x76, 0x6f, 0x72, 0x62, 0x69, 0x73};
Packit a38265
  const unsigned char preamble_theora[7] =
Packit a38265
    {0x81, 0x74, 0x68, 0x65, 0x6f, 0x72, 0x61};
Packit a38265
  const unsigned char preamble_flac[4] =
Packit a38265
    {0x04, 0x00, 0x00, 0x00};
Packit a38265
  const unsigned char preamble_kate[9] =
Packit a38265
    {0x81, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00};
Packit a38265
Packit a38265
Packit a38265
  switch(packet_type) {
Packit a38265
    case OGGZ_CONTENT_VORBIS:
Packit a38265
      preamble_length = sizeof preamble_vorbis;
Packit a38265
      preamble = preamble_vorbis;
Packit a38265
      break;
Packit a38265
    case OGGZ_CONTENT_THEORA:
Packit a38265
      preamble_length = sizeof preamble_theora;
Packit a38265
      preamble = preamble_theora;
Packit a38265
      break;
Packit a38265
    case OGGZ_CONTENT_FLAC:
Packit a38265
      preamble_length = sizeof preamble_flac;
Packit a38265
      preamble = preamble_flac;
Packit a38265
      break;
Packit a38265
    case OGGZ_CONTENT_KATE:
Packit a38265
      preamble_length = sizeof preamble_kate;
Packit a38265
      preamble = preamble_kate;
Packit a38265
      break;
Packit a38265
    case OGGZ_CONTENT_PCM:
Packit a38265
    case OGGZ_CONTENT_SPEEX:
Packit a38265
      preamble_length = 0;
Packit a38265
      preamble = 0;
Packit a38265
      /* No preamble for these */
Packit a38265
      break;
Packit a38265
    default:
Packit a38265
      return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  comment_length = oggz_comments_encode (oggz, serialno, 0, 0);
Packit a38265
  if(comment_length <= 0) {
Packit a38265
    return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  buf_size = preamble_length + comment_length;
Packit a38265
Packit a38265
  if(packet_type == OGGZ_CONTENT_FLAC && comment_length >= 0x00ffffff) {
Packit a38265
    return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  c_packet = oggz_malloc(sizeof *c_packet);
Packit a38265
  if(c_packet) {
Packit a38265
    memset(c_packet, 0, sizeof *c_packet);
Packit a38265
    c_packet->packetno = 1;
Packit a38265
    c_packet->packet = oggz_malloc(buf_size);
Packit a38265
  }
Packit a38265
Packit a38265
  if(c_packet && c_packet->packet) {
Packit a38265
    buffer = c_packet->packet;
Packit a38265
    if(preamble_length) {
Packit a38265
      memcpy(buffer, preamble, preamble_length);
Packit a38265
      if(packet_type == OGGZ_CONTENT_FLAC) {
Packit a38265
	/* Use comment_length-1 as we will be stripping the Vorbis
Packit a38265
	   framing byte. */
Packit a38265
	/* MACRO */
Packit a38265
	writeint24be(c_packet->packet, 1, (comment_length-1) );
Packit a38265
	if(FLAC_final_metadata_block) 
Packit a38265
	  {
Packit a38265
	    c_packet->packet[0] |= 0x80;
Packit a38265
	  }
Packit a38265
      }
Packit a38265
      buffer += preamble_length;
Packit a38265
    }
Packit a38265
    oggz_comments_encode (oggz, serialno, buffer, comment_length);
Packit a38265
    c_packet->bytes = buf_size;
Packit a38265
    /* The framing byte for Vorbis shouldn't affect any of the other
Packit a38265
       types, but strip it anyway. */
Packit a38265
    if(packet_type != OGGZ_CONTENT_VORBIS)
Packit a38265
      {
Packit a38265
	c_packet->bytes -= 1;
Packit a38265
      }
Packit a38265
  } else {
Packit a38265
    oggz_free(c_packet);
Packit a38265
    c_packet = 0;
Packit a38265
  }
Packit a38265
Packit a38265
  return c_packet;
Packit a38265
}
Packit a38265
Packit a38265
/* In Flac, OggPCM, Speex, Theora Vorbis, and Kate the comment packet will
Packit a38265
   be second in the stream, i.e. packetno=1, and it will have granulepos=0 */
Packit a38265
ogg_packet *
Packit a38265
oggz_comments_generate(OGGZ * oggz, long serialno,
Packit a38265
		      int FLAC_final_metadata_block)
Packit a38265
{
Packit a38265
  OggzStreamContent packet_type;
Packit a38265
Packit a38265
  packet_type = oggz_stream_get_content (oggz, serialno);
Packit a38265
Packit a38265
  return oggz_comment_generate (oggz, serialno, packet_type,
Packit a38265
                                FLAC_final_metadata_block);
Packit a38265
}
Packit a38265
Packit a38265
void oggz_packet_destroy(ogg_packet *packet) {
Packit a38265
  if(packet) {
Packit a38265
    if(packet->packet)
Packit a38265
      {
Packit a38265
	oggz_free(packet->packet);
Packit a38265
      }
Packit a38265
    oggz_free(packet);
Packit a38265
  }
Packit a38265
  return;
Packit a38265
}