/*
 *  mod_musicindex.h
 *  mod_musicindex
 *
 *  $Id: mod_musicindex.h 1013 2012-09-01 17:42:41Z varenet $
 *
 *  Created by Thibaut VARENE on Thu Mar 20 2003.
 *  Copyright (c) 2003-2006 Regis BOUDIN
 *  Copyright (c) 2003-2012 Thibaut VARENE
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version 2.1,
 *  as published by the Free Software Foundation.
 *
 */

#ifndef MOD_MUSICINDEX_H
#define MOD_MUSICINDEX_H

#include <httpd.h>
#include <http_config.h>
#include <http_request.h>
#include <http_protocol.h>
#include <http_log.h>

/* AP24 bw compat 2.0/2.2 */
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(musicindex);
#endif

/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

/* Define to the full name of this package. */
#undef PACKAGE_NAME

/* Define to the full name and version of this package. */
#undef PACKAGE_STRING

/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME

/* Define to the version of this package. */
#undef PACKAGE_VERSION

#include "mod_musicindex_config.h"

/*
 * Current version number & authors
 */
#define MUSIC_VERSION_STRING	PACKAGE_VERSION		/**< Version string */
#define MUSIC_AUTHORS_STRING	"R. Boudin & T. Varene"	/**< Authors */
#define MUSIC_HEADER_STRING	"mod_musicindex/" MUSIC_VERSION_STRING

/*
 * Compile time configuration options
 * Comment explains what happens when they are #define'd
 * TODO some of these should be ./configure'able
 */
#undef NO_TITLE_STRIP	/* Disable title pretty printing for untagged files */
#undef NO_BR_NOMINAL	/* Disable display of nominal bitrate when available */
#undef BE_INSECURE	/* Enable insecure auth credential leaking in urls */
#undef DEBUG		/* Enable debug output */

/* these are not defined on BSD */
#ifndef FALSE
 #define FALSE 0
 #define TRUE !FALSE
#endif

#ifndef C_HAS_ATTRIBUTE
 #define __attribute__(x)	/* NOTHING */
#endif

#ifdef C_HAS_BUILTIN_EXPECT
 #define likely(x)       __builtin_expect(!!(x), 1)
 #define unlikely(x)     __builtin_expect(!!(x), 0)
#else
 #define likely(x)	(x)
 #define unlikely(x)	(x)
#endif

#ifdef HAVE_SYS_TIME_H
 #define STRUCTTV struct timeval
 #if (!defined(HAVE_TIMERSUB) && !defined(timersub))
  #define timersub(tvp, uvp, vvp)					\
	do {								\
		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
		if ((vvp)->tv_usec < 0) {				\
			(vvp)->tv_sec--;				\
			(vvp)->tv_usec += 1000000;			\
		}							\
	} while (0)
 #endif
#else
 #define STRUCTTV int
#endif

/* a very common macro */
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

/**
 * Enumerates sort options in a more elegant way than using dozens of defines.
 * This enum should be kept in sync with the #sort_order_functions[] array in sort.c,
 * otherwise it's all about games.
 */
enum {
	SB_NULL,		/**< Avoid 0 value which has all sorts of nasty side effects */
	SB_ALBUM,		/**< Sort by album */
	SB_POSN,		/**< Sort by Part Of Set nb */
	SB_TRACK,		/**< Sort by track */
	SB_ARTIST,		/**< Sort by artist */
	SB_TITLE,		/**< Sort by title */
	SB_LENGTH,		/**< Sort by length */
	SB_BITRATE,		/**< Sort by bitrate */
	SB_FREQ,		/**< Sort by frequency (samplerate) */
	SB_FILETYPE,		/**< Sort by filetype */
	SB_FILENAME,		/**< Sort by filename */
	SB_URI,			/**< Sort by URI */
	SB_GENRE,		/**< Sort by genre */
	SB_DATE,		/**< Sort by date */
	SB_SIZE,		/**< Sort by filesize */
	SB_MTIME,		/**< Sort by modification time */
	SB_RANDOM,		/**< Sort randomly */
	SB_DIR,			/**< Sort by directory */
	SB_MAX,			/**< Number of elements in this enum */
};


#define SB_DEFAULT	SB_URI	/**< Default sort order (used in search results) */

/*
 * Flags used in mu_config struct, fields "options" and "options_not".
 * 16 bits max.
 */
 
/* First, the flags set by the configuration */
#define MI_ACTIVE	(1 << 0)	/**< Music indexing is enabled */

#define MI_ALLOWSTREAM	(1 << 1)	/**< Stream is permitted */
#define MI_ALLOWDWNLD	(1 << 2)	/**< Download is permitted */
#define MI_ALLOWSEARCH	(1 << 3)	/**< Search is permitted */
#define MI_ALLOWTARBALL	(1 << 4)	/**< Tarball generation is permitted */

