2004-01-28 20:02:12 -05:00
|
|
|
/* Icecast
|
|
|
|
*
|
|
|
|
* This program is distributed under the GNU General Public License, version 2.
|
|
|
|
* A copy of this license is included with this source.
|
|
|
|
*
|
|
|
|
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
|
|
|
|
* Michael Smith <msmith@xiph.org>,
|
|
|
|
* oddsock <oddsock@xiph.org>,
|
|
|
|
* Karl Heyes <karl@xiph.org>
|
|
|
|
* and others (see AUTHORS for details).
|
|
|
|
*/
|
|
|
|
|
2003-07-20 21:58:54 -04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2002-08-09 04:16:51 -04:00
|
|
|
#include <string.h>
|
|
|
|
#include <libxml/xmlmemory.h>
|
|
|
|
#include <libxml/debugXML.h>
|
|
|
|
#include <libxml/HTMLtree.h>
|
|
|
|
#include <libxml/xmlIO.h>
|
|
|
|
#include <libxml/xinclude.h>
|
|
|
|
#include <libxml/catalog.h>
|
|
|
|
#include <libxslt/xslt.h>
|
|
|
|
#include <libxslt/xsltInternals.h>
|
|
|
|
#include <libxslt/transform.h>
|
|
|
|
#include <libxslt/xsltutils.h>
|
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2005-08-11 23:27:09 -04:00
|
|
|
#ifdef HAVE_SYS_TIME_H
|
2002-08-13 08:46:45 -04:00
|
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
|
2005-08-11 23:27:09 -04:00
|
|
|
#ifdef WIN32
|
|
|
|
#define snprintf _snprintf
|
|
|
|
#endif
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2003-07-16 15:41:59 -04:00
|
|
|
#include "thread/thread.h"
|
|
|
|
#include "avl/avl.h"
|
|
|
|
#include "httpp/httpp.h"
|
|
|
|
#include "net/sock.h"
|
2002-08-09 04:16:51 -04:00
|
|
|
|
|
|
|
#include "connection.h"
|
|
|
|
|
|
|
|
#include "global.h"
|
|
|
|
#include "refbuf.h"
|
|
|
|
#include "client.h"
|
|
|
|
#include "stats.h"
|
2005-06-18 06:54:53 -04:00
|
|
|
#include "fserve.h"
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
#define CATMODULE "xslt"
|
2003-07-16 15:41:59 -04:00
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
#include "logging.h"
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *filename;
|
|
|
|
time_t last_modified;
|
|
|
|
time_t cache_age;
|
|
|
|
xsltStylesheetPtr stylesheet;
|
|
|
|
} stylesheet_cache_t;
|
|
|
|
|
2005-08-11 23:27:09 -04:00
|
|
|
#ifndef HAVE_XSLTSAVERESULTTOSTRING
|
|
|
|
int xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len, xmlDocPtr result, xsltStylesheetPtr style) {
|
|
|
|
xmlOutputBufferPtr buf;
|
|
|
|
|
|
|
|
*doc_txt_ptr = NULL;
|
|
|
|
*doc_txt_len = 0;
|
|
|
|
if (result->children == NULL)
|
|
|
|
return(0);
|
|
|
|
|
|
|
|
buf = xmlAllocOutputBuffer(NULL);
|
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
return(-1);
|
|
|
|
xsltSaveResultTo(buf, result, style);
|
|
|
|
if (buf->conv != NULL) {
|
|
|
|
*doc_txt_len = buf->conv->use;
|
|
|
|
*doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
|
|
|
|
} else {
|
|
|
|
*doc_txt_len = buf->buffer->use;
|
|
|
|
*doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
|
|
|
|
}
|
|
|
|
(void)xmlOutputBufferClose(buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
/* Keep it small... */
|
|
|
|
#define CACHESIZE 3
|
|
|
|
|
2005-08-11 23:27:09 -04:00
|
|
|
static stylesheet_cache_t cache[CACHESIZE];
|
|
|
|
static mutex_t xsltlock;
|
2002-08-13 08:46:45 -04:00
|
|
|
|
2005-12-17 07:23:09 -05:00
|
|
|
void xslt_initialize(void)
|
2002-08-13 08:46:45 -04:00
|
|
|
{
|
2005-08-11 23:27:09 -04:00
|
|
|
xmlSubstituteEntitiesDefault(1);
|
|
|
|
xmlLoadExtDtdDefaultValue = 1;
|
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
memset(cache, 0, sizeof(stylesheet_cache_t)*CACHESIZE);
|
|
|
|
thread_mutex_create(&xsltlock);
|
2005-04-18 10:32:26 -04:00
|
|
|
xmlSubstituteEntitiesDefault(1);
|
|
|
|
xmlLoadExtDtdDefaultValue = 1;
|
2002-08-13 08:46:45 -04:00
|
|
|
}
|
|
|
|
|
2005-12-17 07:23:09 -05:00
|
|
|
void xslt_shutdown(void) {
|
2002-08-13 08:46:45 -04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0; i < CACHESIZE; i++) {
|
|
|
|
if(cache[i].filename)
|
|
|
|
free(cache[i].filename);
|
|
|
|
if(cache[i].stylesheet)
|
|
|
|
xsltFreeStylesheet(cache[i].stylesheet);
|
|
|
|
}
|
|
|
|
|
2005-04-18 10:32:26 -04:00
|
|
|
thread_mutex_destroy (&xsltlock);
|
2002-08-13 08:46:45 -04:00
|
|
|
xsltCleanupGlobals();
|
|
|
|
}
|
|
|
|
|
2005-12-17 07:23:09 -05:00
|
|
|
static int evict_cache_entry(void) {
|
2003-07-24 01:24:00 -04:00
|
|
|
int i, age=0, oldest=0;
|
2002-08-13 08:46:45 -04:00
|
|
|
|
|
|
|
for(i=0; i < CACHESIZE; i++) {
|
|
|
|
if(cache[i].cache_age > age) {
|
|
|
|
age = cache[i].cache_age;
|
|
|
|
oldest = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xsltFreeStylesheet(cache[oldest].stylesheet);
|
|
|
|
free(cache[oldest].filename);
|
|
|
|
|
|
|
|
return oldest;
|
|
|
|
}
|
|
|
|
|
2004-10-26 10:21:36 -04:00
|
|
|
static xsltStylesheetPtr xslt_get_stylesheet(const char *fn) {
|
2002-08-13 08:46:45 -04:00
|
|
|
int i;
|
|
|
|
int empty = -1;
|
|
|
|
struct stat file;
|
|
|
|
|
|
|
|
if(stat(fn, &file)) {
|
2005-02-15 19:54:55 -05:00
|
|
|
WARN2("Error checking for stylesheet file \"%s\": %s", fn,
|
|
|
|
strerror(errno));
|
2002-08-13 08:46:45 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i=0; i < CACHESIZE; i++) {
|
|
|
|
if(cache[i].filename)
|
|
|
|
{
|
2002-08-13 09:53:07 -04:00
|
|
|
#ifdef _WIN32
|
|
|
|
if(!stricmp(fn, cache[i].filename))
|
|
|
|
#else
|
2002-08-13 08:46:45 -04:00
|
|
|
if(!strcmp(fn, cache[i].filename))
|
2002-08-13 09:53:07 -04:00
|
|
|
#endif
|
2002-08-13 08:46:45 -04:00
|
|
|
{
|
|
|
|
if(file.st_mtime > cache[i].last_modified)
|
|
|
|
{
|
|
|
|
xsltFreeStylesheet(cache[i].stylesheet);
|
|
|
|
|
|
|
|
cache[i].last_modified = file.st_mtime;
|
|
|
|
cache[i].stylesheet = xsltParseStylesheetFile(fn);
|
|
|
|
cache[i].cache_age = time(NULL);
|
|
|
|
}
|
2002-12-29 04:21:32 -05:00
|
|
|
DEBUG1("Using cached sheet %i", i);
|
2002-08-13 08:46:45 -04:00
|
|
|
return cache[i].stylesheet;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
empty = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(empty>=0)
|
|
|
|
i = empty;
|
|
|
|
else
|
|
|
|
i = evict_cache_entry();
|
|
|
|
|
|
|
|
cache[i].last_modified = file.st_mtime;
|
|
|
|
cache[i].filename = strdup(fn);
|
|
|
|
cache[i].stylesheet = xsltParseStylesheetFile(fn);
|
|
|
|
cache[i].cache_age = time(NULL);
|
|
|
|
return cache[i].stylesheet;
|
|
|
|
}
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2004-10-26 10:21:36 -04:00
|
|
|
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
|
2002-08-09 04:16:51 -04:00
|
|
|
{
|
2003-03-14 21:10:19 -05:00
|
|
|
xmlDocPtr res;
|
|
|
|
xsltStylesheetPtr cur;
|
2005-06-18 06:54:53 -04:00
|
|
|
xmlChar *string;
|
2005-07-04 16:11:15 -04:00
|
|
|
int len, problem = 0;
|
2007-08-10 20:24:51 -04:00
|
|
|
const char *mediatype = NULL;
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2005-08-16 12:56:24 -04:00
|
|
|
xmlSetGenericErrorFunc ("", log_parse_failure);
|
|
|
|
xsltSetGenericErrorFunc ("", log_parse_failure);
|
|
|
|
|
2002-08-13 08:46:45 -04:00
|
|
|
thread_mutex_lock(&xsltlock);
|
|
|
|
cur = xslt_get_stylesheet(xslfilename);
|
|
|
|
|
2005-06-18 06:54:53 -04:00
|
|
|
if (cur == NULL)
|
|
|
|
{
|
2005-01-11 11:36:34 -05:00
|
|
|
thread_mutex_unlock(&xsltlock);
|
2005-06-18 06:54:53 -04:00
|
|
|
ERROR1 ("problem reading stylesheet \"%s\"", xslfilename);
|
|
|
|
client_send_404 (client, "Could not parse XSLT file");
|
2002-08-09 04:16:51 -04:00
|
|
|
return;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2005-06-18 06:54:53 -04:00
|
|
|
res = xsltApplyStylesheet(cur, doc, NULL);
|
2002-08-09 04:16:51 -04:00
|
|
|
|
2005-07-04 16:11:15 -04:00
|
|
|
if (xsltSaveResultToString (&string, &len, res, cur) < 0)
|
|
|
|
problem = 1;
|
2007-08-10 20:24:51 -04:00
|
|
|
|
|
|
|
/* lets find out the content type to use */
|
|
|
|
if (cur->mediaType)
|
|
|
|
mediatype = (char *)cur->mediaType;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* check method for the default, a missing method assumes xml */
|
|
|
|
if (cur->method && xmlStrcmp (cur->method, "html") == 0)
|
|
|
|
mediatype = "text/html";
|
|
|
|
else
|
|
|
|
if (cur->method && xmlStrcmp (cur->method, "text") == 0)
|
|
|
|
mediatype = "text/plain";
|
|
|
|
else
|
|
|
|
mediatype = "text/xml";
|
|
|
|
}
|
2005-07-04 16:11:15 -04:00
|
|
|
if (problem == 0)
|
2005-06-18 06:54:53 -04:00
|
|
|
{
|
2007-08-10 20:24:51 -04:00
|
|
|
/* the 100 is to allow for the hardcoded headers */
|
|
|
|
unsigned int full_len = strlen (mediatype) + len + 100;
|
|
|
|
refbuf_t *refbuf = refbuf_new (full_len);
|
2005-06-18 06:54:53 -04:00
|
|
|
|
2005-07-04 16:11:15 -04:00
|
|
|
if (string == NULL)
|
2007-08-10 20:24:51 -04:00
|
|
|
string = xmlCharStrdup ("");
|
|
|
|
snprintf (refbuf->data, full_len,
|
|
|
|
"HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
|
|
|
|
mediatype, len, string);
|
|
|
|
|
2005-06-18 06:54:53 -04:00
|
|
|
client->respcode = 200;
|
2005-08-11 23:27:09 -04:00
|
|
|
client_set_queue (client, NULL);
|
2007-08-10 20:24:51 -04:00
|
|
|
client->refbuf = refbuf;
|
|
|
|
refbuf->len = strlen (refbuf->data);
|
2005-06-18 06:54:53 -04:00
|
|
|
fserve_add_client (client, NULL);
|
|
|
|
xmlFree (string);
|
|
|
|
}
|
2005-07-04 16:11:15 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
WARN1 ("problem applying stylesheet \"%s\"", xslfilename);
|
|
|
|
client_send_404 (client, "XSLT problem");
|
|
|
|
}
|
2007-08-10 20:24:51 -04:00
|
|
|
thread_mutex_unlock (&xsltlock);
|
2002-08-09 04:16:51 -04:00
|
|
|
xmlFreeDoc(res);
|
|
|
|
}
|
|
|
|
|