/** One of stream, download or tarball is allowed */
#define	MI_ALLOWFETCH	(MI_ALLOWSTREAM | MI_ALLOWDWNLD | MI_ALLOWTARBALL)

/* XXX TODO : move these flags to a different parameter in the structure */
/* The flags set on requests */
#define MI_STREAM	(1 << 5)	/**< Stream request */
#define MI_ALL		(1 << 6)	/**< Request for all files */
#define MI_RECURSIVE	(1 << 7)	/**< Recursive action */
#define MI_RSS		(1 << 8)	/**< RSS feed */
#define MI_PODCAST	(1 << 9)	/**< Podcast */
#define MI_RANDOMDIR	(1 << 10)	/**< Random directory redirection. */
#define MI_TARBALL	(1 << 11)	/**< Tarball creation. */

#define MI_STREAMLST	(MI_STREAM)
#define MI_STREAMALL	(MI_STREAM | MI_ALL)
#define MI_STREAMCOOKIE	(MI_STREAM | MI_COOKIEOP)
#define MI_STREAMRQ	(MI_STREAMLST | MI_STREAMALL | MI_STREAMCOOKIE)

#define MI_DWNLDLST	(MI_TARBALL)
#define MI_DWNLDALL	(MI_TARBALL | MI_ALL)
#define MI_DWNLDCOOKIE	(MI_TARBALL | MI_COOKIEOP)
#define MI_DWNLDRQ	(MI_DWNLDLST | MI_DWNLDALL | MI_DWNLDCOOKIE)

/* The cookie-related flags */
#define MI_COOKIEOP	(1 << 12)	/**< Any cookie operation */
#define MI_COOKIEADD	(1 << 13)	/**< Add a file to the playlist */
#define MI_COOKIEDEL	(1 << 14)	/**< Remove a file from the playlist */

#define MI_COOKIEADDLST (MI_COOKIEOP | MI_COOKIEADD)
#define MI_COOKIEDELLST (MI_COOKIEOP | MI_COOKIEDEL)
#define MI_COOKIEADDALL	(MI_COOKIEOP | MI_COOKIEADD | MI_ALL)
#define MI_COOKIEPURGE	(MI_COOKIEOP | MI_COOKIEDEL | MI_ALL)
#define MI_COOKIESTREAM	(MI_COOKIEOP | MI_STREAM)
#define MI_COOKIEDWNLD	(MI_COOKIEOP | MI_TARBALL)

#define MI_ALLOPS	(MI_COOKIEOP | \
			MI_COOKIEADD | \
			MI_COOKIEDEL | \
			MI_STREAM    | \
			MI_TARBALL   | \
			MI_ALL)

/* Flags for internal use */
#define MI_QUICKPL	(1 << 15)	/**< "Quick Play" (disable bitrate/length) */

/* Extended options set: Flags not stored in config options but used directly.
   These can exceed the 16bit boundary, with care. */
#define	MI_CUSTOM	(1 << 16)	/**< Used to deal with custom playlist in make_music_entry */
#define MI_ALLOWRSS	(1 << 17)	/**< Used within make_music_entry to flag permitted RSS on directories */

/*** End of mu_config flags ***/


/*
 * Flags used in mu_ent struct, field "flags".
 * 8 bits max.
 */

/* Following can be cached */
#define EF_VBR		(1 << 0)	/**< File is VBR */
/* Following can *NOT* be cached and must be updated everytime */
#define EF_INCACHE	(1 << 1)	/**< File is already in cache */
#define EF_ALLOWSTREAM	(1 << 2)	/**< Stream is permitted */
#define EF_ALLOWDWNLD	(1 << 3)	/**< Download is permitted */
#define EF_ALLOWTARBALL	(1 << 4)	/**< Tarball is permitted (used for directories) */
#define EF_ALLOWRSS	(1 << 5)	/**< gross workaround for directories */

/** defines which flags will actually be saved in cache */
#define EF_FLAGSTOSAVE	(EF_VBR)

/*** End of mu_ent flags ***/

#define MAX_STRING	1024		/**< Common reference for maximum string size. */
#define MAX_GENRE	64		/**< max length for genre string. longest seen so far: 37 */
#define MAX_FNAME	255		/**< max length for filenames. according to http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits 255 bytes/chars seems a common limit */
#define MAX_PATHNAME	4096		/**< max length for pathnames. according to http://en.wikipedia.org/wiki/Comparison_of_file_systems#cite_note-note-12-9 Linux has a limit of 4,096, which looks sensible */

#define MI_LOG_PREFIX	"[mod_musicindex] "	/**< String used to prefix log messages */

#ifndef BUILD_FOR_APACHE2
 
 /* some things have to be redefined for apache 1.3 to use the same declaration
    in both apache 1&2 versions of the program */
 #define apr_pool_t	pool		/**< type name is different in apache 1.3 and 2 */

 #define AP_INIT_NO_ARGS(directive, func, mconfig, where, help) \
    { directive, func, mconfig, where, NO_ARGS, help }
 #define AP_INIT_RAW_ARGS(directive, func, mconfig, where, help) \
    { directive, func, mconfig, where, RAW_ARGS, help }
 #define AP_INIT_TAKE1(directive, func, mconfig, where, help) \
    { directive, func, mconfig, where, TAKE1, help }
 #define AP_INIT_FLAG(directive, func, mconfig, where, help) \
    { directive, func, mconfig, where, FLAG, help }

 #define ap_is_directory(pool, name) ap_is_directory(name)	/**< function changed in apache2 */
 #define ap_set_content_type(r, string) (r->content_type = string)
 #define apr_pool_create(child, parent) ((*(child)) = ap_make_sub_pool(parent))
 #define ap_sub_req_lookup_uri(p, r, ptr) ap_sub_req_lookup_uri(p, r)
 #define REQUEST_USER(rec) (rec->connection->user)

 #define apr_pool_destroy ap_destroy_pool

 #define apr_table_get ap_table_get
 #define apr_table_set ap_table_set
 #define apr_table_setn ap_table_setn
 #define apr_palloc ap_palloc
 #define apr_pcalloc ap_pcalloc
 #define apr_pstrdup ap_pstrdup
 #define apr_pstrndup ap_pstrndup
 #define apr_pstrcat ap_pstrcat
 #define apr_psprintf ap_psprintf
 #define apr_base64_encode_len ap_base64encode_len
 #define apr_base64_encode ap_base64encode
 #define apr_base64_decode_len ap_base64decode_len
 #define apr_base64_decode ap_base64decode

#else


 #if defined(AP20)
  /* nada */
 #elif defined(AP22)
  #define ap_http_method ap_http_scheme
 #elif defined(AP24)
  #define ap_http_method ap_http_scheme
  #define ap_get_server_name(r) ap_get_server_name_for_url(r)
 #else
  #error "unknown apache API"
 #endif

 #include <apr_strings.h>
 #include <apr_base64.h>
 #include <ap_compat.h>

 #ifdef APLOG_ERR
  #undef APLOG_ERR
  #define APLOG_ERR LOG_ERR, 0		/**< dirty trick because ap_log_rerror takes one more arg in apache2 */
 #endif
 #ifdef APLOG_WARNING
  #undef APLOG_WARNING
  #define APLOG_WARNING LOG_WARNING, 0	/**< dirty trick because ap_log_rerror takes one more arg in apache2 */
 #endif
 #ifdef APLOG_NOTICE
  #undef APLOG_NOTICE
  #define APLOG_NOTICE LOG_NOTICE, 0	/**< dirty trick because ap_log_rerror takes one more arg in apache2 */
 #endif
 #ifdef APLOG_DEBUG
  #undef APLOG_DEBUG
  #define APLOG_DEBUG LOG_DEBUG, 0	/**< dirty trick because ap_log_rerror takes one more arg in apache2 */
 #endif

 #define REQUEST_USER(rec) (rec->user)

#endif	/* ! __BUILD_FOR_APACHE2 */

/** 
 * Music entry. Contains various data about an associated file.
 * Special care has been taken toward typing to avoid memory waste.
 */
typedef struct mu_ent {
	const struct mu_ent *next;
	const char *file;	/**< file uri. Relative uri unless MI_CUSTOM. */
	const char *filename;	/**< filename (as in uri). file absolute *path* if MI_TARBALL */
	const char *uri;	/**< file URI (unique) */
	const char * restrict album;	/**< album (if any) */
	const char * restrict artist;	/**< artist (if any) */
	const char * restrict title;	/**< title (as shown on the webpages) */
	const char * restrict genre;	/**< genre (type of music - if any) */
	unsigned long bitrate;	/**< bitrate in bits per second */
	unsigned long size;	/**< size in bytes */
	unsigned long mtime;	/**< last modification time */
	unsigned short freq;	/**< samplerate frequency */
	unsigned short length;	/**< length in seconds */
	unsigned short date;	/**< date (year actually) */
	unsigned char flags;	/**< flags associated with the file */
	unsigned char track;	/**< track number */
	unsigned char posn;	/**< "part of a set" number, that is, disc number */
	signed char filetype;	/**< file type, associated to a display string and a mimetype string */
} /* __attribute__((packed)) */ mu_ent;

/**
 * Pack of music entries.
 * This structure holds a pointer to the head of a list of mu_ent items, and
 * the number of files and directories that can be found in that list.
 */
typedef struct {
	const mu_ent *head;	/**< pointer to the head of mu_ent list */
	const mu_ent *fhead;	/**< pointer to the head of the songs list (if any) */
	unsigned long filenb;	/**< number of files in the list */
	unsigned long fsize;	/**< total size of the pack (files) in bytes */
	unsigned short dirnb;	/**< number of directories in the list */
} mu_pack;

typedef struct mu_config mu_config;

/**
 * Cache backend structure, defining a standard set of operations that all
 * cache backends may implement.
 */
typedef struct {
	/** It can be used in many weird ways, but is mostly expected to either
	    return NULL if the directory backend can't provide a full directory
	    listing, and otherwise populate the entries with whatever is in the
	    cache. If return is !NULL, (cache_backend::readdir()) will be
	    subsequently called on the provided return pointer.
	    See musicindex_opendir() for details. */
	void* (*opendir)(request_rec *r, mu_pack *const pack,
			const char * const filename, const char * const uri, unsigned long soptions);

	/** If the above call provided a dir handler, this function will be used.
	    Its primary intent is to provide a way for the cache backend to let
	    the program know that some files couldn't be processed at cache level,
	    and that they need to be parsed by the standard playlist system.
	    Each consecutive call to this function should return the name of a
	    file in the current directory to be processed, terminating with NULL,
	    a la readdir. The only nitpick here is that files listed here will
	    still hit make_cache_entry(), that is the following cache_backend::make_entry() */
	const char* (*readdir)(void *dir);

	/** At the end of directory processing, this function is called, still
	    mimicing opendir/readdir/closedir. */
	void (*closedir)(void *dir);
	
	/** This call reads cached metadata for a given file */
	mu_ent* (*make_entry)(request_rec *r, apr_pool_t *pool, FILE *const in,
		const char *const filename);

	/** This call tells the cache backend to write metadata for a given file. */
	void (*write)(request_rec *r, const mu_ent *const entry,
				  const char * const name);

	/** This call is guaranteed to run before any other cache backend function
	    is called anywhere in the code path. */ 
	void (*prologue)(request_rec *r);

	/** This call is guaranteed to run after any other cache backend function
	    is called in the current module execution */
	void (*epilogue)(request_rec *r);
} cache_backend;

/** Configuration handling structure. We use it to share configuration
 * throughout the whole module */
struct mu_config {
	/* non settings, these are piggy-backed on the config struct */
	char * restrict search;		/**< string to search */
	const char * restrict custom_list;	/**< custom playlist */

	/* actual settings */
	const char * restrict title;	/**< title of the page */
	const char * restrict directory;	/**< directory where musicindex files are stored */
	const char * restrict css;	/**< path to the css */
	const char * restrict iceserver;	/**< adress of an icecast server */

	const cache_backend * restrict cache;	/**< registered cache backend (if any) */
	void * restrict cache_setup;	/**< cache private data (if any) */

	signed short dir_per_line;	/**< number of directories to display per line. If <0 disable "pretty folders" */
	unsigned short cookie_life;	/**< cookie lifetime in seconds */
	short rss_items;		/**< number of entries in the RSS file */

	unsigned short options;		/**< diverse options */
	unsigned short options_not;	/**< used to disable options */

	unsigned short sets;		/**< which field are dynamically set. Used to merge configs */

	unsigned char order[SB_MAX+1];	/**< Sort order. */
	unsigned char fields[SB_MAX+1];	/**< Fields to display */
};


/* i18n stuff */
#ifdef HAVE_GETTEXT
 #include <libintl.h>
 #define _(String)	gettext(String)
#else
 #define _(String)	String
#endif	/* HAVE_GETTEXT */

#define mi_rerror(format, ...) \
	ap_log_rerror(APLOG_MARK, APLOG_ERR, r, MI_LOG_PREFIX "(%s) " format, __func__, ## __VA_ARGS__)
#define mi_serror(format, ...) \
	ap_log_error(APLOG_MARK, APLOG_ERR, s, MI_LOG_PREFIX "(%s) " format, __func__, ## __VA_ARGS__)

/* this could arguably use APLOG_DEBUG but in most configurations this is never logged. Use it later ;-) */
#ifdef DEBUG
 #define mi_rdebug(format, ...) \
	ap_log_rerror(APLOG_MARK, APLOG_NOTICE, r, MI_LOG_PREFIX "(%s) " format, __func__, ## __VA_ARGS__)
#else
 #define mi_rdebug(format, ...)	/* nothing */
#endif

extern module MODULE_VAR_EXPORT musicindex_module;

#endif	/* MOD_MUSICINDEX_H */
