[xiph-commits] r14987 - in icecast/tags: . icecast-2.3.2 icecast-2.3.2/admin icecast-2.3.2/conf icecast-2.3.2/doc icecast-2.3.2/examples icecast-2.3.2/src icecast-2.3.2/web icecast-2.3.2/win32

karl at svn.xiph.org karl at svn.xiph.org
Sun Jun 1 20:19:44 PDT 2008


Author: karl
Date: 2008-06-01 20:19:35 -0700 (Sun, 01 Jun 2008)
New Revision: 14987

Added:
   icecast/tags/icecast-2.3.2/
   icecast/tags/icecast-2.3.2/Makefile.am
   icecast/tags/icecast-2.3.2/NEWS
   icecast/tags/icecast-2.3.2/README
   icecast/tags/icecast-2.3.2/admin/
   icecast/tags/icecast-2.3.2/admin/listclients.xsl
   icecast/tags/icecast-2.3.2/admin/listmounts.xsl
   icecast/tags/icecast-2.3.2/admin/manageauth.xsl
   icecast/tags/icecast-2.3.2/admin/moveclients.xsl
   icecast/tags/icecast-2.3.2/admin/response.xsl
   icecast/tags/icecast-2.3.2/admin/stats.xsl
   icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl
   icecast/tags/icecast-2.3.2/admin/xspf.xsl
   icecast/tags/icecast-2.3.2/conf/
   icecast/tags/icecast-2.3.2/conf/icecast.xml.in
   icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in
   icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in
   icecast/tags/icecast-2.3.2/configure.in
   icecast/tags/icecast-2.3.2/doc/
   icecast/tags/icecast-2.3.2/doc/Makefile.am
   icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html
   icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html
   icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html
   icecast/tags/icecast-2.3.2/doc/masterslave.png
   icecast/tags/icecast-2.3.2/doc/relay.png
   icecast/tags/icecast-2.3.2/examples/icecast_auth-1.0.tar.gz
   icecast/tags/icecast-2.3.2/icecast.spec
   icecast/tags/icecast-2.3.2/src/
   icecast/tags/icecast-2.3.2/src/Makefile.am
   icecast/tags/icecast-2.3.2/src/admin.c
   icecast/tags/icecast-2.3.2/src/admin.h
   icecast/tags/icecast-2.3.2/src/auth.c
   icecast/tags/icecast-2.3.2/src/auth.h
   icecast/tags/icecast-2.3.2/src/auth_htpasswd.c
   icecast/tags/icecast-2.3.2/src/auth_htpasswd.h
   icecast/tags/icecast-2.3.2/src/auth_url.c
   icecast/tags/icecast-2.3.2/src/auth_url.h
   icecast/tags/icecast-2.3.2/src/cfgfile.c
   icecast/tags/icecast-2.3.2/src/cfgfile.h
   icecast/tags/icecast-2.3.2/src/client.c
   icecast/tags/icecast-2.3.2/src/client.h
   icecast/tags/icecast-2.3.2/src/compat.h
   icecast/tags/icecast-2.3.2/src/connection.c
   icecast/tags/icecast-2.3.2/src/connection.h
   icecast/tags/icecast-2.3.2/src/event.c
   icecast/tags/icecast-2.3.2/src/format.c
   icecast/tags/icecast-2.3.2/src/format.h
   icecast/tags/icecast-2.3.2/src/format_kate.c
   icecast/tags/icecast-2.3.2/src/format_kate.h
   icecast/tags/icecast-2.3.2/src/format_mp3.c
   icecast/tags/icecast-2.3.2/src/format_ogg.c
   icecast/tags/icecast-2.3.2/src/format_skeleton.c
   icecast/tags/icecast-2.3.2/src/format_skeleton.h
   icecast/tags/icecast-2.3.2/src/format_speex.c
   icecast/tags/icecast-2.3.2/src/format_vorbis.c
   icecast/tags/icecast-2.3.2/src/fserve.c
   icecast/tags/icecast-2.3.2/src/fserve.h
   icecast/tags/icecast-2.3.2/src/global.c
   icecast/tags/icecast-2.3.2/src/global.h
   icecast/tags/icecast-2.3.2/src/logging.c
   icecast/tags/icecast-2.3.2/src/main.c
   icecast/tags/icecast-2.3.2/src/refbuf.c
   icecast/tags/icecast-2.3.2/src/refbuf.h
   icecast/tags/icecast-2.3.2/src/slave.c
   icecast/tags/icecast-2.3.2/src/slave.h
   icecast/tags/icecast-2.3.2/src/source.c
   icecast/tags/icecast-2.3.2/src/stats.c
   icecast/tags/icecast-2.3.2/src/stats.h
   icecast/tags/icecast-2.3.2/src/util.c
   icecast/tags/icecast-2.3.2/src/util.h
   icecast/tags/icecast-2.3.2/src/xslt.c
   icecast/tags/icecast-2.3.2/src/yp.c
   icecast/tags/icecast-2.3.2/web/
   icecast/tags/icecast-2.3.2/web/auth.xsl
   icecast/tags/icecast-2.3.2/web/server_version.xsl
   icecast/tags/icecast-2.3.2/web/status.xsl
   icecast/tags/icecast-2.3.2/web/style.css
   icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp
   icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp
   icecast/tags/icecast-2.3.2/win32/icecast.dsp
   icecast/tags/icecast-2.3.2/win32/icecast2.iss
   icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp
   icecast/tags/icecast-2.3.2/win32/icecastService.cpp
   icecast/tags/icecast-2.3.2/win32/icecastService.dsp
Removed:
   icecast/tags/icecast-2.3.2/Makefile.am
   icecast/tags/icecast-2.3.2/NEWS
   icecast/tags/icecast-2.3.2/README
   icecast/tags/icecast-2.3.2/admin/
   icecast/tags/icecast-2.3.2/admin/listclients.xsl
   icecast/tags/icecast-2.3.2/admin/listmounts.xsl
   icecast/tags/icecast-2.3.2/admin/manageauth.xsl
   icecast/tags/icecast-2.3.2/admin/moveclients.xsl
   icecast/tags/icecast-2.3.2/admin/response.xsl
   icecast/tags/icecast-2.3.2/admin/stats.xsl
   icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl
   icecast/tags/icecast-2.3.2/conf/
   icecast/tags/icecast-2.3.2/conf/icecast.xml.in
   icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in
   icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in
   icecast/tags/icecast-2.3.2/configure.in
   icecast/tags/icecast-2.3.2/doc/
   icecast/tags/icecast-2.3.2/doc/Makefile.am
   icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html
   icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html
   icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html
   icecast/tags/icecast-2.3.2/examples/icecast_auth-1.0.tar.gz
   icecast/tags/icecast-2.3.2/icecast.spec
   icecast/tags/icecast-2.3.2/src/
   icecast/tags/icecast-2.3.2/src/Makefile.am
   icecast/tags/icecast-2.3.2/src/admin.c
   icecast/tags/icecast-2.3.2/src/admin.h
   icecast/tags/icecast-2.3.2/src/auth.c
   icecast/tags/icecast-2.3.2/src/auth.h
   icecast/tags/icecast-2.3.2/src/auth_htpasswd.c
   icecast/tags/icecast-2.3.2/src/auth_htpasswd.h
   icecast/tags/icecast-2.3.2/src/auth_url.c
   icecast/tags/icecast-2.3.2/src/auth_url.h
   icecast/tags/icecast-2.3.2/src/cfgfile.c
   icecast/tags/icecast-2.3.2/src/cfgfile.h
   icecast/tags/icecast-2.3.2/src/client.c
   icecast/tags/icecast-2.3.2/src/client.h
   icecast/tags/icecast-2.3.2/src/compat.h
   icecast/tags/icecast-2.3.2/src/connection.c
   icecast/tags/icecast-2.3.2/src/connection.h
   icecast/tags/icecast-2.3.2/src/event.c
   icecast/tags/icecast-2.3.2/src/format.c
   icecast/tags/icecast-2.3.2/src/format.h
   icecast/tags/icecast-2.3.2/src/format_mp3.c
   icecast/tags/icecast-2.3.2/src/format_ogg.c
   icecast/tags/icecast-2.3.2/src/format_speex.c
   icecast/tags/icecast-2.3.2/src/format_vorbis.c
   icecast/tags/icecast-2.3.2/src/fserve.c
   icecast/tags/icecast-2.3.2/src/fserve.h
   icecast/tags/icecast-2.3.2/src/global.c
   icecast/tags/icecast-2.3.2/src/global.h
   icecast/tags/icecast-2.3.2/src/logging.c
   icecast/tags/icecast-2.3.2/src/main.c
   icecast/tags/icecast-2.3.2/src/refbuf.c
   icecast/tags/icecast-2.3.2/src/refbuf.h
   icecast/tags/icecast-2.3.2/src/slave.c
   icecast/tags/icecast-2.3.2/src/slave.h
   icecast/tags/icecast-2.3.2/src/source.c
   icecast/tags/icecast-2.3.2/src/stats.c
   icecast/tags/icecast-2.3.2/src/stats.h
   icecast/tags/icecast-2.3.2/src/util.c
   icecast/tags/icecast-2.3.2/src/util.h
   icecast/tags/icecast-2.3.2/src/xslt.c
   icecast/tags/icecast-2.3.2/src/yp.c
   icecast/tags/icecast-2.3.2/web/
   icecast/tags/icecast-2.3.2/web/auth.xsl
   icecast/tags/icecast-2.3.2/web/server_version.xsl
   icecast/tags/icecast-2.3.2/web/status.xsl
   icecast/tags/icecast-2.3.2/web/style.css
   icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp
   icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp
   icecast/tags/icecast-2.3.2/win32/icecast.dsp
   icecast/tags/icecast-2.3.2/win32/icecast2.iss
   icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp
   icecast/tags/icecast-2.3.2/win32/icecastService.cpp
   icecast/tags/icecast-2.3.2/win32/icecastService.dsp
Log:
Tag the Icecast 2.3.2 release


Copied: icecast/tags/icecast-2.3.2 (from rev 10752, icecast/trunk/icecast)

Deleted: icecast/tags/icecast-2.3.2/Makefile.am
===================================================================
--- icecast/trunk/icecast/Makefile.am	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,23 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-AUTOMAKE_OPTIONS = 1.6 foreign dist-zip
-ACLOCAL_AMFLAGS = -I m4
-
-SUBDIRS = src conf debian doc web admin win32 examples
-
-EXTRA_DIST = HACKING m4/acx_pthread.m4 m4/ogg.m4 \
-    m4/theora.m4 m4/vorbis.m4 m4/speex.m4 \
-    m4/xiph_compiler.m4 m4/xiph_curl.m4 m4/xiph_net.m4 \
-    m4/xiph_types.m4 m4/xiph_xml2.m4 icecast.spec
-
-docdir = $(datadir)/doc/$(PACKAGE)
-doc_DATA = README AUTHORS COPYING NEWS TODO ChangeLog
-
-debug:
-	$(MAKE) all CFLAGS="@DEBUG@"
-
-profile:
-	$(MAKE) all CFLAGS="@PROFILE@"
-
-static:
-	$(MAKE) all LDFLAGS="${LDFLAGS} -all-static"

Copied: icecast/tags/icecast-2.3.2/Makefile.am (from rev 14197, icecast/trunk/icecast/Makefile.am)
===================================================================
--- icecast/tags/icecast-2.3.2/Makefile.am	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,23 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign dist-zip
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src conf debian doc web admin win32 examples
+
+EXTRA_DIST = HACKING m4/acx_pthread.m4 m4/ogg.m4 \
+    m4/theora.m4 m4/vorbis.m4 m4/speex.m4 \
+    m4/xiph_compiler.m4 m4/xiph_curl.m4 m4/xiph_net.m4 \
+    m4/xiph_types.m4 m4/xiph_xml2.m4 icecast.spec
+
+docdir = $(datadir)/doc/$(PACKAGE)
+doc_DATA = README AUTHORS COPYING NEWS TODO ChangeLog
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+
+static:
+	$(MAKE) all LDFLAGS="${LDFLAGS} -all-static"

Deleted: icecast/tags/icecast-2.3.2/NEWS
===================================================================
--- icecast/trunk/icecast/NEWS	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/NEWS	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,247 +0,0 @@
-Icecast 2.3 
------------------------------------------------------------------------------
-**** New features for 2.3.0 ****
-
-- Streaming support for ogg speex, ogg flac, ogg midi
-
-- intro file support - per mount settable
-   Intro files will play when a listener first connects to a stream.  This
-   is designed for station jingles and the like.  If you don't broadcast 
-   in ogg vorbis, you must make sure the bitrate/samplerate/number of 
-   channels match up to your stream.
-
-- on-demand relays, global and per-relay settable
-   On demand relays only connect to the relayed content when there are
-   listeners attached to the relay.  This can save bandwidth in certain cases.
-
-- fallback to file, extends on the intro file handling.
-   With this feature, you can specify a "fallback file" which will be played
-   in a loop and sent your currently connected listeners in the event of a
-   source client disconnect.  This means your listeners stay connected while
-   you fix your disconnect problem.  Same rules regarding bitrate/samplerate/
-   number of channels apply as with intro files.
-
-- new mount-level settings
-   1. public, type/subtype, genre settings, stream description,
-      stream url, stream name, bitrate (override what is sent from the source
-      client)
-   2. mp3 metadata interval
-   3. on-[dis]connect scripts can be stated per-mount, invoked at source 
-      start/stop and take 1 arg which is the mountpoint.
-
-- New URL listener authenticator.
-   This delegates your listener authorization to an external application.
-   URL calls are made on listener connect/disconnect as well as source
-   connect/disconnect.  It is meant for large broadcasters who have existing
-   authentication systems that need to be integrated into.  Included is 
-   an example php-based application that can be used in conjunction with 
-   the url authenticator to manage a simple subscription-based broadcast.
-
-- HTPasswd authenticator uses in-memory structures now.
-
-- On demand files now can be fed through an authenticator
-
-- Update to admin/web xslt interface
-
-- Icecast can now be installed as a win32 service
-
-**** Fixes for 2.3.0 ****
-- real/helix works
-- win32 access log correct
-- stats client is stable now (curl -X STATS http://admin@host:port/)
-- show mountpoints on stats that are inactive but have an active fallback
-- more updates over HUP possible
-- improved stability under heavy load
-- moving clients will no longer sometimes deadlock the server
-- avoid small writes to reduce TCP overhead.
-
-
-Icecast 2.2 
------------------------------------------------------------------------------
-****New features for 2.2 (in no particular order):****
-
-- Theora Video support -
-Icecast now supports video streaming via theora.  Currently, we require the latest
-(alpha 4) version of libtheora.  This is an optional compile, so if you don't
-have theora then icecast will safely ignore it
-
-- Shoutcast style source client support -
-Icecast now supports the connection protocol used by the Shoutcast DSP source
-client.  This is the same connection protocol used by their NSV encoding tools.
-This means that not only can you use the Shoutcast DSP to stream to icecast, but
-that you can also stream NSV via their tools.
-
-- AAC is added as a supported streaming format -
-Not too many source clients support streaming in this format, but we support it.
-
-- Cluster password -
-Now you can specify a cluster password as a <mount> option in the config. This
-will allow you to cluster multiple servers/mounts into a single listing on the 
-stream directory.  Note that this is different than "grouping" which groups together
-streams coming from the same physical IP and with the same stream name.  Clusters
-are meant for relays of the same stream and will only be listed *once* in the stream
-directory.  When a listener tunes into a cluster, they will be served an m3u file
-with all the clusters for that stream.
-
-- Playlist Log -
-This is an option setting that will create an audit trail of metadata that comes through
-icecast.  It is a single file that contains information for all mountpoints.
-
-- Range Support for static files -
-We now support seeking in files served off the icecast fserve.
-
-- Metadata Update via Admin -
-We now support metadata updates via the admin interface for both MP3 AND Ogg Vorbis
-streams.
-
-- Per mount hidden stats and YP prevention -
-You many now indicate certains mounts to be excluded (i.e. hidden) from the main
-status.xsl page.  This is useful when using local private relays.  You can also
-override the YP setting (as in disable) on a per-mount basis.  Also useful for
-local private relays.
-
-- Multiple example config files -
-We now have multiple config files for you to use as a base.  A "simple" one for 
-quick-start, and a more detailed "advanced" one with all the features, as well
-as a "shoutcast compatable" one, which shows how you'd config for using the
-shoutcast DSP.
-
-- Relay user/pass -
-You can now specify authentication used by a relay.  This is for the case where
-you have listener authentication enabled for a mountpoint, and want to connect
-a relay to it.
-
-Icecast 2.1 11/04/2004
------------------------------------------------------------------------------
-****New features for 2.1 (in no particular order):****
-
--Listener Authentication-
-
-  Icecast now supports listener authentication.  This provides a mechanism for
-  creating/maintaining users and passwords for listeners.  Currently, we only
-  have implemented a simple, file-based storage for users and passwords.  New
-  authenticators are on the horizon (such as URL-based or possibly MySQL based)
-  New admin pages were also added for the maintenance of users/passwords. Please
-  check the docs for a more detailed description of this new feature.
-
--Multi-Level Fallbacks-
- 
-  Multi-level fallbacks allow for specifications of a series of fallback mounts
-  that you can use to automatically move listeners in the event of a source being
-  disconnected.  In the event of new listeners attaching to the source stream that has
-  been disconnected, they are routed to the fallback mount (if specified).
-  Icecast now has fallback-override capability as well which means that in the event
-  of a source stream failure, listeners are moved to the fallback mount, and then
-  automatically recaptured when the original source stream returns.
-
--Burst-On-Connect-
-
-  This is an new, optional config setting which will send a initial burst of 
-  data to connecting listeners.  This has the effect of reducing 
-  (significantly) the startup buffer latency from the end-user perspective.  
-  This option is enabled by default.
-
-
-****New Enhancements for 2.1****
-
--Update to admin interface-
-
-  This interface has been cleaned up quite a bit and made a bit nicer.
-
--Rewrite of the YP listing code-
-
-  The icecast yp code has received a complete overhaul by karl, and it's a much
-  more stable and failure-resistant implementation.
-
--Lots and lots of bugs fixed-
-
-  Check the ChangeLog for a complete list of these...
-
------------------------------------------------------------------------------
-2003-10-12
-    Added documentation
-
-2003-04-23
-    Support aliases
-
-2003-03-09
-    Support listening on multiple sockets.
-
-2003-03-08
-    Support for shoutcast source protocol added.
-
-2003-03-08
-    Started implementing generic admin interface. Supports (so far): 
-    - dynamic configuration of mount fallbacks
-        /admin/fallbacks?mount=/mount&fallback=/fallback
-    - setting of mp3 metadata
-        /admin/metadata?mount=/mount&mode=updinfo&song=New%20Title
-    - dumping raw xml stats
-        /admin/rawstats
-    - listing all connected clients on a mountpoint: 
-        /admin/listclients?mount=/mountname
-
-2003-03-05
-    Implemented the ability to reread the config file on SIGHUP. For now, this
-    does not affect configuration for currently running sources (only new
-    sources and global parameters like max-listeners)
-
-2003-03-02 
-    More features:	 
-    -- per mountpoint listener maxima   
-    -- static configuration of mountpoint fallbacks   
-    -- stream dumping (write incoming stream to disk)
-
-2003-02-27
-    Fix log buffering on win32 - previously, logs were never flushed, so they
-    only got output every few tens or hundreds of lines.
-
-2003-02-27
-	Support new icy-audio-info header, to communicate various parameters to
-    clients and yp servers, including sample rate, quality, channels, bitrate
-
-2003-02-25
-    Full support for relaying mp3 metadata (if turned on in config file)
-
-2003-02-25
-    Allow configuration of maximum client queue length (in bytes)
-
-2003-02-14
-    Finished full IPv6 support. 
-
-2003-02-12
-	Allow configuring local mountpoint seperately from remote mountpoint for
-    relays
-
-2003-02-12
-	Per mountpoint usernames and passwords (for sources)
-
-2003-02-11
-	Now that it's been officially assigned, use application/ogg instead of
-	application/x-ogg
-
-2003-02-07
-    Allow relaying of mp3 streams from icecast 1.x and shoutcast
-
-2003-02-07
-    Added ability to configure individual relays (rather than just all streams
-    from a single server).
-
-2003-02-03
-    Added support for YP directory services listings
-	are only used by the yp listing routines
-
-2003-02-03
-    Support command line parameter -b to run in the background (not supported
-    on win32)
-
-2002-12-31 
-    Implement configurable mountpoint fallbacks (on source exit, clients are
-    transferred to another mountpoint automatically, without disconnecting
-    them)
-
-2002-12-31
-    Implemented full mp3 metadata support.
-
-(older stuff is missing from here)
-

Copied: icecast/tags/icecast-2.3.2/NEWS (from rev 14985, icecast/trunk/icecast/NEWS)
===================================================================
--- icecast/tags/icecast-2.3.2/NEWS	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/NEWS	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,361 @@
+Icecast 2.3.2
+-----------------------------------------------------------------------------
+- Character set support.
+Most non-Ogg streams (eg MP3) send metadata as Latin1 but it could be in other
+character sets.  As icecast uses UTF-8, we need to convert to UTF-8 so that web
+pages and stream directories render correctly,
+  . Allow a per-mount <charset> setting.  For when source clients do not
+  indicate which character set is in use.
+  . a charset= parameter to the metadata request.
+  . Default for non-Ogg content is now Latin-1 (aka ISO-8859-1),  Ogg content
+  still use UTF-8.
+
+- Authentication
+  . Each mountpoint authentication is isolated so one mount authentication does
+  not slow another mountpoint authentication down.
+  . XSL pages can require authentication.
+  . Add some sanity checks for incorrect settings.
+
+- Listening socket update
+  . Allow multiple ports to be allocated which allow multiple shoutcast source
+  clients to stream on the same icecast instance.
+  . No internal limit on the number of listen-sockets that can be used.
+  . Allow for a listen-socket to be marked SSL capable. While any client could
+  connect on these, the idea is really for /admin requests.
+  . allow for a <listen-socket> to bind to ipv4 and ipv6. BSDs may require to
+  the use of bind-address.
+
+- XSL update
+  . XSL pages include an xspf link for non-auth streams.
+  . XSL pages in both webroot and adminroot can take a mount= arg to limit
+  which stats are transformed.
+  . XSL files can specify the content-type to return to the client. It is no
+  longer fixed to html
+
+- Updates for stream directory handling.
+  . A stream is not registered with the directory until after the first minute
+  has passed. some streams have been stuck in a connect/disconnect loop.
+  . Increase retry time on failure, if the stream add is rejected then it will
+  be down to a misconfiguration so a long retry delay is performed.
+  . A YP server that is not responding is ignored for a while
+
+- Updates for Win32
+  . Build cleanups,
+  . Newer versions of dependency libs (libxml2/libxslt/libcurl etc)
+  . buffer overflow fixes when many streams are active
+
+- Accept/Ban IP support.
+Now takes filenames for explicitly accepting or denying requests from specified
+IP addresses.  Useful in cases where firewall access is not available.  The
+files, if defined, are re-read automatically if updated.
+
+- A Mountpoint is exported to the slaves even if no mount section is defined for
+it. You can still use the hidden setting to prevent that from happening.
+
+- Relays handle redirection (HTTP 302) if one is received at startup.
+
+- Automatically generate XSPF playlist like we do with M3U, the mountpoint
+extension is .xspf 
+
+- Header updates for proxy handling and certain clients like some shoutcast
+source clients and flash players.
+
+- Added Kate/Skeleton codecs to Ogg handler.
+
+- Various stats cleanups. Added some global stats (server ID, email contact etc).
+
+- Documentation updates.
+
+- The streamlist passed from master to slave had a limited length, so if there
+were many streams then some could of been left out.
+
+- Relay startup/shutdown is cleaner.
+
+- several build cleanups.
+
+- several resource leaks and race conditions fixed
+
+
+Icecast 2.3.1
+-----------------------------------------------------------------------------
+
+- new tag <logsize> in <logging> state the trigger size (in KB) for cycling the
+log files.
+
+- new tag <logarchive> in <logging> enable (1) if you want to use a timestamp
+for an extension when cycling logs. 
+
+- Handling of certain shoutcast source clients is fixed, this typically
+affected NSV source clients
+
+- A race in source shutdown when listeners are authenticated could lead to
+server crash
+
+- An audio glitch was possible in playback of vorbis streams when a new logical
+stream started (eg metadata update).
+
+- stats speedup. Processing large numbers of stats was slow. Typically only
+seen when using lots of streams on the same server.
+
+- 404 responses were being sent back in some places, now 403 is sent back.
+
+- Auth URL now handles the authentication to scripts better.
+  The order in which username/password are selected is
+  1. url is http://user:pass@host:port/....
+  2. <param name="username"> and <param name="password">
+  3. with listener_add/remove, listener supplied username/password is used.
+
+- A streams intro file can now be changed, using HUP, on active streams.
+
+- mount without a name crashed the server
+
+- Various documentation updates
+
+
+Icecast 2.3 
+-----------------------------------------------------------------------------
+**** New features for 2.3.0 ****
+
+- Streaming support for ogg speex, ogg flac, ogg midi
+
+- intro file support - per mount settable
+   Intro files will play when a listener first connects to a stream.  This
+   is designed for station jingles and the like.  If you don't broadcast 
+   in ogg vorbis, you must make sure the bitrate/samplerate/number of 
+   channels match up to your stream.
+
+- on-demand relays, global and per-relay settable
+   On demand relays only connect to the relayed content when there are
+   listeners attached to the relay.  This can save bandwidth in certain cases.
+
+- fallback to file, extends on the intro file handling.
+   With this feature, you can specify a "fallback file" which will be played
+   in a loop and sent your currently connected listeners in the event of a
+   source client disconnect.  This means your listeners stay connected while
+   you fix your disconnect problem.  Same rules regarding bitrate/samplerate/
+   number of channels apply as with intro files.
+
+- new mount-level settings
+   1. public, type/subtype, genre settings, stream description,
+      stream url, stream name, bitrate (override what is sent from the source
+      client)
+   2. mp3 metadata interval
+   3. on-[dis]connect scripts can be stated per-mount, invoked at source 
+      start/stop and take 1 arg which is the mountpoint.
+
+- New URL listener authenticator.
+   This delegates your listener authorization to an external application.
+   URL calls are made on listener connect/disconnect as well as source
+   connect/disconnect.  It is meant for large broadcasters who have existing
+   authentication systems that need to be integrated into.  Included is 
+   an example php-based application that can be used in conjunction with 
+   the url authenticator to manage a simple subscription-based broadcast.
+
+- HTPasswd authenticator uses in-memory structures now.
+
+- On demand files now can be fed through an authenticator
+
+- Update to admin/web xslt interface
+
+- Icecast can now be installed as a win32 service
+
+**** Fixes for 2.3.0 ****
+- real/helix works
+- win32 access log correct
+- stats client is stable now (curl -X STATS http://admin@host:port/)
+- show mountpoints on stats that are inactive but have an active fallback
+- more updates over HUP possible
+- improved stability under heavy load
+- moving clients will no longer sometimes deadlock the server
+- avoid small writes to reduce TCP overhead.
+
+
+Icecast 2.2 
+-----------------------------------------------------------------------------
+****New features for 2.2 (in no particular order):****
+
+- Theora Video support -
+Icecast now supports video streaming via theora.  Currently, we require the latest
+(alpha 4) version of libtheora.  This is an optional compile, so if you don't
+have theora then icecast will safely ignore it
+
+- Shoutcast style source client support -
+Icecast now supports the connection protocol used by the Shoutcast DSP source
+client.  This is the same connection protocol used by their NSV encoding tools.
+This means that not only can you use the Shoutcast DSP to stream to icecast, but
+that you can also stream NSV via their tools.
+
+- AAC is added as a supported streaming format -
+Not too many source clients support streaming in this format, but we support it.
+
+- Cluster password -
+Now you can specify a cluster password as a <mount> option in the config. This
+will allow you to cluster multiple servers/mounts into a single listing on the 
+stream directory.  Note that this is different than "grouping" which groups together
+streams coming from the same physical IP and with the same stream name.  Clusters
+are meant for relays of the same stream and will only be listed *once* in the stream
+directory.  When a listener tunes into a cluster, they will be served an m3u file
+with all the clusters for that stream.
+
+- Playlist Log -
+This is an option setting that will create an audit trail of metadata that comes through
+icecast.  It is a single file that contains information for all mountpoints.
+
+- Range Support for static files -
+We now support seeking in files served off the icecast fserve.
+
+- Metadata Update via Admin -
+We now support metadata updates via the admin interface for both MP3 AND Ogg Vorbis
+streams.
+
+- Per mount hidden stats and YP prevention -
+You many now indicate certains mounts to be excluded (i.e. hidden) from the main
+status.xsl page.  This is useful when using local private relays.  You can also
+override the YP setting (as in disable) on a per-mount basis.  Also useful for
+local private relays.
+
+- Multiple example config files -
+We now have multiple config files for you to use as a base.  A "simple" one for 
+quick-start, and a more detailed "advanced" one with all the features, as well
+as a "shoutcast compatable" one, which shows how you'd config for using the
+shoutcast DSP.
+
+- Relay user/pass -
+You can now specify authentication used by a relay.  This is for the case where
+you have listener authentication enabled for a mountpoint, and want to connect
+a relay to it.
+
+Icecast 2.1 11/04/2004
+-----------------------------------------------------------------------------
+****New features for 2.1 (in no particular order):****
+
+-Listener Authentication-
+
+  Icecast now supports listener authentication.  This provides a mechanism for
+  creating/maintaining users and passwords for listeners.  Currently, we only
+  have implemented a simple, file-based storage for users and passwords.  New
+  authenticators are on the horizon (such as URL-based or possibly MySQL based)
+  New admin pages were also added for the maintenance of users/passwords. Please
+  check the docs for a more detailed description of this new feature.
+
+-Multi-Level Fallbacks-
+ 
+  Multi-level fallbacks allow for specifications of a series of fallback mounts
+  that you can use to automatically move listeners in the event of a source being
+  disconnected.  In the event of new listeners attaching to the source stream that has
+  been disconnected, they are routed to the fallback mount (if specified).
+  Icecast now has fallback-override capability as well which means that in the event
+  of a source stream failure, listeners are moved to the fallback mount, and then
+  automatically recaptured when the original source stream returns.
+
+-Burst-On-Connect-
+
+  This is an new, optional config setting which will send a initial burst of 
+  data to connecting listeners.  This has the effect of reducing 
+  (significantly) the startup buffer latency from the end-user perspective.  
+  This option is enabled by default.
+
+
+****New Enhancements for 2.1****
+
+-Update to admin interface-
+
+  This interface has been cleaned up quite a bit and made a bit nicer.
+
+-Rewrite of the YP listing code-
+
+  The icecast yp code has received a complete overhaul by karl, and it's a much
+  more stable and failure-resistant implementation.
+
+-Lots and lots of bugs fixed-
+
+  Check the ChangeLog for a complete list of these...
+
+-----------------------------------------------------------------------------
+2003-10-12
+    Added documentation
+
+2003-04-23
+    Support aliases
+
+2003-03-09
+    Support listening on multiple sockets.
+
+2003-03-08
+    Support for shoutcast source protocol added.
+
+2003-03-08
+    Started implementing generic admin interface. Supports (so far): 
+    - dynamic configuration of mount fallbacks
+        /admin/fallbacks?mount=/mount&fallback=/fallback
+    - setting of mp3 metadata
+        /admin/metadata?mount=/mount&mode=updinfo&song=New%20Title
+    - dumping raw xml stats
+        /admin/rawstats
+    - listing all connected clients on a mountpoint: 
+        /admin/listclients?mount=/mountname
+
+2003-03-05
+    Implemented the ability to reread the config file on SIGHUP. For now, this
+    does not affect configuration for currently running sources (only new
+    sources and global parameters like max-listeners)
+
+2003-03-02 
+    More features:	 
+    -- per mountpoint listener maxima   
+    -- static configuration of mountpoint fallbacks   
+    -- stream dumping (write incoming stream to disk)
+
+2003-02-27
+    Fix log buffering on win32 - previously, logs were never flushed, so they
+    only got output every few tens or hundreds of lines.
+
+2003-02-27
+	Support new icy-audio-info header, to communicate various parameters to
+    clients and yp servers, including sample rate, quality, channels, bitrate
+
+2003-02-25
+    Full support for relaying mp3 metadata (if turned on in config file)
+
+2003-02-25
+    Allow configuration of maximum client queue length (in bytes)
+
+2003-02-14
+    Finished full IPv6 support. 
+
+2003-02-12
+	Allow configuring local mountpoint seperately from remote mountpoint for
+    relays
+
+2003-02-12
+	Per mountpoint usernames and passwords (for sources)
+
+2003-02-11
+	Now that it's been officially assigned, use application/ogg instead of
+	application/x-ogg
+
+2003-02-07
+    Allow relaying of mp3 streams from icecast 1.x and shoutcast
+
+2003-02-07
+    Added ability to configure individual relays (rather than just all streams
+    from a single server).
+
+2003-02-03
+    Added support for YP directory services listings
+	are only used by the yp listing routines
+
+2003-02-03
+    Support command line parameter -b to run in the background (not supported
+    on win32)
+
+2002-12-31 
+    Implement configurable mountpoint fallbacks (on source exit, clients are
+    transferred to another mountpoint automatically, without disconnecting
+    them)
+
+2002-12-31
+    Implemented full mp3 metadata support.
+
+(older stuff is missing from here)
+

Deleted: icecast/tags/icecast-2.3.2/README
===================================================================
--- icecast/trunk/icecast/README	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/README	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,56 +0,0 @@
-icecast 2.x - README
----------------------------------------------------------------------
-
-Icecast is a streaming media server which currently supports Ogg 
-Vorbis and MP3 audio streams. It can be used to create an Internet 
-radio station or a privately running jukebox and many things in 
-between. It is very versatile in that new formats can be added 
-relatively easily and supports open standards for commuincation and 
-interaction.
-
-Icecast is distributed under the GNU GPL, version 2. A copy of this
-license is included with this software in the COPYING file.
-
-Prerequisites
----------------------------------------------------------------------
-icecast requires the following packages :
-
-* libxml2 - http://xmlsoft.org/downloads.html
-* libxslt - http://xmlsoft.org/XSLT/downloads.html
-* curl - http://curl.haxx.se/download.html (>= version 7.10 required)
-  NOTE: icecast may be compiled without curl, however this will
-        disable all Directory server interaction (YP).
-* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required)
-
-A Note About RPMS
----------------------------------------------------------------------
-This section only applies to you if your operating system uses RPMS.
-
-In order to build icecast, you will need to install the "devel" RPM
-packages for each of the prerequisite packages in addition to the
-normal RPMS for each package.
-
-please check the websites for each of the prerequisite packages for
-appropriate download links for RPMS.
-
-
-Build/Install
----------------------------------------------------------------------
-To build icecast on a Unix platform, perform the following :
-
-Run
-   ./configure
-   make
-   make install
-
-To build and install this release.
-
-A sample config file will be placed in /usr/local/etc (on UNIX) or in 
-the current working directory (on Win32) and is called icecast.xml
-
-Documentation for icecast is available in the doc directory, by 
-viewing doc/index.html in a browser.
-
-Please email us at icecast at xiph.org or icecast-dev at xiph.org, or come and see
-us at irc.freenode.net, channel #icecast, if you have any troubles.
-

Copied: icecast/tags/icecast-2.3.2/README (from rev 14985, icecast/trunk/icecast/README)
===================================================================
--- icecast/tags/icecast-2.3.2/README	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/README	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,57 @@
+icecast 2.x - README
+---------------------------------------------------------------------
+
+Icecast is a streaming media server which currently supports Ogg
+streaming including the vorbis and theora codecs. Also icecast can
+handle other streams like MP3/AAC/NSV.
+It can be used to create an Internet radio station or a privately
+running jukebox and many things in between. It is very versatile in
+that new formats can be added relatively easily and supports open
+standards for communication and interaction.
+
+Icecast is distributed under the GNU GPL, version 2. A copy of this
+license is included with this software in the COPYING file.
+
+Prerequisites
+---------------------------------------------------------------------
+icecast requires the following packages :
+
+* libxml2 - http://xmlsoft.org/downloads.html
+* libxslt - http://xmlsoft.org/XSLT/downloads.html
+* curl - http://curl.haxx.se/download.html (>= version 7.10 required)
+  NOTE: icecast may be compiled without curl, however this will
+        disable Stream Directory server interaction (YP) and URL based 
+        authentication.
+* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required)
+
+A note about prerequisite packages
+---------------------------------------------------------------------
+Most distributions have some sort of package management repository for
+pre-built packages (eg rpm, deb etc).  These setups often have a runtime
+package, which is usually installed for you by default, and enables you
+to run applications that depend on them.  However if you are building
+icecast from source then the runtime system is not enough. You will also
+need a development package named something like libxslt-devel
+
+Build/Install
+---------------------------------------------------------------------
+To build icecast on a Unix platform, perform the following :
+
+Run
+   ./configure
+   make
+   make install
+
+This is the typical procedure if you download the tar file.  If you retrive
+the code from subversion or want to rebuild the configure then run autogen.sh
+instead of the configure above. Most people do not need to run autogen.sh
+
+A sample config file will be placed in /usr/local/etc (on UNIX) or in 
+the current working directory (on Win32) and is called icecast.xml
+
+Documentation for icecast is available in the doc directory, by 
+viewing doc/index.html in a browser.
+
+Please email us at icecast at xiph.org or icecast-dev at xiph.org, or come and see
+us at irc.freenode.net, channel #icecast, if you have any troubles.
+

Copied: icecast/tags/icecast-2.3.2/admin (from rev 13533, icecast/trunk/icecast/admin)

Deleted: icecast/tags/icecast-2.3.2/admin/listclients.xsl
===================================================================
--- icecast/trunk/icecast/admin/listclients.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/listclients.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,95 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-
-
-<h1>Listener Stats</h1>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<xsl:for-each select="source">
-<h3>
-
-<xsl:choose>
-<xsl:when test="authenticator">
-<a href="/auth.xsl"><img border="0" src="/key.png"/></a>
-</xsl:when>
-<xsl:otherwise>
-<a href="{@mount}.m3u"><img border="0" src="/tunein.png"/></a>
-</xsl:otherwise>
-</xsl:choose>
-<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
-Mount Point : (<xsl:value-of select="@mount" />)
-</h3>
-	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
-	<tr>        
-	    <td align="center">
-			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> | 
-        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
-			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
-        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
-	    </td></tr>
-	</table>
-<br />
-<table cellspacing="1" border="1" bordercolor="#C0C0C0" >
-		<tr>
-				<td ><center><b>IP</b></center></td>
-				<td ><center><b>Connected For</b></center></td>
-				<td ><center><b>User Agent</b></center></td>
-				<td ><center><b>Action</b></center></td>
-		</tr>
-<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
-<xsl:for-each select="listener">
-		<tr>
-				<td align="center"><xsl:value-of select="IP" /><xsl:if test="username"> (<xsl:value-of select="username" />)</xsl:if></td>
-				<td align="center"><xsl:value-of select="Connected" /> seconds</td>
-				<td align="center"><xsl:value-of select="UserAgent" /></td>
-				<td align="center"><a href="killclient.xsl?mount={$themount}&amp;id={ID}">Kick</a></td>
-		</tr>
-</xsl:for-each>
-</table>
-<br />
-<br />
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/listclients.xsl (from rev 14850, icecast/trunk/icecast/admin/listclients.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/listclients.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/listclients.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,101 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+
+
+<h1>Listener Stats</h1>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<div class="streamheader">
+    <table cellspacing="0" cellpadding="0" >
+        <colgroup align="left" />
+        <colgroup align="right" width="300" />
+        <tr>
+            <td><h3>Mount Point <xsl:value-of select="@mount" /></h3></td>
+            <xsl:choose>
+                <xsl:when test="authenticator">
+                    <td align="right"><a class="auth" href="/auth.xsl">Login</a></td>
+                </xsl:when>
+                <xsl:otherwise>
+                    <td align="right">
+                        <a href="{@mount}.m3u">M3U</a>
+                        <a href="{@mount}.xspf">XSPF</a></td>
+                </xsl:otherwise>
+            </xsl:choose>
+    </tr></table>
+</div>
+
+<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>        
+	    <td align="center">
+			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a>
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a>
+			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a>
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+	    </td></tr>
+	</table>
+<br />
+<table cellspacing="0" cellpadding="5" border="1" bordercolor="#C0C0C0" >
+		<tr>
+				<td ><center><b>IP</b></center></td>
+				<td ><center><b>Seconds Connected</b></center></td>
+				<td ><center><b>User Agent</b></center></td>
+				<td ><center><b>Action</b></center></td>
+		</tr>
+<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
+<xsl:for-each select="listener">
+		<tr>
+				<td align="center"><xsl:value-of select="IP" /><xsl:if test="username"> (<xsl:value-of select="username" />)</xsl:if></td>
+				<td align="center"><xsl:value-of select="Connected" /></td>
+				<td align="center"><xsl:value-of select="UserAgent" /></td>
+				<td align="center"><a href="killclient.xsl?mount={$themount}&amp;id={ID}">Kick</a></td>
+		</tr>
+</xsl:for-each>
+</table>
+<br />
+<br />
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/listmounts.xsl
===================================================================
--- icecast/trunk/icecast/admin/listmounts.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/listmounts.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,77 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-
-<h1>Active Mountpoints</h1>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<xsl:for-each select="source">
-<h3>
-<xsl:choose>
-<xsl:when test="authenticator">
-<a href="/auth.xsl"><img border="0" src="/key.png"/></a>
-</xsl:when>
-<xsl:otherwise>
-<a href="{@mount}.m3u"><img border="0" src="/tunein.png"/></a>
-</xsl:otherwise>
-</xsl:choose>
-<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
-Mount Point : (<xsl:value-of select="@mount" />)
-</h3>
-	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
-	<tr>        
-	    <td align="center">
-			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> | 
-        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
-			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
-        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
-                <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
-	    </td></tr>
-	</table>
-<br />
-<p><xsl:value-of select="listeners" /> Listener(s)</p>
-<br></br>
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/listmounts.xsl (from rev 14850, icecast/trunk/icecast/admin/listmounts.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/listmounts.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/listmounts.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,84 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+
+<h1>Active Mountpoints</h1>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<div class="streamheader">
+    <table cellspacing="0" cellpadding="0" >
+        <colgroup align="left" />
+        <colgroup align="right" width="300" />
+        <tr>
+            <td><h3>Mount Point <xsl:value-of select="@mount" /></h3></td>
+            <xsl:choose>
+                <xsl:when test="authenticator">
+                    <td align="right"><a class="auth" href="/auth.xsl">Login</a></td>
+                </xsl:when>
+                <xsl:otherwise>
+                    <td align="right">
+                        <a href="{@mount}.m3u">M3U</a>
+                        <a href="{@mount}.xspf">XSPF</a></td>
+                </xsl:otherwise>
+            </xsl:choose>
+    </tr></table>
+</div>
+
+<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>        
+	    <td align="center">
+			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a>
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a>
+			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a>
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+                <xsl:if test="authenticator"><a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
+	    </td></tr>
+	</table>
+<br />
+<p><xsl:value-of select="listeners" /> Listener(s)</p>
+<br></br>
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/manageauth.xsl
===================================================================
--- icecast/trunk/icecast/admin/manageauth.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/manageauth.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,99 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-<h2>Show defined users</h2>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<xsl:for-each select="iceresponse">
-<xsl:value-of select="message" /> 
-</xsl:for-each>
-<xsl:for-each select="source">
-<h3>
-<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
-(<xsl:value-of select="@mount" />)</h3>
-	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
-	<tr>        
-	    <td align="center">
-			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> |
-        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
-			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
-        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
-	    </td></tr>
-	</table>
-<br></br>
-<form method="GET" action="manageauth.xsl">
-<table cellpadding="2" cellspacing="4" border="0" >
-		<tr>
-				<td ><b>User Id</b></td>
-				<td ></td>
-		</tr>
-<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
-<xsl:for-each select="User">
-		<tr>
-				<td><xsl:value-of select="username" /></td>
-				<td><a class="nav2" href="manageauth.xsl?mount={$themount}&amp;username={username}&amp;action=delete">delete</a></td>
-		</tr>
-</xsl:for-each>
-</table>
-<table cellpadding="2" cellspacing="4" border="0" >
-		<tr>
-				<td ><b>User Id</b></td>
-				<td ><b>Password</b></td>
-		</tr>
-		<tr>
-				<td ><input type="text" name="username" /></td>
-				<td ><input type="text" name="password" /></td>
-		</tr>
-		<tr>
-				<td colspan="2"><input type="Submit" name="Submit" value="Add New User" /></td>
-		</tr>
-</table>
-<input type="hidden" name="mount" value="{@mount}"/>
-<input type="hidden" name="action" value="add"/>
-</form>
-<br />
-<br />
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/manageauth.xsl (from rev 14850, icecast/trunk/icecast/admin/manageauth.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/manageauth.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/manageauth.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,98 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="iceresponse">
+<xsl:value-of select="message" /> 
+</xsl:for-each>
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)</h3>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>        
+	    <td align="center">
+			<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a>
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a>
+			<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a>
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+	    </td></tr>
+	</table>
+<br></br>
+<form method="GET" action="manageauth.xsl">
+<table cellpadding="2" cellspacing="4" border="0" >
+		<tr>
+				<td ><b>User Id</b></td>
+				<td ></td>
+		</tr>
+<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
+<xsl:for-each select="User">
+		<tr>
+				<td><xsl:value-of select="username" /></td>
+				<td><a class="nav2" href="manageauth.xsl?mount={$themount}&amp;username={username}&amp;action=delete">delete</a></td>
+		</tr>
+</xsl:for-each>
+</table>
+<table cellpadding="2" cellspacing="4" border="0" >
+		<tr>
+				<td ><b>User Id</b></td>
+				<td ><b>Password</b></td>
+		</tr>
+		<tr>
+				<td ><input type="text" name="username" /></td>
+				<td ><input type="text" name="password" /></td>
+		</tr>
+		<tr>
+				<td colspan="2"><input type="Submit" name="Submit" value="Add New User" /></td>
+		</tr>
+</table>
+<input type="hidden" name="mount" value="{@mount}"/>
+<input type="hidden" name="action" value="add"/>
+</form>
+<br />
+<br />
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/moveclients.xsl
===================================================================
--- icecast/trunk/icecast/admin/moveclients.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/moveclients.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,63 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-<xsl:variable name = "currentmount" ><xsl:value-of select="current_source" /></xsl:variable>
-<h1>Moving Listeners From (<xsl:value-of select="current_source" />)</h1>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>Move to which mountpoint ?</h3>
-<xsl:for-each select="source">
-	<table border="0" cellpadding="1" cellspacing="5" >
-	<tr>        
-		<td>Move from (<xsl:copy-of select="$currentmount" />) to (<xsl:value-of select="@mount" />)</td>
-		<td><xsl:value-of select="listeners" /> Listeners</td>
-		<td><a class="nav2" href="moveclients.xsl?mount={$currentmount}&amp;destination={@mount}">Move Clients</a></td>
-	</tr>        
-	</table>
-<br />
-<br />
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/moveclients.xsl (from rev 14850, icecast/trunk/icecast/admin/moveclients.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/moveclients.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/moveclients.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,63 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+<xsl:variable name = "currentmount" ><xsl:value-of select="current_source" /></xsl:variable>
+<h1>Moving Listeners From (<xsl:value-of select="current_source" />)</h1>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Move to which mountpoint ?</h3>
+<xsl:for-each select="source">
+	<table border="0" cellpadding="1" cellspacing="5" >
+	<tr>        
+		<td>Move from (<xsl:copy-of select="$currentmount" />) to (<xsl:value-of select="@mount" />)</td>
+		<td><xsl:value-of select="listeners" /> Listeners</td>
+		<td><a class="nav2" href="moveclients.xsl?mount={$currentmount}&amp;destination={@mount}">Move Clients</a></td>
+	</tr>        
+	</table>
+<br />
+<br />
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/response.xsl
===================================================================
--- icecast/trunk/icecast/admin/response.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/response.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,56 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/iceresponse" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-<h1>Icecast Server Response</h1>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>Response</h3>
-<xsl:for-each select="/iceresponse">
-Message : <xsl:value-of select="message" /><br></br>
-Return Code: <xsl:value-of select="return" /><br></br>
-</xsl:for-each>
-<br />
-<br />
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/response.xsl (from rev 13534, icecast/trunk/icecast/admin/response.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/response.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/response.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,56 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/iceresponse" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a> |
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+<h1>Icecast Server Response</h1>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Response</h3>
+<xsl:for-each select="/iceresponse">
+Message : <xsl:value-of select="message" /><br></br>
+Return Code: <xsl:value-of select="return" /><br></br>
+</xsl:for-each>
+<br />
+<br />
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/stats.xsl
===================================================================
--- icecast/trunk/icecast/admin/stats.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/stats.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,117 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-<!--header menu -->
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> | 
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-<!--end header menu -->
-
-<!--global server stats-->
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>Global Server Stats</h3>
-<table border="0" cellpadding="4">
-<xsl:for-each select="/icestats">
-<xsl:for-each select="*">
-<xsl:if test = "name()!='source'"> 
-<tr>
-	<td width="130"><xsl:value-of select="name()" /></td>
-	<td class="streamdata"><xsl:value-of select="." /></td>
-</tr>
-</xsl:if>
-</xsl:for-each>
-</xsl:for-each>
-</table>
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-<!--end global server stats-->
-
-<!--mount point stats-->
-<xsl:for-each select="source">
-<xsl:if test = "listeners!=''"> 
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>
-<xsl:choose>
-<xsl:when test="authenticator">
-<a href="/auth.xsl"><img border="0" src="/key.png"/></a>
-</xsl:when>
-<xsl:otherwise>
-<a href="{@mount}.m3u"><img border="0" src="/tunein.png"/></a>
-</xsl:otherwise>
-</xsl:choose>
-Mount Point : (<xsl:value-of select="@mount" />)
-</h3>
-	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
-	<tr>        
-	    <td align="center">
-		    <a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> | 
-        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a> | 
-        	<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
-        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
-                <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
-	    </td></tr>
-	</table>
-<br />
-<table cellpadding="5" cellspacing="0" border="0">
-	<xsl:for-each select="*">
-	<tr>
-		<td width="130"><xsl:value-of select="name()" /></td>
-		<td class="streamdata"><xsl:value-of select="." /></td>
-	</tr>
-	</xsl:for-each>
-</table>
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-</xsl:if>
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-
-
-<!--end mount point stats-->
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/stats.xsl (from rev 14850, icecast/trunk/icecast/admin/stats.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/stats.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/stats.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,124 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+<!--header menu -->
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+<!--end header menu -->
+
+<!--global server stats-->
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Global Server Stats</h3>
+<table border="0" cellpadding="4">
+<xsl:for-each select="/icestats">
+<xsl:for-each select="*">
+<xsl:if test = "name()!='source'"> 
+<tr>
+	<td width="130"><xsl:value-of select="name()" /></td>
+	<td class="streamdata"><xsl:value-of select="." /></td>
+</tr>
+</xsl:if>
+</xsl:for-each>
+</xsl:for-each>
+</table>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+<!--end global server stats-->
+
+<!--mount point stats-->
+<xsl:for-each select="source">
+<xsl:if test = "listeners!=''"> 
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+    <div class="streamheader">
+        <table cellspacing="0" cellpadding="0" >
+            <colgroup align="left" />
+            <colgroup align="right" width="300" />
+            <tr>
+                <td><h3>Mount Point <xsl:value-of select="@mount" /></h3></td>
+                <xsl:choose>
+                    <xsl:when test="authenticator">
+                        <td align="right"><a class="auth" href="/auth.xsl">Login</a></td>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <td align="right">
+                            <a href="{@mount}.m3u">M3U</a>
+                            <a href="{@mount}.xspf">XSPF</a></td>
+                    </xsl:otherwise>
+                </xsl:choose>
+        </tr></table>
+    </div>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>        
+	    <td align="center">
+		    <a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a>
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a>
+        	<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a>
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+                <xsl:if test="authenticator"><a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
+	    </td></tr>
+	</table>
+<br />
+<table cellpadding="5" cellspacing="0" border="0">
+	<xsl:for-each select="*">
+	<tr>
+		<td width="130"><xsl:value-of select="name()" /></td>
+		<td class="streamdata"><xsl:value-of select="." /></td>
+	</tr>
+	</xsl:for-each>
+</table>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+</xsl:if>
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+
+
+<!--end mount point stats-->
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl
===================================================================
--- icecast/trunk/icecast/admin/updatemetadata.xsl	2007-08-10 21:33:16 UTC (rev 13533)
+++ icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,66 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="/style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Admin</center></h2>
-<br />
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-	<table border="0" cellpadding="1" cellspacing="3">
-	<tr>        
-	    <td align="center">
-	        <a class="nav" href="stats.xsl">Admin Home</a> |
-		    <a class="nav" href="listmounts.xsl">List Mountpoints</a> | 
-        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
-        	<a class="nav" href="/status.xsl">Index</a>
-	    </td></tr>
-	</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-<h1>Update Metadata</h1>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<xsl:for-each select="source">
-<h3>
-<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
-(<xsl:value-of select="@mount" />)</h3>
-
-<form method="GET" action="/admin/metadata.xsl">
-<table border="0" cellpadding="4">
-<tr><td>Metadata : <input type="text" name="song"/></td></tr>
-<tr><td><input type="Submit" value="Update"/></td></tr>
-</table>
-<input type="hidden" name="mount" value="{@mount}"/>
-<input type="hidden" name="mode" value="updinfo"/>
-</form>
-
-<br />
-<br />
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl (from rev 14850, icecast/trunk/icecast/admin/updatemetadata.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/updatemetadata.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,67 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Admin</h2>
+<br />
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+	        <a class="nav" href="stats.xsl">Admin Home</a>
+		    <a class="nav" href="listmounts.xsl">List Mountpoints</a>
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a>
+        	<a class="nav" href="/status.xsl">Index</a>
+	    </td></tr>
+	</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+<h1>Update Metadata</h1>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)</h3>
+
+<form method="GET" action="/admin/metadata.xsl">
+<table border="0" cellpadding="4">
+<tr><td>Metadata : <input type="text" name="song"/></td></tr>
+<tr><td><input type="Submit" value="Update"/></td></tr>
+</table>
+<input type="hidden" name="mount" value="{@mount}"/>
+<input type="hidden" name="mode" value="updinfo"/>
+<input type="hidden" name="charset" value="UTF-8"/>
+</form>
+
+<br />
+<br />
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/admin/xspf.xsl (from rev 13539, icecast/trunk/icecast/admin/xspf.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/admin/xspf.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/admin/xspf.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,73 @@
+<!--
+    XSPF xslt stylesheet for Icecast 2.3.1 and above
+    Copyright (C) 2007 Thomas B. Ruecker, tbr at ruecker-itk.de
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License
+    as published by the Free Software Foundation; either version 2
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the
+    Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+-->
+
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" media-type="application/xspf+xml"
+        method="xml" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<playlist version="1" xmlns="http://xspf.org/ns/0/">
+	<title><xsl:value-of select="server" /></title>
+	<creator><xsl:value-of select="server" /></creator>
+	<trackList >
+<!-- end of "header" -->
+
+<xsl:for-each select="source">
+
+<!-- do we need to do something about streams that need auth?-->
+
+<track>
+    <location><xsl:value-of select="listenurl" /></location>
+
+
+<xsl:if test="artist"><creator><xsl:value-of select="artist" /></creator></xsl:if>
+<title><xsl:value-of select="title" /></title>
+<!-- The <xsl:text>\n</xsl:text> elements in the following part are used 
+to enforce linebreaks this format seems to be expected by clients -->
+<annotation>
+<xsl:if test="server_name">Stream Title: <xsl:value-of select="server_name" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="server_description">Stream Description: <xsl:value-of select="server_description" /></xsl:if>
+Content Type:<xsl:value-of select="server_type" /><xsl:text>
+</xsl:text>
+<xsl:if test="bitrate">Bitrate: <xsl:value-of select="bitrate" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="quality">Quality: <xsl:value-of select="quality" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="video_quality">Video Quality: <xsl:value-of select="video_quality" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="frame_size">Framesize: <xsl:value-of select="frame_size" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="frame_rate">Framerate: <xsl:value-of select="frame_rate" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="listeners">Current Listeners: <xsl:value-of select="listeners" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="listener_peak">Peak Listeners: <xsl:value-of select="listener_peak" /><xsl:text>
+</xsl:text></xsl:if>
+<xsl:if test="genre">Stream Genre: <xsl:value-of select="genre" /></xsl:if>
+</annotation>
+
+<xsl:if test="server_url"><info><xsl:value-of select="server_url" /></info></xsl:if>
+
+</track>
+
+</xsl:for-each>
+</trackList>
+</playlist>
+
+</xsl:template>
+</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/conf (from rev 14436, icecast/trunk/icecast/conf)

Deleted: icecast/tags/icecast-2.3.2/conf/icecast.xml.in
===================================================================
--- icecast/trunk/icecast/conf/icecast.xml.in	2008-01-26 16:42:37 UTC (rev 14436)
+++ icecast/tags/icecast-2.3.2/conf/icecast.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,177 +0,0 @@
-<icecast>
-    <limits>
-        <clients>100</clients>
-        <sources>2</sources>
-        <threadpool>5</threadpool>
-        <queue-size>524288</queue-size>
-        <client-timeout>30</client-timeout>
-        <header-timeout>15</header-timeout>
-        <source-timeout>10</source-timeout>
-        <!-- If enabled, this will provide a burst of data when a client 
-             first connects, thereby significantly reducing the startup 
-             time for listeners that do substantial buffering. However,
-             it also significantly increases latency between the source
-             client and listening client.  For low-latency setups, you
-             might want to disable this. -->
-        <burst-on-connect>1</burst-on-connect>
-        <!-- same as burst-on-connect, but this allows for being more
-             specific on how much to burst. Most people won't need to
-             change from the default 64k. Applies to all mountpoints  -->
-        <burst-size>65535</burst-size>
-    </limits>
-
-    <authentication>
-        <!-- Sources log in with username 'source' -->
-        <source-password>hackme</source-password>
-        <!-- Relays log in username 'relay' -->
-        <relay-password>hackme</relay-password>
-
-        <!-- Admin logs in with the username given below -->
-        <admin-user>admin</admin-user>
-        <admin-password>hackme</admin-password>
-    </authentication>
-
-    <!-- Uncomment this if you want directory listings -->
-    <!--
-    <directory>
-        <yp-url-timeout>15</yp-url-timeout>
-        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
-    </directory>
-     -->
-
-    <!-- This is the hostname other people will use to connect to your server.
-    It affects mainly the urls generated by Icecast for playlists and yp
-    listings. -->
-    <hostname>localhost</hostname>
-
-    <!-- You can use these two if you only want a single listening socket -->
-    <!--<port>8000</port> -->
-    <!--<bind-address>127.0.0.1</bind-address>-->
-
-    <!-- You may have multiple <listener> elements -->
-    <listen-socket>
-        <port>8000</port>
-        <!-- <bind-address>127.0.0.1</bind-address> -->
-    </listen-socket>
-    <!--
-    <listen-socket>
-        <port>8001</port>
-    </listen-socket>
-    -->
-
-    <!--<master-server>127.0.0.1</master-server>-->
-    <!--<master-server-port>8001</master-server-port>-->
-    <!--<master-update-interval>120</master-update-interval>-->
-    <!--<master-password>hackme</master-password>-->
-
-    <!-- setting this makes all relays on-demand unless overridden, this is
-         useful for master relays which do not have <relay> definitions here.
-         The default is 0 -->
-    <!--<relays-on-demand>1</relays-on-demand>-->
-
-    <!--
-    <relay>
-        <server>127.0.0.1</server>
-        <port>8001</port>
-        <mount>/example.ogg</mount>
-        <local-mount>/different.ogg</local-mount>
-        <on-demand>0</on-demand>
-
-        <relay-shoutcast-metadata>0</relay-shoutcast-metadata>
-    </relay>
-    -->
-
-    <!-- Only define a <mount> section if you want to use advanced options,
-         like alternative usernames or passwords
-    <mount>
-        <mount-name>/example-complex.ogg</mount-name>
-
-        <username>othersource</username>
-        <password>hackmemore</password>
-
-        <max-listeners>1</max-listeners>
-        <dump-file>/tmp/dump-example1.ogg</dump-file>
-        <burst-size>65536</burst-size>
-        <fallback-mount>/example2.ogg</fallback-mount>
-        <fallback-override>1</fallback-override>
-        <fallback-when-full>1</fallback-when-full>
-        <intro>/example_intro.ogg</intro>
-        <hidden>1</hidden>
-        <no-yp>1</no-yp>
-        <authentication type="htpasswd">
-                <option name="filename" value="myauth"/>
-                <option name="allow_duplicate_users" value="0"/>
-        </authentication>
-        <on-connect>/home/icecast/bin/stream-start</on-connect>
-        <on-disconnect>/home/icecast/bin/stream-stop</on-disconnect>
-    </mount>
-
-    <mount>
-        <mount-name>/auth_example.ogg</mount-name>
-        <authentication type="url">
-            <option name="mount_add"       value="http://myauthserver.net/notify_mount.php"/>
-            <option name="mount_remove"    value="http://myauthserver.net/notify_mount.php"/>
-            <option name="listener_add"    value="http://myauthserver.net/notify_listener.php"/>
-            <option name="listener_remove" value="http://myauthserver.net/notify_listener.php"/>
-        </authentication>
-    </mount>
-
-    -->
-
-    <fileserve>1</fileserve>
-
-    <!-- set the mountpoint for a shoutcast source to use, the default if not
-         specified is /stream but you can change it here if an alternative is
-         wanted or an extension is required
-    <shoutcast-mount>/live.nsv</shoutcast-mount>
-    -->
-
-    <paths>
-		<!-- basedir is only used if chroot is enabled -->
-        <basedir>@pkgdatadir@</basedir>
-
-        <!-- Note that if <chroot> is turned on below, these paths must both
-             be relative to the new root, not the original root -->
-        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
-        <webroot>@pkgdatadir@/web</webroot>
-        <adminroot>@pkgdatadir@/admin</adminroot>
-        <!-- <pidfile>@pkgdatadir@/icecast.pid</pidfile> -->
-
-        <!-- Aliases: treat requests for 'source' path as being for 'dest' path
-             May be made specific to a port or bound address using the "port"
-             and "bind-address" attributes.
-          -->
-        <!--
-        <alias source="/foo" dest="/bar"/>
-          -->
-        <!-- Aliases: can also be used for simple redirections as well,
-             this example will redirect all requests for http://server:port/ to
-             the status page
-          -->
-        <alias source="/" dest="/status.xsl"/>
-    </paths>
-
-    <logging>
-        <accesslog>access.log</accesslog>
-        <errorlog>error.log</errorlog>
-        <!-- <playlistlog>playlist.log</playlistlog> -->
-      	<loglevel>4</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
-      	<logsize>10000</logsize> <!-- Max size of a logfile -->
-        <!-- If logarchive is enabled (1), then when logsize is reached
-             the logfile will be moved to [error|access|playlist].log.DATESTAMP,
-             otherwise it will be moved to [error|access|playlist].log.old.
-             Default is non-archive mode (i.e. overwrite)
-        -->
-        <!-- <logarchive>1</logarchive> -->
-    </logging>
-
-    <security>
-        <chroot>0</chroot>
-        <!--
-        <changeowner>
-            <user>nobody</user>
-            <group>nogroup</group>
-        </changeowner>
-        -->
-    </security>
-</icecast>

Copied: icecast/tags/icecast-2.3.2/conf/icecast.xml.in (from rev 14777, icecast/trunk/icecast/conf/icecast.xml.in)
===================================================================
--- icecast/tags/icecast-2.3.2/conf/icecast.xml.in	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/conf/icecast.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,174 @@
+<icecast>
+    <limits>
+        <clients>100</clients>
+        <sources>2</sources>
+        <threadpool>5</threadpool>
+        <queue-size>524288</queue-size>
+        <client-timeout>30</client-timeout>
+        <header-timeout>15</header-timeout>
+        <source-timeout>10</source-timeout>
+        <!-- If enabled, this will provide a burst of data when a client 
+             first connects, thereby significantly reducing the startup 
+             time for listeners that do substantial buffering. However,
+             it also significantly increases latency between the source
+             client and listening client.  For low-latency setups, you
+             might want to disable this. -->
+        <burst-on-connect>1</burst-on-connect>
+        <!-- same as burst-on-connect, but this allows for being more
+             specific on how much to burst. Most people won't need to
+             change from the default 64k. Applies to all mountpoints  -->
+        <burst-size>65535</burst-size>
+    </limits>
+
+    <authentication>
+        <!-- Sources log in with username 'source' -->
+        <source-password>hackme</source-password>
+        <!-- Relays log in username 'relay' -->
+        <relay-password>hackme</relay-password>
+
+        <!-- Admin logs in with the username given below -->
+        <admin-user>admin</admin-user>
+        <admin-password>hackme</admin-password>
+    </authentication>
+
+    <!-- set the mountpoint for a shoutcast source to use, the default if not
+         specified is /stream but you can change it here if an alternative is
+         wanted or an extension is required
+    <shoutcast-mount>/live.nsv</shoutcast-mount>
+    -->
+
+    <!-- Uncomment this if you want directory listings -->
+    <!--
+    <directory>
+        <yp-url-timeout>15</yp-url-timeout>
+        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
+    </directory>
+     -->
+
+    <!-- This is the hostname other people will use to connect to your server.
+    It affects mainly the urls generated by Icecast for playlists and yp
+    listings. -->
+    <hostname>localhost</hostname>
+
+    <!-- You may have multiple <listener> elements -->
+    <listen-socket>
+        <port>8000</port>
+        <!-- <bind-address>127.0.0.1</bind-address> -->
+        <!-- <shoutcast-mount>/stream</shoutcast-mount> -->
+    </listen-socket>
+    <!--
+    <listen-socket>
+        <port>8001</port>
+    </listen-socket>
+    -->
+
+    <!--<master-server>127.0.0.1</master-server>-->
+    <!--<master-server-port>8001</master-server-port>-->
+    <!--<master-update-interval>120</master-update-interval>-->
+    <!--<master-password>hackme</master-password>-->
+
+    <!-- setting this makes all relays on-demand unless overridden, this is
+         useful for master relays which do not have <relay> definitions here.
+         The default is 0 -->
+    <!--<relays-on-demand>1</relays-on-demand>-->
+
+    <!--
+    <relay>
+        <server>127.0.0.1</server>
+        <port>8001</port>
+        <mount>/example.ogg</mount>
+        <local-mount>/different.ogg</local-mount>
+        <on-demand>0</on-demand>
+
+        <relay-shoutcast-metadata>0</relay-shoutcast-metadata>
+    </relay>
+    -->
+
+    <!-- Only define a <mount> section if you want to use advanced options,
+         like alternative usernames or passwords
+    <mount>
+        <mount-name>/example-complex.ogg</mount-name>
+
+        <username>othersource</username>
+        <password>hackmemore</password>
+
+        <max-listeners>1</max-listeners>
+        <dump-file>/tmp/dump-example1.ogg</dump-file>
+        <burst-size>65536</burst-size>
+        <fallback-mount>/example2.ogg</fallback-mount>
+        <fallback-override>1</fallback-override>
+        <fallback-when-full>1</fallback-when-full>
+        <intro>/example_intro.ogg</intro>
+        <hidden>1</hidden>
+        <no-yp>1</no-yp>
+        <authentication type="htpasswd">
+                <option name="filename" value="myauth"/>
+                <option name="allow_duplicate_users" value="0"/>
+        </authentication>
+        <on-connect>/home/icecast/bin/stream-start</on-connect>
+        <on-disconnect>/home/icecast/bin/stream-stop</on-disconnect>
+    </mount>
+
+    <mount>
+        <mount-name>/auth_example.ogg</mount-name>
+        <authentication type="url">
+            <option name="mount_add"       value="http://myauthserver.net/notify_mount.php"/>
+            <option name="mount_remove"    value="http://myauthserver.net/notify_mount.php"/>
+            <option name="listener_add"    value="http://myauthserver.net/notify_listener.php"/>
+            <option name="listener_remove" value="http://myauthserver.net/notify_listener.php"/>
+        </authentication>
+    </mount>
+
+    -->
+
+    <fileserve>1</fileserve>
+
+    <paths>
+		<!-- basedir is only used if chroot is enabled -->
+        <basedir>@pkgdatadir@</basedir>
+
+        <!-- Note that if <chroot> is turned on below, these paths must both
+             be relative to the new root, not the original root -->
+        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
+        <webroot>@pkgdatadir@/web</webroot>
+        <adminroot>@pkgdatadir@/admin</adminroot>
+        <!-- <pidfile>@pkgdatadir@/icecast.pid</pidfile> -->
+
+        <!-- Aliases: treat requests for 'source' path as being for 'dest' path
+             May be made specific to a port or bound address using the "port"
+             and "bind-address" attributes.
+          -->
+        <!--
+        <alias source="/foo" dest="/bar"/>
+          -->
+        <!-- Aliases: can also be used for simple redirections as well,
+             this example will redirect all requests for http://server:port/ to
+             the status page
+          -->
+        <alias source="/" dest="/status.xsl"/>
+    </paths>
+
+    <logging>
+        <accesslog>access.log</accesslog>
+        <errorlog>error.log</errorlog>
+        <!-- <playlistlog>playlist.log</playlistlog> -->
+      	<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
+      	<logsize>10000</logsize> <!-- Max size of a logfile -->
+        <!-- If logarchive is enabled (1), then when logsize is reached
+             the logfile will be moved to [error|access|playlist].log.DATESTAMP,
+             otherwise it will be moved to [error|access|playlist].log.old.
+             Default is non-archive mode (i.e. overwrite)
+        -->
+        <!-- <logarchive>1</logarchive> -->
+    </logging>
+
+    <security>
+        <chroot>0</chroot>
+        <!--
+        <changeowner>
+            <user>nobody</user>
+            <group>nogroup</group>
+        </changeowner>
+        -->
+    </security>
+</icecast>

Deleted: icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in
===================================================================
--- icecast/trunk/icecast/conf/icecast_shoutcast_compat.xml.in	2008-01-26 16:42:37 UTC (rev 14436)
+++ icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,60 +0,0 @@
-<!-- This config file can be used to configure icecast
-     in shoutcast compatibility mode which will allow
-     you to connect the Shoutcast DSP (or other Nullsoft
-     encoders such as the NSV encoder). Note this is just
-     a minimal config, check the main icecast.xml.dist file
-     for a complete list of possible configuration options -->
-<icecast>
-    <limits>
-        <sources>2</sources>
-    </limits>
-    <authentication>
-        <!-- Configure the shoutcast DSP to use this password -->
-        <source-password>hackme</source-password>
-        <!-- This is used for icecast's web interface -->
-        <admin-user>admin</admin-user>
-        <admin-password>hackme</admin-password>
-    </authentication>
-    <directory>
-        <yp-url-timeout>15</yp-url-timeout>
-        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
-    </directory>
-    <!-- This is the manufactured mount name for your shoutcast 
-         compatible stream. Since the shoutcast DSP (and NSV encoder)
-         cannot specify a mountpoint, you must enter it here -->
-    <shoutcast-mount>/stream</shoutcast-mount>
-    <!-- Use this if you are streaming NSV using the nullsoft NSV encoder
-    <shoutcast-mount>/stream.nsv</shoutcast-mount>
-    -->
-    <!-- This is the hostname other people will use to connect to your server.
-    It affects mainly the urls generated by Icecast for playlists and yp
-    listings. -->
-    <hostname>localhost</hostname>
-    <!-- You MUST define 2 ports, port and port +1 -->
-    <listen-socket>
-        <!-- Configure the shoutcast DSP with *this* port 
-             the shoutcast DSP actually will connect the
-             encoder to this port + 1 -->
-        <port>8000</port>
-    </listen-socket>
-    <listen-socket>
-        <!-- This port *must* be one larger than the one defined
-             above and defined as 'shoutcast-compat' -->
-        <port>8001</port>
-        <shoutcast-compat>1</shoutcast-compat>
-    </listen-socket>
-    <fileserve>1</fileserve>
-    <paths>
-        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
-        <webroot>@pkgdatadir@/web</webroot>
-        <adminroot>@pkgdatadir@/admin</adminroot>
-    </paths>
-    <logging>
-        <accesslog>access.log</accesslog>
-        <errorlog>error.log</errorlog>
-      	<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
-    </logging>
-    <security>
-        <chroot>0</chroot>
-    </security>
-</icecast>

Copied: icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in (from rev 14437, icecast/trunk/icecast/conf/icecast_shoutcast_compat.xml.in)
===================================================================
--- icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/conf/icecast_shoutcast_compat.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,48 @@
+<!-- This config file can be used to configure icecast
+     in shoutcast compatibility mode which will allow
+     you to connect the Shoutcast DSP (or other Nullsoft
+     encoders such as the NSV encoder). Note this is just
+     a minimal config, check the main icecast.xml.dist file
+     for a complete list of possible configuration options -->
+<icecast>
+    <limits>
+        <sources>2</sources>
+    </limits>
+    <authentication>
+        <!-- Configure the shoutcast DSP to use this password -->
+        <source-password>hackme</source-password>
+        <!-- This is used for icecast's web interface -->
+        <admin-user>admin</admin-user>
+        <admin-password>hackme</admin-password>
+    </authentication>
+    <directory>
+        <yp-url-timeout>15</yp-url-timeout>
+        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
+    </directory>
+    <!-- This is the hostname other people will use to connect to your server.
+    It affects mainly the urls generated by Icecast for playlists and yp
+    listings. -->
+    <hostname>localhost</hostname>
+    <!-- You MUST define 2 ports, port and port +1 -->
+    <listen-socket>
+        <!-- Configure the shoutcast DSP with *this* port 
+             the shoutcast DSP actually will connect the
+             encoder to this port + 1 -->
+        <port>8000</port>
+        <shoutcast-mount>/stream</shoutcast-mount>
+    </listen-socket>
+    <fileserve>1</fileserve>
+    <paths>
+        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
+        <webroot>@pkgdatadir@/web</webroot>
+        <adminroot>@pkgdatadir@/admin</adminroot>
+    </paths>
+    <logging>
+        <accesslog>access.log</accesslog>
+        <errorlog>error.log</errorlog>
+      	<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
+    </logging>
+    <security>
+        <chroot>0</chroot>
+    </security>
+</icecast>

Deleted: icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in
===================================================================
--- icecast/trunk/icecast/conf/icecast_urlauth.xml.in	2008-01-26 16:42:37 UTC (rev 14436)
+++ icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,49 +0,0 @@
-<icecast>
-    <!-- This is a simple example of using the url authenticator. -->
-    <limits>
-        <clients>100</clients>
-        <sources>2</sources>
-        <threadpool>5</threadpool>
-        <queue-size>524288</queue-size>
-        <client-timeout>30</client-timeout>
-        <header-timeout>15</header-timeout>
-        <source-timeout>10</source-timeout>
-        <burst-on-connect>1</burst-on-connect>
-        <burst-size>65535</burst-size>
-    </limits>
-    <authentication>
-        <source-password>hackme</source-password>
-        <relay-password>hackme</relay-password>
-        <admin-user>admin</admin-user>
-        <admin-password>hackme</admin-password>
-    </authentication>
-    <hostname>localhost</hostname>
-    <listen-socket>
-        <port>8000</port>
-    </listen-socket>
-    <mount>
-        <mount-name>/auth_example.ogg</mount-name>
-        <authentication type="url">
-            <option name="mount_add"       value="http://myauthserver.net/action.php"/>
-            <option name="mount_remove"    value="http://myauthserver.net/action.php"/>
-            <option name="listener_add"    value="http://myauthserver.net/action.php"/>
-            <option name="listener_remove" value="http://myauthserver.net/action.php"/>
-        </authentication>
-    </mount>
-    <fileserve>1</fileserve>
-    <paths>
-        <basedir>@pkgdatadir@</basedir>
-        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
-        <webroot>@pkgdatadir@/web</webroot>
-        <adminroot>@pkgdatadir@/admin</adminroot>
-        <alias source="/" dest="/status.xsl"/>
-    </paths>
-    <logging>
-        <accesslog>access.log</accesslog>
-        <errorlog>error.log</errorlog>
-      	<loglevel>4</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
-    </logging>
-    <security>
-        <chroot>0</chroot>
-    </security>
-</icecast>

Copied: icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in (from rev 14437, icecast/trunk/icecast/conf/icecast_urlauth.xml.in)
===================================================================
--- icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/conf/icecast_urlauth.xml.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,47 @@
+<icecast>
+    <!-- This is a simple example of using the url authenticator. -->
+    <limits>
+        <clients>100</clients>
+        <sources>2</sources>
+        <queue-size>524288</queue-size>
+        <client-timeout>30</client-timeout>
+        <header-timeout>15</header-timeout>
+        <source-timeout>10</source-timeout>
+        <burst-size>65535</burst-size>
+    </limits>
+    <authentication>
+        <source-password>hackme</source-password>
+        <relay-password>hackme</relay-password>
+        <admin-user>admin</admin-user>
+        <admin-password>hackme</admin-password>
+    </authentication>
+    <hostname>localhost</hostname>
+    <listen-socket>
+        <port>8000</port>
+    </listen-socket>
+    <mount>
+        <mount-name>/auth_example.ogg</mount-name>
+        <authentication type="url">
+            <option name="mount_add"       value="http://myauthserver.net/action.php"/>
+            <option name="mount_remove"    value="http://myauthserver.net/action.php"/>
+            <option name="listener_add"    value="http://myauthserver.net/action.php"/>
+            <option name="listener_remove" value="http://myauthserver.net/action.php"/>
+        </authentication>
+    </mount>
+    <fileserve>1</fileserve>
+    <paths>
+        <basedir>@pkgdatadir@</basedir>
+        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
+        <webroot>@pkgdatadir@/web</webroot>
+        <adminroot>@pkgdatadir@/admin</adminroot>
+        <alias source="/" dest="/status.xsl"/>
+    </paths>
+    <logging>
+        <accesslog>access.log</accesslog>
+        <errorlog>error.log</errorlog>
+      	<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
+    </logging>
+    <security>
+        <chroot>0</chroot>
+    </security>
+</icecast>

Deleted: icecast/tags/icecast-2.3.2/configure.in
===================================================================
--- icecast/trunk/icecast/configure.in	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/configure.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,152 +0,0 @@
-AC_INIT([Icecast], [2.3.1], [icecast at xiph.org])
-
-AC_PREREQ(2.54)
-AC_CONFIG_SRCDIR(src/main.c)
-dnl Process this file with autoconf to produce a configure script.
-
-AM_INIT_AUTOMAKE
-AM_CONFIG_HEADER(config.h)
-AM_MAINTAINER_MODE
-
-AC_PROG_CC
-AC_CANONICAL_HOST
-AC_PROG_LIBTOOL
-
-dnl Set some options based on environment
-
-DEBUG="-g"
-if test -z "$GCC"; then
-    XIPH_CPPFLAGS="-D_REENTRANT"
-    case $host in 
-        *-*-irix*)
-                XIPH_CPPFLAGS="$XIPH_CPPFLAGS -w -signed"
-                PROFILE="-p -g3 -O2 -signed -D_REENTRANT"
-                ;;
-        *-*-solaris*)
-                XIPH_CFLAGS="-xO4 -xcg92"
-                XIPH_CPPFLAGS="$XIPH_CPPFLAGS -v -w -fsimple -fast"
-                PROFILE="-xpg -g -Dsuncc"
-                ;;
-        *)
-                XIPH_CFLAGS="-O"
-                PROFILE="-g -p"
-                ;;
-    esac
-
-    case "$host" in
-        *openbsd* | *irix*)
-        ;;
-        *) AC_DEFINE([_XOPEN_SOURCE], 600, [Define if you have POSIX and XPG specifications])
-        ;;
-    esac
-
-else
-    XIPH_CPPFLAGS="-Wall -ffast-math -fsigned-char"
-    PROFILE="-pg -g"
-    AC_DEFINE([_GNU_SOURCE], 1, [Define to include GNU extensions to POSIX])
-fi
-
-dnl Checks for programs.
-
-dnl Checks for libraries.
-
-dnl Checks for header files.
-AC_HEADER_STDC
-
-AC_CHECK_HEADERS([alloca.h])
-AC_CHECK_HEADERS(pwd.h, AC_DEFINE(CHUID, 1, [Define if you have pwd.h]),,)
-AC_CHECK_HEADERS(unistd.h, AC_DEFINE(CHROOT, 1, [Define if you have unistd.h]),,)
-
-dnl Checks for typedefs, structures, and compiler characteristics.
-
-dnl Check for types
-
-dnl Checks for library functions.
-AC_CHECK_FUNCS(localtime_r poll)
-AC_SEARCH_LIBS(nanosleep, rt posix4, AC_DEFINE(HAVE_NANOSLEEP, 1,
-    [Define if you have nanosleep]))
-XIPH_NET
-
-dnl -- configure options --
-
-XIPH_PATH_XSLT
-XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS])
-
-XIPH_PATH_VORBIS([
-    XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
-    XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
-    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS])
-    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_vorbis.o"
-    ],
-    [AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed])
-    ])
-
-XIPH_PATH_THEORA([
-    XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$THEORA_CFLAGS])
-    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$THEORA_LDFLAGS])
-    XIPH_VAR_PREPEND([XIPH_LIBS],[$THEORA_LIBS])
-    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_theora.o"
-    ],
-    [ AC_MSG_WARN([Theora disabled!])
-    ])
-
-XIPH_PATH_SPEEX(
-    [ XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$SPEEX_CFLAGS])
-    XIPH_VAR_PREPEND([XIPH_LIBS],[$SPEEX_LIBS])
-    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$SPEEX_LDFLAGS])
-    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_speex.o"
-    ],
-    [ AC_MSG_WARN([Speex support disabled!])
-    ])
-
-ACX_PTHREAD(, AC_MSG_ERROR([POSIX threads missing]))
-XIPH_VAR_APPEND([XIPH_CFLAGS],[$PTHREAD_CFLAGS])
-XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$PTHREAD_CPPFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS],[$PTHREAD_LIBS])
-
-XIPH_PATH_CURL([
-    AC_CHECK_DECL([CURLOPT_NOSIGNAL],
-        [ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code])
-        ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o"
-        enable_curl="yes"
-        XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS])
-        XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS])
-        ], [ AC_MSG_NOTICE([Your curl dev files are too old (7.10 or above required)])
-        ], [#include <curl/curl.h>
-        ])
-    ],[ AC_MSG_NOTICE([libcurl not found])
-    ])
-dnl -- YP support --
-AC_ARG_ENABLE([yp],
-        AC_HELP_STRING([--disable-yp],[disable YP directory support]),
-        enable_yp="$enableval",
-        enable_yp="yes")
-if test "x$enable_yp" = "xyes" -a "x$enable_curl" = xyes
-then
-    AC_DEFINE([USE_YP], 1, [Define to compile in YP support code])
-    ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o"
-else
-    AC_MSG_NOTICE([YP support disabled])
-fi
-
-dnl Make substitutions
-
-AC_SUBST(XIPH_CPPFLAGS)
-AC_SUBST(XIPH_CFLAGS)
-AC_SUBST(XIPH_LIBS)
-AC_SUBST(XIPH_LDFLAGS)
-AC_SUBST(PTHREAD_CPPFLAGS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(LIBTOOL_DEPS)
-AC_SUBST(LIBS)
-AC_SUBST(DEBUG)
-AC_SUBST(CFLAGS)
-AC_SUBST(PROFILE)
-AC_SUBST(ICECAST_OPTIONAL)
-
-AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile
-src/httpp/Makefile src/thread/Makefile src/log/Makefile
-src/net/Makefile src/timing/Makefile doc/Makefile web/Makefile
-admin/Makefile win32/Makefile win32/res/Makefile examples/Makefile])

Copied: icecast/tags/icecast-2.3.2/configure.in (from rev 14985, icecast/trunk/icecast/configure.in)
===================================================================
--- icecast/tags/icecast-2.3.2/configure.in	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/configure.in	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,156 @@
+AC_INIT([Icecast], [2.3.2], [icecast at xiph.org])
+
+AC_PREREQ(2.54)
+AC_CONFIG_SRCDIR(src/main.c)
+dnl Process this file with autoconf to produce a configure script.
+
+AM_INIT_AUTOMAKE
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_CANONICAL_HOST
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl Set some options based on environment
+
+DEBUG="-g"
+if test -z "$GCC"; then
+    XIPH_CPPFLAGS="-D_REENTRANT"
+    PROFILE="-g -p"
+else
+    XIPH_CPPFLAGS="-Wall -ffast-math -fsigned-char"
+    PROFILE="-pg -g"
+    AC_DEFINE([_GNU_SOURCE], 1, [Define to include GNU extensions to POSIX])
+fi
+
+dnl Checks for programs.
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_TIME
+
+AC_CHECK_HEADERS([alloca.h])
+AC_CHECK_HEADERS(pwd.h, AC_DEFINE(CHUID, 1, [Define if you have pwd.h]),,)
+AC_CHECK_HEADERS(unistd.h, AC_DEFINE(CHROOT, 1, [Define if you have unistd.h]),,)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+XIPH_C__FUNC__
+
+dnl Check for types
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(localtime_r poll)
+AC_SEARCH_LIBS(nanosleep, rt posix4, AC_DEFINE(HAVE_NANOSLEEP, 1,
+    [Define if you have nanosleep]))
+XIPH_NET
+
+dnl -- configure options --
+
+XIPH_PATH_XSLT
+XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS])
+XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS])
+
+XIPH_PATH_VORBIS([
+    XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
+    XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
+    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS])
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_vorbis.o"
+    ],
+    [AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed])
+    ])
+
+XIPH_PATH_THEORA([
+    XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$THEORA_CFLAGS])
+    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$THEORA_LDFLAGS])
+    XIPH_VAR_PREPEND([XIPH_LIBS],[$THEORA_LIBS])
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_theora.o"
+    ],
+    [ AC_MSG_WARN([Theora disabled!])
+    ])
+
+XIPH_PATH_SPEEX(
+    [ XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$SPEEX_CFLAGS])
+    XIPH_VAR_PREPEND([XIPH_LIBS],[$SPEEX_LIBS])
+    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$SPEEX_LDFLAGS])
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_speex.o"
+    ],
+    [ AC_MSG_WARN([Speex support disabled!])
+    ])
+
+AC_CHECK_LIB(kate, kate_decode_init,[have_kate=yes],[have_kate=no], -logg)
+if test "x$have_kate" == "xyes"
+then
+  AC_CHECK_LIB(oggkate, kate_ogg_decode_headerin,[have_kate=yes],[have_kate=no],-lkate -logg)
+  if test "x$have_kate" == "xyes"
+  then
+    KATE_LIBS="-loggkate -lkate -logg"
+    AC_DEFINE([HAVE_KATE],[1],[Define if you have libkate])
+  fi
+fi
+dnl we still use format_kate as it doesn't need libkate to work
+#ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_kate.o"
+
+ACX_PTHREAD(, AC_MSG_ERROR([POSIX threads missing]))
+XIPH_VAR_APPEND([XIPH_CFLAGS],[$PTHREAD_CFLAGS])
+XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$PTHREAD_CPPFLAGS])
+XIPH_VAR_PREPEND([XIPH_LIBS],[$PTHREAD_LIBS])
+
+XIPH_PATH_CURL([
+    AC_CHECK_DECL([CURLOPT_NOSIGNAL],
+        [ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code])
+        AC_CHECK_FUNCS([curl_global_init])
+        ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o"
+        enable_curl="yes"
+        XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS])
+        XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS])
+        ], [ AC_MSG_NOTICE([Your curl dev files are too old (7.10 or above required)])
+        ], [#include <curl/curl.h>
+        ])
+    ],[ AC_MSG_NOTICE([libcurl not found])
+    ])
+dnl -- YP support --
+AC_ARG_ENABLE([yp],
+        AC_HELP_STRING([--disable-yp],[disable YP directory support]),
+        enable_yp="$enableval",
+        enable_yp="yes")
+if test "x$enable_yp" = "xyes" -a "x$enable_curl" = xyes
+then
+    AC_DEFINE([USE_YP], 1, [Define to compile in YP support code])
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o"
+else
+    AC_MSG_NOTICE([YP support disabled])
+fi
+XIPH_PATH_OPENSSL([
+    XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$OPENSSL_CFLAGS])
+    XIPH_VAR_APPEND([XIPH_LDFLAGS],[$OPENSSL_LDFLAGS])
+    XIPH_VAR_PREPEND([XIPH_LIBS],[$OPENSSL_LIBS])
+    ],
+    [ AC_MSG_NOTICE([SSL disabled!])
+    ])
+
+dnl Make substitutions
+
+AC_SUBST(XIPH_CPPFLAGS)
+AC_SUBST(XIPH_CFLAGS)
+AC_SUBST(XIPH_LIBS)
+AC_SUBST(XIPH_LDFLAGS)
+AC_SUBST(PTHREAD_CPPFLAGS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(LIBTOOL_DEPS)
+AC_SUBST(LIBS)
+AC_SUBST(DEBUG)
+AC_SUBST(CFLAGS)
+AC_SUBST(PROFILE)
+AC_SUBST(ICECAST_OPTIONAL)
+AC_SUBST(HAVE_KATE)
+AC_SUBST(KATE_LIBS)
+
+AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile
+src/httpp/Makefile src/thread/Makefile src/log/Makefile
+src/net/Makefile src/timing/Makefile doc/Makefile web/Makefile
+admin/Makefile win32/Makefile win32/res/Makefile examples/Makefile])

Copied: icecast/tags/icecast-2.3.2/doc (from rev 14243, icecast/trunk/icecast/doc)

Deleted: icecast/tags/icecast-2.3.2/doc/Makefile.am
===================================================================
--- icecast/trunk/icecast/doc/Makefile.am	2007-11-28 01:15:16 UTC (rev 14243)
+++ icecast/tags/icecast-2.3.2/doc/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,14 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-AUTOMAKE_OPTIONS = foreign
-
-docdir = $(datadir)/doc/icecast
-doc_DATA = index.html icecast2_admin.html icecast2_basicsetup.html \
-	icecast2_config_file.html icecast2_faq.html icecast2_glossary.html \
-	icecast2_introduction.html icecast2_relay.html icecast2_stats.html \
-	icecast2_win32.html icecast2_yp.html icecast2_listenerauth.html icecast2_changes.html \
-	listener_auth1.jpg listener_auth2.jpg listener_auth3.jpg
-
-EXTRA_DIST = Index.hhk icecast2.hhc icecast2.hhp index_win32.html \
-	stats1.jpg style.css win32_section1.html win32_section2.html \
-	win32_section3.html windowtitle.jpg $(doc_DATA)

Copied: icecast/tags/icecast-2.3.2/doc/Makefile.am (from rev 14437, icecast/trunk/icecast/doc/Makefile.am)
===================================================================
--- icecast/tags/icecast-2.3.2/doc/Makefile.am	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/doc/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,15 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+docdir = $(datadir)/doc/icecast
+doc_DATA = index.html icecast2_admin.html icecast2_basicsetup.html \
+	icecast2_config_file.html icecast2_faq.html icecast2_glossary.html \
+	icecast2_introduction.html icecast2_relay.html icecast2_stats.html \
+	icecast2_win32.html icecast2_yp.html icecast2_listenerauth.html icecast2_changes.html \
+	listener_auth1.jpg listener_auth2.jpg listener_auth3.jpg \
+	masterslave.png relay.png
+
+EXTRA_DIST = Index.hhk icecast2.hhc icecast2.hhp index_win32.html \
+	stats1.jpg style.css win32_section1.html win32_section2.html \
+	win32_section3.html windowtitle.jpg $(doc_DATA)

Deleted: icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html
===================================================================
--- icecast/trunk/icecast/doc/icecast2_basicsetup.html	2007-11-28 01:15:16 UTC (rev 14243)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,77 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-<title>Icecast v2.x Documentation</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body>
-<div class="boxtest">
-<h1>Icecast 2 Basic Setup</h1>
-<hr id='titlebar' />
-<p>
-<br />
-<br />
-<br />
-</p>
-<h2>Basic Requirements</h2>
-<p>This section will describe the essential requirements in setting up a simple Internet radio station.  It is by no means a complete list but should give you enough to get started.</p>
-<p>There are two major components involved: the streaming server (icecast in this case) and the source client.  The icecast server will be the place where all listeners of your station will connect.  The source client (in general) runs on a separate machine than icecast, but does not necessarily need to.  Source clients send the content to icecast and provide the stream data (encoded audio) that is then relayed out to listeners by icecast.</p>
-<p>It is important to note that not all source clients work with icecast2.  You will need to check to make sure that icecast2 is supported by your chosen source client.</p>
-<p>
-<br />
-<br />
-<br />
-</p>
-<h2>The Basics</h2>
-<p>Each icecast server can house multiple broadcasts (or mountpoints) each containing a separate stream of content. A 'mountpoint' is a unique name on your server identifying a particular stream - it looks like a filename, such as '/stream.ogg'. A listener can only listen to a single mountpoint at a time.  This means you can have a single icecast server contain either multiple broadcasts with different content, or possibly the same broadcast but with streams of different bitrates or qualities.  In this case each broadcast or stream is a separate mountpoint.</p>
-<p>At this point, the steps outlined here related to the Unix version or Win32 console version of icecast.  Icecast is also available in a Win32 GUI version, and the steps are similar in setup, but not quite the same.</p>
-<p>The first step in the process is to find and install the icecast2 server itself.  How to do this is not contained within this documentation.  After installation you should have and icecast binary and 3 directories</p>
-<div style="text-align:center;">
-<table border="1" width="75%">
-<tr><td>conf</td><td>Contains the icecast configuration file (icecast.xml) which defines all the configuration parameters for the server.</td></tr>
-<tr><td>admin</td><td>Contains xslt files which are used by the icecast server to provide a web-based front end to the administration capabilities of the server.</td></tr>
-<tr><td>logs</td><td>This is a blank directory which (if specified in the config file) will contain all the logs (there are 2) for icecast.</td></tr>
-</table>
-</div>
-<p>The next step is to edit the icecast.xml file and set the appropriate values.  Most of the default values are fine as provided, and for a basic setup the following entries should be changed :
-<br /><br />
-</p>
-<pre>
-&lt;source-password&gt; - will be used by the source client
-&lt;admin-password&gt; - will be used to access admin features of icecast
-&lt;listen-socket&gt; (both port and bind-address)
-&lt;logdir&gt; - directory where log files will be placed
-&lt;webroot&gt; - any static content can be placed here (file serving root)
-&lt;adminroot&gt; - directory containing admin xslt files
-</pre>
-<p>Once the configuration file is modified, you should be able to start the server with the following command</p>
-<pre>
-icecast -c /path/to/icecast.xml
-</pre>
-<p>If no error messages are generated, then check the error.log file for the 'server started'
-message, it will look something like :-</p>
-<pre>
-[2003-10-31  13:04:49] INFO main/main.c Icecast 2.3.0 server started
-</pre>
-<p>You may notice slight variations to the line above, the time will no doubt be different,
-and on some platforms the main.c is just main, but the key thing here is that the server is
-started, logging is working and the version is shown.
-</p>
-<p>You can also verify that it started by visiting the following URL : http://yourip:port/admin/stats.xml.  You should be prompted for a username and password.  Enter the username "admin" and the password you entered for &lt;admin-password&gt;.  If all is well, you should see an small XML tree which represents icecast statistics (more about that later).</p>
-<p>Now that the icecast server is started you must now configure your source client.  The information you will need for the source client is the following : <br />
-<br />
-IP address and Port of the icecast server - both of these come from &lt;listen-socket&gt;<br />
-source password - from &lt;source-password&gt;
-</p>
-<p>Additionally, you will need to choose a mountpoint and specify this in the source client.  Icecast does not need to know about each mount point (although you can configure settings for specific mountpoint - this is covered under Advanced configuration) there are, however, some points to mention regarding mountpoints.  All Ogg Vorbis streams should have mountpoints that end in .ogg (i,e. /mystream.ogg).  This is due to the lazy way most media players infer the type of stream.  MP3 streams usually do not contain an extension (/mystream).  Mount points also should not contain any spaces or odd characters (again due to the lazy way many of the media players are coded).</p>
-<p>Once you have configured your source client, you should be able to connect it to the icecast server.  Verify that it is connected by hitting the stats.xml URL that was mentioned above.</p>
-<p>Now that you have the source connnected, listening to the stream involves simply opening the appropriate following URL in a browser: http://yourip:port/mounpointyouspecified.m3u.  So, for instance, if you attached your source client to an icecast server located at 192.168.1.10:8000 with a mountpoint of /mystream.ogg, then you would open : http://192.168.1.10:8000/mystream.ogg.m3u.  Note that the .m3u extention will serve up a link that opens most media players.  Also it is important to note that m3u need not contain only MP3 stream, it can contain streams of arbitrary content-type and is used by icecast to serve a playlist that represents your broadcast to listening clients.  Alternatively you can open up the stream URL directly within your media player (http://192.168.1.10:8000/mystream.ogg in this case)</p>
-<p>
-<br />
-<br />
-<br />
-</p>
-</div>
-</body>
-</html>

Copied: icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html (from rev 14440, icecast/trunk/icecast/doc/icecast2_basicsetup.html)
===================================================================
--- icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_basicsetup.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<title>Icecast v2.x Documentation</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body>
+<div class="boxtest">
+<h1>Icecast 2 Basic Setup</h1>
+<hr id='titlebar' />
+<p>
+<br />
+<br />
+<br />
+</p>
+<h2>Basic Requirements</h2>
+<p>This section will describe the essential requirements in setting up a simple Internet radio station.  It is by no means a complete list but should give you enough to get started.</p>
+<p>There are two major components involved: the streaming server (icecast in this case) and the source client.  The icecast server will be the place where all listeners of your station will connect.  The source client (in general) runs on a separate machine than icecast, but does not necessarily need to.  Source clients send the content to icecast and provide the stream data (encoded audio) that is then relayed out to listeners by icecast.</p>
+<p>It is important to note that not all source clients work with icecast2.  You will need to check to make sure that icecast2 is supported by your chosen source client.</p>
+<p>
+<br />
+<br />
+<br />
+</p>
+<h2>The Basics</h2>
+<p>Each icecast server can house multiple broadcasts (or mountpoints) each containing a separate stream of content. A 'mountpoint' is a unique name on your server identifying a particular stream - it looks like a filename, such as '/stream.ogg'. A listener can only listen to a single mountpoint at a time.  This means you can have a single icecast server contain either multiple broadcasts with different content, or possibly the same broadcast but with streams of different bitrates or qualities.  In this case each broadcast or stream is a separate mountpoint.</p>
+<p>At this point, the steps outlined here related to the Unix version or Win32 console version of icecast.  Icecast is also available in a Win32 GUI version, and the steps are similar in setup, but not quite the same.</p>
+<p>The first step in the process is to find and install the icecast2 server itself.  How to do this is not contained within this documentation.  After installation you should have and icecast binary and 3 directories</p>
+<div style="text-align:center;">
+<table border="1" width="75%">
+<tr><td>conf</td><td>Contains the icecast configuration file (icecast.xml) which defines all the configuration parameters for the server.</td></tr>
+<tr><td>admin</td><td>Contains xslt files which are used by the icecast server to provide a web-based front end to the administration capabilities of the server.</td></tr>
+<tr><td>logs</td><td>This is a blank directory which (if specified in the config file) will contain all the logs (there are 2) for icecast.</td></tr>
+</table>
+</div>
+<p>The next step is to edit the icecast.xml file and set the appropriate values.  Most of the
+specified values in the samples are fine, for a basic setup the following entries should be
+specified, and if neccessary, changed to suite your situation :
+<br /><br />
+</p>
+<pre>
+&lt;hostname&gt; - DNS name or IP address used for stream directory listings.
+&lt;source-password&gt; - will be used for the source client authentication
+&lt;admin-password&gt; - will be used for authenticating admin features of icecast
+&lt;listen-socket&gt; (both port and bind-address)
+&lt;logdir&gt; - directory where log files will be placed
+&lt;webroot&gt; - directory for non admin content (file serving root), status page is provided
+&lt;adminroot&gt; - directory containing admin xslt files
+</pre>
+<p>Most will not need to change adminroot/webroot as those are only read by icecast but icecast
+needs to create files in the logdir which MAY mean you want to be more selective. The other
+settings above will need to be provided by the icecast administrator </p>
+<p>Once the configuration file is modified, you should be able to start the server with the following command</p>
+<pre>
+icecast -c /path/to/icecast.xml
+</pre>
+<p>If no error messages are generated, then check the error.log file for the 'server started'
+message, it will look something like :-</p>
+<pre>
+[2003-10-31  13:04:49] INFO main/main.c Icecast 2.3.0 server started
+</pre>
+<p>You may notice slight variations to the line above, the time will no doubt be different,
+and on some platforms the main.c is just main, but the key thing here is that the server is
+started, logging is working and the version is shown.
+</p>
+<p>You can also verify that it started by visiting the following URL : http://yourip:port/admin/stats.xml.  You should be prompted for a username and password.  Enter the username "admin" and the password you entered for &lt;admin-password&gt;.  If all is well, you should see an small XML tree which represents icecast statistics (more about that later).</p>
+<p>Now that the icecast server is started you must now configure your source client.  The information you will need for the source client is the following : <br />
+<br />
+IP address and Port of the icecast server - both of these come from &lt;listen-socket&gt;<br />
+source password - from &lt;source-password&gt;
+</p>
+<p>Additionally, you will need to choose a mountpoint and specify this in the source client.  Icecast does not need to know about each mount point (although you can configure settings for specific mountpoint - this is covered under Advanced configuration) there are, however, some points to mention regarding mountpoints.  All Ogg Vorbis streams should have mountpoints that end in .ogg (i,e. /mystream.ogg).  This is due to the lazy way most media players infer the type of stream.  MP3 streams usually do not contain an extension (/mystream).  Mount points also should not contain any spaces or odd characters (again due to the lazy way many of the media players are coded).</p>
+<p>Once you have configured your source client, you should be able to connect it to the icecast server.  Verify that it is connected by hitting the stats.xml URL that was mentioned above.</p>
+<p>Now that you have the source connnected, listening to the stream involves simply opening the appropriate following URL in a browser: http://yourip:port/mounpointyouspecified.m3u.  So, for instance, if you attached your source client to an icecast server located at 192.168.1.10:8000 with a mountpoint of /mystream.ogg, then you would open : http://192.168.1.10:8000/mystream.ogg.m3u.  Note that the .m3u extention will serve up a link that opens most media players.  Also it is important to note that m3u need not contain only MP3 stream, it can contain streams of arbitrary content-type and is used by icecast to serve a playlist that represents your broadcast to listening clients.  Alternatively you can open up the stream URL directly within your media player (http://192.168.1.10:8000/mystream.ogg in this case)</p>
+<p>
+<br />
+<br />
+<br />
+</p>
+</div>
+</body>
+</html>

Deleted: icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html
===================================================================
--- icecast/trunk/icecast/doc/icecast2_config_file.html	2007-11-28 01:15:16 UTC (rev 14243)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,690 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-<title>Icecast v2.x Documentation</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body>
-<div class="boxtest">
-<h1>Icecast 2 Config File</h1>
-<hr id='titlebar' />
-<h3>Overview</h3>
-<p>
-This section will describe each section of the config file and is grouped into the following sections:
-</p>
-<ul>
-<li><a href="#limits">Limits</a></li>
-<li><a href="#authentication">Authentication</a></li>
-<li><a href="#yp">YP Directory Settings</a></li>
-<li><a href="#misc">Misc Server settings</a></li>
-<li><a href="#relay">Relay settings</a></li>
-<li><a href="#mount">Mount Specific settings</a></li>
-<li><a href="#path">File path settings</a></li>
-<li><a href="#log">Logging</a></li>
-<li><a href="#security">Security</a></li>
-</ul>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="limits"></a>
-<h2>Limits</h2>
-<pre>
-    &lt;limits&gt;
-        &lt;clients&gt;100&lt;/clients&gt;
-        &lt;sources&gt;2&lt;/sources&gt;
-        &lt;threadpool&gt;5&lt;/threadpool&gt;
-        &lt;queue-size&gt;102400&lt;/queue-size&gt;
-        &lt;client-timeout&gt;30&lt;/client-timeout&gt;
-        &lt;header-timeout&gt;15&lt;/header-timeout&gt;
-        &lt;source-timeout&gt;10&lt;/source-timeout&gt;
-        &lt;burst-on-connect&gt;1&lt;/burst-on-connect&gt;
-        &lt;burst-size&gt;65536&lt;/burst-size&gt;
-    &lt;/limits&gt;
-</pre>
-<p>This section contains server level settings that, in general, do not need to be changed.  Only modify this section if you are know what you are doing.
-</p>
-<h4>clients</h4>
-<div class="indentedbox">
-Total number of concurrent clients supported by the server.  Listeners are considered clients, but so is accesses to any static content (i.e. fileserved content) and also any requests to gather stats.  These are max *concurrent* connections for the entire server (not per mountpoint).
-</div>
-<h4>sources</h4>
-<div class="indentedbox">
-Maximum number of connected sources supported by the server.
-</div>
-<h4>threadpool</h4>
-<div class="indentedbox">
-This is the number of threads that are started to handle client connections.  You may need to increase this value if you are running a high traffic stream.  This recommended value is for a small to medium traffic server.
-</div>
-<h4>queue-size</h4>
-<div class="indentedbox">
-This is the maximum size (in bytes) of a client (listener) queue.  A listener may temporarily lag behind due to network congestion and in this case an internal queue is maintained for each listener.  If the queue grows larger than this config value, then the listener will be removed from the stream.
-</div>
-<h4>client-timeout</h4>
-<div class="indentedbox">
-This does not seem to be used.
-</div>
-<h4>header-timeout</h4>
-<div class="indentedbox">
-The maximum time (in seconds) to wait for a request to come in once the client has made a connection to the server.  In general this value should not need to be tweaked.
-</div>
-<h4>source-timeout</h4>
-<div class="indentedbox">
-If a connected source does not send any data within this timeout period (in seconds), then the source connection will be removed from the server.
-</div>
-<h4>burst-on-connect</h4>
-<div class="indentedbox">
-With this enabled, a connecting client will be sent a burst of audio data from the stream.  This will have the effect of reducing the startup time for the stream from the perspective of the listener.  This is due to the fact that most media players have local buffers that must be filled before the stream begins to play.  This may introduce a small latency in the stream (difference in time between when the source plays a clip and the listener hears a clip).  If this latency is important to you, then you can disable this feature.  The latency is bitrate-dependent, but as an example, for a 128kbps stream, the latency between the source and the player is ~ 1.5 secs WITHOUT burst on connect, and WITH burst on connect the latency is 3 secs.
-</div>
-<h4>burst-size</h4>
-<div class="indentedbox">
-The burst size is the amount of data (in bytes) to burst to a client at connection time. Like
-burst-on-connect, this is to quickly fill the pre-buffer used by media players. The default
-is 64kbytes which is a typical size used by most clients so changing it is not usually required.
-This setting applies to all mountpoints.
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="authentication"></a>
-<h2>Authentication</h2>
-<pre>
-    &lt;authentication&gt;
-        &lt;source-password&gt;hackme&lt;/source-password&gt;
-        &lt;relay-user&gt;relay&lt;/relay-user&gt;
-        &lt;relay-password&gt;hackme&lt;/relay-password&gt;
-        &lt;admin-user&gt;admin&lt;/admin-user&gt;
-        &lt;admin-password&gt;hackme&lt;/admin-password&gt;
-    &lt;/authentication&gt;
-</pre>
-<p>This section contains all the users and passwords used for administration purposes or to connect sources and relays.
-</p>
-<h4>source-password</h4>
-<div class="indentedbox">
-The unencrypted password used by sources to connect to icecast2.  Currently, the username for all source connections must be 'source'.  This is likely to change in the future.
-</div>
-<h4>relay-user</h4>
-<div class="indentedbox">
-Used in the master server as part of the authentication when a slave requests
-the list of streams to relay.  The default username is 'relay'
-</div>
-<h4>relay-password</h4>
-<div class="indentedbox">
-Used in the master server as part of the authentication when a slave requests
-the list of streams to relay.
-</div>
-<h4>admin-user</h4>
-<h4>admin-password</h4>
-<div class="indentedbox">
-The username/password used for all administration functions.  This includes retrieving statistics, accessing the web-based administration screens, etc.  A list of these functions can be found in the "Administration" section of the manual.
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="yp"></a>
-<h2>YP Directory Settings</h2>
-<pre>
-    &lt;directory&gt;
-        &lt;yp-url-timeout&gt;15&lt;/yp-url-timeout&gt;
-        &lt;yp-url&gt;http://dir.xiph.org/cgi-bin/yp-cgi&lt;/yp-url&gt;
-    &lt;/directory&gt;
-</pre>
-<p>This section contains all the settings for listing a stream on any of the Icecast2 YP Directory servers.  Multiple occurances of this section can be specified in order to be listed on multiple directory servers.
-</p>
-<h4>yp-url-timeout</h4>
-<div class="indentedbox">
-This value is the maximum time icecast2 will wait for a response from a particular directory server.  The recommended value should be sufficient for most directory servers.
-</div>
-<h4>yp-url</h4>
-<div class="indentedbox">
-The URL which icecast2 uses to communicate with the Directory server.  The value for this setting is provided by the owner of the Directory server.
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="misc"></a>
-<h2>Misc Server Settings</h2>
-<pre>
-    &lt;hostname&gt;localhost&lt;hostname&gt;
-
-    &lt;-- You can use these two if you only want a single listening socket --&gt;
-    &lt;-- &lt;port&gt;8000&lt;/port&gt; --&gt;
-    &lt;-- &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt; --&gt;
-
-    &lt;-- You may have multiple &lt;listen-socket&gt; elements --&gt;
-    &lt;listen-socket&gt;
-        &lt;port&gt;8000&lt;/port&gt;
-        &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt;
-    &lt;/listen-socket&gt;
-    &lt;listen-socket&gt;
-        &lt;port&gt;8001&lt;/port&gt;
-        &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt;
-        &lt;shoutcast-compat&gt;1&lt;/shoutcast-compat&gt;
-    &lt;/listen-socket&gt;
-
-    &lt;fileserve&gt;1&lt;/fileserve&gt;
-    &lt;shoutcast-mount&gt;/live.nsv&lt;/shoutcast-mount&gt;
-</pre>
-<p>This section contains miscellaneous server settings.  Note that multiple listen-socket
-sections may be configured in order to have icecast2 listen on multiple network interfaces.
-If a bind-address is not specified for a particular listen-socket, then the socket will be
-bound to all interfaces. Generally, you won't need to set bind-address. There is an internal
-limit of 20 listening sockets currently, which may be extended in later releases.
-</p>
-<h4>port</h4>
-<div class="indentedbox">
-The TCP port that will be used to accept client connections.
-</div>
-<h4>bind-address</h4>
-<div class="indentedbox">
-An optional IP address that can be used to bind to a specific network card.  If not supplied, then it will bind to all interfaces.
-</div>
-<h4>shoutcast-compat</h4>
-<div class="indentedbox">
-This optional flag will indicate that this port will operate in 'shoutcast-compatibility' mode.  Due to major differences in the source client connection protocol, if you wish to use any of the shoutcast DJ tools, you will need to configure at least one socket as shoutcast-compatible.  Note that when in this mode, only source clients (and specifically shoutcast source clients) will be able to attach to this port.  All listeners may connect to any of the ports defined without this flag.  Also, for proper Shoutcast DSP compatibility, you must define a listen socket with a port one less than the one defined as 'shoutcast-compat'.  This means if you define 8001 as shoutcast-compat, then you will need to define a listen port of 8000 and it must not also be defined as shoutcast-compat.  See the example config file in the distribution for more info.
-</div>
-<h4>fileserve</h4>
-<div class="indentedbox">
-This flag turns on the icecast2 fileserver from which static files can be served.  All files are served relative to the path specified in the &lt;paths&gt;&lt;webroot&gt; configuration setting.
-</div>
-<h4>shoutcast-mount</h4>
-<div class="indentedbox">
-An optional mountpoint to use when shoutcast DSP compatible clients connect. The default is /stream but can
-be overridden here to use an alternative name which may include an extension that some clients require for
-certain formats.
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="relay"></a>
-<h2>Relay Settings</h2>
-<pre>
-    &lt;master-server&gt;127.0.0.1&lt;/master-server&gt;
-    &lt;master-server-port&gt;8001&lt;/master-server-port&gt;
-    &lt;master-update-interval&gt;120&lt;/master-update-interval&gt;
-    &lt;master-username&gt;relay&lt;/master-username&gt;
-    &lt;master-password&gt;hackme&lt;/master-password&gt;
-    &lt;relays-on-demand&gt;0&lt;/relays-on-demand&gt;
-
-    &lt;relay&gt;
-        &lt;server&gt;127.0.0.1&lt;/server&gt;
-        &lt;port&gt;8001&lt;/port&gt;
-        &lt;mount&gt;/example.ogg&lt;/mount&gt;
-        &lt;local-mount&gt;/different.ogg&lt;/local-mount&gt;
-        &lt;username&gt;joe&lt;/username&gt;
-        &lt;password&gt;soap&lt;/password&gt;
-        &lt;relay-shoutcast-metadata&gt;0&lt;/relay-shoutcast-metadata&gt;
-        &lt;on-demand&gt;0&lt;/on-demand&gt;
-    &lt;/relay&gt;
-</pre>
-<p>This section contains the server's relay settings. There are two types of relays: a "Master server relay" or a "Specific Mountpoint relay."  A Master server relay is only supported between icecast2 servers and is used to relays all mountpoints on a remote icecast2 server.
-</p>
-<h3>Master Relay</h3>
-<p>
-The following diagram shows the basics of doing a Master relay.  Note that Server 1 is configured with the &lt;master-server&gt;, &lt;master-server-port&gt;, etc settings and Server 2 is the server from which Server 1 will pull all attached mountpoints and relay them.  Using a Master Server relay, ALL mountpoints on Server 2 will be relayed.  If only specific mountpoints need to be relayed, then you must configure Server 1 as a "Specific Mountpoint Relay".  Both Master server relays and Specific Mountpoint relays begin their "relaying" when the Server is started.
-</p>
-<pre>
-      |-----|                       |-----|
-      |     |  all mountpoints      |     | /mount1
-      |     | &lt;-------------------  |     | /mount2.ogg
-      |-----|                       |-----| /mount3
-      Icecast 2                     Icecast 2
-      Server 1                      Server 2
-      (RELAY SERVER)                (MASTER SERVER)
-
-     configured with
-     &lt;master-server&gt;
-     settings
-
-</pre>
-<p>
-A server is configured as a Master Server relay by specifying the &lt;master-server&gt;, &lt;master-server-port&gt;,&lt;master-update-interval&gt;,&lt;master-password&gt; values in the config file.  The server that is being relayed does not need any special configuration.
-</p>
-
-<h4>master-server</h4>
-<div class="indentedbox">
-This is the IP for the server which contains the mountpoints to be relayed (Master Server).
-</div>
-<h4>master-server-port</h4>
-<div class="indentedbox">
-This is the TCP Port for the server which contains the mountpoints to be relayed (Master Server).
-</div>
-<h4>master-update-interval</h4>
-<div class="indentedbox">
-The interval (in seconds) that the Relay Server will poll the Master Server for any new mountpoints to relay.
-</div>
-<h4>master-username</h4>
-<div class="indentedbox">
-This is the relay username on the master server.  It is used to query the
-server for a list of mountpoints to relay. If not specified then 'relay' is
-used
-</div>
-<h4>master-password</h4>
-<div class="indentedbox">
-This is the relay password on the Master server.  It is used to query the
-server for a list of mountpoints to relay.
-</div>
-<h4>relays-on-demand</h4>
-<div class="indentedbox">
-    <p>Changes the default on-demand setting for relays, so a stream is only relayed if
-    listeners are connected. 1=enabled, 0=disabled (default).
-    </p>
-</div>
-
-<br />
-<h3>Specific Mountpoint Relay</h3>
-The following diagram shows the basics of doing a Specific Mountpoint relay.  Note that Server 1 is configured with the &lt;relay&gt; settings and Server 2 is the server from which Server 1 will pull the specified mountpoint(s) and relay them.  Using a Specific Mountpoint Relay, only those mountpoints specified on Server 1 will be relayed from Server 2.
-
-<pre>
-      |-----|                       |-----|
-      |     |      /mount3          |     | /mount1
-      |     | &lt;-------------------  |     | /mount2.ogg
-      |-----|                       |-----| /mount3
-      Icecast 2                     Icecast 2/Shoutcast/Icecast
-      Server 1                      Server 2
-      (RELAY SERVER)                (REMOTE SERVER)
-
-     configured with
-     &lt;relay&gt;
-     settings
-
-</pre>
-
-Specific Mountpoint Relays can be configured to relay from an Icecast 2 server, as well as Icecast 1.x and Shoutcast.
-A server is configured as a Specific Mountpoint Server relay by specifying a &lt;relay&gt; XML chunk in the config file for each mountpoint to be relayed.  The server that is being relayed does not need any special configuration.
-
-<pre>
-    &lt;relay&gt;
-        &lt;server&gt;127.0.0.1&lt;/server&gt;
-        &lt;port&gt;8001&lt;/port&gt;
-        &lt;mount&gt;/example.ogg&lt;/mount&gt;
-        &lt;local-mount&gt;/different.ogg&lt;/local-mount&gt;
-        &lt;username&gt;joe&lt;/username&gt;
-        &lt;password&gt;soap&lt;/password&gt;
-        &lt;relay-shoutcast-metadata&gt;0&lt;/relay-shoutcast-metadata&gt;
-        &lt;on-demand&gt;1&lt;/on-demand&gt;
-    &lt;/relay&gt;
-</pre>
-
-
-<h4>server</h4>
-<div class="indentedbox">
-This is the IP for the server which contains the mountpoint to be relayed.
-</div>
-<h4>port</h4>
-<div class="indentedbox">
-This is the TCP Port for the server which contains the mountpoint to be relayed.
-</div>
-<h4>mount</h4>
-<div class="indentedbox">
-The mountpoint located on the remote server.  If you are relaying a shoutcast stream, this must be '/'.
-</div>
-<h4>local-mount</h4>
-<div class="indentedbox">
-The name to use for the local mountpoint.  This is what the mount will be named on the RELAY SERVER.
-</div>
-<h4>username</h4>
-<div class="indentedbox">
-The source of the relay may require authentication itself, if so state the username here.
-</div>
-<h4>password</h4>
-<div class="indentedbox">
-The source of the relay may require authentication itself, if so state the password here.
-</div>
-<h4>relay-shoutcast-metadata</h4>
-<div class="indentedbox">
-If you are relaying a Shoutcast stream, you need to specify this indicator to also relay the metadata (song titles) that is part of the Shoutcast stream (1=enabled, 0=disabled).
-</div>
-<h4>on-demand</h4>
-<div class="indentedbox">
-    <p>An on-demand relay will only retrieve the stream if there are listeners connected
-    1=enabled, 0=disabled (default is &lt;relays-on-demand&gt;).
-    </p>
-</div>
-
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="mount"></a>
-<h2>Mount Specific Settings</h2>
-<pre>
-    &lt;mount&gt;
-        &lt;mount-name&gt;/example-complex.ogg&lt;/mount-name&gt;
-        &lt;username&gt;othersource&lt;/username&gt;
-        &lt;password&gt;hackmemore&lt;/password&gt;
-        &lt;max-listeners&gt;1&lt;/max-listeners&gt;
-        &lt;max-listener-duration&gt;3600&lt;/max-listener-duration&gt;
-        &lt;dump-file&gt;/tmp/dump-example1.ogg&lt;/dump-file&gt;
-        &lt;intro&gt;/intro.ogg&lt;/intro&gt;
-        &lt;fallback-mount&gt;/example2.ogg&lt;/fallback-mount&gt;
-        &lt;fallback-override&gt;1&lt;/fallback-override&gt;
-        &lt;fallback-when-full&gt;1&lt;/fallback-when-full&gt;
-        &lt;public&gt;1&lt;/public&gt;
-        &lt;stream-name&gt;My audio stream&lt;/stream-name&gt;
-        &lt;stream-description&gt;My audio description&lt;/stream-description&gt;
-        &lt;stream-url&gt;http://some.place.com&lt;/stream-url&gt;
-        &lt;genre&gt;classical&lt;/genre&gt;
-        &lt;bitrate&gt;64&lt;/bitrate&gt;
-        &lt;type&gt;application/ogg&lt;/type&gt;
-        &lt;subtype&gt;vorbis&lt;/subtype&gt;
-        &lt;hidden&gt;1&lt;/hidden&gt;
-        &lt;burst-size&gt;65536&lt;/burst-size&gt;
-        &lt;mp3-metadata-interval&gt;4096&lt;/mp3-metadata-interval&gt;
-        &lt;authentication type="htpasswd"&gt;
-                &lt;option name="filename" value="myauth"/&gt;
-                &lt;option name="allow_duplicate_users" value="0"/&gt;
-        &lt;/authentication&gt;
-        &lt;on-connect&gt;/home/icecast/bin/source-start&lt;/on-connect&gt;
-        &lt;on-disconnect&gt;/home/icecast/bin/source-end&lt;/on-disconnect&gt;
-    &lt;/mount&gt;
-</pre>
-<p>This section contains the settings which apply only to a specific mountpoint and applies to
-an incoming stream whether it is a relay or a source client. The purpose of the mount definition
-is to state certain information that can override either global/default settings or settings
-provided from the incoming stream.
-</p>
-<p>A mount does not need to be stated for each incoming source although you may want to
-specific certain settings like the maximum number of listeners or a mountpoint specific
-username/password.  As a general rule, only define what you need to but each mount definition
-needs at least the mount-name.  Changes to most of these will apply across a configuration file
-re-read even on active streams, however some only apply when the stream starts or ends.
-</p>
-<h4>mount-name</h4>
-<div class="indentedbox">
-The name of the mount point for which these settings apply.
-</div>
-<h4>username</h4>
-<div class="indentedbox">
-An optional value which will set the username that a source must use to connect using this mountpoint.
-</div>
-<h4>password</h4>
-<div class="indentedbox">
-An optional value which will set the password that a source must use to connect using this mountpoint.
-</div>
-<h4>max-listeners</h4>
-<div class="indentedbox">
-An optional value which will set the maximum number of listeners that can be attached to this mountpoint.
-</div>
-<h4>max-listener-duration</h4>
-<div class="indentedbox">
-    An optional value which will set the length of time a listener will stay connected to the
-    stream. An auth component may override this.
-</div>
-<h4>dump-file</h4>
-<div class="indentedbox">
-An optional value which will set the filename which will be a dump of the stream coming through on this mountpoint.
-</div>
-<h4>intro</h4>
-<div class="indentedbox">
-    <p>An optional value which will specify the file those contents will be sent to new listeners
-    when they connect but before the normal stream is sent. Make sure the format of the file
-    specified matches the streaming format.  The specified file is appended to webroot before
-    being opened.
-    </p>
-</div>
-<h4>fallback-mount</h4>
-<div class="indentedbox">
-This optional value specifies a mountpoint that clients are automatically moved to if the source
-shuts down or is not streaming at the time a listener connects.  Only one can be listed in each
-mount and should refer to another mountpoint on the same server that is streaming in the same
-streaming format.<br>
-If clients cannot fallback to another mountpoint, due to a missing fallback-mount or it states a
-mountpoint that is just not available, then those clients will be disconnected.
-If clients are falling back to a mountpoint and the fallback-mount is not actively streaming
-but defines a fallback-mount itself then those clients may be moved there instead.
-This multi-level fallback allows clients to cascade several mountpoints.
-    <p>A fallback mount can also state a file that is located in webroot.  This is useful for
-    playing a pre-recorded file in the case of a stream going down. It will repeat until either
-    the listener disconnects or a stream comes back available and takes the listeners back.
-    As per usual, the file format should match the stream format, failing to do so may cause
-    problems with playback.
-    </p>
-    <p>Note that the fallback file is not timed so be careful if you intend to relay this.
-    They are fine on slave streams but don't use them on master streams, if you do then the
-    relay will consume stream data at a faster rate and the listeners on the relay would
-    eventually get kicked off.
-    </p>
-</div>
-<h4>fallback-override</h4>
-<div class="indentedbox">
-When enabled, this allows a connecting source client or relay on this mountpoint to move
-listening clients back from the fallback mount.
-</div>
-<h4>fallback-when-full</h4>
-<div class="indentedbox">
-    <p>When set to 1, this will cause new listeners, when the max listener count for the
-    mountpoint has been reached, to move to the fallback mount if there is one specified.
-    </p>
-</div>
-<h4>no-yp (deprecated)</h4>
-<div class="indentedbox">
-    <p>Setting this option prevents this mountpoint from advertising on YP.  The default is 0
-    so YP advertising can occur however you may want to prevent it here if you intend listeners
-    to connect to a local relay instead. Deprecated option, replaced by &lt;public&gt;
-    </p>
-</div>
-<h4>public</h4>
-<div class="indentedbox">
-    <p>The default setting for this is -1 indicating that it is up to the source client or
-    relay to determine if this mountpoint should advertise. A setting of 0 will prevent any
-    advertising and a setting of 1 will force it to advertise. If you do force advertising
-    you may need to set other settings listed below as the YP server can refuse to advertise
-    if there is not enough information provided.
-    </p>
-</div>
-<h4>stream-name</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified name to the stats (and therefore YP) for this
-    mountpoint even if the source client/relay provide one.
-    </p>
-</div>
-<h4>stream-description</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified description to the stats (and therefore YP) for
-    this mountpoint even if the source client/relay provide one.
-    </p>
-</div>
-<h4>stream-url</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified URL to the stats (and therefore YP) for this
-    mountpoint even if the source client/relay provide one.  The URL is generally for
-    directing people to a website.
-    </p>
-</div>
-<h4>genre</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified genre to the stats (and therefore YP) for this
-    mountpoint even if the source client/relay provide one. This can be anything be using
-    certain key words can help searches in the YP directories.
-    </p>
-</div>
-<h4>bitrate</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified bitrate to the stats (and therefore YP) for this
-    mountpoint even if the source client/relay provide one.  This is stated in kbps.
-    </p>
-</div>
-<h4>type</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified mime type to the stats (and therefore YP) for
-    this mountpoint even if the source client/relay provide one.  It is very unlikely that
-    this will be needed.
-    </p>
-</div>
-<h4>subtype</h4>
-<div class="indentedbox">
-    <p>Setting this will add the specified subtype to the stats (and therefore YP) for
-    this mountpoint. The subtype is really to help the YP server to identify the components
-    of the type. An example setting is vorbis/theora do indicate the codecs in an Ogg stream
-    </p>
-</div>
-<h4>burst-size</h4>
-<div class="indentedbox">
-This optional setting allows for providing a burst size which overrides the default burst size
-as defined in limits.  The value is in bytes.
-</div>
-<h4>mp3-metadata-interval</h4>
-<div class="indentedbox">
-    <p>This optional setting specifies what interval, in bytes, there is between metadata
-    updates within shoutcast compatible streams. This only applies to new listeners connecting
-    on this mountpoint, not existing listeners falling back to this mountpoint.  The default
-    is either the hardcoded server default or the value passed from a relay.
-    </p>
-</div>
-<h4>hidden</h4>
-<div class="indentedbox">
-Enable this to prevent this mount from being shown on the xsl pages.  This is mainly
-for cases where a local relay is configured and you do not want the source of the local
-relay to be shown
-</div>
-<h4>authentication</h4>
-<div class="indentedbox">
-This specifies that the named mount point will require listener authentication.  Currently, we only support a file-based authentication scheme (type=htpasswd).  Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes.  These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg).  Users and Passwords are maintained via the web admin interface.  A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens.  You can read more about listener authentication <a href="icecast2_listenerauth.html">here</a>.
-</div>
-<h4>on-connect</h4>
-<div class="indentedbox">
-    <p>State a program that is run when the source is started. It is passed a parameter which
-    is the name of the mountpoint that is starting. The processing of the stream does not wait
-    for the script to end. This option is not available on win32
-    </p>
-</div>
-<h4>on-disconnect</h4>
-<div class="indentedbox">
-    <p>State a program that is run when the source ends. It is passed a parameter which is the
-    name of the mountpoint that has ended. The processing of the stream does not wait for the
-    script to end. This option is not available on win32
-    </p>
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="path"></a>
-<h2>Path Settings</h2>
-<pre>
-    &lt;paths&gt;
-        &lt;basedir&gt;./&lt;/basedir&gt;
-        &lt;logdir&gt;./logs&lt;/logdir&gt;
-        &lt;pidfile&gt;./icecast.pid&lt;/pidfile&gt;
-        &lt;webroot&gt;./web&lt;/webroot&gt;
-        &lt;adminroot&gt;./admin&lt;/adminroot&gt;
-        &lt;alias source="/foo" dest="/bar"/&gt;
-    &lt;/paths&gt;
-</pre>
-<p>This section contains paths which are used for various things within icecast.  All paths (other than any aliases) should not end in a '/'.
-</p>
-<h4>basedir</h4>
-<div class="indentedbox">
-This path is used in conjunction with the chroot settings, and specified the base directory that is chrooted to when the server is started.  This feature is not supported on win32.
-</div>
-<h4>logdir</h4>
-<div class="indentedbox">
-This path specifies the base directory used for logging. Both the error.log and access.log will be created relative to this directory.
-</div>
-<h4>pidfile</h4>
-<div class="indentedbox">
-This pathname specifies the file to write at startup and to remove at normal shutdown. The file contains the process id of the icecast process. This could be read and used for sending signals icecast.
-</div>
-<h4>webroot</h4>
-<div class="indentedbox">
-This path specifies the base directory used for all static file requests.  This directory can contain all standard file types (including mp3s and ogg vorbis files).  For example, if webroot is set to /var/share/icecast2, and a request for http://server:port/mp3/stuff.mp3 comes in, then the file /var/share/icecast2/mp3/stuff.mp3 will be served.
-</div>
-<h4>adminroot</h4>
-<div class="indentedbox">
-This path specifies the base directory used for all admin requests.  More specifically, this is used to hold the XSLT scripts used for the web-based admin interface.  The admin directory contained within the icecast distribution contains these files.
-</div>
-<h4>alias source="/foo" dest="/bar"</h4>
-<div class="indentedbox">
-Aliases are used to provide a way to create multiple mountpoints that refer to the same mountpoint.
-</div>
-<p>
-<br />
-<br />
-<br />
-</p>
-<a name="log"></a>
-<h2>Logging Settings</h2>
-<pre>
-    &lt;logging&gt;
-        &lt;accesslog&gt;access.log&lt;/accesslog&gt;
-        &lt;errorlog&gt;error.log&lt;/errorlog&gt;
-        &lt;playlistlog&gt;playlist.log&lt;/playlistlog&gt;
-      	&lt;loglevel&gt;4&lt;/loglevel&gt; &lt;-- 4 Debug, 3 Info, 2 Warn, 1 Error --&gt;
-    &lt;/logging&gt;
-</pre>
-<p>This section contains information relating to logging within icecast.  There are two logfiles currently generated by icecast, an error.log (where all log messages are placed) and an access.log (where all stream/admin/http requests are logged).
-</p>
-<p>Note that on non-win32 platforms, a HUP signal can be sent to icecast in which the log files are re-opened for appending giving the ability move/remove the log files.
-</p>
-<h4>accesslog</h4>
-<div class="indentedbox">
-Into this file, all requests made to the icecast2 will be logged.  This file is relative to the path specified by the &lt;logdir&gt; config value.
-</div>
-<h4>errorlog</h4>
-<div class="indentedbox">
-All icecast generated log messages will be written to this file.  If the loglevel is set too high (Debug for instance) then this file can grow fairly large over time.  Currently, there is no log-rotation implemented.
-</div>
-<h4>playlistlog</h4>
-<div class="indentedbox">
-Into this file, a log of all metadata for each mountpoint will be written.  The format of the logfile will most likely change over time as we narrow in on a standard format for this.  Currently, the file is pipe delimited.  This option is optional and can be removed entirely from the config file.
-</div>
-<h4>logsize</h4>
-<div class="indentedbox">
-This value specifies (in Kbytes) the maxmimum size of any of the log files.  When the logfile grows beyond this value,
-icecast will either rename it to logfile.old, or add a timestamp to the archived file (if logarchive is enabled).
-</div>
-<h4>logarchive</h4>
-<div class="indentedbox">
-If this value is set, then icecast will append a timestamp to the end of the logfile name when logsize has been reached.
-If disabled, then the default behavior is to rename the logfile to logfile.old (overwriting any previously saved
-logfiles).  We disable this by default to prevent the filling up of filesystems for people who don't care (or know) that 
-their logs are growing.
-</div>
-<h4>loglevel</h4>
-<div class="indentedbox">
-Indicates what messages are logged by icecast.  Log messages are categorized into one of 4 types, Debug, Info, Warn, and Error.<br /><br />The following mapping can be used to set the appropraite value :
-<ul>
-<li>loglevel = 4 - Debug, Info, Warn, Error messages are printed</li>
-<li>loglevel = 3 - Info, Warn, Error messages are printed</li>
-<li>loglevel = 2 - Warn, Error messages are printed</li>
-<li>loglevel = 1 - Error messages only are printed</li>
-</ul>
-</div>
-<br />
-<a name="security"></a>
-<h2>Security Settings</h2>
-<pre>
-    &lt;security&gt;
-        &lt;chroot&gt;0&lt;/chroot&gt;
-        &lt;changeowner&gt;
-            &lt;user&gt;nobody&lt;/user&gt;
-            &lt;group&gt;nogroup&lt;/group&gt;
-	&lt;/changeowner&gt;
-    &lt;/security&gt;
-</pre>
-<p>This section contains configuration settings that can be used to secure the icecast server by performing a chroot to a secured location.  This is currently not supported on win32.
-</p>
-<h4>chroot</h4>
-<div class="indentedbox">
-An indicator which specifies whether a chroot() will be done when the server is started.  The chrooted path is specified by the &lt;basedir&gt; configuration value.
-</div>
-<h4>changeowner</h4>
-<div class="indentedbox">
-This section indicates the user and group that will own the icecast process when it is started.  These need to be valid users on the system.
-</div>
-</div>
-</body>
-</html>

Copied: icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html (from rev 14437, icecast/trunk/icecast/doc/icecast2_config_file.html)
===================================================================
--- icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_config_file.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,756 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<title>Icecast v2.x Documentation</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body>
+<div class="boxtest">
+<h1>Icecast 2 Config File</h1>
+<hr id='titlebar' />
+<h3>Overview</h3>
+<p>
+This section will describe each section of the config file and is grouped into the following sections:
+</p>
+<ul>
+<li><a href="#limits">Limits</a></li>
+<li><a href="#authentication">Authentication</a></li>
+<li><a href="#yp">Stream Directory Settings</a></li>
+<li><a href="#misc">Misc Server settings</a></li>
+<li><a href="#relay">Relay settings</a></li>
+<li><a href="#mount">Mount Specific settings</a></li>
+<li><a href="#path">File path settings</a></li>
+<li><a href="#log">Logging</a></li>
+<li><a href="#security">Security</a></li>
+</ul>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="limits"></a>
+<h2>Limits</h2>
+<pre>
+    &lt;limits&gt;
+        &lt;clients&gt;100&lt;/clients&gt;
+        &lt;sources&gt;2&lt;/sources&gt;
+        &lt;queue-size&gt;102400&lt;/queue-size&gt;
+        &lt;client-timeout&gt;30&lt;/client-timeout&gt;
+        &lt;header-timeout&gt;15&lt;/header-timeout&gt;
+        &lt;source-timeout&gt;10&lt;/source-timeout&gt;
+        &lt;burst-on-connect&gt;1&lt;/burst-on-connect&gt;
+        &lt;burst-size&gt;65536&lt;/burst-size&gt;
+    &lt;/limits&gt;
+</pre>
+<p>This section contains server level settings that, in general, do not need to be changed.  Only modify this section if you are know what you are doing.
+</p>
+<h4>clients</h4>
+<div class="indentedbox">
+Total number of concurrent clients supported by the server.  Listeners are considered clients, but so are accesses to any static content (i.e. fileserved content) and also any requests to gather stats.  These are max *concurrent* connections for the entire server (not per mountpoint).
+</div>
+<h4>sources</h4>
+<div class="indentedbox">
+Maximum number of connected sources supported by the server. This includes active relays and source clients
+</div>
+<h4>queue-size</h4>
+<div class="indentedbox">
+    This is the maximum size (in bytes) of the stream queue.  A listener may temporarily lag behind
+    due to network congestion and in this case an internal queue is maintained for the listeners. 
+    If the queue grows larger than this config value, then it is truncated and any listeners found
+    will be removed from the stream.<br />
+    This will be the default setting for the streams which is 512k unless overridden here. You can
+    override this in the individual mount settings which can be useful if you have a mixture of high
+    bandwidth video and low bitrate audio streams.
+</div>
+<h4>client-timeout</h4>
+<div class="indentedbox">
+This does not seem to be used.
+</div>
+<h4>header-timeout</h4>
+<div class="indentedbox">
+The maximum time (in seconds) to wait for a request to come in once the client has made a connection to the server.  In general this value should not need to be tweaked.
+</div>
+<h4>source-timeout</h4>
+<div class="indentedbox">
+If a connected source does not send any data within this timeout period (in seconds), then the source connection will be removed from the server.
+</div>
+<h4>burst-on-connect</h4>
+<div class="indentedbox">
+    This setting is really just an alias for burst-size. When enabled the burst-size is 64 kbytes
+    and disabled the burst-size is 0 kbytes. This option is deprecated, use burst-size instead.
+</div>
+<h4>burst-size</h4>
+<div class="indentedbox">
+The burst size is the amount of data (in bytes) to burst to a client at connection time. Like
+burst-on-connect, this is to quickly fill the pre-buffer used by media players. The default
+is 64 kbytes which is a typical size used by most clients so changing it is not usually required.
+This setting applies to all mountpoints unless overridden in the mount settings.
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="authentication"></a>
+<h2>Authentication</h2>
+<pre>
+    &lt;authentication&gt;
+        &lt;source-password&gt;hackme&lt;/source-password&gt;
+        &lt;relay-user&gt;relay&lt;/relay-user&gt;
+        &lt;relay-password&gt;hackme&lt;/relay-password&gt;
+        &lt;admin-user&gt;admin&lt;/admin-user&gt;
+        &lt;admin-password&gt;hackme&lt;/admin-password&gt;
+    &lt;/authentication&gt;
+</pre>
+<p>This section contains all the usernames and passwords used for administration purposes or to connect sources and relays.
+</p>
+<h4>source-password</h4>
+<div class="indentedbox">
+    The unencrypted password used by sources to connect to icecast2.  The default username
+    for all source connections is 'source' but this option allows to specify a default
+    password.  This and the username can be changed in the individual mount sections.
+</div>
+<h4>relay-user</h4>
+<div class="indentedbox">
+Used in the master server as part of the authentication when a slave requests
+the list of streams to relay.  The default username is 'relay'
+</div>
+<h4>relay-password</h4>
+<div class="indentedbox">
+Used in the master server as part of the authentication when a slave requests
+the list of streams to relay.
+</div>
+<h4>admin-user</h4>
+<h4>admin-password</h4>
+<div class="indentedbox">
+The username/password used for all administration functions.  This includes retrieving statistics, accessing the web-based administration screens, etc.  A list of these functions can be found in the "Administration" section of the manual.
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="yp"></a>
+<h2>Stream Directory Settings</h2>
+<pre>
+    &lt;directory&gt;
+        &lt;yp-url-timeout&gt;15&lt;/yp-url-timeout&gt;
+        &lt;yp-url&gt;http://dir.xiph.org/cgi-bin/yp-cgi&lt;/yp-url&gt;
+    &lt;/directory&gt;
+</pre>
+<p>This section contains all the settings for listing a stream on any of the Icecast2 YP Directory servers.  Multiple occurances of this section can be specified in order to be listed on multiple directory servers.
+</p>
+<h4>yp-url-timeout</h4>
+<div class="indentedbox">
+This value is the maximum time icecast2 will wait for a response from a particular directory server.  The recommended value should be sufficient for most directory servers.
+</div>
+<h4>yp-url</h4>
+<div class="indentedbox">
+The URL which icecast2 uses to communicate with the Directory server.  The value for this setting is provided by the owner of the Directory server.
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="misc"></a>
+<h2>Misc Server Settings</h2>
+<p>Server wide settings.</p>
+<pre>
+    &lt;hostname&gt;localhost&lt;hostname&gt;
+    &lt;fileserve&gt;1&lt;/fileserve&gt;
+    &lt;server-id&gt;icecast 2.3&lt;/server-id&gt;
+</pre>
+
+<h4>hostname</h4>
+<div class="indentedbox">
+    This is the DNS name or IP address that will be used for the stream directory lookups or
+    possibily the playlist generation if a Host header is not provided. While localhost is shown
+    as an example, in fact you will want something that your listeners can use.
+</div>
+<h4>fileserve</h4>
+<div class="indentedbox">
+    This flag turns on the icecast2 fileserver from which static files can be served.  All files
+    are served relative to the path specified in the &lt;paths&gt;&lt;webroot&gt; configuration
+    setting. By default the setting is enabled so that requests for the images on the status
+    page are retrievable.
+</div>
+<h4>server-id</h4>
+<div class="indentedbox">
+    This optional setting allows for the administrator of the server to override the default
+    server identification. The default is icecast followed by a version number and most will
+    not care to change it however this setting will change that.
+</div>
+
+<p>The following shows how you can specify the listening settings for the server.</p><p>The
+first shows an example of a common and simple way to define a listening socket
+<pre>
+    &lt;listen-socket&gt;
+        &lt;port&gt;8000&lt;/port&gt;
+    &lt;/listen-socket&gt;
+</pre>
+<p>Using this as a basis we can extend this with an &lt;bind-address&gt; setting to limit which
+address icecast will listen on. Most will not need to use bind-address and often get confused
+by using it when there is no need. Another possibility is to use an &lt;ssl&gt; boolean setting
+which informs icecast that a secured connection is to be used. A common use for using a secure
+connection would be for admin page access.</p>
+<p>The following shows how we can extend a single listen-socket to work with shoutcast style
+source clients. There are two issues shoutcast source clients have over icecast source clients,
+one is the lack of mountpoint and the second is the requirement of two ports. Both of these
+issues are handled by a simple addition in the listen-socket.</p>
+<pre>
+    &lt;listen-socket&gt;
+        &lt;port&gt;8000&lt;/port&gt;
+        &lt;shoutcast-mount&gt;/live.mp3&lt;/shoutcast-mount&gt;
+    &lt;/listen-socket&gt;
+</pre>
+<p>As before the port specified is allocated but this time the shoutcast-mount implicity defines
+a second listening socket whose port number is always one higher than the port defined, this also
+informs icecast of which mountpoint the shoutcast source client on this socket will be using.
+Using this approach you can allow multiple shoutcast source clients to connect at the same time.
+<p>The following is just to show the longer approach to defining shoutcast compatability.</p>
+<pre>
+    &lt;shoutcast-mount&gt;/live.nsv&lt;/shoutcast-mount&gt;
+
+    &lt;-- You may have multiple &lt;listen-socket&gt; elements --&gt;
+    &lt;listen-socket&gt;
+        &lt;port&gt;8000&lt;/port&gt;
+    &lt;/listen-socket&gt;
+
+    &lt;listen-socket&gt;
+        &lt;port&gt;8001&lt;/port&gt;
+        &lt;shoutcast-compat&gt;1&lt;/shoutcast-compat&gt;
+    &lt;/listen-socket&gt;
+</pre>
+
+<p>Note that multiple listen-socket sections may be configured in order to have icecast2 listen
+on multiple network interfaces or multiple ports.
+If a bind-address is not specified for a particular listen-socket, then the socket will be
+bound to all interfaces (including IPv6 if available). For most people, the bind-address option
+will not be required and often confuses people.
+</p>
+<h4>port</h4>
+<div class="indentedbox">
+The TCP port that will be used to accept client connections.
+</div>
+<h4>bind-address</h4>
+<div class="indentedbox">
+An optional IP address that can be used to bind to a specific network card.  If not supplied, then it will bind to all interfaces.
+</div>
+<h4>shoutcast-mount</h4>
+<div class="indentedbox">
+    An optional mountpoint setting to be used when shoutcast DSP compatible clients connect. The
+    default global setting is /stream but can be overridden here to use an alternative name which
+    may include an extension that some clients require for certain formats.<br /><br />
+    Defining this within a listen-socket group tells icecast that this port and the subsequent
+    port are to be used for shoutcast compatible source clients. This is an alternative to the
+    shoutcast-compat approach as this implicitly defines the second listening socket and allows
+    for specifying multiple sockets using different mountpoints for shoutcast source
+    clients. The shoutcast-mount outside of a listen-socket group is the global setting of the
+    mountpoint to use.
+</div>
+<h4>shoutcast-compat</h4>
+<div class="indentedbox">
+This optional flag will indicate that this port will operate in 'shoutcast-compatibility' mode.  Due to major differences in the source client connection protocol, if you wish to use any of the shoutcast DJ tools, you will need to configure at least one socket as shoutcast-compatible.  Note that when in this mode, only source clients (and specifically shoutcast source clients) will be able to attach to this port.  All listeners may connect to any of the ports defined without this flag.  Also, for proper Shoutcast DSP compatibility, you must define a listen socket with a port one less than the one defined as 'shoutcast-compat'.  This means if you define 8001 as shoutcast-compat, then you will need to define a listen port of 8000 and it must not also be defined as shoutcast-compat.  See the example config file in the distribution for more info.
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="relay"></a>
+<h2>Relaying Streams</h2>
+
+<p>This section contains the servers relay settings. The relays are implemented using a pull system where
+the receiving server connects as if its a listener to the sending server.  There are two types of relay
+setups: a "Master server relay" or a "Specific Mountpoint relay." 
+</p>
+<h3>Master Relay</h3>
+<p>
+A Master server relay is only supported between icecast2 servers and is used to relay a number of
+mountpoints from a remote icecast2 server.
+<pre>
+    &lt;master-server&gt;127.0.0.1&lt;/master-server&gt;
+    &lt;master-server-port&gt;8001&lt;/master-server-port&gt;
+    &lt;master-update-interval&gt;120&lt;/master-update-interval&gt;
+    &lt;master-username&gt;relay&lt;/master-username&gt;
+    &lt;master-password&gt;hackme&lt;/master-password&gt;
+    &lt;relays-on-demand&gt;0&lt;/relays-on-demand&gt;
+</pre>
+<br />
+<p>The following diagram shows the basics of using a Master relay.  Please note that the slave is
+configured with the &lt;master-server&gt;, &lt;master-server-port&gt;, etc settings and the
+master is the icecast server from which the slave will pull mountpoints and relay them.   Using a
+Master server relay, all non-hidden mountpoints on the master can be relayed using this mechanism. </p>
+<br />
+<img src="masterslave.png">
+<p>
+A server is configured as a Master Server relay by specifying the &lt;master-server&gt;, &lt;master-server-port&gt;,&lt;master-update-interval&gt;,&lt;master-password&gt; values in the config file.  The server that is being relayed does not need any special configuration.
+</p>
+
+<h4>master-server</h4>
+<div class="indentedbox">
+This is the IP for the server which contains the mountpoints to be relayed (Master Server).
+</div>
+<h4>master-server-port</h4>
+<div class="indentedbox">
+This is the TCP Port for the server which contains the mountpoints to be relayed (Master Server).
+</div>
+<h4>master-update-interval</h4>
+<div class="indentedbox">
+The interval (in seconds) that the Relay Server will poll the Master Server for any new mountpoints to relay.
+</div>
+<h4>master-username</h4>
+<div class="indentedbox">
+This is the relay username on the master server.  It is used to query the
+server for a list of mountpoints to relay. If not specified then 'relay' is
+used
+</div>
+<h4>master-password</h4>
+<div class="indentedbox">
+This is the relay password on the Master server.  It is used to query the
+server for a list of mountpoints to relay.
+</div>
+<h4>relays-on-demand</h4>
+<div class="indentedbox">
+Global on-demand setting for relays. Because you do not have individual relay options when using a
+master server relay, you still may want those relays to only pull the stream when there is at least
+one listener on the slave. The typical case here is to avoid surplus bandwidth costs when no one is
+listening.
+</div>
+
+<br />
+<h3>Specific Mountpoint Relay</h3>
+<p>
+If only specific mountpoints need to be relayed, then you can configure Icecast with a "Specific
+Mountpoint Relay". 
+</p>
+The following diagram shows the basics of using a Specific Mountpoint relay.  Note that the relaying
+Icecast is configured with the &lt;relay&gt; settings and will pull the specified mountpoint(s) and
+relay them to the listeners.  Using a Specific Mountpoint Relay, only those mountpoints specified
+will be relayed.
+
+<br /><br />
+<img src="relay.png">
+<p>
+Specific Mountpoint Relays can be configured to relay from an Icecast 2 server, as well as Icecast 1.x and Shoutcast.
+A server is configured as a Specific Mountpoint Server relay by specifying a &lt;relay&gt; XML chunk in the config file for each mountpoint to be relayed.  The server that is being relayed does not need any special configuration.
+</p>
+<pre>
+    &lt;relay&gt;
+        &lt;server&gt;127.0.0.1&lt;/server&gt;
+        &lt;port&gt;8001&lt;/port&gt;
+        &lt;mount&gt;/example.ogg&lt;/mount&gt;
+        &lt;local-mount&gt;/different.ogg&lt;/local-mount&gt;
+        &lt;username&gt;joe&lt;/username&gt;
+        &lt;password&gt;soap&lt;/password&gt;
+        &lt;relay-shoutcast-metadata&gt;0&lt;/relay-shoutcast-metadata&gt;
+        &lt;on-demand&gt;1&lt;/on-demand&gt;
+    &lt;/relay&gt;
+</pre>
+
+
+<h4>server</h4>
+<div class="indentedbox">
+This is the IP for the server which contains the mountpoint to be relayed.
+</div>
+<h4>port</h4>
+<div class="indentedbox">
+This is the TCP Port for the server which contains the mountpoint to be relayed.
+</div>
+<h4>mount</h4>
+<div class="indentedbox">
+    The mountpoint located on the remote server.  If you are relaying a shoutcast stream, this
+    should be a '/' or '/;name'. 
+</div>
+<h4>local-mount</h4>
+<div class="indentedbox">
+    The name to use for the local mountpoint.  This is what the mount will be named on the relaying
+    server. By default the remote mountpoint name is used.
+</div>
+<h4>username</h4>
+<div class="indentedbox">
+The source of the relay may require authentication itself, if so state the username here.
+</div>
+<h4>password</h4>
+<div class="indentedbox">
+The source of the relay may require authentication itself, if so state the password here.
+</div>
+<h4>relay-shoutcast-metadata</h4>
+<div class="indentedbox">
+    If you are relaying a Shoutcast stream, you may want to specify this indicator to also relay
+    the metadata (song titles) that are part of the Shoutcast data stream (1=enabled, 0=disabled).
+    By default this is enabled but it is up to the remote server on whether it sends any.
+</div>
+<h4>on-demand</h4>
+<div class="indentedbox">
+    <p>An on-demand relay will only retrieve the stream if there are listeners requesting the
+    stream.  1=enabled, 0=disabled (default is &lt;relays-on-demand&gt;). This is useful in cases
+    where you want to limit bandwidth costs when no one is listening. </p>
+</div>
+
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="mount"></a>
+<h2>Mount Specific Settings</h2>
+<pre>
+    &lt;mount&gt;
+        &lt;mount-name&gt;/example-complex.ogg&lt;/mount-name&gt;
+        &lt;username&gt;othersource&lt;/username&gt;
+        &lt;password&gt;hackmemore&lt;/password&gt;
+        &lt;max-listeners&gt;1&lt;/max-listeners&gt;
+        &lt;max-listener-duration&gt;3600&lt;/max-listener-duration&gt;
+        &lt;dump-file&gt;/tmp/dump-example1.ogg&lt;/dump-file&gt;
+        &lt;intro&gt;/intro.ogg&lt;/intro&gt;
+        &lt;fallback-mount&gt;/example2.ogg&lt;/fallback-mount&gt;
+        &lt;fallback-override&gt;1&lt;/fallback-override&gt;
+        &lt;fallback-when-full&gt;1&lt;/fallback-when-full&gt;
+        &lt;charset&gt;ISO8859-1&lt;/charset&gt;
+        &lt;public&gt;1&lt;/public&gt;
+        &lt;stream-name&gt;My audio stream&lt;/stream-name&gt;
+        &lt;stream-description&gt;My audio description&lt;/stream-description&gt;
+        &lt;stream-url&gt;http://some.place.com&lt;/stream-url&gt;
+        &lt;genre&gt;classical&lt;/genre&gt;
+        &lt;bitrate&gt;64&lt;/bitrate&gt;
+        &lt;type&gt;application/ogg&lt;/type&gt;
+        &lt;subtype&gt;vorbis&lt;/subtype&gt;
+        &lt;hidden&gt;1&lt;/hidden&gt;
+        &lt;burst-size&gt;65536&lt;/burst-size&gt;
+        &lt;mp3-metadata-interval&gt;4096&lt;/mp3-metadata-interval&gt;
+        &lt;authentication type="htpasswd"&gt;
+                &lt;option name="filename" value="myauth"/&gt;
+                &lt;option name="allow_duplicate_users" value="0"/&gt;
+        &lt;/authentication&gt;
+        &lt;on-connect&gt;/home/icecast/bin/source-start&lt;/on-connect&gt;
+        &lt;on-disconnect&gt;/home/icecast/bin/source-end&lt;/on-disconnect&gt;
+    &lt;/mount&gt;
+</pre>
+<p>This section contains the settings which apply only to a specific mountpoint and applies to
+an incoming stream whether it is a relay or a source client. The purpose of the mount definition
+is to state certain information that can override either global/default settings or settings
+provided from the incoming stream.
+</p>
+<p>A mount does not need to be stated for each incoming source although you may want to
+specific certain settings like the maximum number of listeners or a mountpoint specific
+username/password.  As a general rule, only define what you need to but each mount definition
+needs at least the mount-name.  Changes to most of these will apply across a configuration file
+re-read even on active streams, however some only apply when the stream starts or ends.
+</p>
+<h4>mount-name</h4>
+<div class="indentedbox">
+The name of the mount point for which these settings apply.
+</div>
+<h4>username</h4>
+<div class="indentedbox">
+An optional value which will set the username that a source must use to connect using this mountpoint.
+</div>
+<h4>password</h4>
+<div class="indentedbox">
+An optional value which will set the password that a source must use to connect using this mountpoint.
+</div>
+<h4>max-listeners</h4>
+<div class="indentedbox">
+An optional value which will set the maximum number of listeners that can be attached to this mountpoint.
+</div>
+<h4>max-listener-duration</h4>
+<div class="indentedbox">
+    An optional value which will set the length of time a listener will stay connected to the
+    stream. An auth component may override this.
+</div>
+<h4>dump-file</h4>
+<div class="indentedbox">
+An optional value which will set the filename which will be a dump of the stream coming through on this mountpoint.
+</div>
+<h4>intro</h4>
+<div class="indentedbox">
+    <p>An optional value which will specify the file those contents will be sent to new listeners
+    when they connect but before the normal stream is sent. Make sure the format of the file
+    specified matches the streaming format.  The specified file is appended to webroot before
+    being opened.
+    </p>
+</div>
+<h4>fallback-mount</h4>
+<div class="indentedbox">
+This optional value specifies a mountpoint that clients are automatically moved to if the source
+shuts down or is not streaming at the time a listener connects.  Only one can be listed in each
+mount and should refer to another mountpoint on the same server that is streaming in the same
+streaming format.<br>
+If clients cannot fallback to another mountpoint, due to a missing fallback-mount or it states a
+mountpoint that is just not available, then those clients will be disconnected.
+If clients are falling back to a mountpoint and the fallback-mount is not actively streaming
+but defines a fallback-mount itself then those clients may be moved there instead.
+This multi-level fallback allows clients to cascade several mountpoints.
+    <p>A fallback mount can also state a file that is located in webroot.  This is useful for
+    playing a pre-recorded file in the case of a stream going down. It will repeat until either
+    the listener disconnects or a stream comes back available and takes the listeners back.
+    As per usual, the file format should match the stream format, failing to do so may cause
+    problems with playback.
+    </p>
+    <p>Note that the fallback file is not timed so be careful if you intend to relay this.
+    They are fine on slave streams but don't use them on master streams, if you do then the
+    relay will consume stream data at a faster rate and the listeners on the relay would
+    eventually get kicked off.
+    </p>
+</div>
+<h4>fallback-override</h4>
+<div class="indentedbox">
+When enabled, this allows a connecting source client or relay on this mountpoint to move
+listening clients back from the fallback mount.
+</div>
+<h4>fallback-when-full</h4>
+<div class="indentedbox">
+    <p>When set to 1, this will cause new listeners, when the max listener count for the
+    mountpoint has been reached, to move to the fallback mount if there is one specified.
+    </p>
+</div>
+<h4>no-yp (deprecated)</h4>
+<div class="indentedbox">
+    <p>Setting this option prevents this mountpoint from advertising on YP.  The default is 0
+    so YP advertising can occur however you may want to prevent it here if you intend listeners
+    to connect to a local relay instead. Deprecated option, replaced by &lt;public&gt;
+    </p>
+</div>
+<h4>charset</h4>
+<div class="indentedbox">
+    <p>For non-Ogg streams like MP3, the metadata that is inserted into the stream often has no
+    defined character set. We have traditionally assumed UTF8 as it allows for multiple language
+    sets on the web pages and stream directory, however many source clients for MP3 type streams
+    have assumed Latin1 (ISO 8859-1) or leave it to whatever character set is in use on the
+    source client system.</p>
+    <p>This character mismatch has been known to cause a problem as the stats engine and stream
+    directory servers want UTF8 so now we assume Latin1 for non-Ogg streams (to handle the common
+    case) but you can specify an alternative character set with this option.
+    <p>The source clients can also specify a charset= parameter to the metadata update URL if
+    they so wish.</p>
+</div>
+<h4>public</h4>
+<div class="indentedbox">
+    <p>The default setting for this is -1 indicating that it is up to the source client or
+    relay to determine if this mountpoint should advertise. A setting of 0 will prevent any
+    advertising and a setting of 1 will force it to advertise. If you do force advertising
+    you may need to set other settings listed below as the YP server can refuse to advertise
+    if there is not enough information provided.
+    </p>
+</div>
+<h4>stream-name</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified name to the stats (and therefore YP) for this
+    mountpoint even if the source client/relay provide one.
+    </p>
+</div>
+<h4>stream-description</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified description to the stats (and therefore YP) for
+    this mountpoint even if the source client/relay provide one.
+    </p>
+</div>
+<h4>stream-url</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified URL to the stats (and therefore YP) for this
+    mountpoint even if the source client/relay provide one.  The URL is generally for
+    directing people to a website.
+    </p>
+</div>
+<h4>genre</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified genre to the stats (and therefore YP) for this
+    mountpoint even if the source client/relay provide one. This can be anything be using
+    certain key words can help searches in the YP directories.
+    </p>
+</div>
+<h4>bitrate</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified bitrate to the stats (and therefore YP) for this
+    mountpoint even if the source client/relay provide one.  This is stated in kbps.
+    </p>
+</div>
+<h4>type</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified mime type to the stats (and therefore YP) for
+    this mountpoint even if the source client/relay provide one.  It is very unlikely that
+    this will be needed.
+    </p>
+</div>
+<h4>subtype</h4>
+<div class="indentedbox">
+    <p>Setting this will add the specified subtype to the stats (and therefore YP) for
+    this mountpoint. The subtype is really to help the YP server to identify the components
+    of the type. An example setting is vorbis/theora do indicate the codecs in an Ogg stream
+    </p>
+</div>
+<h4>burst-size</h4>
+<div class="indentedbox">
+This optional setting allows for providing a burst size which overrides the default burst size
+as defined in limits.  The value is in bytes.
+</div>
+<h4>mp3-metadata-interval</h4>
+<div class="indentedbox">
+    <p>This optional setting specifies what interval, in bytes, there is between metadata
+    updates within shoutcast compatible streams. This only applies to new listeners connecting
+    on this mountpoint, not existing listeners falling back to this mountpoint.  The default
+    is either the hardcoded server default or the value passed from a relay.
+    </p>
+</div>
+<h4>hidden</h4>
+<div class="indentedbox">
+Enable this to prevent this mount from being shown on the xsl pages.  This is mainly
+for cases where a local relay is configured and you do not want the source of the local
+relay to be shown
+</div>
+<h4>authentication</h4>
+<div class="indentedbox">
+This specifies that the named mount point will require listener authentication.  Currently, we only support a file-based authentication scheme (type=htpasswd).  Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes.  These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg).  Users and Passwords are maintained via the web admin interface.  A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens.  You can read more about listener authentication <a href="icecast2_listenerauth.html">here</a>.
+</div>
+<h4>on-connect</h4>
+<div class="indentedbox">
+    <p>State a program that is run when the source is started. It is passed a parameter which
+    is the name of the mountpoint that is starting. The processing of the stream does not wait
+    for the script to end. This option is not available on win32
+    </p>
+</div>
+<h4>on-disconnect</h4>
+<div class="indentedbox">
+    <p>State a program that is run when the source ends. It is passed a parameter which is the
+    name of the mountpoint that has ended. The processing of the stream does not wait for the
+    script to end. This option is not available on win32
+    </p>
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="path"></a>
+<h2>Path Settings</h2>
+<pre>
+    &lt;paths&gt;
+        &lt;basedir&gt;./&lt;/basedir&gt;
+        &lt;logdir&gt;./logs&lt;/logdir&gt;
+        &lt;pidfile&gt;./icecast.pid&lt;/pidfile&gt;
+        &lt;webroot&gt;./web&lt;/webroot&gt;
+        &lt;adminroot&gt;./admin&lt;/adminroot&gt;
+        &lt;allow-ip&gt;/path/to/ip_allowlist&lt;/allow-ip&gt;
+        &lt;deny-ip&gt;/path_to_ip_denylist&lt;/deny-ip&gt;
+        &lt;alias source="/foo" dest="/bar"/&gt;
+    &lt;/paths&gt;
+</pre>
+<p>This section contains paths which are used for various things within icecast.  All paths (other than any aliases) should not end in a '/'.
+</p>
+<h4>basedir</h4>
+<div class="indentedbox">
+This path is used in conjunction with the chroot settings, and specified the base directory that is chrooted to when the server is started.  This feature is not supported on win32.
+</div>
+<h4>logdir</h4>
+<div class="indentedbox">
+This path specifies the base directory used for logging. Both the error.log and access.log will be created relative to this directory.
+</div>
+<h4>pidfile</h4>
+<div class="indentedbox">
+This pathname specifies the file to write at startup and to remove at normal shutdown. The file contains the process id of the icecast process. This could be read and used for sending signals icecast.
+</div>
+<h4>webroot</h4>
+<div class="indentedbox">
+This path specifies the base directory used for all static file requests.  This directory can contain all standard file types (including mp3s and ogg vorbis files).  For example, if webroot is set to /var/share/icecast2, and a request for http://server:port/mp3/stuff.mp3 comes in, then the file /var/share/icecast2/mp3/stuff.mp3 will be served.
+</div>
+<h4>adminroot</h4>
+<div class="indentedbox">
+This path specifies the base directory used for all admin requests.  More specifically, this is used to hold the XSLT scripts used for the web-based admin interface.  The admin directory contained within the icecast distribution contains these files.
+</div>
+<h4>allow-ip</h4>
+<div class="indentedbox">
+    If specified, this specifies the location of a file that contains a list of IP addresses that
+    will be allowed to connect to icecast. This could be useful in cases where a master only
+    feeds known slaves. The format of the file is simple, one IP per line.
+</div>
+<h4>deny-ip</h4>
+<div class="indentedbox">
+    If specified, this specifies the location of a file that contains a list of IP addressess that
+    will be dropped immediately. This is mainly for problem clients when you have no access to any
+    firewall configuration. The format of the file is simple, one IP per line.
+</div>
+<h4>alias source="/foo" dest="/bar"</h4>
+<div class="indentedbox">
+Aliases are used to provide a way to create multiple mountpoints that refer to the same mountpoint.
+</div>
+<p>
+<br />
+<br />
+<br />
+</p>
+<a name="log"></a>
+<h2>Logging Settings</h2>
+<pre>
+    &lt;logging&gt;
+        &lt;accesslog&gt;access.log&lt;/accesslog&gt;
+        &lt;errorlog&gt;error.log&lt;/errorlog&gt;
+        &lt;playlistlog&gt;playlist.log&lt;/playlistlog&gt;
+      	&lt;loglevel&gt;4&lt;/loglevel&gt; &lt;-- 4 Debug, 3 Info, 2 Warn, 1 Error --&gt;
+    &lt;/logging&gt;
+</pre>
+<p>This section contains information relating to logging within icecast.  There are two logfiles currently generated by icecast, an error.log (where all log messages are placed) and an access.log (where all stream/admin/http requests are logged).
+</p>
+<p>Note that on non-win32 platforms, a HUP signal can be sent to icecast in which the log files are re-opened for appending giving the ability move/remove the log files.
+</p>
+<h4>accesslog</h4>
+<div class="indentedbox">
+Into this file, all requests made to the icecast2 will be logged.  This file is relative to the path specified by the &lt;logdir&gt; config value.
+</div>
+<h4>errorlog</h4>
+<div class="indentedbox">
+All icecast generated log messages will be written to this file.  If the loglevel is set too high (Debug for instance) then this file can grow fairly large over time.  Currently, there is no log-rotation implemented.
+</div>
+<h4>playlistlog</h4>
+<div class="indentedbox">
+Into this file, a log of all metadata for each mountpoint will be written.  The format of the logfile will most likely change over time as we narrow in on a standard format for this.  Currently, the file is pipe delimited.  This option is optional and can be removed entirely from the config file.
+</div>
+<h4>logsize</h4>
+<div class="indentedbox">
+This value specifies (in Kbytes) the maxmimum size of any of the log files.  When the logfile grows beyond this value,
+icecast will either rename it to logfile.old, or add a timestamp to the archived file (if logarchive is enabled).
+</div>
+<h4>logarchive</h4>
+<div class="indentedbox">
+If this value is set, then icecast will append a timestamp to the end of the logfile name when logsize has been reached.
+If disabled, then the default behavior is to rename the logfile to logfile.old (overwriting any previously saved
+logfiles).  We disable this by default to prevent the filling up of filesystems for people who don't care (or know) that 
+their logs are growing.
+</div>
+<h4>loglevel</h4>
+<div class="indentedbox">
+Indicates what messages are logged by icecast.  Log messages are categorized into one of 4 types, Debug, Info, Warn, and Error.<br /><br />The following mapping can be used to set the appropraite value :
+<ul>
+<li>loglevel = 4 - Debug, Info, Warn, Error messages are printed</li>
+<li>loglevel = 3 - Info, Warn, Error messages are printed</li>
+<li>loglevel = 2 - Warn, Error messages are printed</li>
+<li>loglevel = 1 - Error messages only are printed</li>
+</ul>
+</div>
+<br />
+<a name="security"></a>
+<h2>Security Settings</h2>
+<pre>
+    &lt;security&gt;
+        &lt;chroot&gt;0&lt;/chroot&gt;
+        &lt;changeowner&gt;
+            &lt;user&gt;nobody&lt;/user&gt;
+            &lt;group&gt;nogroup&lt;/group&gt;
+	&lt;/changeowner&gt;
+    &lt;/security&gt;
+</pre>
+<p>This section contains configuration settings that can be used to secure the icecast server by performing a chroot to a secured location.  This is currently not supported on win32.
+</p>
+<h4>chroot</h4>
+<div class="indentedbox">
+An indicator which specifies whether a chroot() will be done when the server is started.  The chrooted path is specified by the &lt;basedir&gt; configuration value.
+</div>
+<h4>changeowner</h4>
+<div class="indentedbox">
+This section indicates the user and group that will own the icecast process when it is started.  These need to be valid users on the system.
+</div>
+</div>
+</body>
+</html>

Deleted: icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html
===================================================================
--- icecast/trunk/icecast/doc/icecast2_listenerauth.html	2007-11-28 01:15:16 UTC (rev 14243)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,162 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-<title>Icecast v2.x Documentation</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body>
-<div class="boxtest">
-<h1>Icecast 2 Listener Authentication</h1>
-<hr id='titlebar' />
-<br />
-<br />
-<br />
-<h2>Listener Authentication</h2>
-<p>Listener authentication is a feature of icecast which allows you to secure a certain
-mountpoint such that in order to listen, a listener must pass some verification test.  With
-this feature, a simple pay-for-play operation (eg user/pass), or some filtering based on the
-listener connection can be performed.  This section will show you the basics of setting up
-and maintaining this component.</p>
-<p>To define listener authentication, a group of tags are specified in the &lt;mount&gt;
-group relating to the mountpoint. This means that authentication can apply to listeners of
-source clients or relays.</p>
-<br />
-<p>The following authentication mechanisms can apply to listeners</p>
-<ul>
-    <li>HTPASSWD - lookup a named file for a matching username and password</li>
-    <li>URL - issue web requests (eg PHP) to match authentication</li>
-</ul>
-<p>The listener authentication within a specified mount in the icecast XML configuration
-can apply to either to a stream from a source client, relay or a webroot based file. They
-do apply to intro files or fallback streams.
-</p>
-<br />
-<h2>HTPASSWD Listener Authentication</h2>
-<p>In order to use listener authentication, you MUST configure a mount specific option.  This means that you have to provide a &lt;mount&gt; section in the main icecast config file.  The following is an example :</p>
-<pre>
-    &lt;mount&gt;
-        &lt;mount-name&gt;/example.ogg&lt;/mount-name&gt;
-        &lt;authentication type="htpasswd"&gt;
-                &lt;option name="filename" value="myauth"/&gt;
-                &lt;option name="allow_duplicate_users" value="0"/&gt;
-        &lt;/authentication&gt;
-    &lt;/mount&gt;
-</pre>
-<p>To support listener authentication you MUST provide at a minimum &lt;mount-name&gt; and &lt;authentication&gt;.  The mount-name is the name of the mountpoint that you will use to connect your source client with and authentication configures what type of icecast2 authenticator to use.  Currently, only a single type "htpasswd" is implemented.  New authenticators will be added later.  Each authenticator has a variable number of options that are required and these are specified as shown in the example.  The htpasswd authenticator requires a few parameters.  The first, filename, specifies the name of the file to use to store users and passwords.  Note that this file need not exist (and probably will not exist when you first set it up).  Icecast has built-in support for managing users and passwords via the web admin interface.  More on this later in this section.  The second option, allow_duplicate_users, if set to 0, will prevent multiple connections using the same username.  
 Setting this value to 1 will enable mutltiple connections from the same username on a given mountpoint.  Note there is no way to specify a "max connections" for a particular user.
-</p>
-<p>Icecast supports a mixture of streams that require listener authentication and those that do not.  Only mounts that are named in the config file can be configured for listener authentication.</p>
-<br />
-<br />
-<h3>Configuring Users and Passwords</h3>
-<p>Once the appropriate entries are made to the config file, connect your source client (using the mountpoint you named in the config file).  To configure users and passwords for this stream you must use the web-based admin interface.  Navigate to http://server:ip/admin/stats.xsl to begin.  If you have configured everything properly, you should see a screen like the following :</p>
-<img src="listener_auth1.jpg" alt="Screenshot of http://server:ip/admin/stats.xsl" />
-<p>You will see a red key in front of all mountpoint configured for listener authentication.  Also note that this page will only show CONNECTED mountpoints.</p>
-<p>To manage users and passwords for this mountpoint, click on the red key or follow the "Manage Authentication" link.  The following screen will be shown :</p>
-<img src="listener_auth2.jpg" alt="Screenshot of Manage Authentication" />
-<p>This screen will show all the users configured for this mountpoint.  Adding users is as simple as entering a username and password in the fields and clicking "Add New User".  Note that usernames MUST be unique and there are NO restrictions on passwords.  You can delete users by clicking the appropriate delete link next to each user.</p>
-<br />
-<br />
-<br />
-<h3>Finishing it all off</h3>
-<p>Ok, so you've created your users, and you have everything setup properly, how do your users login ?  Well, we've provided a simple login form that you can use for this purpose.  This page (http://server:port/auth.xsl) will bring up a form that users can use to enter their username and password.</p>
-<img src="listener_auth3.jpg" alt="Screenshot of http://server:port/auth.xsl" />
-<p>This page will serve a m3u with the username and password and in most cases should open the correct media player and begin playing your stream</p>
-<br />
-<br />
-<h2>URL</h2>
-<p>Authenticating listeners via the URL method involves icecast, when a listener connects,
-issuing requests to a web server and checking the response headers. If a certain header is
-sent back then the listener connecting is allowed to continue, if not, an error is sent back
-to the listener.</p>
-<p>The URLs specified will invoke some web server scripts like PHP to do any work that they
-may choose to do. All that is required of the scripting language is that POST information can
-be handled and response headers can be sent back. libcurl is used for the requesting so 
-https connections may be possible, but be aware of the extra overhead involved.</p>
-<p>The useragent sent in each curl request will represent the icecast server version. The
-response headers will depend on whether the listener is to be accepted.  In the case of
-rejection, a response header <pre>icecast-auth-message: reason </pre> should also be returned
-for placing in the log files.
-</p>
-<p>In order to use URL based listener authentication, you MUST configure a mount specific
-option.  This means that you have to provide a &lt;mount&gt; section in the main icecast
-config file.  The following shows the list of options available :</p>
-<pre>
-    &lt;mount&gt;
-        &lt;mount-name&gt;/example.ogg&lt;/mount-name&gt;
-        &lt;authentication type="url"&gt;
-            &lt;option name="mount_add" value="http://myauthserver.com/stream_start.php"/&gt;
-            &lt;option name="mount_remove" value="http://myauthserver.com/stream_end.php"/&gt;
-            &lt;option name="listener_add" value="http://myauthserver.com/listener_joined.php"/&gt;
-            &lt;option name="listener_remove" value="http://myauthserver.com/listener_left.php"/&gt;
-            &lt;option name="username" value="user"/&gt;
-            &lt;option name="password" value="pass"/&gt;
-            &lt;option name="auth_header" value="icecast-auth-user: 1"/&gt;
-            &lt;option name="timelimit_header" value="icecast-auth-timelimit:"/&gt;
-        &lt;/authentication&gt;
-    &lt;/mount&gt;
-</pre>
-<p>The options are described below in more detail, each of which is optional, but in each
-case, within the POST data, the value for each setting is encoded.</p>
-<h3>mount_add</h3>
-<p>This URL is for informing the auth server of a stream starting. No listener information
-is passed for this, but can be used to initialise any details the auth server may have.
-</p>
-<p>POST details are</p>
-<pre>
-    action=mount_add&amp;mount=/live&amp;server=myserver.com&amp;port=8000
-</pre>
-<p>Here the details indicate the server name (&lt;hostname&gt;) and mountpoint starting up</p>
-<h3>mount_remove</h3>
-<p>This URL is for informing the auth server of a stream finishing, like the start option, no
-listener details are passed.</p>
-<p>POST details are</p>
-<pre>
-    action=mount_remove&amp;mount=/live&amp;server=myserver.com&amp;port=8000
-</pre>
-<p>like the start option, server name and mountpoint are provided</p>
-</p>
-<h3>listener_add</h3>
-<p>This is most likely to be used if anything. When a listener connects, before anything is
-sent back to them, this request is processed.  The default action is to reject a listener
-unless the auth server sends back a response header which may be stated in the 'header' option
-</p>
-<p>POST details are</p>
-<pre>
-    action=listener_add&amp;server=myserver.com&amp;port=8000&amp;client=1&amp;mount=/live&amp;user=&amp;pass=&amp;ip=127.0.0.1&amp;agent="My%20player"
-</pre>
-<p>There are more details with this, client is the unique ID for the client within icecast,
-user and pass may be blank but come from the HTTP basic auth that the listener states, ip
-is the listeners IP address and agent is the Useragent from the listeners player.</p>
-<p>The mount here (unlike the start/end options) states the requested url including any
-query parameters, so for instance the requested URL can be /stream.ogg&amp;session=xyz, but
-note that each option data is escaped before being passed via POST
-</p>
-<h3>listener_remove</h3>
-<p>This URL is for when a listener connection closes.</p>
-<p>POST details are</p>
-<pre>
-    action=listener_remove&amp;server=myserver.com&amp;port=8000&amp;client=1&amp;mount=/live&amp;user=&amp;pass=&amp;duration=3600
-</pre>
-<p>Again this is similar to the add option, the difference being that a duration is passed
-reflecting the number of seconds the listener was connected for </p>
-<h3>auth_header</h3>
-<p>The expected response header to be returned that allows the authencation to take
-place may be specified here. The default is 
-<pre>icecast-auth-user: 1</pre>
-but it could can anything you like, for instance
-<pre>HTTP 200 OK</pre>
-<h3>timelimit_header</h3>
-<p>Listeners could have a time limit imposed on them, and if this header is sent back with a
-figure (which represents seconds) then that is how long the client will remain connected for.
-</p>
-<br />
-<h2>A note about players and authentication</h2>
-<p>We do not have an exaustive list of players that support listener authentication.  We use
-standard HTTP basic authentication, and in general, many media players support this if they
-support anything at all.  Winamp and Foobar2000 support HTTP basic authentication on windows,
-and XMMS supports it on unix platforms.  Winamp/XMMS as least support the passing of query 
-parameters, other players may also do</p>
-</div>
-</body>
-</html>

Copied: icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html (from rev 14437, icecast/trunk/icecast/doc/icecast2_listenerauth.html)
===================================================================
--- icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/doc/icecast2_listenerauth.html	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,162 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<title>Icecast v2.x Documentation</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body>
+<div class="boxtest">
+<h1>Icecast 2 Listener Authentication</h1>
+<hr id='titlebar' />
+<br />
+<br />
+<br />
+<h2>Listener Authentication</h2>
+<p>Listener authentication is a feature of icecast which allows you to secure a certain
+mountpoint such that in order to listen, a listener must pass some verification test.  With
+this feature, a simple pay-for-play operation (eg user/pass), or some filtering based on the
+listener connection can be performed.  This section will show you the basics of setting up
+and maintaining this component.</p>
+<p>To define listener authentication, a group of tags are specified in the &lt;mount&gt;
+group relating to the mountpoint. This means that authentication can apply to listeners of
+source clients or relays.</p>
+<br />
+<p>The following authentication mechanisms can apply to listeners</p>
+<ul>
+    <li>HTPASSWD - lookup a named file for a matching username and password</li>
+    <li>URL - issue web requests (eg PHP) to match authentication</li>
+</ul>
+<p>The listener authentication within a specified mount in the icecast XML configuration
+can apply to either to a stream from a source client, relay or a webroot based file. They
+do apply to intro files or fallback streams.
+</p>
+<br />
+<h2>HTPASSWD Listener Authentication</h2>
+<p>In order to use listener authentication, you MUST configure a mount specific option.  This means that you have to provide a &lt;mount&gt; section in the main icecast config file.  The following is an example :</p>
+<pre>
+    &lt;mount&gt;
+        &lt;mount-name&gt;/example.ogg&lt;/mount-name&gt;
+        &lt;authentication type="htpasswd"&gt;
+                &lt;option name="filename" value="myauth"/&gt;
+                &lt;option name="allow_duplicate_users" value="0"/&gt;
+        &lt;/authentication&gt;
+    &lt;/mount&gt;
+</pre>
+<p>To support listener authentication you MUST provide at a minimum &lt;mount-name&gt; and &lt;authentication&gt;.  The mount-name is the name of the mountpoint that you will use to connect your source client with and authentication configures what type of icecast2 authenticator to use.  Currently, only a single type "htpasswd" is implemented.  New authenticators will be added later.  Each authenticator has a variable number of options that are required and these are specified as shown in the example.  The htpasswd authenticator requires a few parameters.  The first, filename, specifies the name of the file to use to store users and passwords.  Note that this file need not exist (and probably will not exist when you first set it up).  Icecast has built-in support for managing users and passwords via the web admin interface.  More on this later in this section.  The second option, allow_duplicate_users, if set to 0, will prevent multiple connections using the same username.  
 Setting this value to 1 will enable mutltiple connections from the same username on a given mountpoint.  Note there is no way to specify a "max connections" for a particular user.
+</p>
+<p>Icecast supports a mixture of streams that require listener authentication and those that do not.  Only mounts that are named in the config file can be configured for listener authentication.</p>
+<br />
+<br />
+<h3>Configuring Users and Passwords</h3>
+<p>Once the appropriate entries are made to the config file, connect your source client (using the mountpoint you named in the config file).  To configure users and passwords for this stream you must use the web-based admin interface.  Navigate to http://server:ip/admin/stats.xsl to begin.  If you have configured everything properly, you should see a screen like the following :</p>
+<img src="listener_auth1.jpg" alt="Screenshot of http://server:ip/admin/stats.xsl" />
+<p>You will see a lock in front of all mountpoint configured for listener authentication.  Also note that this page will only show CONNECTED mountpoints.</p>
+<p>To manage users and passwords for this mountpoint, click on the lock or follow the "Manage Authentication" link.  The following screen will be shown :</p>
+<img src="listener_auth2.jpg" alt="Screenshot of Manage Authentication" />
+<p>This screen will show all the users configured for this mountpoint.  Adding users is as simple as entering a username and password in the fields and clicking "Add New User".  Note that usernames MUST be unique and there are NO restrictions on passwords.  You can delete users by clicking the appropriate delete link next to each user.</p>
+<br />
+<br />
+<br />
+<h3>Finishing it all off</h3>
+<p>Ok, so you've created your users, and you have everything setup properly, how do your users login ?  Well, we've provided a simple login form that you can use for this purpose.  This page (http://server:port/auth.xsl) will bring up a form that users can use to enter their username and password.</p>
+<img src="listener_auth3.jpg" alt="Screenshot of http://server:port/auth.xsl" />
+<p>This page will serve a m3u with the username and password and in most cases should open the correct media player and begin playing your stream</p>
+<br />
+<br />
+<h2>URL</h2>
+<p>Authenticating listeners via the URL method involves icecast, when a listener connects,
+issuing requests to a web server and checking the response headers. If a certain header is
+sent back then the listener connecting is allowed to continue, if not, an error is sent back
+to the listener.</p>
+<p>The URLs specified will invoke some web server scripts like PHP to do any work that they
+may choose to do. All that is required of the scripting language is that POST information can
+be handled and response headers can be sent back. libcurl is used for the requesting so 
+https connections may be possible, but be aware of the extra overhead involved.</p>
+<p>The useragent sent in each curl request will represent the icecast server version. The
+response headers will depend on whether the listener is to be accepted.  In the case of
+rejection, a response header <pre>icecast-auth-message: reason </pre> should also be returned
+for placing in the log files.
+</p>
+<p>In order to use URL based listener authentication, you MUST configure a mount specific
+option.  This means that you have to provide a &lt;mount&gt; section in the main icecast
+config file.  The following shows the list of options available :</p>
+<pre>
+    &lt;mount&gt;
+        &lt;mount-name&gt;/example.ogg&lt;/mount-name&gt;
+        &lt;authentication type="url"&gt;
+            &lt;option name="mount_add" value="http://myauthserver.com/stream_start.php"/&gt;
+            &lt;option name="mount_remove" value="http://myauthserver.com/stream_end.php"/&gt;
+            &lt;option name="listener_add" value="http://myauthserver.com/listener_joined.php"/&gt;
+            &lt;option name="listener_remove" value="http://myauthserver.com/listener_left.php"/&gt;
+            &lt;option name="username" value="user"/&gt;
+            &lt;option name="password" value="pass"/&gt;
+            &lt;option name="auth_header" value="icecast-auth-user: 1"/&gt;
+            &lt;option name="timelimit_header" value="icecast-auth-timelimit:"/&gt;
+        &lt;/authentication&gt;
+    &lt;/mount&gt;
+</pre>
+<p>The options are described below in more detail, each of which is optional, but in each
+case, within the POST data, the value for each setting is encoded.</p>
+<h3>mount_add</h3>
+<p>This URL is for informing the auth server of a stream starting. No listener information
+is passed for this, but can be used to initialise any details the auth server may have.
+</p>
+<p>POST details are</p>
+<pre>
+    action=mount_add&amp;mount=/live&amp;server=myserver.com&amp;port=8000
+</pre>
+<p>Here the details indicate the server name (&lt;hostname&gt;) and mountpoint starting up</p>
+<h3>mount_remove</h3>
+<p>This URL is for informing the auth server of a stream finishing, like the start option, no
+listener details are passed.</p>
+<p>POST details are</p>
+<pre>
+    action=mount_remove&amp;mount=/live&amp;server=myserver.com&amp;port=8000
+</pre>
+<p>like the start option, server name and mountpoint are provided</p>
+</p>
+<h3>listener_add</h3>
+<p>This is most likely to be used if anything. When a listener connects, before anything is
+sent back to them, this request is processed.  The default action is to reject a listener
+unless the auth server sends back a response header which may be stated in the 'header' option
+</p>
+<p>POST details are</p>
+<pre>
+    action=listener_add&amp;server=myserver.com&amp;port=8000&amp;client=1&amp;mount=/live&amp;user=&amp;pass=&amp;ip=127.0.0.1&amp;agent="My%20player"
+</pre>
+<p>There are more details with this, client is the unique ID for the client within icecast,
+user and pass may be blank but come from the HTTP basic auth that the listener states, ip
+is the listeners IP address and agent is the Useragent from the listeners player.</p>
+<p>The mount here (unlike the start/end options) states the requested url including any
+query parameters, so for instance the requested URL can be /stream.ogg&amp;session=xyz, but
+note that each option data is escaped before being passed via POST
+</p>
+<h3>listener_remove</h3>
+<p>This URL is for when a listener connection closes.</p>
+<p>POST details are</p>
+<pre>
+    action=listener_remove&amp;server=myserver.com&amp;port=8000&amp;client=1&amp;mount=/live&amp;user=&amp;pass=&amp;duration=3600
+</pre>
+<p>Again this is similar to the add option, the difference being that a duration is passed
+reflecting the number of seconds the listener was connected for </p>
+<h3>auth_header</h3>
+<p>The expected response header to be returned that allows the authencation to take
+place may be specified here. The default is 
+<pre>icecast-auth-user: 1</pre>
+but it could can anything you like, for instance
+<pre>HTTP 200 OK</pre>
+<h3>timelimit_header</h3>
+<p>Listeners could have a time limit imposed on them, and if this header is sent back with a
+figure (which represents seconds) then that is how long the client will remain connected for.
+</p>
+<br />
+<h2>A note about players and authentication</h2>
+<p>We do not have an exaustive list of players that support listener authentication.  We use
+standard HTTP basic authentication, and in general, many media players support this if they
+support anything at all.  Winamp and Foobar2000 support HTTP basic authentication on windows,
+and XMMS supports it on unix platforms.  Winamp/XMMS as least support the passing of query 
+parameters, other players may also do</p>
+</div>
+</body>
+</html>

Copied: icecast/tags/icecast-2.3.2/doc/masterslave.png (from rev 14437, icecast/trunk/icecast/doc/masterslave.png)
===================================================================
(Binary files differ)

Copied: icecast/tags/icecast-2.3.2/doc/relay.png (from rev 14437, icecast/trunk/icecast/doc/relay.png)
===================================================================
(Binary files differ)

Deleted: icecast/tags/icecast-2.3.2/examples/icecast_auth-1.0.tar.gz
===================================================================
(Binary files differ)

Copied: icecast/tags/icecast-2.3.2/examples/icecast_auth-1.0.tar.gz (from rev 14985, icecast/trunk/icecast/examples/icecast_auth-1.0.tar.gz)
===================================================================
(Binary files differ)

Deleted: icecast/tags/icecast-2.3.2/icecast.spec
===================================================================
--- icecast/trunk/icecast/icecast.spec	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/icecast.spec	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,61 +0,0 @@
-Name:		icecast
-Version:	2.3.1
-Release:	0
-Summary:	Xiph Streaming media server that supports multiple audio formats.
-Group:		Applications/Multimedia
-License:	GPL
-URL:		http://www.icecast.org/
-Vendor:		Xiph.org Foundation <team at icecast.org>
-Source:     	http://downloads.us.xiph.org/releases/icecast/%{name}-%{version}.tar.gz
-Prefix:		%{_prefix}
-BuildRoot:	%{_tmppath}/%{name}-root
-
-Requires:       libvorbis >= 1.0
-BuildRequires:	libvorbis-devel >= 1.0
-Requires:       libogg >= 1.0
-BuildRequires:	libogg-devel >= 1.0
-Requires:       curl >= 7.10.0
-BuildRequires:	curl-devel >= 7.10.0
-Requires:       libxml2
-BuildRequires:	libxml2-devel
-Requires:       libxslt
-BuildRequires:	libxslt-devel
-Requires:       libtheora
-BuildRequires:	libtheora-devel
-Requires:       speex
-BuildRequires:	speex-devel
-
-%description
-Icecast is a streaming media server which currently supports Ogg Vorbis 
-and MP3 audio streams. It can be used to create an Internet radio 
-station or a privately running jukebox and many things in between. 
-It is very versatile in that new formats can be added relatively 
-easily and supports open standards for commuincation and interaction.
-
-%prep
-%setup -q -n %{name}-%{version}
-
-%build
-CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix} --mandir=%{_mandir} --sysconfdir=/etc
-make
-
-%install
-[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
-
-make DESTDIR=$RPM_BUILD_ROOT install
-rm -rf $RPM_BUILD_ROOT%{_datadir}/doc/%{name}
-
-%clean 
-[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root)
-%doc README AUTHORS COPYING NEWS TODO ChangeLog
-%doc doc/*.html
-%doc doc/*.jpg
-%doc doc/*.css
-%config(noreplace) /etc/%{name}.xml
-%{_bindir}/icecast
-%{_prefix}/share/icecast/*
-
-%changelog

Copied: icecast/tags/icecast-2.3.2/icecast.spec (from rev 14985, icecast/trunk/icecast/icecast.spec)
===================================================================
--- icecast/tags/icecast-2.3.2/icecast.spec	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/icecast.spec	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,61 @@
+Name:		icecast
+Version:	2.3.2
+Release:	0
+Summary:	Xiph Streaming media server that supports multiple formats.
+Group:		Applications/Multimedia
+License:	GPL
+URL:		http://www.icecast.org/
+Vendor:		Xiph.org Foundation <team at icecast.org>
+Source:     	http://downloads.us.xiph.org/releases/icecast/%{name}-%{version}.tar.gz
+Prefix:		%{_prefix}
+BuildRoot:	%{_tmppath}/%{name}-root
+
+Requires:       libvorbis >= 1.0
+BuildRequires:	libvorbis-devel >= 1.0
+Requires:       libogg >= 1.0
+BuildRequires:	libogg-devel >= 1.0
+Requires:       curl >= 7.10.0
+BuildRequires:	curl-devel >= 7.10.0
+Requires:       libxml2
+BuildRequires:	libxml2-devel
+Requires:       libxslt
+BuildRequires:	libxslt-devel
+Requires:       libtheora
+BuildRequires:	libtheora-devel
+Requires:       speex
+BuildRequires:	speex-devel
+
+%description
+Icecast is a streaming media server which currently supports Ogg Vorbis 
+and MP3 audio streams. It can be used to create an Internet radio 
+station or a privately running jukebox and many things in between. 
+It is very versatile in that new formats can be added relatively 
+easily and supports open standards for commuincation and interaction.
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{_prefix} --mandir=%{_mandir} --sysconfdir=/etc
+make
+
+%install
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+
+make DESTDIR=$RPM_BUILD_ROOT install
+rm -rf $RPM_BUILD_ROOT%{_datadir}/doc/%{name}
+
+%clean 
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc README AUTHORS COPYING NEWS TODO ChangeLog
+%doc doc/*.html
+%doc doc/*.jpg
+%doc doc/*.css
+%config(noreplace) /etc/%{name}.xml
+%{_bindir}/icecast
+%{_prefix}/share/icecast/*
+
+%changelog

Copied: icecast/tags/icecast-2.3.2/src (from rev 13540, icecast/trunk/icecast/src)

Deleted: icecast/tags/icecast-2.3.2/src/Makefile.am
===================================================================
--- icecast/trunk/icecast/src/Makefile.am	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,38 +0,0 @@
-## Process this with automake to create Makefile.in
-
-AUTOMAKE_OPTIONS = foreign
-
-SUBDIRS = avl thread httpp net log timing
-
-bin_PROGRAMS = icecast
-
-noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
-    global.h util.h slave.h source.h stats.h refbuf.h client.h \
-    compat.h fserve.h xslt.h yp.h event.h md5.h \
-    auth.h auth_htpasswd.h auth_url.h \
-    format.h format_ogg.h format_mp3.h \
-    format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h
-icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
-    util.c slave.c source.c stats.c refbuf.c client.c \
-    xslt.c fserve.c event.c admin.c md5.c \
-    format.c format_ogg.c format_mp3.c format_midi.c format_flac.c \
-    auth.c auth_htpasswd.c
-EXTRA_icecast_SOURCES = yp.c \
-    auth_url.c \
-    format_vorbis.c format_theora.c format_speex.c
-    
-icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \
-    httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la
-icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@
-
-AM_CFLAGS = @XIPH_CFLAGS@
-AM_CPPFLAGS = @XIPH_CPPFLAGS@
-AM_LDFLAGS = @XIPH_LDFLAGS@
-
-
-debug:
-	$(MAKE) all CFLAGS="@DEBUG@"
-
-profile:
-	$(MAKE) all CFLAGS="@PROFILE@"
-

Copied: icecast/tags/icecast-2.3.2/src/Makefile.am (from rev 14778, icecast/trunk/icecast/src/Makefile.am)
===================================================================
--- icecast/tags/icecast-2.3.2/src/Makefile.am	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/Makefile.am	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,39 @@
+## Process this with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = avl thread httpp net log timing
+
+bin_PROGRAMS = icecast
+
+noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
+    global.h util.h slave.h source.h stats.h refbuf.h client.h \
+    compat.h fserve.h xslt.h yp.h event.h md5.h \
+    auth.h auth_htpasswd.h auth_url.h \
+    format.h format_ogg.h format_mp3.h \
+    format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
+    format_kate.h format_skeleton.h
+icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
+    util.c slave.c source.c stats.c refbuf.c client.c \
+    xslt.c fserve.c event.c admin.c md5.c \
+    format.c format_ogg.c format_mp3.c format_midi.c format_flac.c \
+    auth.c auth_htpasswd.c format_kate.c format_skeleton.c
+EXTRA_icecast_SOURCES = yp.c \
+    auth_url.c \
+    format_vorbis.c format_theora.c format_speex.c
+
+icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \
+    httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la
+icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@ @KATE_LIBS@
+
+AM_CFLAGS = @XIPH_CFLAGS@
+AM_CPPFLAGS = @XIPH_CPPFLAGS@
+AM_LDFLAGS = @XIPH_LDFLAGS@ @KATE_LIBS@
+
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+

Deleted: icecast/tags/icecast-2.3.2/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/admin.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1008 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <time.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-#include "cfgfile.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "source.h"
-#include "global.h"
-#include "event.h"
-#include "stats.h"
-#include "compat.h"
-#include "xslt.h"
-#include "fserve.h"
-#include "admin.h"
-
-#include "format.h"
-
-#include "logging.h"
-#include "auth.h"
-#ifdef _WIN32
-#define snprintf _snprintf
-#endif
-
-#define CATMODULE "admin"
-
-#define COMMAND_ERROR             (-1)
-
-/* Mount-specific commands */
-#define COMMAND_RAW_FALLBACK        1
-#define COMMAND_RAW_METADATA_UPDATE     2
-#define COMMAND_RAW_SHOW_LISTENERS  3
-#define COMMAND_RAW_MOVE_CLIENTS    4
-#define COMMAND_RAW_MANAGEAUTH      5
-#define COMMAND_SHOUTCAST_METADATA_UPDATE     6
-#define COMMAND_RAW_UPDATEMETADATA      7
-
-#define COMMAND_TRANSFORMED_FALLBACK        50
-#define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
-#define COMMAND_TRANSFORMED_MOVE_CLIENTS    54
-#define COMMAND_TRANSFORMED_MANAGEAUTH      55
-#define COMMAND_TRANSFORMED_UPDATEMETADATA  56
-#define COMMAND_TRANSFORMED_METADATA_UPDATE 57
-
-/* Global commands */
-#define COMMAND_RAW_LIST_MOUNTS             101
-#define COMMAND_RAW_STATS                   102
-#define COMMAND_RAW_LISTSTREAM              103
-#define COMMAND_PLAINTEXT_LISTSTREAM        104
-#define COMMAND_TRANSFORMED_LIST_MOUNTS     201
-#define COMMAND_TRANSFORMED_STATS           202
-#define COMMAND_TRANSFORMED_LISTSTREAM      203
-
-/* Client management commands */
-#define COMMAND_RAW_KILL_CLIENT             301
-#define COMMAND_RAW_KILL_SOURCE             302
-#define COMMAND_TRANSFORMED_KILL_CLIENT     401
-#define COMMAND_TRANSFORMED_KILL_SOURCE     402
-
-/* Admin commands requiring no auth */
-#define COMMAND_BUILDM3U                    501
-
-#define FALLBACK_RAW_REQUEST "fallbacks"
-#define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
-#define SHOUTCAST_METADATA_REQUEST "admin.cgi"
-#define METADATA_RAW_REQUEST "metadata"
-#define METADATA_TRANSFORMED_REQUEST "metadata.xsl"
-#define LISTCLIENTS_RAW_REQUEST "listclients"
-#define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
-#define STATS_RAW_REQUEST "stats"
-#define STATS_TRANSFORMED_REQUEST "stats.xsl"
-#define LISTMOUNTS_RAW_REQUEST "listmounts"
-#define LISTMOUNTS_TRANSFORMED_REQUEST "listmounts.xsl"
-#define STREAMLIST_RAW_REQUEST "streamlist"
-#define STREAMLIST_TRANSFORMED_REQUEST "streamlist.xsl"
-#define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
-#define MOVECLIENTS_RAW_REQUEST "moveclients"
-#define MOVECLIENTS_TRANSFORMED_REQUEST "moveclients.xsl"
-#define KILLCLIENT_RAW_REQUEST "killclient"
-#define KILLCLIENT_TRANSFORMED_REQUEST "killclient.xsl"
-#define KILLSOURCE_RAW_REQUEST "killsource"
-#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
-#define ADMIN_XSL_RESPONSE "response.xsl"
-#define MANAGEAUTH_RAW_REQUEST "manageauth"
-#define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
-#define UPDATEMETADATA_RAW_REQUEST "updatemetadata"
-#define UPDATEMETADATA_TRANSFORMED_REQUEST "updatemetadata.xsl"
-#define DEFAULT_RAW_REQUEST ""
-#define DEFAULT_TRANSFORMED_REQUEST ""
-#define BUILDM3U_RAW_REQUEST "buildm3u"
-
-int admin_get_command(char *command)
-{
-    if(!strcmp(command, FALLBACK_RAW_REQUEST))
-        return COMMAND_RAW_FALLBACK;
-    else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_FALLBACK;
-    else if(!strcmp(command, METADATA_RAW_REQUEST))
-        return COMMAND_RAW_METADATA_UPDATE;
-    else if(!strcmp(command, METADATA_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_METADATA_UPDATE;
-    else if(!strcmp(command, SHOUTCAST_METADATA_REQUEST))
-        return COMMAND_SHOUTCAST_METADATA_UPDATE;
-    else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
-        return COMMAND_RAW_SHOW_LISTENERS;
-    else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_SHOW_LISTENERS;
-    else if(!strcmp(command, STATS_RAW_REQUEST))
-        return COMMAND_RAW_STATS;
-    else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_STATS;
-    else if(!strcmp(command, "stats.xml")) /* The old way */
-        return COMMAND_RAW_STATS;
-    else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
-        return COMMAND_RAW_LIST_MOUNTS;
-    else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_LIST_MOUNTS;
-    else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
-        return COMMAND_RAW_LISTSTREAM;
-    else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
-        return COMMAND_PLAINTEXT_LISTSTREAM;
-    else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
-        return COMMAND_RAW_MOVE_CLIENTS;
-    else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_MOVE_CLIENTS;
-    else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
-        return COMMAND_RAW_KILL_CLIENT;
-    else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_KILL_CLIENT;
-    else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
-        return COMMAND_RAW_KILL_SOURCE;
-    else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_KILL_SOURCE;
-    else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
-        return COMMAND_RAW_MANAGEAUTH;
-    else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_MANAGEAUTH;
-    else if(!strcmp(command, UPDATEMETADATA_RAW_REQUEST))
-        return COMMAND_RAW_UPDATEMETADATA;
-    else if(!strcmp(command, UPDATEMETADATA_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_UPDATEMETADATA;
-    else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
-        return COMMAND_BUILDM3U;
-    else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
-        return COMMAND_TRANSFORMED_STATS;
-    else if(!strcmp(command, DEFAULT_RAW_REQUEST))
-        return COMMAND_TRANSFORMED_STATS;
-    else
-        return COMMAND_ERROR;
-}
-
-static void command_fallback(client_t *client, source_t *source, int response);
-static void command_metadata(client_t *client, source_t *source, int response);
-static void command_shoutcast_metadata(client_t *client, source_t *source);
-static void command_show_listeners(client_t *client, source_t *source,
-        int response);
-static void command_move_clients(client_t *client, source_t *source,
-        int response);
-static void command_stats(client_t *client, int response);
-static void command_list_mounts(client_t *client, int response);
-static void command_kill_client(client_t *client, source_t *source,
-        int response);
-static void command_manageauth(client_t *client, source_t *source,
-        int response);
-static void command_buildm3u(client_t *client, source_t *source,
-        int response);
-static void command_kill_source(client_t *client, source_t *source,
-        int response);
-static void command_updatemetadata(client_t *client, source_t *source,
-        int response);
-static void admin_handle_mount_request(client_t *client, source_t *source,
-        int command);
-static void admin_handle_general_request(client_t *client, int command);
-
-/* build an XML doc containing information about currently running sources.
- * If a mountpoint is passed then that source will not be added to the XML
- * doc even if the source is running */
-xmlDocPtr admin_build_sourcelist (const char *mount)
-{
-    avl_node *node;
-    source_t *source;
-    xmlNodePtr xmlnode, srcnode;
-    xmlDocPtr doc;
-    char buf[22];
-    time_t now = time(NULL);
-
-    doc = xmlNewDoc("1.0");
-    xmlnode = xmlNewDocNode(doc, NULL, "icestats", NULL);
-    xmlDocSetRootElement(doc, xmlnode);
-
-    if (mount) {
-        xmlNewChild(xmlnode, NULL, "current_source", mount);
-    }
-
-    node = avl_get_first(global.source_tree);
-    while(node) {
-        source = (source_t *)node->key;
-        if (mount && strcmp (mount, source->mount) == 0)
-        {
-            node = avl_get_next (node);
-            continue;
-        }
-
-        if (source->running || source->on_demand)
-        {
-            ice_config_t *config;
-            mount_proxy *mountinfo;
-
-            srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
-            xmlSetProp(srcnode, "mount", source->mount);
-
-            xmlNewChild(srcnode, NULL, "fallback", 
-                    (source->fallback_mount != NULL)?
-                    source->fallback_mount:"");
-            snprintf (buf, sizeof(buf), "%lu", source->listeners);
-            xmlNewChild(srcnode, NULL, "listeners", buf);
-
-            config = config_get_config();
-            mountinfo = config_find_mount (config, source->mount);
-            if (mountinfo && mountinfo->auth)
-            {
-                xmlNewChild(srcnode, NULL, "authenticator",
-                        mountinfo->auth->type);
-            }
-            config_release_config();
-
-            if (source->running)
-            {
-                if (source->client) 
-                {
-                    snprintf (buf, sizeof(buf), "%lu",
-                            (unsigned long)(now - source->con->con_time));
-                    xmlNewChild (srcnode, NULL, "Connected", buf);
-                }
-                xmlNewChild (srcnode, NULL, "content-type", 
-                        source->format->contenttype);
-            }
-        }
-        node = avl_get_next(node);
-    }
-    return(doc);
-}
-
-void admin_send_response (xmlDocPtr doc, client_t *client,
-        int response, const char *xslt_template)
-{
-    if (response == RAW)
-    {
-        xmlChar *buff = NULL;
-        int len = 0;
-        unsigned int buf_len;
-        const char *http = "HTTP/1.0 200 OK\r\n"
-               "Content-Type: text/xml\r\n"
-               "Content-Length: ";
-        xmlDocDumpMemory(doc, &buff, &len);
-        buf_len = strlen (http) + len + 20;
-        client_set_queue (client, NULL);
-        client->refbuf = refbuf_new (buf_len);
-        len = snprintf (client->refbuf->data, buf_len, "%s%d\r\n\r\n%s", http, len, buff);
-        client->refbuf->len = len;
-        xmlFree(buff);
-        client->respcode = 200;
-        fserve_add_client (client, NULL);
-    }
-    if (response == TRANSFORMED)
-    {
-        char *fullpath_xslt_template;
-        int fullpath_xslt_template_len;
-        ice_config_t *config = config_get_config();
-
-        fullpath_xslt_template_len = strlen (config->adminroot_dir) + 
-            strlen (xslt_template) + 2;
-        fullpath_xslt_template = malloc(fullpath_xslt_template_len);
-        snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s",
-            config->adminroot_dir, PATH_SEPARATOR, xslt_template);
-        config_release_config();
-
-        DEBUG1("Sending XSLT (%s)", fullpath_xslt_template);
-        xslt_transform(doc, fullpath_xslt_template, client);
-        free(fullpath_xslt_template);
-    }
-}
-
-
-void admin_handle_request(client_t *client, char *uri)
-{
-    char *mount, *command_string;
-    int command;
-    int noauth = 0;
-
-    DEBUG1("Admin request (%s)", uri);
-    if (!((strcmp(uri, "/admin.cgi") == 0) ||
-         (strncmp("/admin/", uri, 7) == 0))) {
-        ERROR0("Internal error: admin request isn't");
-        client_send_401(client);
-        return;
-    }
-
-    if (strcmp(uri, "/admin.cgi") == 0) {
-        command_string = uri + 1;
-    }
-    else {
-        command_string = uri + 7;
-    }
-
-    DEBUG1("Got command (%s)", command_string);
-    command = admin_get_command(command_string);
-
-    if(command < 0) {
-        ERROR1("Error parsing command string or unrecognised command: %s",
-                command_string);
-        client_send_400(client, "Unrecognised command");
-        return;
-    }
-
-    if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
-
-        ice_config_t *config;
-        char *pass = httpp_get_query_param (client->parser, "pass");
-        if (pass == NULL)
-        {
-            client_send_400 (client, "missing pass parameter");
-            return;
-        }
-        config = config_get_config ();
-        httpp_set_query_param (client->parser, "mount", config->shoutcast_mount);
-        httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY");
-        httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass);
-        config_release_config ();
-    }
-
-    mount = httpp_get_query_param(client->parser, "mount");
-
-    if(mount != NULL) {
-        source_t *source;
-
-        if (command == COMMAND_BUILDM3U) {
-            noauth = 1;
-        }
-        /* This is a mount request, handle it as such */
-        if (!noauth) {
-            if(!connection_check_admin_pass(client->parser)) {
-                if(!connection_check_source_pass(client->parser, mount)) {
-                    INFO1("Bad or missing password on mount modification admin "
-                          "request (command: %s)", command_string);
-                    client_send_401(client);
-                    return;
-                }
-            }
-        }
-        
-        avl_tree_rlock(global.source_tree);
-        source = source_find_mount_raw(mount);
-
-        if (source == NULL)
-        {
-            WARN2("Admin command %s on non-existent source %s", 
-                    command_string, mount);
-            avl_tree_unlock(global.source_tree);
-            client_send_400(client, "Source does not exist");
-        }
-        else
-        {
-            if (source->running == 0 && source->on_demand == 0)
-            {
-                avl_tree_unlock (global.source_tree);
-                INFO2("Received admin command %s on unavailable mount \"%s\"",
-                        command_string, mount);
-                client_send_400 (client, "Source is not available");
-                return;
-            }
-            if (command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
-                    source->shoutcast_compat == 0)
-            {
-                avl_tree_unlock (global.source_tree);
-                ERROR0 ("illegal change of metadata on non-shoutcast "
-                        "compatible stream");
-                client_send_400 (client, "illegal metadata call");
-                return;
-            }
-            INFO2("Received admin command %s on mount \"%s\"", 
-                    command_string, mount);
-            admin_handle_mount_request(client, source, command);
-            avl_tree_unlock(global.source_tree);
-        }
-    }
-    else {
-
-        if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
-        /* this request is used by a slave relay to retrieve
-           mounts from the master, so handle this request
-           validating against the relay password */
-            if(!connection_check_relay_pass(client->parser)) {
-                INFO1("Bad or missing password on admin command "
-                      "request (command: %s)", command_string);
-                client_send_401(client);
-                return;
-            }
-        }
-        else {
-            if(!connection_check_admin_pass(client->parser)) {
-                INFO1("Bad or missing password on admin command "
-                      "request (command: %s)", command_string);
-                client_send_401(client);
-                return;
-            }
-        }
-        
-        admin_handle_general_request(client, command);
-    }
-}
-
-static void admin_handle_general_request(client_t *client, int command)
-{
-    switch(command) {
-        case COMMAND_RAW_STATS:
-            command_stats(client, RAW);
-            break;
-        case COMMAND_RAW_LIST_MOUNTS:
-            command_list_mounts(client, RAW);
-            break;
-        case COMMAND_RAW_LISTSTREAM:
-            command_list_mounts(client, RAW);
-            break;
-        case COMMAND_PLAINTEXT_LISTSTREAM:
-            command_list_mounts(client, PLAINTEXT);
-            break;
-        case COMMAND_TRANSFORMED_STATS:
-            command_stats(client, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_LIST_MOUNTS:
-            command_list_mounts(client, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_LISTSTREAM:
-            command_list_mounts(client, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
-            command_list_mounts(client, TRANSFORMED);
-            break;
-        default:
-            WARN0("General admin request not recognised");
-            client_send_400(client, "Unknown admin request");
-            return;
-    }
-}
-
-static void admin_handle_mount_request(client_t *client, source_t *source, 
-        int command)
-{
-    switch(command) {
-        case COMMAND_RAW_FALLBACK:
-            command_fallback(client, source, RAW);
-            break;
-        case COMMAND_RAW_METADATA_UPDATE:
-            command_metadata(client, source, RAW);
-            break;
-        case COMMAND_TRANSFORMED_METADATA_UPDATE:
-            command_metadata(client, source, TRANSFORMED);
-            break;
-        case COMMAND_SHOUTCAST_METADATA_UPDATE:
-            command_shoutcast_metadata(client, source);
-            break;
-        case COMMAND_RAW_SHOW_LISTENERS:
-            command_show_listeners(client, source, RAW);
-            break;
-        case COMMAND_RAW_MOVE_CLIENTS:
-            command_move_clients(client, source, RAW);
-            break;
-        case COMMAND_RAW_KILL_CLIENT:
-            command_kill_client(client, source, RAW);
-            break;
-        case COMMAND_RAW_KILL_SOURCE:
-            command_kill_source(client, source, RAW);
-            break;
-        case COMMAND_TRANSFORMED_FALLBACK:
-            command_fallback(client, source, RAW);
-            break;
-        case COMMAND_TRANSFORMED_SHOW_LISTENERS:
-            command_show_listeners(client, source, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
-            command_move_clients(client, source, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_KILL_CLIENT:
-            command_kill_client(client, source, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_KILL_SOURCE:
-            command_kill_source(client, source, TRANSFORMED);
-            break;
-        case COMMAND_TRANSFORMED_MANAGEAUTH:
-            command_manageauth(client, source, TRANSFORMED);
-            break;
-        case COMMAND_RAW_MANAGEAUTH:
-            command_manageauth(client, source, RAW);
-            break;
-        case COMMAND_TRANSFORMED_UPDATEMETADATA:
-            command_updatemetadata(client, source, TRANSFORMED);
-            break;
-        case COMMAND_RAW_UPDATEMETADATA:
-            command_updatemetadata(client, source, RAW);
-            break;
-        case COMMAND_BUILDM3U:
-            command_buildm3u(client, source, RAW);
-            break;
-        default:
-            WARN0("Mount request not recognised");
-            client_send_400(client, "Mount request unknown");
-            break;
-    }
-}
-
-#define COMMAND_REQUIRE(client,name,var) \
-    do { \
-        (var) = httpp_get_query_param((client)->parser, (name)); \
-        if((var) == NULL) { \
-            client_send_400((client), "Missing parameter"); \
-            return; \
-        } \
-    } while(0);
-#define COMMAND_OPTIONAL(client,name,var) \
-    (var) = httpp_get_query_param((client)->parser, (name))
-
-static void html_success(client_t *client, char *message)
-{
-    client->respcode = 200;
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 
-            "<html><head><title>Admin request successful</title></head>"
-            "<body><p>%s</p></body></html>", message);
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-
-static void command_move_clients(client_t *client, source_t *source,
-    int response)
-{
-    char *dest_source;
-    source_t *dest;
-    xmlDocPtr doc;
-    xmlNodePtr node;
-    char buf[255];
-    int parameters_passed = 0;
-
-    DEBUG0("Doing optional check");
-    if((COMMAND_OPTIONAL(client, "destination", dest_source))) {
-        parameters_passed = 1;
-    }
-    DEBUG1("Done optional check (%d)", parameters_passed);
-    if (!parameters_passed) {
-        doc = admin_build_sourcelist(source->mount);
-        admin_send_response(doc, client, response, 
-             MOVECLIENTS_TRANSFORMED_REQUEST);
-        xmlFreeDoc(doc);
-        return;
-    }
-
-    dest = source_find_mount (dest_source);
-
-    if (dest == NULL)
-    {
-        client_send_400 (client, "No such destination");
-        return;
-    }
-
-    if (strcmp (dest->mount, source->mount) == 0)
-    {
-        client_send_400 (client, "supplied mountpoints are identical");
-        return;
-    }
-
-    if (dest->running == 0 && dest->on_demand == 0)
-    {
-        client_send_400 (client, "Destination not running");
-        return;
-    }
-
-    INFO2 ("source is \"%s\", destination is \"%s\"", source->mount, dest->mount);
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
-    xmlDocSetRootElement(doc, node);
-
-    source_move_clients (source, dest);
-
-    memset(buf, '\000', sizeof(buf));
-    snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
-            source->mount, dest_source);
-    xmlNewChild(node, NULL, "message", buf);
-    xmlNewChild(node, NULL, "return", "1");
-
-    admin_send_response(doc, client, response, 
-        ADMIN_XSL_RESPONSE);
-    xmlFreeDoc(doc);
-}
-
-static void command_show_listeners(client_t *client, source_t *source,
-    int response)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node, srcnode, listenernode;
-    avl_node *client_node;
-    client_t *current;
-    char buf[22];
-    char *userAgent = NULL;
-    time_t now = time(NULL);
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
-    srcnode = xmlNewChild(node, NULL, "source", NULL);
-    xmlSetProp(srcnode, "mount", source->mount);
-    xmlDocSetRootElement(doc, node);
-
-    memset(buf, '\000', sizeof(buf));
-    snprintf (buf, sizeof(buf), "%lu", source->listeners);
-    xmlNewChild(srcnode, NULL, "Listeners", buf);
-
-    avl_tree_rlock(source->client_tree);
-
-    client_node = avl_get_first(source->client_tree);
-    while(client_node) {
-        current = (client_t *)client_node->key;
-        listenernode = xmlNewChild(srcnode, NULL, "listener", NULL);
-        xmlNewChild(listenernode, NULL, "IP", current->con->ip);
-        userAgent = httpp_getvar(current->parser, "user-agent");
-        if (userAgent) {
-            xmlNewChild(listenernode, NULL, "UserAgent", userAgent);
-        }
-        else {
-            xmlNewChild(listenernode, NULL, "UserAgent", "Unknown");
-        }
-        memset(buf, '\000', sizeof(buf));
-        snprintf(buf, sizeof(buf), "%lu", (unsigned long)(now - current->con->con_time));
-        xmlNewChild(listenernode, NULL, "Connected", buf);
-        memset(buf, '\000', sizeof(buf));
-        snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
-        xmlNewChild(listenernode, NULL, "ID", buf);
-        if (current->username) {
-            xmlNewChild(listenernode, NULL, "username", current->username);
-        }
-        client_node = avl_get_next(client_node);
-    }
-
-    avl_tree_unlock(source->client_tree);
-    admin_send_response(doc, client, response, 
-        LISTCLIENTS_TRANSFORMED_REQUEST);
-    xmlFreeDoc(doc);
-}
-
-static void command_buildm3u(client_t *client, source_t *source,
-    int response)
-{
-    char *username = NULL;
-    char *password = NULL;
-    ice_config_t *config;
-
-    COMMAND_REQUIRE(client, "username", username);
-    COMMAND_REQUIRE(client, "password", password);
-
-    client->respcode = 200;
-    config = config_get_config();
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-Type: audio/x-mpegurl\r\n"
-        "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
-        "http://%s:%s@%s:%d%s\r\n",
-        username,
-        password,
-        config->hostname,
-        config->port,
-        source->mount
-    );
-    config_release_config();
-
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-
-static void command_manageauth(client_t *client, source_t *source,
-    int response)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node, srcnode, msgnode;
-    char *action = NULL;
-    char *username = NULL;
-    char *password = NULL;
-    char *message = NULL;
-    int ret = AUTH_OK;
-    ice_config_t *config = config_get_config ();
-    mount_proxy *mountinfo = config_find_mount (config, source->mount);
-
-    if((COMMAND_OPTIONAL(client, "action", action))) {
-        if (mountinfo == NULL || mountinfo->auth == NULL)
-        {
-            WARN1 ("manage auth request for %s but no facility available", source->mount);
-            config_release_config ();
-            client_send_404 (client, "no such auth facility");
-            return;
-        }
-        if (!strcmp(action, "add")) {
-            COMMAND_REQUIRE(client, "username", username);
-            COMMAND_REQUIRE(client, "password", password);
-            ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
-            if (ret == AUTH_FAILED) {
-                message = strdup("User add failed - check the icecast error log");
-            }
-            if (ret == AUTH_USERADDED) {
-                message = strdup("User added");
-            }
-            if (ret == AUTH_USEREXISTS) {
-                message = strdup("User already exists - not added");
-            }
-        }
-        if (!strcmp(action, "delete")) {
-            COMMAND_REQUIRE(client, "username", username);
-            ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
-            if (ret == AUTH_FAILED) {
-                message = strdup("User delete failed - check the icecast error log");
-            }
-            if (ret == AUTH_USERDELETED) {
-                message = strdup("User deleted");
-            }
-        }
-    }
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
-    srcnode = xmlNewChild(node, NULL, "source", NULL);
-    xmlSetProp(srcnode, "mount", source->mount);
-
-    if (message) {
-        msgnode = xmlNewChild(node, NULL, "iceresponse", NULL);
-        xmlNewChild(msgnode, NULL, "message", message);
-    }
-
-    xmlDocSetRootElement(doc, node);
-
-    if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
-        mountinfo->auth->listuser (mountinfo->auth, srcnode);
-
-    config_release_config ();
-
-    admin_send_response(doc, client, response, 
-        MANAGEAUTH_TRANSFORMED_REQUEST);
-    if (message) {
-        free(message);
-    }
-    xmlFreeDoc(doc);
-}
-
-static void command_kill_source(client_t *client, source_t *source,
-    int response)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node;
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
-    xmlNewChild(node, NULL, "message", "Source Removed");
-    xmlNewChild(node, NULL, "return", "1");
-    xmlDocSetRootElement(doc, node);
-
-    source->running = 0;
-
-    admin_send_response(doc, client, response, 
-        ADMIN_XSL_RESPONSE);
-    xmlFreeDoc(doc);
-}
-
-static void command_kill_client(client_t *client, source_t *source,
-    int response)
-{
-    char *idtext;
-    int id;
-    client_t *listener;
-    xmlDocPtr doc;
-    xmlNodePtr node;
-    char buf[50] = "";
-
-    COMMAND_REQUIRE(client, "id", idtext);
-
-    id = atoi(idtext);
-
-    listener = source_find_client(source, id);
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
-    xmlDocSetRootElement(doc, node);
-    DEBUG1("Response is %d", response);
-
-    if(listener != NULL) {
-        INFO1("Admin request: client %d removed", id);
-
-        /* This tags it for removal on the next iteration of the main source
-         * loop
-         */
-        listener->con->error = 1;
-        memset(buf, '\000', sizeof(buf));
-        snprintf(buf, sizeof(buf)-1, "Client %d removed", id);
-        xmlNewChild(node, NULL, "message", buf);
-        xmlNewChild(node, NULL, "return", "1");
-    }
-    else {
-        memset(buf, '\000', sizeof(buf));
-        snprintf(buf, sizeof(buf)-1, "Client %d not found", id);
-        xmlNewChild(node, NULL, "message", buf);
-        xmlNewChild(node, NULL, "return", "0");
-    }
-    admin_send_response(doc, client, response, 
-        ADMIN_XSL_RESPONSE);
-    xmlFreeDoc(doc);
-}
-
-static void command_fallback(client_t *client, source_t *source,
-    int response)
-{
-    char *fallback;
-    char *old;
-
-    DEBUG0("Got fallback request");
-
-    COMMAND_REQUIRE(client, "fallback", fallback);
-
-    old = source->fallback_mount;
-    source->fallback_mount = strdup(fallback);
-    free(old);
-
-    html_success(client, "Fallback configured");
-}
-
-static void command_metadata(client_t *client, source_t *source,
-    int response)
-{
-    char *action;
-    char *song, *title, *artist;
-    format_plugin_t *plugin;
-    xmlDocPtr doc;
-    xmlNodePtr node;
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
-    xmlDocSetRootElement(doc, node);
-
-    DEBUG0("Got metadata update request");
-
-    COMMAND_REQUIRE(client, "mode", action);
-    COMMAND_OPTIONAL(client, "song", song);
-    COMMAND_OPTIONAL(client, "title", title);
-    COMMAND_OPTIONAL(client, "artist", artist);
-
-    if (strcmp (action, "updinfo") != 0)
-    {
-        xmlNewChild(node, NULL, "message", "No such action");
-        xmlNewChild(node, NULL, "return", "0");
-        admin_send_response(doc, client, response, 
-            ADMIN_XSL_RESPONSE);
-        xmlFreeDoc(doc);
-        return;
-    }
-
-    plugin = source->format;
-
-    if (plugin && plugin->set_tag)
-    {
-        if (song)
-        {
-            plugin->set_tag (plugin, "song", song);
-            INFO2 ("Metadata on mountpoint %s changed to \"%s\"", source->mount, song);
-        }
-        else
-        {
-            if (artist && title)
-            {
-                plugin->set_tag (plugin, "title", title);
-                plugin->set_tag (plugin, "artist", artist);
-                INFO3("Metadata on mountpoint %s changed to \"%s - %s\"",
-                        source->mount, artist, title);
-            }
-        }
-    }
-    else
-    {
-        xmlNewChild(node, NULL, "message", 
-            "Mountpoint will not accept URL updates");
-        xmlNewChild(node, NULL, "return", "1");
-        admin_send_response(doc, client, response, 
-            ADMIN_XSL_RESPONSE);
-        xmlFreeDoc(doc);
-        return;
-    }
-
-    xmlNewChild(node, NULL, "message", "Metadata update successful");
-    xmlNewChild(node, NULL, "return", "1");
-    admin_send_response(doc, client, response, 
-        ADMIN_XSL_RESPONSE);
-    xmlFreeDoc(doc);
-}
-
-static void command_shoutcast_metadata(client_t *client, source_t *source)
-{
-    char *action;
-    char *value;
-
-    DEBUG0("Got shoutcast metadata update request");
-
-    COMMAND_REQUIRE(client, "mode", action);
-    COMMAND_REQUIRE(client, "song", value);
-
-    if (strcmp (action, "updinfo") != 0)
-    {
-        client_send_400 (client, "No such action");
-        return;
-    }
-
-    if (source->format && source->format->set_tag)
-    {
-        source->format->set_tag (source->format, "title", value);
-
-        DEBUG2("Metadata on mountpoint %s changed to \"%s\"", 
-                source->mount, value);
-        html_success(client, "Metadata update successful");
-    }
-    else
-    {
-        client_send_400 (client, "mountpoint will not accept URL updates");
-    }
-}
-
-static void command_stats(client_t *client, int response) {
-    xmlDocPtr doc;
-
-    DEBUG0("Stats request, sending xml stats");
-
-    stats_get_xml(&doc, 1, NULL);
-    admin_send_response(doc, client, response, STATS_TRANSFORMED_REQUEST);
-    xmlFreeDoc(doc);
-    return;
-}
-
-static void command_list_mounts(client_t *client, int response)
-{
-    DEBUG0("List mounts request");
-
-    if (response == PLAINTEXT)
-    {
-        char *buf;
-        int remaining = PER_CLIENT_REFBUF_SIZE;
-        int ret;
-
-        buf = client->refbuf->data;
-        ret = snprintf (buf, remaining,
-                "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
-
-        stats_get_streamlist (client->refbuf->data+ret, remaining-ret);
-
-        client->refbuf->len = strlen (client->refbuf->data);
-        fserve_add_client (client, NULL);
-    }
-    else
-    {
-        avl_tree_rlock (global.source_tree);
-        xmlDocPtr doc = admin_build_sourcelist(NULL);
-        avl_tree_unlock (global.source_tree);
-
-        admin_send_response(doc, client, response, 
-            LISTMOUNTS_TRANSFORMED_REQUEST);
-        xmlFreeDoc(doc);
-    }
-}
-
-static void command_updatemetadata(client_t *client, source_t *source,
-    int response)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node, srcnode;
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
-    srcnode = xmlNewChild(node, NULL, "source", NULL);
-    xmlSetProp(srcnode, "mount", source->mount);
-    xmlDocSetRootElement(doc, node);
-
-    admin_send_response(doc, client, response, 
-        UPDATEMETADATA_TRANSFORMED_REQUEST);
-    xmlFreeDoc(doc);
-}

Copied: icecast/tags/icecast-2.3.2/src/admin.c (from rev 14812, icecast/trunk/icecast/src/admin.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/admin.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/admin.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1034 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "cfgfile.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "source.h"
+#include "global.h"
+#include "event.h"
+#include "stats.h"
+#include "compat.h"
+#include "xslt.h"
+#include "fserve.h"
+#include "admin.h"
+
+#include "format.h"
+
+#include "logging.h"
+#include "auth.h"
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "admin"
+
+#define COMMAND_ERROR             (-1)
+
+/* Mount-specific commands */
+#define COMMAND_RAW_FALLBACK        1
+#define COMMAND_RAW_METADATA_UPDATE     2
+#define COMMAND_RAW_SHOW_LISTENERS  3
+#define COMMAND_RAW_MOVE_CLIENTS    4
+#define COMMAND_RAW_MANAGEAUTH      5
+#define COMMAND_SHOUTCAST_METADATA_UPDATE     6
+#define COMMAND_RAW_UPDATEMETADATA      7
+
+#define COMMAND_TRANSFORMED_FALLBACK        50
+#define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
+#define COMMAND_TRANSFORMED_MOVE_CLIENTS    54
+#define COMMAND_TRANSFORMED_MANAGEAUTH      55
+#define COMMAND_TRANSFORMED_UPDATEMETADATA  56
+#define COMMAND_TRANSFORMED_METADATA_UPDATE 57
+
+/* Global commands */
+#define COMMAND_RAW_LIST_MOUNTS             101
+#define COMMAND_RAW_STATS                   102
+#define COMMAND_RAW_LISTSTREAM              103
+#define COMMAND_PLAINTEXT_LISTSTREAM        104
+#define COMMAND_TRANSFORMED_LIST_MOUNTS     201
+#define COMMAND_TRANSFORMED_STATS           202
+#define COMMAND_TRANSFORMED_LISTSTREAM      203
+
+/* Client management commands */
+#define COMMAND_RAW_KILL_CLIENT             301
+#define COMMAND_RAW_KILL_SOURCE             302
+#define COMMAND_TRANSFORMED_KILL_CLIENT     401
+#define COMMAND_TRANSFORMED_KILL_SOURCE     402
+
+/* Admin commands requiring no auth */
+#define COMMAND_BUILDM3U                    501
+
+#define FALLBACK_RAW_REQUEST "fallbacks"
+#define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
+#define SHOUTCAST_METADATA_REQUEST "admin.cgi"
+#define METADATA_RAW_REQUEST "metadata"
+#define METADATA_TRANSFORMED_REQUEST "metadata.xsl"
+#define LISTCLIENTS_RAW_REQUEST "listclients"
+#define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
+#define STATS_RAW_REQUEST "stats"
+#define STATS_TRANSFORMED_REQUEST "stats.xsl"
+#define LISTMOUNTS_RAW_REQUEST "listmounts"
+#define LISTMOUNTS_TRANSFORMED_REQUEST "listmounts.xsl"
+#define STREAMLIST_RAW_REQUEST "streamlist"
+#define STREAMLIST_TRANSFORMED_REQUEST "streamlist.xsl"
+#define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
+#define MOVECLIENTS_RAW_REQUEST "moveclients"
+#define MOVECLIENTS_TRANSFORMED_REQUEST "moveclients.xsl"
+#define KILLCLIENT_RAW_REQUEST "killclient"
+#define KILLCLIENT_TRANSFORMED_REQUEST "killclient.xsl"
+#define KILLSOURCE_RAW_REQUEST "killsource"
+#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
+#define ADMIN_XSL_RESPONSE "response.xsl"
+#define MANAGEAUTH_RAW_REQUEST "manageauth"
+#define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
+#define UPDATEMETADATA_RAW_REQUEST "updatemetadata"
+#define UPDATEMETADATA_TRANSFORMED_REQUEST "updatemetadata.xsl"
+#define DEFAULT_RAW_REQUEST ""
+#define DEFAULT_TRANSFORMED_REQUEST ""
+#define BUILDM3U_RAW_REQUEST "buildm3u"
+
+int admin_get_command(const char *command)
+{
+    if(!strcmp(command, FALLBACK_RAW_REQUEST))
+        return COMMAND_RAW_FALLBACK;
+    else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_FALLBACK;
+    else if(!strcmp(command, METADATA_RAW_REQUEST))
+        return COMMAND_RAW_METADATA_UPDATE;
+    else if(!strcmp(command, METADATA_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_METADATA_UPDATE;
+    else if(!strcmp(command, SHOUTCAST_METADATA_REQUEST))
+        return COMMAND_SHOUTCAST_METADATA_UPDATE;
+    else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
+        return COMMAND_RAW_SHOW_LISTENERS;
+    else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_SHOW_LISTENERS;
+    else if(!strcmp(command, STATS_RAW_REQUEST))
+        return COMMAND_RAW_STATS;
+    else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_STATS;
+    else if(!strcmp(command, "stats.xml")) /* The old way */
+        return COMMAND_RAW_STATS;
+    else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
+        return COMMAND_RAW_LIST_MOUNTS;
+    else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_LIST_MOUNTS;
+    else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
+        return COMMAND_RAW_LISTSTREAM;
+    else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
+        return COMMAND_PLAINTEXT_LISTSTREAM;
+    else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
+        return COMMAND_RAW_MOVE_CLIENTS;
+    else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_MOVE_CLIENTS;
+    else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
+        return COMMAND_RAW_KILL_CLIENT;
+    else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_KILL_CLIENT;
+    else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
+        return COMMAND_RAW_KILL_SOURCE;
+    else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_KILL_SOURCE;
+    else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
+        return COMMAND_RAW_MANAGEAUTH;
+    else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_MANAGEAUTH;
+    else if(!strcmp(command, UPDATEMETADATA_RAW_REQUEST))
+        return COMMAND_RAW_UPDATEMETADATA;
+    else if(!strcmp(command, UPDATEMETADATA_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_UPDATEMETADATA;
+    else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
+        return COMMAND_BUILDM3U;
+    else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_STATS;
+    else if(!strcmp(command, DEFAULT_RAW_REQUEST))
+        return COMMAND_TRANSFORMED_STATS;
+    else
+        return COMMAND_ERROR;
+}
+
+static void command_fallback(client_t *client, source_t *source, int response);
+static void command_metadata(client_t *client, source_t *source, int response);
+static void command_shoutcast_metadata(client_t *client, source_t *source);
+static void command_show_listeners(client_t *client, source_t *source,
+        int response);
+static void command_move_clients(client_t *client, source_t *source,
+        int response);
+static void command_stats(client_t *client, const char *mount, int response);
+static void command_list_mounts(client_t *client, int response);
+static void command_kill_client(client_t *client, source_t *source,
+        int response);
+static void command_manageauth(client_t *client, source_t *source,
+        int response);
+static void command_buildm3u(client_t *client, const char *mount);
+static void command_kill_source(client_t *client, source_t *source,
+        int response);
+static void command_updatemetadata(client_t *client, source_t *source,
+        int response);
+static void admin_handle_mount_request(client_t *client, source_t *source,
+        int command);
+static void admin_handle_general_request(client_t *client, int command);
+
+/* build an XML doc containing information about currently running sources.
+ * If a mountpoint is passed then that source will not be added to the XML
+ * doc even if the source is running */
+xmlDocPtr admin_build_sourcelist (const char *mount)
+{
+    avl_node *node;
+    source_t *source;
+    xmlNodePtr xmlnode, srcnode;
+    xmlDocPtr doc;
+    char buf[22];
+    time_t now = time(NULL);
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    xmlnode = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
+    xmlDocSetRootElement(doc, xmlnode);
+
+    if (mount) {
+        xmlNewChild (xmlnode, NULL, XMLSTR("current_source"), XMLSTR(mount));
+    }
+
+    node = avl_get_first(global.source_tree);
+    while(node) {
+        source = (source_t *)node->key;
+        if (mount && strcmp (mount, source->mount) == 0)
+        {
+            node = avl_get_next (node);
+            continue;
+        }
+
+        if (source->running || source->on_demand)
+        {
+            ice_config_t *config;
+            mount_proxy *mountinfo;
+
+            srcnode = xmlNewChild(xmlnode, NULL, XMLSTR("source"), NULL);
+            xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
+
+            xmlNewChild(srcnode, NULL, XMLSTR("fallback"), 
+                    (source->fallback_mount != NULL)?
+                    XMLSTR(source->fallback_mount):XMLSTR(""));
+            snprintf (buf, sizeof(buf), "%lu", source->listeners);
+            xmlNewChild(srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));
+
+            config = config_get_config();
+            mountinfo = config_find_mount (config, source->mount);
+            if (mountinfo && mountinfo->auth)
+            {
+                xmlNewChild(srcnode, NULL, XMLSTR("authenticator"),
+                        XMLSTR(mountinfo->auth->type));
+            }
+            config_release_config();
+
+            if (source->running)
+            {
+                if (source->client) 
+                {
+                    snprintf (buf, sizeof(buf), "%lu",
+                            (unsigned long)(now - source->con->con_time));
+                    xmlNewChild (srcnode, NULL, XMLSTR("Connected"), XMLSTR(buf));
+                }
+                xmlNewChild (srcnode, NULL, XMLSTR("content-type"), 
+                        XMLSTR(source->format->contenttype));
+            }
+        }
+        node = avl_get_next(node);
+    }
+    return(doc);
+}
+
+void admin_send_response (xmlDocPtr doc, client_t *client,
+        int response, const char *xslt_template)
+{
+    if (response == RAW)
+    {
+        xmlChar *buff = NULL;
+        int len = 0;
+        unsigned int buf_len;
+        const char *http = "HTTP/1.0 200 OK\r\n"
+               "Content-Type: text/xml\r\n"
+               "Content-Length: ";
+        xmlDocDumpMemory(doc, &buff, &len);
+        buf_len = strlen (http) + len + 20;
+        client_set_queue (client, NULL);
+        client->refbuf = refbuf_new (buf_len);
+        len = snprintf (client->refbuf->data, buf_len, "%s%d\r\n\r\n%s", http, len, buff);
+        client->refbuf->len = len;
+        xmlFree(buff);
+        client->respcode = 200;
+        fserve_add_client (client, NULL);
+    }
+    if (response == TRANSFORMED)
+    {
+        char *fullpath_xslt_template;
+        int fullpath_xslt_template_len;
+        ice_config_t *config = config_get_config();
+
+        fullpath_xslt_template_len = strlen (config->adminroot_dir) + 
+            strlen (xslt_template) + 2;
+        fullpath_xslt_template = malloc(fullpath_xslt_template_len);
+        snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s",
+            config->adminroot_dir, PATH_SEPARATOR, xslt_template);
+        config_release_config();
+
+        DEBUG1("Sending XSLT (%s)", fullpath_xslt_template);
+        xslt_transform(doc, fullpath_xslt_template, client);
+        free(fullpath_xslt_template);
+    }
+}
+
+
+void admin_handle_request(client_t *client, const char *uri)
+{
+    const char *mount, *command_string;
+    int command;
+
+    DEBUG1("Admin request (%s)", uri);
+    if (!((strcmp(uri, "/admin.cgi") == 0) ||
+         (strncmp("/admin/", uri, 7) == 0))) {
+        ERROR0("Internal error: admin request isn't");
+        client_send_401(client);
+        return;
+    }
+
+    if (strcmp(uri, "/admin.cgi") == 0) {
+        command_string = uri + 1;
+    }
+    else {
+        command_string = uri + 7;
+    }
+
+    DEBUG1("Got command (%s)", command_string);
+    command = admin_get_command(command_string);
+
+    if(command < 0) {
+        ERROR1("Error parsing command string or unrecognised command: %s",
+                command_string);
+        client_send_400(client, "Unrecognised command");
+        return;
+    }
+
+    if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
+
+        ice_config_t *config;
+        const char *sc_mount;
+        const char *pass = httpp_get_query_param (client->parser, "pass");
+        listener_t *listener;
+
+        if (pass == NULL)
+        {
+            client_send_400 (client, "missing pass parameter");
+            return;
+        }
+        config = config_get_config ();
+        sc_mount = config->shoutcast_mount;
+        listener = config_get_listen_sock (config, client->con);
+        if (listener && listener->shoutcast_mount)
+            sc_mount = listener->shoutcast_mount;
+
+        httpp_set_query_param (client->parser, "mount", sc_mount);
+        httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY");
+        httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass);
+        config_release_config ();
+    }
+
+    mount = httpp_get_query_param(client->parser, "mount");
+
+    if(mount != NULL) {
+        source_t *source;
+
+        /* this request does not require auth but can apply to files on webroot */
+        if (command == COMMAND_BUILDM3U)
+        {
+            command_buildm3u (client, mount);
+            return;
+        }
+        /* This is a mount request, handle it as such */
+        if (!connection_check_admin_pass(client->parser))
+        {
+            if (!connection_check_source_pass(client->parser, mount))
+            {
+                INFO1("Bad or missing password on mount modification admin "
+                        "request (command: %s)", command_string);
+                client_send_401(client);
+                return;
+            }
+        }
+        
+        avl_tree_rlock(global.source_tree);
+        source = source_find_mount_raw(mount);
+
+        if (source == NULL)
+        {
+            WARN2("Admin command %s on non-existent source %s", 
+                    command_string, mount);
+            avl_tree_unlock(global.source_tree);
+            client_send_400(client, "Source does not exist");
+        }
+        else
+        {
+            if (source->running == 0 && source->on_demand == 0)
+            {
+                avl_tree_unlock (global.source_tree);
+                INFO2("Received admin command %s on unavailable mount \"%s\"",
+                        command_string, mount);
+                client_send_400 (client, "Source is not available");
+                return;
+            }
+            if (command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
+                    source->shoutcast_compat == 0)
+            {
+                avl_tree_unlock (global.source_tree);
+                ERROR0 ("illegal change of metadata on non-shoutcast "
+                        "compatible stream");
+                client_send_400 (client, "illegal metadata call");
+                return;
+            }
+            INFO2("Received admin command %s on mount \"%s\"", 
+                    command_string, mount);
+            admin_handle_mount_request(client, source, command);
+            avl_tree_unlock(global.source_tree);
+        }
+    }
+    else {
+
+        if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
+        /* this request is used by a slave relay to retrieve
+           mounts from the master, so handle this request
+           validating against the relay password */
+            if(!connection_check_relay_pass(client->parser)) {
+                INFO1("Bad or missing password on admin command "
+                      "request (command: %s)", command_string);
+                client_send_401(client);
+                return;
+            }
+        }
+        else {
+            if(!connection_check_admin_pass(client->parser)) {
+                INFO1("Bad or missing password on admin command "
+                      "request (command: %s)", command_string);
+                client_send_401(client);
+                return;
+            }
+        }
+        
+        admin_handle_general_request(client, command);
+    }
+}
+
+static void admin_handle_general_request(client_t *client, int command)
+{
+    switch(command) {
+        case COMMAND_RAW_STATS:
+            command_stats(client, NULL, RAW);
+            break;
+        case COMMAND_RAW_LIST_MOUNTS:
+            command_list_mounts(client, RAW);
+            break;
+        case COMMAND_RAW_LISTSTREAM:
+            command_list_mounts(client, RAW);
+            break;
+        case COMMAND_PLAINTEXT_LISTSTREAM:
+            command_list_mounts(client, PLAINTEXT);
+            break;
+        case COMMAND_TRANSFORMED_STATS:
+            command_stats(client, NULL, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_LIST_MOUNTS:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_LISTSTREAM:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        default:
+            WARN0("General admin request not recognised");
+            client_send_400(client, "Unknown admin request");
+            return;
+    }
+}
+
+static void admin_handle_mount_request(client_t *client, source_t *source, 
+        int command)
+{
+    switch(command) {
+        case COMMAND_RAW_STATS:
+            command_stats(client, source->mount, RAW);
+            break;
+        case COMMAND_RAW_FALLBACK:
+            command_fallback(client, source, RAW);
+            break;
+        case COMMAND_RAW_METADATA_UPDATE:
+            command_metadata(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_METADATA_UPDATE:
+            command_metadata(client, source, TRANSFORMED);
+            break;
+        case COMMAND_SHOUTCAST_METADATA_UPDATE:
+            command_shoutcast_metadata(client, source);
+            break;
+        case COMMAND_RAW_SHOW_LISTENERS:
+            command_show_listeners(client, source, RAW);
+            break;
+        case COMMAND_RAW_MOVE_CLIENTS:
+            command_move_clients(client, source, RAW);
+            break;
+        case COMMAND_RAW_KILL_CLIENT:
+            command_kill_client(client, source, RAW);
+            break;
+        case COMMAND_RAW_KILL_SOURCE:
+            command_kill_source(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_STATS:
+            command_stats(client, source->mount, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_FALLBACK:
+            command_fallback(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_SHOW_LISTENERS:
+            command_show_listeners(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
+            command_move_clients(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_KILL_CLIENT:
+            command_kill_client(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_KILL_SOURCE:
+            command_kill_source(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MANAGEAUTH:
+            command_manageauth(client, source, TRANSFORMED);
+            break;
+        case COMMAND_RAW_MANAGEAUTH:
+            command_manageauth(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_UPDATEMETADATA:
+            command_updatemetadata(client, source, TRANSFORMED);
+            break;
+        case COMMAND_RAW_UPDATEMETADATA:
+            command_updatemetadata(client, source, RAW);
+            break;
+        default:
+            WARN0("Mount request not recognised");
+            client_send_400(client, "Mount request unknown");
+            break;
+    }
+}
+
+#define COMMAND_REQUIRE(client,name,var) \
+    do { \
+        (var) = httpp_get_query_param((client)->parser, (name)); \
+        if((var) == NULL) { \
+            client_send_400((client), "Missing parameter"); \
+            return; \
+        } \
+    } while(0);
+#define COMMAND_OPTIONAL(client,name,var) \
+    (var) = httpp_get_query_param((client)->parser, (name))
+
+static void html_success(client_t *client, char *message)
+{
+    client->respcode = 200;
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 
+            "<html><head><title>Admin request successful</title></head>"
+            "<body><p>%s</p></body></html>", message);
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+
+static void command_move_clients(client_t *client, source_t *source,
+    int response)
+{
+    const char *dest_source;
+    source_t *dest;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    char buf[255];
+    int parameters_passed = 0;
+
+    DEBUG0("Doing optional check");
+    if((COMMAND_OPTIONAL(client, "destination", dest_source))) {
+        parameters_passed = 1;
+    }
+    DEBUG1("Done optional check (%d)", parameters_passed);
+    if (!parameters_passed) {
+        doc = admin_build_sourcelist(source->mount);
+        admin_send_response(doc, client, response, 
+             MOVECLIENTS_TRANSFORMED_REQUEST);
+        xmlFreeDoc(doc);
+        return;
+    }
+
+    dest = source_find_mount (dest_source);
+
+    if (dest == NULL)
+    {
+        client_send_400 (client, "No such destination");
+        return;
+    }
+
+    if (strcmp (dest->mount, source->mount) == 0)
+    {
+        client_send_400 (client, "supplied mountpoints are identical");
+        return;
+    }
+
+    if (dest->running == 0 && dest->on_demand == 0)
+    {
+        client_send_400 (client, "Destination not running");
+        return;
+    }
+
+    INFO2 ("source is \"%s\", destination is \"%s\"", source->mount, dest->mount);
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+    xmlDocSetRootElement(doc, node);
+
+    source_move_clients (source, dest);
+
+    memset(buf, '\000', sizeof(buf));
+    snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
+            source->mount, dest_source);
+    xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR(buf));
+    xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
+
+    admin_send_response(doc, client, response, 
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+}
+
+static void command_show_listeners(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode, listenernode;
+    avl_node *client_node;
+    client_t *current;
+    char buf[22];
+    const char *userAgent = NULL;
+    time_t now = time(NULL);
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
+    srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
+    xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
+    xmlDocSetRootElement(doc, node);
+
+    memset(buf, '\000', sizeof(buf));
+    snprintf (buf, sizeof(buf), "%lu", source->listeners);
+    xmlNewChild(srcnode, NULL, XMLSTR("Listeners"), XMLSTR(buf));
+
+    avl_tree_rlock(source->client_tree);
+
+    client_node = avl_get_first(source->client_tree);
+    while(client_node) {
+        current = (client_t *)client_node->key;
+        listenernode = xmlNewChild(srcnode, NULL, XMLSTR("listener"), NULL);
+        xmlNewChild(listenernode, NULL, XMLSTR("IP"), XMLSTR(current->con->ip));
+        userAgent = httpp_getvar(current->parser, "user-agent");
+        if (userAgent) {
+            xmlNewChild(listenernode, NULL, XMLSTR("UserAgent"), XMLSTR(userAgent));
+        }
+        else {
+            xmlNewChild(listenernode, NULL, XMLSTR("UserAgent"), XMLSTR("Unknown"));
+        }
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf), "%lu", (unsigned long)(now - current->con->con_time));
+        xmlNewChild(listenernode, NULL, XMLSTR("Connected"), XMLSTR(buf));
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
+        xmlNewChild(listenernode, NULL, XMLSTR("ID"), XMLSTR(buf));
+        if (current->username) {
+            xmlNewChild(listenernode, NULL, XMLSTR("username"), XMLSTR(current->username));
+        }
+        client_node = avl_get_next(client_node);
+    }
+
+    avl_tree_unlock(source->client_tree);
+    admin_send_response(doc, client, response, 
+        LISTCLIENTS_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+}
+
+static void command_buildm3u(client_t *client,  const char *mount)
+{
+    const char *username = NULL;
+    const char *password = NULL;
+    ice_config_t *config;
+
+    COMMAND_REQUIRE(client, "username", username);
+    COMMAND_REQUIRE(client, "password", password);
+
+    client->respcode = 200;
+    config = config_get_config();
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+        "HTTP/1.0 200 OK\r\n"
+        "Content-Type: audio/x-mpegurl\r\n"
+        "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
+        "http://%s:%s@%s:%d%s\r\n",
+        username,
+        password,
+        config->hostname,
+        config->port,
+        mount
+    );
+    config_release_config();
+
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+
+static void command_manageauth(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode, msgnode;
+    const char *action = NULL;
+    const char *username = NULL;
+    char *message = NULL;
+    int ret = AUTH_OK;
+    ice_config_t *config = config_get_config ();
+    mount_proxy *mountinfo = config_find_mount (config, source->mount);
+
+    do
+    {
+        if (mountinfo == NULL || mountinfo->auth == NULL)
+        {
+            WARN1 ("manage auth request for %s but no facility available", source->mount);
+            break;
+        }
+        COMMAND_OPTIONAL(client, "action", action);
+        COMMAND_OPTIONAL (client, "username", username);
+
+        if (action == NULL)
+            action = "list";
+
+        if (!strcmp(action, "add"))
+        {
+            const char *password = NULL;
+            COMMAND_OPTIONAL (client, "password", password);
+
+            if (username == NULL || password == NULL)
+            {
+                WARN1 ("manage auth request add for %s but no user/pass", source->mount);
+                break;
+            }
+            ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
+            if (ret == AUTH_FAILED) {
+                message = strdup("User add failed - check the icecast error log");
+            }
+            if (ret == AUTH_USERADDED) {
+                message = strdup("User added");
+            }
+            if (ret == AUTH_USEREXISTS) {
+                message = strdup("User already exists - not added");
+            }
+        }
+        if (!strcmp(action, "delete"))
+        {
+            if (username == NULL)
+            {
+                WARN1 ("manage auth request delete for %s but no username", source->mount);
+                break;
+            }
+            ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
+            if (ret == AUTH_FAILED) {
+                message = strdup("User delete failed - check the icecast error log");
+            }
+            if (ret == AUTH_USERDELETED) {
+                message = strdup("User deleted");
+            }
+        }
+
+        doc = xmlNewDoc (XMLSTR("1.0"));
+        node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
+        srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
+        xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
+
+        if (message) {
+            msgnode = xmlNewChild(node, NULL, XMLSTR("iceresponse"), NULL);
+            xmlNewChild(msgnode, NULL, XMLSTR("message"), XMLSTR(message));
+        }
+
+        xmlDocSetRootElement(doc, node);
+
+        if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
+            mountinfo->auth->listuser (mountinfo->auth, srcnode);
+
+        config_release_config ();
+
+        admin_send_response(doc, client, response, 
+                MANAGEAUTH_TRANSFORMED_REQUEST);
+        free (message);
+        xmlFreeDoc(doc);
+        return;
+    } while (0);
+
+    config_release_config ();
+    client_send_400 (client, "missing parameter");
+}
+
+static void command_kill_source(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node;
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+    xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR("Source Removed"));
+    xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
+    xmlDocSetRootElement(doc, node);
+
+    source->running = 0;
+
+    admin_send_response(doc, client, response, 
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+}
+
+static void command_kill_client(client_t *client, source_t *source,
+    int response)
+{
+    const char *idtext;
+    int id;
+    client_t *listener;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    char buf[50] = "";
+
+    COMMAND_REQUIRE(client, "id", idtext);
+
+    id = atoi(idtext);
+
+    listener = source_find_client(source, id);
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+    xmlDocSetRootElement(doc, node);
+    DEBUG1("Response is %d", response);
+
+    if(listener != NULL) {
+        INFO1("Admin request: client %d removed", id);
+
+        /* This tags it for removal on the next iteration of the main source
+         * loop
+         */
+        listener->con->error = 1;
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "Client %d removed", id);
+        xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR(buf));
+        xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
+    }
+    else {
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "Client %d not found", id);
+        xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR(buf));
+        xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("0"));
+    }
+    admin_send_response(doc, client, response, 
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+}
+
+static void command_fallback(client_t *client, source_t *source,
+    int response)
+{
+    const char *fallback;
+    char *old;
+
+    DEBUG0("Got fallback request");
+
+    COMMAND_REQUIRE(client, "fallback", fallback);
+
+    old = source->fallback_mount;
+    source->fallback_mount = strdup(fallback);
+    free(old);
+
+    html_success(client, "Fallback configured");
+}
+
+static void command_metadata(client_t *client, source_t *source,
+    int response)
+{
+    const char *action;
+    const char *song, *title, *artist, *charset;
+    format_plugin_t *plugin;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode (doc, NULL, XMLSTR("iceresponse"), NULL);
+    xmlDocSetRootElement(doc, node);
+
+    DEBUG0("Got metadata update request");
+
+    COMMAND_REQUIRE(client, "mode", action);
+    COMMAND_OPTIONAL(client, "song", song);
+    COMMAND_OPTIONAL(client, "title", title);
+    COMMAND_OPTIONAL(client, "artist", artist);
+    COMMAND_OPTIONAL(client, "charset", charset);
+
+    if (strcmp (action, "updinfo") != 0)
+    {
+        xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR("No such action"));
+        xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("0"));
+        admin_send_response(doc, client, response, 
+            ADMIN_XSL_RESPONSE);
+        xmlFreeDoc(doc);
+        return;
+    }
+
+    plugin = source->format;
+
+    if (plugin && plugin->set_tag)
+    {
+        if (song)
+        {
+            plugin->set_tag (plugin, "song", song, charset);
+            INFO2 ("Metadata on mountpoint %s changed to \"%s\"", source->mount, song);
+        }
+        else
+        {
+            if (artist && title)
+            {
+                plugin->set_tag (plugin, "title", title, charset);
+                plugin->set_tag (plugin, "artist", artist, charset);
+                INFO3("Metadata on mountpoint %s changed to \"%s - %s\"",
+                        source->mount, artist, title);
+            }
+        }
+    }
+    else
+    {
+        xmlNewChild(node, NULL, XMLSTR("message"), 
+            XMLSTR("Mountpoint will not accept URL updates"));
+        xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
+        admin_send_response(doc, client, response, 
+            ADMIN_XSL_RESPONSE);
+        xmlFreeDoc(doc);
+        return;
+    }
+
+    xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR("Metadata update successful"));
+    xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
+    admin_send_response(doc, client, response, 
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+}
+
+static void command_shoutcast_metadata(client_t *client, source_t *source)
+{
+    const char *action;
+    const char *value;
+
+    DEBUG0("Got shoutcast metadata update request");
+
+    COMMAND_REQUIRE(client, "mode", action);
+    COMMAND_REQUIRE(client, "song", value);
+
+    if (strcmp (action, "updinfo") != 0)
+    {
+        client_send_400 (client, "No such action");
+        return;
+    }
+
+    if (source->format && source->format->set_tag)
+    {
+        source->format->set_tag (source->format, "title", value, NULL);
+
+        DEBUG2("Metadata on mountpoint %s changed to \"%s\"", 
+                source->mount, value);
+        html_success(client, "Metadata update successful");
+    }
+    else
+    {
+        client_send_400 (client, "mountpoint will not accept URL updates");
+    }
+}
+
+static void command_stats(client_t *client, const char *mount, int response) {
+    xmlDocPtr doc;
+
+    DEBUG0("Stats request, sending xml stats");
+
+    stats_get_xml(&doc, 1, mount);
+    admin_send_response(doc, client, response, STATS_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+    return;
+}
+
+static void command_list_mounts(client_t *client, int response)
+{
+    DEBUG0("List mounts request");
+
+    if (response == PLAINTEXT)
+    {
+        snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+                "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
+        client->refbuf->len = strlen (client->refbuf->data);
+        client->respcode = 200;
+
+        client->refbuf->next = stats_get_streams ();
+        fserve_add_client (client, NULL);
+    }
+    else
+    {
+        xmlDocPtr doc;
+        avl_tree_rlock (global.source_tree);
+        doc = admin_build_sourcelist(NULL);
+        avl_tree_unlock (global.source_tree);
+
+        admin_send_response(doc, client, response, 
+            LISTMOUNTS_TRANSFORMED_REQUEST);
+        xmlFreeDoc(doc);
+    }
+}
+
+static void command_updatemetadata(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode;
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
+    srcnode = xmlNewChild (node, NULL, XMLSTR("source"), NULL);
+    xmlSetProp (srcnode, XMLSTR("mount"), XMLSTR(source->mount));
+    xmlDocSetRootElement(doc, node);
+
+    admin_send_response(doc, client, response, 
+        UPDATEMETADATA_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+}

Deleted: icecast/tags/icecast-2.3.2/src/admin.h
===================================================================
--- icecast/trunk/icecast/src/admin.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/admin.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,30 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __ADMIN_H__
-#define __ADMIN_H__
-
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-#include "refbuf.h"
-#include "client.h"
-
-#define RAW         1
-#define TRANSFORMED 2
-#define PLAINTEXT   3
-
-void admin_handle_request(client_t *client, char *uri);
-void admin_send_response(xmlDocPtr doc, client_t *client, 
-        int response, const char *xslt_template);
-
-#endif  /* __ADMIN_H__ */

Copied: icecast/tags/icecast-2.3.2/src/admin.h (from rev 13559, icecast/trunk/icecast/src/admin.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/admin.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/admin.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,30 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __ADMIN_H__
+#define __ADMIN_H__
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "refbuf.h"
+#include "client.h"
+
+#define RAW         1
+#define TRANSFORMED 2
+#define PLAINTEXT   3
+
+void admin_handle_request(client_t *client, const char *uri);
+void admin_send_response(xmlDocPtr doc, client_t *client, 
+        int response, const char *xslt_template);
+
+#endif  /* __ADMIN_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/auth.c
===================================================================
--- icecast/trunk/icecast/src/auth.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,605 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/** 
- * Client authentication functions
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include "auth.h"
-#include "auth_htpasswd.h"
-#include "auth_url.h"
-#include "source.h"
-#include "client.h"
-#include "cfgfile.h"
-#include "stats.h"
-#include "httpp/httpp.h"
-#include "fserve.h"
-
-#include "logging.h"
-#define CATMODULE "auth"
-
-
-static volatile auth_client *clients_to_auth;
-static volatile unsigned int auth_pending_count;
-static volatile int auth_running;
-static mutex_t auth_lock;
-static thread_type *auth_thread;
-
-
-static void auth_client_setup (mount_proxy *mountinfo, client_t *client)
-{
-    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
-    char *header = httpp_getvar(client->parser, "authorization");
-    char *userpass, *tmp;
-    char *username, *password;
-
-    do
-    {
-        if (header == NULL)
-            break;
-
-        if (strncmp(header, "Basic ", 6) == 0)
-        {
-            userpass = util_base64_decode (header+6);
-            if (userpass == NULL)
-            {
-                WARN1("Base64 decode of Authorization header \"%s\" failed",
-                        header+6);
-                break;
-            }
-
-            tmp = strchr(userpass, ':');
-            if (tmp == NULL)
-            { 
-                free (userpass);
-                break;
-            }
-
-            *tmp = 0;
-            username = userpass;
-            password = tmp+1;
-            client->username = strdup (username);
-            client->password = strdup (password);
-            free (userpass);
-            break;
-        }
-        INFO1 ("unhandled authorization header: %s", header);
-
-    } while (0);
-
-    thread_mutex_lock (&mountinfo->auth->lock);
-    client->auth = mountinfo->auth;
-    client->auth->refcount++;
-    thread_mutex_unlock (&mountinfo->auth->lock);
-}
-
-
-static void queue_auth_client (auth_client *auth_user)
-{
-    thread_mutex_lock (&auth_lock);
-    auth_user->next = (auth_client *)clients_to_auth;
-    clients_to_auth = auth_user;
-    auth_pending_count++;
-    thread_mutex_unlock (&auth_lock);
-}
-
-
-/* release the auth. It is referred to by multiple structures so this is
- * refcounted and only actual freed after the last use
- */
-void auth_release (auth_t *authenticator)
-{
-    if (authenticator == NULL)
-        return;
-
-    thread_mutex_lock (&authenticator->lock);
-    authenticator->refcount--;
-    if (authenticator->refcount)
-    {
-        thread_mutex_unlock (&authenticator->lock);
-        return;
-    }
-
-    if (authenticator->free)
-        authenticator->free (authenticator);
-    xmlFree (authenticator->type);
-    thread_mutex_unlock (&authenticator->lock);
-    thread_mutex_destroy (&authenticator->lock);
-    free (authenticator);
-}
-
-
-void auth_client_free (auth_client *auth_user)
-{
-    if (auth_user == NULL)
-        return;
-    if (auth_user->client)
-    {
-        client_t *client = auth_user->client;
-
-        if (client->respcode)
-            client_destroy (client);
-        else
-            client_send_401 (client);
-        auth_user->client = NULL;
-    }
-    free (auth_user->mount);
-    free (auth_user);
-}
-
-
-/* wrapper function for auth thread to authenticate new listener
- * connection details
- */
-static void auth_new_listener (auth_client *auth_user)
-{
-    client_t *client = auth_user->client;
-
-    if (client->auth->authenticate)
-    {
-        if (client->auth->authenticate (auth_user) != AUTH_OK)
-        {
-            auth_release (client->auth);
-            client->auth = NULL;
-            return;
-        }
-    }
-    if (auth_postprocess_client (auth_user) < 0)
-        INFO1 ("client %lu failed", client->con->id);
-}
-
-
-/* wrapper function are auth thread to authenticate new listener
- * connections
- */
-static void auth_remove_listener (auth_client *auth_user)
-{
-    client_t *client = auth_user->client;
-
-    if (client->auth->release_client)
-        client->auth->release_client (auth_user);
-    auth_release (client->auth);
-    client->auth = NULL;
-    return;
-}
-
-
-/* The auth thread main loop. */
-static void *auth_run_thread (void *arg)
-{
-    INFO0 ("Authentication thread started");
-    while (1)
-    {
-        if (clients_to_auth)
-        {
-            auth_client *auth_user;
-
-            thread_mutex_lock (&auth_lock);
-            auth_user = (auth_client*)clients_to_auth;
-            clients_to_auth = auth_user->next;
-            auth_pending_count--;
-            thread_mutex_unlock (&auth_lock);
-            auth_user->next = NULL;
-
-            if (auth_user->process)
-                auth_user->process (auth_user);
-            else
-                ERROR0 ("client auth process not set");
-
-            auth_client_free (auth_user);
-
-            continue;
-        }
-        /* is there a request to shutdown */
-        if (auth_running == 0)
-            break;
-        thread_sleep (150000);
-    }
-    INFO0 ("Authenication thread shutting down");
-    return NULL;
-}
-
-
-/* Check whether this client is currently on this mount, the client may be
- * on either the active or pending lists.
- * return 1 if ok to add or 0 to prevent
- */
-static int check_duplicate_logins (source_t *source, client_t *client)
-{
-    auth_t *auth = client->auth;
-
-    /* allow multiple authenticated relays */
-    if (client->username == NULL)
-        return 1;
-
-    if (auth && auth->allow_duplicate_users == 0)
-    {
-        avl_node *node;
-
-        avl_tree_rlock (source->client_tree);
-        node = avl_get_first (source->client_tree);
-        while (node)
-        {   
-            client_t *existing_client = (client_t *)node->key;
-            if (existing_client->username && 
-                    strcmp (existing_client->username, client->username) == 0)
-            {
-                avl_tree_unlock (source->client_tree);
-                return 0;
-            }
-            node = avl_get_next (node);
-        }       
-        avl_tree_unlock (source->client_tree);
-
-        avl_tree_rlock (source->pending_tree);
-        node = avl_get_first (source->pending_tree);
-        while (node)
-        {
-            client_t *existing_client = (client_t *)node->key;
-            if (existing_client->username && 
-                    strcmp (existing_client->username, client->username) == 0)
-            {
-                avl_tree_unlock (source->pending_tree);
-                return 0;
-            }
-            node = avl_get_next (node);
-        }
-        avl_tree_unlock (source->pending_tree);
-    }
-    return 1;
-}
-
-
-/* if 0 is returned then the client should not be touched, however if -1
- * is returned then the caller is responsible for handling the client
- */
-static int add_client_to_source (source_t *source, client_t *client)
-{
-    int loop = 10;
-    do
-    {
-        DEBUG3 ("max on %s is %ld (cur %lu)", source->mount,
-                source->max_listeners, source->listeners);
-        if (source->max_listeners == -1)
-            break;
-        if (source->listeners < (unsigned long)source->max_listeners)
-            break;
-
-        if (loop && source->fallback_when_full && source->fallback_mount)
-        {
-            source_t *next = source_find_mount (source->fallback_mount);
-            if (!next) {
-                ERROR2("Fallback '%s' for full source '%s' not found", 
-                        source->mount, source->fallback_mount);
-                return -1;
-            }
-
-            INFO1 ("stream full trying %s", next->mount);
-            source = next;
-            loop--;
-            continue;
-        }
-        /* now we fail the client */
-        return -1;
-
-    } while (1);
-
-    client->write_to_client = format_generic_write_to_client;
-    client->check_buffer = format_check_http_buffer;
-    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
-    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
-
-    /* lets add the client to the active list */
-    avl_tree_wlock (source->pending_tree);
-    avl_insert (source->pending_tree, client);
-    avl_tree_unlock (source->pending_tree);
-
-    if (source->running == 0 && source->on_demand)
-    {
-        /* enable on-demand relay to start, wake up the slave thread */
-        DEBUG0("kicking off on-demand relay");
-        source->on_demand_req = 1;
-    }
-    DEBUG1 ("Added client to %s", source->mount);
-    return 0;
-}
-
-
-/* Add listener to the pending lists of either the  source or fserve thread.
- * This can be run from the connection or auth thread context
- */
-static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, client_t *client)
-{
-    int ret = 0;
-    source_t *source = NULL;
-
-    avl_tree_rlock (global.source_tree);
-    source = source_find_mount (mount);
-
-    if (source)
-    {
-        if (client->auth && check_duplicate_logins (source, client) == 0)
-        {
-            avl_tree_unlock (global.source_tree);
-            return -1;
-        }
-        if (mountinfo)
-        {
-            /* set a per-mount disconnect time if auth hasn't set one already */
-            if (mountinfo->max_listener_duration && client->con->discon_time == 0)
-                client->con->discon_time = time(NULL) + mountinfo->max_listener_duration;
-        }
-
-        ret = add_client_to_source (source, client);
-        avl_tree_unlock (global.source_tree);
-        if (ret == 0)
-            DEBUG0 ("client authenticated, passed to source");
-    }
-    else
-    {
-        avl_tree_unlock (global.source_tree);
-        fserve_client_create (client, mount);
-    }
-    return ret;
-}
-
-
-int auth_postprocess_client (auth_client *auth_user)
-{
-    int ret;
-    ice_config_t *config = config_get_config();
-
-    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
-    auth_user->client->authenticated = 1;
-
-    ret = add_authenticated_client (auth_user->mount, mountinfo, auth_user->client);
-    config_release_config();
-
-    if (ret < 0)
-        client_send_401 (auth_user->client);
-    auth_user->client = NULL;
-
-    return ret;
-}
-
-
-/* Add a listener. Check for any mount information that states any
- * authentication to be used.
- */
-void add_client (const char *mount, client_t *client)
-{
-    mount_proxy *mountinfo; 
-    ice_config_t *config = config_get_config();
-
-    mountinfo = config_find_mount (config, mount);
-    if (mountinfo && mountinfo->no_mount)
-    {
-        config_release_config ();
-        client_send_403 (client, "mountpoint unavailable");
-        return;
-    }
-    if (mountinfo && mountinfo->auth)
-    {
-        auth_client *auth_user;
-
-        if (auth_pending_count > 30)
-        {
-            config_release_config ();
-            WARN0 ("too many clients awaiting authentication");
-            client_send_403 (client, "busy, please try again later");
-            return;
-        }
-        auth_client_setup (mountinfo, client);
-        config_release_config ();
-
-        if (client->auth == NULL)
-        {
-            client_send_401 (client);
-            return;
-        }
-        auth_user = calloc (1, sizeof (auth_client));
-        if (auth_user == NULL)
-        {
-            client_send_401 (client);
-            return;
-        }
-        auth_user->mount = strdup (mount);
-        auth_user->process = auth_new_listener;
-        auth_user->client = client;
-
-        INFO0 ("adding client for authentication");
-        queue_auth_client (auth_user);
-    }
-    else
-    {
-        int ret = add_authenticated_client (mount, mountinfo, client);
-        config_release_config ();
-        if (ret < 0)
-            client_send_403 (client, "max listeners reached");
-    }
-}
-
-
-/* determine whether we need to process this client further. This
- * involves any auth exit, typically for external auth servers.
- */
-int release_client (client_t *client)
-{
-    if (client->auth)
-    {
-        auth_client *auth_user = calloc (1, sizeof (auth_client));
-        if (auth_user == NULL)
-            return 0;
-
-        auth_user->mount = strdup (httpp_getvar (client->parser, HTTPP_VAR_URI));
-        auth_user->process = auth_remove_listener;
-        auth_user->client = client;
-
-        queue_auth_client (auth_user);
-        return 1;
-    }
-    return 0;
-}
-
-
-static void get_authenticator (auth_t *auth, config_options_t *options)
-{
-    do
-    {
-        DEBUG1 ("type is %s", auth->type);
-#ifdef HAVE_AUTH_URL
-        if (strcmp (auth->type, "url") == 0)
-        {
-            auth_get_url_auth (auth, options);
-            break;
-        }
-#endif
-        if (strcmp (auth->type, "htpasswd") == 0)
-        {
-            auth_get_htpasswd_auth (auth, options);
-            break;
-        }
-        
-        ERROR1("Unrecognised authenticator type: \"%s\"", auth->type);
-        return;
-    } while (0);
-
-    auth->refcount = 1;
-    while (options)
-    {
-        if (strcmp(options->name, "allow_duplicate_users") == 0)
-            auth->allow_duplicate_users = atoi (options->value);
-        options = options->next;
-    }
-}
-
-
-auth_t *auth_get_authenticator (xmlNodePtr node)
-{
-    auth_t *auth = calloc (1, sizeof (auth_t));
-    config_options_t *options = NULL, **next_option = &options;
-    xmlNodePtr option;
-
-    if (auth == NULL)
-        return NULL;
-
-    option = node->xmlChildrenNode;
-    while (option)
-    {
-        xmlNodePtr current = option;
-        option = option->next;
-        if (strcmp (current->name, "option") == 0)
-        {
-            config_options_t *opt = calloc (1, sizeof (config_options_t));
-            opt->name = xmlGetProp (current, "name");
-            if (opt->name == NULL)
-            {
-                free(opt);
-                continue;
-            }
-            opt->value = xmlGetProp (current, "value");
-            if (opt->value == NULL)
-            {
-                xmlFree (opt->name);
-                free (opt);
-                continue;
-            }
-            *next_option = opt;
-            next_option = &opt->next;
-        }
-        else
-            if (strcmp (current->name, "text") != 0)
-                WARN1 ("unknown auth setting (%s)", current->name);
-    }
-    auth->type = xmlGetProp (node, "type");
-    get_authenticator (auth, options);
-    thread_mutex_create (&auth->lock);
-    while (options)
-    {
-        config_options_t *opt = options;
-        options = opt->next;
-        xmlFree (opt->name);
-        xmlFree (opt->value);
-        free (opt);
-    }
-    return auth;
-}
-
-
-/* called when the stream starts, so that authentication engine can do any
- * cleanup/initialisation.
- */
-void auth_stream_start (mount_proxy *mountinfo, const char *mount)
-{
-    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
-    {
-        auth_client *auth_user = calloc (1, sizeof (auth_client));
-        if (auth_user)
-        {
-            auth_user->mount = strdup (mount);
-            auth_user->process = mountinfo->auth->stream_start;
-
-            queue_auth_client (auth_user);
-        }
-    }
-}
-
-
-/* Called when the stream ends so that the authentication engine can do
- * any authentication cleanup
- */
-void auth_stream_end (mount_proxy *mountinfo, const char *mount)
-{
-    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
-    {
-        auth_client *auth_user = calloc (1, sizeof (auth_client));
-        if (auth_user)
-        {
-            auth_user->mount = strdup (mount);
-            auth_user->process = mountinfo->auth->stream_end;
-
-            queue_auth_client (auth_user);
-        }
-    }
-}
-
-
-/* these are called at server start and termination */
-
-void auth_initialise (void)
-{
-    clients_to_auth = NULL;
-    auth_pending_count = 0;
-    auth_running = 1;
-    thread_mutex_create (&auth_lock);
-    auth_thread = thread_create ("auth thread", auth_run_thread, NULL, THREAD_ATTACHED);
-}
-
-void auth_shutdown (void)
-{
-    if (auth_thread)
-    {
-        auth_running = 0;
-        thread_join (auth_thread);
-        INFO0 ("Auth thread has terminated");
-    }
-}
-

Copied: icecast/tags/icecast-2.3.2/src/auth.c (from rev 14788, icecast/trunk/icecast/src/auth.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,703 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/** 
+ * Client authentication functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "auth.h"
+#include "auth_htpasswd.h"
+#include "auth_url.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "stats.h"
+#include "httpp/httpp.h"
+#include "fserve.h"
+
+#include "logging.h"
+#define CATMODULE "auth"
+
+
+static mutex_t auth_lock;
+
+
+static auth_client *auth_client_setup (const char *mount, client_t *client)
+{
+    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
+    const char *header = httpp_getvar(client->parser, "authorization");
+    char *userpass, *tmp;
+    char *username, *password;
+    auth_client *auth_user;
+
+    do
+    {
+        if (header == NULL)
+            break;
+
+        if (strncmp(header, "Basic ", 6) == 0)
+        {
+            userpass = util_base64_decode (header+6);
+            if (userpass == NULL)
+            {
+                WARN1("Base64 decode of Authorization header \"%s\" failed",
+                        header+6);
+                break;
+            }
+
+            tmp = strchr(userpass, ':');
+            if (tmp == NULL)
+            { 
+                free (userpass);
+                break;
+            }
+
+            *tmp = 0;
+            username = userpass;
+            password = tmp+1;
+            client->username = strdup (username);
+            client->password = strdup (password);
+            free (userpass);
+            break;
+        }
+        INFO1 ("unhandled authorization header: %s", header);
+
+    } while (0);
+
+    auth_user = calloc (1, sizeof(auth_client));
+    auth_user->mount = strdup (mount);
+    auth_user->client = client;
+    return auth_user;
+}
+
+
+static void queue_auth_client (auth_client *auth_user, mount_proxy *mountinfo)
+{
+    auth_t *auth;
+
+    if (auth_user == NULL || (mountinfo == NULL && auth_user->client
+                && auth_user->client->auth == NULL))
+        return;
+    auth_user->next = NULL;
+    if (mountinfo)
+    {
+        auth = mountinfo->auth;
+        thread_mutex_lock (&auth->lock);
+        if (auth_user->client)
+            auth_user->client->auth = auth;
+        auth->refcount++;
+    }
+    else
+    {
+        auth = auth_user->client->auth;
+        thread_mutex_lock (&auth->lock);
+    }
+    DEBUG2 ("...refcount on auth_t %s is now %d", auth->mount, auth->refcount);
+    *auth->tailp = auth_user;
+    auth->tailp = &auth_user->next;
+    auth->pending_count++;
+    INFO2 ("auth on %s has %d pending", auth->mount, auth->pending_count);
+    thread_mutex_unlock (&auth->lock);
+}
+
+
+/* release the auth. It is referred to by multiple structures so this is
+ * refcounted and only actual freed after the last use
+ */
+void auth_release (auth_t *authenticator)
+{
+    if (authenticator == NULL)
+        return;
+
+    thread_mutex_lock (&authenticator->lock);
+    authenticator->refcount--;
+    DEBUG2 ("...refcount on auth_t %s is now %d", authenticator->mount, authenticator->refcount);
+    if (authenticator->refcount)
+    {
+        thread_mutex_unlock (&authenticator->lock);
+        return;
+    }
+
+    /* cleanup auth thread attached to this auth */
+    authenticator->running = 0;
+    thread_join (authenticator->thread);
+
+    if (authenticator->free)
+        authenticator->free (authenticator);
+    xmlFree (authenticator->type);
+    thread_mutex_unlock (&authenticator->lock);
+    thread_mutex_destroy (&authenticator->lock);
+    free (authenticator->mount);
+    free (authenticator);
+}
+
+
+static void auth_client_free (auth_client *auth_user)
+{
+    if (auth_user == NULL)
+        return;
+    if (auth_user->client)
+    {
+        client_t *client = auth_user->client;
+
+        if (client->respcode)
+            client_destroy (client);
+        else
+            client_send_401 (client);
+        auth_user->client = NULL;
+    }
+    free (auth_user->mount);
+    free (auth_user);
+}
+
+
+/* verify that the listener is still connected. */
+static int is_listener_connected (client_t *client)
+{
+    int ret = 1;
+    if (client)
+    {
+        if (sock_active (client->con->sock) == 0)
+            ret = 0;
+    }
+    return ret;
+}
+
+
+/* wrapper function for auth thread to authenticate new listener
+ * connection details
+ */
+static void auth_new_listener (auth_t *auth, auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+
+    /* make sure there is still a client at this point, a slow backend request
+     * can be avoided if client has disconnected */
+    if (is_listener_connected (client) == 0)
+    {
+        DEBUG0 ("listener is no longer connected");
+        client->respcode = 400;
+        auth_release (client->auth);
+        client->auth = NULL;
+        return;
+    }
+    if (auth->authenticate)
+    {
+        if (auth->authenticate (auth_user) != AUTH_OK)
+        {
+            auth_release (client->auth);
+            client->auth = NULL;
+            return;
+        }
+    }
+    if (auth_postprocess_listener (auth_user) < 0)
+    {
+        auth_release (client->auth);
+        client->auth = NULL;
+        INFO1 ("client %lu failed", client->con->id);
+    }
+}
+
+
+/* wrapper function for auth thread to drop listener connections
+ */
+static void auth_remove_listener (auth_t *auth, auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+
+    if (client->auth->release_listener)
+        client->auth->release_listener (auth_user);
+    auth_release (client->auth);
+    client->auth = NULL;
+    /* client is going, so auth is not an issue at this point */
+    client->authenticated = 0;
+}
+
+
+/* Callback from auth thread to handle a stream start event, this applies
+ * to both source clients and relays.
+ */
+static void stream_start_callback (auth_t *auth, auth_client *auth_user)
+{
+    if (auth->stream_start)
+        auth->stream_start (auth_user);
+    auth_release (auth);
+}
+
+
+/* Callback from auth thread to handle a stream start event, this applies
+ * to both source clients and relays.
+ */
+static void stream_end_callback (auth_t *auth, auth_client *auth_user)
+{
+    if (auth->stream_end)
+        auth->stream_end (auth_user);
+    auth_release (auth);
+}
+
+
+/* The auth thread main loop. */
+static void *auth_run_thread (void *arg)
+{
+    auth_t *auth = arg;
+
+    INFO0 ("Authentication thread started");
+    while (auth->running)
+    {
+        /* usually no clients are waiting, so don't bother taking locks */
+        if (auth->head)
+        {
+            auth_client *auth_user;
+
+            /* may become NULL before lock taken */
+            thread_mutex_lock (&auth->lock);
+            auth_user = (auth_client*)auth->head;
+            if (auth_user == NULL)
+            {
+                thread_mutex_unlock (&auth->lock);
+                continue;
+            }
+            DEBUG2 ("%d client(s) pending on %s", auth->pending_count, auth->mount);
+            auth->head = auth_user->next;
+            if (auth->head == NULL)
+                auth->tailp = &auth->head;
+            auth->pending_count--;
+            thread_mutex_unlock (&auth->lock);
+            auth_user->next = NULL;
+
+            if (auth_user->process)
+                auth_user->process (auth, auth_user);
+            else
+                ERROR0 ("client auth process not set");
+
+            auth_client_free (auth_user);
+
+            continue;
+        }
+        thread_sleep (150000);
+    }
+    INFO0 ("Authenication thread shutting down");
+    return NULL;
+}
+
+
+/* Check whether this client is currently on this mount, the client may be
+ * on either the active or pending lists.
+ * return 1 if ok to add or 0 to prevent
+ */
+static int check_duplicate_logins (source_t *source, client_t *client, auth_t *auth)
+{
+    /* allow multiple authenticated relays */
+    if (client->username == NULL)
+        return 1;
+
+    if (auth && auth->allow_duplicate_users == 0)
+    {
+        avl_node *node;
+
+        avl_tree_rlock (source->client_tree);
+        node = avl_get_first (source->client_tree);
+        while (node)
+        {   
+            client_t *existing_client = (client_t *)node->key;
+            if (existing_client->username && 
+                    strcmp (existing_client->username, client->username) == 0)
+            {
+                avl_tree_unlock (source->client_tree);
+                return 0;
+            }
+            node = avl_get_next (node);
+        }       
+        avl_tree_unlock (source->client_tree);
+
+        avl_tree_rlock (source->pending_tree);
+        node = avl_get_first (source->pending_tree);
+        while (node)
+        {
+            client_t *existing_client = (client_t *)node->key;
+            if (existing_client->username && 
+                    strcmp (existing_client->username, client->username) == 0)
+            {
+                avl_tree_unlock (source->pending_tree);
+                return 0;
+            }
+            node = avl_get_next (node);
+        }
+        avl_tree_unlock (source->pending_tree);
+    }
+    return 1;
+}
+
+
+/* if 0 is returned then the client should not be touched, however if -1
+ * is returned then the caller is responsible for handling the client
+ */
+static int add_listener_to_source (source_t *source, client_t *client)
+{
+    int loop = 10;
+    do
+    {
+        DEBUG3 ("max on %s is %ld (cur %lu)", source->mount,
+                source->max_listeners, source->listeners);
+        if (source->max_listeners == -1)
+            break;
+        if (source->listeners < (unsigned long)source->max_listeners)
+            break;
+
+        if (loop && source->fallback_when_full && source->fallback_mount)
+        {
+            source_t *next = source_find_mount (source->fallback_mount);
+            if (!next) {
+                ERROR2("Fallback '%s' for full source '%s' not found", 
+                        source->mount, source->fallback_mount);
+                return -1;
+            }
+
+            INFO1 ("stream full trying %s", next->mount);
+            source = next;
+            loop--;
+            continue;
+        }
+        /* now we fail the client */
+        return -1;
+
+    } while (1);
+
+    client->write_to_client = format_generic_write_to_client;
+    client->check_buffer = format_check_http_buffer;
+    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
+    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
+
+    /* lets add the client to the active list */
+    avl_tree_wlock (source->pending_tree);
+    avl_insert (source->pending_tree, client);
+    avl_tree_unlock (source->pending_tree);
+
+    if (source->running == 0 && source->on_demand)
+    {
+        /* enable on-demand relay to start, wake up the slave thread */
+        DEBUG0("kicking off on-demand relay");
+        source->on_demand_req = 1;
+    }
+    DEBUG1 ("Added client to %s", source->mount);
+    return 0;
+}
+
+
+/* Add listener to the pending lists of either the  source or fserve thread.
+ * This can be run from the connection or auth thread context
+ */
+static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client)
+{
+    int ret = 0;
+    source_t *source = NULL;
+
+    client->authenticated = 1;
+
+    /* Here we are parsing the URI request to see if the extension is .xsl, if
+     * so, then process this request as an XSLT request
+     */
+    if (util_check_valid_extension (mount) == XSLT_CONTENT)
+    {
+        /* If the file exists, then transform it, otherwise, write a 404 */
+        DEBUG0("Stats request, sending XSL transformed stats");
+        stats_transform_xslt (client, mount);
+        return 0;
+    }
+
+    avl_tree_rlock (global.source_tree);
+    source = source_find_mount (mount);
+
+    if (source)
+    {
+        if (mountinfo)
+        {
+            if (check_duplicate_logins (source, client, mountinfo->auth) == 0)
+            {
+                avl_tree_unlock (global.source_tree);
+                return -1;
+            }
+
+            /* set a per-mount disconnect time if auth hasn't set one already */
+            if (mountinfo->max_listener_duration && client->con->discon_time == 0)
+                client->con->discon_time = time(NULL) + mountinfo->max_listener_duration;
+        }
+
+        ret = add_listener_to_source (source, client);
+        avl_tree_unlock (global.source_tree);
+        if (ret == 0)
+            DEBUG0 ("client authenticated, passed to source");
+    }
+    else
+    {
+        avl_tree_unlock (global.source_tree);
+        fserve_client_create (client, mount);
+    }
+    return ret;
+}
+
+
+int auth_postprocess_listener (auth_client *auth_user)
+{
+    int ret;
+    client_t *client = auth_user->client;
+    ice_config_t *config = config_get_config();
+
+    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
+
+    ret = add_authenticated_listener (auth_user->mount, mountinfo, client);
+    config_release_config();
+
+    if (ret < 0)
+        client_send_401 (auth_user->client);
+    auth_user->client = NULL;
+
+    return ret;
+}
+
+
+/* Add a listener. Check for any mount information that states any
+ * authentication to be used.
+ */
+void auth_add_listener (const char *mount, client_t *client)
+{
+    mount_proxy *mountinfo; 
+    ice_config_t *config = config_get_config();
+
+    mountinfo = config_find_mount (config, mount);
+    if (mountinfo && mountinfo->no_mount)
+    {
+        config_release_config ();
+        client_send_403 (client, "mountpoint unavailable");
+        return;
+    }
+    if (mountinfo && mountinfo->auth && mountinfo->auth->authenticate)
+    {
+        auth_client *auth_user;
+
+        if (mountinfo->auth->pending_count > 100)
+        {
+            config_release_config ();
+            WARN0 ("too many clients awaiting authentication");
+            client_send_403 (client, "busy, please try again later");
+            return;
+        }
+        auth_user = auth_client_setup (mount, client);
+        auth_user->process = auth_new_listener;
+        INFO0 ("adding client for authentication");
+        queue_auth_client (auth_user, mountinfo);
+        config_release_config ();
+    }
+    else
+    {
+        int ret = add_authenticated_listener (mount, mountinfo, client);
+        config_release_config ();
+        if (ret < 0)
+            client_send_403 (client, "max listeners reached");
+    }
+}
+
+
+/* determine whether we need to process this client further. This
+ * involves any auth exit, typically for external auth servers.
+ */
+int auth_release_listener (client_t *client)
+{
+    if (client->authenticated)
+    {
+        const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
+
+        /* drop any queue reference here, we do not want a race between the source thread
+         * and the auth/fserve thread */
+        client_set_queue (client, NULL);
+
+        if (mount && client->auth && client->auth->release_listener)
+        {
+            auth_client *auth_user = auth_client_setup (mount, client);
+            auth_user->process = auth_remove_listener;
+            queue_auth_client (auth_user, NULL);
+            return 1;
+        }
+        client->authenticated = 0;
+    }
+    return 0;
+}
+
+
+static int get_authenticator (auth_t *auth, config_options_t *options)
+{
+    if (auth->type == NULL)
+    {
+        WARN0 ("no authentication type defined");
+        return -1;
+    }
+    do
+    {
+        DEBUG1 ("type is %s", auth->type);
+
+        if (strcmp (auth->type, "url") == 0)
+        {
+#ifdef HAVE_AUTH_URL
+            if (auth_get_url_auth (auth, options) < 0)
+                return -1;
+            break;
+#else
+            ERROR0 ("Auth URL disabled");
+            return -1;
+#endif
+        }
+        if (strcmp (auth->type, "htpasswd") == 0)
+        {
+            if (auth_get_htpasswd_auth (auth, options) < 0)
+                return -1;
+            break;
+        }
+
+        ERROR1("Unrecognised authenticator type: \"%s\"", auth->type);
+        return -1;
+    } while (0);
+
+    while (options)
+    {
+        if (strcmp (options->name, "allow_duplicate_users") == 0)
+            auth->allow_duplicate_users = atoi ((char*)options->value);
+        options = options->next;
+    }
+    return 0;
+}
+
+
+auth_t *auth_get_authenticator (xmlNodePtr node)
+{
+    auth_t *auth = calloc (1, sizeof (auth_t));
+    config_options_t *options = NULL, **next_option = &options;
+    xmlNodePtr option;
+
+    if (auth == NULL)
+        return NULL;
+
+    option = node->xmlChildrenNode;
+    while (option)
+    {
+        xmlNodePtr current = option;
+        option = option->next;
+        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
+        {
+            config_options_t *opt = calloc (1, sizeof (config_options_t));
+            opt->name = (char *)xmlGetProp (current, XMLSTR("name"));
+            if (opt->name == NULL)
+            {
+                free(opt);
+                continue;
+            }
+            opt->value = (char *)xmlGetProp (current, XMLSTR("value"));
+            if (opt->value == NULL)
+            {
+                xmlFree (opt->name);
+                free (opt);
+                continue;
+            }
+            *next_option = opt;
+            next_option = &opt->next;
+        }
+        else
+            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
+                WARN1 ("unknown auth setting (%s)", current->name);
+    }
+    auth->type = (char*)xmlGetProp (node, XMLSTR("type"));
+    if (get_authenticator (auth, options) < 0)
+    {
+        xmlFree (auth->type);
+        free (auth);
+        auth = NULL;
+    }
+    else
+    {
+        auth->tailp = &auth->head;
+        thread_mutex_create (&auth->lock);
+        auth->refcount = 1;
+        auth->running = 1;
+        auth->thread = thread_create ("auth thread", auth_run_thread, auth, THREAD_ATTACHED);
+    }
+
+    while (options)
+    {
+        config_options_t *opt = options;
+        options = opt->next;
+        xmlFree (opt->name);
+        xmlFree (opt->value);
+        free (opt);
+    }
+    return auth;
+}
+
+
+/* called when the stream starts, so that authentication engine can do any
+ * cleanup/initialisation.
+ */
+void auth_stream_start (mount_proxy *mountinfo, const char *mount)
+{
+    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
+    {
+        auth_client *auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user)
+        {
+            auth_user->mount = strdup (mount);
+            auth_user->process = stream_start_callback;
+
+            queue_auth_client (auth_user, mountinfo);
+        }
+    }
+}
+
+
+/* Called when the stream ends so that the authentication engine can do
+ * any authentication cleanup
+ */
+void auth_stream_end (mount_proxy *mountinfo, const char *mount)
+{
+    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
+    {
+        auth_client *auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user)
+        {
+            auth_user->mount = strdup (mount);
+            auth_user->process = stream_end_callback;
+
+            queue_auth_client (auth_user, mountinfo);
+        }
+    }
+}
+
+
+/* these are called at server start and termination */
+
+void auth_initialise (void)
+{
+    thread_mutex_create (&auth_lock);
+}
+
+void auth_shutdown (void)
+{
+    thread_mutex_destroy (&auth_lock);
+    INFO0 ("Auth shutdown");
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/auth.h
===================================================================
--- icecast/trunk/icecast/src/auth.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,98 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __AUTH_H__
-#define __AUTH_H__
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-struct source_tag;
-struct auth_tag;
-
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include "cfgfile.h"
-#include "client.h"
-#include "thread/thread.h"
-
-typedef enum
-{
-    AUTH_UNDEFINED,
-    AUTH_OK,
-    AUTH_FAILED,
-    AUTH_FORBIDDEN,
-    AUTH_USERADDED,
-    AUTH_USEREXISTS,
-    AUTH_USERDELETED
-} auth_result;
-
-typedef struct auth_client_tag
-{
-    char        *mount;
-    client_t    *client;
-    void        (*process)(struct auth_client_tag *auth_user);
-    struct auth_client_tag *next;
-} auth_client;
-
-
-typedef struct auth_tag
-{
-    char *mount;
-
-    /* Authenticate using the given username and password */
-    auth_result (*authenticate)(auth_client *aclient);
-    auth_result (*release_client)(auth_client *auth_user);
-
-    /* callbacks to specific auth for notifying auth server on source
-     * startup or shutdown
-     */
-    void (*stream_start)(auth_client *auth_user);
-    void (*stream_end)(auth_client *auth_user);
-
-    void (*free)(struct auth_tag *self);
-    auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password);
-    auth_result (*deleteuser)(struct auth_tag *auth, const char *username);
-    auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode);
-
-    mutex_t lock;
-    int refcount;
-    int allow_duplicate_users;
-
-    void *state;
-    char *type;
-} auth_t;
-
-void add_client (const char *mount, client_t *client);
-int  release_client (client_t *client);
-
-void auth_initialise (void);
-void auth_shutdown (void);
-
-auth_t  *auth_get_authenticator (xmlNodePtr node);
-void    auth_release (auth_t *authenticator);
-
-/* call to send a url request when source starts */
-void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
-
-/* call to send a url request when source ends */
-void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
-
-/* called from auth thread, after the client has successfully authenticated
- * and requires adding to source or fserve. */
-int auth_postprocess_client (auth_client *auth_user);
-
-#endif
-
-

Copied: icecast/tags/icecast-2.3.2/src/auth.h (from rev 14114, icecast/trunk/icecast/src/auth.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,107 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+struct source_tag;
+struct auth_tag;
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "cfgfile.h"
+#include "client.h"
+#include "thread/thread.h"
+
+typedef enum
+{
+    AUTH_UNDEFINED,
+    AUTH_OK,
+    AUTH_FAILED,
+    AUTH_FORBIDDEN,
+    AUTH_USERADDED,
+    AUTH_USEREXISTS,
+    AUTH_USERDELETED
+} auth_result;
+
+typedef struct auth_client_tag
+{
+    char        *mount;
+    client_t    *client;
+    void        (*process)(struct auth_tag *auth, struct auth_client_tag *auth_user);
+    struct auth_client_tag *next;
+} auth_client;
+
+
+typedef struct auth_tag
+{
+    char *mount;
+
+    /* Authenticate using the given username and password */
+    auth_result (*authenticate)(auth_client *aclient);
+    auth_result (*release_listener)(auth_client *auth_user);
+
+    /* auth handler for source startup, no client passed as it may disappear */
+    void (*stream_start)(auth_client *auth_user);
+
+    /* auth handler for source exit, no client passed as it may disappear */
+    void (*stream_end)(auth_client *auth_user);
+
+    /* auth state-specific free call */
+    void (*free)(struct auth_tag *self);
+
+    auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password);
+    auth_result (*deleteuser)(struct auth_tag *auth, const char *username);
+    auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode);
+
+    mutex_t lock;
+    int running;
+    int refcount;
+    int allow_duplicate_users;
+
+    thread_type *thread;
+
+    /* per-auth queue for clients */
+    auth_client *head, **tailp;
+    int pending_count;
+
+    void *state;
+    char *type;
+} auth_t;
+
+void auth_add_listener (const char *mount, client_t *client);
+int  auth_release_listener (client_t *client);
+
+void auth_initialise (void);
+void auth_shutdown (void);
+
+auth_t  *auth_get_authenticator (xmlNodePtr node);
+void    auth_release (auth_t *authenticator);
+
+/* call to send a url request when source starts */
+void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
+
+/* call to send a url request when source ends */
+void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
+
+/* called from auth thread, after the client has successfully authenticated
+ * and requires adding to source or fserve. */
+int auth_postprocess_listener (auth_client *auth_user);
+
+#endif
+
+

Deleted: icecast/tags/icecast-2.3.2/src/auth_htpasswd.c
===================================================================
--- icecast/trunk/icecast/src/auth_htpasswd.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth_htpasswd.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,418 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/** 
- * Client authentication functions
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "auth.h"
-#include "source.h"
-#include "client.h"
-#include "cfgfile.h"
-#include "httpp/httpp.h"
-#include "md5.h"
-
-#include "logging.h"
-#define CATMODULE "auth_htpasswd"
-
-#ifdef WIN32
-#define snprintf _snprintf
-#endif
-
-static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
-static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
-static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode);
-static int _free_user (void *key);
-
-typedef struct
-{
-    char *name;
-    char *pass;
-} htpasswd_user;
-
-typedef struct {
-    char *filename;
-    rwlock_t file_rwlock;
-    avl_tree *users;
-    time_t mtime;
-} htpasswd_auth_state;
-
-static void htpasswd_clear(auth_t *self) {
-    htpasswd_auth_state *state = self->state;
-    free(state->filename);
-    if (state->users)
-        avl_tree_free (state->users, _free_user);
-    thread_rwlock_destroy(&state->file_rwlock);
-    free(state);
-}
-
-static int get_line(FILE *file, char *buf, int len)
-{
-    if(fgets(buf, len, file)) {
-        int len = strlen(buf);
-        if(len > 0 && buf[len-1] == '\n') {
-            buf[--len] = 0;
-            if(len > 0 && buf[len-1] == '\r')
-                buf[--len] = 0;
-        }
-        return 1;
-    }
-    return 0;
-}
-
-/* md5 hash */
-static char *get_hash(const char *data, int len)
-{
-    struct MD5Context context;
-    unsigned char digest[16];
-
-    MD5Init(&context);
-
-    MD5Update(&context, data, len);
-
-    MD5Final(digest, &context);
-
-    return util_bin_to_hex(digest, 16);
-}
-
-#define MAX_LINE_LEN 512
-
-
-static int compare_users (void *arg, void *a, void *b)
-{
-    htpasswd_user *user1 = (htpasswd_user *)a;
-    htpasswd_user *user2 = (htpasswd_user *)b;
-
-    return strcmp (user1->name, user2->name);
-}
-
-
-static int _free_user (void *key)
-{
-    htpasswd_user *user = (htpasswd_user *)key;
-
-    free (user->name); /* ->pass is part of same buffer */
-    free (user);
-    return 1;
-}
-
-
-static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
-{
-    FILE *passwdfile;
-    avl_tree *new_users;
-    int num = 0;
-    struct stat file_stat;
-    char *sep;
-    char line [MAX_LINE_LEN];
-
-    if (stat (htpasswd->filename, &file_stat) < 0)
-    {
-        WARN1 ("failed to check status of %s", htpasswd->filename);
-
-        /* Create a dummy users tree for things to use later */
-        thread_rwlock_wlock (&htpasswd->file_rwlock);
-        if(!htpasswd->users)
-            htpasswd->users = avl_tree_new(compare_users, NULL);
-        thread_rwlock_unlock (&htpasswd->file_rwlock);
-
-        return;
-    }
-
-    if (file_stat.st_mtime == htpasswd->mtime)
-    {
-        /* common case, no update to file */
-        return;
-    }
-    INFO1 ("re-reading htpasswd file \"%s\"", htpasswd->filename);
-    passwdfile = fopen (htpasswd->filename, "rb");
-    if (passwdfile == NULL)
-    {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                htpasswd->filename, strerror(errno));
-        return;
-    }
-    htpasswd->mtime = file_stat.st_mtime;
-
-    new_users = avl_tree_new (compare_users, NULL);
-
-    while (get_line(passwdfile, line, MAX_LINE_LEN))
-    {
-        int len;
-        htpasswd_user *entry;
-
-        num++;
-        if(!line[0] || line[0] == '#')
-            continue;
-
-        sep = strrchr (line, ':');
-        if (sep == NULL)
-        {
-            WARN2("No separator on line %d (%s)", num, htpasswd->filename);
-            continue;
-        }
-        entry = calloc (1, sizeof (htpasswd_user));
-        len = strlen (line) + 1;
-        entry->name = malloc (len);
-        *sep = 0;
-        memcpy (entry->name, line, len);
-        entry->pass = entry->name + (sep-line) + 1;
-        avl_insert (new_users, entry);
-    }
-    fclose (passwdfile);
-
-    thread_rwlock_wlock (&htpasswd->file_rwlock);
-    if (htpasswd->users)
-        avl_tree_free (htpasswd->users, _free_user);
-    htpasswd->users = new_users;
-    thread_rwlock_unlock (&htpasswd->file_rwlock);
-}
-
-
-static auth_result htpasswd_auth (auth_client *auth_user)
-{
-    auth_t *auth = auth_user->client->auth;
-    htpasswd_auth_state *htpasswd = auth->state;
-    client_t *client = auth_user->client;
-    htpasswd_user entry;
-    void *result;
-
-    if (client->username == NULL || client->password == NULL)
-        return AUTH_FAILED;
-
-    htpasswd_recheckfile (htpasswd);
-
-    thread_rwlock_rlock (&htpasswd->file_rwlock);
-    entry.name = client->username;
-    if (avl_get_by_key (htpasswd->users, &entry, &result) == 0)
-    {
-        htpasswd_user *found = result;
-        char *hashed_pw;
-
-        thread_rwlock_unlock (&htpasswd->file_rwlock);
-        hashed_pw = get_hash (client->password, strlen (client->password));
-        if (strcmp (found->pass, hashed_pw) == 0)
-        {
-            free (hashed_pw);
-            return AUTH_OK;
-        }
-        free (hashed_pw);
-        DEBUG0 ("incorrect password for client");
-        return AUTH_FAILED;
-    }
-    DEBUG1 ("no such username: %s", client->username);
-    thread_rwlock_unlock (&htpasswd->file_rwlock);
-    return AUTH_FAILED;
-}
-
-
-int  auth_get_htpasswd_auth (auth_t *authenticator, config_options_t *options)
-{
-    htpasswd_auth_state *state;
-
-    authenticator->authenticate = htpasswd_auth;
-    authenticator->free = htpasswd_clear;
-    authenticator->adduser = htpasswd_adduser;
-    authenticator->deleteuser = htpasswd_deleteuser;
-    authenticator->listuser = htpasswd_userlist;
-
-    state = calloc(1, sizeof(htpasswd_auth_state));
-
-    while(options) {
-        if(!strcmp(options->name, "filename"))
-            state->filename = strdup(options->value);
-        options = options->next;
-    }
-
-    if(!state->filename) {
-        free(state);
-        ERROR0("No filename given in options for authenticator.");
-        return -1;
-    }
-
-    authenticator->state = state;
-    DEBUG1("Configured htpasswd authentication using password file %s", 
-            state->filename);
-
-    thread_rwlock_create(&state->file_rwlock);
-    htpasswd_recheckfile (state);
-
-    return 0;
-}
-
-
-static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
-{
-    FILE *passwdfile;
-    char *hashed_password = NULL;
-    htpasswd_auth_state *state = auth->state;
-    htpasswd_user entry;
-    void *result;
-
-    htpasswd_recheckfile (state);
-
-    thread_rwlock_wlock (&state->file_rwlock);
-
-    entry.name = (char*)username;
-    if (avl_get_by_key (state->users, &entry, &result) == 0)
-    {
-        thread_rwlock_unlock (&state->file_rwlock);
-        return AUTH_USEREXISTS;
-    }
-
-    passwdfile = fopen(state->filename, "ab");
-
-    if (passwdfile == NULL)
-    {
-        thread_rwlock_unlock (&state->file_rwlock);
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
-    }
-
-    hashed_password = get_hash(password, strlen(password));
-    if (hashed_password) {
-        fprintf(passwdfile, "%s:%s\n", username, hashed_password);
-        free(hashed_password);
-    }
-
-    fclose(passwdfile);
-    thread_rwlock_unlock (&state->file_rwlock);
-
-    return AUTH_USERADDED;
-}
-
-
-static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
-{
-    FILE *passwdfile;
-    FILE *tmp_passwdfile;
-    htpasswd_auth_state *state;
-    char line[MAX_LINE_LEN];
-    char *sep;
-    char *tmpfile = NULL;
-    int tmpfile_len = 0;
-    struct stat file_info;
-
-    state = auth->state;
-    thread_rwlock_wlock (&state->file_rwlock);
-    passwdfile = fopen(state->filename, "rb");
-
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        thread_rwlock_unlock (&state->file_rwlock);
-        return AUTH_FAILED;
-    }
-    tmpfile_len = strlen(state->filename) + 6;
-    tmpfile = calloc(1, tmpfile_len);
-    snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
-    if (stat (tmpfile, &file_info) == 0)
-    {
-        WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile);
-        free (tmpfile);
-        fclose (passwdfile);
-        thread_rwlock_unlock (&state->file_rwlock);
-        return AUTH_FAILED;
-    }
-
-    tmp_passwdfile = fopen(tmpfile, "wb");
-
-    if(tmp_passwdfile == NULL) {
-        WARN2("Failed to open temporary authentication database \"%s\": %s", 
-                tmpfile, strerror(errno));
-        fclose(passwdfile);
-        free(tmpfile);
-        thread_rwlock_unlock (&state->file_rwlock);
-        return AUTH_FAILED;
-    }
-
-
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
-
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No separator in line");
-            continue;
-        }
-
-        *sep = 0;
-        if (strcmp(username, line)) {
-            /* We did not match on the user, so copy it to the temp file */
-            /* and put the : back in */
-            *sep = ':';
-            fprintf(tmp_passwdfile, "%s\n", line);
-        }
-    }
-
-    fclose(tmp_passwdfile);
-    fclose(passwdfile);
-
-    /* Now move the contents of the tmp file to the original */
-    /* Windows won't let us rename a file if the destination file
-       exists...so, lets remove the original first */
-    if (remove(state->filename) != 0) {
-        ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
-                tmpfile, state->filename, strerror(errno));
-    }
-    else {
-        if (rename(tmpfile, state->filename) != 0) {
-            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
-                    tmpfile, state->filename, strerror(errno));
-        }
-    }
-    free(tmpfile);
-    thread_rwlock_unlock (&state->file_rwlock);
-    htpasswd_recheckfile (state);
-
-    return AUTH_USERDELETED;
-}
-
-
-static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
-{
-    htpasswd_auth_state *state;
-    xmlNodePtr newnode;
-    avl_node *node;
-
-    state = auth->state;
-
-    htpasswd_recheckfile (state);
-
-    thread_rwlock_rlock (&state->file_rwlock);
-    node = avl_get_first (state->users);
-    while (node)
-    {
-        htpasswd_user *user = (htpasswd_user *)node->key;
-        newnode = xmlNewChild (srcnode, NULL, "User", NULL);
-        xmlNewChild(newnode, NULL, "username", user->name);
-        xmlNewChild(newnode, NULL, "password", user->pass);
-        node = avl_get_next (node);
-    }
-    thread_rwlock_unlock (&state->file_rwlock);
-
-    return AUTH_OK;
-}
-

Copied: icecast/tags/icecast-2.3.2/src/auth_htpasswd.c (from rev 14788, icecast/trunk/icecast/src/auth_htpasswd.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth_htpasswd.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth_htpasswd.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,409 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/** 
+ * Client authentication functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+#include "md5.h"
+
+#include "logging.h"
+#define CATMODULE "auth_htpasswd"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode);
+static int _free_user (void *key);
+
+typedef struct
+{
+    char *name;
+    char *pass;
+} htpasswd_user;
+
+typedef struct {
+    char *filename;
+    rwlock_t file_rwlock;
+    avl_tree *users;
+    time_t mtime;
+} htpasswd_auth_state;
+
+static void htpasswd_clear(auth_t *self) {
+    htpasswd_auth_state *state = self->state;
+    free(state->filename);
+    if (state->users)
+        avl_tree_free (state->users, _free_user);
+    thread_rwlock_destroy(&state->file_rwlock);
+    free(state);
+}
+
+
+/* md5 hash */
+static char *get_hash(const char *data, int len)
+{
+    struct MD5Context context;
+    unsigned char digest[16];
+
+    MD5Init(&context);
+
+    MD5Update(&context, (const unsigned char *)data, len);
+
+    MD5Final(digest, &context);
+
+    return util_bin_to_hex(digest, 16);
+}
+
+
+static int compare_users (void *arg, void *a, void *b)
+{
+    htpasswd_user *user1 = (htpasswd_user *)a;
+    htpasswd_user *user2 = (htpasswd_user *)b;
+
+    return strcmp (user1->name, user2->name);
+}
+
+
+static int _free_user (void *key)
+{
+    htpasswd_user *user = (htpasswd_user *)key;
+
+    free (user->name); /* ->pass is part of same buffer */
+    free (user);
+    return 1;
+}
+
+
+static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
+{
+    FILE *passwdfile;
+    avl_tree *new_users;
+    int num = 0;
+    struct stat file_stat;
+    char *sep;
+    char line [MAX_LINE_LEN];
+
+    if (stat (htpasswd->filename, &file_stat) < 0)
+    {
+        WARN1 ("failed to check status of %s", htpasswd->filename);
+
+        /* Create a dummy users tree for things to use later */
+        thread_rwlock_wlock (&htpasswd->file_rwlock);
+        if(!htpasswd->users)
+            htpasswd->users = avl_tree_new(compare_users, NULL);
+        thread_rwlock_unlock (&htpasswd->file_rwlock);
+
+        return;
+    }
+
+    if (file_stat.st_mtime == htpasswd->mtime)
+    {
+        /* common case, no update to file */
+        return;
+    }
+    INFO1 ("re-reading htpasswd file \"%s\"", htpasswd->filename);
+    passwdfile = fopen (htpasswd->filename, "rb");
+    if (passwdfile == NULL)
+    {
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                htpasswd->filename, strerror(errno));
+        return;
+    }
+    htpasswd->mtime = file_stat.st_mtime;
+
+    new_users = avl_tree_new (compare_users, NULL);
+
+    while (get_line(passwdfile, line, MAX_LINE_LEN))
+    {
+        int len;
+        htpasswd_user *entry;
+
+        num++;
+        if(!line[0] || line[0] == '#')
+            continue;
+
+        sep = strrchr (line, ':');
+        if (sep == NULL)
+        {
+            WARN2("No separator on line %d (%s)", num, htpasswd->filename);
+            continue;
+        }
+        entry = calloc (1, sizeof (htpasswd_user));
+        len = strlen (line) + 1;
+        entry->name = malloc (len);
+        *sep = 0;
+        memcpy (entry->name, line, len);
+        entry->pass = entry->name + (sep-line) + 1;
+        avl_insert (new_users, entry);
+    }
+    fclose (passwdfile);
+
+    thread_rwlock_wlock (&htpasswd->file_rwlock);
+    if (htpasswd->users)
+        avl_tree_free (htpasswd->users, _free_user);
+    htpasswd->users = new_users;
+    thread_rwlock_unlock (&htpasswd->file_rwlock);
+}
+
+
+static auth_result htpasswd_auth (auth_client *auth_user)
+{
+    auth_t *auth = auth_user->client->auth;
+    htpasswd_auth_state *htpasswd = auth->state;
+    client_t *client = auth_user->client;
+    htpasswd_user entry;
+    void *result;
+
+    if (client->username == NULL || client->password == NULL)
+        return AUTH_FAILED;
+
+    if (htpasswd->filename == NULL)
+    {
+        ERROR0("No filename given in options for authenticator.");
+        return AUTH_FAILED;
+    }
+    htpasswd_recheckfile (htpasswd);
+
+    thread_rwlock_rlock (&htpasswd->file_rwlock);
+    entry.name = client->username;
+    if (avl_get_by_key (htpasswd->users, &entry, &result) == 0)
+    {
+        htpasswd_user *found = result;
+        char *hashed_pw;
+
+        thread_rwlock_unlock (&htpasswd->file_rwlock);
+        hashed_pw = get_hash (client->password, strlen (client->password));
+        if (strcmp (found->pass, hashed_pw) == 0)
+        {
+            free (hashed_pw);
+            return AUTH_OK;
+        }
+        free (hashed_pw);
+        DEBUG0 ("incorrect password for client");
+        return AUTH_FAILED;
+    }
+    DEBUG1 ("no such username: %s", client->username);
+    thread_rwlock_unlock (&htpasswd->file_rwlock);
+    return AUTH_FAILED;
+}
+
+
+int  auth_get_htpasswd_auth (auth_t *authenticator, config_options_t *options)
+{
+    htpasswd_auth_state *state;
+
+    authenticator->authenticate = htpasswd_auth;
+    authenticator->free = htpasswd_clear;
+    authenticator->adduser = htpasswd_adduser;
+    authenticator->deleteuser = htpasswd_deleteuser;
+    authenticator->listuser = htpasswd_userlist;
+
+    state = calloc(1, sizeof(htpasswd_auth_state));
+
+    while(options) {
+        if(!strcmp(options->name, "filename"))
+        {
+            free (state->filename);
+            state->filename = strdup(options->value);
+        }
+        options = options->next;
+    }
+
+    if (state->filename)
+        INFO1("Configured htpasswd authentication using password file \"%s\"", 
+                state->filename);
+    else
+        ERROR0("No filename given in options for authenticator.");
+
+    authenticator->state = state;
+
+    thread_rwlock_create(&state->file_rwlock);
+    htpasswd_recheckfile (state);
+
+    return 0;
+}
+
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
+{
+    FILE *passwdfile;
+    char *hashed_password = NULL;
+    htpasswd_auth_state *state = auth->state;
+    htpasswd_user entry;
+    void *result;
+
+    htpasswd_recheckfile (state);
+
+    thread_rwlock_wlock (&state->file_rwlock);
+
+    entry.name = (char*)username;
+    if (avl_get_by_key (state->users, &entry, &result) == 0)
+    {
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_USEREXISTS;
+    }
+
+    passwdfile = fopen(state->filename, "ab");
+
+    if (passwdfile == NULL)
+    {
+        thread_rwlock_unlock (&state->file_rwlock);
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                state->filename, strerror(errno));
+        return AUTH_FAILED;
+    }
+
+    hashed_password = get_hash(password, strlen(password));
+    if (hashed_password) {
+        fprintf(passwdfile, "%s:%s\n", username, hashed_password);
+        free(hashed_password);
+    }
+
+    fclose(passwdfile);
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return AUTH_USERADDED;
+}
+
+
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
+{
+    FILE *passwdfile;
+    FILE *tmp_passwdfile;
+    htpasswd_auth_state *state;
+    char line[MAX_LINE_LEN];
+    char *sep;
+    char *tmpfile = NULL;
+    int tmpfile_len = 0;
+    struct stat file_info;
+
+    state = auth->state;
+    thread_rwlock_wlock (&state->file_rwlock);
+    passwdfile = fopen(state->filename, "rb");
+
+    if(passwdfile == NULL) {
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                state->filename, strerror(errno));
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+    tmpfile_len = strlen(state->filename) + 6;
+    tmpfile = calloc(1, tmpfile_len);
+    snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
+    if (stat (tmpfile, &file_info) == 0)
+    {
+        WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile);
+        free (tmpfile);
+        fclose (passwdfile);
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+
+    tmp_passwdfile = fopen(tmpfile, "wb");
+
+    if(tmp_passwdfile == NULL) {
+        WARN2("Failed to open temporary authentication database \"%s\": %s", 
+                tmpfile, strerror(errno));
+        fclose(passwdfile);
+        free(tmpfile);
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+
+
+    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
+        if(!line[0] || line[0] == '#')
+            continue;
+
+        sep = strchr(line, ':');
+        if(sep == NULL) {
+            DEBUG0("No separator in line");
+            continue;
+        }
+
+        *sep = 0;
+        if (strcmp(username, line)) {
+            /* We did not match on the user, so copy it to the temp file */
+            /* and put the : back in */
+            *sep = ':';
+            fprintf(tmp_passwdfile, "%s\n", line);
+        }
+    }
+
+    fclose(tmp_passwdfile);
+    fclose(passwdfile);
+
+    /* Now move the contents of the tmp file to the original */
+    /* Windows won't let us rename a file if the destination file
+       exists...so, lets remove the original first */
+    if (remove(state->filename) != 0) {
+        ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
+                tmpfile, state->filename, strerror(errno));
+    }
+    else {
+        if (rename(tmpfile, state->filename) != 0) {
+            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
+                    tmpfile, state->filename, strerror(errno));
+        }
+    }
+    free(tmpfile);
+    thread_rwlock_unlock (&state->file_rwlock);
+    htpasswd_recheckfile (state);
+
+    return AUTH_USERDELETED;
+}
+
+
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
+{
+    htpasswd_auth_state *state;
+    xmlNodePtr newnode;
+    avl_node *node;
+
+    state = auth->state;
+
+    htpasswd_recheckfile (state);
+
+    thread_rwlock_rlock (&state->file_rwlock);
+    node = avl_get_first (state->users);
+    while (node)
+    {
+        htpasswd_user *user = (htpasswd_user *)node->key;
+        newnode = xmlNewChild (srcnode, NULL, XMLSTR("User"), NULL);
+        xmlNewChild(newnode, NULL, XMLSTR("username"), XMLSTR(user->name));
+        xmlNewChild(newnode, NULL, XMLSTR("password"), XMLSTR(user->pass));
+        node = avl_get_next (node);
+    }
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return AUTH_OK;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/auth_htpasswd.h
===================================================================
--- icecast/trunk/icecast/src/auth_htpasswd.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth_htpasswd.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,24 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __AUTH_HTPASSWD_H__
-#define __AUTH_HTPASSWD_H__
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-void auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
-
-#endif
-
-

Copied: icecast/tags/icecast-2.3.2/src/auth_htpasswd.h (from rev 13595, icecast/trunk/icecast/src/auth_htpasswd.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth_htpasswd.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth_htpasswd.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,24 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_HTPASSWD_H__
+#define __AUTH_HTPASSWD_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
+
+#endif
+
+

Deleted: icecast/tags/icecast-2.3.2/src/auth_url.c
===================================================================
--- icecast/trunk/icecast/src/auth_url.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth_url.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,539 +0,0 @@
-/* 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 at xiph.org>, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* 
- * Client authentication via URL functions
- *
- * authenticate user via a URL, this is done via libcurl so https can also
- * be handled. The request will have POST information about the request in
- * the form of
- *
- * action=listener_add&client=1&server=host&port=8000&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&agent=""
- *
- * For a user to be accecpted the following HTTP header needs
- * to be returned (the actual string can be specified in the xml file)
- *
- * icecast-auth-user: 1
- *
- * A listening client may also be configured as only to stay connected for a
- * certain length of time. eg The auth server may only allow a 15 minute
- * playback by sending back.
- *
- * icecast-auth-timelimit: 900
- *
- * On client disconnection another request can be sent to a URL with the POST
- * information of
- *
- * action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600
- *
- * client refers to the icecast client identification number. mount refers
- * to the mountpoint (beginning with / and may contain query parameters eg ?&
- * encoded) and duration is the amount of time in seconds. user and pass
- * setting can be blank
- *
- * On stream start and end, another url can be issued to help clear any user
- * info stored at the auth server. Useful for abnormal outage/termination
- * cases.
- *
- * action=mount_add&mount=/live&server=myserver.com&port=8000
- * action=mount_remove&mount=/live&server=myserver.com&port=8000
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#ifndef _WIN32
-#include <sys/wait.h>
-#include <strings.h>
-#else
-#define snprintf _snprintf
-#define strncasecmp strnicmp
-#endif
-
-#include <curl/curl.h>
-
-#include "auth.h"
-#include "source.h"
-#include "client.h"
-#include "cfgfile.h"
-#include "httpp/httpp.h"
-
-#include "logging.h"
-#define CATMODULE "auth_url"
-
-typedef struct {
-    char *addurl;
-    char *removeurl;
-    char *stream_start;
-    char *stream_end;
-    char *username;
-    char *password;
-    char *auth_header;
-    int  auth_header_len;
-    char *timelimit_header;
-    int  timelimit_header_len;
-    char *userpwd;
-    CURL *handle;
-    char errormsg [CURL_ERROR_SIZE];
-} auth_url;
-
-
-static void auth_url_clear(auth_t *self)
-{
-    auth_url *url;
-
-    INFO0 ("Doing auth URL cleanup");
-    url = self->state;
-    curl_easy_cleanup (url->handle);
-    free (url->username);
-    free (url->password);
-    free (url->removeurl);
-    free (url->addurl);
-    free (url->stream_start);
-    free (url->stream_end);
-    free (url->auth_header);
-    free (url->timelimit_header);
-    free (url->userpwd);
-    free (url);
-}
-
-
-#ifdef CURLOPT_PASSWDFUNCTION
-/* make sure that prompting at the console does not occur */
-static int my_getpass(void *client, char *prompt, char *buffer, int buflen)
-{
-    buffer[0] = '\0';
-    return 0;
-}
-#endif
-
-
-static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
-{
-    auth_client *auth_user = stream;
-    unsigned bytes = size * nmemb;
-    client_t *client = auth_user->client;
-
-    if (client)
-    {
-        auth_t *auth = client->auth;
-        auth_url *url = auth->state;
-        if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0)
-            client->authenticated = 1;
-        if (strncasecmp (ptr, url->timelimit_header, url->timelimit_header_len) == 0)
-        {
-            unsigned int limit = 0;
-            sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit);
-            client->con->discon_time = time(NULL) + limit;
-        }
-        if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0)
-        {
-            char *eol;
-            snprintf (url->errormsg, sizeof (url->errormsg), "%s", (char*)ptr+22);
-            eol = strchr (url->errormsg, '\r');
-            if (eol == NULL)
-                eol = strchr (url->errormsg, '\n');
-            if (eol)
-                *eol = '\0';
-        }
-    }
-
-    return (int)bytes;
-}
-
-/* capture returned data, but don't do anything with it */
-static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
-{
-    return (int)(size*nmemb);
-}
-
-
-static auth_result url_remove_client (auth_client *auth_user)
-{
-    client_t *client = auth_user->client;
-    auth_t *auth = client->auth;
-    auth_url *url = auth->state;
-    time_t duration = time(NULL) - client->con->con_time;
-    char *username, *password, *mount, *server;
-    ice_config_t *config;
-    int port;
-    char *userpwd = NULL, post [4096];
-
-    if (url->removeurl == NULL)
-        return AUTH_OK;
-    config = config_get_config ();
-    server = util_url_escape (config->hostname);
-    port = config->port;
-    config_release_config ();
-
-    if (client->username)
-        username = util_url_escape (client->username);
-    else
-        username = strdup ("");
-
-    if (client->password)
-        password = util_url_escape (client->password);
-    else
-        password = strdup ("");
-
-    /* get the full uri (with query params if available) */
-    mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
-    if (mount == NULL)
-        mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
-    mount = util_url_escape (mount);
-
-    snprintf (post, sizeof (post),
-            "action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
-            "&user=%s&pass=%s&duration=%lu",
-            server, port, client->con->id, mount, username,
-            password, (long unsigned)duration);
-    free (server);
-    free (mount);
-    free (username);
-    free (password);
-
-    if (strchr (url->removeurl, '@') == NULL)
-    {
-        if (url->userpwd)
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
-        else
-        {
-            /* auth'd requests may not have a user/pass, but may use query args */
-            if (client->username && client->password)
-            {
-                int len = strlen (client->username) + strlen (client->password) + 2;
-                userpwd = malloc (len);
-                snprintf (userpwd, len, "%s:%s", client->username, client->password);
-                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
-            }
-            else
-                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-        }
-    }
-    else
-    {
-        /* url has user/pass but libcurl may need to clear any existing settings */
-        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    }
-    curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl);
-    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
-    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
-
-    if (curl_easy_perform (url->handle))
-        WARN2 ("auth to server %s failed with %s", url->removeurl, url->errormsg);
-
-    free (userpwd);
-
-    return AUTH_OK;
-}
-
-
-static auth_result url_add_client (auth_client *auth_user)
-{
-    client_t *client = auth_user->client;
-    auth_t *auth = client->auth;
-    auth_url *url = auth->state;
-    int res = 0, port;
-    char *agent, *user_agent, *username, *password;
-    char *mount, *ipaddr, *server;
-    ice_config_t *config;
-    char *userpwd = NULL, post [4096];
-
-    if (url->addurl == NULL)
-        return AUTH_OK;
-
-    config = config_get_config ();
-    server = util_url_escape (config->hostname);
-    port = config->port;
-    config_release_config ();
-    agent = httpp_getvar (client->parser, "user-agent");
-    if (agent == NULL)
-        agent = "-";
-    user_agent = util_url_escape (agent);
-    if (client->username)
-        username  = util_url_escape (client->username);
-    else
-        username = strdup ("");
-    if (client->password)
-        password  = util_url_escape (client->password);
-    else
-        password = strdup ("");
-
-    /* get the full uri (with query params if available) */
-    mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
-    if (mount == NULL)
-        mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
-    mount = util_url_escape (mount);
-    ipaddr = util_url_escape (client->con->ip);
-
-    snprintf (post, sizeof (post),
-            "action=listener_add&server=%s&port=%d&client=%lu&mount=%s"
-            "&user=%s&pass=%s&ip=%s&agent=%s",
-            server, port, client->con->id, mount, username,
-            password, ipaddr, user_agent);
-    free (server);
-    free (mount);
-    free (user_agent);
-    free (username);
-    free (password);
-    free (ipaddr);
-
-    if (strchr (url->addurl, '@') == NULL)
-    {
-        if (url->userpwd)
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
-        else
-        {
-            /* auth'd requests may not have a user/pass, but may use query args */
-            if (client->username && client->password)
-            {
-                int len = strlen (client->username) + strlen (client->password) + 2;
-                userpwd = malloc (len);
-                snprintf (userpwd, len, "%s:%s", client->username, client->password);
-                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
-            }
-            else
-                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-        }
-    }
-    else
-    {
-        /* url has user/pass but libcurl may need to clear any existing settings */
-        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    }
-    curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl);
-    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
-    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
-    url->errormsg[0] = '\0';
-
-    res = curl_easy_perform (url->handle);
-
-    free (userpwd);
-
-    if (res)
-    {
-        WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg);
-        return AUTH_FAILED;
-    }
-    /* we received a response, lets see what it is */
-    if (client->authenticated)
-        return AUTH_OK;
-    INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
-    return AUTH_FAILED;
-}
-
-
-/* called by auth thread when a source starts, there is no client_t in
- * this case
- */
-static void url_stream_start (auth_client *auth_user)
-{
-    char *mount, *server;
-    ice_config_t *config = config_get_config ();
-    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
-    auth_t *auth = mountinfo->auth;
-    auth_url *url = auth->state;
-    char *stream_start_url;
-    int port;
-    char post [4096];
-
-    if (url->stream_start == NULL)
-    {
-        config_release_config ();
-        return;
-    }
-    server = util_url_escape (config->hostname);
-    port = config->port;
-    stream_start_url = strdup (url->stream_start);
-    /* we don't want this auth disappearing from under us while
-     * the connection is in progress */
-    mountinfo->auth->refcount++;
-    config_release_config ();
-    mount = util_url_escape (auth_user->mount);
-
-    snprintf (post, sizeof (post),
-            "action=mount_add&mount=%s&server=%s&port=%d", mount, server, port);
-    free (server);
-    free (mount);
-
-    if (strchr (url->stream_start, '@') == NULL)
-    {
-        if (url->userpwd)
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
-        else
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    }
-    else
-        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    curl_easy_setopt (url->handle, CURLOPT_URL, stream_start_url);
-    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
-    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
-
-    if (curl_easy_perform (url->handle))
-        WARN2 ("auth to server %s failed with %s", stream_start_url, url->errormsg);
-
-    auth_release (auth);
-    free (stream_start_url);
-    return;
-}
-
-
-static void url_stream_end (auth_client *auth_user)
-{
-    char *mount, *server;
-    ice_config_t *config = config_get_config ();
-    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
-    auth_t *auth = mountinfo->auth;
-    auth_url *url = auth->state;
-    char *stream_end_url;
-    int port;
-    char post [4096];
-
-    if (url->stream_end == NULL)
-    {
-        config_release_config ();
-        return;
-    }
-    server = util_url_escape (config->hostname);
-    port = config->port;
-    stream_end_url = strdup (url->stream_end);
-    /* we don't want this auth disappearing from under us while
-     * the connection is in progress */
-    mountinfo->auth->refcount++;
-    config_release_config ();
-    mount = util_url_escape (auth_user->mount);
-
-    snprintf (post, sizeof (post),
-            "action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port);
-    free (server);
-    free (mount);
-
-    if (strchr (url->stream_end, '@') == NULL)
-    {
-        if (url->userpwd)
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
-        else
-            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    }
-    else
-        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
-    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
-    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
-    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
-
-    if (curl_easy_perform (url->handle))
-        WARN2 ("auth to server %s failed with %s", stream_end_url, url->errormsg);
-
-    auth_release (auth);
-    free (stream_end_url);
-    return;
-}
-
-
-static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
-{
-    return AUTH_FAILED;
-}
-
-static auth_result auth_url_deleteuser (auth_t *auth, const char *username)
-{
-    return AUTH_FAILED;
-}
-
-static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
-{
-    return AUTH_FAILED;
-}
-
-int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
-{
-    auth_url *url_info;
-
-    authenticator->authenticate = url_add_client;
-    authenticator->release_client = url_remove_client;
-
-    authenticator->free = auth_url_clear;
-    authenticator->adduser = auth_url_adduser;
-    authenticator->deleteuser = auth_url_deleteuser;
-    authenticator->listuser = auth_url_listuser;
-
-    authenticator->stream_start = url_stream_start;
-    authenticator->stream_end = url_stream_end;
-
-    url_info = calloc(1, sizeof(auth_url));
-    url_info->auth_header = strdup ("icecast-auth-user: 1\r\n");
-    url_info->timelimit_header = strdup ("icecast-auth-timelimit:");
-
-    while(options) {
-        if(!strcmp(options->name, "username"))
-            url_info->username = strdup (options->value);
-        if(!strcmp(options->name, "password"))
-            url_info->password = strdup (options->value);
-        if(!strcmp(options->name, "listener_add"))
-            url_info->addurl = strdup (options->value);
-        if(!strcmp(options->name, "listener_remove"))
-            url_info->removeurl = strdup (options->value);
-        if(!strcmp(options->name, "mount_add"))
-            url_info->stream_start = strdup (options->value);
-        if(!strcmp(options->name, "mount_remove"))
-            url_info->stream_end = strdup (options->value);
-        if(!strcmp(options->name, "auth_header"))
-        {
-            free (url_info->auth_header);
-            url_info->auth_header = strdup (options->value);
-        }
-        if (strcmp(options->name, "timelimit_header") == 0)
-        {
-            free (url_info->timelimit_header);
-            url_info->timelimit_header = strdup (options->value);
-        }
-        options = options->next;
-    }
-    url_info->handle = curl_easy_init ();
-    if (url_info->handle == NULL)
-    {
-        free (url_info);
-        return -1;
-    }
-    if (url_info->auth_header)
-        url_info->auth_header_len = strlen (url_info->auth_header);
-    if (url_info->timelimit_header)
-        url_info->timelimit_header_len = strlen (url_info->timelimit_header);
-
-    curl_easy_setopt (url_info->handle, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
-    curl_easy_setopt (url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
-    curl_easy_setopt (url_info->handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
-    curl_easy_setopt (url_info->handle, CURLOPT_WRITEDATA, url_info->handle);
-    curl_easy_setopt (url_info->handle, CURLOPT_NOSIGNAL, 1L);
-    curl_easy_setopt (url_info->handle, CURLOPT_TIMEOUT, 15L);
-#ifdef CURLOPT_PASSWDFUNCTION
-    curl_easy_setopt (url_info->handle, CURLOPT_PASSWDFUNCTION, my_getpass);
-#endif
-    curl_easy_setopt (url_info->handle, CURLOPT_ERRORBUFFER, &url_info->errormsg[0]);
-
-    if (url_info->username && url_info->password)
-    {
-        int len = strlen (url_info->username) + strlen (url_info->password) + 2;
-        url_info->userpwd = malloc (len);
-        snprintf (url_info->userpwd, len, "%s:%s", url_info->username, url_info->password);
-    }
-
-    authenticator->state = url_info;
-    INFO0("URL based authentication setup");
-    return 0;
-}
-

Copied: icecast/tags/icecast-2.3.2/src/auth_url.c (from rev 14115, icecast/trunk/icecast/src/auth_url.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth_url.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth_url.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,561 @@
+/* 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 at xiph.org>, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* 
+ * Client authentication via URL functions
+ *
+ * authenticate user via a URL, this is done via libcurl so https can also
+ * be handled. The request will have POST information about the request in
+ * the form of
+ *
+ * action=listener_add&client=1&server=host&port=8000&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&agent=""
+ *
+ * For a user to be accecpted the following HTTP header needs
+ * to be returned (the actual string can be specified in the xml file)
+ *
+ * icecast-auth-user: 1
+ *
+ * A listening client may also be configured as only to stay connected for a
+ * certain length of time. eg The auth server may only allow a 15 minute
+ * playback by sending back.
+ *
+ * icecast-auth-timelimit: 900
+ *
+ * On client disconnection another request can be sent to a URL with the POST
+ * information of
+ *
+ * action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600
+ *
+ * client refers to the icecast client identification number. mount refers
+ * to the mountpoint (beginning with / and may contain query parameters eg ?&
+ * encoded) and duration is the amount of time in seconds. user and pass
+ * setting can be blank
+ *
+ * On stream start and end, another url can be issued to help clear any user
+ * info stored at the auth server. Useful for abnormal outage/termination
+ * cases.
+ *
+ * action=mount_add&mount=/live&server=myserver.com&port=8000
+ * action=mount_remove&mount=/live&server=myserver.com&port=8000
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#ifndef _WIN32
+#include <sys/wait.h>
+#include <strings.h>
+#else
+#define snprintf _snprintf
+#define strncasecmp strnicmp
+#endif
+
+#include <curl/curl.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+#define CATMODULE "auth_url"
+
+typedef struct {
+    char *addurl;
+    char *removeurl;
+    char *stream_start;
+    char *stream_end;
+    char *username;
+    char *password;
+    char *auth_header;
+    int  auth_header_len;
+    char *timelimit_header;
+    int  timelimit_header_len;
+    char *userpwd;
+    CURL *handle;
+    char errormsg [CURL_ERROR_SIZE];
+} auth_url;
+
+
+static void auth_url_clear(auth_t *self)
+{
+    auth_url *url;
+
+    INFO0 ("Doing auth URL cleanup");
+    url = self->state;
+    self->state = NULL;
+    curl_easy_cleanup (url->handle);
+    free (url->username);
+    free (url->password);
+    free (url->removeurl);
+    free (url->addurl);
+    free (url->stream_start);
+    free (url->stream_end);
+    free (url->auth_header);
+    free (url->timelimit_header);
+    free (url->userpwd);
+    free (url);
+}
+
+
+#ifdef CURLOPT_PASSWDFUNCTION
+/* make sure that prompting at the console does not occur */
+static int my_getpass(void *client, char *prompt, char *buffer, int buflen)
+{
+    buffer[0] = '\0';
+    return 0;
+}
+#endif
+
+
+static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    auth_client *auth_user = stream;
+    unsigned bytes = size * nmemb;
+    client_t *client = auth_user->client;
+
+    if (client)
+    {
+        auth_t *auth = client->auth;
+        auth_url *url = auth->state;
+        if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0)
+            client->authenticated = 1;
+        if (strncasecmp (ptr, url->timelimit_header, url->timelimit_header_len) == 0)
+        {
+            unsigned int limit = 0;
+            sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit);
+            client->con->discon_time = time(NULL) + limit;
+        }
+        if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0)
+        {
+            char *eol;
+            snprintf (url->errormsg, sizeof (url->errormsg), "%s", (char*)ptr+22);
+            eol = strchr (url->errormsg, '\r');
+            if (eol == NULL)
+                eol = strchr (url->errormsg, '\n');
+            if (eol)
+                *eol = '\0';
+        }
+    }
+
+    return (int)bytes;
+}
+
+/* capture returned data, but don't do anything with it */
+static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    return (int)(size*nmemb);
+}
+
+
+static auth_result url_remove_listener (auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+    auth_t *auth = client->auth;
+    auth_url *url = auth->state;
+    time_t duration = time(NULL) - client->con->con_time;
+    char *username, *password, *mount, *server;
+    const char *mountreq;
+    ice_config_t *config;
+    int port;
+    char *userpwd = NULL, post [4096];
+
+    if (url->removeurl == NULL)
+        return AUTH_OK;
+    config = config_get_config ();
+    server = util_url_escape (config->hostname);
+    port = config->port;
+    config_release_config ();
+
+    if (client->username)
+        username = util_url_escape (client->username);
+    else
+        username = strdup ("");
+
+    if (client->password)
+        password = util_url_escape (client->password);
+    else
+        password = strdup ("");
+
+    /* get the full uri (with query params if available) */
+    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
+    if (mountreq == NULL)
+        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
+    mount = util_url_escape (mountreq);
+
+    snprintf (post, sizeof (post),
+            "action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
+            "&user=%s&pass=%s&duration=%lu",
+            server, port, client->con->id, mount, username,
+            password, (long unsigned)duration);
+    free (server);
+    free (mount);
+    free (username);
+    free (password);
+
+    if (strchr (url->removeurl, '@') == NULL)
+    {
+        if (url->userpwd)
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
+        else
+        {
+            /* auth'd requests may not have a user/pass, but may use query args */
+            if (client->username && client->password)
+            {
+                int len = strlen (client->username) + strlen (client->password) + 2;
+                userpwd = malloc (len);
+                snprintf (userpwd, len, "%s:%s", client->username, client->password);
+                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
+            }
+            else
+                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+        }
+    }
+    else
+    {
+        /* url has user/pass but libcurl may need to clear any existing settings */
+        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    }
+    curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl);
+    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
+
+    if (curl_easy_perform (url->handle))
+        WARN2 ("auth to server %s failed with %s", url->removeurl, url->errormsg);
+
+    free (userpwd);
+
+    return AUTH_OK;
+}
+
+
+static auth_result url_add_listener (auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+    auth_t *auth = client->auth;
+    auth_url *url = auth->state;
+    int res = 0, port;
+    const char *agent;
+    char *user_agent, *username, *password;
+    const char *mountreq;
+    char *mount, *ipaddr, *server;
+    ice_config_t *config;
+    char *userpwd = NULL, post [4096];
+
+    if (url->addurl == NULL)
+        return AUTH_OK;
+
+    config = config_get_config ();
+    server = util_url_escape (config->hostname);
+    port = config->port;
+    config_release_config ();
+    agent = httpp_getvar (client->parser, "user-agent");
+    if (agent == NULL)
+        agent = "-";
+    user_agent = util_url_escape (agent);
+    if (client->username)
+        username  = util_url_escape (client->username);
+    else
+        username = strdup ("");
+    if (client->password)
+        password  = util_url_escape (client->password);
+    else
+        password = strdup ("");
+
+    /* get the full uri (with query params if available) */
+    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
+    if (mountreq == NULL)
+        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
+    mount = util_url_escape (mountreq);
+    ipaddr = util_url_escape (client->con->ip);
+
+    snprintf (post, sizeof (post),
+            "action=listener_add&server=%s&port=%d&client=%lu&mount=%s"
+            "&user=%s&pass=%s&ip=%s&agent=%s",
+            server, port, client->con->id, mount, username,
+            password, ipaddr, user_agent);
+    free (server);
+    free (mount);
+    free (user_agent);
+    free (username);
+    free (password);
+    free (ipaddr);
+
+    if (strchr (url->addurl, '@') == NULL)
+    {
+        if (url->userpwd)
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
+        else
+        {
+            /* auth'd requests may not have a user/pass, but may use query args */
+            if (client->username && client->password)
+            {
+                int len = strlen (client->username) + strlen (client->password) + 2;
+                userpwd = malloc (len);
+                snprintf (userpwd, len, "%s:%s", client->username, client->password);
+                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
+            }
+            else
+                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+        }
+    }
+    else
+    {
+        /* url has user/pass but libcurl may need to clear any existing settings */
+        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    }
+    curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl);
+    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
+    url->errormsg[0] = '\0';
+
+    res = curl_easy_perform (url->handle);
+
+    free (userpwd);
+
+    if (res)
+    {
+        WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg);
+        return AUTH_FAILED;
+    }
+    /* we received a response, lets see what it is */
+    if (client->authenticated)
+        return AUTH_OK;
+    INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
+    return AUTH_FAILED;
+}
+
+
+/* called by auth thread when a source starts, there is no client_t in
+ * this case
+ */
+static void url_stream_start (auth_client *auth_user)
+{
+    char *mount, *server;
+    ice_config_t *config = config_get_config ();
+    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
+    auth_t *auth = mountinfo->auth;
+    auth_url *url = auth->state;
+    char *stream_start_url;
+    int port;
+    char post [4096];
+
+    if (url->stream_start == NULL)
+    {
+        config_release_config ();
+        return;
+    }
+    server = util_url_escape (config->hostname);
+    port = config->port;
+    stream_start_url = strdup (url->stream_start);
+    /* we don't want this auth disappearing from under us while
+     * the connection is in progress */
+    mountinfo->auth->refcount++;
+    config_release_config ();
+    mount = util_url_escape (auth_user->mount);
+
+    snprintf (post, sizeof (post),
+            "action=mount_add&mount=%s&server=%s&port=%d", mount, server, port);
+    free (server);
+    free (mount);
+
+    if (strchr (url->stream_start, '@') == NULL)
+    {
+        if (url->userpwd)
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
+        else
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    }
+    else
+        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    curl_easy_setopt (url->handle, CURLOPT_URL, stream_start_url);
+    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
+
+    if (curl_easy_perform (url->handle))
+        WARN2 ("auth to server %s failed with %s", stream_start_url, url->errormsg);
+
+    auth_release (auth);
+    free (stream_start_url);
+    return;
+}
+
+
+static void url_stream_end (auth_client *auth_user)
+{
+    char *mount, *server;
+    ice_config_t *config = config_get_config ();
+    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
+    auth_t *auth = mountinfo->auth;
+    auth_url *url = auth->state;
+    char *stream_end_url;
+    int port;
+    char post [4096];
+
+    if (url->stream_end == NULL)
+    {
+        config_release_config ();
+        return;
+    }
+    server = util_url_escape (config->hostname);
+    port = config->port;
+    stream_end_url = strdup (url->stream_end);
+    /* we don't want this auth disappearing from under us while
+     * the connection is in progress */
+    mountinfo->auth->refcount++;
+    config_release_config ();
+    mount = util_url_escape (auth_user->mount);
+
+    snprintf (post, sizeof (post),
+            "action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port);
+    free (server);
+    free (mount);
+
+    if (strchr (url->stream_end, '@') == NULL)
+    {
+        if (url->userpwd)
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
+        else
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    }
+    else
+        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
+    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
+
+    if (curl_easy_perform (url->handle))
+        WARN2 ("auth to server %s failed with %s", stream_end_url, url->errormsg);
+
+    auth_release (auth);
+    free (stream_end_url);
+    return;
+}
+
+
+static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_deleteuser (auth_t *auth, const char *username)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
+{
+    return AUTH_FAILED;
+}
+
+int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
+{
+    auth_url *url_info;
+
+    authenticator->free = auth_url_clear;
+    authenticator->adduser = auth_url_adduser;
+    authenticator->deleteuser = auth_url_deleteuser;
+    authenticator->listuser = auth_url_listuser;
+
+    url_info = calloc(1, sizeof(auth_url));
+    authenticator->state = url_info;
+
+    /* default headers */
+    url_info->auth_header = strdup ("icecast-auth-user: 1\r\n");
+    url_info->timelimit_header = strdup ("icecast-auth-timelimit:");
+
+    while(options) {
+        if(!strcmp(options->name, "username"))
+        {
+            free (url_info->username);
+            url_info->username = strdup (options->value);
+        }
+        if(!strcmp(options->name, "password"))
+        {
+            free (url_info->password);
+            url_info->password = strdup (options->value);
+        }
+        if(!strcmp(options->name, "listener_add"))
+        {
+            authenticator->authenticate = url_add_listener;
+            free (url_info->addurl);
+            url_info->addurl = strdup (options->value);
+        }
+        if(!strcmp(options->name, "listener_remove"))
+        {
+            authenticator->release_listener = url_remove_listener;
+            free (url_info->removeurl);
+            url_info->removeurl = strdup (options->value);
+        }
+        if(!strcmp(options->name, "mount_add"))
+        {
+            authenticator->stream_start = url_stream_start;
+            free (url_info->stream_start);
+            url_info->stream_start = strdup (options->value);
+        }
+        if(!strcmp(options->name, "mount_remove"))
+        {
+            authenticator->stream_end = url_stream_end;
+            free (url_info->stream_end);
+            url_info->stream_end = strdup (options->value);
+        }
+        if(!strcmp(options->name, "auth_header"))
+        {
+            free (url_info->auth_header);
+            url_info->auth_header = strdup (options->value);
+        }
+        if (strcmp(options->name, "timelimit_header") == 0)
+        {
+            free (url_info->timelimit_header);
+            url_info->timelimit_header = strdup (options->value);
+        }
+        options = options->next;
+    }
+    url_info->handle = curl_easy_init ();
+    if (url_info->handle == NULL)
+    {
+        auth_url_clear (authenticator);
+        return -1;
+    }
+    if (url_info->auth_header)
+        url_info->auth_header_len = strlen (url_info->auth_header);
+    if (url_info->timelimit_header)
+        url_info->timelimit_header_len = strlen (url_info->timelimit_header);
+
+    curl_easy_setopt (url_info->handle, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
+    curl_easy_setopt (url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
+    curl_easy_setopt (url_info->handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
+    curl_easy_setopt (url_info->handle, CURLOPT_WRITEDATA, url_info->handle);
+    curl_easy_setopt (url_info->handle, CURLOPT_NOSIGNAL, 1L);
+    curl_easy_setopt (url_info->handle, CURLOPT_TIMEOUT, 15L);
+#ifdef CURLOPT_PASSWDFUNCTION
+    curl_easy_setopt (url_info->handle, CURLOPT_PASSWDFUNCTION, my_getpass);
+#endif
+    curl_easy_setopt (url_info->handle, CURLOPT_ERRORBUFFER, &url_info->errormsg[0]);
+
+    if (url_info->username && url_info->password)
+    {
+        int len = strlen (url_info->username) + strlen (url_info->password) + 2;
+        url_info->userpwd = malloc (len);
+        snprintf (url_info->userpwd, len, "%s:%s", url_info->username, url_info->password);
+    }
+
+    INFO0("URL based authentication setup");
+    return 0;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/auth_url.h
===================================================================
--- icecast/trunk/icecast/src/auth_url.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/auth_url.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,24 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __AUTH_URL_H__
-#define __AUTH_URL_H__
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-int *auth_get_url_auth (auth_t *authenticator, config_options_t *options);
-
-#endif
-
-

Copied: icecast/tags/icecast-2.3.2/src/auth_url.h (from rev 14042, icecast/trunk/icecast/src/auth_url.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/auth_url.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/auth_url.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,24 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_URL_H__
+#define __AUTH_URL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int auth_get_url_auth (auth_t *authenticator, config_options_t *options);
+
+#endif
+
+

Deleted: icecast/tags/icecast-2.3.2/src/cfgfile.c
===================================================================
--- icecast/trunk/icecast/src/cfgfile.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/cfgfile.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1062 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-
-#include "thread/thread.h"
-#include "cfgfile.h"
-#include "refbuf.h"
-#include "client.h"
-#include "logging.h" 
-
-#define CATMODULE "CONFIG"
-#define CONFIG_DEFAULT_LOCATION "Earth"
-#define CONFIG_DEFAULT_ADMIN "icemaster at localhost"
-#define CONFIG_DEFAULT_CLIENT_LIMIT 256
-#define CONFIG_DEFAULT_SOURCE_LIMIT 16
-#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
-#define CONFIG_DEFAULT_BURST_SIZE (64*1024)
-#define CONFIG_DEFAULT_THREADPOOL_SIZE 4
-#define CONFIG_DEFAULT_CLIENT_TIMEOUT 30
-#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
-#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
-#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
-#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
-#define CONFIG_DEFAULT_MASTER_USERNAME "relay"
-#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
-#define CONFIG_DEFAULT_ICE_LOGIN 0
-#define CONFIG_DEFAULT_FILESERVE 1
-#define CONFIG_DEFAULT_TOUCH_FREQ 5
-#define CONFIG_DEFAULT_HOSTNAME "localhost"
-#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
-#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
-#define CONFIG_DEFAULT_ERROR_LOG "error.log"
-#define CONFIG_DEFAULT_LOG_LEVEL 4
-#define CONFIG_DEFAULT_CHROOT 0
-#define CONFIG_DEFAULT_CHUID 0
-#define CONFIG_DEFAULT_USER NULL
-#define CONFIG_DEFAULT_GROUP NULL
-#define CONFIG_MASTER_UPDATE_INTERVAL 120
-#define CONFIG_YP_URL_TIMEOUT 10
-
-#ifndef _WIN32
-#define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast"
-#define CONFIG_DEFAULT_LOG_DIR "/usr/local/icecast/logs"
-#define CONFIG_DEFAULT_WEBROOT_DIR "/usr/local/icecast/webroot"
-#define CONFIG_DEFAULT_ADMINROOT_DIR "/usr/local/icecast/admin"
-#else
-#define CONFIG_DEFAULT_BASE_DIR ".\\"
-#define CONFIG_DEFAULT_LOG_DIR ".\\logs"
-#define CONFIG_DEFAULT_WEBROOT_DIR ".\\webroot"
-#define CONFIG_DEFAULT_ADMINROOT_DIR ".\\admin"
-#endif
-
-static ice_config_t _current_configuration;
-static ice_config_locks _locks;
-
-static void _set_defaults(ice_config_t *c);
-static void _parse_root(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_directory(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_logging(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_security(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *c);
-static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *c);
-static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
-
-static void create_locks(void) {
-    thread_mutex_create(&_locks.relay_lock);
-    thread_rwlock_create(&_locks.config_lock);
-}
-
-static void release_locks(void) {
-    thread_mutex_destroy(&_locks.relay_lock);
-    thread_rwlock_destroy(&_locks.config_lock);
-}
-
-void config_initialize(void) {
-    create_locks();
-}
-
-void config_shutdown(void) {
-    config_get_config();
-    config_clear(&_current_configuration);
-    config_release_config();
-    release_locks();
-}
-
-void config_init_configuration(ice_config_t *configuration)
-{
-    memset(configuration, 0, sizeof(ice_config_t));
-    _set_defaults(configuration);
-}
-
-static void config_clear_mount (mount_proxy *mount)
-{
-    config_options_t *option;
-
-    xmlFree (mount->mountname);
-    xmlFree (mount->username);
-    xmlFree (mount->password);
-    xmlFree (mount->dumpfile);
-    xmlFree (mount->intro_filename);
-    xmlFree (mount->on_connect);
-    xmlFree (mount->on_disconnect);
-    xmlFree (mount->fallback_mount);
-    xmlFree (mount->stream_name);
-    xmlFree (mount->stream_description);
-    xmlFree (mount->stream_url);
-    xmlFree (mount->stream_genre);
-    xmlFree (mount->bitrate);
-    xmlFree (mount->type);
-    xmlFree (mount->cluster_password);
-
-    xmlFree (mount->auth_type);
-    option = mount->auth_options;
-    while (option)
-    {
-        config_options_t *nextopt = option->next;
-        xmlFree (option->name);
-        xmlFree (option->value);
-        free (option);
-        option = nextopt;
-    }
-    auth_release (mount->auth);
-    free (mount);
-}
-
-
-void config_clear(ice_config_t *c)
-{
-    ice_config_dir_t *dirnode, *nextdirnode;
-    relay_server *relay, *nextrelay;
-    mount_proxy *mount, *nextmount;
-    aliases *alias, *nextalias;
-    int i;
-
-    if (c->config_filename)
-        free(c->config_filename);
-
-    if (c->location && c->location != CONFIG_DEFAULT_LOCATION) 
-        xmlFree(c->location);
-    if (c->admin && c->admin != CONFIG_DEFAULT_ADMIN) 
-        xmlFree(c->admin);
-    if (c->source_password && c->source_password != CONFIG_DEFAULT_SOURCE_PASSWORD)
-        xmlFree(c->source_password);
-    if (c->admin_username)
-        xmlFree(c->admin_username);
-    if (c->admin_password)
-        xmlFree(c->admin_password);
-    if (c->relay_username)
-        xmlFree(c->relay_username);
-    if (c->relay_password)
-        xmlFree(c->relay_password);
-    if (c->hostname && c->hostname != CONFIG_DEFAULT_HOSTNAME) 
-        xmlFree(c->hostname);
-    if (c->base_dir && c->base_dir != CONFIG_DEFAULT_BASE_DIR) 
-        xmlFree(c->base_dir);
-    if (c->log_dir && c->log_dir != CONFIG_DEFAULT_LOG_DIR) 
-        xmlFree(c->log_dir);
-    if (c->webroot_dir && c->webroot_dir != CONFIG_DEFAULT_WEBROOT_DIR)
-        xmlFree(c->webroot_dir);
-    if (c->adminroot_dir && c->adminroot_dir != CONFIG_DEFAULT_ADMINROOT_DIR)
-        xmlFree(c->adminroot_dir);
-    if (c->pidfile)
-        xmlFree(c->pidfile);
-    if (c->playlist_log && c->playlist_log != CONFIG_DEFAULT_PLAYLIST_LOG) 
-        xmlFree(c->playlist_log);
-    if (c->access_log && c->access_log != CONFIG_DEFAULT_ACCESS_LOG) 
-        xmlFree(c->access_log);
-    if (c->error_log && c->error_log != CONFIG_DEFAULT_ERROR_LOG) 
-        xmlFree(c->error_log);
-    if (c->shoutcast_mount && c->shoutcast_mount != CONFIG_DEFAULT_SHOUTCAST_MOUNT)
-        xmlFree(c->shoutcast_mount);
-    for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
-        if (c->listeners[i].bind_address) xmlFree(c->listeners[i].bind_address);
-    }
-    if (c->master_server) xmlFree(c->master_server);
-    if (c->master_username) xmlFree(c->master_username);
-    if (c->master_password) xmlFree(c->master_password);
-    if (c->user) xmlFree(c->user);
-    if (c->group) xmlFree(c->group);
-
-    thread_mutex_lock(&(_locks.relay_lock));
-    relay = c->relay;
-    while(relay) {
-        nextrelay = relay->next;
-        xmlFree(relay->server);
-        xmlFree(relay->mount);
-        xmlFree(relay->localmount);
-        free(relay);
-        relay = nextrelay;
-    }
-    thread_mutex_unlock(&(_locks.relay_lock));
-
-    mount = c->mounts;
-    while(mount) {
-        nextmount = mount->next;
-        config_clear_mount (mount);
-        mount = nextmount;
-    }
-
-    alias = c->aliases;
-    while(alias) {
-        nextalias = alias->next;
-        xmlFree(alias->source);
-        xmlFree(alias->destination);
-        xmlFree(alias->bind_address);
-        free(alias);
-        alias = nextalias;
-    }
-
-    dirnode = c->dir_list;
-    while(dirnode) {
-        nextdirnode = dirnode->next;
-        xmlFree(dirnode->host);
-        free(dirnode);
-        dirnode = nextdirnode;
-    }
-#ifdef USE_YP
-    i = 0;
-    while (i < c->num_yp_directories)
-    {
-        xmlFree (c->yp_url[i]);
-        i++;
-    }
-#endif
-
-    memset(c, 0, sizeof(ice_config_t));
-}
-
-int config_initial_parse_file(const char *filename)
-{
-    /* Since we're already pointing at it, we don't need to copy it in place */
-    return config_parse_file(filename, &_current_configuration);
-}
-
-int config_parse_file(const char *filename, ice_config_t *configuration)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node;
-
-    if (filename == NULL || strcmp(filename, "") == 0) return CONFIG_EINSANE;
-    
-    xmlInitParser();
-    doc = xmlParseFile(filename);
-    if (doc == NULL) {
-        return CONFIG_EPARSE;
-    }
-
-    node = xmlDocGetRootElement(doc);
-    if (node == NULL) {
-        xmlFreeDoc(doc);
-        xmlCleanupParser();
-        return CONFIG_ENOROOT;
-    }
-
-    if (strcmp(node->name, "icecast") != 0) {
-        xmlFreeDoc(doc);
-        xmlCleanupParser();
-        return CONFIG_EBADROOT;
-    }
-
-    config_init_configuration(configuration);
-
-    configuration->config_filename = (char *)strdup(filename);
-
-    _parse_root(doc, node->xmlChildrenNode, configuration);
-
-    xmlFreeDoc(doc);
-
-    return 0;
-}
-
-int config_parse_cmdline(int arg, char **argv)
-{
-    return 0;
-}
-
-ice_config_locks *config_locks(void)
-{
-    return &_locks;
-}
-
-void config_release_config(void)
-{
-    thread_rwlock_unlock(&(_locks.config_lock));
-}
-
-ice_config_t *config_get_config(void)
-{
-    thread_rwlock_rlock(&(_locks.config_lock));
-    return &_current_configuration;
-}
-
-ice_config_t *config_grab_config(void)
-{
-    thread_rwlock_wlock(&(_locks.config_lock));
-    return &_current_configuration;
-}
-
-/* MUST be called with the lock held! */
-void config_set_config(ice_config_t *config) {
-    memcpy(&_current_configuration, config, sizeof(ice_config_t));
-}
-
-ice_config_t *config_get_config_unlocked(void)
-{
-    return &_current_configuration;
-}
-
-static void _set_defaults(ice_config_t *configuration)
-{
-    configuration->location = CONFIG_DEFAULT_LOCATION;
-    configuration->admin = CONFIG_DEFAULT_ADMIN;
-    configuration->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
-    configuration->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
-    configuration->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
-    configuration->threadpool_size = CONFIG_DEFAULT_THREADPOOL_SIZE;
-    configuration->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
-    configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
-    configuration->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
-    configuration->source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
-    configuration->shoutcast_mount = CONFIG_DEFAULT_SHOUTCAST_MOUNT;
-    configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
-    configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
-    configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
-    configuration->on_demand = 0;
-    configuration->dir_list = NULL;
-    configuration->hostname = CONFIG_DEFAULT_HOSTNAME;
-    configuration->port = 0;
-    configuration->listeners[0].port = 0;
-    configuration->listeners[0].bind_address = NULL;
-    configuration->listeners[0].shoutcast_compat = 0;
-    configuration->master_server = NULL;
-    configuration->master_server_port = 0;
-    configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
-    configuration->master_username = xmlStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
-    configuration->master_password = NULL;
-    configuration->base_dir = CONFIG_DEFAULT_BASE_DIR;
-    configuration->log_dir = CONFIG_DEFAULT_LOG_DIR;
-    configuration->webroot_dir = CONFIG_DEFAULT_WEBROOT_DIR;
-    configuration->adminroot_dir = CONFIG_DEFAULT_ADMINROOT_DIR;
-    configuration->playlist_log = CONFIG_DEFAULT_PLAYLIST_LOG;
-    configuration->access_log = CONFIG_DEFAULT_ACCESS_LOG;
-    configuration->error_log = CONFIG_DEFAULT_ERROR_LOG;
-    configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
-    configuration->chroot = CONFIG_DEFAULT_CHROOT;
-    configuration->chuid = CONFIG_DEFAULT_CHUID;
-    configuration->user = CONFIG_DEFAULT_USER;
-    configuration->group = CONFIG_DEFAULT_GROUP;
-    configuration->num_yp_directories = 0;
-    configuration->relay_username = xmlStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
-    configuration->relay_password = NULL;
-    /* default to a typical prebuffer size used by clients */
-    configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE;
-}
-
-static void _parse_root(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *configuration)
-{
-    char *tmp;
-
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "location") == 0) {
-            if (configuration->location && configuration->location != CONFIG_DEFAULT_LOCATION) xmlFree(configuration->location);
-            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "admin") == 0) {
-            if (configuration->admin && configuration->admin != CONFIG_DEFAULT_ADMIN) xmlFree(configuration->admin);
-            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if(strcmp(node->name, "authentication") == 0) {
-            _parse_authentication(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "source-password") == 0) {
-            /* TODO: This is the backwards-compatibility location */
-            char *mount, *pass;
-            if ((mount = (char *)xmlGetProp(node, "mount")) != NULL) {
-                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-                /* FIXME: This is a placeholder for per-mount passwords */
-            }
-            else {
-                if (configuration->source_password && configuration->source_password != CONFIG_DEFAULT_SOURCE_PASSWORD) xmlFree(configuration->source_password);
-                configuration->source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            }
-        } else if (strcmp(node->name, "icelogin") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->ice_login = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "fileserve") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->fileserve = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "relays-on-demand") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->on_demand = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "hostname") == 0) {
-            if (configuration->hostname && configuration->hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(configuration->hostname);
-            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "listen-socket") == 0) {
-            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "port") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->port = atoi(tmp);
-            configuration->listeners[0].port = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "bind-address") == 0) {
-            if (configuration->listeners[0].bind_address) 
-                xmlFree(configuration->listeners[0].bind_address);
-            configuration->listeners[0].bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "master-server") == 0) {
-            if (configuration->master_server) xmlFree(configuration->master_server);
-            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "master-username") == 0) {
-            if (configuration->master_username) xmlFree(configuration->master_username);
-            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "master-password") == 0) {
-            if (configuration->master_password) xmlFree(configuration->master_password);
-            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "master-server-port") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->master_server_port = atoi(tmp);
-            xmlFree (tmp);
-        } else if (strcmp(node->name, "master-update-interval") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->master_update_interval = atoi(tmp);
-            xmlFree (tmp);
-        } else if (strcmp(node->name, "shoutcast-mount") == 0) {
-            if (configuration->shoutcast_mount &&
-                    configuration->shoutcast_mount != CONFIG_DEFAULT_SHOUTCAST_MOUNT)
-                xmlFree(configuration->shoutcast_mount);
-            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "limits") == 0) {
-            _parse_limits(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "relay") == 0) {
-            _parse_relay(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "mount") == 0) {
-            _parse_mount(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "directory") == 0) {
-            _parse_directory(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "paths") == 0) {
-            _parse_paths(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "logging") == 0) {
-            _parse_logging(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "security") == 0) {
-            _parse_security(doc, node->xmlChildrenNode, configuration);
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *configuration)
-{
-    char *tmp;
-
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "clients") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->client_limit = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "sources") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->source_limit = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "queue-size") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->queue_size_limit = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "threadpool") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->threadpool_size = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "client-timeout") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->client_timeout = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "header-timeout") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->header_timeout = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "source-timeout") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->source_timeout = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "burst-on-connect") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            if (atoi(tmp) == 0)
-                configuration->burst_size = 0;
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "burst-size") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->burst_size = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *configuration)
-{
-    char *tmp;
-    mount_proxy *mount = calloc(1, sizeof(mount_proxy));
-    mount_proxy *current = configuration->mounts;
-    mount_proxy *last=NULL;
-    
-    /* default <mount> settings */
-    mount->max_listeners = -1;
-    mount->burst_size = -1;
-    mount->mp3_meta_interval = -1;
-    mount->yp_public = -1;
-    mount->next = NULL;
-
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "mount-name") == 0) {
-            mount->mountname = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "username") == 0) {
-            mount->username = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "password") == 0) {
-            mount->password = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "dump-file") == 0) {
-            mount->dumpfile = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "intro") == 0) {
-            mount->intro_filename = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "fallback-mount") == 0) {
-            mount->fallback_mount = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "fallback-when-full") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->fallback_when_full = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "max-listeners") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->max_listeners = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "mp3-metadata-interval") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->mp3_meta_interval = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "fallback-override") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->fallback_override = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "no-mount") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->no_mount = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "no-yp") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->yp_public = atoi(tmp) == 0 ? -1 : 0;
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "hidden") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->hidden = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "authentication") == 0) {
-            mount->auth = auth_get_authenticator (node);
-        }
-        else if (strcmp(node->name, "on-connect") == 0) {
-            mount->on_connect = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "on-disconnect") == 0) {
-            mount->on_disconnect = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "max-listener-duration") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->max_listener_duration = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "queue-size") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->queue_size_limit = atoi (tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "source-timeout") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            if (tmp)
-            {
-                mount->source_timeout = atoi (tmp);
-                xmlFree(tmp);
-            }
-        } else if (strcmp(node->name, "burst-size") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->burst_size = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "cluster-password") == 0) {
-            mount->cluster_password = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "stream-name") == 0) {
-            mount->stream_name = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "stream-description") == 0) {
-            mount->stream_description = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "stream-url") == 0) {
-            mount->stream_url = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "genre") == 0) {
-            mount->stream_genre = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "bitrate") == 0) {
-            mount->bitrate = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "public") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            mount->yp_public = atoi (tmp);
-            if(tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "type") == 0) {
-            mount->type = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "subtype") == 0) {
-            mount->subtype = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-    } while ((node = node->next));
-
-    /* make sure we have at least the mountpoint name */
-    if (mount->mountname == NULL)
-    {
-        config_clear_mount (mount);
-        return;
-    }
-    while(current) {
-        last = current;
-        current = current->next;
-    }
-
-    if(last)
-        last->next = mount;
-    else
-        configuration->mounts = mount;
-}
-
-
-static void _parse_relay(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    char *tmp;
-    relay_server *relay = calloc(1, sizeof(relay_server));
-    relay_server *current = configuration->relay;
-    relay_server *last=NULL;
-
-    while(current) {
-        last = current;
-        current = current->next;
-    }
-
-    if(last)
-        last->next = relay;
-    else
-        configuration->relay = relay;
-
-    relay->next = NULL;
-    relay->mp3metadata = 1;
-    relay->on_demand = configuration->on_demand;
-
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "server") == 0) {
-            relay->server = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "port") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            relay->port = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "mount") == 0) {
-            relay->mount = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "local-mount") == 0) {
-            relay->localmount = (char *)xmlNodeListGetString(
-                    doc, node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "relay-shoutcast-metadata") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            relay->mp3metadata = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "username") == 0) {
-            relay->username = (char *)xmlNodeListGetString(doc,
-                    node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "password") == 0) {
-            relay->password = (char *)xmlNodeListGetString(doc,
-                    node->xmlChildrenNode, 1);
-        }
-        else if (strcmp(node->name, "on-demand") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            relay->on_demand = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        }
-    } while ((node = node->next));
-    if (relay->localmount == NULL)
-        relay->localmount = xmlStrdup (relay->mount);
-}
-
-static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    listener_t *listener = NULL;
-    int i;
-    char *tmp;
-
-    for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
-        if(configuration->listeners[i].port <= 0) {
-            listener = &(configuration->listeners[i]);
-            break;
-        }
-    }
-
-    if (listener == NULL)
-        return;
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "port") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            if(configuration->port == 0)
-                configuration->port = atoi(tmp);
-            listener->port = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "shoutcast-compat") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            listener->shoutcast_compat = atoi(tmp);
-            if(tmp) xmlFree(tmp);
-        }
-        else if (strcmp(node->name, "bind-address") == 0) {
-            listener->bind_address = (char *)xmlNodeListGetString(doc, 
-                    node->xmlChildrenNode, 1);
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "source-password") == 0) {
-            char *mount, *pass;
-            if ((mount = (char *)xmlGetProp(node, "mount")) != NULL) {
-                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-                /* FIXME: This is a placeholder for per-mount passwords */
-            }
-            else {
-                if (configuration->source_password && 
-                        configuration->source_password != 
-                        CONFIG_DEFAULT_SOURCE_PASSWORD) 
-                    xmlFree(configuration->source_password);
-                configuration->source_password = 
-                    (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            }
-        } else if (strcmp(node->name, "admin-password") == 0) {
-            if(configuration->admin_password)
-                xmlFree(configuration->admin_password);
-            configuration->admin_password =
-                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "admin-user") == 0) {
-            if(configuration->admin_username)
-                xmlFree(configuration->admin_username);
-            configuration->admin_username =
-                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "relay-password") == 0) {
-            if(configuration->relay_password)
-                xmlFree(configuration->relay_password);
-            configuration->relay_password =
-                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "relay-user") == 0) {
-            if(configuration->relay_username)
-                xmlFree(configuration->relay_username);
-            configuration->relay_username =
-                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_directory(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    char *tmp;
-
-    if (configuration->num_yp_directories >= MAX_YP_DIRECTORIES) {
-        ERROR0("Maximum number of yp directories exceeded!");
-        return;
-    }
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "yp-url") == 0) {
-            if (configuration->yp_url[configuration->num_yp_directories]) 
-                xmlFree(configuration->yp_url[configuration->num_yp_directories]);
-            configuration->yp_url[configuration->num_yp_directories] = 
-                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "yp-url-timeout") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->yp_url_timeout[configuration->num_yp_directories] = 
-                atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "server") == 0) {
-            _add_server(doc, node->xmlChildrenNode, configuration);
-        } else if (strcmp(node->name, "touch-interval") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->yp_touch_interval[configuration->num_yp_directories] =
-                atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        }
-    } while ((node = node->next));
-    configuration->num_yp_directories++;
-}
-
-static void _parse_paths(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    char *temp;
-    aliases *alias, *current, *last;
-
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "basedir") == 0) {
-            if (configuration->base_dir && configuration->base_dir != CONFIG_DEFAULT_BASE_DIR) xmlFree(configuration->base_dir);
-            configuration->base_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "logdir") == 0) {
-            if (configuration->log_dir && configuration->log_dir != CONFIG_DEFAULT_LOG_DIR) xmlFree(configuration->log_dir);
-            configuration->log_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "pidfile") == 0) {
-            if (configuration->pidfile) xmlFree(configuration->pidfile);
-            configuration->pidfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "webroot") == 0) {
-            if (configuration->webroot_dir && configuration->webroot_dir != CONFIG_DEFAULT_WEBROOT_DIR) xmlFree(configuration->webroot_dir);
-            configuration->webroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            if(configuration->webroot_dir[strlen(configuration->webroot_dir)-1] == '/')
-                configuration->webroot_dir[strlen(configuration->webroot_dir)-1] = 0;
-        } else if (strcmp(node->name, "adminroot") == 0) {
-            if (configuration->adminroot_dir && configuration->adminroot_dir != CONFIG_DEFAULT_ADMINROOT_DIR) 
-                xmlFree(configuration->adminroot_dir);
-            configuration->adminroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            if(configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] == '/')
-                configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] = 0;
-        } else if (strcmp(node->name, "alias") == 0) {
-            alias = malloc(sizeof(aliases));
-            alias->next = NULL;
-            alias->source = xmlGetProp(node, "source");
-            if(alias->source == NULL) {
-                free(alias);
-                continue;
-            }
-            alias->destination = xmlGetProp(node, "dest");
-            if(alias->destination == NULL) {
-                xmlFree(alias->source);
-                free(alias);
-                continue;
-            }
-            temp = NULL;
-            temp = xmlGetProp(node, "port");
-            if(temp != NULL) {
-                alias->port = atoi(temp);
-                xmlFree(temp);
-            }
-            else
-                alias->port = -1;
-            alias->bind_address = xmlGetProp(node, "bind-address");
-            current = configuration->aliases;
-            last = NULL;
-            while(current) {
-                last = current;
-                current = current->next;
-            }
-            if(last)
-                last->next = alias;
-            else
-                configuration->aliases = alias;
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_logging(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "accesslog") == 0) {
-            if (configuration->access_log && configuration->access_log != CONFIG_DEFAULT_ACCESS_LOG) xmlFree(configuration->access_log);
-            configuration->access_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "errorlog") == 0) {
-            if (configuration->error_log && configuration->error_log != CONFIG_DEFAULT_ERROR_LOG) xmlFree(configuration->error_log);
-            configuration->error_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "playlistlog") == 0) {
-            if (configuration->playlist_log && configuration->playlist_log != CONFIG_DEFAULT_PLAYLIST_LOG) xmlFree(configuration->playlist_log);
-            configuration->playlist_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-        } else if (strcmp(node->name, "logsize") == 0) {
-            char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->logsize = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "loglevel") == 0) {
-           char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-           configuration->loglevel = atoi(tmp);
-           if (tmp) xmlFree(tmp);
-        } else if (strcmp(node->name, "logarchive") == 0) {
-            char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->logarchive = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        }
-    } while ((node = node->next));
-}
-
-static void _parse_security(xmlDocPtr doc, xmlNodePtr node,
-        ice_config_t *configuration)
-{
-   char *tmp;
-   xmlNodePtr oldnode;
-
-   do {
-       if (node == NULL) break;
-       if (xmlIsBlankNode(node)) continue;
-
-       if (strcmp(node->name, "chroot") == 0) {
-           tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-           configuration->chroot = atoi(tmp);
-           if (tmp) xmlFree(tmp);
-       } else if (strcmp(node->name, "changeowner") == 0) {
-           configuration->chuid = 1;
-           oldnode = node;
-           node = node->xmlChildrenNode;
-           do {
-               if(node == NULL) break;
-               if(xmlIsBlankNode(node)) continue;
-               if(strcmp(node->name, "user") == 0) {
-                   if(configuration->user) xmlFree(configuration->user);
-                   configuration->user = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-               } else if(strcmp(node->name, "group") == 0) {
-                   if(configuration->group) xmlFree(configuration->group);
-                   configuration->group = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-               }
-           } while((node = node->next));
-           node = oldnode;
-       }
-   } while ((node = node->next));
-}
-
-static void _add_server(xmlDocPtr doc, xmlNodePtr node, 
-        ice_config_t *configuration)
-{
-    ice_config_dir_t *dirnode, *server;
-    int addnode;
-    char *tmp;
-
-    server = (ice_config_dir_t *)malloc(sizeof(ice_config_dir_t));
-    server->touch_interval = configuration->touch_interval;
-    server->host = NULL;
-    addnode = 0;
-    
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "host") == 0) {
-            server->host = (char *)xmlNodeListGetString(doc, 
-                    node->xmlChildrenNode, 1);
-            addnode = 1;
-        } else if (strcmp(node->name, "touch-interval") == 0) {
-            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            server->touch_interval = atoi(tmp);
-            if (tmp) xmlFree(tmp);
-        }
-        server->next = NULL;
-    } while ((node = node->next));
-
-    if (addnode) {
-        dirnode = configuration->dir_list;
-        if (dirnode == NULL) {
-            configuration->dir_list = server;
-        } else {
-            while (dirnode->next) dirnode = dirnode->next;
-            
-            dirnode->next = server;
-        }
-        
-        server = NULL;
-        addnode = 0;
-    }
-    else {
-        free (server);
-    }
-    
-}
-
-
-/* return the mount details that match the supplied mountpoint */
-mount_proxy *config_find_mount (ice_config_t *config, const char *mount)
-{
-    mount_proxy *mountinfo = config->mounts;
-
-    while (mountinfo)
-    {
-        if (strcmp (mountinfo->mountname, mount) == 0)
-            break;
-        mountinfo = mountinfo->next;
-    }
-    return mountinfo;
-}
-

Copied: icecast/tags/icecast-2.3.2/src/cfgfile.c (from rev 14866, icecast/trunk/icecast/src/cfgfile.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/cfgfile.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/cfgfile.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1155 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+#include "thread/thread.h"
+#include "cfgfile.h"
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h" 
+
+#define CATMODULE "CONFIG"
+#define CONFIG_DEFAULT_LOCATION "Earth"
+#define CONFIG_DEFAULT_ADMIN "icemaster at localhost"
+#define CONFIG_DEFAULT_CLIENT_LIMIT 256
+#define CONFIG_DEFAULT_SOURCE_LIMIT 16
+#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
+#define CONFIG_DEFAULT_BURST_SIZE (64*1024)
+#define CONFIG_DEFAULT_THREADPOOL_SIZE 4
+#define CONFIG_DEFAULT_CLIENT_TIMEOUT 30
+#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
+#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
+#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
+#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
+#define CONFIG_DEFAULT_MASTER_USERNAME "relay"
+#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
+#define CONFIG_DEFAULT_ICE_LOGIN 0
+#define CONFIG_DEFAULT_FILESERVE 1
+#define CONFIG_DEFAULT_TOUCH_FREQ 5
+#define CONFIG_DEFAULT_HOSTNAME "localhost"
+#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
+#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
+#define CONFIG_DEFAULT_ERROR_LOG "error.log"
+#define CONFIG_DEFAULT_LOG_LEVEL 3
+#define CONFIG_DEFAULT_CHROOT 0
+#define CONFIG_DEFAULT_CHUID 0
+#define CONFIG_DEFAULT_USER NULL
+#define CONFIG_DEFAULT_GROUP NULL
+#define CONFIG_MASTER_UPDATE_INTERVAL 120
+#define CONFIG_YP_URL_TIMEOUT 10
+
+#ifndef _WIN32
+#define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast"
+#define CONFIG_DEFAULT_LOG_DIR "/usr/local/icecast/logs"
+#define CONFIG_DEFAULT_WEBROOT_DIR "/usr/local/icecast/webroot"
+#define CONFIG_DEFAULT_ADMINROOT_DIR "/usr/local/icecast/admin"
+#define MIMETYPESFILE "/etc/mime.types"
+#else
+#define CONFIG_DEFAULT_BASE_DIR ".\\"
+#define CONFIG_DEFAULT_LOG_DIR ".\\logs"
+#define CONFIG_DEFAULT_WEBROOT_DIR ".\\webroot"
+#define CONFIG_DEFAULT_ADMINROOT_DIR ".\\admin"
+#define MIMETYPESFILE ".\\mime.types"
+#endif
+
+static ice_config_t _current_configuration;
+static ice_config_locks _locks;
+
+static void _set_defaults(ice_config_t *c);
+static void _parse_root(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_directory(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_logging(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_security(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *c);
+static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *c);
+static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+
+static void create_locks(void) {
+    thread_mutex_create(&_locks.relay_lock);
+    thread_rwlock_create(&_locks.config_lock);
+}
+
+static void release_locks(void) {
+    thread_mutex_destroy(&_locks.relay_lock);
+    thread_rwlock_destroy(&_locks.config_lock);
+}
+
+void config_initialize(void) {
+    create_locks();
+}
+
+void config_shutdown(void) {
+    config_get_config();
+    config_clear(&_current_configuration);
+    config_release_config();
+    release_locks();
+}
+
+void config_init_configuration(ice_config_t *configuration)
+{
+    memset(configuration, 0, sizeof(ice_config_t));
+    _set_defaults(configuration);
+}
+
+static void config_clear_mount (mount_proxy *mount)
+{
+    config_options_t *option;
+
+    if (mount->mountname)       xmlFree (mount->mountname);
+    if (mount->username)        xmlFree (mount->username);
+    if (mount->password)        xmlFree (mount->password);
+    if (mount->dumpfile)        xmlFree (mount->dumpfile);
+    if (mount->intro_filename)  xmlFree (mount->intro_filename);
+    if (mount->on_connect)      xmlFree (mount->on_connect);
+    if (mount->on_disconnect)   xmlFree (mount->on_disconnect);
+    if (mount->fallback_mount)  xmlFree (mount->fallback_mount);
+    if (mount->stream_name)     xmlFree (mount->stream_name);
+    if (mount->stream_description)  xmlFree (mount->stream_description);
+    if (mount->stream_url)      xmlFree (mount->stream_url);
+    if (mount->stream_genre)    xmlFree (mount->stream_genre);
+    if (mount->bitrate)         xmlFree (mount->bitrate);
+    if (mount->type)            xmlFree (mount->type);
+    if (mount->charset)         xmlFree (mount->charset);
+    if (mount->cluster_password)    xmlFree (mount->cluster_password);
+
+    if (mount->auth_type)       xmlFree (mount->auth_type);
+    option = mount->auth_options;
+    while (option)
+    {
+        config_options_t *nextopt = option->next;
+        if (option->name)   xmlFree (option->name);
+        if (option->value)  xmlFree (option->value);
+        free (option);
+        option = nextopt;
+    }
+    auth_release (mount->auth);
+    free (mount);
+}
+
+listener_t *config_clear_listener (listener_t *listener)
+{
+    listener_t *next = NULL;
+    if (listener)
+    {
+        next = listener->next;
+        if (listener->bind_address)     xmlFree (listener->bind_address);
+        if (listener->shoutcast_mount)  xmlFree (listener->shoutcast_mount);
+        free (listener);
+    }
+    return next;
+}
+
+void config_clear(ice_config_t *c)
+{
+    ice_config_dir_t *dirnode, *nextdirnode;
+    relay_server *relay, *nextrelay;
+    mount_proxy *mount, *nextmount;
+    aliases *alias, *nextalias;
+    int i;
+
+    free(c->config_filename);
+
+    xmlFree (c->server_id);
+    if (c->location) xmlFree(c->location);
+    if (c->admin) xmlFree(c->admin);
+    if (c->source_password) xmlFree(c->source_password);
+    if (c->admin_username)
+        xmlFree(c->admin_username);
+    if (c->admin_password)
+        xmlFree(c->admin_password);
+    if (c->relay_username)
+        xmlFree(c->relay_username);
+    if (c->relay_password)
+        xmlFree(c->relay_password);
+    if (c->hostname) xmlFree(c->hostname);
+    if (c->base_dir) xmlFree(c->base_dir);
+    if (c->log_dir) xmlFree(c->log_dir);
+    if (c->webroot_dir) xmlFree(c->webroot_dir);
+    if (c->adminroot_dir) xmlFree(c->adminroot_dir);
+    if (c->cert_file) xmlFree(c->cert_file);
+    if (c->pidfile)
+        xmlFree(c->pidfile);
+    if (c->banfile) xmlFree(c->banfile);
+    if (c->allowfile) xmlFree(c->allowfile);
+    if (c->playlist_log) xmlFree(c->playlist_log);
+    if (c->access_log) xmlFree(c->access_log);
+    if (c->error_log) xmlFree(c->error_log);
+    if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
+    if (c->master_server) xmlFree(c->master_server);
+    if (c->master_username) xmlFree(c->master_username);
+    if (c->master_password) xmlFree(c->master_password);
+    if (c->user) xmlFree(c->user);
+    if (c->group) xmlFree(c->group);
+    if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
+
+    while ((c->listen_sock = config_clear_listener (c->listen_sock)))
+        ;
+
+    thread_mutex_lock(&(_locks.relay_lock));
+    relay = c->relay;
+    while(relay) {
+        nextrelay = relay->next;
+        xmlFree(relay->server);
+        xmlFree(relay->mount);
+        xmlFree(relay->localmount);
+        free(relay);
+        relay = nextrelay;
+    }
+    thread_mutex_unlock(&(_locks.relay_lock));
+
+    mount = c->mounts;
+    while(mount) {
+        nextmount = mount->next;
+        config_clear_mount (mount);
+        mount = nextmount;
+    }
+
+    alias = c->aliases;
+    while(alias) {
+        nextalias = alias->next;
+        xmlFree(alias->source);
+        xmlFree(alias->destination);
+        xmlFree(alias->bind_address);
+        free(alias);
+        alias = nextalias;
+    }
+
+    dirnode = c->dir_list;
+    while(dirnode) {
+        nextdirnode = dirnode->next;
+        xmlFree(dirnode->host);
+        free(dirnode);
+        dirnode = nextdirnode;
+    }
+#ifdef USE_YP
+    i = 0;
+    while (i < c->num_yp_directories)
+    {
+        xmlFree (c->yp_url[i]);
+        i++;
+    }
+#endif
+
+    memset(c, 0, sizeof(ice_config_t));
+}
+
+int config_initial_parse_file(const char *filename)
+{
+    /* Since we're already pointing at it, we don't need to copy it in place */
+    return config_parse_file(filename, &_current_configuration);
+}
+
+int config_parse_file(const char *filename, ice_config_t *configuration)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node;
+
+    if (filename == NULL || strcmp(filename, "") == 0) return CONFIG_EINSANE;
+    
+    doc = xmlParseFile(filename);
+    if (doc == NULL) {
+        return CONFIG_EPARSE;
+    }
+
+    node = xmlDocGetRootElement(doc);
+    if (node == NULL) {
+        xmlFreeDoc(doc);
+        xmlCleanupParser();
+        return CONFIG_ENOROOT;
+    }
+
+    if (xmlStrcmp (node->name, XMLSTR("icecast")) != 0) {
+        xmlFreeDoc(doc);
+        xmlCleanupParser();
+        return CONFIG_EBADROOT;
+    }
+
+    config_init_configuration(configuration);
+
+    configuration->config_filename = strdup (filename);
+
+    _parse_root(doc, node->xmlChildrenNode, configuration);
+
+    xmlFreeDoc(doc);
+
+    return 0;
+}
+
+int config_parse_cmdline(int arg, char **argv)
+{
+    return 0;
+}
+
+ice_config_locks *config_locks(void)
+{
+    return &_locks;
+}
+
+void config_release_config(void)
+{
+    thread_rwlock_unlock(&(_locks.config_lock));
+}
+
+ice_config_t *config_get_config(void)
+{
+    thread_rwlock_rlock(&(_locks.config_lock));
+    return &_current_configuration;
+}
+
+ice_config_t *config_grab_config(void)
+{
+    thread_rwlock_wlock(&(_locks.config_lock));
+    return &_current_configuration;
+}
+
+/* MUST be called with the lock held! */
+void config_set_config(ice_config_t *config) {
+    memcpy(&_current_configuration, config, sizeof(ice_config_t));
+}
+
+ice_config_t *config_get_config_unlocked(void)
+{
+    return &_current_configuration;
+}
+
+static void _set_defaults(ice_config_t *configuration)
+{
+    configuration->location = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOCATION);
+    configuration->server_id = (char *)xmlCharStrdup (ICECAST_VERSION_STRING);
+    configuration->admin = (char *)xmlCharStrdup (CONFIG_DEFAULT_ADMIN);
+    configuration->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
+    configuration->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
+    configuration->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
+    configuration->threadpool_size = CONFIG_DEFAULT_THREADPOOL_SIZE;
+    configuration->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
+    configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
+    configuration->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
+    configuration->source_password = (char *)xmlCharStrdup (CONFIG_DEFAULT_SOURCE_PASSWORD);
+    configuration->shoutcast_mount = (char *)xmlCharStrdup (CONFIG_DEFAULT_SHOUTCAST_MOUNT);
+    configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
+    configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
+    configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
+    configuration->on_demand = 0;
+    configuration->dir_list = NULL;
+    configuration->hostname = (char *)xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);
+    configuration->mimetypes_fn = (char *)xmlCharStrdup (MIMETYPESFILE);
+    configuration->master_server = NULL;
+    configuration->master_server_port = 0;
+    configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
+    configuration->master_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
+    configuration->master_password = NULL;
+    configuration->base_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_BASE_DIR);
+    configuration->log_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOG_DIR);
+    configuration->webroot_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_WEBROOT_DIR);
+    configuration->adminroot_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_ADMINROOT_DIR);
+    configuration->playlist_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_PLAYLIST_LOG);
+    configuration->access_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_ACCESS_LOG);
+    configuration->error_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_ERROR_LOG);
+    configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
+    configuration->chroot = CONFIG_DEFAULT_CHROOT;
+    configuration->chuid = CONFIG_DEFAULT_CHUID;
+    configuration->user = NULL;
+    configuration->group = NULL;
+    configuration->num_yp_directories = 0;
+    configuration->relay_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
+    configuration->relay_password = NULL;
+    /* default to a typical prebuffer size used by clients */
+    configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE;
+}
+
+static void _parse_root(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    configuration->listen_sock = calloc (1, sizeof (*configuration->listen_sock));
+    configuration->listen_sock->port = 8000;
+    configuration->listen_sock_count = 1;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("location")) == 0) {
+            if (configuration->location) xmlFree(configuration->location);
+            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("admin")) == 0) {
+            if (configuration->admin) xmlFree(configuration->admin);
+            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("server-id")) == 0) {
+            xmlFree (configuration->server_id);
+            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if(xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
+            _parse_authentication(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("source-password")) == 0) {
+            /* TODO: This is the backwards-compatibility location */
+            char *mount, *pass;
+            if ((mount = (char *)xmlGetProp(node, XMLSTR("mount"))) != NULL) {
+                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+                /* FIXME: This is a placeholder for per-mount passwords */
+            }
+            else {
+                if (configuration->source_password) xmlFree(configuration->source_password);
+                configuration->source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            }
+        } else if (xmlStrcmp (node->name, XMLSTR("icelogin")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->ice_login = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("fileserve")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->fileserve = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("relays-on-demand")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->on_demand = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("hostname")) == 0) {
+            if (configuration->hostname) xmlFree(configuration->hostname);
+            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("mime-types")) == 0) {
+            if (configuration->mimetypes_fn) xmlFree(configuration->mimetypes_fn);
+            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("listen-socket")) == 0) {
+            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("port")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->port = atoi(tmp);
+            configuration->listen_sock->port = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("bind-address")) == 0) {
+            if (configuration->listen_sock->bind_address) 
+                xmlFree(configuration->listen_sock->bind_address);
+            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("master-server")) == 0) {
+            if (configuration->master_server) xmlFree(configuration->master_server);
+            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("master-username")) == 0) {
+            if (configuration->master_username) xmlFree(configuration->master_username);
+            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("master-password")) == 0) {
+            if (configuration->master_password) xmlFree(configuration->master_password);
+            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("master-server-port")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->master_server_port = atoi(tmp);
+            xmlFree (tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("master-update-interval")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->master_update_interval = atoi(tmp);
+            xmlFree (tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("shoutcast-mount")) == 0) {
+            if (configuration->shoutcast_mount) xmlFree(configuration->shoutcast_mount);
+            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("limits")) == 0) {
+            _parse_limits(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("relay")) == 0) {
+            _parse_relay(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("mount")) == 0) {
+            _parse_mount(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("directory")) == 0) {
+            _parse_directory(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("paths")) == 0) {
+            _parse_paths(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("logging")) == 0) {
+            _parse_logging(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("security")) == 0) {
+            _parse_security(doc, node->xmlChildrenNode, configuration);
+        }
+    } while ((node = node->next));
+
+    /* drop the first listening socket details if more than one is defined, as we only
+     * have port or listen-socket not both */
+    if (configuration->listen_sock_count > 1)
+    {
+        configuration->listen_sock = config_clear_listener (configuration->listen_sock);
+        configuration->listen_sock_count--;
+    }
+    if (configuration->port == 0)
+        configuration->port = 8000;
+}
+
+static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("clients")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->client_limit = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("sources")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->source_limit = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->queue_size_limit = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("threadpool")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->threadpool_size = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("client-timeout")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->client_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("header-timeout")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->header_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->source_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("burst-on-connect")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if (atoi(tmp) == 0)
+                configuration->burst_size = 0;
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->burst_size = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *configuration)
+{
+    char *tmp;
+    mount_proxy *mount = calloc(1, sizeof(mount_proxy));
+    mount_proxy *current = configuration->mounts;
+    mount_proxy *last=NULL;
+    
+    /* default <mount> settings */
+    mount->max_listeners = -1;
+    mount->burst_size = -1;
+    mount->mp3_meta_interval = -1;
+    mount->yp_public = -1;
+    mount->next = NULL;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("mount-name")) == 0) {
+            mount->mountname = (char *)xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("username")) == 0) {
+            mount->username = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("password")) == 0) {
+            mount->password = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("dump-file")) == 0) {
+            mount->dumpfile = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("intro")) == 0) {
+            mount->intro_filename = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("fallback-mount")) == 0) {
+            mount->fallback_mount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("fallback-when-full")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->fallback_when_full = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("max-listeners")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->max_listeners = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("charset")) == 0) {
+            mount->charset = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("mp3-metadata-interval")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->mp3_meta_interval = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("fallback-override")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->fallback_override = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("no-mount")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->no_mount = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("no-yp")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->yp_public = atoi(tmp) == 0 ? -1 : 0;
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("hidden")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->hidden = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
+            mount->auth = auth_get_authenticator (node);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
+            mount->on_connect = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("on-disconnect")) == 0) {
+            mount->on_disconnect = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("max-listener-duration")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->max_listener_duration = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->queue_size_limit = atoi (tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if (tmp)
+            {
+                mount->source_timeout = atoi (tmp);
+                xmlFree(tmp);
+            }
+        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->burst_size = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("cluster-password")) == 0) {
+            mount->cluster_password = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("stream-name")) == 0) {
+            mount->stream_name = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("stream-description")) == 0) {
+            mount->stream_description = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("stream-url")) == 0) {
+            mount->stream_url = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("genre")) == 0) {
+            mount->stream_genre = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("bitrate")) == 0) {
+            mount->bitrate = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("public")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->yp_public = atoi (tmp);
+            if(tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("type")) == 0) {
+            mount->type = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("subtype")) == 0) {
+            mount->subtype = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+    } while ((node = node->next));
+
+    /* make sure we have at least the mountpoint name */
+    if (mount->mountname == NULL)
+    {
+        config_clear_mount (mount);
+        return;
+    }
+    if (mount->auth)
+        mount->auth->mount = strdup ((char *)mount->mountname);
+    while(current) {
+        last = current;
+        current = current->next;
+    }
+
+    if(last)
+        last->next = mount;
+    else
+        configuration->mounts = mount;
+}
+
+
+static void _parse_relay(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+    relay_server *relay = calloc(1, sizeof(relay_server));
+    relay_server *current = configuration->relay;
+    relay_server *last=NULL;
+
+    while(current) {
+        last = current;
+        current = current->next;
+    }
+
+    if(last)
+        last->next = relay;
+    else
+        configuration->relay = relay;
+
+    relay->next = NULL;
+    relay->mp3metadata = 1;
+    relay->on_demand = configuration->on_demand;
+    relay->server = (char *)xmlCharStrdup ("127.0.0.1");
+    relay->mount = (char *)xmlCharStrdup ("/");
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("server")) == 0) {
+            if (relay->server) xmlFree (relay->server);
+            relay->server = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("port")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->port = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("mount")) == 0) {
+            if (relay->mount) xmlFree (relay->mount);
+            relay->mount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("local-mount")) == 0) {
+            if (relay->localmount) xmlFree (relay->localmount);
+            relay->localmount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("relay-shoutcast-metadata")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->mp3metadata = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("username")) == 0) {
+            if (relay->username) xmlFree (relay->username);
+            relay->username = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("password")) == 0) {
+            if (relay->password) xmlFree (relay->password);
+            relay->password = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("on-demand")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->on_demand = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+    if (relay->localmount == NULL)
+        relay->localmount = (char *)xmlStrdup (XMLSTR(relay->mount));
+}
+
+static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+    listener_t *listener = calloc (1, sizeof(listener_t));
+
+    if (listener == NULL)
+        return;
+    listener->port = 8000;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("port")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->port == 0)
+                configuration->port = atoi(tmp);
+            listener->port = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("ssl")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            listener->ssl = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("shoutcast-compat")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            listener->shoutcast_compat = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("shoutcast-mount")) == 0) {
+            if (listener->shoutcast_mount) xmlFree (listener->shoutcast_mount);
+            listener->shoutcast_mount = (char *)xmlNodeListGetString(doc, 
+                    node->xmlChildrenNode, 1);
+        }
+        else if (xmlStrcmp (node->name, XMLSTR("bind-address")) == 0) {
+            if (listener->bind_address) xmlFree (listener->bind_address);
+            listener->bind_address = (char *)xmlNodeListGetString(doc, 
+                    node->xmlChildrenNode, 1);
+        }
+    } while ((node = node->next));
+
+    /* we know there's at least one of these, so add this new one after the first
+     * that way it can be removed easily later on */
+    listener->next = configuration->listen_sock->next;
+    configuration->listen_sock->next = listener;
+    configuration->listen_sock_count++;
+    if (listener->shoutcast_mount)
+    {
+        listener_t *sc_port = calloc (1, sizeof (listener_t));
+        sc_port->port = listener->port+1;
+        sc_port->shoutcast_compat = 1;
+        sc_port->shoutcast_mount = (char*)xmlStrdup (XMLSTR(listener->shoutcast_mount));
+        if (listener->bind_address)
+            sc_port->bind_address = (char*)xmlStrdup (XMLSTR(listener->bind_address));
+
+        sc_port->next = listener->next;
+        listener->next = sc_port;
+        configuration->listen_sock_count++;
+    }
+}
+
+static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("source-password")) == 0) {
+            char *mount, *pass;
+            if ((mount = (char *)xmlGetProp(node, XMLSTR("mount"))) != NULL) {
+                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+                /* FIXME: This is a placeholder for per-mount passwords */
+            }
+            else {
+                if (configuration->source_password)
+                    xmlFree(configuration->source_password);
+                configuration->source_password = 
+                    (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            }
+        } else if (xmlStrcmp (node->name, XMLSTR("admin-password")) == 0) {
+            if(configuration->admin_password)
+                xmlFree(configuration->admin_password);
+            configuration->admin_password =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("admin-user")) == 0) {
+            if(configuration->admin_username)
+                xmlFree(configuration->admin_username);
+            configuration->admin_username =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("relay-password")) == 0) {
+            if(configuration->relay_password)
+                xmlFree(configuration->relay_password);
+            configuration->relay_password =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("relay-user")) == 0) {
+            if(configuration->relay_username)
+                xmlFree(configuration->relay_username);
+            configuration->relay_username =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_directory(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    if (configuration->num_yp_directories >= MAX_YP_DIRECTORIES) {
+        ERROR0("Maximum number of yp directories exceeded!");
+        return;
+    }
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("yp-url")) == 0) {
+            if (configuration->yp_url[configuration->num_yp_directories]) 
+                xmlFree(configuration->yp_url[configuration->num_yp_directories]);
+            configuration->yp_url[configuration->num_yp_directories] = 
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("yp-url-timeout")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->yp_url_timeout[configuration->num_yp_directories] = 
+                atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("server")) == 0) {
+            _add_server(doc, node->xmlChildrenNode, configuration);
+        } else if (xmlStrcmp (node->name, XMLSTR("touch-interval")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->yp_touch_interval[configuration->num_yp_directories] =
+                atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+    if (configuration->yp_url [configuration->num_yp_directories] == NULL)
+        return;
+    configuration->num_yp_directories++;
+}
+
+static void _parse_paths(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *temp;
+    aliases *alias, *current, *last;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("basedir")) == 0) {
+            if (configuration->base_dir) xmlFree(configuration->base_dir);
+            configuration->base_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("logdir")) == 0) {
+            if (configuration->log_dir) xmlFree(configuration->log_dir);
+            configuration->log_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("pidfile")) == 0) {
+            if (configuration->pidfile) xmlFree(configuration->pidfile);
+            configuration->pidfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("deny-ip")) == 0) {
+            if (configuration->banfile) xmlFree(configuration->banfile);
+            configuration->banfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("allow-ip")) == 0) {
+            if (configuration->allowfile) xmlFree(configuration->allowfile);
+            configuration->allowfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("ssl-certificate")) == 0) {
+            if (configuration->cert_file) xmlFree(configuration->cert_file);
+            configuration->cert_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("webroot")) == 0) {
+            if (configuration->webroot_dir) xmlFree(configuration->webroot_dir);
+            configuration->webroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->webroot_dir[strlen(configuration->webroot_dir)-1] == '/')
+                configuration->webroot_dir[strlen(configuration->webroot_dir)-1] = 0;
+        } else if (xmlStrcmp (node->name, XMLSTR("adminroot")) == 0) {
+            if (configuration->adminroot_dir) 
+                xmlFree(configuration->adminroot_dir);
+            configuration->adminroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] == '/')
+                configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] = 0;
+        } else if (xmlStrcmp (node->name, XMLSTR("alias")) == 0) {
+            alias = malloc(sizeof(aliases));
+            alias->next = NULL;
+            alias->source = (char *)xmlGetProp(node, XMLSTR("source"));
+            if(alias->source == NULL) {
+                free(alias);
+                continue;
+            }
+            alias->destination = (char *)xmlGetProp(node, XMLSTR("dest"));
+            if(alias->destination == NULL) {
+                xmlFree(alias->source);
+                free(alias);
+                continue;
+            }
+            temp = NULL;
+            temp = (char *)xmlGetProp(node, XMLSTR("port"));
+            if(temp != NULL) {
+                alias->port = atoi(temp);
+                xmlFree(temp);
+            }
+            else
+                alias->port = -1;
+            alias->bind_address = (char *)xmlGetProp(node, XMLSTR("bind-address"));
+            current = configuration->aliases;
+            last = NULL;
+            while(current) {
+                last = current;
+                current = current->next;
+            }
+            if(last)
+                last->next = alias;
+            else
+                configuration->aliases = alias;
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_logging(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("accesslog")) == 0) {
+            if (configuration->access_log) xmlFree(configuration->access_log);
+            configuration->access_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("errorlog")) == 0) {
+            if (configuration->error_log) xmlFree(configuration->error_log);
+            configuration->error_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("playlistlog")) == 0) {
+            if (configuration->playlist_log) xmlFree(configuration->playlist_log);
+            configuration->playlist_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (xmlStrcmp (node->name, XMLSTR("logsize")) == 0) {
+            char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->logsize = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("loglevel")) == 0) {
+           char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+           configuration->loglevel = atoi(tmp);
+           if (tmp) xmlFree(tmp);
+        } else if (xmlStrcmp (node->name, XMLSTR("logarchive")) == 0) {
+            char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->logarchive = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_security(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+   char *tmp;
+   xmlNodePtr oldnode;
+
+   do {
+       if (node == NULL) break;
+       if (xmlIsBlankNode(node)) continue;
+
+       if (xmlStrcmp (node->name, XMLSTR("chroot")) == 0) {
+           tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+           configuration->chroot = atoi(tmp);
+           if (tmp) xmlFree(tmp);
+       } else if (xmlStrcmp (node->name, XMLSTR("changeowner")) == 0) {
+           configuration->chuid = 1;
+           oldnode = node;
+           node = node->xmlChildrenNode;
+           do {
+               if(node == NULL) break;
+               if(xmlIsBlankNode(node)) continue;
+               if(xmlStrcmp (node->name, XMLSTR("user")) == 0) {
+                   if(configuration->user) xmlFree(configuration->user);
+                   configuration->user = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+               } else if(xmlStrcmp (node->name, XMLSTR("group")) == 0) {
+                   if(configuration->group) xmlFree(configuration->group);
+                   configuration->group = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+               }
+           } while((node = node->next));
+           node = oldnode;
+       }
+   } while ((node = node->next));
+}
+
+static void _add_server(xmlDocPtr doc, xmlNodePtr node, 
+        ice_config_t *configuration)
+{
+    ice_config_dir_t *dirnode, *server;
+    int addnode;
+    char *tmp;
+
+    server = (ice_config_dir_t *)malloc(sizeof(ice_config_dir_t));
+    server->touch_interval = configuration->touch_interval;
+    server->host = NULL;
+    addnode = 0;
+    
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (xmlStrcmp (node->name, XMLSTR("host")) == 0) {
+            server->host = (char *)xmlNodeListGetString(doc, 
+                    node->xmlChildrenNode, 1);
+            addnode = 1;
+        } else if (xmlStrcmp (node->name, XMLSTR("touch-interval")) == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            server->touch_interval = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+        server->next = NULL;
+    } while ((node = node->next));
+
+    if (addnode) {
+        dirnode = configuration->dir_list;
+        if (dirnode == NULL) {
+            configuration->dir_list = server;
+        } else {
+            while (dirnode->next) dirnode = dirnode->next;
+            
+            dirnode->next = server;
+        }
+        
+        server = NULL;
+        addnode = 0;
+    }
+    else {
+        free (server);
+    }
+    
+}
+
+
+/* return the mount details that match the supplied mountpoint */
+mount_proxy *config_find_mount (ice_config_t *config, const char *mount)
+{
+    mount_proxy *mountinfo = config->mounts;
+
+    while (mountinfo)
+    {
+        if (strcmp (mountinfo->mountname, mount) == 0)
+            break;
+        mountinfo = mountinfo->next;
+    }
+    return mountinfo;
+}
+
+/* Helper function to locate the configuration details of the listening 
+ * socket
+ */
+listener_t *config_get_listen_sock (ice_config_t *config, connection_t *con)
+{
+    listener_t *listener;
+    int i = 0;
+
+    listener = config->listen_sock;
+    global_lock();
+    while (listener)
+    {
+        if (i >= global.server_sockets)
+            listener = NULL;
+        else
+        {
+            if (global.serversock[i] == con->serversock)
+                break;
+            listener = listener->next;
+            i++;
+        }
+    }
+    global_unlock();
+    return listener;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/cfgfile.h
===================================================================
--- icecast/trunk/icecast/src/cfgfile.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/cfgfile.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,200 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __CFGFILE_H__
-#define __CFGFILE_H__
-
-#define CONFIG_EINSANE -1
-#define CONFIG_ENOROOT -2
-#define CONFIG_EBADROOT -3
-#define CONFIG_EPARSE -4
-
-#define MAX_YP_DIRECTORIES 25
-
-struct _mount_proxy;
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "auth.h"
-#include "global.h"
-
-typedef struct ice_config_dir_tag
-{
-    char *host;
-    int touch_interval;
-    struct ice_config_dir_tag *next;
-} ice_config_dir_t;
-
-typedef struct _config_options {
-    char *name;
-    char *value;
-    struct _config_options *next;
-} config_options_t;
-
-typedef struct _mount_proxy {
-    char *mountname; /* The mountpoint this proxy is used for */
-
-    char *username; /* Username and password for this mountpoint. If unset, */
-    char *password; /* falls back to global source password */
-
-    char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
-                       to not dump. */
-    char *intro_filename;   /* Send contents of file to client before the stream */
-    int fallback_when_full; /* switch new listener to fallback source
-                               when max listeners reached */
-    int max_listeners; /* Max listeners for this mountpoint only. -1 to not 
-                          limit here (i.e. only use the global limit) */
-    char *fallback_mount; /* Fallback mountname */
-
-    int fallback_override; /* When this source arrives, do we steal back
-                              clients from the fallback? */
-    int no_mount; /* Do we permit direct requests of this mountpoint? (or only
-                     indirect, through fallbacks) */
-    int burst_size; /* amount to send to a new client if possible, -1 take
-                     * from global setting */
-    unsigned int queue_size_limit;
-    int hidden; /* Do we list this on the xsl pages */
-    unsigned int source_timeout;  /* source timeout in seconds */
-    int mp3_meta_interval; /* outgoing per-stream metadata interval */
-
-    char *auth_type; /* Authentication type */
-    struct auth_tag *auth;
-    char *cluster_password;
-    config_options_t *auth_options; /* Options for this type */
-    char *on_connect;
-    char *on_disconnect;
-    unsigned int max_listener_duration;
-
-    char *stream_name;
-    char *stream_description;
-    char *stream_url;
-    char *stream_genre;
-    char *bitrate;
-    char *type;
-    char *subtype;
-    int yp_public;
-
-    struct _mount_proxy *next;
-} mount_proxy;
-
-typedef struct _aliases {
-    char *source;
-    char *destination;
-    int port;
-    char *bind_address;
-    struct _aliases *next;
-}aliases;
-
-typedef struct {
-    int port;
-    char *bind_address;
-    int shoutcast_compat;
-} listener_t;
-
-typedef struct ice_config_tag
-{
-    char *config_filename;
-
-    char *location;
-    char *admin;
-
-    int client_limit;
-    int source_limit;
-    unsigned int queue_size_limit;
-    int threadpool_size;
-    unsigned int burst_size;
-    int client_timeout;
-    int header_timeout;
-    int source_timeout;
-    int ice_login;
-    int fileserve;
-    int on_demand; /* global setting for all relays */
-
-    char *shoutcast_mount;
-    char *source_password;
-    char *admin_username;
-    char *admin_password;
-    char *relay_username;
-    char *relay_password;
-
-    int touch_interval;
-    ice_config_dir_t *dir_list;
-
-    char *hostname;
-    int port;
-
-    listener_t listeners[MAX_LISTEN_SOCKETS];
-
-    char *master_server;
-    int master_server_port;
-    int master_update_interval;
-    char *master_username;
-    char *master_password;
-
-    relay_server *relay;
-
-    mount_proxy *mounts;
-
-    char *base_dir;
-    char *log_dir;
-    char *pidfile;
-    char *webroot_dir;
-    char *adminroot_dir;
-    aliases *aliases;
-
-    char *access_log;
-    char *error_log;
-    char *playlist_log;
-    int loglevel;
-    int logsize;
-    int logarchive;
-
-    int chroot;
-    int chuid;
-    char *user;
-    char *group;
-    char *yp_url[MAX_YP_DIRECTORIES];
-    int    yp_url_timeout[MAX_YP_DIRECTORIES];
-    int    yp_touch_interval[MAX_YP_DIRECTORIES];
-    int num_yp_directories;
-} ice_config_t;
-
-typedef struct {
-    rwlock_t config_lock;
-    mutex_t relay_lock;
-} ice_config_locks;
-
-void config_initialize(void);
-void config_shutdown(void);
-
-int config_parse_file(const char *filename, ice_config_t *configuration);
-int config_initial_parse_file(const char *filename);
-int config_parse_cmdline(int arg, char **argv);
-void config_set_config(ice_config_t *config);
-void config_clear(ice_config_t *config);
-mount_proxy *config_find_mount (ice_config_t *config, const char *mount);
-
-int config_rehash(void);
-
-ice_config_locks *config_locks(void);
-
-ice_config_t *config_get_config(void);
-ice_config_t *config_grab_config(void);
-void config_release_config(void);
-
-/* To be used ONLY in one-time startup code */
-ice_config_t *config_get_config_unlocked(void);
-
-#endif  /* __CFGFILE_H__ */
-
-
-

Copied: icecast/tags/icecast-2.3.2/src/cfgfile.h (from rev 14045, icecast/trunk/icecast/src/cfgfile.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/cfgfile.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/cfgfile.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,215 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __CFGFILE_H__
+#define __CFGFILE_H__
+
+#define CONFIG_EINSANE -1
+#define CONFIG_ENOROOT -2
+#define CONFIG_EBADROOT -3
+#define CONFIG_EPARSE -4
+
+#define MAX_YP_DIRECTORIES 25
+
+struct _mount_proxy;
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "auth.h"
+#include "global.h"
+#include "connection.h"
+
+#define XMLSTR(str) ((xmlChar *)(str)) 
+
+typedef struct ice_config_dir_tag
+{
+    char *host;
+    int touch_interval;
+    struct ice_config_dir_tag *next;
+} ice_config_dir_t;
+
+typedef struct _config_options {
+    char *name;
+    char *value;
+    struct _config_options *next;
+} config_options_t;
+
+typedef struct _mount_proxy {
+    char *mountname; /* The mountpoint this proxy is used for */
+
+    char *username; /* Username and password for this mountpoint. If unset, */
+    char *password; /* falls back to global source password */
+
+    char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
+                       to not dump. */
+    char *intro_filename;   /* Send contents of file to client before the stream */
+    int fallback_when_full; /* switch new listener to fallback source
+                               when max listeners reached */
+    int max_listeners; /* Max listeners for this mountpoint only. -1 to not 
+                          limit here (i.e. only use the global limit) */
+    char *fallback_mount; /* Fallback mountname */
+
+    int fallback_override; /* When this source arrives, do we steal back
+                              clients from the fallback? */
+    int no_mount; /* Do we permit direct requests of this mountpoint? (or only
+                     indirect, through fallbacks) */
+    int burst_size; /* amount to send to a new client if possible, -1 take
+                     * from global setting */
+    unsigned int queue_size_limit;
+    int hidden; /* Do we list this on the xsl pages */
+    unsigned int source_timeout;  /* source timeout in seconds */
+    char *charset;  /* character set if not utf8 */
+    int mp3_meta_interval; /* outgoing per-stream metadata interval */
+
+    char *auth_type; /* Authentication type */
+    struct auth_tag *auth;
+    char *cluster_password;
+    config_options_t *auth_options; /* Options for this type */
+    char *on_connect;
+    char *on_disconnect;
+    unsigned int max_listener_duration;
+
+    char *stream_name;
+    char *stream_description;
+    char *stream_url;
+    char *stream_genre;
+    char *bitrate;
+    char *type;
+    char *subtype;
+    int yp_public;
+
+    struct _mount_proxy *next;
+} mount_proxy;
+
+typedef struct _aliases {
+    char *source;
+    char *destination;
+    int port;
+    char *bind_address;
+    struct _aliases *next;
+}aliases;
+
+typedef struct _listener_t {
+    struct _listener_t *next;
+    int port;
+    char *bind_address;
+    int shoutcast_compat;
+    char *shoutcast_mount;
+    int ssl;
+} listener_t;
+
+typedef struct ice_config_tag
+{
+    char *config_filename;
+
+    char *location;
+    char *admin;
+
+    int client_limit;
+    int source_limit;
+    unsigned int queue_size_limit;
+    int threadpool_size;
+    unsigned int burst_size;
+    int client_timeout;
+    int header_timeout;
+    int source_timeout;
+    int ice_login;
+    int fileserve;
+    int on_demand; /* global setting for all relays */
+
+    char *shoutcast_mount;
+    char *source_password;
+    char *admin_username;
+    char *admin_password;
+    char *relay_username;
+    char *relay_password;
+
+    int touch_interval;
+    ice_config_dir_t *dir_list;
+
+    char *hostname;
+    int port;
+    char *mimetypes_fn;
+
+    listener_t *listen_sock;
+    unsigned int listen_sock_count;
+
+    char *master_server;
+    int master_server_port;
+    int master_update_interval;
+    char *master_username;
+    char *master_password;
+
+    relay_server *relay;
+
+    mount_proxy *mounts;
+
+    char *server_id;
+    char *base_dir;
+    char *log_dir;
+    char *pidfile;
+    char *banfile;
+    char *allowfile;
+    char *cert_file;
+    char *webroot_dir;
+    char *adminroot_dir;
+    aliases *aliases;
+
+    char *access_log;
+    char *error_log;
+    char *playlist_log;
+    int loglevel;
+    int logsize;
+    int logarchive;
+
+    int chroot;
+    int chuid;
+    char *user;
+    char *group;
+    char *yp_url[MAX_YP_DIRECTORIES];
+    int    yp_url_timeout[MAX_YP_DIRECTORIES];
+    int    yp_touch_interval[MAX_YP_DIRECTORIES];
+    int num_yp_directories;
+} ice_config_t;
+
+typedef struct {
+    rwlock_t config_lock;
+    mutex_t relay_lock;
+} ice_config_locks;
+
+void config_initialize(void);
+void config_shutdown(void);
+
+int config_parse_file(const char *filename, ice_config_t *configuration);
+int config_initial_parse_file(const char *filename);
+int config_parse_cmdline(int arg, char **argv);
+void config_set_config(ice_config_t *config);
+listener_t *config_clear_listener (listener_t *listener);
+void config_clear(ice_config_t *config);
+mount_proxy *config_find_mount (ice_config_t *config, const char *mount);
+listener_t *config_get_listen_sock (ice_config_t *config, connection_t *con);
+
+int config_rehash(void);
+
+ice_config_locks *config_locks(void);
+
+ice_config_t *config_get_config(void);
+ice_config_t *config_grab_config(void);
+void config_release_config(void);
+
+/* To be used ONLY in one-time startup code */
+ice_config_t *config_get_config_unlocked(void);
+
+#endif  /* __CFGFILE_H__ */
+
+
+

Deleted: icecast/tags/icecast-2.3.2/src/client.c
===================================================================
--- icecast/trunk/icecast/src/client.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/client.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,230 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* client.c
-**
-** client interface implementation
-**
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "httpp/httpp.h"
-
-#include "cfgfile.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "format.h"
-#include "stats.h"
-#include "fserve.h"
-
-#include "client.h"
-#include "logging.h"
-
-#ifdef _WIN32
-#define snprintf _snprintf
-#endif
-
-#undef CATMODULE
-#define CATMODULE "client"
-
-/* create a client_t with the provided connection and parser details. Return
- * 0 on success, -1 if server limit has been reached.  In either case a
- * client_t is returned just in case a message needs to be returned. Should
- * be called with global lock held.
- */
-int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
-{
-    ice_config_t *config;
-    client_t *client = (client_t *)calloc(1, sizeof(client_t));
-    int ret = -1;
-
-    if (client == NULL)
-        abort();
-
-    config = config_get_config ();
-
-    global.clients++;
-    if (config->client_limit < global.clients)
-        WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients);
-    else
-        ret = 0;
-
-    config_release_config ();
-
-    stats_event_args (NULL, "clients", "%d", global.clients);
-    client->con = con;
-    client->parser = parser;
-    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
-    client->refbuf->len = 0; /* force reader code to ignore buffer contents */
-    client->pos = 0;
-    client->write_to_client = format_generic_write_to_client;
-    *c_ptr = client;
-
-    return ret;
-}
-
-void client_destroy(client_t *client)
-{
-    if (client == NULL)
-        return;
-
-    /* release the buffer now, as the buffer could be on the source queue
-     * and may of disappeared after auth completes */
-    if (client->refbuf)
-    {
-        refbuf_release (client->refbuf);
-        client->refbuf = NULL;
-    }
-
-    if (release_client (client))
-        return;
-
-    /* write log entry if ip is set (some things don't set it, like outgoing 
-     * slave requests
-     */
-    if (client->respcode && client->parser)
-        logging_access(client);
-    
-    if (client->con)
-        connection_close(client->con);
-    if (client->parser)
-        httpp_destroy(client->parser);
-
-    global_lock ();
-    global.clients--;
-    stats_event_args (NULL, "clients", "%d", global.clients);
-    global_unlock ();
-
-    /* we need to free client specific format data (if any) */
-    if (client->free_client_data)
-        client->free_client_data (client);
-
-    free(client->username);
-    free(client->password);
-
-    free(client);
-}
-
-
-/* helper function for reading data from a client */
-int client_read_bytes (client_t *client, void *buf, unsigned len)
-{
-    int bytes;
-    
-    if (client->refbuf && client->refbuf->len)
-    {
-        /* we have data to read from a refbuf first */
-        if (client->refbuf->len < len)
-            len = client->refbuf->len;
-        memcpy (buf, client->refbuf->data, len);
-        if (len < client->refbuf->len)
-        {
-            char *ptr = client->refbuf->data;
-            memmove (ptr, ptr+len, client->refbuf->len - len);
-        }
-        client->refbuf->len -= len;
-        return len;
-    }
-    bytes = sock_read_bytes (client->con->sock, buf, len);
-    if (bytes > 0)
-        return bytes;
-
-    if (bytes < 0)
-    {
-        if (sock_recoverable (sock_error()))
-            return -1;
-        WARN0 ("source connection has died");
-    }
-    client->con->error = 1;
-    return -1;
-}
-
-
-void client_send_400(client_t *client, char *message) {
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 400 Bad Request\r\n"
-            "Content-Type: text/html\r\n\r\n"
-            "<b>%s</b>\r\n", message);
-    client->respcode = 400;
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-void client_send_404(client_t *client, char *message) {
-
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 404 File Not Found\r\n"
-            "Content-Type: text/html\r\n\r\n"
-            "<b>%s</b>\r\n", message);
-    client->respcode = 404;
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-
-void client_send_401(client_t *client) {
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 401 Authentication Required\r\n"
-            "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
-            "\r\n"
-            "You need to authenticate\r\n");
-    client->respcode = 401;
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-void client_send_403(client_t *client, const char *reason)
-{
-    if (reason == NULL)
-        reason = "Forbidden";
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 403 %s\r\n\r\n", reason);
-    client->respcode = 403;
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client (client, NULL);
-}
-
-
-/* helper function for sending the data to a client */
-int client_send_bytes (client_t *client, const void *buf, unsigned len)
-{
-    int ret = sock_write_bytes (client->con->sock, buf, len);
-    if (ret < 0 && !sock_recoverable (sock_error()))
-    {
-        DEBUG0 ("Client connection died");
-        client->con->error = 1;
-    }
-    if (ret > 0)
-        client->con->sent_bytes += ret;
-    return ret;
-}
-
-void client_set_queue (client_t *client, refbuf_t *refbuf)
-{
-    refbuf_t *to_release = client->refbuf;
-
-    client->refbuf = refbuf;
-    if (refbuf)
-        refbuf_addref (client->refbuf);
-    client->pos = 0;
-    if (to_release)
-        refbuf_release (to_release);
-}
-

Copied: icecast/tags/icecast-2.3.2/src/client.c (from rev 14206, icecast/trunk/icecast/src/client.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/client.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/client.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,221 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* client.c
+**
+** client interface implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+
+#include "cfgfile.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "format.h"
+#include "stats.h"
+#include "fserve.h"
+
+#include "client.h"
+#include "logging.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+#undef CATMODULE
+#define CATMODULE "client"
+
+/* create a client_t with the provided connection and parser details. Return
+ * 0 on success, -1 if server limit has been reached.  In either case a
+ * client_t is returned just in case a message needs to be returned. Should
+ * be called with global lock held.
+ */
+int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
+{
+    ice_config_t *config;
+    client_t *client = (client_t *)calloc(1, sizeof(client_t));
+    int ret = -1;
+
+    if (client == NULL)
+        abort();
+
+    config = config_get_config ();
+
+    global.clients++;
+    if (config->client_limit < global.clients)
+        WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients);
+    else
+        ret = 0;
+
+    config_release_config ();
+
+    stats_event_args (NULL, "clients", "%d", global.clients);
+    client->con = con;
+    client->parser = parser;
+    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
+    client->refbuf->len = 0; /* force reader code to ignore buffer contents */
+    client->pos = 0;
+    client->write_to_client = format_generic_write_to_client;
+    *c_ptr = client;
+
+    return ret;
+}
+
+void client_destroy(client_t *client)
+{
+    if (client == NULL)
+        return;
+
+    /* release the buffer now, as the buffer could be on the source queue
+     * and may of disappeared after auth completes */
+    if (client->refbuf)
+    {
+        refbuf_release (client->refbuf);
+        client->refbuf = NULL;
+    }
+
+    if (auth_release_listener (client))
+        return;
+
+    /* write log entry if ip is set (some things don't set it, like outgoing 
+     * slave requests
+     */
+    if (client->respcode && client->parser)
+        logging_access(client);
+
+    if (client->con)
+        connection_close(client->con);
+    if (client->parser)
+        httpp_destroy(client->parser);
+
+    global_lock ();
+    global.clients--;
+    stats_event_args (NULL, "clients", "%d", global.clients);
+    global_unlock ();
+
+    /* we need to free client specific format data (if any) */
+    if (client->free_client_data)
+        client->free_client_data (client);
+
+    free(client->username);
+    free(client->password);
+
+    free(client);
+}
+
+
+/* helper function for reading data from a client */
+int client_read_bytes (client_t *client, void *buf, unsigned len)
+{
+    int bytes;
+
+    if (client->refbuf && client->refbuf->len)
+    {
+        /* we have data to read from a refbuf first */
+        if (client->refbuf->len < len)
+            len = client->refbuf->len;
+        memcpy (buf, client->refbuf->data, len);
+        if (len < client->refbuf->len)
+        {
+            char *ptr = client->refbuf->data;
+            memmove (ptr, ptr+len, client->refbuf->len - len);
+        }
+        client->refbuf->len -= len;
+        return len;
+    }
+    bytes = client->con->read (client->con, buf, len);
+
+    if (bytes == -1 && client->con->error)
+        DEBUG0 ("reading from connection has failed");
+
+    return bytes;
+}
+
+
+void client_send_400(client_t *client, char *message) {
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 400 Bad Request\r\n"
+            "Content-Type: text/html\r\n\r\n"
+            "<b>%s</b>\r\n", message);
+    client->respcode = 400;
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+void client_send_404(client_t *client, char *message) {
+
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 404 File Not Found\r\n"
+            "Content-Type: text/html\r\n\r\n"
+            "<b>%s</b>\r\n", message);
+    client->respcode = 404;
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+
+void client_send_401(client_t *client) {
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 401 Authentication Required\r\n"
+            "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
+            "\r\n"
+            "You need to authenticate\r\n");
+    client->respcode = 401;
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+void client_send_403(client_t *client, const char *reason)
+{
+    if (reason == NULL)
+        reason = "Forbidden";
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 403 %s\r\n\r\n", reason);
+    client->respcode = 403;
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client (client, NULL);
+}
+
+
+/* helper function for sending the data to a client */
+int client_send_bytes (client_t *client, const void *buf, unsigned len)
+{
+    int ret = client->con->send (client->con, buf, len);
+
+    if (client->con->error)
+        DEBUG0 ("Client connection died");
+
+    return ret;
+}
+
+void client_set_queue (client_t *client, refbuf_t *refbuf)
+{
+    refbuf_t *to_release = client->refbuf;
+
+    client->refbuf = refbuf;
+    if (refbuf)
+        refbuf_addref (client->refbuf);
+    client->pos = 0;
+    if (to_release)
+        refbuf_release (to_release);
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/client.h
===================================================================
--- icecast/trunk/icecast/src/client.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/client.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,81 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* client.h
-**
-** client data structions and function definitions
-**
-*/
-#ifndef __CLIENT_H__
-#define __CLIENT_H__
-
-#include "connection.h"
-#include "refbuf.h"
-#include "httpp/httpp.h"
-
-typedef struct _client_tag
-{
-    /* the client's connection */
-    connection_t *con;
-    /* the client's http headers */
-    http_parser_t *parser;
-
-    /* http response code for this client */
-    int respcode;
-
-    /* auth completed, 0 not yet, 1 passed */
-    int authenticated;
-
-    /* is client getting intro data */
-    long intro_offset;
-
-    /* where in the queue the client is */
-    refbuf_t *refbuf;
-
-    /* position in first buffer */
-    unsigned long pos;
-
-    /* auth used for this client */
-    struct auth_tag *auth;
-
-    /* Client username, if authenticated */
-    char *username;
-
-    /* Client password, if authenticated */
-    char *password;
-
-    /* Format-handler-specific data for this client */
-    void *format_data;
-
-    /* function to call to release format specific resources */
-    void (*free_client_data)(struct _client_tag *client);
-
-    /* write out data associated with client */
-    int (*write_to_client)(struct _client_tag *client);
-
-    /* function to check if refbuf needs updating */
-    int (*check_buffer)(struct source_tag *source, struct _client_tag *client);
-
-} client_t;
-
-int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser);
-void client_destroy(client_t *client);
-void client_send_504(client_t *client, char *message);
-void client_send_404(client_t *client, char *message);
-void client_send_401(client_t *client);
-void client_send_403(client_t *client, const char *reason);
-void client_send_400(client_t *client, char *message);
-int client_send_bytes (client_t *client, const void *buf, unsigned len);
-int client_read_bytes (client_t *client, void *buf, unsigned len);
-void client_set_queue (client_t *client, refbuf_t *refbuf);
-
-#endif  /* __CLIENT_H__ */

Copied: icecast/tags/icecast-2.3.2/src/client.h (from rev 14306, icecast/trunk/icecast/src/client.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/client.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/client.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,81 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* client.h
+**
+** client data structions and function definitions
+**
+*/
+#ifndef __CLIENT_H__
+#define __CLIENT_H__
+
+#include "connection.h"
+#include "refbuf.h"
+#include "httpp/httpp.h"
+
+typedef struct _client_tag
+{
+    /* the client's connection */
+    connection_t *con;
+    /* the client's http headers */
+    http_parser_t *parser;
+
+    /* http response code for this client */
+    int respcode;
+
+    /* auth completed, 0 not yet, 1 passed */
+    int authenticated;
+
+    /* is client getting intro data */
+    long intro_offset;
+
+    /* where in the queue the client is */
+    refbuf_t *refbuf;
+
+    /* position in first buffer */
+    unsigned int pos;
+
+    /* auth used for this client */
+    struct auth_tag *auth;
+
+    /* Client username, if authenticated */
+    char *username;
+
+    /* Client password, if authenticated */
+    char *password;
+
+    /* Format-handler-specific data for this client */
+    void *format_data;
+
+    /* function to call to release format specific resources */
+    void (*free_client_data)(struct _client_tag *client);
+
+    /* write out data associated with client */
+    int (*write_to_client)(struct _client_tag *client);
+
+    /* function to check if refbuf needs updating */
+    int (*check_buffer)(struct source_tag *source, struct _client_tag *client);
+
+} client_t;
+
+int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser);
+void client_destroy(client_t *client);
+void client_send_504(client_t *client, char *message);
+void client_send_404(client_t *client, char *message);
+void client_send_401(client_t *client);
+void client_send_403(client_t *client, const char *reason);
+void client_send_400(client_t *client, char *message);
+int client_send_bytes (client_t *client, const void *buf, unsigned len);
+int client_read_bytes (client_t *client, void *buf, unsigned len);
+void client_set_queue (client_t *client, refbuf_t *refbuf);
+
+#endif  /* __CLIENT_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/compat.h
===================================================================
--- icecast/trunk/icecast/src/compat.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/compat.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,56 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __COMPAT_H__
-#define __COMPAT_H__
-
-/* compat.h
- * 
- * This file contains most of the ugliness for header portability
- * and common types across various systems like Win32, Linux and
- * Solaris.
- */
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <unistd.h>
-#endif
-
-
-/* Make sure we define 64 bit types */
-#ifdef _WIN32
-#  define PATH_SEPARATOR "\\"
-#  define size_t unsigned int
-#  define ssize_t int
-#  define int64_t __int64
-#  define uint64_t unsigned __int64
-#  define uint32_t unsigned int
-#else
-#  define PATH_SEPARATOR "/"
-#  if defined(HAVE_STDINT_H)
-#    include <stdint.h>
-#  elif defined(HAVE_INTTYPES_H)
-#    include <inttypes.h>
-#  endif
-#endif
-
-#ifdef _WIN32
-#define FORMAT_INT64      "%I64d"
-#define FORMAT_UINT64     "%I64u"
-#else
-#define FORMAT_INT64      "%lld"
-#define FORMAT_UINT64     "%llu"
-#endif
-
-#endif /* __COMPAT_H__ */
-

Copied: icecast/tags/icecast-2.3.2/src/compat.h (from rev 14223, icecast/trunk/icecast/src/compat.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/compat.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/compat.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,57 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __COMPAT_H__
+#define __COMPAT_H__
+
+/* compat.h
+ * 
+ * This file contains most of the ugliness for header portability
+ * and common types across various systems like Win32, Linux and
+ * Solaris.
+ */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+#  include <sys/time.h>
+#  include <time.h>
+#else
+#  ifdef HAVE_SYS_TIME_H
+#    include <sys/time.h>
+#  else
+#    include <time.h>
+#  endif
+#endif
+
+/* Make sure we define 64 bit types */
+#ifdef _WIN32
+#  define PATH_SEPARATOR "\\"
+#  define size_t unsigned int
+#  define ssize_t int
+#  define int64_t __int64
+#  define uint64_t unsigned __int64
+#  define uint32_t unsigned int
+#  define PRIu64  "I64u"
+#else
+#  define PATH_SEPARATOR "/"
+#  if defined(HAVE_INTTYPES_H)
+#    include <inttypes.h>
+#  elif defined(HAVE_STDINT_H)
+#    include <stdint.h>
+#  endif
+#endif
+
+#endif /* __COMPAT_H__ */
+

Deleted: icecast/tags/icecast-2.3.2/src/connection.c
===================================================================
--- icecast/trunk/icecast/src/connection.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/connection.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1085 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#ifdef HAVE_POLL
-#include <sys/poll.h>
-#endif
-
-#ifndef _WIN32
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#else
-#include <winsock2.h>
-#define snprintf _snprintf
-#define strcasecmp stricmp
-#define strncasecmp strnicmp
-#endif
-
-#include "compat.h"
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "net/sock.h"
-#include "httpp/httpp.h"
-
-#include "cfgfile.h"
-#include "global.h"
-#include "util.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "logging.h"
-#include "xslt.h"
-#include "fserve.h"
-#include "sighandler.h"
-
-#include "yp.h"
-#include "source.h"
-#include "format.h"
-#include "format_mp3.h"
-#include "event.h"
-#include "admin.h"
-#include "auth.h"
-
-#define CATMODULE "connection"
-
-/* Two different major types of source authentication.
-   Shoutcast style is used only by the Shoutcast DSP
-   and is a crazy version of HTTP.  It looks like :
-     Source Client -> Connects to port + 1
-     Source Client -> sends encoder password (plaintext)\r\n
-     Icecast -> reads encoder password, if ok, sends OK2\r\n, else disconnects
-     Source Client -> reads OK2\r\n, then sends http-type request headers
-                      that contain the stream details (icy-name, etc..)
-     Icecast -> reads headers, stores them
-     Source Client -> starts sending MP3 data
-     Source Client -> periodically updates metadata via admin.cgi call
-
-   Icecast auth style uses HTTP and Basic Authorization.
-*/
-#define SHOUTCAST_SOURCE_AUTH 1
-#define ICECAST_SOURCE_AUTH 0
-
-typedef struct client_queue_tag {
-    client_t *client;
-    int offset;
-    int stream_offset;
-    int shoutcast;
-    struct client_queue_tag *next;
-} client_queue_t;
-
-typedef struct _thread_queue_tag {
-    thread_type *thread_id;
-    struct _thread_queue_tag *next;
-} thread_queue_t;
-
-static mutex_t _connection_mutex;
-static volatile unsigned long _current_id = 0;
-static int _initialized = 0;
-static thread_type *tid;
-
-static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
-static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
-static mutex_t _con_queue_mutex;
-static mutex_t _req_queue_mutex;
-
-rwlock_t _source_shutdown_rwlock;
-
-static void *_handle_connection(void *arg);
-
-void connection_initialize(void)
-{
-    if (_initialized) return;
-    
-    thread_mutex_create(&_connection_mutex);
-    thread_mutex_create(&_con_queue_mutex);
-    thread_mutex_create(&_req_queue_mutex);
-    thread_mutex_create(&move_clients_mutex);
-    thread_rwlock_create(&_source_shutdown_rwlock);
-    thread_cond_create(&global.shutdown_cond);
-    _req_queue = NULL;
-    _req_queue_tail = &_req_queue;
-    _con_queue = NULL;
-    _con_queue_tail = &_con_queue;
-
-    _initialized = 1;
-}
-
-void connection_shutdown(void)
-{
-    if (!_initialized) return;
-    
-    thread_cond_destroy(&global.shutdown_cond);
-    thread_rwlock_destroy(&_source_shutdown_rwlock);
-    thread_mutex_destroy(&_con_queue_mutex);
-    thread_mutex_destroy(&_req_queue_mutex);
-    thread_mutex_destroy(&_connection_mutex);
-    thread_mutex_destroy(&move_clients_mutex);
-
-    _initialized = 0;
-}
-
-static unsigned long _next_connection_id(void)
-{
-    unsigned long id;
-
-    thread_mutex_lock(&_connection_mutex);
-    id = _current_id++;
-    thread_mutex_unlock(&_connection_mutex);
-
-    return id;
-}
-
-connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
-{
-    connection_t *con;
-    con = (connection_t *)calloc(1, sizeof(connection_t));
-    if (con)
-    {
-        con->sock = sock;
-        con->serversock = serversock;
-        con->con_time = time(NULL);
-        con->id = _next_connection_id();
-        con->ip = ip;
-    }
-
-    return con;
-}
-
-static int wait_for_serversock(int timeout)
-{
-#ifdef HAVE_POLL
-    struct pollfd ufds[MAX_LISTEN_SOCKETS];
-    int i, ret;
-
-    for(i=0; i < global.server_sockets; i++) {
-        ufds[i].fd = global.serversock[i];
-        ufds[i].events = POLLIN;
-        ufds[i].revents = 0;
-    }
-
-    ret = poll(ufds, global.server_sockets, timeout);
-    if(ret < 0) {
-        return -2;
-    }
-    else if(ret == 0) {
-        return -1;
-    }
-    else {
-        int dst;
-        for(i=0; i < global.server_sockets; i++) {
-            if(ufds[i].revents & POLLIN)
-                return ufds[i].fd;
-            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
-            {
-                if (ufds[i].revents & (POLLHUP|POLLERR))
-                {
-                    close (global.serversock[i]);
-                    WARN0("Had to close a listening socket");
-                }
-                global.serversock[i] = -1;
-            }
-        }
-        /* remove any closed sockets */
-        for(i=0, dst=0; i < global.server_sockets; i++)
-        {
-            if (global.serversock[i] == -1)
-                continue;
-            if (i!=dst)
-                global.serversock[dst] = global.serversock[i];
-            dst++;
-        }
-        global.server_sockets = dst;
-        return -1;
-    }
-#else
-    fd_set rfds;
-    struct timeval tv, *p=NULL;
-    int i, ret;
-    int max = -1;
-
-    FD_ZERO(&rfds);
-
-    for(i=0; i < global.server_sockets; i++) {
-        FD_SET(global.serversock[i], &rfds);
-        if(global.serversock[i] > max)
-            max = global.serversock[i];
-    }
-
-    if(timeout >= 0) {
-        tv.tv_sec = timeout/1000;
-        tv.tv_usec = (timeout % 1000) * 1000;
-        p = &tv;
-    }
-
-    ret = select(max+1, &rfds, NULL, NULL, p);
-    if(ret < 0) {
-        return -2;
-    }
-    else if(ret == 0) {
-        return -1;
-    }
-    else {
-        for(i=0; i < global.server_sockets; i++) {
-            if(FD_ISSET(global.serversock[i], &rfds))
-                return global.serversock[i];
-        }
-        return -1; /* Should be impossible, stop compiler warnings */
-    }
-#endif
-}
-
-static connection_t *_accept_connection(void)
-{
-    int sock;
-    connection_t *con;
-    char *ip;
-    int serversock; 
-
-    serversock = wait_for_serversock(100);
-    if(serversock < 0)
-        return NULL;
-
-    /* malloc enough room for a full IP address (including ipv6) */
-    ip = (char *)malloc(MAX_ADDR_LEN);
-
-    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
-    if (sock >= 0)
-    {
-        /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
-        if (strncmp (ip, "::ffff:", 7) == 0)
-            memmove (ip, ip+7, strlen (ip+7)+1);
-
-        con = connection_create (sock, serversock, ip);
-        if (con)
-            return con;
-        sock_close (sock);
-    }
-    else
-    {
-        if (!sock_recoverable(sock_error()))
-        {
-            WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
-            thread_sleep (500000);
-        }
-    }
-    free(ip);
-    return NULL;
-}
-
-
-/* add client to connection queue. At this point some header information
- * has been collected, so we now pass it onto the connection thread for
- * further processing
- */
-static void _add_connection (client_queue_t *node)
-{
-    thread_mutex_lock (&_con_queue_mutex);
-    *_con_queue_tail = node;
-    _con_queue_tail = (volatile client_queue_t **)&node->next;
-    thread_mutex_unlock (&_con_queue_mutex);
-}
-
-
-/* this returns queued clients for the connection thread. headers are
- * already provided, but need to be parsed.
- */
-static client_queue_t *_get_connection(void)
-{
-    client_queue_t *node = NULL;
-
-    /* common case, no new connections so don't bother taking locks */
-    if (_con_queue)
-    {
-        thread_mutex_lock (&_con_queue_mutex);
-        node = (client_queue_t *)_con_queue;
-        _con_queue = node->next;
-        if (_con_queue == NULL)
-            _con_queue_tail = &_con_queue;
-        thread_mutex_unlock (&_con_queue_mutex);
-        node->next = NULL;
-    }
-    return node;
-}
-
-
-/* run along queue checking for any data that has come in or a timeout */
-static void process_request_queue (void)
-{
-    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
-    ice_config_t *config = config_get_config ();
-    int timeout = config->header_timeout;
-    config_release_config();
-
-    while (*node_ref)
-    {
-        client_queue_t *node = *node_ref;
-        client_t *client = node->client;
-        int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
-        char *buf = client->refbuf->data + node->offset;
-
-        if (len > 0)
-        {
-            if (client->con->con_time + timeout <= time(NULL))
-                len = 0;
-            else
-                len = client_read_bytes (client, buf, len);
-        }
-
-        if (len > 0)
-        {
-            int pass_it = 1;
-            char *ptr;
-
-            /* handle \n, \r\n and nsvcap which for some strange reason has
-             * EOL as \r\r\n */
-            node->offset += len;
-            client->refbuf->data [node->offset] = '\000';
-            do
-            {
-                if (node->shoutcast == 1)
-                {
-                    /* password line */
-                    if (strstr (client->refbuf->data, "\r\r\n") != NULL)
-                        break;
-                    if (strstr (client->refbuf->data, "\r\n") != NULL)
-                        break;
-                    if (strstr (client->refbuf->data, "\n") != NULL)
-                        break;
-                }
-                /* stream_offset refers to the start of any data sent after the
-                 * http style headers, we don't want to lose those */
-                ptr = strstr (client->refbuf->data, "\r\r\n\r\r\n");
-                if (ptr)
-                {
-                    node->stream_offset = (ptr+6) - client->refbuf->data;
-                    break;
-                }
-                ptr = strstr (client->refbuf->data, "\r\n\r\n");
-                if (ptr)
-                {
-                    node->stream_offset = (ptr+4) - client->refbuf->data;
-                    break;
-                }
-                ptr = strstr (client->refbuf->data, "\n\n");
-                if (ptr)
-                {
-                    node->stream_offset = (ptr+2) - client->refbuf->data;
-                    break;
-                }
-                pass_it = 0;
-            } while (0);
-
-            if (pass_it)
-            {
-                thread_mutex_lock (&_req_queue_mutex);
-                if ((client_queue_t **)_req_queue_tail == &(node->next))
-                    _req_queue_tail = (volatile client_queue_t **)node_ref;
-                *node_ref = node->next;
-                node->next = NULL;
-                thread_mutex_unlock (&_req_queue_mutex);
-                _add_connection (node);
-                continue;
-            }
-        }
-        else
-        {
-            if (len == 0 || client->con->error)
-            {
-                thread_mutex_lock (&_req_queue_mutex);
-                if ((client_queue_t **)_req_queue_tail == &node->next)
-                    _req_queue_tail = (volatile client_queue_t **)node_ref;
-                *node_ref = node->next;
-                thread_mutex_unlock (&_req_queue_mutex);
-                client_destroy (client);
-                free (node);
-                continue;
-            }
-        }
-        node_ref = &node->next;
-    }
-}
-
-
-/* add node to the queue of requests. This is where the clients are when
- * initial http details are read.
- */
-static void _add_request_queue (client_queue_t *node)
-{
-    thread_mutex_lock (&_req_queue_mutex);
-    *_req_queue_tail = node;
-    _req_queue_tail = (volatile client_queue_t **)&node->next;
-    thread_mutex_unlock (&_req_queue_mutex);
-}
-
-
-void connection_accept_loop(void)
-{
-    connection_t *con;
-
-    tid = thread_create ("connection thread", _handle_connection, NULL, THREAD_ATTACHED);
-
-    while (global.running == ICE_RUNNING)
-    {
-        con = _accept_connection();
-
-        if (con)
-        {
-            client_queue_t *node;
-            ice_config_t *config;
-            int i;
-            client_t *client = NULL;
-
-            global_lock();
-            if (client_create (&client, con, NULL) < 0)
-            {
-                global_unlock();
-                client_send_403 (client, "Icecast connection limit reached");
-                continue;
-            }
-            global_unlock();
-
-            /* setup client for reading incoming http */
-            client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000';
-
-            node = calloc (1, sizeof (client_queue_t));
-            if (node == NULL)
-            {
-                client_destroy (client);
-                continue;
-            }
-            node->client = client;
-
-            /* Check for special shoutcast compatability processing */
-            config = config_get_config();
-            for (i = 0; i < global.server_sockets; i++)
-            {
-                if (global.serversock[i] == con->serversock)
-                {
-                    if (config->listeners[i].shoutcast_compat)
-                        node->shoutcast = 1;
-                }
-            }
-            config_release_config(); 
-
-            sock_set_blocking (client->con->sock, SOCK_NONBLOCK);
-            sock_set_nodelay (client->con->sock);
-
-            _add_request_queue (node);
-            stats_event_inc (NULL, "connections");
-        }
-        process_request_queue ();
-    }
-
-    /* Give all the other threads notification to shut down */
-    thread_cond_broadcast(&global.shutdown_cond);
-
-    if (tid)
-        thread_join (tid);
-
-    /* wait for all the sources to shutdown */
-    thread_rwlock_wlock(&_source_shutdown_rwlock);
-    thread_rwlock_unlock(&_source_shutdown_rwlock);
-}
-
-
-/* Called when activating a source. Verifies that the source count is not
- * exceeded and applies any initial parameters.
- */
-int connection_complete_source (source_t *source, int response)
-{
-    ice_config_t *config = config_get_config();
-
-    global_lock ();
-    DEBUG1 ("sources count is %d", global.sources);
-
-    if (global.sources < config->source_limit)
-    {
-        char *contenttype;
-        mount_proxy *mountinfo;
-        format_type_t format_type;
-
-        /* setup format handler */
-        contenttype = httpp_getvar (source->parser, "content-type");
-        if (contenttype != NULL)
-        {
-            format_type = format_get_type (contenttype);
-
-            if (format_type == FORMAT_ERROR)
-            {
-                global_unlock();
-                config_release_config();
-                if (response)
-                {
-                    client_send_403 (source->client, "Content-type not supported");
-                    source->client = NULL;
-                }
-                WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
-                return -1;
-            }
-        }
-        else
-        {
-            WARN0("No content-type header, falling back to backwards compatibility mode "
-                    "for icecast 1.x relays. Assuming content is mp3.");
-            format_type = FORMAT_TYPE_GENERIC;
-        }
-
-        if (format_get_plugin (format_type, source) < 0)
-        {
-            global_unlock();
-            config_release_config();
-            if (response)
-            {
-                client_send_403 (source->client, "internal format allocation problem");
-                source->client = NULL;
-            }
-            WARN1 ("plugin format failed for \"%s\"", source->mount);
-            return -1;
-        }
-
-        global.sources++;
-        stats_event_args (NULL, "sources", "%d", global.sources);
-        global_unlock();
-
-        source->running = 1;
-        mountinfo = config_find_mount (config, source->mount);
-        source_update_settings (config, source, mountinfo);
-        config_release_config();
-        slave_rebuild_mounts();
-
-        source->shutdown_rwlock = &_source_shutdown_rwlock;
-        DEBUG0 ("source is ready to start");
-
-        return 0;
-    }
-    WARN1("Request to add source when maximum source limit "
-            "reached %d", global.sources);
-
-    global_unlock();
-    config_release_config();
-
-    if (response)
-    {
-        client_send_403 (source->client, "too many sources connected");
-        source->client = NULL;
-    }
-
-    return -1;
-}
-
-
-static int _check_pass_http(http_parser_t *parser, 
-        char *correctuser, char *correctpass)
-{
-    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
-    char *header = httpp_getvar(parser, "authorization");
-    char *userpass, *tmp;
-    char *username, *password;
-
-    if(header == NULL)
-        return 0;
-
-    if(strncmp(header, "Basic ", 6))
-        return 0;
-
-    userpass = util_base64_decode(header+6);
-    if(userpass == NULL) {
-        WARN1("Base64 decode of Authorization header \"%s\" failed",
-                header+6);
-        return 0;
-    }
-
-    tmp = strchr(userpass, ':');
-    if(!tmp) {
-        free(userpass);
-        return 0;
-    }
-    *tmp = 0;
-    username = userpass;
-    password = tmp+1;
-
-    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
-        free(userpass);
-        return 0;
-    }
-    free(userpass);
-
-    return 1;
-}
-
-static int _check_pass_icy(http_parser_t *parser, char *correctpass)
-{
-    char *password;
-
-    password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
-    if(!password)
-        return 0;
-
-    if (strcmp(password, correctpass))
-        return 0;
-    else
-        return 1;
-}
-
-static int _check_pass_ice(http_parser_t *parser, char *correctpass)
-{
-    char *password;
-
-    password = httpp_getvar(parser, "ice-password");
-    if(!password)
-        password = "";
-
-    if (strcmp(password, correctpass))
-        return 0;
-    else
-        return 1;
-}
-
-int connection_check_admin_pass(http_parser_t *parser)
-{
-    int ret;
-    ice_config_t *config = config_get_config();
-    char *pass = config->admin_password;
-    char *user = config->admin_username;
-    char *protocol;
-
-    if(!pass || !user) {
-        config_release_config();
-        return 0;
-    }
-
-    protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
-    if (protocol && strcmp (protocol, "ICY") == 0)
-        ret = _check_pass_icy (parser, pass);
-    else 
-        ret = _check_pass_http (parser, user, pass);
-    config_release_config();
-    return ret;
-}
-
-int connection_check_relay_pass(http_parser_t *parser)
-{
-    int ret;
-    ice_config_t *config = config_get_config();
-    char *pass = config->relay_password;
-    char *user = config->relay_username;
-
-    if(!pass || !user) {
-        config_release_config();
-        return 0;
-    }
-
-    ret = _check_pass_http(parser, user, pass);
-    config_release_config();
-    return ret;
-}
-
-int connection_check_source_pass(http_parser_t *parser, const char *mount)
-{
-    ice_config_t *config = config_get_config();
-    char *pass = config->source_password;
-    char *user = "source";
-    int ret;
-    int ice_login = config->ice_login;
-    char *protocol;
-
-    mount_proxy *mountinfo = config_find_mount (config, mount);
-
-    if (mountinfo)
-    {
-        if (mountinfo->password)
-            pass = mountinfo->password;
-        if (mountinfo->username)
-            user = mountinfo->username;
-    }
-
-    if(!pass) {
-        WARN0("No source password set, rejecting source");
-        config_release_config();
-        return 0;
-    }
-
-    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
-    if(protocol != NULL && !strcmp(protocol, "ICY")) {
-        ret = _check_pass_icy(parser, pass);
-    }
-    else {
-        ret = _check_pass_http(parser, user, pass);
-        if(!ret && ice_login)
-        {
-            ret = _check_pass_ice(parser, pass);
-            if(ret)
-                WARN0("Source is using deprecated icecast login");
-        }
-    }
-    config_release_config();
-    return ret;
-}
-
-
-static void _handle_source_request (client_t *client, char *uri, int auth_style)
-{
-    source_t *source;
-
-    INFO1("Source logging in at mountpoint \"%s\"", uri);
-
-    if (uri[0] != '/')
-    {
-        WARN0 ("source mountpoint not starting with /");
-        client_send_401 (client);
-        return;
-    }
-    if (auth_style == ICECAST_SOURCE_AUTH) {
-        if (connection_check_source_pass (client->parser, uri) == 0)
-        {
-            /* We commonly get this if the source client is using the wrong
-             * protocol: attempt to diagnose this and return an error
-             */
-            /* TODO: Do what the above comment says */
-            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
-            client_send_401(client);
-            return;
-        }
-    }
-    source = source_reserve (uri);
-    if (source)
-    {
-        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
-            source->shoutcast_compat = 1;
-        }
-        source->client = client;
-        source->parser = client->parser;
-        source->con = client->con;
-        if (connection_complete_source (source, 1) < 0)
-        {
-            source_clear_source (source);
-            source_free_source (source);
-        }
-        else
-        {
-            refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
-            client->respcode = 200;
-            snprintf (ok->data, PER_CLIENT_REFBUF_SIZE,
-                    "HTTP/1.0 200 OK\r\n\r\n");
-            ok->len = strlen (ok->data);
-            /* we may have unprocessed data read in, so don't overwrite it */
-            ok->associated = client->refbuf;
-            client->refbuf = ok;
-            fserve_add_client_callback (client, source_client_callback, source);
-        }
-    }
-    else
-    {
-        client_send_403 (client, "Mountpoint in use");
-        WARN1 ("Mountpoint %s in use", uri);
-    }
-}
-
-
-static void _handle_stats_request (client_t *client, char *uri)
-{
-    stats_event_inc(NULL, "stats_connections");
-
-    if (connection_check_admin_pass (client->parser) == 0)
-    {
-        client_send_401 (client);
-        ERROR0("Bad password for stats connection");
-        return;
-    }
-
-    client->respcode = 200;
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 200 OK\r\n\r\n");
-    client->refbuf->len = strlen (client->refbuf->data);
-    fserve_add_client_callback (client, stats_callback, NULL);
-}
-
-static void _handle_get_request (client_t *client, char *passed_uri)
-{
-    int fileserve;
-    int port;
-    int i;
-    char *serverhost = NULL;
-    int serverport = 0;
-    aliases *alias;
-    ice_config_t *config;
-    char *uri = passed_uri;
-
-    config = config_get_config();
-    fileserve = config->fileserve;
-    port = config->port;
-    for(i = 0; i < global.server_sockets; i++) {
-        if(global.serversock[i] == client->con->serversock) {
-            serverhost = config->listeners[i].bind_address;
-            serverport = config->listeners[i].port;
-            break;
-        }
-    }
-    alias = config->aliases;
-
-    /* there are several types of HTTP GET clients
-    ** media clients, which are looking for a source (eg, URI = /stream.ogg)
-    ** stats clients, which are looking for /admin/stats.xml
-    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
-    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
-    ** we need to handle the latter two before the former, as the latter two
-    ** aren't subject to the limits.
-    */
-    /* TODO: add GUID-xxxxxx */
-
-    /* Handle aliases */
-    while(alias) {
-        if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
-            uri = strdup (alias->destination);
-            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
-            break;
-        }
-        alias = alias->next;
-    }
-    config_release_config();
-
-    stats_event_inc(NULL, "client_connections");
-
-    /* Dispatch all admin requests */
-    if ((strcmp(uri, "/admin.cgi") == 0) ||
-        (strncmp(uri, "/admin/", 7) == 0)) {
-        admin_handle_request(client, uri);
-        if (uri != passed_uri) free (uri);
-        return;
-    }
-
-    /* Here we are parsing the URI request to see
-    ** if the extension is .xsl, if so, then process
-    ** this request as an XSLT request
-    */
-    if (util_check_valid_extension (uri) == XSLT_CONTENT)
-    {
-        /* If the file exists, then transform it, otherwise, write a 404 */
-        DEBUG0("Stats request, sending XSL transformed stats");
-        stats_transform_xslt (client, uri);
-        if (uri != passed_uri) free (uri);
-        return;
-    }
-
-    add_client (uri, client);
-    if (uri != passed_uri) free (uri);
-}
-
-static void _handle_shoutcast_compatible (client_queue_t *node)
-{
-    char *http_compliant;
-    int http_compliant_len = 0;
-    http_parser_t *parser;
-    ice_config_t *config = config_get_config ();
-    char *shoutcast_mount;
-    client_t *client = node->client;
-
-    if (node->shoutcast == 1)
-    {
-        char *source_password, *ptr, *headers;
-        mount_proxy *mountinfo = config_find_mount (config, config->shoutcast_mount);
-
-        if (mountinfo && mountinfo->password)
-            source_password = strdup (mountinfo->password);
-        else
-            source_password = strdup (config->source_password);
-        config_release_config();
-
-        /* Get rid of trailing \r\n or \n after password */
-        ptr = strstr (client->refbuf->data, "\r\r\n");
-        if (ptr)
-            headers = ptr+3;
-        else
-        {
-            ptr = strstr (client->refbuf->data, "\r\n");
-            if (ptr)
-                headers = ptr+2;
-            else
-            {
-                ptr = strstr (client->refbuf->data, "\n");
-                if (ptr)
-                    headers = ptr+1;
-            }
-        }
-
-        if (ptr == NULL)
-        {
-            client_destroy (client);
-            free (source_password);
-            free (node);
-            return;
-        }
-        *ptr = '\0';
-
-        if (strcmp (client->refbuf->data, source_password) == 0)
-        {
-            client->respcode = 200;
-            /* send this non-blocking but if there is only a partial write
-             * then leave to header timeout */
-            sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
-            node->offset -= (headers - client->refbuf->data);
-            memmove (client->refbuf->data, headers, node->offset+1);
-            node->shoutcast = 2;
-            /* we've checked the password, now send it back for reading headers */
-            _add_request_queue (node);
-            free (source_password);
-            return;
-        }
-        else
-            INFO1 ("password does not match \"%s\"", client->refbuf->data);
-        client_destroy (client);
-        free (node);
-        return;
-    }
-    shoutcast_mount = strdup (config->shoutcast_mount);
-    config_release_config();
-    /* Here we create a valid HTTP request based of the information
-       that was passed in via the non-HTTP style protocol above. This
-       means we can use some of our existing code to handle this case */
-    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
-    http_compliant = (char *)calloc(1, http_compliant_len);
-    snprintf (http_compliant, http_compliant_len,
-            "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
-    parser = httpp_create_parser();
-    httpp_initialize(parser, NULL);
-    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
-    {
-        /* we may have more than just headers, so prepare for it */
-        if (node->stream_offset == node->offset)
-            client->refbuf->len = 0;
-        else
-        {
-            char *ptr = client->refbuf->data;
-            client->refbuf->len = node->offset - node->stream_offset;
-            memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
-        }
-        client->parser = parser;
-        _handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
-    }
-    else {
-        httpp_destroy (parser);
-        client_destroy (client);
-    }
-    free (http_compliant);
-    free (shoutcast_mount);
-    free (node);
-    return;
-}
-
-
-/* Connection thread. Here we take clients off the connection queue and check
- * the contents provided. We set up the parser then hand off to the specific
- * request handler.
- */
-static void *_handle_connection(void *arg)
-{
-    http_parser_t *parser;
-    char *rawuri, *uri;
-
-    while (global.running == ICE_RUNNING) {
-
-        client_queue_t *node = _get_connection();
-
-        if (node)
-        {
-            client_t *client = node->client;
-
-            /* Check for special shoutcast compatability processing */
-            if (node->shoutcast) 
-            {
-                _handle_shoutcast_compatible (node);
-                continue;
-            }
-
-            /* process normal HTTP headers */
-            parser = httpp_create_parser();
-            httpp_initialize(parser, NULL);
-            client->parser = parser;
-            if (httpp_parse (parser, client->refbuf->data, node->offset))
-            {
-                /* we may have more than just headers, so prepare for it */
-                if (node->stream_offset == node->offset)
-                    client->refbuf->len = 0;
-                else
-                {
-                    char *ptr = client->refbuf->data;
-                    client->refbuf->len = node->offset - node->stream_offset;
-                    memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
-                }
-                free (node);
-                
-                if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
-                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
-                    ERROR0("Bad HTTP protocol detected");
-                    client_destroy (client);
-                    continue;
-                }
-
-                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
-                uri = util_normalise_uri(rawuri);
-
-                if (uri == NULL)
-                {
-                    client_destroy (client);
-                    continue;
-                }
-
-                if (parser->req_type == httpp_req_source) {
-                    _handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
-                }
-                else if (parser->req_type == httpp_req_stats) {
-                    _handle_stats_request (client, uri);
-                }
-                else if (parser->req_type == httpp_req_get) {
-                    _handle_get_request (client, uri);
-                }
-                else {
-                    ERROR0("Wrong request type from client");
-                    client_send_400 (client, "unknown request");
-                }
-
-                free(uri);
-            } 
-            else
-            {
-                free (node);
-                ERROR0("HTTP request parsing failed");
-                client_destroy (client);
-            }
-            continue;
-        }
-        thread_sleep (50000);
-    }
-    DEBUG0 ("Connection thread done");
-
-    return NULL;
-}
-
-void connection_close(connection_t *con)
-{
-    sock_close(con->sock);
-    if (con->ip) free(con->ip);
-    if (con->host) free(con->host);
-    free(con);
-}

Copied: icecast/tags/icecast-2.3.2/src/connection.c (from rev 14219, icecast/trunk/icecast/src/connection.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/connection.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/connection.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1458 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#else
+#include <winsock2.h>
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#include "compat.h"
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "net/sock.h"
+#include "httpp/httpp.h"
+
+#include "cfgfile.h"
+#include "global.h"
+#include "util.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "xslt.h"
+#include "fserve.h"
+#include "sighandler.h"
+
+#include "yp.h"
+#include "source.h"
+#include "format.h"
+#include "format_mp3.h"
+#include "event.h"
+#include "admin.h"
+#include "auth.h"
+
+#define CATMODULE "connection"
+
+/* Two different major types of source authentication.
+   Shoutcast style is used only by the Shoutcast DSP
+   and is a crazy version of HTTP.  It looks like :
+     Source Client -> Connects to port + 1
+     Source Client -> sends encoder password (plaintext)\r\n
+     Icecast -> reads encoder password, if ok, sends OK2\r\n, else disconnects
+     Source Client -> reads OK2\r\n, then sends http-type request headers
+                      that contain the stream details (icy-name, etc..)
+     Icecast -> reads headers, stores them
+     Source Client -> starts sending MP3 data
+     Source Client -> periodically updates metadata via admin.cgi call
+
+   Icecast auth style uses HTTP and Basic Authorization.
+*/
+#define SHOUTCAST_SOURCE_AUTH 1
+#define ICECAST_SOURCE_AUTH 0
+
+typedef struct client_queue_tag {
+    client_t *client;
+    int offset;
+    int stream_offset;
+    int shoutcast;
+    char *shoutcast_mount;
+    struct client_queue_tag *next;
+} client_queue_t;
+
+typedef struct _thread_queue_tag {
+    thread_type *thread_id;
+    struct _thread_queue_tag *next;
+} thread_queue_t;
+
+typedef struct
+{
+    char *filename;
+    time_t file_recheck;
+    time_t file_mtime;
+    avl_tree *contents;
+} cache_file_contents;
+
+static mutex_t _connection_mutex;
+static volatile unsigned long _current_id = 0;
+static int _initialized = 0;
+static thread_type *tid;
+
+static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
+static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
+static mutex_t _con_queue_mutex;
+static mutex_t _req_queue_mutex;
+
+static int ssl_ok;
+#ifdef HAVE_OPENSSL
+static SSL_CTX *ssl_ctx;
+#endif
+
+/* filtering client connection based on IP */
+cache_file_contents banned_ip, allowed_ip;
+
+rwlock_t _source_shutdown_rwlock;
+
+static void *_handle_connection(void *arg);
+
+static int compare_ip (void *arg, void *a, void *b)
+{
+    const char *ip = (const char *)a;
+    const char *pattern = (const char *)b;
+
+    return strcmp (pattern, ip);
+}
+
+
+static int free_filtered_ip (void*x)
+{
+    free (x);
+    return 1;
+}
+
+
+void connection_initialize(void)
+{
+    if (_initialized) return;
+    
+    thread_mutex_create(&_connection_mutex);
+    thread_mutex_create(&_con_queue_mutex);
+    thread_mutex_create(&_req_queue_mutex);
+    thread_mutex_create(&move_clients_mutex);
+    thread_rwlock_create(&_source_shutdown_rwlock);
+    thread_cond_create(&global.shutdown_cond);
+    _req_queue = NULL;
+    _req_queue_tail = &_req_queue;
+    _con_queue = NULL;
+    _con_queue_tail = &_con_queue;
+
+    banned_ip.contents = NULL;
+    banned_ip.file_mtime = 0;
+
+    allowed_ip.contents = NULL;
+    allowed_ip.file_mtime = 0;
+
+    _initialized = 1;
+}
+
+void connection_shutdown(void)
+{
+    if (!_initialized) return;
+    
+#ifdef HAVE_OPENSSL
+    SSL_CTX_free (ssl_ctx);
+#endif
+    if (banned_ip.contents)  avl_tree_free (banned_ip.contents, free_filtered_ip);
+    if (allowed_ip.contents) avl_tree_free (allowed_ip.contents, free_filtered_ip);
+ 
+    thread_cond_destroy(&global.shutdown_cond);
+    thread_rwlock_destroy(&_source_shutdown_rwlock);
+    thread_mutex_destroy(&_con_queue_mutex);
+    thread_mutex_destroy(&_req_queue_mutex);
+    thread_mutex_destroy(&_connection_mutex);
+    thread_mutex_destroy(&move_clients_mutex);
+
+    _initialized = 0;
+}
+
+static unsigned long _next_connection_id(void)
+{
+    unsigned long id;
+
+    thread_mutex_lock(&_connection_mutex);
+    id = _current_id++;
+    thread_mutex_unlock(&_connection_mutex);
+
+    return id;
+}
+
+
+#ifdef HAVE_OPENSSL
+static void get_ssl_certificate (ice_config_t *config)
+{
+    SSL_METHOD *method;
+    ssl_ok = 0;
+
+    SSL_load_error_strings();                /* readable error messages */
+    SSL_library_init();                      /* initialize library */
+
+    method = SSLv23_server_method();
+    ssl_ctx = SSL_CTX_new (method);
+
+    do
+    {
+        if (config->cert_file == NULL)
+            break;
+        if (SSL_CTX_use_certificate_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0)
+        {
+            WARN1 ("Invalid cert file %s", config->cert_file);
+            break;
+        }
+        if (SSL_CTX_use_PrivateKey_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0)
+        {
+            WARN1 ("Invalid private key file %s", config->cert_file);
+            break;
+        }
+        if (!SSL_CTX_check_private_key (ssl_ctx))
+        {
+            ERROR1 ("Invalid %s - Private key does not match cert public key", config->cert_file);
+            break;
+        }
+        ssl_ok = 1;
+        INFO1 ("SSL certificate found at %s", config->cert_file);
+        return;
+    } while (0);
+    INFO0 ("No SSL capability on any configured ports");
+}
+
+
+/* handlers for reading and writing a connection_t when there is ssl
+ * configured on the listening port
+ */
+static int connection_read_ssl (connection_t *con, void *buf, size_t len)
+{
+    int bytes = SSL_read (con->ssl, buf, len);
+
+    if (bytes < 0)
+    {
+        switch (SSL_get_error (con->ssl, bytes))
+        {
+            case SSL_ERROR_WANT_READ:
+            case SSL_ERROR_WANT_WRITE:
+                return -1;
+        }
+        con->error = 1;
+    }
+    return bytes;
+}
+
+static int connection_send_ssl (connection_t *con, const void *buf, size_t len)
+{
+    int bytes = SSL_write (con->ssl, buf, len);
+
+    if (bytes < 0)
+    {
+        switch (SSL_get_error (con->ssl, bytes))
+        {
+            case SSL_ERROR_WANT_READ:
+            case SSL_ERROR_WANT_WRITE:
+                return -1;
+        }
+        con->error = 1;
+    }
+    else
+        con->sent_bytes += bytes;
+    return bytes;
+}
+#else
+
+/* SSL not compiled in, so at least log it */
+static void get_ssl_certificate (ice_config_t *config)
+{
+    ssl_ok = 0;
+    INFO0 ("No SSL capability");
+}
+#endif /* HAVE_OPENSSL */
+
+
+/* handlers (default) for reading and writing a connection_t, no encrpytion
+ * used just straight access to the socket
+ */
+static int connection_read (connection_t *con, void *buf, size_t len)
+{
+    int bytes = sock_read_bytes (con->sock, buf, len);
+    if (bytes == 0)
+        con->error = 1;
+    if (bytes == -1 && !sock_recoverable (sock_error()))
+        con->error = 1;
+    return bytes;
+}
+
+static int connection_send (connection_t *con, const void *buf, size_t len)
+{
+    int bytes = sock_write_bytes (con->sock, buf, len);
+    if (bytes < 0)
+    {
+        if (!sock_recoverable (sock_error()))
+            con->error = 1;
+    }
+    else
+        con->sent_bytes += bytes;
+    return bytes;
+}
+
+
+/* function to handle the re-populating of the avl tree containing IP addresses
+ * for deciding whether a connection of an incoming request is to be dropped.
+ */
+static void recheck_ip_file (cache_file_contents *cache)
+{
+    time_t now = time(NULL);
+    if (now >= cache->file_recheck)
+    {
+        struct stat file_stat;
+        FILE *file = NULL;
+        int count = 0;
+        avl_tree *new_ips;
+        char line [MAX_LINE_LEN];
+
+        cache->file_recheck = now + 10;
+        if (cache->filename == NULL)
+        {
+            if (cache->contents)
+            {
+                avl_tree_free (cache->contents, free_filtered_ip);
+                cache->contents = NULL;
+            }
+            return;
+        }
+        if (stat (cache->filename, &file_stat) < 0)
+        {
+            WARN2 ("failed to check status of \"%s\": %s", cache->filename, strerror(errno));
+            return;
+        }
+        if (file_stat.st_mtime == cache->file_mtime)
+            return; /* common case, no update to file */
+
+        cache->file_mtime = file_stat.st_mtime;
+
+        file = fopen (cache->filename, "r");
+        if (file == NULL)
+        {
+            WARN2("Failed to open file \"%s\": %s", cache->filename, strerror (errno));
+            return;
+        }
+
+        new_ips = avl_tree_new (compare_ip, NULL);
+
+        while (get_line (file, line, MAX_LINE_LEN))
+        {
+            char *str;
+            if(!line[0] || line[0] == '#')
+                continue;
+            count++;
+            str = strdup (line);
+            if (str)
+                avl_insert (new_ips, str);
+        }
+        fclose (file);
+        INFO2 ("%d entries read from file \"%s\"", count, cache->filename);
+
+        if (cache->contents) avl_tree_free (cache->contents, free_filtered_ip);
+        cache->contents = new_ips;
+    }
+}
+
+
+/* return 0 if the passed ip address is not to be handled by icecast, non-zero otherwise */
+static int accept_ip_address (char *ip)
+{
+    void *result;
+
+    recheck_ip_file (&banned_ip);
+    recheck_ip_file (&allowed_ip);
+
+    if (banned_ip.contents)
+    {
+        if (avl_get_by_key (banned_ip.contents, ip, &result) == 0)
+        {
+            DEBUG1 ("%s is banned", ip);
+            return 0;
+        }
+    }
+    if (allowed_ip.contents)
+    {
+        if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0)
+        {
+            DEBUG1 ("%s is allowed", ip);
+            return 1;
+        }
+        else
+        {
+            DEBUG1 ("%s is not allowed", ip);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+
+connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
+{
+    connection_t *con;
+    con = (connection_t *)calloc(1, sizeof(connection_t));
+    if (con)
+    {
+        con->sock = sock;
+        con->serversock = serversock;
+        con->con_time = time(NULL);
+        con->id = _next_connection_id();
+        con->ip = ip;
+        con->read = connection_read;
+        con->send = connection_send;
+    }
+
+    return con;
+}
+
+/* prepare connection for interacting over a SSL connection
+ */
+void connection_uses_ssl (connection_t *con)
+{
+#ifdef HAVE_OPENSSL
+    con->read = connection_read_ssl;
+    con->send = connection_send_ssl;
+    con->ssl = SSL_new (ssl_ctx);
+    SSL_set_accept_state (con->ssl);
+    SSL_set_fd (con->ssl, con->sock);
+#endif
+}
+
+static sock_t wait_for_serversock(int timeout)
+{
+#ifdef HAVE_POLL
+    struct pollfd ufds [global.server_sockets];
+    int i, ret;
+
+    for(i=0; i < global.server_sockets; i++) {
+        ufds[i].fd = global.serversock[i];
+        ufds[i].events = POLLIN;
+        ufds[i].revents = 0;
+    }
+
+    ret = poll(ufds, global.server_sockets, timeout);
+    if(ret < 0) {
+        return SOCK_ERROR;
+    }
+    else if(ret == 0) {
+        return SOCK_ERROR;
+    }
+    else {
+        int dst;
+        for(i=0; i < global.server_sockets; i++) {
+            if(ufds[i].revents & POLLIN)
+                return ufds[i].fd;
+            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
+            {
+                if (ufds[i].revents & (POLLHUP|POLLERR))
+                {
+                    sock_close (global.serversock[i]);
+                    WARN0("Had to close a listening socket");
+                }
+                global.serversock[i] = SOCK_ERROR;
+            }
+        }
+        /* remove any closed sockets */
+        for(i=0, dst=0; i < global.server_sockets; i++)
+        {
+            if (global.serversock[i] == SOCK_ERROR)
+                continue;
+            if (i!=dst)
+                global.serversock[dst] = global.serversock[i];
+            dst++;
+        }
+        global.server_sockets = dst;
+        return SOCK_ERROR;
+    }
+#else
+    fd_set rfds;
+    struct timeval tv, *p=NULL;
+    int i, ret;
+    sock_t max = SOCK_ERROR;
+
+    FD_ZERO(&rfds);
+
+    for(i=0; i < global.server_sockets; i++) {
+        FD_SET(global.serversock[i], &rfds);
+        if (max == SOCK_ERROR || global.serversock[i] > max)
+            max = global.serversock[i];
+    }
+
+    if(timeout >= 0) {
+        tv.tv_sec = timeout/1000;
+        tv.tv_usec = (timeout % 1000) * 1000;
+        p = &tv;
+    }
+
+    ret = select(max+1, &rfds, NULL, NULL, p);
+    if(ret < 0) {
+        return SOCK_ERROR;
+    }
+    else if(ret == 0) {
+        return SOCK_ERROR;
+    }
+    else {
+        for(i=0; i < global.server_sockets; i++) {
+            if(FD_ISSET(global.serversock[i], &rfds))
+                return global.serversock[i];
+        }
+        return SOCK_ERROR; /* Should be impossible, stop compiler warnings */
+    }
+#endif
+}
+
+static connection_t *_accept_connection(void)
+{
+    sock_t sock, serversock; 
+    char *ip;
+
+    serversock = wait_for_serversock(100);
+    if (serversock == SOCK_ERROR)
+        return NULL;
+
+    /* malloc enough room for a full IP address (including ipv6) */
+    ip = (char *)malloc(MAX_ADDR_LEN);
+
+    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
+    if (sock != SOCK_ERROR)
+    {
+        connection_t *con = NULL;
+        /* Make any IPv4 mapped IPv6 address look like a normal IPv4 address */
+        if (strncmp (ip, "::ffff:", 7) == 0)
+            memmove (ip, ip+7, strlen (ip+7)+1);
+
+        if (accept_ip_address (ip))
+            con = connection_create (sock, serversock, ip);
+        if (con)
+            return con;
+        sock_close (sock);
+    }
+    else
+    {
+        if (!sock_recoverable(sock_error()))
+        {
+            WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
+            thread_sleep (500000);
+        }
+    }
+    free(ip);
+    return NULL;
+}
+
+
+/* add client to connection queue. At this point some header information
+ * has been collected, so we now pass it onto the connection thread for
+ * further processing
+ */
+static void _add_connection (client_queue_t *node)
+{
+    thread_mutex_lock (&_con_queue_mutex);
+    *_con_queue_tail = node;
+    _con_queue_tail = (volatile client_queue_t **)&node->next;
+    thread_mutex_unlock (&_con_queue_mutex);
+}
+
+
+/* this returns queued clients for the connection thread. headers are
+ * already provided, but need to be parsed.
+ */
+static client_queue_t *_get_connection(void)
+{
+    client_queue_t *node = NULL;
+
+    /* common case, no new connections so don't bother taking locks */
+    if (_con_queue)
+    {
+        thread_mutex_lock (&_con_queue_mutex);
+        node = (client_queue_t *)_con_queue;
+        _con_queue = node->next;
+        if (_con_queue == NULL)
+            _con_queue_tail = &_con_queue;
+        thread_mutex_unlock (&_con_queue_mutex);
+        node->next = NULL;
+    }
+    return node;
+}
+
+
+/* run along queue checking for any data that has come in or a timeout */
+static void process_request_queue (void)
+{
+    client_queue_t **node_ref = (client_queue_t **)&_req_queue;
+    ice_config_t *config = config_get_config ();
+    int timeout = config->header_timeout;
+    config_release_config();
+
+    while (*node_ref)
+    {
+        client_queue_t *node = *node_ref;
+        client_t *client = node->client;
+        int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
+        char *buf = client->refbuf->data + node->offset;
+
+        if (len > 0)
+        {
+            if (client->con->con_time + timeout <= time(NULL))
+                len = 0;
+            else
+                len = client_read_bytes (client, buf, len);
+        }
+
+        if (len > 0)
+        {
+            int pass_it = 1;
+            char *ptr;
+
+            /* handle \n, \r\n and nsvcap which for some strange reason has
+             * EOL as \r\r\n */
+            node->offset += len;
+            client->refbuf->data [node->offset] = '\000';
+            do
+            {
+                if (node->shoutcast == 1)
+                {
+                    /* password line */
+                    if (strstr (client->refbuf->data, "\r\r\n") != NULL)
+                        break;
+                    if (strstr (client->refbuf->data, "\r\n") != NULL)
+                        break;
+                    if (strstr (client->refbuf->data, "\n") != NULL)
+                        break;
+                }
+                /* stream_offset refers to the start of any data sent after the
+                 * http style headers, we don't want to lose those */
+                ptr = strstr (client->refbuf->data, "\r\r\n\r\r\n");
+                if (ptr)
+                {
+                    node->stream_offset = (ptr+6) - client->refbuf->data;
+                    break;
+                }
+                ptr = strstr (client->refbuf->data, "\r\n\r\n");
+                if (ptr)
+                {
+                    node->stream_offset = (ptr+4) - client->refbuf->data;
+                    break;
+                }
+                ptr = strstr (client->refbuf->data, "\n\n");
+                if (ptr)
+                {
+                    node->stream_offset = (ptr+2) - client->refbuf->data;
+                    break;
+                }
+                pass_it = 0;
+            } while (0);
+
+            if (pass_it)
+            {
+                thread_mutex_lock (&_req_queue_mutex);
+                if ((client_queue_t **)_req_queue_tail == &(node->next))
+                    _req_queue_tail = (volatile client_queue_t **)node_ref;
+                *node_ref = node->next;
+                node->next = NULL;
+                thread_mutex_unlock (&_req_queue_mutex);
+                _add_connection (node);
+                continue;
+            }
+        }
+        else
+        {
+            if (len == 0 || client->con->error)
+            {
+                thread_mutex_lock (&_req_queue_mutex);
+                if ((client_queue_t **)_req_queue_tail == &node->next)
+                    _req_queue_tail = (volatile client_queue_t **)node_ref;
+                *node_ref = node->next;
+                thread_mutex_unlock (&_req_queue_mutex);
+                client_destroy (client);
+                free (node);
+                continue;
+            }
+        }
+        node_ref = &node->next;
+    }
+}
+
+
+/* add node to the queue of requests. This is where the clients are when
+ * initial http details are read.
+ */
+static void _add_request_queue (client_queue_t *node)
+{
+    thread_mutex_lock (&_req_queue_mutex);
+    *_req_queue_tail = node;
+    _req_queue_tail = (volatile client_queue_t **)&node->next;
+    thread_mutex_unlock (&_req_queue_mutex);
+}
+
+
+void connection_accept_loop(void)
+{
+    connection_t *con;
+    ice_config_t *config;
+
+    config = config_get_config ();
+    get_ssl_certificate (config);
+    config_release_config ();
+
+    tid = thread_create ("connection thread", _handle_connection, NULL, THREAD_ATTACHED);
+
+    while (global.running == ICE_RUNNING)
+    {
+        con = _accept_connection();
+
+        if (con)
+        {
+            client_queue_t *node;
+            ice_config_t *config;
+            client_t *client = NULL;
+            listener_t *listener;
+
+            global_lock();
+            if (client_create (&client, con, NULL) < 0)
+            {
+                global_unlock();
+                client_send_403 (client, "Icecast connection limit reached");
+                /* don't be too eager as this is an imposed hard limit */
+                thread_sleep (400000);
+                continue;
+            }
+            global_unlock();
+
+            /* setup client for reading incoming http */
+            client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000';
+
+            node = calloc (1, sizeof (client_queue_t));
+            if (node == NULL)
+            {
+                client_destroy (client);
+                continue;
+            }
+            node->client = client;
+
+            config = config_get_config();
+            listener = config_get_listen_sock (config, client->con);
+
+            if (listener)
+            {
+                if (listener->shoutcast_compat)
+                    node->shoutcast = 1;
+                if (listener->ssl && ssl_ok)
+                    connection_uses_ssl (client->con);
+                if (listener->shoutcast_mount)
+                    node->shoutcast_mount = strdup (listener->shoutcast_mount);
+            }
+            config_release_config();
+
+            sock_set_blocking (client->con->sock, SOCK_NONBLOCK);
+            sock_set_nodelay (client->con->sock);
+
+            _add_request_queue (node);
+            stats_event_inc (NULL, "connections");
+        }
+        process_request_queue ();
+    }
+
+    /* Give all the other threads notification to shut down */
+    thread_cond_broadcast(&global.shutdown_cond);
+
+    if (tid)
+        thread_join (tid);
+
+    /* wait for all the sources to shutdown */
+    thread_rwlock_wlock(&_source_shutdown_rwlock);
+    thread_rwlock_unlock(&_source_shutdown_rwlock);
+}
+
+
+/* Called when activating a source. Verifies that the source count is not
+ * exceeded and applies any initial parameters.
+ */
+int connection_complete_source (source_t *source, int response)
+{
+    ice_config_t *config = config_get_config();
+
+    global_lock ();
+    DEBUG1 ("sources count is %d", global.sources);
+
+    if (global.sources < config->source_limit)
+    {
+        const char *contenttype;
+        mount_proxy *mountinfo;
+        format_type_t format_type;
+
+        /* setup format handler */
+        contenttype = httpp_getvar (source->parser, "content-type");
+        if (contenttype != NULL)
+        {
+            format_type = format_get_type (contenttype);
+
+            if (format_type == FORMAT_ERROR)
+            {
+                global_unlock();
+                config_release_config();
+                if (response)
+                {
+                    client_send_403 (source->client, "Content-type not supported");
+                    source->client = NULL;
+                }
+                WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
+                return -1;
+            }
+        }
+        else
+        {
+            WARN0("No content-type header, falling back to backwards compatibility mode "
+                    "for icecast 1.x relays. Assuming content is mp3.");
+            format_type = FORMAT_TYPE_GENERIC;
+        }
+
+        if (format_get_plugin (format_type, source) < 0)
+        {
+            global_unlock();
+            config_release_config();
+            if (response)
+            {
+                client_send_403 (source->client, "internal format allocation problem");
+                source->client = NULL;
+            }
+            WARN1 ("plugin format failed for \"%s\"", source->mount);
+            return -1;
+        }
+
+        global.sources++;
+        stats_event_args (NULL, "sources", "%d", global.sources);
+        global_unlock();
+
+        source->running = 1;
+        mountinfo = config_find_mount (config, source->mount);
+        source_update_settings (config, source, mountinfo);
+        config_release_config();
+        slave_rebuild_mounts();
+
+        source->shutdown_rwlock = &_source_shutdown_rwlock;
+        DEBUG0 ("source is ready to start");
+
+        return 0;
+    }
+    WARN1("Request to add source when maximum source limit "
+            "reached %d", global.sources);
+
+    global_unlock();
+    config_release_config();
+
+    if (response)
+    {
+        client_send_403 (source->client, "too many sources connected");
+        source->client = NULL;
+    }
+
+    return -1;
+}
+
+
+static int _check_pass_http(http_parser_t *parser, 
+        const char *correctuser, const char *correctpass)
+{
+    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
+    const char *header = httpp_getvar(parser, "authorization");
+    char *userpass, *tmp;
+    char *username, *password;
+
+    if(header == NULL)
+        return 0;
+
+    if(strncmp(header, "Basic ", 6))
+        return 0;
+
+    userpass = util_base64_decode(header+6);
+    if(userpass == NULL) {
+        WARN1("Base64 decode of Authorization header \"%s\" failed",
+                header+6);
+        return 0;
+    }
+
+    tmp = strchr(userpass, ':');
+    if(!tmp) {
+        free(userpass);
+        return 0;
+    }
+    *tmp = 0;
+    username = userpass;
+    password = tmp+1;
+
+    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
+        free(userpass);
+        return 0;
+    }
+    free(userpass);
+
+    return 1;
+}
+
+static int _check_pass_icy(http_parser_t *parser, const char *correctpass)
+{
+    const char *password;
+
+    password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
+    if(!password)
+        return 0;
+
+    if (strcmp(password, correctpass))
+        return 0;
+    else
+        return 1;
+}
+
+static int _check_pass_ice(http_parser_t *parser, const char *correctpass)
+{
+    const char *password;
+
+    password = httpp_getvar(parser, "ice-password");
+    if(!password)
+        password = "";
+
+    if (strcmp(password, correctpass))
+        return 0;
+    else
+        return 1;
+}
+
+int connection_check_admin_pass(http_parser_t *parser)
+{
+    int ret;
+    ice_config_t *config = config_get_config();
+    char *pass = config->admin_password;
+    char *user = config->admin_username;
+    const char *protocol;
+
+    if(!pass || !user) {
+        config_release_config();
+        return 0;
+    }
+
+    protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
+    if (protocol && strcmp (protocol, "ICY") == 0)
+        ret = _check_pass_icy (parser, pass);
+    else 
+        ret = _check_pass_http (parser, user, pass);
+    config_release_config();
+    return ret;
+}
+
+int connection_check_relay_pass(http_parser_t *parser)
+{
+    int ret;
+    ice_config_t *config = config_get_config();
+    char *pass = config->relay_password;
+    char *user = config->relay_username;
+
+    if(!pass || !user) {
+        config_release_config();
+        return 0;
+    }
+
+    ret = _check_pass_http(parser, user, pass);
+    config_release_config();
+    return ret;
+}
+
+int connection_check_source_pass(http_parser_t *parser, const char *mount)
+{
+    ice_config_t *config = config_get_config();
+    char *pass = config->source_password;
+    char *user = "source";
+    int ret;
+    int ice_login = config->ice_login;
+    const char *protocol;
+
+    mount_proxy *mountinfo = config_find_mount (config, mount);
+
+    if (mountinfo)
+    {
+        if (mountinfo->password)
+            pass = mountinfo->password;
+        if (mountinfo->username)
+            user = mountinfo->username;
+    }
+
+    if(!pass) {
+        WARN0("No source password set, rejecting source");
+        config_release_config();
+        return 0;
+    }
+
+    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
+    if(protocol != NULL && !strcmp(protocol, "ICY")) {
+        ret = _check_pass_icy(parser, pass);
+    }
+    else {
+        ret = _check_pass_http(parser, user, pass);
+        if(!ret && ice_login)
+        {
+            ret = _check_pass_ice(parser, pass);
+            if(ret)
+                WARN0("Source is using deprecated icecast login");
+        }
+    }
+    config_release_config();
+    return ret;
+}
+
+
+static void _handle_source_request (client_t *client, char *uri, int auth_style)
+{
+    source_t *source;
+
+    INFO1("Source logging in at mountpoint \"%s\"", uri);
+
+    if (uri[0] != '/')
+    {
+        WARN0 ("source mountpoint not starting with /");
+        client_send_401 (client);
+        return;
+    }
+    if (auth_style == ICECAST_SOURCE_AUTH) {
+        if (connection_check_source_pass (client->parser, uri) == 0)
+        {
+            /* We commonly get this if the source client is using the wrong
+             * protocol: attempt to diagnose this and return an error
+             */
+            /* TODO: Do what the above comment says */
+            INFO1("Source (%s) attempted to login with invalid or missing password", uri);
+            client_send_401(client);
+            return;
+        }
+    }
+    source = source_reserve (uri);
+    if (source)
+    {
+        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
+            source->shoutcast_compat = 1;
+        }
+        source->client = client;
+        source->parser = client->parser;
+        source->con = client->con;
+        if (connection_complete_source (source, 1) < 0)
+        {
+            source_clear_source (source);
+            source_free_source (source);
+        }
+        else
+        {
+            refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
+            client->respcode = 200;
+            snprintf (ok->data, PER_CLIENT_REFBUF_SIZE,
+                    "HTTP/1.0 200 OK\r\n\r\n");
+            ok->len = strlen (ok->data);
+            /* we may have unprocessed data read in, so don't overwrite it */
+            ok->associated = client->refbuf;
+            client->refbuf = ok;
+            fserve_add_client_callback (client, source_client_callback, source);
+        }
+    }
+    else
+    {
+        client_send_403 (client, "Mountpoint in use");
+        WARN1 ("Mountpoint %s in use", uri);
+    }
+}
+
+
+static void _handle_stats_request (client_t *client, char *uri)
+{
+    stats_event_inc(NULL, "stats_connections");
+
+    if (connection_check_admin_pass (client->parser) == 0)
+    {
+        client_send_401 (client);
+        ERROR0("Bad password for stats connection");
+        return;
+    }
+
+    client->respcode = 200;
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+            "HTTP/1.0 200 OK\r\n\r\n");
+    client->refbuf->len = strlen (client->refbuf->data);
+    fserve_add_client_callback (client, stats_callback, NULL);
+}
+
+static void _handle_get_request (client_t *client, char *passed_uri)
+{
+    int port;
+    char *serverhost = NULL;
+    int serverport = 0;
+    aliases *alias;
+    ice_config_t *config;
+    char *uri = passed_uri;
+    listener_t *listen_sock;
+
+    config = config_get_config();
+    port = config->port;
+
+    listen_sock = config_get_listen_sock (config, client->con);
+    if (listen_sock)
+    {
+        serverhost = listen_sock->bind_address;
+        serverport = listen_sock->port;
+    }
+    alias = config->aliases;
+
+    /* there are several types of HTTP GET clients
+    ** media clients, which are looking for a source (eg, URI = /stream.ogg)
+    ** stats clients, which are looking for /admin/stats.xml
+    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
+    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
+    ** we need to handle the latter two before the former, as the latter two
+    ** aren't subject to the limits.
+    */
+    /* TODO: add GUID-xxxxxx */
+
+    /* Handle aliases */
+    while(alias) {
+        if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
+            uri = strdup (alias->destination);
+            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
+            break;
+        }
+        alias = alias->next;
+    }
+    config_release_config();
+
+    stats_event_inc(NULL, "client_connections");
+
+    /* Dispatch all admin requests */
+    if ((strcmp(uri, "/admin.cgi") == 0) ||
+        (strncmp(uri, "/admin/", 7) == 0)) {
+        admin_handle_request(client, uri);
+        if (uri != passed_uri) free (uri);
+        return;
+    }
+    auth_add_listener (uri, client);
+    if (uri != passed_uri) free (uri);
+}
+
+static void _handle_shoutcast_compatible (client_queue_t *node)
+{
+    char *http_compliant;
+    int http_compliant_len = 0;
+    http_parser_t *parser;
+    ice_config_t *config = config_get_config ();
+    char *shoutcast_mount;
+    client_t *client = node->client;
+
+    if (node->shoutcast_mount)
+        shoutcast_mount = node->shoutcast_mount;
+    else
+        shoutcast_mount = config->shoutcast_mount;
+
+    if (node->shoutcast == 1)
+    {
+        char *source_password, *ptr, *headers;
+        mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount);
+
+        if (mountinfo && mountinfo->password)
+            source_password = strdup (mountinfo->password);
+        else
+            source_password = strdup (config->source_password);
+        config_release_config();
+
+        /* Get rid of trailing \r\n or \n after password */
+        ptr = strstr (client->refbuf->data, "\r\r\n");
+        if (ptr)
+            headers = ptr+3;
+        else
+        {
+            ptr = strstr (client->refbuf->data, "\r\n");
+            if (ptr)
+                headers = ptr+2;
+            else
+            {
+                ptr = strstr (client->refbuf->data, "\n");
+                if (ptr)
+                    headers = ptr+1;
+            }
+        }
+
+        if (ptr == NULL)
+        {
+            client_destroy (client);
+            free (source_password);
+            free (node->shoutcast_mount);
+            free (node);
+            return;
+        }
+        *ptr = '\0';
+
+        if (strcmp (client->refbuf->data, source_password) == 0)
+        {
+            client->respcode = 200;
+            /* send this non-blocking but if there is only a partial write
+             * then leave to header timeout */
+            sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
+            node->offset -= (headers - client->refbuf->data);
+            memmove (client->refbuf->data, headers, node->offset+1);
+            node->shoutcast = 2;
+            /* we've checked the password, now send it back for reading headers */
+            _add_request_queue (node);
+            free (source_password);
+            return;
+        }
+        else
+            INFO1 ("password does not match \"%s\"", client->refbuf->data);
+        client_destroy (client);
+        free (source_password);
+        free (node->shoutcast_mount);
+        free (node);
+        return;
+    }
+    /* actually make a copy as we are dropping the config lock */
+    shoutcast_mount = strdup (shoutcast_mount);
+    config_release_config();
+    /* Here we create a valid HTTP request based of the information
+       that was passed in via the non-HTTP style protocol above. This
+       means we can use some of our existing code to handle this case */
+    http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
+    http_compliant = (char *)calloc(1, http_compliant_len);
+    snprintf (http_compliant, http_compliant_len,
+            "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
+    parser = httpp_create_parser();
+    httpp_initialize(parser, NULL);
+    if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
+    {
+        /* we may have more than just headers, so prepare for it */
+        if (node->stream_offset == node->offset)
+            client->refbuf->len = 0;
+        else
+        {
+            char *ptr = client->refbuf->data;
+            client->refbuf->len = node->offset - node->stream_offset;
+            memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
+        }
+        client->parser = parser;
+        _handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
+    }
+    else {
+        httpp_destroy (parser);
+        client_destroy (client);
+    }
+    free (http_compliant);
+    free (shoutcast_mount);
+    free (node->shoutcast_mount);
+    free (node);
+    return;
+}
+
+
+/* Connection thread. Here we take clients off the connection queue and check
+ * the contents provided. We set up the parser then hand off to the specific
+ * request handler.
+ */
+static void *_handle_connection(void *arg)
+{
+    http_parser_t *parser;
+    const char *rawuri;
+
+    while (global.running == ICE_RUNNING) {
+
+        client_queue_t *node = _get_connection();
+
+        if (node)
+        {
+            client_t *client = node->client;
+
+            /* Check for special shoutcast compatability processing */
+            if (node->shoutcast) 
+            {
+                _handle_shoutcast_compatible (node);
+                continue;
+            }
+
+            /* process normal HTTP headers */
+            parser = httpp_create_parser();
+            httpp_initialize(parser, NULL);
+            client->parser = parser;
+            if (httpp_parse (parser, client->refbuf->data, node->offset))
+            {
+                char *uri;
+
+                /* we may have more than just headers, so prepare for it */
+                if (node->stream_offset == node->offset)
+                    client->refbuf->len = 0;
+                else
+                {
+                    char *ptr = client->refbuf->data;
+                    client->refbuf->len = node->offset - node->stream_offset;
+                    memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
+                }
+
+                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
+
+                /* assign a port-based shoutcast mountpoint if required */
+                if (node->shoutcast_mount && strcmp (rawuri, "/admin.cgi") == 0)
+                    httpp_set_query_param (client->parser, "mount", node->shoutcast_mount);
+
+                free (node->shoutcast_mount);
+                free (node);
+                
+                if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
+                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
+                    ERROR0("Bad HTTP protocol detected");
+                    client_destroy (client);
+                    continue;
+                }
+
+                uri = util_normalise_uri(rawuri);
+
+                if (uri == NULL)
+                {
+                    client_destroy (client);
+                    continue;
+                }
+
+                if (parser->req_type == httpp_req_source) {
+                    _handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
+                }
+                else if (parser->req_type == httpp_req_stats) {
+                    _handle_stats_request (client, uri);
+                }
+                else if (parser->req_type == httpp_req_get) {
+                    _handle_get_request (client, uri);
+                }
+                else {
+                    ERROR0("Wrong request type from client");
+                    client_send_400 (client, "unknown request");
+                }
+
+                free(uri);
+            } 
+            else
+            {
+                free (node);
+                ERROR0("HTTP request parsing failed");
+                client_destroy (client);
+            }
+            continue;
+        }
+        thread_sleep (50000);
+    }
+    DEBUG0 ("Connection thread done");
+
+    return NULL;
+}
+
+
+/* called when listening thread is not checking for incoming connections */
+int connection_setup_sockets (ice_config_t *config)
+{
+    int count = 0;
+    listener_t *listener, **prev;
+
+    free (banned_ip.filename);
+    banned_ip.filename = NULL;
+    free (allowed_ip.filename);
+    allowed_ip.filename = NULL;
+
+    global_lock();
+    if (global.serversock)
+    {
+        for (; count < global.server_sockets; count++)
+            sock_close (global.serversock [count]);
+        free (global.serversock);
+        global.serversock = NULL;
+    }
+    if (config == NULL)
+    {
+        global_unlock();
+        return 0;
+    }
+
+    /* setup the banned/allowed IP filenames from the xml */
+    if (config->banfile)
+        banned_ip.filename = strdup (config->banfile);
+
+    if (config->allowfile)
+        allowed_ip.filename = strdup (config->allowfile);
+
+    count = 0;
+    global.serversock = calloc (config->listen_sock_count, sizeof (sock_t));
+
+    listener = config->listen_sock; 
+    prev = &config->listen_sock;
+    while (listener)
+    {
+        int successful = 0;
+
+        do
+        {
+            sock_t sock = sock_get_server_socket (listener->port, listener->bind_address);
+            if (sock == SOCK_ERROR)
+                break;
+            if (sock_listen (sock, ICE_LISTEN_QUEUE) == SOCK_ERROR)
+            {
+                sock_close (sock);
+                break;
+            }
+            sock_set_blocking (sock, SOCK_NONBLOCK);
+            successful = 1;
+            global.serversock [count] = sock;
+            count++;
+        } while(0);
+        if (successful == 0)
+        {
+            if (listener->bind_address)
+                ERROR2 ("Could not create listener socket on port %d bind %s",
+                        listener->port, listener->bind_address);
+            else
+                ERROR1 ("Could not create listener socket on port %d", listener->port);
+            /* remove failed connection */
+            *prev = config_clear_listener (listener);
+            listener = *prev;
+            continue;
+        }
+        if (listener->bind_address)
+            INFO2 ("listener socket on port %d address %s", listener->port, listener->bind_address);
+        else
+            INFO1 ("listener socket on port %d", listener->port);
+        prev = &listener->next;
+        listener = listener->next;
+    }
+    global.server_sockets = count;
+    global_unlock();
+
+    if (count == 0)
+        ERROR0 ("No listening sockets established");
+
+    return count;
+}
+
+
+void connection_close(connection_t *con)
+{
+    sock_close(con->sock);
+    if (con->ip) free(con->ip);
+    if (con->host) free(con->host);
+#ifdef HAVE_OPENSSL
+    if (con->ssl) { SSL_shutdown (con->ssl); SSL_free (con->ssl); }
+#endif
+    free(con);
+}

Deleted: icecast/tags/icecast-2.3.2/src/connection.h
===================================================================
--- icecast/trunk/icecast/src/connection.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/connection.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,56 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __CONNECTION_H__
-#define __CONNECTION_H__
-
-#include <sys/types.h>
-#include <time.h>
-#include "compat.h"
-#include "httpp/httpp.h"
-#include "thread/thread.h"
-#include "net/sock.h"
-
-struct _client_tag;
-struct source_tag;
-
-typedef struct connection_tag
-{
-    unsigned long id;
-
-    time_t con_time;
-    time_t discon_time;
-    uint64_t sent_bytes;
-
-    int sock;
-    int serversock;
-    int error;
-
-    char *ip;
-    char *host;
-
-} connection_t;
-
-void connection_initialize(void);
-void connection_shutdown(void);
-void connection_accept_loop(void);
-void connection_close(connection_t *con);
-connection_t *connection_create (sock_t sock, sock_t serversock, char *ip);
-int connection_complete_source (struct source_tag *source, int response);
-
-int connection_check_source_pass(http_parser_t *parser, const char *mount);
-int connection_check_relay_pass(http_parser_t *parser);
-int connection_check_admin_pass(http_parser_t *parser);
-
-extern rwlock_t _source_shutdown_rwlock;
-
-#endif  /* __CONNECTION_H__ */

Copied: icecast/tags/icecast-2.3.2/src/connection.h (from rev 14043, icecast/trunk/icecast/src/connection.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/connection.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/connection.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,69 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __CONNECTION_H__
+#define __CONNECTION_H__
+
+#include <sys/types.h>
+#include <time.h>
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#include "compat.h"
+#include "httpp/httpp.h"
+#include "thread/thread.h"
+#include "net/sock.h"
+
+struct _client_tag;
+struct source_tag;
+struct ice_config_tag;
+
+typedef struct connection_tag
+{
+    unsigned long id;
+
+    time_t con_time;
+    time_t discon_time;
+    uint64_t sent_bytes;
+
+    sock_t sock;
+    sock_t serversock;
+    int error;
+
+#ifdef HAVE_OPENSSL
+    SSL *ssl;   /* SSL handler */
+#endif
+    int (*send)(struct connection_tag *handle, const void *buf, size_t len);
+    int (*read)(struct connection_tag *handle, void *buf, size_t len);
+
+    char *ip;
+    char *host;
+
+} connection_t;
+
+void connection_initialize(void);
+void connection_shutdown(void);
+void connection_accept_loop(void);
+int  connection_setup_sockets (struct ice_config_tag *config);
+void connection_close(connection_t *con);
+connection_t *connection_create (sock_t sock, sock_t serversock, char *ip);
+int connection_complete_source (struct source_tag *source, int response);
+
+int connection_check_source_pass(http_parser_t *parser, const char *mount);
+int connection_check_relay_pass(http_parser_t *parser);
+int connection_check_admin_pass(http_parser_t *parser);
+
+extern rwlock_t _source_shutdown_rwlock;
+
+#endif  /* __CONNECTION_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/event.c
===================================================================
--- icecast/trunk/icecast/src/event.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/event.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,69 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "event.h"
-#include "cfgfile.h"
-#include "yp.h"
-#include "source.h"
-
-#include "refbuf.h"
-#include "client.h"
-#include "logging.h"
-#include "slave.h"
-
-#define CATMODULE "event"
-
-void event_config_read(void *arg)
-{
-    int ret;
-    ice_config_t *config;
-    ice_config_t new_config;
-    /* reread config file */
-
-    config = config_grab_config(); /* Both to get the lock, and to be able
-                                     to find out the config filename */
-    xmlSetGenericErrorFunc ("config", log_parse_failure);
-    ret = config_parse_file(config->config_filename, &new_config);
-    if(ret < 0) {
-        ERROR0("Error parsing config, not replacing existing config");
-        switch(ret) {
-            case CONFIG_EINSANE:
-                ERROR0("Config filename null or blank");
-                break;
-            case CONFIG_ENOROOT:
-                ERROR1("Root element not found in %s", config->config_filename);
-                break;
-            case CONFIG_EBADROOT:
-                ERROR1("Not an icecast2 config file: %s",
-                        config->config_filename);
-                break;
-            default:
-                ERROR1("Parse error in reading %s", config->config_filename);
-                break;
-        }
-        config_release_config();
-    }
-    else {
-        config_clear(config);
-        config_set_config(&new_config);
-        restart_logging (config_get_config_unlocked());
-        yp_recheck_config (config_get_config_unlocked());
-
-        config_release_config();
-        slave_recheck_all();
-    }
-}
-

Copied: icecast/tags/icecast-2.3.2/src/event.c (from rev 14031, icecast/trunk/icecast/src/event.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/event.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/event.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,73 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "event.h"
+#include "cfgfile.h"
+#include "yp.h"
+#include "source.h"
+
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h"
+#include "slave.h"
+#include "fserve.h"
+#include "stats.h"
+
+#define CATMODULE "event"
+
+void event_config_read(void *arg)
+{
+    int ret;
+    ice_config_t *config;
+    ice_config_t new_config;
+    /* reread config file */
+
+    config = config_grab_config(); /* Both to get the lock, and to be able
+                                     to find out the config filename */
+    xmlSetGenericErrorFunc ("config", log_parse_failure);
+    ret = config_parse_file(config->config_filename, &new_config);
+    if(ret < 0) {
+        ERROR0("Error parsing config, not replacing existing config");
+        switch(ret) {
+            case CONFIG_EINSANE:
+                ERROR0("Config filename null or blank");
+                break;
+            case CONFIG_ENOROOT:
+                ERROR1("Root element not found in %s", config->config_filename);
+                break;
+            case CONFIG_EBADROOT:
+                ERROR1("Not an icecast2 config file: %s",
+                        config->config_filename);
+                break;
+            default:
+                ERROR1("Parse error in reading %s", config->config_filename);
+                break;
+        }
+        config_release_config();
+    }
+    else {
+        config_clear(config);
+        config_set_config(&new_config);
+        config = config_get_config_unlocked();
+        restart_logging (config);
+        yp_recheck_config (config);
+        fserve_recheck_mime_types (config);
+        stats_global (config);
+        config_release_config();
+        slave_update_all_mounts();
+    }
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/format.c
===================================================================
--- icecast/trunk/icecast/src/format.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,358 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; -*- */
-/* format.c
-**
-** format plugin implementation
-**
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-#include <time.h>
-
-#include "connection.h"
-#include "refbuf.h"
-
-#include "source.h"
-#include "format.h"
-#include "global.h"
-#include "httpp/httpp.h"
-
-#include "format_ogg.h"
-#include "format_mp3.h"
-
-#include "logging.h"
-#include "stats.h"
-#define CATMODULE "format"
-
-#ifdef WIN32
-#define strcasecmp stricmp
-#define strncasecmp strnicmp
-#define snprintf _snprintf
-#endif
-
-static int format_prepare_headers (source_t *source, client_t *client);
-
-
-format_type_t format_get_type(char *contenttype)
-{
-    if(strcmp(contenttype, "application/x-ogg") == 0)
-        return FORMAT_TYPE_OGG; /* Backwards compatibility */
-    else if(strcmp(contenttype, "application/ogg") == 0)
-        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
-    else
-        /* We default to the Generic format handler, which
-           can handle many more formats than just mp3 */
-        return FORMAT_TYPE_GENERIC;
-}
-
-int format_get_plugin(format_type_t type, source_t *source)
-{
-    int ret = -1;
-
-    switch (type) {
-    case FORMAT_TYPE_OGG:
-        ret = format_ogg_get_plugin (source);
-        break;
-    case FORMAT_TYPE_GENERIC:
-        ret = format_mp3_get_plugin (source);
-        break;
-    default:
-        break;
-    }
-    if (ret < 0)
-        stats_event (source->mount, "content-type", 
-                source->format->contenttype);
-
-    return ret;
-}
-
-
-/* clients need to be start from somewhere in the queue so we will look for
- * a refbuf which has been previously marked as a sync point. 
- */
-static void find_client_start (source_t *source, client_t *client)
-{
-    refbuf_t *refbuf = source->burst_point;
-
-    /* we only want to attempt a burst at connection time, not midstream
-     * however streams like theora may not have the most recent page marked as
-     * a starting point, so look for one from the burst point */
-    if (client->intro_offset == -1 && source->stream_data_tail
-            && source->stream_data_tail->sync_point)
-        refbuf = source->stream_data_tail;
-    else
-    {
-        size_t size = client->intro_offset;
-        refbuf = source->burst_point;
-        while (size > 0 && refbuf && refbuf->next)
-        {
-            size -= refbuf->len;
-            refbuf = refbuf->next;
-        }
-    }
-
-    while (refbuf)
-    {
-        if (refbuf->sync_point)
-        {
-            client_set_queue (client, refbuf);
-            client->check_buffer = format_advance_queue;
-            client->write_to_client = source->format->write_buf_to_client;
-            client->intro_offset = -1;
-            break;
-        }
-        refbuf = refbuf->next;
-    }
-}
-
-
-static int get_file_data (FILE *intro, client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-    size_t bytes;
-
-    if (intro == NULL || fseek (intro, client->intro_offset, SEEK_SET) < 0)
-        return 0;
-    bytes = fread (refbuf->data, 1, 4096, intro);
-    if (bytes == 0)
-        return 0;
-
-    refbuf->len = bytes;
-    return 1;
-}
-
-
-/* call to check the buffer contents for file reading. move the client
- * to right place in the queue at end of file else repeat file if queue
- * is not ready yet.
- */
-int format_check_file_buffer (source_t *source, client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-
-    if (refbuf == NULL)
-    {
-        /* client refers to no data, must be from a move */
-        if (source->client)
-        {
-            find_client_start (source, client);
-            return -1;
-        }
-        /* source -> file fallback, need a refbuf for data */
-        refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
-        client->refbuf = refbuf;
-        client->pos = refbuf->len;
-        client->intro_offset = 0;
-    }
-    if (client->pos == refbuf->len)
-    {
-        if (get_file_data (source->intro_file, client))
-        {
-            client->pos = 0;
-            client->intro_offset += refbuf->len;
-        }
-        else
-        {
-            if (source->stream_data_tail)
-            {
-                /* better find the right place in queue for this client */
-                client_set_queue (client, NULL);
-                find_client_start (source, client);
-            }
-            else
-                client->intro_offset = 0;  /* replay intro file */
-            return -1;
-        }
-    }
-    return 0;
-}
-
-
-/* call this to verify that the HTTP data has been sent and if so setup
- * callbacks to the appropriate format functions
- */
-int format_check_http_buffer (source_t *source, client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-
-    if (refbuf == NULL)
-        return -1;
-
-    if (client->respcode == 0)
-    {
-        DEBUG0("processing pending client headers");
-
-        client->respcode = 200;
-        if (format_prepare_headers (source, client) < 0)
-        {
-            ERROR0 ("internal problem, dropping client");
-            client->con->error = 1;
-            return -1;
-        }
-        stats_event_inc (NULL, "listener_connections");
-        stats_event_inc (source->mount, "listener_connections");
-    }
-
-    if (client->pos == refbuf->len)
-    {
-        client->write_to_client = source->format->write_buf_to_client;
-        client->check_buffer = format_check_file_buffer;
-        client->intro_offset = 0;
-        client->pos = refbuf->len = 4096;
-        return -1;
-    }
-    return 0;
-}
-
-
-int format_generic_write_to_client (client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-    int ret;
-    const char *buf = refbuf->data + client->pos;
-    unsigned int len = refbuf->len - client->pos;
-
-    ret = client_send_bytes (client, buf, len);
-
-    if (ret > 0)
-        client->pos += ret;
-
-    return ret;
-}
-
-
-/* This is the commonly used for source streams, here we just progress to
- * the next buffer in the queue if there is no more left to be written from 
- * the existing buffer.
- */
-int format_advance_queue (source_t *source, client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-
-    if (refbuf == NULL)
-        return -1;
-
-    if (refbuf->next == NULL && client->pos == refbuf->len)
-        return -1;
-
-    /* move to the next buffer if we have finished with the current one */
-    if (refbuf->next && client->pos == refbuf->len)
-    {
-        client_set_queue (client, refbuf->next);
-        refbuf = client->refbuf;
-    }
-    return 0;
-}
-
-
-static int format_prepare_headers (source_t *source, client_t *client)
-{
-    unsigned remaining;
-    char *ptr;
-    int bytes;
-    int bitrate_filtered = 0;
-    avl_node *node;
-
-    remaining = client->refbuf->len;
-    ptr = client->refbuf->data;
-    client->respcode = 200;
-
-    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
-            "Content-Type: %s\r\n", source->format->contenttype);
-
-    remaining -= bytes;
-    ptr += bytes;
-
-    /* iterate through source http headers and send to client */
-    avl_tree_rlock(source->parser->vars);
-    node = avl_get_first(source->parser->vars);
-    while (node)
-    {
-        int next = 1;
-        http_var_t *var = (http_var_t *)node->key;
-        bytes = 0;
-        if (!strcasecmp(var->name, "ice-audio-info"))
-        {
-            /* convert ice-audio-info to icy-br */
-            char *brfield = NULL;
-            unsigned int bitrate;
-
-            if (bitrate_filtered == 0)
-                brfield = strstr(var->value, "bitrate=");
-            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
-            {           
-                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
-                next = 0;
-                bitrate_filtered = 1;
-            }
-            else
-                /* show ice-audio_info header as well because of relays */
-                bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value);
-        }
-        else
-        {
-            if (strcasecmp(var->name, "ice-password") &&
-                strcasecmp(var->name, "icy-metaint"))
-            {
-                if (!strncasecmp("ice-", var->name, 4))
-                {
-                    if (!strcasecmp("ice-public", var->name))
-                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
-                    else
-                        if (!strcasecmp ("ice-bitrate", var->name))
-                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
-                        else
-                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
-                                    var->name + 3, var->value);
-                }
-                else
-                    if (!strncasecmp("icy-", var->name, 4))
-                    {
-                        bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
-                                var->name + 3, var->value);
-                    }
-            }
-        }
-
-        remaining -= bytes;
-        ptr += bytes;
-        if (next)
-            node = avl_get_next(node);
-    }
-    avl_tree_unlock(source->parser->vars);
-
-    bytes = snprintf (ptr, remaining, "Server: %s\r\n", ICECAST_VERSION_STRING);
-    remaining -= bytes;
-    ptr += bytes;
-
-    bytes = snprintf (ptr, remaining, "\r\n");
-    remaining -= bytes;
-    ptr += bytes;
-
-    client->refbuf->len -= remaining;
-    if (source->format->create_client_data)
-        if (source->format->create_client_data (source, client) < 0)
-            return -1;
-    return 0;
-}
-
-

Copied: icecast/tags/icecast-2.3.2/src/format.c (from rev 14786, icecast/trunk/icecast/src/format.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,373 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; -*- */
+/* format.c
+**
+** format plugin implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include "connection.h"
+#include "refbuf.h"
+
+#include "source.h"
+#include "format.h"
+#include "global.h"
+#include "httpp/httpp.h"
+
+#include "format_ogg.h"
+#include "format_mp3.h"
+
+#include "logging.h"
+#include "stats.h"
+#define CATMODULE "format"
+
+#ifdef WIN32
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#define snprintf _snprintf
+#endif
+
+static int format_prepare_headers (source_t *source, client_t *client);
+
+
+format_type_t format_get_type (const char *contenttype)
+{
+    if(strcmp(contenttype, "application/x-ogg") == 0)
+        return FORMAT_TYPE_OGG; /* Backwards compatibility */
+    else if(strcmp(contenttype, "application/ogg") == 0)
+        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
+    else if(strcmp(contenttype, "audio/ogg") == 0)
+        return FORMAT_TYPE_OGG;
+    else if(strcmp(contenttype, "video/ogg") == 0)
+        return FORMAT_TYPE_OGG;
+    else
+        /* We default to the Generic format handler, which
+           can handle many more formats than just mp3 */
+        return FORMAT_TYPE_GENERIC;
+}
+
+int format_get_plugin(format_type_t type, source_t *source)
+{
+    int ret = -1;
+
+    switch (type) {
+    case FORMAT_TYPE_OGG:
+        ret = format_ogg_get_plugin (source);
+        break;
+    case FORMAT_TYPE_GENERIC:
+        ret = format_mp3_get_plugin (source);
+        break;
+    default:
+        break;
+    }
+    if (ret < 0)
+        stats_event (source->mount, "content-type", 
+                source->format->contenttype);
+
+    return ret;
+}
+
+
+/* clients need to be start from somewhere in the queue so we will look for
+ * a refbuf which has been previously marked as a sync point. 
+ */
+static void find_client_start (source_t *source, client_t *client)
+{
+    refbuf_t *refbuf = source->burst_point;
+
+    /* we only want to attempt a burst at connection time, not midstream
+     * however streams like theora may not have the most recent page marked as
+     * a starting point, so look for one from the burst point */
+    if (client->intro_offset == -1 && source->stream_data_tail
+            && source->stream_data_tail->sync_point)
+        refbuf = source->stream_data_tail;
+    else
+    {
+        size_t size = client->intro_offset;
+        refbuf = source->burst_point;
+        while (size > 0 && refbuf && refbuf->next)
+        {
+            size -= refbuf->len;
+            refbuf = refbuf->next;
+        }
+    }
+
+    while (refbuf)
+    {
+        if (refbuf->sync_point)
+        {
+            client_set_queue (client, refbuf);
+            client->check_buffer = format_advance_queue;
+            client->write_to_client = source->format->write_buf_to_client;
+            client->intro_offset = -1;
+            break;
+        }
+        refbuf = refbuf->next;
+    }
+}
+
+
+static int get_file_data (FILE *intro, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+    size_t bytes;
+
+    if (intro == NULL || fseek (intro, client->intro_offset, SEEK_SET) < 0)
+        return 0;
+    bytes = fread (refbuf->data, 1, 4096, intro);
+    if (bytes == 0)
+        return 0;
+
+    refbuf->len = (unsigned int)bytes;
+    return 1;
+}
+
+
+/* call to check the buffer contents for file reading. move the client
+ * to right place in the queue at end of file else repeat file if queue
+ * is not ready yet.
+ */
+int format_check_file_buffer (source_t *source, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+
+    if (refbuf == NULL)
+    {
+        /* client refers to no data, must be from a move */
+        if (source->client)
+        {
+            find_client_start (source, client);
+            return -1;
+        }
+        /* source -> file fallback, need a refbuf for data */
+        refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
+        client->refbuf = refbuf;
+        client->pos = refbuf->len;
+        client->intro_offset = 0;
+    }
+    if (client->pos == refbuf->len)
+    {
+        if (get_file_data (source->intro_file, client))
+        {
+            client->pos = 0;
+            client->intro_offset += refbuf->len;
+        }
+        else
+        {
+            if (source->stream_data_tail)
+            {
+                /* better find the right place in queue for this client */
+                client_set_queue (client, NULL);
+                find_client_start (source, client);
+            }
+            else
+                client->intro_offset = 0;  /* replay intro file */
+            return -1;
+        }
+    }
+    return 0;
+}
+
+
+/* call this to verify that the HTTP data has been sent and if so setup
+ * callbacks to the appropriate format functions
+ */
+int format_check_http_buffer (source_t *source, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+
+    if (refbuf == NULL)
+        return -1;
+
+    if (client->respcode == 0)
+    {
+        DEBUG0("processing pending client headers");
+
+        if (format_prepare_headers (source, client) < 0)
+        {
+            ERROR0 ("internal problem, dropping client");
+            client->con->error = 1;
+            return -1;
+        }
+        client->respcode = 200;
+        stats_event_inc (NULL, "listeners");
+        stats_event_inc (NULL, "listener_connections");
+        stats_event_inc (source->mount, "listener_connections");
+    }
+
+    if (client->pos == refbuf->len)
+    {
+        client->write_to_client = source->format->write_buf_to_client;
+        client->check_buffer = format_check_file_buffer;
+        client->intro_offset = 0;
+        client->pos = refbuf->len = 4096;
+        return -1;
+    }
+    return 0;
+}
+
+
+int format_generic_write_to_client (client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+    int ret;
+    const char *buf = refbuf->data + client->pos;
+    unsigned int len = refbuf->len - client->pos;
+
+    ret = client_send_bytes (client, buf, len);
+
+    if (ret > 0)
+        client->pos += ret;
+
+    return ret;
+}
+
+
+/* This is the commonly used for source streams, here we just progress to
+ * the next buffer in the queue if there is no more left to be written from 
+ * the existing buffer.
+ */
+int format_advance_queue (source_t *source, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+
+    if (refbuf == NULL)
+        return -1;
+
+    if (refbuf->next == NULL && client->pos == refbuf->len)
+        return -1;
+
+    /* move to the next buffer if we have finished with the current one */
+    if (refbuf->next && client->pos == refbuf->len)
+    {
+        client_set_queue (client, refbuf->next);
+        refbuf = client->refbuf;
+    }
+    return 0;
+}
+
+
+static int format_prepare_headers (source_t *source, client_t *client)
+{
+    unsigned remaining;
+    char *ptr;
+    int bytes;
+    int bitrate_filtered = 0;
+    avl_node *node;
+    ice_config_t *config;
+
+    remaining = client->refbuf->len;
+    ptr = client->refbuf->data;
+    client->respcode = 200;
+
+    bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
+            "Content-Type: %s\r\n", source->format->contenttype);
+
+    remaining -= bytes;
+    ptr += bytes;
+
+    /* iterate through source http headers and send to client */
+    avl_tree_rlock(source->parser->vars);
+    node = avl_get_first(source->parser->vars);
+    while (node)
+    {
+        int next = 1;
+        http_var_t *var = (http_var_t *)node->key;
+        bytes = 0;
+        if (!strcasecmp(var->name, "ice-audio-info"))
+        {
+            /* convert ice-audio-info to icy-br */
+            char *brfield = NULL;
+            unsigned int bitrate;
+
+            if (bitrate_filtered == 0)
+                brfield = strstr(var->value, "bitrate=");
+            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
+            {           
+                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
+                next = 0;
+                bitrate_filtered = 1;
+            }
+            else
+                /* show ice-audio_info header as well because of relays */
+                bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value);
+        }
+        else
+        {
+            if (strcasecmp(var->name, "ice-password") &&
+                strcasecmp(var->name, "icy-metaint"))
+            {
+                if (!strncasecmp("ice-", var->name, 4))
+                {
+                    if (!strcasecmp("ice-public", var->name))
+                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
+                    else
+                        if (!strcasecmp ("ice-bitrate", var->name))
+                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
+                        else
+                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
+                                    var->name + 3, var->value);
+                }
+                else
+                    if (!strncasecmp("icy-", var->name, 4))
+                    {
+                        bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
+                                var->name + 3, var->value);
+                    }
+            }
+        }
+
+        remaining -= bytes;
+        ptr += bytes;
+        if (next)
+            node = avl_get_next(node);
+    }
+    avl_tree_unlock(source->parser->vars);
+
+    config = config_get_config();
+    bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id);
+    config_release_config();
+    remaining -= bytes;
+    ptr += bytes;
+
+    /* prevent proxy servers from caching */
+    bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n");
+    remaining -= bytes;
+    ptr += bytes;
+
+    bytes = snprintf (ptr, remaining, "\r\n");
+    remaining -= bytes;
+    ptr += bytes;
+
+    client->refbuf->len -= remaining;
+    if (source->format->create_client_data)
+        if (source->format->create_client_data (source, client) < 0)
+            return -1;
+    return 0;
+}
+
+

Deleted: icecast/tags/icecast-2.3.2/src/format.h
===================================================================
--- icecast/trunk/icecast/src/format.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,71 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* format.h
-**
-** format plugin header
-**
-*/
-#ifndef __FORMAT_H__
-#define __FORMAT_H__
-
-#include "client.h"
-#include "refbuf.h"
-#include "httpp/httpp.h"
-
-struct source_tag;
-struct _mount_proxy;
-
-typedef enum _format_type_tag
-{
-    FORMAT_ERROR, /* No format, source not processable */
-    FORMAT_TYPE_OGG,
-    FORMAT_TYPE_GENERIC
-} format_type_t;
-
-typedef struct _format_plugin_tag
-{
-    format_type_t type;
-
-    /* we need to know the mount to report statistics */
-    char *mount;
-
-    char *contenttype;
-    uint64_t read_bytes;
-    uint64_t sent_bytes;
-
-    refbuf_t *(*get_buffer)(struct source_tag *);
-    int (*write_buf_to_client)(client_t *client);
-    void (*write_buf_to_file)(struct source_tag *source, refbuf_t *refbuf);
-    int (*create_client_data)(struct source_tag *source, client_t *client);
-    void (*set_tag)(struct _format_plugin_tag *plugin, char *tag, char *value);
-    void (*free_plugin)(struct _format_plugin_tag *self);
-    void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
-
-    /* for internal state management */
-    void *_state;
-} format_plugin_t;
-
-format_type_t format_get_type(char *contenttype);
-char *format_get_mimetype(format_type_t type);
-int format_get_plugin(format_type_t type, struct source_tag *source);
-
-int format_generic_write_to_client (client_t *client);
-int format_advance_queue (struct source_tag *source, client_t *client);
-int format_check_http_buffer (struct source_tag *source, client_t *client);
-int format_check_file_buffer (struct source_tag *source, client_t *client);
-
-void format_send_general_headers(format_plugin_t *format, 
-        struct source_tag *source, client_t *client);
-
-#endif  /* __FORMAT_H__ */
-

Copied: icecast/tags/icecast-2.3.2/src/format.h (from rev 13663, icecast/trunk/icecast/src/format.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,72 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format.h
+**
+** format plugin header
+**
+*/
+#ifndef __FORMAT_H__
+#define __FORMAT_H__
+
+#include "client.h"
+#include "refbuf.h"
+#include "httpp/httpp.h"
+
+struct source_tag;
+struct _mount_proxy;
+
+typedef enum _format_type_tag
+{
+    FORMAT_ERROR, /* No format, source not processable */
+    FORMAT_TYPE_OGG,
+    FORMAT_TYPE_GENERIC
+} format_type_t;
+
+typedef struct _format_plugin_tag
+{
+    format_type_t type;
+
+    /* we need to know the mount to report statistics */
+    char *mount;
+
+    const char *contenttype;
+    char *charset;
+    uint64_t read_bytes;
+    uint64_t sent_bytes;
+
+    refbuf_t *(*get_buffer)(struct source_tag *);
+    int (*write_buf_to_client)(client_t *client);
+    void (*write_buf_to_file)(struct source_tag *source, refbuf_t *refbuf);
+    int (*create_client_data)(struct source_tag *source, client_t *client);
+    void (*set_tag)(struct _format_plugin_tag *plugin, const char *tag, const char *value, const char *charset);
+    void (*free_plugin)(struct _format_plugin_tag *self);
+    void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
+
+    /* for internal state management */
+    void *_state;
+} format_plugin_t;
+
+format_type_t format_get_type(const char *contenttype);
+char *format_get_mimetype(format_type_t type);
+int format_get_plugin(format_type_t type, struct source_tag *source);
+
+int format_generic_write_to_client (client_t *client);
+int format_advance_queue (struct source_tag *source, client_t *client);
+int format_check_http_buffer (struct source_tag *source, client_t *client);
+int format_check_file_buffer (struct source_tag *source, client_t *client);
+
+void format_send_general_headers(format_plugin_t *format, 
+        struct source_tag *source, client_t *client);
+
+#endif  /* __FORMAT_H__ */
+

Copied: icecast/tags/icecast-2.3.2/src/format_kate.c (from rev 14778, icecast/trunk/icecast/src/format_kate.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_kate.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_kate.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,232 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+/* Ogg codec handler for kate logical streams */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+#ifdef HAVE_KATE
+#include <kate/oggkate.h>
+#endif
+
+typedef struct source_tag source_t;
+
+#include "refbuf.h"
+#include "format_ogg.h"
+#include "format_kate.h"
+#include "client.h"
+#include "stats.h"
+
+#define CATMODULE "format-kate"
+#include "logging.h"
+
+
+typedef struct _kate_codec_tag
+{
+    int             headers_done;
+#ifdef HAVE_KATE
+    kate_info       ki;
+    kate_comment    kc;
+#endif
+    int             num_headers;
+    int             granule_shift;
+    ogg_int64_t     last_iframe;
+    ogg_int64_t     prev_granulepos;
+} kate_codec_t;
+
+
+static void kate_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    kate_codec_t *kate = codec->specific;
+
+    DEBUG0 ("freeing kate codec");
+    /* TODO: should i replace with something or just remove
+    stats_event (ogg_info->mount, "video_bitrate", NULL);
+    stats_event (ogg_info->mount, "video_quality", NULL);
+    stats_event (ogg_info->mount, "frame_rate", NULL);
+    stats_event (ogg_info->mount, "frame_size", NULL);
+    */
+#ifdef HAVE_KATE
+    kate_info_clear (&kate->ki);
+    kate_comment_clear (&kate->kc);
+#endif
+    ogg_stream_clear (&codec->os);
+    free (kate);
+    free (codec);
+}
+
+
+/* kate pages are not rebuilt, so here we just for headers and then
+ * pass them straight through to the the queue
+ */
+static refbuf_t *process_kate_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page)
+{
+    kate_codec_t *kate = codec->specific;
+    ogg_packet packet;
+    int header_page = 0;
+    refbuf_t *refbuf = NULL;
+    ogg_int64_t granulepos;
+
+    if (ogg_stream_pagein (&codec->os, page) < 0)
+    {
+        ogg_info->error = 1;
+        return NULL;
+    }
+    granulepos = ogg_page_granulepos (page);
+
+    while (ogg_stream_packetout (&codec->os, &packet) > 0)
+    {
+        if (!kate->headers_done)
+        {
+#ifdef HAVE_KATE
+            int ret = kate_ogg_decode_headerin (&kate->ki, &kate->kc, &packet);
+            if (ret < 0)
+            {
+                ogg_info->error = 1;
+                WARN0 ("problem with kate header");
+                return NULL;
+            }
+            header_page = 1;
+            kate->num_headers = kate->ki.num_headers;
+            codec->headers++;
+            if (ret > 0)
+            {
+                kate->headers_done = 1;
+                /* TODO: what to replace this with ?
+                ogg_info->bitrate += theora->ti.target_bitrate;
+                stats_event_args (ogg_info->mount, "video_bitrate", "%ld",
+                        (long)theora->ti.target_bitrate);
+                stats_event_args (ogg_info->mount, "video_quality", "%ld",
+                        (long)theora->ti.quality);
+                stats_event_args (ogg_info->mount, "frame_size", "%ld x %ld",
+                        (long)theora->ti.frame_width,
+                        (long)theora->ti.frame_height);
+                stats_event_args (ogg_info->mount, "frame_rate", "%.2f",
+                        (float)theora->ti.fps_numerator/theora->ti.fps_denominator);
+                */
+            }
+            continue;
+#else
+            header_page = (packet.bytes>0 && (packet.packet[0]&0x80));
+            if (!header_page)
+                break;
+            codec->headers++;
+            if (packet.packet[0]==0x80)
+            {
+                if (packet.bytes<64) return NULL;
+                /* we peek for the number of headers to expect */
+                kate->num_headers = packet.packet[11];
+            }
+            continue;
+#endif
+        }
+
+        if (codec->headers < kate->num_headers)
+        {
+            ogg_info->error = 1;
+            ERROR0 ("Not enough header packets");
+            return NULL;
+        }
+    }
+    if (header_page)
+    {
+        format_ogg_attach_header (ogg_info, page);
+        return NULL;
+    }
+
+    refbuf = make_refbuf_with_page (page);
+    /* DEBUG3 ("refbuf %p has pageno %ld, %llu", refbuf, ogg_page_pageno (page), (uint64_t)granulepos); */
+
+    if (codec->possible_start)
+    {
+        /* we don't bother trying to know where we can start, we'll just
+           start whenever we have to, video's more important and in the majority
+           of the cases it's ok if we lose an event we're seeking in the middle
+           of, as we won't have display artifacts as we'd have with video */
+        codec->possible_start->sync_point = 1;
+        refbuf_release (codec->possible_start);
+        codec->possible_start = NULL;
+    }
+    if (granulepos != kate->prev_granulepos || granulepos == 0)
+    {
+        if (codec->possible_start)
+            refbuf_release (codec->possible_start);
+        refbuf_addref (refbuf);
+        codec->possible_start = refbuf;
+    }
+    kate->prev_granulepos = granulepos;
+
+    return refbuf;
+}
+
+
+/* Check if specified BOS page is the start of a kate stream and
+ * if so, create a codec structure for handling it
+ */
+ogg_codec_t *initial_kate_page (format_plugin_t *plugin, ogg_page *page)
+{
+    ogg_state_t *ogg_info = plugin->_state;
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    kate_codec_t *kate_codec = calloc (1, sizeof (kate_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+#ifdef HAVE_KATE
+    kate_info_init (&kate_codec->ki);
+    kate_comment_init (&kate_codec->kc);
+#endif
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    DEBUG0("checking for kate codec");
+#ifdef HAVE_KATE
+    if (kate_ogg_decode_headerin (&kate_codec->ki, &kate_codec->kc, &packet) < 0)
+    {
+        kate_info_clear (&kate_codec->ki);
+        kate_comment_clear (&kate_codec->kc);
+        ogg_stream_clear (&codec->os);
+        free (kate_codec);
+        free (codec);
+        return NULL;
+    }
+#else
+    /* we don't have libkate, so we examine the packet magic by hand */
+    if ((packet.bytes<9) || memcmp(packet.packet, "\x80kate\0\0\0\0", 9))
+    {
+        ogg_stream_clear (&codec->os);
+        free (kate_codec);
+        free (codec);
+        return NULL;
+    }
+#endif
+
+    INFO0 ("seen initial kate header");
+    codec->specific = kate_codec;
+    codec->process_page = process_kate_page;
+    codec->codec_free = kate_codec_free;
+    codec->headers = 1;
+    codec->name = "Kate";
+
+    format_ogg_attach_header (ogg_info, page);
+    ogg_info->codec_sync = codec;
+    return codec;
+}
+

Copied: icecast/tags/icecast-2.3.2/src/format_kate.h (from rev 14778, icecast/trunk/icecast/src/format_kate.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_kate.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_kate.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,21 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+#ifndef __FORMAT_KATE_H
+#define __FORMAT_KATE_H
+
+#include "format_ogg.h"
+
+ogg_codec_t *initial_kate_page (format_plugin_t *plugin, ogg_page *page);
+
+#endif /* __FORMAT_KATE_H */

Deleted: icecast/tags/icecast-2.3.2/src/format_mp3.c
===================================================================
--- icecast/trunk/icecast/src/format_mp3.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format_mp3.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,685 +0,0 @@
-/* -*- c-basic-offset: 4; -*- */
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* format_mp3.c
-**
-** format plugin for mp3
-**
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-
-#include "refbuf.h"
-#include "source.h"
-#include "client.h"
-
-#include "stats.h"
-#include "format.h"
-#include "httpp/httpp.h"
-
-#include "logging.h"
-
-#include "format_mp3.h"
-
-#ifdef WIN32
-#define strcasecmp stricmp
-#define strncasecmp strnicmp
-#define snprintf _snprintf
-#endif
-
-#define CATMODULE "format-mp3"
-
-/* Note that this seems to be 8192 in shoutcast - perhaps we want to be the
- * same for compability with crappy clients?
- */
-#define ICY_METADATA_INTERVAL 16000
-
-static void format_mp3_free_plugin(format_plugin_t *self);
-static refbuf_t *mp3_get_filter_meta (source_t *source);
-static refbuf_t *mp3_get_no_meta (source_t *source);
-
-static int  format_mp3_create_client_data (source_t *source, client_t *client);
-static void free_mp3_client_data (client_t *client);
-static int format_mp3_write_buf_to_client(client_t *client);
-static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf);
-static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value);
-static void format_mp3_apply_settings(client_t *client, format_plugin_t *format, mount_proxy *mount);
-
-
-typedef struct {
-   unsigned int interval;
-   int metadata_offset;
-   unsigned int since_meta_block;
-   int in_metadata;
-   refbuf_t *associated;
-} mp3_client_data;
-
-int format_mp3_get_plugin (source_t *source)
-{
-    char *metadata;
-    format_plugin_t *plugin;
-    mp3_state *state = calloc(1, sizeof(mp3_state));
-    refbuf_t *meta;
-
-    plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
-
-    plugin->type = FORMAT_TYPE_GENERIC;
-    plugin->get_buffer = mp3_get_no_meta;
-    plugin->write_buf_to_client = format_mp3_write_buf_to_client;
-    plugin->write_buf_to_file = write_mp3_to_file;
-    plugin->create_client_data = format_mp3_create_client_data;
-    plugin->free_plugin = format_mp3_free_plugin;
-    plugin->set_tag = mp3_set_tag;
-    plugin->apply_settings = format_mp3_apply_settings;
-
-    plugin->contenttype = httpp_getvar (source->parser, "content-type");
-    if (plugin->contenttype == NULL) {
-        /* We default to MP3 audio for old clients without content types */
-        plugin->contenttype = "audio/mpeg";
-    }
-
-    plugin->_state = state;
-
-    /* initial metadata needs to be blank for sending to clients and for
-       comparing with new metadata */
-    meta = refbuf_new (17);
-    memcpy (meta->data, "\001StreamTitle='';", 17);
-    state->metadata = meta;
-    state->interval = -1;
-
-    metadata = httpp_getvar (source->parser, "icy-metaint");
-    if (metadata)
-    {
-        state->inline_metadata_interval = atoi (metadata);
-        if (state->inline_metadata_interval > 0)
-        {
-            state->offset = 0;
-            plugin->get_buffer = mp3_get_filter_meta;
-            state->interval = state->inline_metadata_interval;
-        }
-    }
-    source->format = plugin;
-    thread_mutex_create (&state->url_lock);
-
-    return 0;
-}
-
-
-static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value)
-{
-    mp3_state *source_mp3 = plugin->_state;
-    unsigned int len;
-    const char meta[] = "StreamTitle='";
-    int size = sizeof (meta) + 1;
-
-    if (tag==NULL || value == NULL)
-        return;
-
-    len = strlen (value)+1;
-    size += len;
-    /* protect against multiple updaters */
-    thread_mutex_lock (&source_mp3->url_lock);
-    if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
-    {
-        char *p = strdup (value);
-        if (p)
-        {
-            free (source_mp3->url_title);
-            free (source_mp3->url_artist);
-            source_mp3->url_artist = NULL;
-            source_mp3->url_title = p;
-            source_mp3->update_metadata = 1;
-        }
-    }
-    else if (strcmp (tag, "artist") == 0)
-    {
-        char *p = strdup (value);
-        if (p)
-        {
-            free (source_mp3->url_artist);
-            source_mp3->url_artist = p;
-            source_mp3->update_metadata = 1;
-        }
-    }
-    thread_mutex_unlock (&source_mp3->url_lock);
-}
-
-
-static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned int meta_len)
-{
-    if (metadata)
-    {
-        char *end, *p;
-        int len;
-
-        do
-        {
-            metadata++;
-            if (strncmp (metadata, "StreamTitle='", 13))
-                break;
-            if ((end = strstr (metadata, "\';")) == NULL)
-                break;
-            len = (end - metadata) - 13;
-            p = calloc (1, len+1);
-            if (p)
-            {
-                memcpy (p, metadata+13, len);
-                logging_playlist (source->mount, p, source->listeners);
-                stats_event (source->mount, "title", p);
-                yp_touch (source->mount);
-                free (p);
-            }
-        } while (0);
-    }
-}
-
-
-static void format_mp3_apply_settings (client_t *client, format_plugin_t *format, mount_proxy *mount)
-{
-    mp3_state *source_mp3 = format->_state;
-
-    if (mount == NULL || mount->mp3_meta_interval < 0)
-    {
-        char *metadata = httpp_getvar (client->parser, "icy-metaint");
-        source_mp3->interval = -1;
-        if (metadata)
-        {
-            int interval = atoi (metadata);
-            if (interval > 0)
-                source_mp3->interval = interval;
-        }
-    }
-    else
-        source_mp3->interval = mount->mp3_meta_interval;
-    DEBUG1 ("mp3 interval %d", source_mp3->interval);
-}
-
-
-/* called from the source thread when the metadata has been updated.
- * The artist title are checked and made ready for clients to send
- */
-static void mp3_set_title (source_t *source)
-{
-    const char meta[] = "StreamTitle='";
-    int size;
-    unsigned char len_byte;
-    refbuf_t *p;
-    unsigned int len = sizeof(meta) + 2; /* the StreamTitle, quotes, ; and null */
-    mp3_state *source_mp3 = source->format->_state;
-
-    /* make sure the url data does not disappear from under us */
-    thread_mutex_lock (&source_mp3->url_lock);
-
-    /* work out message length */
-    if (source_mp3->url_artist)
-        len += strlen (source_mp3->url_artist);
-    if (source_mp3->url_title)
-        len += strlen (source_mp3->url_title);
-    if (source_mp3->url_artist && source_mp3->url_title)
-        len += 3;
-#define MAX_META_LEN 255*16
-    if (len > MAX_META_LEN)
-    {
-        thread_mutex_unlock (&source_mp3->url_lock);
-        WARN1 ("Metadata too long at %d chars", len);
-        return;
-    }
-    /* work out the metadata len byte */
-    len_byte = (len-1) / 16 + 1;
-
-    /* now we know how much space to allocate, +1 for the len byte */
-    size = len_byte * 16 + 1;
-
-    p = refbuf_new (size);
-    if (p)
-    {
-        mp3_state *source_mp3 = source->format->_state;
-
-        memset (p->data, '\0', size);
-        if (source_mp3->url_artist && source_mp3->url_title)
-            snprintf (p->data, size, "%c%s%s - %s';", len_byte, meta,
-                    source_mp3->url_artist, source_mp3->url_title);
-        else
-            snprintf (p->data, size, "%c%s%s';", len_byte, meta,
-                    source_mp3->url_title);
-        filter_shoutcast_metadata (source, p->data, size);
-
-        refbuf_release (source_mp3->metadata);
-        source_mp3->metadata = p;
-    }
-    thread_mutex_unlock (&source_mp3->url_lock);
-}
-
-
-/* send the appropriate metadata, and return the number of bytes written
- * which is 0 or greater.  Check the client in_metadata value afterwards
- * to see if all metadata has been sent
- */
-static int send_mp3_metadata (client_t *client, refbuf_t *associated)
-{
-    int ret = 0;
-    unsigned char *metadata;
-    int meta_len;
-    mp3_client_data *client_mp3 = client->format_data;
-
-    /* If there is a change in metadata then send it else
-     * send a single zero value byte in its place
-     */
-    if (associated && associated != client_mp3->associated)
-    {
-        metadata = associated->data + client_mp3->metadata_offset;
-        meta_len = associated->len - client_mp3->metadata_offset;
-    }
-    else
-    {
-        if (associated)
-        {
-            metadata = "\0";
-            meta_len = 1;
-        }
-        else
-        {
-            char *meta = "\001StreamTitle='';";
-            metadata = meta + client_mp3->metadata_offset;
-            meta_len = 17 - client_mp3->metadata_offset;
-        }
-    }
-    ret = client_send_bytes (client, metadata, meta_len);
-
-    if (ret == meta_len)
-    {
-        client_mp3->associated = associated;
-        client_mp3->metadata_offset = 0;
-        client_mp3->in_metadata = 0;
-        client_mp3->since_meta_block = 0;
-        return ret;
-    }
-    if (ret > 0)
-        client_mp3->metadata_offset += ret;
-    else
-        ret = 0;
-    client_mp3->in_metadata = 1;
-
-    return ret;
-}
-
-
-/* Handler for writing mp3 data to a client, taking into account whether
- * client has requested shoutcast style metadata updates
- */
-static int format_mp3_write_buf_to_client(client_t *client)
-{
-    int ret, written = 0;
-    mp3_client_data *client_mp3 = client->format_data;
-    refbuf_t *refbuf = client->refbuf;
-    char *buf = refbuf->data + client->pos;
-    unsigned int len = refbuf->len - client->pos;
-
-    do
-    {
-        /* send any unwritten metadata to the client */
-        if (client_mp3->in_metadata)
-        {
-            refbuf_t *associated = refbuf->associated;
-            ret = send_mp3_metadata (client, associated);
-
-            if (client_mp3->in_metadata)
-                break;
-            written += ret;
-        }
-        /* see if we need to send the current metadata to the client */
-        if (client_mp3->interval)
-        {
-            unsigned int remaining = client_mp3->interval -
-                client_mp3->since_meta_block;
-
-            /* sending the metadata block */
-            if (remaining <= len)
-            {
-                /* send any mp3 before the metadata block */
-                if (remaining)
-                {
-                    ret = client_send_bytes (client, buf, remaining);
-
-                    if (ret > 0)
-                    {
-                        client_mp3->since_meta_block += ret;
-                        client->pos += ret;
-                    }
-                    if (ret < (int)remaining)
-                        break;
-                    written += ret;
-                }
-                ret = send_mp3_metadata (client, refbuf->associated);
-                if (client_mp3->in_metadata)
-                    break;
-                written += ret;
-                /* change buf and len */
-                buf += remaining;
-                len -= remaining;
-                /* limit how much mp3 we send if using small intervals */
-                if (len > client_mp3->interval)
-                    len = client_mp3->interval;
-            }
-        }
-        /* write any mp3, maybe after the metadata block */
-        if (len)
-        {
-            ret = client_send_bytes (client, buf, len);
-
-            if (ret > 0)
-            {
-                client_mp3->since_meta_block += ret;
-                client->pos += ret;
-            }
-            if (ret < (int)len)
-                break;
-            written += ret;
-        }
-        ret = 0;
-    } while (0);
-
-    if (ret > 0)
-        written += ret;
-    return written;
-}
-
-static void format_mp3_free_plugin(format_plugin_t *self)
-{
-    /* free the plugin instance */
-    mp3_state *state = self->_state;
-
-    thread_mutex_destroy (&state->url_lock);
-    free (state->url_artist);
-    free (state->url_title);
-    refbuf_release (state->metadata);
-    refbuf_release (state->read_data);
-    free(state);
-    free(self);
-}
-
-
-/* This does the actual reading, making sure the read data is packaged in
- * blocks of 1400 bytes (near the common MTU size). This is because many
- * incoming streams come in small packets which could waste a lot of 
- * bandwidth with many listeners due to headers and such like.
- */
-static int complete_read (source_t *source)
-{
-    int bytes;
-    format_plugin_t *format = source->format;
-    mp3_state *source_mp3 = format->_state;
-    char *buf;
-    refbuf_t *refbuf;
-
-#define REFBUF_SIZE 1400
-
-    if (source_mp3->read_data == NULL)
-    {
-        source_mp3->read_data = refbuf_new (REFBUF_SIZE); 
-        source_mp3->read_count = 0;
-    }
-    buf = source_mp3->read_data->data + source_mp3->read_count;
-
-    bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count);
-    if (bytes < 0)
-    {
-        if (source->client->con->error)
-        {
-            refbuf_release (source_mp3->read_data);
-            source_mp3->read_data = NULL;
-        }
-        return 0;
-    }
-    source_mp3->read_count += bytes;
-    refbuf = source_mp3->read_data;
-    refbuf->len = source_mp3->read_count;
-    format->read_bytes += bytes;
-
-    if (source_mp3->read_count < REFBUF_SIZE)
-    {
-        if (source_mp3->read_count == 0)
-        {
-            refbuf_release (source_mp3->read_data);
-            source_mp3->read_data = NULL;
-        }
-        return 0;
-    }
-    return 1;
-}
-
-
-/* read an mp3 stream which does not have shoutcast style metadata */
-static refbuf_t *mp3_get_no_meta (source_t *source)
-{
-    refbuf_t *refbuf;
-    mp3_state *source_mp3 = source->format->_state;
-
-    if (complete_read (source) == 0)
-        return NULL;
-
-    refbuf = source_mp3->read_data;
-    source_mp3->read_data = NULL;
-
-    if (source_mp3->update_metadata)
-    {
-        mp3_set_title (source);
-        source_mp3->update_metadata = 0;
-    }
-    refbuf->associated = source_mp3->metadata;
-    refbuf_addref (source_mp3->metadata);
-    refbuf->sync_point = 1;
-    return refbuf;
-}
-
-
-/* read mp3 data with inlined metadata from the source. Filter out the
- * metadata so that the mp3 data itself is store on the queue and the
- * metadata is is associated with it
- */
-static refbuf_t *mp3_get_filter_meta (source_t *source)
-{
-    refbuf_t *refbuf;
-    format_plugin_t *plugin = source->format;
-    mp3_state *source_mp3 = plugin->_state;
-    unsigned char *src;
-    unsigned int bytes, mp3_block;
-
-    if (complete_read (source) == 0)
-        return NULL;
-
-    refbuf = source_mp3->read_data;
-    source_mp3->read_data = NULL;
-    src = refbuf->data;
-
-    if (source_mp3->update_metadata)
-    {
-        mp3_set_title (source);
-        source_mp3->update_metadata = 0;
-    }
-    /* fill the buffer with the read data */
-    bytes = source_mp3->read_count;
-    refbuf->len = 0;
-    while (bytes > 0)
-    {
-        unsigned int metadata_remaining;
-
-        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;
-
-        /* is there only enough to account for mp3 data */
-        if (bytes <= mp3_block)
-        {
-            refbuf->len += bytes;
-            source_mp3->offset += bytes;
-            break;
-        }
-        /* we have enough data to get to the metadata
-         * block, but only transfer upto it */
-        if (mp3_block)
-        {
-            src += mp3_block;
-            bytes -= mp3_block;
-            refbuf->len += mp3_block;
-            source_mp3->offset += mp3_block;
-            continue;
-        }
-
-        /* process the inline metadata, len == 0 indicates not seen any yet */
-        if (source_mp3->build_metadata_len == 0)
-        {
-            memset (source_mp3->build_metadata, 0,
-                    sizeof (source_mp3->build_metadata));
-            source_mp3->build_metadata_offset = 0;
-            source_mp3->build_metadata_len = 1 + (*src * 16);
-        }
-
-        /* do we have all of the metatdata block */
-        metadata_remaining = source_mp3->build_metadata_len -
-            source_mp3->build_metadata_offset;
-        if (bytes < metadata_remaining)
-        {
-            memcpy (source_mp3->build_metadata +
-                    source_mp3->build_metadata_offset, src, bytes);
-            source_mp3->build_metadata_offset += bytes;
-            break;
-        }
-        /* copy all bytes except the last one, that way we 
-         * know a null byte terminates the message */
-        memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset,
-                src, metadata_remaining-1);
-
-        /* overwrite metadata in the buffer */
-        bytes -= metadata_remaining;
-        memmove (src, src+metadata_remaining, bytes);
-
-        /* assign metadata if it's greater than 1 byte, and the text has changed */
-        if (source_mp3->build_metadata_len > 1 &&
-                strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
-        {
-            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
-            memcpy (meta->data, source_mp3->build_metadata,
-                    source_mp3->build_metadata_len);
-
-            DEBUG1("shoutcast metadata %.4080s", meta->data+1);
-            if (strncmp (meta->data+1, "StreamTitle=", 12) == 0)
-            {
-                filter_shoutcast_metadata (source, source_mp3->build_metadata,
-                        source_mp3->build_metadata_len);
-                refbuf_release (source_mp3->metadata);
-                source_mp3->metadata = meta;
-            }
-            else
-            {
-                ERROR0 ("Incorrect metadata format, ending stream");
-                source->running = 0;
-                refbuf_release (refbuf);
-                refbuf_release (meta);
-                return NULL;
-            }
-        }
-        source_mp3->offset = 0;
-        source_mp3->build_metadata_len = 0;
-    }
-    /* the data we have just read may of just been metadata */
-    if (refbuf->len == 0)
-    {
-        refbuf_release (refbuf);
-        return NULL;
-    }
-    refbuf->associated = source_mp3->metadata;
-    refbuf_addref (source_mp3->metadata);
-    refbuf->sync_point = 1;
-
-    return refbuf;
-}
-
-
-static int format_mp3_create_client_data(source_t *source, client_t *client)
-{
-    mp3_client_data *client_mp3 = calloc(1,sizeof(mp3_client_data));
-    mp3_state *source_mp3 = source->format->_state;
-    const char *metadata; 
-    /* the +-2 is for overwriting the last set of \r\n */
-    unsigned remaining = 4096 - client->refbuf->len + 2;
-    char *ptr = client->refbuf->data + client->refbuf->len - 2;
-    int bytes;
-
-    if (client_mp3 == NULL)
-        return -1;
-
-    /* hack for flash player, it wants a length */
-    if (httpp_getvar(client->parser, "x-flash-version"))
-    {
-        bytes = snprintf (ptr, remaining, "Content-Length: 347122319\r\n");
-        remaining -= bytes;
-        ptr += bytes;
-    }
-
-    client->format_data = client_mp3;
-    client->free_client_data = free_mp3_client_data;
-    metadata = httpp_getvar(client->parser, "icy-metadata");
-    if (metadata && atoi(metadata))
-    {
-        if (source_mp3->interval >= 0)
-            client_mp3->interval = source_mp3->interval;
-        else
-            client_mp3->interval = ICY_METADATA_INTERVAL;
-        if (client_mp3->interval)
-        {
-            bytes = snprintf (ptr, remaining, "icy-metaint:%u\r\n",
-                    client_mp3->interval);
-            if (bytes > 0)
-            {
-                remaining -= bytes;
-                ptr += bytes;
-            }
-        }
-    }
-    bytes = snprintf (ptr, remaining, "\r\n");
-    remaining -= bytes;
-    ptr += bytes;
-
-    client->refbuf->len = 4096 - remaining;
-
-    return 0;
-}
-
-
-static void free_mp3_client_data (client_t *client)
-{
-    free (client->format_data);
-    client->format_data = NULL;
-}
-
-
-static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
-{
-    if (refbuf->len == 0)
-        return;
-    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
-    {
-        WARN0 ("Write to dump file failed, disabling");
-        fclose (source->dumpfile);
-        source->dumpfile = NULL;
-    }
-}
-

Copied: icecast/tags/icecast-2.3.2/src/format_mp3.c (from rev 14014, icecast/trunk/icecast/src/format_mp3.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_mp3.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_mp3.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,709 @@
+/* -*- c-basic-offset: 4; -*- */
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_mp3.c
+**
+** format plugin for mp3
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+
+#include "format_mp3.h"
+
+#ifdef WIN32
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "format-mp3"
+
+/* Note that this seems to be 8192 in shoutcast - perhaps we want to be the
+ * same for compability with crappy clients?
+ */
+#define ICY_METADATA_INTERVAL 16000
+
+static void format_mp3_free_plugin(format_plugin_t *self);
+static refbuf_t *mp3_get_filter_meta (source_t *source);
+static refbuf_t *mp3_get_no_meta (source_t *source);
+
+static int  format_mp3_create_client_data (source_t *source, client_t *client);
+static void free_mp3_client_data (client_t *client);
+static int format_mp3_write_buf_to_client(client_t *client);
+static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf);
+static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset);
+static void format_mp3_apply_settings(client_t *client, format_plugin_t *format, mount_proxy *mount);
+
+
+typedef struct {
+   unsigned int interval;
+   int metadata_offset;
+   unsigned int since_meta_block;
+   int in_metadata;
+   refbuf_t *associated;
+} mp3_client_data;
+
+int format_mp3_get_plugin (source_t *source)
+{
+    const char *metadata;
+    format_plugin_t *plugin;
+    mp3_state *state = calloc(1, sizeof(mp3_state));
+    refbuf_t *meta;
+
+    plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
+
+    plugin->type = FORMAT_TYPE_GENERIC;
+    plugin->get_buffer = mp3_get_no_meta;
+    plugin->write_buf_to_client = format_mp3_write_buf_to_client;
+    plugin->write_buf_to_file = write_mp3_to_file;
+    plugin->create_client_data = format_mp3_create_client_data;
+    plugin->free_plugin = format_mp3_free_plugin;
+    plugin->set_tag = mp3_set_tag;
+    plugin->apply_settings = format_mp3_apply_settings;
+
+    plugin->contenttype = httpp_getvar (source->parser, "content-type");
+    if (plugin->contenttype == NULL) {
+        /* We default to MP3 audio for old clients without content types */
+        plugin->contenttype = "audio/mpeg";
+    }
+
+    plugin->_state = state;
+
+    /* initial metadata needs to be blank for sending to clients and for
+       comparing with new metadata */
+    meta = refbuf_new (17);
+    memcpy (meta->data, "\001StreamTitle='';", 17);
+    state->metadata = meta;
+    state->interval = -1;
+
+    metadata = httpp_getvar (source->parser, "icy-metaint");
+    if (metadata)
+    {
+        state->inline_metadata_interval = atoi (metadata);
+        if (state->inline_metadata_interval > 0)
+        {
+            state->offset = 0;
+            plugin->get_buffer = mp3_get_filter_meta;
+            state->interval = state->inline_metadata_interval;
+        }
+    }
+    source->format = plugin;
+    thread_mutex_create (&state->url_lock);
+
+    return 0;
+}
+
+
+static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset)
+{
+    mp3_state *source_mp3 = plugin->_state;
+    unsigned int len;
+    const char meta[] = "StreamTitle='";
+    int size = sizeof (meta) + 1;
+    char *value;
+
+    if (tag==NULL || in_value == NULL)
+        return;
+
+    /* protect against multiple updaters */
+    thread_mutex_lock (&source_mp3->url_lock);
+
+    value = util_conv_string (in_value, charset, plugin->charset);
+    if (value == NULL)
+        value = strdup (in_value);
+
+    len = strlen (value)+1;
+    size += len;
+
+    if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
+    {
+        free (source_mp3->url_title);
+        free (source_mp3->url_artist);
+        source_mp3->url_artist = NULL;
+        source_mp3->url_title = value;
+        source_mp3->update_metadata = 1;
+    }
+    else if (strcmp (tag, "artist") == 0)
+    {
+        free (source_mp3->url_artist);
+        source_mp3->url_artist = value;
+        source_mp3->update_metadata = 1;
+    }
+    else
+        free (value);
+    thread_mutex_unlock (&source_mp3->url_lock);
+}
+
+
+static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned int meta_len)
+{
+    if (metadata)
+    {
+        char *end, *p;
+        int len;
+
+        do
+        {
+            metadata++;
+            if (strncmp (metadata, "StreamTitle='", 13))
+                break;
+            if ((end = strstr (metadata+13, "\';")) == NULL)
+                break;
+            len = (end - metadata) - 13;
+            p = calloc (1, len+1);
+            if (p)
+            {
+                memcpy (p, metadata+13, len);
+                logging_playlist (source->mount, p, source->listeners);
+                stats_event_conv (source->mount, "title", p, source->format->charset);
+                yp_touch (source->mount);
+                free (p);
+            }
+        } while (0);
+    }
+}
+
+
+static void format_mp3_apply_settings (client_t *client, format_plugin_t *format, mount_proxy *mount)
+{
+    mp3_state *source_mp3 = format->_state;
+
+    source_mp3->interval = -1;
+    free (format->charset);
+    format->charset = NULL;
+
+    if (mount)
+    {
+        if (mount->mp3_meta_interval > 0)
+            source_mp3->interval = mount->mp3_meta_interval;
+        if (mount->charset)
+            format->charset = strdup (mount->charset);
+    }
+    if (source_mp3->interval <= 0)
+    {
+        const char *metadata = httpp_getvar (client->parser, "icy-metaint");
+        source_mp3->interval = ICY_METADATA_INTERVAL;
+        if (metadata)
+        {
+            int interval = atoi (metadata);
+            if (interval > 0)
+                source_mp3->interval = interval;
+        }
+    }
+
+    if (format->charset == NULL)
+        format->charset = strdup ("ISO8859-1");
+
+    DEBUG1 ("sending metadata interval %d", source_mp3->interval);
+    DEBUG1 ("charset %s", format->charset);
+}
+
+
+/* called from the source thread when the metadata has been updated.
+ * The artist title are checked and made ready for clients to send
+ */
+static void mp3_set_title (source_t *source)
+{
+    const char meta[] = "StreamTitle='";
+    int size;
+    unsigned char len_byte;
+    refbuf_t *p;
+    unsigned int len = sizeof(meta) + 2; /* the StreamTitle, quotes, ; and null */
+    mp3_state *source_mp3 = source->format->_state;
+
+    /* make sure the url data does not disappear from under us */
+    thread_mutex_lock (&source_mp3->url_lock);
+
+    /* work out message length */
+    if (source_mp3->url_artist)
+        len += strlen (source_mp3->url_artist);
+    if (source_mp3->url_title)
+        len += strlen (source_mp3->url_title);
+    if (source_mp3->url_artist && source_mp3->url_title)
+        len += 3;
+#define MAX_META_LEN 255*16
+    if (len > MAX_META_LEN)
+    {
+        thread_mutex_unlock (&source_mp3->url_lock);
+        WARN1 ("Metadata too long at %d chars", len);
+        return;
+    }
+    /* work out the metadata len byte */
+    len_byte = (len-1) / 16 + 1;
+
+    /* now we know how much space to allocate, +1 for the len byte */
+    size = len_byte * 16 + 1;
+
+    p = refbuf_new (size);
+    if (p)
+    {
+        mp3_state *source_mp3 = source->format->_state;
+
+        memset (p->data, '\0', size);
+        if (source_mp3->url_artist && source_mp3->url_title)
+            snprintf (p->data, size, "%c%s%s - %s';", len_byte, meta,
+                    source_mp3->url_artist, source_mp3->url_title);
+        else
+            snprintf (p->data, size, "%c%s%s';", len_byte, meta,
+                    source_mp3->url_title);
+        filter_shoutcast_metadata (source, p->data, size);
+
+        refbuf_release (source_mp3->metadata);
+        source_mp3->metadata = p;
+    }
+    thread_mutex_unlock (&source_mp3->url_lock);
+}
+
+
+/* send the appropriate metadata, and return the number of bytes written
+ * which is 0 or greater.  Check the client in_metadata value afterwards
+ * to see if all metadata has been sent
+ */
+static int send_mp3_metadata (client_t *client, refbuf_t *associated)
+{
+    int ret = 0;
+    char *metadata;
+    int meta_len;
+    mp3_client_data *client_mp3 = client->format_data;
+
+    /* If there is a change in metadata then send it else
+     * send a single zero value byte in its place
+     */
+    if (associated && associated != client_mp3->associated)
+    {
+        metadata = associated->data + client_mp3->metadata_offset;
+        meta_len = associated->len - client_mp3->metadata_offset;
+    }
+    else
+    {
+        if (associated)
+        {
+            metadata = "\0";
+            meta_len = 1;
+        }
+        else
+        {
+            char *meta = "\001StreamTitle='';";
+            metadata = meta + client_mp3->metadata_offset;
+            meta_len = 17 - client_mp3->metadata_offset;
+        }
+    }
+    ret = client_send_bytes (client, metadata, meta_len);
+
+    if (ret == meta_len)
+    {
+        client_mp3->associated = associated;
+        client_mp3->metadata_offset = 0;
+        client_mp3->in_metadata = 0;
+        client_mp3->since_meta_block = 0;
+        return ret;
+    }
+    if (ret > 0)
+        client_mp3->metadata_offset += ret;
+    else
+        ret = 0;
+    client_mp3->in_metadata = 1;
+
+    return ret;
+}
+
+
+/* Handler for writing mp3 data to a client, taking into account whether
+ * client has requested shoutcast style metadata updates
+ */
+static int format_mp3_write_buf_to_client(client_t *client)
+{
+    int ret, written = 0;
+    mp3_client_data *client_mp3 = client->format_data;
+    refbuf_t *refbuf = client->refbuf;
+    char *buf = refbuf->data + client->pos;
+    unsigned int len = refbuf->len - client->pos;
+
+    do
+    {
+        /* send any unwritten metadata to the client */
+        if (client_mp3->in_metadata)
+        {
+            refbuf_t *associated = refbuf->associated;
+            ret = send_mp3_metadata (client, associated);
+
+            if (client_mp3->in_metadata)
+                break;
+            written += ret;
+        }
+        /* see if we need to send the current metadata to the client */
+        if (client_mp3->interval)
+        {
+            unsigned int remaining = client_mp3->interval -
+                client_mp3->since_meta_block;
+
+            /* sending the metadata block */
+            if (remaining <= len)
+            {
+                /* send any mp3 before the metadata block */
+                if (remaining)
+                {
+                    ret = client_send_bytes (client, buf, remaining);
+
+                    if (ret > 0)
+                    {
+                        client_mp3->since_meta_block += ret;
+                        client->pos += ret;
+                    }
+                    if (ret < (int)remaining)
+                        break;
+                    written += ret;
+                }
+                ret = send_mp3_metadata (client, refbuf->associated);
+                if (client_mp3->in_metadata)
+                    break;
+                written += ret;
+                /* change buf and len */
+                buf += remaining;
+                len -= remaining;
+                /* limit how much mp3 we send if using small intervals */
+                if (len > client_mp3->interval)
+                    len = client_mp3->interval;
+            }
+        }
+        /* write any mp3, maybe after the metadata block */
+        if (len)
+        {
+            ret = client_send_bytes (client, buf, len);
+
+            if (ret > 0)
+            {
+                client_mp3->since_meta_block += ret;
+                client->pos += ret;
+            }
+            if (ret < (int)len)
+                break;
+            written += ret;
+        }
+        ret = 0;
+    } while (0);
+
+    if (ret > 0)
+        written += ret;
+    return written;
+}
+
+static void format_mp3_free_plugin(format_plugin_t *self)
+{
+    /* free the plugin instance */
+    mp3_state *state = self->_state;
+
+    thread_mutex_destroy (&state->url_lock);
+    free (state->url_artist);
+    free (state->url_title);
+    free (self->charset);
+    refbuf_release (state->metadata);
+    refbuf_release (state->read_data);
+    free(state);
+    free(self);
+}
+
+
+/* This does the actual reading, making sure the read data is packaged in
+ * blocks of 1400 bytes (near the common MTU size). This is because many
+ * incoming streams come in small packets which could waste a lot of 
+ * bandwidth with many listeners due to headers and such like.
+ */
+static int complete_read (source_t *source)
+{
+    int bytes;
+    format_plugin_t *format = source->format;
+    mp3_state *source_mp3 = format->_state;
+    char *buf;
+    refbuf_t *refbuf;
+
+#define REFBUF_SIZE 1400
+
+    if (source_mp3->read_data == NULL)
+    {
+        source_mp3->read_data = refbuf_new (REFBUF_SIZE); 
+        source_mp3->read_count = 0;
+    }
+    buf = source_mp3->read_data->data + source_mp3->read_count;
+
+    bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count);
+    if (bytes < 0)
+    {
+        if (source->client->con->error)
+        {
+            refbuf_release (source_mp3->read_data);
+            source_mp3->read_data = NULL;
+        }
+        return 0;
+    }
+    source_mp3->read_count += bytes;
+    refbuf = source_mp3->read_data;
+    refbuf->len = source_mp3->read_count;
+    format->read_bytes += bytes;
+
+    if (source_mp3->read_count < REFBUF_SIZE)
+    {
+        if (source_mp3->read_count == 0)
+        {
+            refbuf_release (source_mp3->read_data);
+            source_mp3->read_data = NULL;
+        }
+        return 0;
+    }
+    return 1;
+}
+
+
+/* read an mp3 stream which does not have shoutcast style metadata */
+static refbuf_t *mp3_get_no_meta (source_t *source)
+{
+    refbuf_t *refbuf;
+    mp3_state *source_mp3 = source->format->_state;
+
+    if (complete_read (source) == 0)
+        return NULL;
+
+    refbuf = source_mp3->read_data;
+    source_mp3->read_data = NULL;
+
+    if (source_mp3->update_metadata)
+    {
+        mp3_set_title (source);
+        source_mp3->update_metadata = 0;
+    }
+    refbuf->associated = source_mp3->metadata;
+    refbuf_addref (source_mp3->metadata);
+    refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+/* read mp3 data with inlined metadata from the source. Filter out the
+ * metadata so that the mp3 data itself is store on the queue and the
+ * metadata is is associated with it
+ */
+static refbuf_t *mp3_get_filter_meta (source_t *source)
+{
+    refbuf_t *refbuf;
+    format_plugin_t *plugin = source->format;
+    mp3_state *source_mp3 = plugin->_state;
+    unsigned char *src;
+    unsigned int bytes, mp3_block;
+
+    if (complete_read (source) == 0)
+        return NULL;
+
+    refbuf = source_mp3->read_data;
+    source_mp3->read_data = NULL;
+    src = (unsigned char *)refbuf->data;
+
+    if (source_mp3->update_metadata)
+    {
+        mp3_set_title (source);
+        source_mp3->update_metadata = 0;
+    }
+    /* fill the buffer with the read data */
+    bytes = source_mp3->read_count;
+    refbuf->len = 0;
+    while (bytes > 0)
+    {
+        unsigned int metadata_remaining;
+
+        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;
+
+        /* is there only enough to account for mp3 data */
+        if (bytes <= mp3_block)
+        {
+            refbuf->len += bytes;
+            source_mp3->offset += bytes;
+            break;
+        }
+        /* we have enough data to get to the metadata
+         * block, but only transfer upto it */
+        if (mp3_block)
+        {
+            src += mp3_block;
+            bytes -= mp3_block;
+            refbuf->len += mp3_block;
+            source_mp3->offset += mp3_block;
+            continue;
+        }
+
+        /* process the inline metadata, len == 0 indicates not seen any yet */
+        if (source_mp3->build_metadata_len == 0)
+        {
+            memset (source_mp3->build_metadata, 0,
+                    sizeof (source_mp3->build_metadata));
+            source_mp3->build_metadata_offset = 0;
+            source_mp3->build_metadata_len = 1 + (*src * 16);
+        }
+
+        /* do we have all of the metatdata block */
+        metadata_remaining = source_mp3->build_metadata_len -
+            source_mp3->build_metadata_offset;
+        if (bytes < metadata_remaining)
+        {
+            memcpy (source_mp3->build_metadata +
+                    source_mp3->build_metadata_offset, src, bytes);
+            source_mp3->build_metadata_offset += bytes;
+            break;
+        }
+        /* copy all bytes except the last one, that way we 
+         * know a null byte terminates the message */
+        memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset,
+                src, metadata_remaining-1);
+
+        /* overwrite metadata in the buffer */
+        bytes -= metadata_remaining;
+        memmove (src, src+metadata_remaining, bytes);
+
+        /* assign metadata if it's greater than 1 byte, and the text has changed */
+        if (source_mp3->build_metadata_len > 1 &&
+                strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
+        {
+            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
+            memcpy (meta->data, source_mp3->build_metadata,
+                    source_mp3->build_metadata_len);
+
+            DEBUG1("shoutcast metadata %.4080s", meta->data+1);
+            if (strncmp (meta->data+1, "StreamTitle=", 12) == 0)
+            {
+                filter_shoutcast_metadata (source, source_mp3->build_metadata,
+                        source_mp3->build_metadata_len);
+                refbuf_release (source_mp3->metadata);
+                source_mp3->metadata = meta;
+            }
+            else
+            {
+                ERROR0 ("Incorrect metadata format, ending stream");
+                source->running = 0;
+                refbuf_release (refbuf);
+                refbuf_release (meta);
+                return NULL;
+            }
+        }
+        source_mp3->offset = 0;
+        source_mp3->build_metadata_len = 0;
+    }
+    /* the data we have just read may of just been metadata */
+    if (refbuf->len == 0)
+    {
+        refbuf_release (refbuf);
+        return NULL;
+    }
+    refbuf->associated = source_mp3->metadata;
+    refbuf_addref (source_mp3->metadata);
+    refbuf->sync_point = 1;
+
+    return refbuf;
+}
+
+
+static int format_mp3_create_client_data(source_t *source, client_t *client)
+{
+    mp3_client_data *client_mp3 = calloc(1,sizeof(mp3_client_data));
+    mp3_state *source_mp3 = source->format->_state;
+    const char *metadata; 
+    /* the +-2 is for overwriting the last set of \r\n */
+    unsigned remaining = 4096 - client->refbuf->len + 2;
+    char *ptr = client->refbuf->data + client->refbuf->len - 2;
+    int bytes;
+    const char *useragent;
+
+    if (client_mp3 == NULL)
+        return -1;
+
+    /* hack for flash player, it wants a length.  It has also been reported that the useragent
+     * appears as MSIE if run in internet explorer */
+    useragent = httpp_getvar (client->parser, "user-agent");
+    if (httpp_getvar(client->parser, "x-flash-version") ||
+            (useragent && strstr(useragent, "MSIE")))
+    {
+        bytes = snprintf (ptr, remaining, "Content-Length: 221183499\r\n");
+        remaining -= bytes;
+        ptr += bytes;
+        /* avoid browser caching, reported via forum */
+        bytes = snprintf (ptr, remaining, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n");
+        remaining -= bytes;
+        ptr += bytes; 
+    }
+
+    client->format_data = client_mp3;
+    client->free_client_data = free_mp3_client_data;
+    metadata = httpp_getvar(client->parser, "icy-metadata");
+    if (metadata && atoi(metadata))
+    {
+        if (source_mp3->interval >= 0)
+            client_mp3->interval = source_mp3->interval;
+        else
+            client_mp3->interval = ICY_METADATA_INTERVAL;
+        if (client_mp3->interval)
+        {
+            bytes = snprintf (ptr, remaining, "icy-metaint:%u\r\n",
+                    client_mp3->interval);
+            if (bytes > 0)
+            {
+                remaining -= bytes;
+                ptr += bytes;
+            }
+        }
+    }
+    bytes = snprintf (ptr, remaining, "\r\n");
+    remaining -= bytes;
+    ptr += bytes;
+
+    client->refbuf->len = 4096 - remaining;
+
+    return 0;
+}
+
+
+static void free_mp3_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    if (refbuf->len == 0)
+        return;
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+    }
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/format_ogg.c
===================================================================
--- icecast/trunk/icecast/src/format_ogg.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format_ogg.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,580 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* format_ogg.c
- *
- * format plugin for Ogg
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <ogg/ogg.h>
-
-#include "refbuf.h"
-#include "source.h"
-#include "client.h"
-
-#include "stats.h"
-#include "format.h"
-#include "format_ogg.h"
-#include "format_vorbis.h"
-#ifdef HAVE_THEORA
-#include "format_theora.h"
-#endif
-#ifdef HAVE_SPEEX
-#include "format_speex.h"
-#endif
-#include "format_midi.h"
-#include "format_flac.h"
-
-#ifdef _WIN32
-#define snprintf _snprintf
-#endif
-
-#define CATMODULE "format-ogg"
-#include "logging.h"
-
-struct _ogg_state_tag;
-
-static void format_ogg_free_plugin (format_plugin_t *plugin);
-static int  create_ogg_client_data(source_t *source, client_t *client);
-static void free_ogg_client_data (client_t *client);
-
-static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
-static refbuf_t *ogg_get_buffer (source_t *source);
-static int write_buf_to_client (client_t *client);
-
-
-struct ogg_client
-{
-    refbuf_t *headers;
-    refbuf_t *header_page;
-    unsigned pos;
-    int headers_sent;
-};
-
-
-refbuf_t *make_refbuf_with_page (ogg_page *page)
-{
-    refbuf_t *refbuf = refbuf_new (page->header_len + page->body_len);
-
-    memcpy (refbuf->data, page->header, page->header_len);
-    memcpy (refbuf->data+page->header_len, page->body, page->body_len);
-    return refbuf;
-}
-
-
-/* routine for taking the provided page (should be a header page) and
- * placing it on the collection of header pages
- */
-void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page)
-{
-    refbuf_t *refbuf = make_refbuf_with_page (page);
-
-    if (ogg_page_bos (page))
-    {
-        DEBUG0 ("attaching BOS page");
-        if (*ogg_info->bos_end == NULL)
-            ogg_info->header_pages_tail = refbuf;
-        refbuf->next = *ogg_info->bos_end;
-        *ogg_info->bos_end = refbuf;
-        ogg_info->bos_end = &refbuf->next;
-        return;
-    }
-    DEBUG0 ("attaching header page");
-    if (ogg_info->header_pages_tail)
-        ogg_info->header_pages_tail->next = refbuf;
-    ogg_info->header_pages_tail = refbuf;
-
-    if (ogg_info->header_pages == NULL)
-        ogg_info->header_pages = refbuf;
-}
-
-
-void format_ogg_free_headers (ogg_state_t *ogg_info)
-{
-    refbuf_t *header;
-
-    /* release the header pages first */
-    DEBUG0 ("releasing header pages");
-    header = ogg_info->header_pages;
-    while (header)
-    {
-        refbuf_t *to_release = header;
-        header = header->next;
-        refbuf_release (to_release);
-    }
-    ogg_info->header_pages = NULL;
-    ogg_info->header_pages_tail = NULL;
-    ogg_info->bos_end = &ogg_info->header_pages;
-}
-
-
-/* release the memory used for the codec and header pages from the module */
-static void free_ogg_codecs (ogg_state_t *ogg_info)
-{
-    ogg_codec_t *codec;
-
-    if (ogg_info == NULL)
-        return;
-
-    format_ogg_free_headers (ogg_info);
-
-    /* now free the codecs */
-    codec = ogg_info->codecs;
-    DEBUG0 ("freeing codecs");
-    while (codec)
-    {
-        ogg_codec_t *next = codec->next;
-        if (codec->possible_start)
-            refbuf_release (codec->possible_start);
-        codec->codec_free (ogg_info, codec);
-        codec = next;
-    }
-    ogg_info->codecs = NULL;
-    ogg_info->current = NULL;
-    ogg_info->bos_completed = 0;
-    ogg_info->codec_count = 0;
-}
-
-
-int format_ogg_get_plugin (source_t *source)
-{
-    format_plugin_t *plugin;
-    ogg_state_t *state = calloc (1, sizeof (ogg_state_t));
-
-    plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
-
-    plugin->type = FORMAT_TYPE_OGG;
-    plugin->get_buffer = ogg_get_buffer;
-    plugin->write_buf_to_client = write_buf_to_client;
-    plugin->write_buf_to_file = write_ogg_to_file;
-    plugin->create_client_data = create_ogg_client_data;
-    plugin->free_plugin = format_ogg_free_plugin;
-    plugin->set_tag = NULL;
-    plugin->contenttype = "application/ogg";
-
-    ogg_sync_init (&state->oy);
-
-    plugin->_state = state;
-    source->format = plugin;
-    state->mount = source->mount;
-    state->bos_end = &state->header_pages;
-
-    return 0;
-}
-
-
-static void format_ogg_free_plugin (format_plugin_t *plugin)
-{
-    ogg_state_t *state = plugin->_state;
-
-    /* free memory associated with this plugin instance */
-    free_ogg_codecs (state);
-    free (state->artist);
-    free (state->title);
-
-    ogg_sync_clear (&state->oy);
-    free (state);
-
-    free (plugin);
-}
-
-
-/* a new BOS page has been seen so check which codec it is */
-static int process_initial_page (format_plugin_t *plugin, ogg_page *page)
-{
-    ogg_state_t *ogg_info = plugin->_state;
-    ogg_codec_t *codec;
-
-    if (ogg_info->bos_completed)
-    {
-        ogg_info->bitrate = 0;
-        ogg_info->codec_sync = NULL;
-        /* need to zap old list of codecs when next group of BOS pages appear */
-        free_ogg_codecs (ogg_info);
-    }
-    do
-    {
-        if (ogg_info->codec_count > 10)
-        {
-            ERROR0 ("many codecs in stream, playing safe, dropping source");
-            ogg_info->error = 1;
-            return -1;
-        }
-        codec = initial_vorbis_page (plugin, page);
-        if (codec)
-            break;
-#ifdef HAVE_THEORA
-        codec = initial_theora_page (plugin, page);
-        if (codec)
-            break;
-#endif
-        codec = initial_midi_page (plugin, page);
-        if (codec)
-            break;
-        codec = initial_flac_page (plugin, page);
-        if (codec)
-            break;
-#ifdef HAVE_SPEEX
-        codec = initial_speex_page (plugin, page);
-        if (codec)
-            break;
-#endif
-
-        /* any others */
-        ERROR0 ("Seen BOS page with unknown type");
-        ogg_info->error = 1;
-        return -1;
-    } while (0);
-
-    if (codec)
-    {
-        /* add codec to list */
-        codec->next = ogg_info->codecs;
-        ogg_info->codecs = codec;
-        ogg_info->codec_count++;
-    }
-
-    return 0;
-}
-
-
-/* This is called when there has been a change in the metadata. Usually
- * artist and title are provided separately so here we update the stats
- * and write log entry if required.
- */
-static void update_comments (source_t *source)
-{
-    ogg_state_t *ogg_info = source->format->_state;
-    char *title = ogg_info->title;
-    char *artist = ogg_info->artist;
-    char *metadata = NULL;
-    unsigned int len = 1; /* space for the nul byte at least */
-    ogg_codec_t *codec;
-    char codec_names [100] = "";
-
-    if (ogg_info->artist)
-    {
-        if (title)
-        {
-            len += strlen(artist) + strlen(title) + 3;
-            metadata = calloc (1, len);
-            snprintf (metadata, len, "%s - %s", artist, title);
-        }
-        else
-        {
-            len += strlen(artist);
-            metadata = calloc (1, len);
-            snprintf (metadata, len, "%s", artist);
-        }
-    }
-    else
-    {
-        if (title)
-        {
-            len += strlen (title);
-            metadata = calloc (1, len);
-            snprintf (metadata, len, "%s", title);
-        }
-    }
-    if (metadata)
-    {
-        logging_playlist (source->mount, metadata, source->listeners);
-        free (metadata);
-    }
-    stats_event (source->mount, "artist", artist);
-    stats_event (source->mount, "title", title);
-
-    codec = ogg_info->codecs;
-    while (codec)
-    {
-        if (codec->name)
-        {
-            int len = strlen (codec_names);
-            int remaining = sizeof (codec_names) - len;
-            char *where = codec_names + len;
-            char *separator = "/";
-            if (len == 0)
-                separator = "";
-            snprintf (where, remaining, "%s%s", separator, codec->name);
-        }
-        codec = codec->next;
-    }
-    stats_event (source->mount, "subtype", codec_names);
-    yp_touch (source->mount);
-}
-
-
-/* called when preparing a refbuf with audio data to be passed
- * back for queueing
- */
-static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf)
-{
-    ogg_state_t *ogg_info = source->format->_state;
-    refbuf_t *header = ogg_info->header_pages;
-
-    while (header)
-    {
-        refbuf_addref (header);
-        header = header->next;
-    }
-    refbuf->associated = ogg_info->header_pages;
-
-    if (ogg_info->log_metadata)
-    {
-        update_comments (source);
-        ogg_info->log_metadata = 0;
-    }
-    /* listeners can start anywhere unless the codecs themselves are
-     * marking starting points */
-    if (ogg_info->codec_sync == NULL)
-        refbuf->sync_point = 1;
-    return refbuf;
-}
-
-
-/* process the incoming page. this requires searching through the
- * currently known codecs that have been seen in the stream
- */
-static refbuf_t *process_ogg_page (ogg_state_t *ogg_info, ogg_page *page)
-{
-    ogg_codec_t *codec = ogg_info->codecs;
-    refbuf_t *refbuf = NULL;
-
-    while (codec)
-    {
-        if (ogg_page_serialno (page) == codec->os.serialno)
-        {
-            if (codec->process_page)
-                refbuf = codec->process_page (ogg_info, codec, page);
-            break;
-        }
-
-        codec = codec->next;
-    }
-    ogg_info->current = codec;
-    return refbuf;
-}
-
-
-/* main plugin handler for getting a buffer for the queue. In here we
- * just add an incoming page to the codecs and process it until either
- * more data is needed or we prodice a buffer for the queue.
- */
-static refbuf_t *ogg_get_buffer (source_t *source)
-{
-    ogg_state_t *ogg_info = source->format->_state;
-    format_plugin_t *format = source->format;
-    char *data = NULL;
-    int bytes = 0;
-
-    while (1)
-    {
-        while (1)
-        {
-            ogg_page page;
-            refbuf_t *refbuf = NULL;
-            ogg_codec_t *codec = ogg_info->current;
-
-            /* if a codec has just been given a page then process it */
-            if (codec && codec->process)
-            {
-                refbuf = codec->process (ogg_info, codec);
-                if (refbuf)
-                    return complete_buffer (source, refbuf);
-
-                ogg_info->current = NULL;
-            }
-
-            if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
-            {
-                if (ogg_page_bos (&page))
-                {
-                    process_initial_page (source->format, &page);
-                }
-                else
-                {
-                    ogg_info->bos_completed = 1;
-                    refbuf = process_ogg_page (ogg_info, &page);
-                }
-                if (ogg_info->error)
-                {
-                    ERROR0 ("Problem processing stream");
-                    source->running = 0;
-                    return NULL;
-                }
-                if (refbuf)
-                    return complete_buffer (source, refbuf);
-                continue;
-            }
-            /* need more stream data */
-            break;
-        }
-        /* we need more data to continue getting pages */
-        data = ogg_sync_buffer (&ogg_info->oy, 4096);
-
-        bytes = client_read_bytes (source->client, data, 4096);
-        if (bytes <= 0)
-        {
-            ogg_sync_wrote (&ogg_info->oy, 0);
-            return NULL;
-        }
-        format->read_bytes += bytes;
-        ogg_sync_wrote (&ogg_info->oy, bytes);
-    }
-}
-
-
-static int create_ogg_client_data (source_t *source, client_t *client) 
-{
-    struct ogg_client *client_data = calloc (1, sizeof (struct ogg_client));
-    int ret = -1;
-
-    if (client_data)
-    {
-        client_data->headers_sent = 1;
-        client->format_data = client_data;
-        client->free_client_data = free_ogg_client_data;
-        ret = 0;
-    }
-    return ret;
-}
-
-
-static void free_ogg_client_data (client_t *client)
-{
-    free (client->format_data);
-    client->format_data = NULL;
-}
-
-
-/* send out the header pages. These are for all codecs but are
- * in the order for the stream, ie BOS pages first
- */
-static int send_ogg_headers (client_t *client, refbuf_t *headers)
-{
-    struct ogg_client *client_data = client->format_data;
-    refbuf_t *refbuf;
-    int written = 0;
-
-    if (client_data->headers_sent)
-    {
-        client_data->header_page = headers;
-        client_data->pos = 0;
-        client_data->headers_sent = 0;
-    }
-    refbuf = client_data->header_page;
-    while (refbuf)
-    {
-        char *data = refbuf->data + client_data->pos;
-        unsigned len = refbuf->len - client_data->pos;
-        int ret;
-
-        ret = client_send_bytes (client, data, len);
-        if (ret > 0)
-           written += ret;
-        if (ret < (int)len)
-            return written ? written : -1;
-        client_data->pos += ret;
-        if (client_data->pos == refbuf->len)
-        {
-            refbuf = refbuf->next;
-            client_data->header_page = refbuf;
-            client_data->pos = 0;
-        }
-    }
-    client_data->headers_sent = 1;
-    client_data->headers = headers;
-    return written;
-}
-
-
-/* main client write routine for sending ogg data. Each refbuf has a
- * single page so we only need to determine if there are new headers
- */
-static int write_buf_to_client (client_t *client)
-{
-    refbuf_t *refbuf = client->refbuf;
-    char *buf = refbuf->data + client->pos;
-    unsigned len = refbuf->len - client->pos;
-    struct ogg_client *client_data = client->format_data;
-    int ret, written = 0;
-
-    do
-    {
-        if (client_data->headers != refbuf->associated)
-        {
-            ret = send_ogg_headers (client, refbuf->associated);
-            if (client_data->headers_sent == 0)
-                break;
-            written += ret;
-        }
-        ret = client_send_bytes (client, buf, len);
-
-        if (ret > 0)
-            client->pos += ret;
-
-        if (ret < (int)len)
-            break;
-        written += ret;
-        /* we have now written the page(s) */
-        ret = 0;
-    } while (0);
-
-    if (ret > 0)
-       written += ret;
-    return written;
-}
-
-
-static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
-{
-    int ret = 1;
-
-    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
-    {
-        WARN0 ("Write to dump file failed, disabling");
-        fclose (source->dumpfile);
-        source->dumpfile = NULL;
-        ret = 0;
-    }
-    return ret;
-}
-
-
-static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
-{
-    ogg_state_t *ogg_info = source->format->_state;
-
-    if (ogg_info->file_headers != refbuf->associated)
-    {
-        refbuf_t *header = refbuf->associated;
-        while (header)
-        {
-            if (write_ogg_data (source, header) == 0)
-                return;
-            header = header->next;
-        }
-        ogg_info->file_headers = refbuf->associated;
-    }
-    write_ogg_data (source, refbuf);
-}
-
-

Copied: icecast/tags/icecast-2.3.2/src/format_ogg.c (from rev 14786, icecast/trunk/icecast/src/format_ogg.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_ogg.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_ogg.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,590 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_ogg.c
+ *
+ * format plugin for Ogg
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+#include "format_ogg.h"
+#include "format_vorbis.h"
+#ifdef HAVE_THEORA
+#include "format_theora.h"
+#endif
+#ifdef HAVE_SPEEX
+#include "format_speex.h"
+#endif
+#include "format_midi.h"
+#include "format_flac.h"
+#include "format_kate.h"
+#include "format_skeleton.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "format-ogg"
+#include "logging.h"
+
+struct _ogg_state_tag;
+
+static void format_ogg_free_plugin (format_plugin_t *plugin);
+static int  create_ogg_client_data(source_t *source, client_t *client);
+static void free_ogg_client_data (client_t *client);
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
+static refbuf_t *ogg_get_buffer (source_t *source);
+static int write_buf_to_client (client_t *client);
+
+
+struct ogg_client
+{
+    refbuf_t *headers;
+    refbuf_t *header_page;
+    unsigned pos;
+    int headers_sent;
+};
+
+
+refbuf_t *make_refbuf_with_page (ogg_page *page)
+{
+    refbuf_t *refbuf = refbuf_new (page->header_len + page->body_len);
+
+    memcpy (refbuf->data, page->header, page->header_len);
+    memcpy (refbuf->data+page->header_len, page->body, page->body_len);
+    return refbuf;
+}
+
+
+/* routine for taking the provided page (should be a header page) and
+ * placing it on the collection of header pages
+ */
+void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page)
+{
+    refbuf_t *refbuf = make_refbuf_with_page (page);
+
+    if (ogg_page_bos (page))
+    {
+        DEBUG0 ("attaching BOS page");
+        if (*ogg_info->bos_end == NULL)
+            ogg_info->header_pages_tail = refbuf;
+        refbuf->next = *ogg_info->bos_end;
+        *ogg_info->bos_end = refbuf;
+        ogg_info->bos_end = &refbuf->next;
+        return;
+    }
+    DEBUG0 ("attaching header page");
+    if (ogg_info->header_pages_tail)
+        ogg_info->header_pages_tail->next = refbuf;
+    ogg_info->header_pages_tail = refbuf;
+
+    if (ogg_info->header_pages == NULL)
+        ogg_info->header_pages = refbuf;
+}
+
+
+void format_ogg_free_headers (ogg_state_t *ogg_info)
+{
+    refbuf_t *header;
+
+    /* release the header pages first */
+    DEBUG0 ("releasing header pages");
+    header = ogg_info->header_pages;
+    while (header)
+    {
+        refbuf_t *to_release = header;
+        header = header->next;
+        refbuf_release (to_release);
+    }
+    ogg_info->header_pages = NULL;
+    ogg_info->header_pages_tail = NULL;
+    ogg_info->bos_end = &ogg_info->header_pages;
+}
+
+
+/* release the memory used for the codec and header pages from the module */
+static void free_ogg_codecs (ogg_state_t *ogg_info)
+{
+    ogg_codec_t *codec;
+
+    if (ogg_info == NULL)
+        return;
+
+    format_ogg_free_headers (ogg_info);
+
+    /* now free the codecs */
+    codec = ogg_info->codecs;
+    DEBUG0 ("freeing codecs");
+    while (codec)
+    {
+        ogg_codec_t *next = codec->next;
+        if (codec->possible_start)
+            refbuf_release (codec->possible_start);
+        codec->codec_free (ogg_info, codec);
+        codec = next;
+    }
+    ogg_info->codecs = NULL;
+    ogg_info->current = NULL;
+    ogg_info->bos_completed = 0;
+    ogg_info->codec_count = 0;
+}
+
+
+int format_ogg_get_plugin (source_t *source)
+{
+    format_plugin_t *plugin;
+    ogg_state_t *state = calloc (1, sizeof (ogg_state_t));
+
+    plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
+
+    plugin->type = FORMAT_TYPE_OGG;
+    plugin->get_buffer = ogg_get_buffer;
+    plugin->write_buf_to_client = write_buf_to_client;
+    plugin->write_buf_to_file = write_ogg_to_file;
+    plugin->create_client_data = create_ogg_client_data;
+    plugin->free_plugin = format_ogg_free_plugin;
+    plugin->set_tag = NULL;
+    if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
+        httpp_setvar (source->parser, "content-type", "application/ogg");
+    plugin->contenttype = httpp_getvar (source->parser, "content-type");
+
+    ogg_sync_init (&state->oy);
+
+    plugin->_state = state;
+    source->format = plugin;
+    state->mount = source->mount;
+    state->bos_end = &state->header_pages;
+
+    return 0;
+}
+
+
+static void format_ogg_free_plugin (format_plugin_t *plugin)
+{
+    ogg_state_t *state = plugin->_state;
+
+    /* free memory associated with this plugin instance */
+    free_ogg_codecs (state);
+    free (state->artist);
+    free (state->title);
+
+    ogg_sync_clear (&state->oy);
+    free (state);
+
+    free (plugin);
+}
+
+
+/* a new BOS page has been seen so check which codec it is */
+static int process_initial_page (format_plugin_t *plugin, ogg_page *page)
+{
+    ogg_state_t *ogg_info = plugin->_state;
+    ogg_codec_t *codec;
+
+    if (ogg_info->bos_completed)
+    {
+        ogg_info->bitrate = 0;
+        ogg_info->codec_sync = NULL;
+        /* need to zap old list of codecs when next group of BOS pages appear */
+        free_ogg_codecs (ogg_info);
+    }
+    do
+    {
+        if (ogg_info->codec_count > 10)
+        {
+            ERROR0 ("many codecs in stream, playing safe, dropping source");
+            ogg_info->error = 1;
+            return -1;
+        }
+        codec = initial_vorbis_page (plugin, page);
+        if (codec)
+            break;
+#ifdef HAVE_THEORA
+        codec = initial_theora_page (plugin, page);
+        if (codec)
+            break;
+#endif
+        codec = initial_midi_page (plugin, page);
+        if (codec)
+            break;
+        codec = initial_flac_page (plugin, page);
+        if (codec)
+            break;
+#ifdef HAVE_SPEEX
+        codec = initial_speex_page (plugin, page);
+        if (codec)
+            break;
+#endif
+        codec = initial_kate_page (plugin, page);
+        if (codec)
+            break;
+        codec = initial_skeleton_page (plugin, page);
+        if (codec)
+            break;
+
+        /* any others */
+        ERROR0 ("Seen BOS page with unknown type");
+        ogg_info->error = 1;
+        return -1;
+    } while (0);
+
+    if (codec)
+    {
+        /* add codec to list */
+        codec->next = ogg_info->codecs;
+        ogg_info->codecs = codec;
+        ogg_info->codec_count++;
+    }
+
+    return 0;
+}
+
+
+/* This is called when there has been a change in the metadata. Usually
+ * artist and title are provided separately so here we update the stats
+ * and write log entry if required.
+ */
+static void update_comments (source_t *source)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+    char *title = ogg_info->title;
+    char *artist = ogg_info->artist;
+    char *metadata = NULL;
+    unsigned int len = 1; /* space for the nul byte at least */
+    ogg_codec_t *codec;
+    char codec_names [100] = "";
+
+    if (ogg_info->artist)
+    {
+        if (title)
+        {
+            len += strlen(artist) + strlen(title) + 3;
+            metadata = calloc (1, len);
+            snprintf (metadata, len, "%s - %s", artist, title);
+        }
+        else
+        {
+            len += strlen(artist);
+            metadata = calloc (1, len);
+            snprintf (metadata, len, "%s", artist);
+        }
+    }
+    else
+    {
+        if (title)
+        {
+            len += strlen (title);
+            metadata = calloc (1, len);
+            snprintf (metadata, len, "%s", title);
+        }
+    }
+    if (metadata)
+    {
+        logging_playlist (source->mount, metadata, source->listeners);
+        free (metadata);
+    }
+    stats_event (source->mount, "artist", artist);
+    stats_event (source->mount, "title", title);
+
+    codec = ogg_info->codecs;
+    while (codec)
+    {
+        if (codec->name)
+        {
+            int len = strlen (codec_names);
+            int remaining = sizeof (codec_names) - len;
+            char *where = codec_names + len;
+            char *separator = "/";
+            if (len == 0)
+                separator = "";
+            snprintf (where, remaining, "%s%s", separator, codec->name);
+        }
+        codec = codec->next;
+    }
+    stats_event (source->mount, "subtype", codec_names);
+    yp_touch (source->mount);
+}
+
+
+/* called when preparing a refbuf with audio data to be passed
+ * back for queueing
+ */
+static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+    refbuf_t *header = ogg_info->header_pages;
+
+    while (header)
+    {
+        refbuf_addref (header);
+        header = header->next;
+    }
+    refbuf->associated = ogg_info->header_pages;
+
+    if (ogg_info->log_metadata)
+    {
+        update_comments (source);
+        ogg_info->log_metadata = 0;
+    }
+    /* listeners can start anywhere unless the codecs themselves are
+     * marking starting points */
+    if (ogg_info->codec_sync == NULL)
+        refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+/* process the incoming page. this requires searching through the
+ * currently known codecs that have been seen in the stream
+ */
+static refbuf_t *process_ogg_page (ogg_state_t *ogg_info, ogg_page *page)
+{
+    ogg_codec_t *codec = ogg_info->codecs;
+    refbuf_t *refbuf = NULL;
+
+    while (codec)
+    {
+        if (ogg_page_serialno (page) == codec->os.serialno)
+        {
+            if (codec->process_page)
+                refbuf = codec->process_page (ogg_info, codec, page);
+            break;
+        }
+
+        codec = codec->next;
+    }
+    ogg_info->current = codec;
+    return refbuf;
+}
+
+
+/* main plugin handler for getting a buffer for the queue. In here we
+ * just add an incoming page to the codecs and process it until either
+ * more data is needed or we prodice a buffer for the queue.
+ */
+static refbuf_t *ogg_get_buffer (source_t *source)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+    format_plugin_t *format = source->format;
+    char *data = NULL;
+    int bytes = 0;
+
+    while (1)
+    {
+        while (1)
+        {
+            ogg_page page;
+            refbuf_t *refbuf = NULL;
+            ogg_codec_t *codec = ogg_info->current;
+
+            /* if a codec has just been given a page then process it */
+            if (codec && codec->process)
+            {
+                refbuf = codec->process (ogg_info, codec);
+                if (refbuf)
+                    return complete_buffer (source, refbuf);
+
+                ogg_info->current = NULL;
+            }
+
+            if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
+            {
+                if (ogg_page_bos (&page))
+                {
+                    process_initial_page (source->format, &page);
+                }
+                else
+                {
+                    ogg_info->bos_completed = 1;
+                    refbuf = process_ogg_page (ogg_info, &page);
+                }
+                if (ogg_info->error)
+                {
+                    ERROR0 ("Problem processing stream");
+                    source->running = 0;
+                    return NULL;
+                }
+                if (refbuf)
+                    return complete_buffer (source, refbuf);
+                continue;
+            }
+            /* need more stream data */
+            break;
+        }
+        /* we need more data to continue getting pages */
+        data = ogg_sync_buffer (&ogg_info->oy, 4096);
+
+        bytes = client_read_bytes (source->client, data, 4096);
+        if (bytes <= 0)
+        {
+            ogg_sync_wrote (&ogg_info->oy, 0);
+            return NULL;
+        }
+        format->read_bytes += bytes;
+        ogg_sync_wrote (&ogg_info->oy, bytes);
+    }
+}
+
+
+static int create_ogg_client_data (source_t *source, client_t *client) 
+{
+    struct ogg_client *client_data = calloc (1, sizeof (struct ogg_client));
+    int ret = -1;
+
+    if (client_data)
+    {
+        client_data->headers_sent = 1;
+        client->format_data = client_data;
+        client->free_client_data = free_ogg_client_data;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void free_ogg_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+/* send out the header pages. These are for all codecs but are
+ * in the order for the stream, ie BOS pages first
+ */
+static int send_ogg_headers (client_t *client, refbuf_t *headers)
+{
+    struct ogg_client *client_data = client->format_data;
+    refbuf_t *refbuf;
+    int written = 0;
+
+    if (client_data->headers_sent)
+    {
+        client_data->header_page = headers;
+        client_data->pos = 0;
+        client_data->headers_sent = 0;
+    }
+    refbuf = client_data->header_page;
+    while (refbuf)
+    {
+        char *data = refbuf->data + client_data->pos;
+        unsigned len = refbuf->len - client_data->pos;
+        int ret;
+
+        ret = client_send_bytes (client, data, len);
+        if (ret > 0)
+           written += ret;
+        if (ret < (int)len)
+            return written ? written : -1;
+        client_data->pos += ret;
+        if (client_data->pos == refbuf->len)
+        {
+            refbuf = refbuf->next;
+            client_data->header_page = refbuf;
+            client_data->pos = 0;
+        }
+    }
+    client_data->headers_sent = 1;
+    client_data->headers = headers;
+    return written;
+}
+
+
+/* main client write routine for sending ogg data. Each refbuf has a
+ * single page so we only need to determine if there are new headers
+ */
+static int write_buf_to_client (client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+    char *buf = refbuf->data + client->pos;
+    unsigned len = refbuf->len - client->pos;
+    struct ogg_client *client_data = client->format_data;
+    int ret, written = 0;
+
+    do
+    {
+        if (client_data->headers != refbuf->associated)
+        {
+            ret = send_ogg_headers (client, refbuf->associated);
+            if (client_data->headers_sent == 0)
+                break;
+            written += ret;
+        }
+        ret = client_send_bytes (client, buf, len);
+
+        if (ret > 0)
+            client->pos += ret;
+
+        if (ret < (int)len)
+            break;
+        written += ret;
+        /* we have now written the page(s) */
+        ret = 0;
+    } while (0);
+
+    if (ret > 0)
+       written += ret;
+    return written;
+}
+
+
+static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
+{
+    int ret = 1;
+
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+
+    if (ogg_info->file_headers != refbuf->associated)
+    {
+        refbuf_t *header = refbuf->associated;
+        while (header)
+        {
+            if (write_ogg_data (source, header) == 0)
+                return;
+            header = header->next;
+        }
+        ogg_info->file_headers = refbuf->associated;
+    }
+    write_ogg_data (source, refbuf);
+}
+
+

Copied: icecast/tags/icecast-2.3.2/src/format_skeleton.c (from rev 14867, icecast/trunk/icecast/src/format_skeleton.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_skeleton.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_skeleton.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,101 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+/* Ogg codec handler for skeleton logical streams */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+typedef struct source_tag source_t;
+
+#include "refbuf.h"
+#include "format_ogg.h"
+#include "format_skeleton.h"
+#include "client.h"
+#include "stats.h"
+
+#define CATMODULE "format-skeleton"
+#include "logging.h"
+
+
+static void skeleton_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    DEBUG0 ("freeing skeleton codec");
+    ogg_stream_clear (&codec->os);
+    free (codec);
+}
+
+
+/* skeleton pages are not rebuilt, so here we just for headers and then
+ * pass them straight through to the the queue
+ */
+static refbuf_t *process_skeleton_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page)
+{
+    ogg_packet packet;
+
+    if (ogg_stream_pagein (&codec->os, page) < 0)
+    {
+        ogg_info->error = 1;
+        return NULL;
+    }
+
+    while (ogg_stream_packetout (&codec->os, &packet) > 0)
+    {
+        codec->headers++;
+    }
+
+    /* all skeleon packets are headers */
+    format_ogg_attach_header (ogg_info, page);
+    return NULL;
+}
+
+
+/* Check if specified BOS page is the start of a skeleton stream and
+ * if so, create a codec structure for handling it
+ */
+ogg_codec_t *initial_skeleton_page (format_plugin_t *plugin, ogg_page *page)
+{
+    ogg_state_t *ogg_info = plugin->_state;
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    DEBUG0("checking for skeleton codec");
+
+    if ((packet.bytes<8) || memcmp(packet.packet, "fishead\0", 8))
+    {
+        ogg_stream_clear (&codec->os);
+        free (codec);
+        return NULL;
+    }
+
+    INFO0 ("seen initial skeleton header");
+    codec->process_page = process_skeleton_page;
+    codec->codec_free = skeleton_codec_free;
+    codec->headers = 1;
+    codec->name = "Skeleton";
+
+    format_ogg_attach_header (ogg_info, page);
+    ogg_info->codec_sync = codec;
+    return codec;
+}
+

Copied: icecast/tags/icecast-2.3.2/src/format_skeleton.h (from rev 14778, icecast/trunk/icecast/src/format_skeleton.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_skeleton.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_skeleton.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,21 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+#ifndef __FORMAT_SKELETON_H
+#define __FORMAT_SKELETON_H
+
+#include "format_ogg.h"
+
+ogg_codec_t *initial_skeleton_page (format_plugin_t *plugin, ogg_page *page);
+
+#endif /* __FORMAT_SKELETON_H */

Deleted: icecast/tags/icecast-2.3.2/src/format_speex.c
===================================================================
--- icecast/trunk/icecast/src/format_speex.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format_speex.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,92 +0,0 @@
-/* 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 at xiph.org>, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-
-/* Ogg codec handler for speex streams */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <ogg/ogg.h>
-#include <speex/speex_header.h>
-
-typedef struct source_tag source_t;
-
-#include "format_speex.h"
-#include "refbuf.h"
-#include "client.h"
-
-#define CATMODULE "format-speex"
-#include "logging.h"
-
-static void speex_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    ogg_stream_clear (&codec->os);
-    free (codec);
-}
-
-
-static refbuf_t *process_speex_page (ogg_state_t *ogg_info,
-        ogg_codec_t *codec, ogg_page *page)
-{
-    refbuf_t *refbuf;
-
-    if (codec->headers < 2)
-    {
-        ogg_packet packet;
-
-        ogg_stream_pagein (&codec->os, page);
-        while (ogg_stream_packetout (&codec->os, &packet) > 0)
-        {
-           /* first time around (normal case) yields comments */
-           codec->headers++;
-        }
-        /* add header page to associated list */
-        format_ogg_attach_header (ogg_info, page);
-        return NULL;
-    }
-    refbuf = make_refbuf_with_page (page);
-    return refbuf;
-}
-
-
-ogg_codec_t *initial_speex_page (format_plugin_t *plugin, ogg_page *page)
-{
-    ogg_state_t *ogg_info = plugin->_state;
-    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
-    ogg_packet packet;
-    SpeexHeader *header;
-
-    ogg_stream_init (&codec->os, ogg_page_serialno (page));
-    ogg_stream_pagein (&codec->os, page);
-
-    ogg_stream_packetout (&codec->os, &packet);
-
-    DEBUG0("checking for speex codec");
-    header = speex_packet_to_header (packet.packet, packet.bytes);
-    if (header == NULL)
-    {
-        ogg_stream_clear (&codec->os);
-        free (header);
-        free (codec);
-        return NULL;
-    }
-    INFO0 ("seen initial speex header");
-    codec->process_page = process_speex_page;
-    codec->codec_free = speex_codec_free;
-    codec->headers = 1;
-    format_ogg_attach_header (ogg_info, page);
-    free (header);
-    return codec;
-}

Copied: icecast/tags/icecast-2.3.2/src/format_speex.c (from rev 13933, icecast/trunk/icecast/src/format_speex.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_speex.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_speex.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,92 @@
+/* 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 at xiph.org>, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+/* Ogg codec handler for speex streams */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ogg/ogg.h>
+#include <speex/speex_header.h>
+
+typedef struct source_tag source_t;
+
+#include "format_speex.h"
+#include "refbuf.h"
+#include "client.h"
+
+#define CATMODULE "format-speex"
+#include "logging.h"
+
+static void speex_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    ogg_stream_clear (&codec->os);
+    free (codec);
+}
+
+
+static refbuf_t *process_speex_page (ogg_state_t *ogg_info,
+        ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+
+    if (codec->headers < 2)
+    {
+        ogg_packet packet;
+
+        ogg_stream_pagein (&codec->os, page);
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           /* first time around (normal case) yields comments */
+           codec->headers++;
+        }
+        /* add header page to associated list */
+        format_ogg_attach_header (ogg_info, page);
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    return refbuf;
+}
+
+
+ogg_codec_t *initial_speex_page (format_plugin_t *plugin, ogg_page *page)
+{
+    ogg_state_t *ogg_info = plugin->_state;
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+    SpeexHeader *header;
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    DEBUG0("checking for speex codec");
+    header = speex_packet_to_header ((char*)packet.packet, packet.bytes);
+    if (header == NULL)
+    {
+        ogg_stream_clear (&codec->os);
+        free (header);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial speex header");
+    codec->process_page = process_speex_page;
+    codec->codec_free = speex_codec_free;
+    codec->headers = 1;
+    format_ogg_attach_header (ogg_info, page);
+    free (header);
+    return codec;
+}

Deleted: icecast/tags/icecast-2.3.2/src/format_vorbis.c
===================================================================
--- icecast/trunk/icecast/src/format_vorbis.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/format_vorbis.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,591 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-
-/* Ogg codec handler for vorbis streams */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <ogg/ogg.h>
-#include <vorbis/codec.h>
-#include <memory.h>
-#include <string.h>
-
-#include "refbuf.h"
-#include "source.h"
-#include "client.h"
-
-#include "format_ogg.h"
-#include "stats.h"
-#include "format.h"
-
-#define CATMODULE "format-vorbis"
-#include "logging.h"
-
-
-typedef struct vorbis_codec_tag
-{
-    vorbis_info vi;
-    vorbis_comment vc;
-
-    int rebuild_comment;
-    int stream_notify;
-    int initial_audio_page;
-
-    ogg_stream_state new_os;
-    int page_samples_trigger;
-    ogg_int64_t prev_granulepos;
-    ogg_packet *prev_packet;
-    ogg_int64_t granulepos;
-    ogg_int64_t initial_page_granulepos;
-    ogg_int64_t samples_in_page;
-    int prev_window;
-    int initial_audio_packet;
-
-    ogg_page bos_page;
-    ogg_packet *header [3];
-    ogg_int64_t prev_page_samples;
-
-    int (*process_packet)(ogg_state_t *ogg_info, ogg_codec_t *codec);
-    refbuf_t *(*get_buffer_page)(ogg_state_t *ogg_info, ogg_codec_t *codec);
-
-} vorbis_codec_t;
-
-static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec);
-static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
-                ogg_codec_t *codec, ogg_page *page);
-static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec);
-static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value);
-
-
-static void free_ogg_packet (ogg_packet *packet)
-{
-    if (packet)
-    {
-        free (packet->packet);
-        free (packet);
-    }
-}
-
-
-static void vorbis_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    vorbis_codec_t *vorbis = codec->specific;
-
-    DEBUG0 ("freeing vorbis codec");
-    stats_event (ogg_info->mount, "audio_bitrate", NULL);
-    stats_event (ogg_info->mount, "audio_channels", NULL);
-    stats_event (ogg_info->mount, "audio_samplerate", NULL);
-    vorbis_info_clear (&vorbis->vi);
-    vorbis_comment_clear (&vorbis->vc);
-    ogg_stream_clear (&codec->os);
-    ogg_stream_clear (&vorbis->new_os);
-    free_ogg_packet (vorbis->header[0]);
-    free_ogg_packet (vorbis->header[1]);
-    free_ogg_packet (vorbis->header[2]);
-    free_ogg_packet (vorbis->prev_packet);
-    free (vorbis->bos_page.header);
-    free (vorbis);
-    free (codec);
-}
-
-
-static ogg_packet *copy_ogg_packet (ogg_packet *packet)
-{
-    ogg_packet *next;
-    do
-    {
-        next = malloc (sizeof (ogg_packet));
-        if (next == NULL)
-            break;
-        memcpy (next, packet, sizeof (ogg_packet));
-        next->packet = malloc (next->bytes);
-        if (next->packet == NULL)
-            break;
-        memcpy (next->packet, packet->packet, next->bytes);
-        return next;
-    } while (0);
-
-    if (next)
-        free (next);
-    return NULL;
-}
-
-
-static void add_audio_packet (vorbis_codec_t *source_vorbis, ogg_packet *packet)
-{
-    if (source_vorbis->initial_audio_packet)
-    {
-        packet->granulepos = 0;
-        source_vorbis->initial_audio_packet = 0;
-    }
-    else
-    {
-        source_vorbis->samples_in_page +=
-            (packet->granulepos - source_vorbis->prev_granulepos);
-        source_vorbis->prev_granulepos = packet->granulepos;
-        source_vorbis->granulepos += source_vorbis->prev_window;
-    }
-    ogg_stream_packetin (&source_vorbis->new_os, packet);
-}
-
-
-static refbuf_t *get_buffer_audio (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    refbuf_t *refbuf = NULL;
-    ogg_page page;
-    vorbis_codec_t *source_vorbis = codec->specific;
-    int (*get_ogg_page)(ogg_stream_state*, ogg_page *) = ogg_stream_pageout;
-
-    if (source_vorbis->samples_in_page > source_vorbis->page_samples_trigger)
-        get_ogg_page = ogg_stream_flush;
-
-    if (get_ogg_page (&source_vorbis->new_os, &page) > 0)
-    {
-        /* squeeze a page copy into a buffer */
-        source_vorbis->samples_in_page -= (ogg_page_granulepos (&page) - source_vorbis->prev_page_samples);
-        source_vorbis->prev_page_samples = ogg_page_granulepos (&page);
-
-        refbuf = make_refbuf_with_page (&page);
-    }
-    return refbuf;
-}
-
-
-static refbuf_t *get_buffer_header (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    int headers_flushed = 0;
-    ogg_page page;
-    vorbis_codec_t *source_vorbis = codec->specific;
-
-    while (ogg_stream_flush (&source_vorbis->new_os, &page) > 0)
-    {
-        format_ogg_attach_header (ogg_info, &page);
-        headers_flushed = 1;
-    }
-    if (headers_flushed)
-    {
-        source_vorbis->get_buffer_page = get_buffer_audio;
-    }
-    return NULL;
-}
-
-
-static refbuf_t *get_buffer_finished (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    vorbis_codec_t *source_vorbis = codec->specific;
-    ogg_page page;
-    refbuf_t *refbuf;
-
-    if (ogg_stream_flush (&source_vorbis->new_os, &page) > 0)
-    {
-        source_vorbis->samples_in_page -= (ogg_page_granulepos (&page) - source_vorbis->prev_page_samples);
-        source_vorbis->prev_page_samples = ogg_page_granulepos (&page);
-
-        refbuf = make_refbuf_with_page (&page);
-        DEBUG0 ("flushing page");
-        return refbuf;
-    }
-    ogg_stream_clear (&source_vorbis->new_os);
-    ogg_stream_init (&source_vorbis->new_os, rand());
-
-    format_ogg_free_headers (ogg_info);
-    source_vorbis->get_buffer_page = NULL;
-    if (source_vorbis->prev_packet)
-        source_vorbis->process_packet = process_vorbis_headers;
-    else
-        source_vorbis->process_packet = NULL;
-
-    if (source_vorbis->initial_audio_packet == 0)
-        source_vorbis->prev_window = 0;
-
-    return NULL;
-}
-
-
-/* push last packet into stream marked with eos */
-static void initiate_flush (vorbis_codec_t *source_vorbis)
-{
-    if (source_vorbis->prev_packet)
-    {
-        /* insert prev_packet with eos */
-        DEBUG0 ("adding EOS packet");
-        source_vorbis->prev_packet->e_o_s = 1;
-        add_audio_packet (source_vorbis, source_vorbis->prev_packet);
-        source_vorbis->prev_packet->e_o_s = 0;
-    }
-    source_vorbis->get_buffer_page = get_buffer_finished;
-    source_vorbis->initial_audio_packet = 1;
-}
-
-
-/* process the vorbis audio packets. Here we just take each packet out 
- * and add them into the new stream, flushing after so many samples. We
- * also check if an new headers are requested after each processed page
- */
-static int process_vorbis_audio (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    vorbis_codec_t *source_vorbis = codec->specific;
-
-    while (1)
-    {
-        int window;
-        ogg_packet packet;
-
-        /* now, lets extract what packets we can */
-        if (ogg_stream_packetout (&codec->os, &packet) <= 0)
-            break;
-
-        /* calculate granulepos for the packet */
-        window = vorbis_packet_blocksize (&source_vorbis->vi, &packet) / 4;
-
-        source_vorbis->granulepos += window;
-        if (source_vorbis->prev_packet)
-        {
-            ogg_packet *prev_packet = source_vorbis->prev_packet;
-
-            add_audio_packet (source_vorbis, prev_packet);
-            free_ogg_packet (prev_packet);
-
-            /* check for short values on first initial page */
-            if (packet . packetno == 4)
-            {
-                if (source_vorbis->initial_page_granulepos < source_vorbis->granulepos)
-                {
-                    source_vorbis->granulepos -= source_vorbis->initial_page_granulepos;
-                    source_vorbis->samples_in_page = source_vorbis->page_samples_trigger;
-                }
-            }
-            /* check for long values on first page */
-            if (packet.granulepos == source_vorbis->initial_page_granulepos)
-            {
-                if (source_vorbis->initial_page_granulepos > source_vorbis->granulepos)
-                    source_vorbis->granulepos = source_vorbis->initial_page_granulepos;
-            }
-
-            if (packet.e_o_s == 0)
-                packet . granulepos = source_vorbis->granulepos;
-        }
-        else
-        {
-            packet . granulepos = 0;
-        }
-
-        /* store the current packet details */
-        source_vorbis->prev_window = window;
-        source_vorbis->prev_packet = copy_ogg_packet (&packet);
-        if (packet.e_o_s)
-        {
-            initiate_flush (source_vorbis);
-            free_ogg_packet (source_vorbis->prev_packet);
-            source_vorbis->prev_packet = NULL;
-            return 1;
-        }
-
-        /* allow for pages to be flushed if there's over a certain number of samples */
-        if (source_vorbis->samples_in_page > source_vorbis->page_samples_trigger)
-            return 1;
-    }
-    if (source_vorbis->stream_notify)
-    {
-        initiate_flush (source_vorbis);
-        source_vorbis->stream_notify = 0;
-        return 1;
-    }
-    return -1;
-}
-
-
-/* This handles the headers at the backend, here we insert the header packets
- * we want for the queue.
- */
-static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    vorbis_codec_t *source_vorbis = codec->specific;
-
-    if (source_vorbis->header [0] == NULL)
-        return 0;
-
-    DEBUG0 ("Adding the 3 header packets");
-    ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [0]);
-    /* NOTE: we could build a separate comment packet each time */
-    if (source_vorbis->rebuild_comment)
-    {
-        vorbis_comment vc;
-        ogg_packet header;
-
-        vorbis_comment_init (&vc);
-        if (ogg_info->artist) 
-            vorbis_comment_add_tag (&vc, "artist", ogg_info->artist);
-        if (ogg_info->title)
-            vorbis_comment_add_tag (&vc, "title", ogg_info->title);
-        vorbis_comment_add_tag (&vc, "server", ICECAST_VERSION_STRING);
-        vorbis_commentheader_out (&vc, &header);
-
-        ogg_stream_packetin (&source_vorbis->new_os, &header);
-        vorbis_comment_clear (&vc);
-        ogg_packet_clear (&header);
-    }
-    else
-        ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [1]);
-    ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [2]);
-    source_vorbis->rebuild_comment = 0;
-
-    ogg_info->log_metadata = 1;
-    source_vorbis->get_buffer_page = get_buffer_header;
-    source_vorbis->process_packet = process_vorbis_audio;
-    source_vorbis->granulepos = source_vorbis->prev_window;
-    source_vorbis->initial_audio_packet = 1;
-    return 1;
-}
-
-
-/* check if the provided BOS page is the start of a vorbis stream. If so
- * then setup a structure so it can be used
- */
-ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page)
-{
-    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
-    ogg_packet packet;
-
-    vorbis_codec_t *vorbis = calloc (1, sizeof (vorbis_codec_t));
-
-    ogg_stream_init (&codec->os, ogg_page_serialno (page));
-    ogg_stream_pagein (&codec->os, page);
-
-    vorbis_info_init (&vorbis->vi);
-    vorbis_comment_init (&vorbis->vc);
-
-    ogg_stream_packetout (&codec->os, &packet);
-
-    DEBUG0("checking for vorbis codec");
-    if (vorbis_synthesis_headerin (&vorbis->vi, &vorbis->vc, &packet) < 0)
-    {
-        ogg_stream_clear (&codec->os);
-        vorbis_info_clear (&vorbis->vi);
-        vorbis_comment_clear (&vorbis->vc);
-        free (vorbis);
-        free (codec);
-        return NULL;
-    }
-    INFO0 ("seen initial vorbis header");
-    codec->specific = vorbis;
-    codec->codec_free = vorbis_codec_free;
-    codec->headers = 1;
-    codec->name = "Vorbis";
-
-    free_ogg_packet (vorbis->header[0]);
-    free_ogg_packet (vorbis->header[1]);
-    free_ogg_packet (vorbis->header[2]);
-    memset (vorbis->header, 0, sizeof (vorbis->header));
-    vorbis->header [0] = copy_ogg_packet (&packet);
-    ogg_stream_init (&vorbis->new_os, rand());
-
-    codec->process_page = process_vorbis_page;
-    codec->process = process_vorbis;
-    plugin->set_tag = vorbis_set_tag;
-
-    vorbis->bos_page.header = malloc (page->header_len + page->body_len);
-    
-    memcpy (vorbis->bos_page.header, page->header, page->header_len);
-    vorbis->bos_page.header_len = page->header_len;
-
-    vorbis->bos_page.body = vorbis->bos_page.header + page->header_len;
-    memcpy (vorbis->bos_page.body, page->body, page->body_len);
-    vorbis->bos_page.body_len = page->body_len;
-
-    return codec;
-}
-
-
-/* called from the admin interface, here we update the artist/title info
- * and schedule a new set of header pages
- */
-static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value)
-{   
-    ogg_state_t *ogg_info = plugin->_state;
-    ogg_codec_t *codec = ogg_info->codecs;
-    vorbis_codec_t *source_vorbis;
-    int change = 0;
-
-    /* avoid updating if multiple codecs in use */
-    if (codec && codec->next == NULL)
-        source_vorbis = codec->specific;
-    else
-        return;
-
-    if (strcmp (tag, "artist") == 0)
-    {
-        char *p = strdup (value);
-        if (p)
-        {
-            free (ogg_info->artist);
-            ogg_info->artist = p;
-            change = 1;
-        }
-    }
-    if (strcmp (tag, "title") == 0)
-    {
-        char *p = strdup (value);
-        if (p)
-        {
-            free (ogg_info->title);
-            ogg_info->title = p;
-            change = 1;
-        }
-    }
-    if (strcmp (tag, "song") == 0)
-    {
-        char *p = strdup (value);
-        if (p)
-        {
-            free (ogg_info->artist);
-            free (ogg_info->title);
-            ogg_info->artist = NULL;
-            ogg_info->title = p;
-            change = 1;
-        }
-    }
-    if (change)
-    {
-        source_vorbis->stream_notify = 1;
-        source_vorbis->rebuild_comment = 1;
-    }
-}
-
-
-/* main backend routine when rebuilding streams. Here we loop until we either
- * have a refbuf to add onto the queue, or we want more data to process.
- */
-static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec)
-{
-    vorbis_codec_t *source_vorbis = codec->specific;
-    refbuf_t *refbuf;
-
-    while (1)
-    {
-        if (source_vorbis->get_buffer_page)
-        {
-            refbuf = source_vorbis->get_buffer_page (ogg_info, codec);
-            if (refbuf)
-                return refbuf;
-        }
-
-        if (source_vorbis->process_packet &&
-                source_vorbis->process_packet (ogg_info, codec) > 0)
-            continue;
-        return NULL;
-    }
-}
-
-
-/* no processing of pages, just wrap them up in a refbuf and pass
- * back for adding to the queue
- */
-static refbuf_t *process_vorbis_passthru_page (ogg_state_t *ogg_info,
-        ogg_codec_t *codec, ogg_page *page)
-{
-    return make_refbuf_with_page (page);
-}
-
-
-/* handle incoming page. as the stream is being rebuilt, we need to
- * add all pages from the stream before processing packets
- */
-static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
-        ogg_codec_t *codec, ogg_page *page)
-{
-    ogg_packet header;
-    vorbis_codec_t *source_vorbis = codec->specific;
-    char *comment;
-
-    if (ogg_stream_pagein (&codec->os, page) < 0)
-    {
-        ogg_info->error = 1;
-        return NULL;
-    }
-    if (codec->headers == 3)
-    {
-        if (source_vorbis->initial_audio_page)
-        {
-            source_vorbis->initial_page_granulepos = ogg_page_granulepos (page);
-            source_vorbis->initial_audio_page = 0;
-        }
-        return NULL;
-    }
-
-    while (codec->headers < 3)
-    {
-        /* now, lets extract the packets */
-        DEBUG1 ("processing incoming header packet (%d)", codec->headers);
-
-        if (ogg_stream_packetout (&codec->os, &header) <= 0)
-        {
-            if (ogg_info->codecs->next)
-                format_ogg_attach_header (ogg_info, page);
-            return NULL;
-        }
-
-        /* change comments here if need be */
-        if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0)
-        {
-            ogg_info->error = 1;
-            WARN0 ("Problem parsing ogg vorbis header");
-            return NULL;
-        }
-        header.granulepos = 0;
-        source_vorbis->header [codec->headers] = copy_ogg_packet (&header);
-        codec->headers++;
-    }
-    DEBUG0 ("we have the header packets now");
-
-    /* if vorbis is the only codec then allow rebuilding of the streams */
-    if (ogg_info->codecs->next == NULL)
-    {
-        /* set queued vorbis pages to contain about 1/2 of a second worth of samples */
-        source_vorbis->page_samples_trigger = source_vorbis->vi.rate / 2;
-        source_vorbis->process_packet = process_vorbis_headers;
-        source_vorbis->initial_audio_page = 1;
-    }
-    else
-    {
-        format_ogg_attach_header (ogg_info, &source_vorbis->bos_page);
-        format_ogg_attach_header (ogg_info, page);
-        codec->process_page = process_vorbis_passthru_page;
-    }
-
-    free (ogg_info->title);
-    comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0);
-    if (comment)
-        ogg_info->title = strdup (comment);
-    else
-        ogg_info->title = NULL;
-
-    free (ogg_info->artist);
-    comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
-    if (comment)
-        ogg_info->artist = strdup (comment);
-    else
-        ogg_info->artist = NULL;
-    ogg_info->log_metadata = 1;
-
-    stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate);
-    stats_event_args (ogg_info->mount, "audio_channels", "%ld", (long)source_vorbis->vi.channels);
-    stats_event_args (ogg_info->mount, "audio_bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal);
-    stats_event_args (ogg_info->mount, "ice-bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal/1000);
-
-    return NULL;
-}
-

Copied: icecast/tags/icecast-2.3.2/src/format_vorbis.c (from rev 13798, icecast/trunk/icecast/src/format_vorbis.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/format_vorbis.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/format_vorbis.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,589 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+
+/* Ogg codec handler for vorbis streams */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#include <memory.h>
+#include <string.h>
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "format_ogg.h"
+#include "stats.h"
+#include "format.h"
+
+#define CATMODULE "format-vorbis"
+#include "logging.h"
+
+
+typedef struct vorbis_codec_tag
+{
+    vorbis_info vi;
+    vorbis_comment vc;
+
+    int rebuild_comment;
+    int stream_notify;
+    int initial_audio_page;
+
+    ogg_stream_state new_os;
+    int page_samples_trigger;
+    ogg_int64_t prev_granulepos;
+    ogg_packet *prev_packet;
+    ogg_int64_t granulepos;
+    ogg_int64_t initial_page_granulepos;
+    ogg_int64_t samples_in_page;
+    int prev_window;
+    int initial_audio_packet;
+
+    ogg_page bos_page;
+    ogg_packet *header [3];
+    ogg_int64_t prev_page_samples;
+
+    int (*process_packet)(ogg_state_t *ogg_info, ogg_codec_t *codec);
+    refbuf_t *(*get_buffer_page)(ogg_state_t *ogg_info, ogg_codec_t *codec);
+
+} vorbis_codec_t;
+
+static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec);
+static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
+                ogg_codec_t *codec, ogg_page *page);
+static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec);
+static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *value, const char *charset);
+
+
+static void free_ogg_packet (ogg_packet *packet)
+{
+    if (packet)
+    {
+        free (packet->packet);
+        free (packet);
+    }
+}
+
+
+static void vorbis_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    vorbis_codec_t *vorbis = codec->specific;
+
+    DEBUG0 ("freeing vorbis codec");
+    stats_event (ogg_info->mount, "audio_bitrate", NULL);
+    stats_event (ogg_info->mount, "audio_channels", NULL);
+    stats_event (ogg_info->mount, "audio_samplerate", NULL);
+    vorbis_info_clear (&vorbis->vi);
+    vorbis_comment_clear (&vorbis->vc);
+    ogg_stream_clear (&codec->os);
+    ogg_stream_clear (&vorbis->new_os);
+    free_ogg_packet (vorbis->header[0]);
+    free_ogg_packet (vorbis->header[1]);
+    free_ogg_packet (vorbis->header[2]);
+    free_ogg_packet (vorbis->prev_packet);
+    free (vorbis->bos_page.header);
+    free (vorbis);
+    free (codec);
+}
+
+
+static ogg_packet *copy_ogg_packet (ogg_packet *packet)
+{
+    ogg_packet *next;
+    do
+    {
+        next = malloc (sizeof (ogg_packet));
+        if (next == NULL)
+            break;
+        memcpy (next, packet, sizeof (ogg_packet));
+        next->packet = malloc (next->bytes);
+        if (next->packet == NULL)
+            break;
+        memcpy (next->packet, packet->packet, next->bytes);
+        return next;
+    } while (0);
+
+    if (next)
+        free (next);
+    return NULL;
+}
+
+
+static void add_audio_packet (vorbis_codec_t *source_vorbis, ogg_packet *packet)
+{
+    if (source_vorbis->initial_audio_packet)
+    {
+        packet->granulepos = 0;
+        source_vorbis->initial_audio_packet = 0;
+    }
+    else
+    {
+        source_vorbis->samples_in_page +=
+            (packet->granulepos - source_vorbis->prev_granulepos);
+        source_vorbis->prev_granulepos = packet->granulepos;
+        source_vorbis->granulepos += source_vorbis->prev_window;
+    }
+    ogg_stream_packetin (&source_vorbis->new_os, packet);
+}
+
+
+static refbuf_t *get_buffer_audio (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    refbuf_t *refbuf = NULL;
+    ogg_page page;
+    vorbis_codec_t *source_vorbis = codec->specific;
+    int (*get_ogg_page)(ogg_stream_state*, ogg_page *) = ogg_stream_pageout;
+
+    if (source_vorbis->samples_in_page > source_vorbis->page_samples_trigger)
+        get_ogg_page = ogg_stream_flush;
+
+    if (get_ogg_page (&source_vorbis->new_os, &page) > 0)
+    {
+        /* squeeze a page copy into a buffer */
+        source_vorbis->samples_in_page -= (ogg_page_granulepos (&page) - source_vorbis->prev_page_samples);
+        source_vorbis->prev_page_samples = ogg_page_granulepos (&page);
+
+        refbuf = make_refbuf_with_page (&page);
+    }
+    return refbuf;
+}
+
+
+static refbuf_t *get_buffer_header (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    int headers_flushed = 0;
+    ogg_page page;
+    vorbis_codec_t *source_vorbis = codec->specific;
+
+    while (ogg_stream_flush (&source_vorbis->new_os, &page) > 0)
+    {
+        format_ogg_attach_header (ogg_info, &page);
+        headers_flushed = 1;
+    }
+    if (headers_flushed)
+    {
+        source_vorbis->get_buffer_page = get_buffer_audio;
+    }
+    return NULL;
+}
+
+
+static refbuf_t *get_buffer_finished (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    vorbis_codec_t *source_vorbis = codec->specific;
+    ogg_page page;
+    refbuf_t *refbuf;
+
+    if (ogg_stream_flush (&source_vorbis->new_os, &page) > 0)
+    {
+        source_vorbis->samples_in_page -= (ogg_page_granulepos (&page) - source_vorbis->prev_page_samples);
+        source_vorbis->prev_page_samples = ogg_page_granulepos (&page);
+
+        refbuf = make_refbuf_with_page (&page);
+        DEBUG0 ("flushing page");
+        return refbuf;
+    }
+    ogg_stream_clear (&source_vorbis->new_os);
+    ogg_stream_init (&source_vorbis->new_os, rand());
+
+    format_ogg_free_headers (ogg_info);
+    source_vorbis->get_buffer_page = NULL;
+    if (source_vorbis->prev_packet)
+        source_vorbis->process_packet = process_vorbis_headers;
+    else
+        source_vorbis->process_packet = NULL;
+
+    if (source_vorbis->initial_audio_packet == 0)
+        source_vorbis->prev_window = 0;
+
+    return NULL;
+}
+
+
+/* push last packet into stream marked with eos */
+static void initiate_flush (vorbis_codec_t *source_vorbis)
+{
+    if (source_vorbis->prev_packet)
+    {
+        /* insert prev_packet with eos */
+        DEBUG0 ("adding EOS packet");
+        source_vorbis->prev_packet->e_o_s = 1;
+        add_audio_packet (source_vorbis, source_vorbis->prev_packet);
+        source_vorbis->prev_packet->e_o_s = 0;
+    }
+    source_vorbis->get_buffer_page = get_buffer_finished;
+    source_vorbis->initial_audio_packet = 1;
+}
+
+
+/* process the vorbis audio packets. Here we just take each packet out 
+ * and add them into the new stream, flushing after so many samples. We
+ * also check if an new headers are requested after each processed page
+ */
+static int process_vorbis_audio (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    vorbis_codec_t *source_vorbis = codec->specific;
+
+    while (1)
+    {
+        int window;
+        ogg_packet packet;
+
+        /* now, lets extract what packets we can */
+        if (ogg_stream_packetout (&codec->os, &packet) <= 0)
+            break;
+
+        /* calculate granulepos for the packet */
+        window = vorbis_packet_blocksize (&source_vorbis->vi, &packet) / 4;
+
+        source_vorbis->granulepos += window;
+        if (source_vorbis->prev_packet)
+        {
+            ogg_packet *prev_packet = source_vorbis->prev_packet;
+
+            add_audio_packet (source_vorbis, prev_packet);
+            free_ogg_packet (prev_packet);
+
+            /* check for short values on first initial page */
+            if (packet . packetno == 4)
+            {
+                if (source_vorbis->initial_page_granulepos < source_vorbis->granulepos)
+                {
+                    source_vorbis->granulepos -= source_vorbis->initial_page_granulepos;
+                    source_vorbis->samples_in_page = source_vorbis->page_samples_trigger;
+                }
+            }
+            /* check for long values on first page */
+            if (packet.granulepos == source_vorbis->initial_page_granulepos)
+            {
+                if (source_vorbis->initial_page_granulepos > source_vorbis->granulepos)
+                    source_vorbis->granulepos = source_vorbis->initial_page_granulepos;
+            }
+
+            if (packet.e_o_s == 0)
+                packet . granulepos = source_vorbis->granulepos;
+        }
+        else
+        {
+            packet . granulepos = 0;
+        }
+
+        /* store the current packet details */
+        source_vorbis->prev_window = window;
+        source_vorbis->prev_packet = copy_ogg_packet (&packet);
+        if (packet.e_o_s)
+        {
+            initiate_flush (source_vorbis);
+            free_ogg_packet (source_vorbis->prev_packet);
+            source_vorbis->prev_packet = NULL;
+            return 1;
+        }
+
+        /* allow for pages to be flushed if there's over a certain number of samples */
+        if (source_vorbis->samples_in_page > source_vorbis->page_samples_trigger)
+            return 1;
+    }
+    if (source_vorbis->stream_notify)
+    {
+        initiate_flush (source_vorbis);
+        source_vorbis->stream_notify = 0;
+        return 1;
+    }
+    return -1;
+}
+
+
+/* This handles the headers at the backend, here we insert the header packets
+ * we want for the queue.
+ */
+static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    vorbis_codec_t *source_vorbis = codec->specific;
+
+    if (source_vorbis->header [0] == NULL)
+        return 0;
+
+    DEBUG0 ("Adding the 3 header packets");
+    ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [0]);
+    /* NOTE: we could build a separate comment packet each time */
+    if (source_vorbis->rebuild_comment)
+    {
+        vorbis_comment vc;
+        ogg_packet header;
+        ice_config_t *config;
+
+        vorbis_comment_init (&vc);
+        if (ogg_info->artist) 
+            vorbis_comment_add_tag (&vc, "artist", ogg_info->artist);
+        if (ogg_info->title)
+            vorbis_comment_add_tag (&vc, "title", ogg_info->title);
+        config = config_get_config();
+        vorbis_comment_add_tag (&vc, "server", config->server_id);
+        config_release_config();
+        vorbis_commentheader_out (&vc, &header);
+
+        ogg_stream_packetin (&source_vorbis->new_os, &header);
+        vorbis_comment_clear (&vc);
+        ogg_packet_clear (&header);
+    }
+    else
+        ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [1]);
+    ogg_stream_packetin (&source_vorbis->new_os, source_vorbis->header [2]);
+    source_vorbis->rebuild_comment = 0;
+
+    ogg_info->log_metadata = 1;
+    source_vorbis->get_buffer_page = get_buffer_header;
+    source_vorbis->process_packet = process_vorbis_audio;
+    source_vorbis->granulepos = source_vorbis->prev_window;
+    source_vorbis->initial_audio_packet = 1;
+    return 1;
+}
+
+
+/* check if the provided BOS page is the start of a vorbis stream. If so
+ * then setup a structure so it can be used
+ */
+ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    vorbis_codec_t *vorbis = calloc (1, sizeof (vorbis_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    vorbis_info_init (&vorbis->vi);
+    vorbis_comment_init (&vorbis->vc);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    DEBUG0("checking for vorbis codec");
+    if (vorbis_synthesis_headerin (&vorbis->vi, &vorbis->vc, &packet) < 0)
+    {
+        ogg_stream_clear (&codec->os);
+        vorbis_info_clear (&vorbis->vi);
+        vorbis_comment_clear (&vorbis->vc);
+        free (vorbis);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial vorbis header");
+    codec->specific = vorbis;
+    codec->codec_free = vorbis_codec_free;
+    codec->headers = 1;
+    codec->name = "Vorbis";
+
+    free_ogg_packet (vorbis->header[0]);
+    free_ogg_packet (vorbis->header[1]);
+    free_ogg_packet (vorbis->header[2]);
+    memset (vorbis->header, 0, sizeof (vorbis->header));
+    vorbis->header [0] = copy_ogg_packet (&packet);
+    ogg_stream_init (&vorbis->new_os, rand());
+
+    codec->process_page = process_vorbis_page;
+    codec->process = process_vorbis;
+    plugin->set_tag = vorbis_set_tag;
+
+    vorbis->bos_page.header = malloc (page->header_len + page->body_len);
+    
+    memcpy (vorbis->bos_page.header, page->header, page->header_len);
+    vorbis->bos_page.header_len = page->header_len;
+
+    vorbis->bos_page.body = vorbis->bos_page.header + page->header_len;
+    memcpy (vorbis->bos_page.body, page->body, page->body_len);
+    vorbis->bos_page.body_len = page->body_len;
+
+    return codec;
+}
+
+
+/* called from the admin interface, here we update the artist/title info
+ * and schedule a new set of header pages
+ */
+static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset)
+{   
+    ogg_state_t *ogg_info = plugin->_state;
+    ogg_codec_t *codec = ogg_info->codecs;
+    vorbis_codec_t *source_vorbis;
+    int change = 0;
+    char *value;
+
+    /* avoid updating if multiple codecs in use */
+    if (codec && codec->next == NULL)
+        source_vorbis = codec->specific;
+    else
+        return;
+
+    value = util_conv_string (in_value, charset, "UTF-8");
+    if (value == NULL)
+        value = strdup (in_value);
+
+    if (strcmp (tag, "artist") == 0)
+    {
+        free (ogg_info->artist);
+        ogg_info->artist = value;
+        change = 1;
+    }
+    else if (strcmp (tag, "title") == 0)
+    {
+        free (ogg_info->title);
+        ogg_info->title = value;
+        change = 1;
+    }
+    else if (strcmp (tag, "song") == 0)
+    {
+        free (ogg_info->artist);
+        free (ogg_info->title);
+        ogg_info->artist = NULL;
+        ogg_info->title = value;
+        change = 1;
+    }
+    if (change)
+    {
+        source_vorbis->stream_notify = 1;
+        source_vorbis->rebuild_comment = 1;
+    }
+    else
+        free (value);
+}
+
+
+/* main backend routine when rebuilding streams. Here we loop until we either
+ * have a refbuf to add onto the queue, or we want more data to process.
+ */
+static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec)
+{
+    vorbis_codec_t *source_vorbis = codec->specific;
+    refbuf_t *refbuf;
+
+    while (1)
+    {
+        if (source_vorbis->get_buffer_page)
+        {
+            refbuf = source_vorbis->get_buffer_page (ogg_info, codec);
+            if (refbuf)
+                return refbuf;
+        }
+
+        if (source_vorbis->process_packet &&
+                source_vorbis->process_packet (ogg_info, codec) > 0)
+            continue;
+        return NULL;
+    }
+}
+
+
+/* no processing of pages, just wrap them up in a refbuf and pass
+ * back for adding to the queue
+ */
+static refbuf_t *process_vorbis_passthru_page (ogg_state_t *ogg_info,
+        ogg_codec_t *codec, ogg_page *page)
+{
+    return make_refbuf_with_page (page);
+}
+
+
+/* handle incoming page. as the stream is being rebuilt, we need to
+ * add all pages from the stream before processing packets
+ */
+static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
+        ogg_codec_t *codec, ogg_page *page)
+{
+    ogg_packet header;
+    vorbis_codec_t *source_vorbis = codec->specific;
+    char *comment;
+
+    if (ogg_stream_pagein (&codec->os, page) < 0)
+    {
+        ogg_info->error = 1;
+        return NULL;
+    }
+    if (codec->headers == 3)
+    {
+        if (source_vorbis->initial_audio_page)
+        {
+            source_vorbis->initial_page_granulepos = ogg_page_granulepos (page);
+            source_vorbis->initial_audio_page = 0;
+        }
+        return NULL;
+    }
+
+    while (codec->headers < 3)
+    {
+        /* now, lets extract the packets */
+        DEBUG1 ("processing incoming header packet (%d)", codec->headers);
+
+        if (ogg_stream_packetout (&codec->os, &header) <= 0)
+        {
+            if (ogg_info->codecs->next)
+                format_ogg_attach_header (ogg_info, page);
+            return NULL;
+        }
+
+        /* change comments here if need be */
+        if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0)
+        {
+            ogg_info->error = 1;
+            WARN0 ("Problem parsing ogg vorbis header");
+            return NULL;
+        }
+        header.granulepos = 0;
+        source_vorbis->header [codec->headers] = copy_ogg_packet (&header);
+        codec->headers++;
+    }
+    DEBUG0 ("we have the header packets now");
+
+    /* if vorbis is the only codec then allow rebuilding of the streams */
+    if (ogg_info->codecs->next == NULL)
+    {
+        /* set queued vorbis pages to contain about 1/2 of a second worth of samples */
+        source_vorbis->page_samples_trigger = source_vorbis->vi.rate / 2;
+        source_vorbis->process_packet = process_vorbis_headers;
+        source_vorbis->initial_audio_page = 1;
+    }
+    else
+    {
+        format_ogg_attach_header (ogg_info, &source_vorbis->bos_page);
+        format_ogg_attach_header (ogg_info, page);
+        codec->process_page = process_vorbis_passthru_page;
+    }
+
+    free (ogg_info->title);
+    comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0);
+    if (comment)
+        ogg_info->title = strdup (comment);
+    else
+        ogg_info->title = NULL;
+
+    free (ogg_info->artist);
+    comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
+    if (comment)
+        ogg_info->artist = strdup (comment);
+    else
+        ogg_info->artist = NULL;
+    ogg_info->log_metadata = 1;
+
+    stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate);
+    stats_event_args (ogg_info->mount, "audio_channels", "%ld", (long)source_vorbis->vi.channels);
+    stats_event_args (ogg_info->mount, "audio_bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal);
+    stats_event_args (ogg_info->mount, "ice-bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal/1000);
+
+    return NULL;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/fserve.c
===================================================================
--- icecast/trunk/icecast/src/fserve.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/fserve.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,710 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-
-#ifdef HAVE_POLL
-#include <sys/poll.h>
-#endif
-
-#ifndef _WIN32
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#else
-#include <winsock2.h>
-#include <windows.h>
-#define snprintf _snprintf
-#define S_ISREG(mode)  ((mode) & _S_IFREG)
-#endif
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "httpp/httpp.h"
-#include "net/sock.h"
-
-#include "connection.h"
-#include "global.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "format.h"
-#include "logging.h"
-#include "cfgfile.h"
-#include "util.h"
-#include "admin.h"
-#include "compat.h"
-
-#include "fserve.h"
-
-#undef CATMODULE
-#define CATMODULE "fserve"
-
-#define BUFSIZE 4096
-
-#ifdef _WIN32
-#define MIMETYPESFILE ".\\mime.types"
-#else
-#define MIMETYPESFILE "/etc/mime.types"
-#endif
-
-static fserve_t *active_list = NULL;
-static volatile fserve_t *pending_list = NULL;
-
-static mutex_t pending_lock;
-static avl_tree *mimetypes = NULL;
-
-static thread_type *fserv_thread;
-static int run_fserv = 0;
-static unsigned int fserve_clients;
-static int client_tree_changed=0;
-
-#ifdef HAVE_POLL
-static struct pollfd *ufds = NULL;
-#else
-static fd_set fds;
-static int fd_max = -1;
-#endif
-
-typedef struct {
-    char *ext;
-    char *type;
-} mime_type;
-
-static void fserve_client_destroy(fserve_t *fclient);
-static int _delete_mapping(void *mapping);
-static void *fserv_thread_function(void *arg);
-static void create_mime_mappings(const char *fn);
-
-void fserve_initialize(void)
-{
-    create_mime_mappings(MIMETYPESFILE);
-
-    thread_mutex_create (&pending_lock);
-
-    run_fserv = 1;
-    stats_event (NULL, "file_connections", "0");
-
-    fserv_thread = thread_create("File Serving Thread", 
-            fserv_thread_function, NULL, THREAD_ATTACHED);
-}
-
-void fserve_shutdown(void)
-{
-    if(!run_fserv)
-        return;
-
-    run_fserv = 0;
-    thread_join(fserv_thread);
-    INFO0("file serving thread stopped");
-    avl_tree_free(mimetypes, _delete_mapping);
-}
-
-#ifdef HAVE_POLL
-int fserve_client_waiting (void)
-{
-    fserve_t *fclient;
-    unsigned int i = 0;
-
-    /* only rebuild ufds if there are clients added/removed */
-    if (client_tree_changed)
-    {
-        client_tree_changed = 0;
-        ufds = realloc(ufds, fserve_clients * sizeof(struct pollfd));
-        fclient = active_list;
-        while (fclient)
-        {
-            ufds[i].fd = fclient->client->con->sock;
-            ufds[i].events = POLLOUT;
-            ufds[i].revents = 0;
-            fclient = fclient->next;
-            i++;
-        }
-    }
-    if (!ufds)
-        thread_sleep(200000);
-    else if (poll(ufds, fserve_clients, 200) > 0)
-    {
-        /* mark any clients that are ready */
-        fclient = active_list;
-        for (i=0; i<fserve_clients; i++)
-        {
-            if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR))
-                fclient->ready = 1;
-            fclient = fclient->next;
-        }
-        return 1;
-    }
-    return 0;
-}
-#else
-int fserve_client_waiting (void)
-{
-    fserve_t *fclient;
-    fd_set realfds;
-
-    /* only rebuild fds if there are clients added/removed */
-    if(client_tree_changed) {
-        client_tree_changed = 0;
-        FD_ZERO(&fds);
-        fd_max = -1;
-        fclient = active_list;
-        while (fclient) {
-            FD_SET (fclient->client->con->sock, &fds);
-            if (fclient->client->con->sock > fd_max)
-                fd_max = fclient->client->con->sock;
-            fclient = fclient->next;
-        }
-    }
-    /* hack for windows, select needs at least 1 descriptor */
-    if (fd_max == -1)
-        thread_sleep (200000);
-    else
-    {
-        struct timeval tv;
-        tv.tv_sec = 0;
-        tv.tv_usec = 200000;
-        /* make a duplicate of the set so we do not have to rebuild it
-         * each time around */
-        memcpy(&realfds, &fds, sizeof(fd_set));
-        if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
-        {
-            /* mark any clients that are ready */
-            fclient = active_list;
-            while (fclient)
-            {
-                if (FD_ISSET (fclient->client->con->sock, &realfds))
-                    fclient->ready = 1;
-                fclient = fclient->next;
-            }
-            return 1;
-        }
-    }
-    return 0;
-}
-#endif
-
-static void wait_for_fds(void) {
-    fserve_t *fclient;
-
-    while (run_fserv)
-    {
-        /* add any new clients here */
-        if (pending_list)
-        {
-            thread_mutex_lock (&pending_lock);
-
-            fclient = (fserve_t*)pending_list;
-            while (fclient)
-            {
-                fserve_t *to_move = fclient;
-                fclient = fclient->next;
-                to_move->next = active_list;
-                active_list = to_move;
-                client_tree_changed = 1;
-                fserve_clients++;
-            }
-            pending_list = NULL;
-            thread_mutex_unlock (&pending_lock);
-        }
-        /* drop out of here if someone is ready */
-        if (fserve_client_waiting())
-            break;
-    }
-}
-
-static void *fserv_thread_function(void *arg)
-{
-    fserve_t *fclient, **trail;
-    int bytes;
-
-    INFO0("file serving thread started");
-    while (run_fserv) {
-        wait_for_fds();
-
-        fclient = active_list;
-        trail = &active_list;
-
-        while (fclient)
-        {
-            /* process this client, if it is ready */
-            if (fclient->ready)
-            {
-                client_t *client = fclient->client;
-                refbuf_t *refbuf = client->refbuf;
-                fclient->ready = 0;
-                if (client->pos == refbuf->len)
-                {
-                    /* Grab a new chunk */
-                    if (fclient->file)
-                        bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
-                    else
-                        bytes = 0;
-                    if (bytes == 0)
-                    {
-                        fserve_t *to_go = fclient;
-                        fclient = fclient->next;
-                        *trail = fclient;
-                        fserve_client_destroy (to_go);
-                        fserve_clients--;
-                        client_tree_changed = 1;
-                        continue;
-                    }
-                    refbuf->len = bytes;
-                    client->pos = 0;
-                }
-
-                /* Now try and send current chunk. */
-                format_generic_write_to_client (client);
-
-                if (client->con->error)
-                {
-                    fserve_t *to_go = fclient;
-                    fclient = fclient->next;
-                    *trail = fclient;
-                    fserve_clients--;
-                    fserve_client_destroy (to_go);
-                    client_tree_changed = 1;
-                    continue;
-                }
-            }
-            trail = &fclient->next;
-            fclient = fclient->next;
-        }
-    }
-
-    /* Shutdown path */
-    thread_mutex_lock (&pending_lock);
-    while (pending_list)
-    {
-        fserve_t *to_go = (fserve_t *)pending_list;
-        pending_list = to_go->next;
-
-        fserve_client_destroy (to_go);
-    }
-    thread_mutex_unlock (&pending_lock);
-
-    while (active_list)
-    {
-        fserve_t *to_go = active_list;
-        active_list = to_go->next;
-        fserve_client_destroy (to_go);
-    }
-
-    return NULL;
-}
-
-char *fserve_content_type (const char *path)
-{
-    char *ext = util_get_extension(path);
-    mime_type exttype = {ext, NULL};
-    void *result;
-
-    if (!avl_get_by_key (mimetypes, &exttype, &result))
-    {
-        mime_type *mime = result;
-        return mime->type;
-    }
-    else {
-        /* Fallbacks for a few basic ones */
-        if(!strcmp(ext, "ogg"))
-            return "application/ogg";
-        else if(!strcmp(ext, "mp3"))
-            return "audio/mpeg";
-        else if(!strcmp(ext, "html"))
-            return "text/html";
-        else if(!strcmp(ext, "css"))
-            return "text/css";
-        else if(!strcmp(ext, "txt"))
-            return "text/plain";
-        else if(!strcmp(ext, "jpg"))
-            return "image/jpeg";
-        else if(!strcmp(ext, "png"))
-            return "image/png";
-        else if(!strcmp(ext, "m3u"))
-            return "audio/x-mpegurl";
-        else
-            return "application/octet-stream";
-    }
-}
-
-static void fserve_client_destroy(fserve_t *fclient)
-{
-    if (fclient)
-    {
-        if (fclient->file)
-            fclose (fclient->file);
-
-        if (fclient->callback)
-            fclient->callback (fclient->client, fclient->arg);
-        else
-            if (fclient->client)
-                client_destroy (fclient->client);
-        free (fclient);
-    }
-}
-
-
-/* client has requested a file, so check for it and send the file.  Do not
- * refer to the client_t afterwards.  return 0 for success, -1 on error.
- */
-int fserve_client_create (client_t *httpclient, const char *path)
-{
-    int bytes;
-    struct stat file_buf;
-    char *range = NULL;
-    int64_t new_content_len = 0;
-    int64_t rangenumber = 0, content_length;
-    int rangeproblem = 0;
-    int ret = 0;
-    char *fullpath;
-    int m3u_requested = 0, m3u_file_available = 1;
-    int xspf_requested = 0, xspf_file_available = 1;
-    ice_config_t *config;
-    FILE *file;
-
-    fullpath = util_get_path_from_normalised_uri (path);
-    INFO2 ("checking for file %s (%s)", path, fullpath);
-
-    if (strcmp (util_get_extension (fullpath), "m3u") == 0)
-        m3u_requested = 1;
-
-    if (strcmp (util_get_extension (fullpath), "xspf") == 0)
-        xspf_requested = 1;
-
-    /* check for the actual file */
-    if (stat (fullpath, &file_buf) != 0)
-    {
-        /* the m3u can be generated, but send an m3u file if available */
-        if (m3u_requested == 0 && xspf_requested == 0)
-        {
-            WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno));
-            client_send_404 (httpclient, "The file you requested could not be found");
-            free (fullpath);
-            return -1;
-        }
-        m3u_file_available = 0;
-        xspf_file_available = 0;
-    }
-
-    httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
-
-    if (m3u_requested && m3u_file_available == 0)
-    {
-        char *host = httpp_getvar (httpclient->parser, "host");
-        char *sourceuri = strdup (path);
-        char *dot = strrchr(sourceuri, '.');
-
-        /* at least a couple of players (fb2k/winamp) are reported to send a 
-         * host header but without the port number. So if we are missing the
-         * port then lets treat it as if no host line was sent */
-        if (host && strchr (host, ':') == NULL)
-            host = NULL;
-
-        *dot = 0;
-        httpclient->respcode = 200;
-        if (host == NULL)
-        {
-            config = config_get_config();
-            snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.0 200 OK\r\n"
-                    "Content-Type: audio/x-mpegurl\r\n\r\n"
-                    "http://%s:%d%s\r\n", 
-                    config->hostname, config->port,
-                    sourceuri
-                    );
-            config_release_config();
-        }
-        else
-        {
-            snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.0 200 OK\r\n"
-                    "Content-Type: audio/x-mpegurl\r\n\r\n"
-                    "http://%s%s\r\n", 
-                    host, 
-                    sourceuri
-                    );
-        }
-        httpclient->refbuf->len = strlen (httpclient->refbuf->data);
-        fserve_add_client (httpclient, NULL);
-        free (sourceuri);
-        free (fullpath);
-        return 0;
-    }
-    if (xspf_requested && xspf_file_available == 0)
-    {
-        xmlDocPtr doc;
-        char *reference = strdup (path);
-        char *eol = strrchr (reference, '.');
-        if (eol)
-            *eol = '\0';
-        stats_get_xml (&doc, 0, reference);
-        free (reference);
-        admin_send_response (doc, httpclient, TRANSFORMED, "xspf.xsl");
-        xmlFreeDoc(doc);
-        return 0;
-    }
-
-    /* on demand file serving check */
-    config = config_get_config();
-    if (config->fileserve == 0)
-    {
-        DEBUG1 ("on demand file \"%s\" refused", fullpath);
-        client_send_404 (httpclient, "The file you requested could not be found");
-        config_release_config();
-        free (fullpath);
-        return -1;
-    }
-    config_release_config();
-
-    if (S_ISREG (file_buf.st_mode) == 0)
-    {
-        client_send_404 (httpclient, "The file you requested could not be found");
-        WARN1 ("found requested file but there is no handler for it: %s", fullpath);
-        free (fullpath);
-        return -1;
-    }
-
-    file = fopen (fullpath, "rb");
-    if (file == NULL)
-    {
-        WARN1 ("Problem accessing file \"%s\"", fullpath);
-        client_send_404 (httpclient, "File not readable");
-        free (fullpath);
-        return -1;
-    }
-    free (fullpath);
-
-    content_length = (int64_t)file_buf.st_size;
-    range = httpp_getvar (httpclient->parser, "range");
-
-    if (range != NULL) {
-        ret = sscanf(range, "bytes=" FORMAT_INT64 "-", &rangenumber);
-        if (ret != 1) {
-            /* format not correct, so lets just assume
-               we start from the beginning */
-            rangeproblem = 1;
-        }
-        if (rangenumber < 0) {
-            rangeproblem = 1;
-        }
-        if (!rangeproblem) {
-            ret = fseek (file, rangenumber, SEEK_SET);
-            if (ret != -1) {
-                new_content_len = content_length - rangenumber;
-                if (new_content_len < 0) {
-                    rangeproblem = 1;
-                }
-            }
-            else {
-                rangeproblem = 1;
-            }
-            if (!rangeproblem) {
-                /* Date: is required on all HTTP1.1 responses */
-                char currenttime[50];
-                time_t now;
-                int strflen;
-                struct tm result;
-                int64_t endpos = rangenumber+new_content_len-1;
-                if (endpos < 0) {
-                    endpos = 0;
-                }
-                time(&now);
-                strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT",
-                                   gmtime_r(&now, &result));
-                httpclient->respcode = 206;
-                bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
-                    "HTTP/1.1 206 Partial Content\r\n"
-                    "Date: %s\r\n"
-                    "Content-Length: " FORMAT_INT64 "\r\n"
-                    "Content-Range: bytes " FORMAT_INT64 \
-                    "-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n"
-                    "Content-Type: %s\r\n\r\n",
-                    currenttime,
-                    new_content_len,
-                    rangenumber,
-                    endpos,
-                    content_length,
-                    fserve_content_type(path));
-            }
-            else {
-                goto fail;
-            }
-        }
-        else {
-            goto fail;
-        }
-    }
-    else {
-
-        httpclient->respcode = 200;
-        bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
-            "HTTP/1.0 200 OK\r\n"
-            "Content-Length: " FORMAT_INT64 "\r\n"
-            "Content-Type: %s\r\n\r\n",
-            content_length,
-            fserve_content_type(path));
-    }
-    httpclient->refbuf->len = bytes;
-    httpclient->pos = 0;
-
-    stats_event_inc (NULL, "file_connections");
-    fserve_add_client (httpclient, file);
-
-    return 0;
-
-fail:
-    fclose (file);
-    httpclient->respcode = 416;
-    sock_write (httpclient->con->sock, 
-            "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
-    client_destroy (httpclient);
-    return -1;
-}
-
-
-/* Add client to fserve thread, client needs to have refbuf set and filled
- * but may provide a NULL file if no data needs to be read
- */
-int fserve_add_client (client_t *client, FILE *file)
-{
-    fserve_t *fclient = calloc (1, sizeof(fserve_t));
-
-    DEBUG0 ("Adding client to file serving engine");
-    if (fclient == NULL)
-    {
-        client_send_404 (client, "memory exhausted");
-        return -1;
-    }
-    fclient->file = file;
-    fclient->client = client;
-    fclient->ready = 0;
-
-    thread_mutex_lock (&pending_lock);
-    fclient->next = (fserve_t *)pending_list;
-    pending_list = fclient;
-    thread_mutex_unlock (&pending_lock);
-
-    return 0;
-}
-
-
-/* add client to file serving engine, but just write out the buffer contents,
- * then pass the client to the callback with the provided arg
- */
-void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg)
-{
-    fserve_t *fclient = calloc (1, sizeof(fserve_t));
-
-    DEBUG0 ("Adding client to file serving engine");
-    if (fclient == NULL)
-    {
-        client_send_404 (client, "memory exhausted");
-        return;
-    }
-    fclient->file = NULL;
-    fclient->client = client;
-    fclient->ready = 0;
-    fclient->callback = callback;
-    fclient->arg = arg;
-
-    thread_mutex_lock (&pending_lock);
-    fclient->next = (fserve_t *)pending_list;
-    pending_list = fclient;
-    thread_mutex_unlock (&pending_lock);
-}
-
-
-static int _delete_mapping(void *mapping) {
-    mime_type *map = mapping;
-    free(map->ext);
-    free(map->type);
-    free(map);
-
-    return 1;
-}
-
-static int _compare_mappings(void *arg, void *a, void *b)
-{
-    return strcmp(
-            ((mime_type *)a)->ext,
-            ((mime_type *)b)->ext);
-}
-
-static void create_mime_mappings(const char *fn) {
-    FILE *mimefile = fopen(fn, "r");
-    char line[4096];
-    char *type, *ext, *cur;
-    mime_type *mapping;
-
-    mimetypes = avl_tree_new(_compare_mappings, NULL);
-
-    if (mimefile == NULL)
-    {
-        WARN1 ("Cannot open mime type file %s", fn);
-        return;
-    }
-
-    while(fgets(line, 4096, mimefile))
-    {
-        line[4095] = 0;
-
-        if(*line == 0 || *line == '#')
-            continue;
-
-        type = line;
-
-        cur = line;
-
-        while(*cur != ' ' && *cur != '\t' && *cur)
-            cur++;
-        if(*cur == 0)
-            continue;
-
-        *cur++ = 0;
-
-        while(1) {
-            while(*cur == ' ' || *cur == '\t')
-                cur++;
-            if(*cur == 0)
-                break;
-
-            ext = cur;
-            while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
-                cur++;
-            *cur++ = 0;
-            if(*ext)
-            {
-                void *tmp;
-                /* Add a new extension->type mapping */
-                mapping = malloc(sizeof(mime_type));
-                mapping->ext = strdup(ext);
-                mapping->type = strdup(type);
-                if(!avl_get_by_key(mimetypes, mapping, &tmp))
-                    avl_delete(mimetypes, mapping, _delete_mapping);
-                avl_insert(mimetypes, mapping);
-            }
-        }
-    }
-
-    fclose(mimefile);
-}
-

Copied: icecast/tags/icecast-2.3.2/src/fserve.c (from rev 14812, icecast/trunk/icecast/src/fserve.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/fserve.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/fserve.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,746 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#include <windows.h>
+#define fseeko fseek
+#define PRIdMAX "ld"
+#define SCNdMAX "ld"
+#define snprintf _snprintf
+#define strncasecmp _strnicmp
+#define S_ISREG(mode)  ((mode) & _S_IFREG)
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "format.h"
+#include "logging.h"
+#include "cfgfile.h"
+#include "util.h"
+#include "admin.h"
+#include "compat.h"
+
+#include "fserve.h"
+
+#undef CATMODULE
+#define CATMODULE "fserve"
+
+#define BUFSIZE 4096
+
+static fserve_t *active_list = NULL;
+static volatile fserve_t *pending_list = NULL;
+
+static mutex_t pending_lock;
+static avl_tree *mimetypes = NULL;
+
+static thread_type *fserv_thread;
+static int run_fserv = 0;
+static unsigned int fserve_clients;
+static int client_tree_changed=0;
+
+#ifdef HAVE_POLL
+static struct pollfd *ufds = NULL;
+#else
+static fd_set fds;
+static sock_t fd_max = SOCK_ERROR;
+#endif
+
+typedef struct {
+    char *ext;
+    char *type;
+} mime_type;
+
+static void fserve_client_destroy(fserve_t *fclient);
+static int _delete_mapping(void *mapping);
+static void *fserv_thread_function(void *arg);
+
+void fserve_initialize(void)
+{
+    ice_config_t *config = config_get_config();
+
+    mimetypes = NULL;
+    thread_mutex_create (&pending_lock);
+
+    fserve_recheck_mime_types (config);
+    config_release_config();
+
+    run_fserv = 1;
+    stats_event (NULL, "file_connections", "0");
+
+    fserv_thread = thread_create("File Serving Thread", 
+            fserv_thread_function, NULL, THREAD_ATTACHED);
+}
+
+void fserve_shutdown(void)
+{
+    if(!run_fserv)
+        return;
+
+    run_fserv = 0;
+    thread_join(fserv_thread);
+    INFO0("file serving thread stopped");
+    if (mimetypes)
+        avl_tree_free (mimetypes, _delete_mapping);
+}
+
+#ifdef HAVE_POLL
+int fserve_client_waiting (void)
+{
+    fserve_t *fclient;
+    unsigned int i = 0;
+
+    /* only rebuild ufds if there are clients added/removed */
+    if (client_tree_changed)
+    {
+        client_tree_changed = 0;
+        ufds = realloc(ufds, fserve_clients * sizeof(struct pollfd));
+        fclient = active_list;
+        while (fclient)
+        {
+            ufds[i].fd = fclient->client->con->sock;
+            ufds[i].events = POLLOUT;
+            ufds[i].revents = 0;
+            fclient = fclient->next;
+            i++;
+        }
+    }
+    if (!ufds)
+        thread_sleep(200000);
+    else if (poll(ufds, fserve_clients, 200) > 0)
+    {
+        /* mark any clients that are ready */
+        fclient = active_list;
+        for (i=0; i<fserve_clients; i++)
+        {
+            if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR))
+                fclient->ready = 1;
+            fclient = fclient->next;
+        }
+        return 1;
+    }
+    return 0;
+}
+#else
+int fserve_client_waiting (void)
+{
+    fserve_t *fclient;
+    fd_set realfds;
+
+    /* only rebuild fds if there are clients added/removed */
+    if(client_tree_changed) {
+        client_tree_changed = 0;
+        FD_ZERO(&fds);
+        fd_max = SOCK_ERROR;
+        fclient = active_list;
+        while (fclient) {
+            FD_SET (fclient->client->con->sock, &fds);
+            if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR)
+                fd_max = fclient->client->con->sock;
+            fclient = fclient->next;
+        }
+    }
+    /* hack for windows, select needs at least 1 descriptor */
+    if (fd_max == SOCK_ERROR)
+        thread_sleep (200000);
+    else
+    {
+        struct timeval tv;
+        tv.tv_sec = 0;
+        tv.tv_usec = 200000;
+        /* make a duplicate of the set so we do not have to rebuild it
+         * each time around */
+        memcpy(&realfds, &fds, sizeof(fd_set));
+        if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
+        {
+            /* mark any clients that are ready */
+            fclient = active_list;
+            while (fclient)
+            {
+                if (FD_ISSET (fclient->client->con->sock, &realfds))
+                    fclient->ready = 1;
+                fclient = fclient->next;
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+#endif
+
+static void wait_for_fds(void) {
+    fserve_t *fclient;
+
+    while (run_fserv)
+    {
+        /* add any new clients here */
+        if (pending_list)
+        {
+            thread_mutex_lock (&pending_lock);
+
+            fclient = (fserve_t*)pending_list;
+            while (fclient)
+            {
+                fserve_t *to_move = fclient;
+                fclient = fclient->next;
+                to_move->next = active_list;
+                active_list = to_move;
+                client_tree_changed = 1;
+                fserve_clients++;
+            }
+            pending_list = NULL;
+            thread_mutex_unlock (&pending_lock);
+        }
+        /* drop out of here if someone is ready */
+        if (fserve_client_waiting())
+            break;
+    }
+}
+
+static void *fserv_thread_function(void *arg)
+{
+    fserve_t *fclient, **trail;
+    size_t bytes;
+
+    INFO0("file serving thread started");
+    while (run_fserv) {
+        wait_for_fds();
+
+        fclient = active_list;
+        trail = &active_list;
+
+        while (fclient)
+        {
+            /* process this client, if it is ready */
+            if (fclient->ready)
+            {
+                client_t *client = fclient->client;
+                refbuf_t *refbuf = client->refbuf;
+                fclient->ready = 0;
+                if (client->pos == refbuf->len)
+                {
+                    /* Grab a new chunk */
+                    if (fclient->file)
+                        bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
+                    else
+                        bytes = 0;
+                    if (bytes == 0)
+                    {
+                        if (refbuf->next == NULL)
+                        {
+                            fserve_t *to_go = fclient;
+                            fclient = fclient->next;
+                            *trail = fclient;
+                            fserve_client_destroy (to_go);
+                            fserve_clients--;
+                            client_tree_changed = 1;
+                            continue;
+                        }
+                        client_set_queue (client, refbuf->next);
+                        refbuf = client->refbuf;
+                        bytes = refbuf->len;
+                    }
+                    refbuf->len = (unsigned int)bytes;
+                    client->pos = 0;
+                }
+
+                /* Now try and send current chunk. */
+                format_generic_write_to_client (client);
+
+                if (client->con->error)
+                {
+                    fserve_t *to_go = fclient;
+                    fclient = fclient->next;
+                    *trail = fclient;
+                    fserve_clients--;
+                    fserve_client_destroy (to_go);
+                    client_tree_changed = 1;
+                    continue;
+                }
+            }
+            trail = &fclient->next;
+            fclient = fclient->next;
+        }
+    }
+
+    /* Shutdown path */
+    thread_mutex_lock (&pending_lock);
+    while (pending_list)
+    {
+        fserve_t *to_go = (fserve_t *)pending_list;
+        pending_list = to_go->next;
+
+        fserve_client_destroy (to_go);
+    }
+    thread_mutex_unlock (&pending_lock);
+
+    while (active_list)
+    {
+        fserve_t *to_go = active_list;
+        active_list = to_go->next;
+        fserve_client_destroy (to_go);
+    }
+
+    return NULL;
+}
+
+/* string returned needs to be free'd */
+char *fserve_content_type (const char *path)
+{
+    char *ext = util_get_extension(path);
+    mime_type exttype = {ext, NULL};
+    void *result;
+    char *type;
+
+    thread_mutex_lock (&pending_lock);
+    if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
+    {
+        mime_type *mime = result;
+        type = strdup (mime->type);
+    }
+    else {
+        /* Fallbacks for a few basic ones */
+        if(!strcmp(ext, "ogg"))
+            type = strdup ("application/ogg");
+        else if(!strcmp(ext, "mp3"))
+            type = strdup ("audio/mpeg");
+        else if(!strcmp(ext, "html"))
+            type = strdup ("text/html");
+        else if(!strcmp(ext, "css"))
+            type = strdup ("text/css");
+        else if(!strcmp(ext, "txt"))
+            type = strdup ("text/plain");
+        else if(!strcmp(ext, "jpg"))
+            type = strdup ("image/jpeg");
+        else if(!strcmp(ext, "png"))
+            type = strdup ("image/png");
+        else if(!strcmp(ext, "m3u"))
+            type = strdup ("audio/x-mpegurl");
+        else if(!strcmp(ext, "aac"))
+            type = strdup ("audio/aac");
+        else
+            type = strdup ("application/octet-stream");
+    }
+    thread_mutex_unlock (&pending_lock);
+    return type;
+}
+
+static void fserve_client_destroy(fserve_t *fclient)
+{
+    if (fclient)
+    {
+        if (fclient->file)
+            fclose (fclient->file);
+
+        if (fclient->callback)
+            fclient->callback (fclient->client, fclient->arg);
+        else
+            if (fclient->client)
+                client_destroy (fclient->client);
+        free (fclient);
+    }
+}
+
+
+/* client has requested a file, so check for it and send the file.  Do not
+ * refer to the client_t afterwards.  return 0 for success, -1 on error.
+ */
+int fserve_client_create (client_t *httpclient, const char *path)
+{
+    int bytes;
+    struct stat file_buf;
+    const char *range = NULL;
+    off_t new_content_len = 0;
+    off_t rangenumber = 0, content_length;
+    int rangeproblem = 0;
+    int ret = 0;
+    char *fullpath;
+    int m3u_requested = 0, m3u_file_available = 1;
+    int xspf_requested = 0, xspf_file_available = 1;
+    ice_config_t *config;
+    FILE *file;
+
+    fullpath = util_get_path_from_normalised_uri (path);
+    INFO2 ("checking for file %s (%s)", path, fullpath);
+
+    if (strcmp (util_get_extension (fullpath), "m3u") == 0)
+        m3u_requested = 1;
+
+    if (strcmp (util_get_extension (fullpath), "xspf") == 0)
+        xspf_requested = 1;
+
+    /* check for the actual file */
+    if (stat (fullpath, &file_buf) != 0)
+    {
+        /* the m3u can be generated, but send an m3u file if available */
+        if (m3u_requested == 0 && xspf_requested == 0)
+        {
+            WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno));
+            client_send_404 (httpclient, "The file you requested could not be found");
+            free (fullpath);
+            return -1;
+        }
+        m3u_file_available = 0;
+        xspf_file_available = 0;
+    }
+
+    httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
+
+    if (m3u_requested && m3u_file_available == 0)
+    {
+        const char *host = httpp_getvar (httpclient->parser, "host");
+        char *sourceuri = strdup (path);
+        char *dot = strrchr(sourceuri, '.');
+
+        /* at least a couple of players (fb2k/winamp) are reported to send a 
+         * host header but without the port number. So if we are missing the
+         * port then lets treat it as if no host line was sent */
+        if (host && strchr (host, ':') == NULL)
+            host = NULL;
+
+        *dot = 0;
+        httpclient->respcode = 200;
+        if (host == NULL)
+        {
+            config = config_get_config();
+            snprintf (httpclient->refbuf->data, BUFSIZE,
+                    "HTTP/1.0 200 OK\r\n"
+                    "Content-Type: audio/x-mpegurl\r\n\r\n"
+                    "http://%s:%d%s\r\n", 
+                    config->hostname, config->port,
+                    sourceuri
+                    );
+            config_release_config();
+        }
+        else
+        {
+            snprintf (httpclient->refbuf->data, BUFSIZE,
+                    "HTTP/1.0 200 OK\r\n"
+                    "Content-Type: audio/x-mpegurl\r\n\r\n"
+                    "http://%s%s\r\n", 
+                    host, 
+                    sourceuri
+                    );
+        }
+        httpclient->refbuf->len = strlen (httpclient->refbuf->data);
+        fserve_add_client (httpclient, NULL);
+        free (sourceuri);
+        free (fullpath);
+        return 0;
+    }
+    if (xspf_requested && xspf_file_available == 0)
+    {
+        xmlDocPtr doc;
+        char *reference = strdup (path);
+        char *eol = strrchr (reference, '.');
+        if (eol)
+            *eol = '\0';
+        stats_get_xml (&doc, 0, reference);
+        free (reference);
+        admin_send_response (doc, httpclient, TRANSFORMED, "xspf.xsl");
+        xmlFreeDoc(doc);
+        return 0;
+    }
+
+    /* on demand file serving check */
+    config = config_get_config();
+    if (config->fileserve == 0)
+    {
+        DEBUG1 ("on demand file \"%s\" refused", fullpath);
+        client_send_404 (httpclient, "The file you requested could not be found");
+        config_release_config();
+        free (fullpath);
+        return -1;
+    }
+    config_release_config();
+
+    if (S_ISREG (file_buf.st_mode) == 0)
+    {
+        client_send_404 (httpclient, "The file you requested could not be found");
+        WARN1 ("found requested file but there is no handler for it: %s", fullpath);
+        free (fullpath);
+        return -1;
+    }
+
+    file = fopen (fullpath, "rb");
+    if (file == NULL)
+    {
+        WARN1 ("Problem accessing file \"%s\"", fullpath);
+        client_send_404 (httpclient, "File not readable");
+        free (fullpath);
+        return -1;
+    }
+    free (fullpath);
+
+    content_length = file_buf.st_size;
+    range = httpp_getvar (httpclient->parser, "range");
+
+    /* full http range handling is currently not done but we deal with the common case */
+    if (range != NULL) {
+        ret = 0;
+        if (strncasecmp (range, "bytes=", 6) == 0)
+            ret = sscanf (range+6, "%" SCNdMAX "-", &rangenumber);
+
+        if (ret != 1) {
+            /* format not correct, so lets just assume
+               we start from the beginning */
+            rangeproblem = 1;
+        }
+        if (rangenumber < 0) {
+            rangeproblem = 1;
+        }
+        if (!rangeproblem) {
+            ret = fseeko (file, rangenumber, SEEK_SET);
+            if (ret != -1) {
+                new_content_len = content_length - rangenumber;
+                if (new_content_len < 0) {
+                    rangeproblem = 1;
+                }
+            }
+            else {
+                rangeproblem = 1;
+            }
+            if (!rangeproblem) {
+                /* Date: is required on all HTTP1.1 responses */
+                char currenttime[50];
+                time_t now;
+                int strflen;
+                struct tm result;
+                off_t endpos = rangenumber+new_content_len-1;
+                char *type;
+
+                if (endpos < 0) {
+                    endpos = 0;
+                }
+                time(&now);
+                strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT",
+                                   gmtime_r(&now, &result));
+                httpclient->respcode = 206;
+                type = fserve_content_type (path);
+                bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
+                    "HTTP/1.1 206 Partial Content\r\n"
+                    "Date: %s\r\n"
+                    "Accept-Ranges: bytes\r\n"
+                    "Content-Length: %" PRIdMAX "\r\n"
+                    "Content-Range: bytes %" PRIdMAX \
+                    "-%" PRIdMAX "/%" PRIdMAX "\r\n"
+                    "Content-Type: %s\r\n\r\n",
+                    currenttime,
+                    new_content_len,
+                    rangenumber,
+                    endpos,
+                    content_length,
+                    type);
+                free (type);
+            }
+            else {
+                goto fail;
+            }
+        }
+        else {
+            goto fail;
+        }
+    }
+    else {
+        char *type = fserve_content_type(path);
+        httpclient->respcode = 200;
+        bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
+            "HTTP/1.0 200 OK\r\n"
+            "Accept-Ranges: bytes\r\n"
+            "Content-Length: %" PRIdMAX "\r\n"
+            "Content-Type: %s\r\n\r\n",
+            content_length,
+            type);
+        free (type);
+    }
+    httpclient->refbuf->len = bytes;
+    httpclient->pos = 0;
+
+    stats_event_inc (NULL, "file_connections");
+    fserve_add_client (httpclient, file);
+
+    return 0;
+
+fail:
+    fclose (file);
+    httpclient->respcode = 416;
+    sock_write (httpclient->con->sock, 
+            "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
+    client_destroy (httpclient);
+    return -1;
+}
+
+
+/* Add client to fserve thread, client needs to have refbuf set and filled
+ * but may provide a NULL file if no data needs to be read
+ */
+int fserve_add_client (client_t *client, FILE *file)
+{
+    fserve_t *fclient = calloc (1, sizeof(fserve_t));
+
+    DEBUG0 ("Adding client to file serving engine");
+    if (fclient == NULL)
+    {
+        client_send_404 (client, "memory exhausted");
+        return -1;
+    }
+    fclient->file = file;
+    fclient->client = client;
+    fclient->ready = 0;
+
+    thread_mutex_lock (&pending_lock);
+    fclient->next = (fserve_t *)pending_list;
+    pending_list = fclient;
+    thread_mutex_unlock (&pending_lock);
+
+    return 0;
+}
+
+
+/* add client to file serving engine, but just write out the buffer contents,
+ * then pass the client to the callback with the provided arg
+ */
+void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg)
+{
+    fserve_t *fclient = calloc (1, sizeof(fserve_t));
+
+    DEBUG0 ("Adding client to file serving engine");
+    if (fclient == NULL)
+    {
+        client_send_404 (client, "memory exhausted");
+        return;
+    }
+    fclient->file = NULL;
+    fclient->client = client;
+    fclient->ready = 0;
+    fclient->callback = callback;
+    fclient->arg = arg;
+
+    thread_mutex_lock (&pending_lock);
+    fclient->next = (fserve_t *)pending_list;
+    pending_list = fclient;
+    thread_mutex_unlock (&pending_lock);
+}
+
+
+static int _delete_mapping(void *mapping) {
+    mime_type *map = mapping;
+    free(map->ext);
+    free(map->type);
+    free(map);
+
+    return 1;
+}
+
+static int _compare_mappings(void *arg, void *a, void *b)
+{
+    return strcmp(
+            ((mime_type *)a)->ext,
+            ((mime_type *)b)->ext);
+}
+
+void fserve_recheck_mime_types (ice_config_t *config)
+{
+    FILE *mimefile;
+    char line[4096];
+    char *type, *ext, *cur;
+    mime_type *mapping;
+    avl_tree *new_mimetypes;
+
+    if (config->mimetypes_fn == NULL)
+        return;
+    mimefile = fopen (config->mimetypes_fn, "r");
+    if (mimefile == NULL)
+    {
+        WARN1 ("Cannot open mime types file %s", config->mimetypes_fn);
+        return;
+    }
+
+    new_mimetypes = avl_tree_new(_compare_mappings, NULL);
+
+    while(fgets(line, 4096, mimefile))
+    {
+        line[4095] = 0;
+
+        if(*line == 0 || *line == '#')
+            continue;
+
+        type = line;
+
+        cur = line;
+
+        while(*cur != ' ' && *cur != '\t' && *cur)
+            cur++;
+        if(*cur == 0)
+            continue;
+
+        *cur++ = 0;
+
+        while(1) {
+            while(*cur == ' ' || *cur == '\t')
+                cur++;
+            if(*cur == 0)
+                break;
+
+            ext = cur;
+            while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
+                cur++;
+            *cur++ = 0;
+            if(*ext)
+            {
+                void *tmp;
+                /* Add a new extension->type mapping */
+                mapping = malloc(sizeof(mime_type));
+                mapping->ext = strdup(ext);
+                mapping->type = strdup(type);
+                if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
+                    avl_delete (new_mimetypes, mapping, _delete_mapping);
+                avl_insert (new_mimetypes, mapping);
+            }
+        }
+    }
+    fclose(mimefile);
+
+    thread_mutex_lock (&pending_lock);
+    if (mimetypes)
+        avl_tree_free (mimetypes, _delete_mapping);
+    mimetypes = new_mimetypes;
+    thread_mutex_unlock (&pending_lock);
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/fserve.h
===================================================================
--- icecast/trunk/icecast/src/fserve.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/fserve.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,41 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __FSERVE_H__
-#define __FSERVE_H__
-
-#include <stdio.h>
-
-typedef void (*fserve_callback_t)(client_t *, void *);
-
-typedef struct _fserve_t
-{
-    client_t *client;
-
-    FILE *file;
-    int ready;
-    void (*callback)(client_t *, void *);
-    void *arg;
-    struct _fserve_t *next;
-} fserve_t;
-
-void fserve_initialize(void);
-void fserve_shutdown(void);
-int fserve_client_create(client_t *httpclient, const char *path);
-int fserve_add_client (client_t *client, FILE *file);
-void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg);
-char *fserve_content_type (const char *path);
-
-
-#endif
-
-

Copied: icecast/tags/icecast-2.3.2/src/fserve.h (from rev 13541, icecast/trunk/icecast/src/fserve.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/fserve.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/fserve.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,43 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __FSERVE_H__
+#define __FSERVE_H__
+
+#include <stdio.h>
+#include "cfgfile.h"
+
+typedef void (*fserve_callback_t)(client_t *, void *);
+
+typedef struct _fserve_t
+{
+    client_t *client;
+
+    FILE *file;
+    int ready;
+    void (*callback)(client_t *, void *);
+    void *arg;
+    struct _fserve_t *next;
+} fserve_t;
+
+void fserve_initialize(void);
+void fserve_shutdown(void);
+int fserve_client_create(client_t *httpclient, const char *path);
+int fserve_add_client (client_t *client, FILE *file);
+void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg);
+char *fserve_content_type (const char *path);
+void fserve_recheck_mime_types (ice_config_t *config);
+
+
+#endif
+
+

Deleted: icecast/tags/icecast-2.3.2/src/global.c
===================================================================
--- icecast/trunk/icecast/src/global.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/global.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,63 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "httpp/httpp.h"
-
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "source.h"
-#include "format.h"
-
-#include "global.h"
-
-ice_global_t global;
-
-static mutex_t _global_mutex;
-
-void global_initialize(void)
-{
-    memset(global.serversock, 0, sizeof(int)*MAX_LISTEN_SOCKETS);
-    global.server_sockets = 0;
-    global.relays = NULL;
-    global.master_relays = NULL;
-    global.running = 0;
-    global.clients = 0;
-    global.sources = 0;
-    global.source_tree = avl_tree_new(source_compare_sources, NULL);
-    thread_mutex_create(&_global_mutex);
-}
-
-void global_shutdown(void)
-{
-    thread_mutex_destroy(&_global_mutex);
-    avl_tree_free(global.source_tree, NULL);
-}
-
-void global_lock(void)
-{
-    thread_mutex_lock(&_global_mutex);
-}
-
-void global_unlock(void)
-{
-    thread_mutex_unlock(&_global_mutex);
-}

Copied: icecast/tags/icecast-2.3.2/src/global.c (from rev 13995, icecast/trunk/icecast/src/global.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/global.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/global.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,62 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "source.h"
+#include "format.h"
+
+#include "global.h"
+
+ice_global_t global;
+
+static mutex_t _global_mutex;
+
+void global_initialize(void)
+{
+    global.server_sockets = 0;
+    global.relays = NULL;
+    global.master_relays = NULL;
+    global.running = 0;
+    global.clients = 0;
+    global.sources = 0;
+    global.source_tree = avl_tree_new(source_compare_sources, NULL);
+    thread_mutex_create(&_global_mutex);
+}
+
+void global_shutdown(void)
+{
+    thread_mutex_destroy(&_global_mutex);
+    avl_tree_free(global.source_tree, NULL);
+}
+
+void global_lock(void)
+{
+    thread_mutex_lock(&_global_mutex);
+}
+
+void global_unlock(void)
+{
+    thread_mutex_unlock(&_global_mutex);
+}

Deleted: icecast/tags/icecast-2.3.2/src/global.h
===================================================================
--- icecast/trunk/icecast/src/global.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/global.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,55 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __GLOBAL_H__
-#define __GLOBAL_H__
-
-#define ICE_LISTEN_QUEUE 5
-
-#define ICE_RUNNING 1
-#define ICE_HALTING 2
-
-#define ICECAST_VERSION_STRING "Icecast " PACKAGE_VERSION
-
-#define MAX_LISTEN_SOCKETS 20
-
-#include "thread/thread.h"
-#include "slave.h"
-
-typedef struct ice_global_tag
-{
-    int serversock[MAX_LISTEN_SOCKETS];
-    int server_sockets;
-
-    int running;
-
-    int sources;
-    int clients;
-    int schedule_config_reread;
-
-    avl_tree *source_tree;
-    /* for locally defined relays */
-    struct _relay_server *relays;
-    /* relays retrieved from master */
-    struct _relay_server *master_relays;
-
-    cond_t shutdown_cond;
-} ice_global_t;
-
-extern ice_global_t global;
-
-void global_initialize(void);
-void global_shutdown(void);
-void global_lock(void);
-void global_unlock(void);
-
-#endif  /* __GLOBAL_H__ */

Copied: icecast/tags/icecast-2.3.2/src/global.h (from rev 14043, icecast/trunk/icecast/src/global.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/global.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/global.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,54 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __GLOBAL_H__
+#define __GLOBAL_H__
+
+#define ICE_LISTEN_QUEUE 5
+
+#define ICE_RUNNING 1
+#define ICE_HALTING 2
+
+#define ICECAST_VERSION_STRING "Icecast " PACKAGE_VERSION
+
+#include "thread/thread.h"
+#include "slave.h"
+#include "net/sock.h"
+
+typedef struct ice_global_tag
+{
+    sock_t *serversock;
+    int server_sockets;
+
+    int running;
+
+    int sources;
+    int clients;
+    int schedule_config_reread;
+
+    avl_tree *source_tree;
+    /* for locally defined relays */
+    struct _relay_server *relays;
+    /* relays retrieved from master */
+    struct _relay_server *master_relays;
+
+    cond_t shutdown_cond;
+} ice_global_t;
+
+extern ice_global_t global;
+
+void global_initialize(void);
+void global_shutdown(void);
+void global_lock(void);
+void global_unlock(void);
+
+#endif  /* __GLOBAL_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/logging.c
===================================================================
--- icecast/trunk/icecast/src/logging.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/logging.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,244 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <time.h>
-#include <string.h>
-
-#include "thread/thread.h"
-#include "httpp/httpp.h"
-
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-
-#include "compat.h"
-#include "cfgfile.h"
-#include "logging.h"
-#include "util.h"
-
-#ifdef _WIN32
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
-#endif
-
-/* the global log descriptors */
-int errorlog = 0;
-int accesslog = 0;
-int playlistlog = 0;
-
-#ifdef _WIN32
-/* Since strftime's %z option on win32 is different, we need
-   to go through a few loops to get the same info as %z */
-int get_clf_time (char *buffer, unsigned len, struct tm *t)
-{
-    char    sign;
-    char    *timezone_string;
-    struct tm gmt;
-    time_t time1 = time(NULL);
-    int time_days, time_hours, time_tz;
-    int tempnum1, tempnum2;
-    struct tm *thetime;
-    time_t now;
-
-    gmtime_r(&time1, &gmt);
-
-    time_days = t->tm_yday - gmt.tm_yday;
-
-    if (time_days < -1) {
-        tempnum1 = 24;
-    }
-    else {
-        tempnum1 = 1;
-    }
-    if (tempnum1 < time_days) {
-       tempnum2 = -24;
-    }
-    else {
-        tempnum2 = time_days*24;
-    }
-
-    time_hours = (tempnum2 + t->tm_hour - gmt.tm_hour);
-    time_tz = time_hours * 60 + t->tm_min - gmt.tm_min;
-
-    if (time_tz < 0) {
-        sign = '-';
-        time_tz = -time_tz;
-    }
-    else {
-        sign = '+';
-    }
-
-    timezone_string = calloc(1, 7);
-    snprintf(timezone_string, 7, " %c%.2d%.2d", sign, time_tz / 60, time_tz % 60);
-
-    now = time(NULL);
-
-    thetime = localtime(&now);
-    strftime (buffer, len-7, "%d/%b/%Y:%H:%M:%S", thetime);
-    strcat(buffer, timezone_string);
-	free(timezone_string);
-    return 1;
-}
-#endif
-/* 
-** ADDR IDENT USER DATE REQUEST CODE BYTES REFERER AGENT [TIME]
-**
-** ADDR = client->con->ip
-** IDENT = always - , we don't support it because it's useless
-** USER = client->username    
-** DATE = _make_date(client->con->con_time)
-** REQUEST = build from client->parser
-** CODE = client->respcode
-** BYTES = client->con->sent_bytes
-** REFERER = get from client->parser
-** AGENT = get from client->parser
-** TIME = timing_get_time() - client->con->con_time
-*/
-void logging_access(client_t *client)
-{
-    char datebuf[128];
-    char reqbuf[1024];
-    struct tm thetime;
-    time_t now;
-    time_t stayed;
-    char *referrer, *user_agent, *username;
-
-    now = time(NULL);
-
-    localtime_r (&now, &thetime);
-    /* build the data */
-#ifdef _WIN32
-    memset(datebuf, '\000', sizeof(datebuf));
-    get_clf_time(datebuf, sizeof(datebuf)-1, &thetime);
-#else
-    strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
-#endif
-    /* build the request */
-    snprintf (reqbuf, sizeof(reqbuf), "%s %s %s/%s",
-            httpp_getvar (client->parser, HTTPP_VAR_REQ_TYPE),
-            httpp_getvar (client->parser, HTTPP_VAR_URI),
-            httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL),
-            httpp_getvar (client->parser, HTTPP_VAR_VERSION));
-
-    stayed = now - client->con->con_time;
-
-    if (client->username == NULL)
-        username = "-"; 
-    else
-        username = client->username;
-
-    referrer = httpp_getvar (client->parser, "referer");
-    if (referrer == NULL)
-        referrer = "-";
-
-    user_agent = httpp_getvar (client->parser, "user-agent");
-    if (user_agent == NULL)
-        user_agent = "-";
-
-    log_write_direct (accesslog,
-            "%s - %s [%s] \"%s\" %d " FORMAT_UINT64 " \"%s\" \"%s\" %lu",
-            client->con->ip,
-            username,
-            datebuf,
-            reqbuf,
-            client->respcode,
-            client->con->sent_bytes,
-            referrer,
-            user_agent,
-            (unsigned long)stayed);
-}
-/* This function will provide a log of metadata for each
-   mountpoint.  The metadata *must* be in UTF-8, and thus
-   you can assume that the log itself is UTF-8 encoded */
-void logging_playlist(const char *mount, const char *metadata, long listeners)
-{
-    char datebuf[128];
-    struct tm thetime;
-    time_t now;
-
-    if (playlistlog == -1) {
-        return;
-    }
-
-    now = time(NULL);
-
-    localtime_r (&now, &thetime);
-    /* build the data */
-#ifdef _WIN32
-    memset(datebuf, '\000', sizeof(datebuf));
-    get_clf_time(datebuf, sizeof(datebuf)-1, &thetime);
-#else
-    strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
-#endif
-    /* This format MAY CHANGE OVER TIME.  We are looking into finding a good
-       standard format for this, if you have any ideas, please let us know */
-    log_write_direct (playlistlog, "%s|%s|%d|%s",
-             datebuf,
-             mount,
-             listeners,
-             metadata);
-}
-
-
-void log_parse_failure (void *ctx, const char *fmt, ...)
-{
-    char line [200];
-    va_list ap;
-    char *eol;
-
-    va_start (ap, fmt);
-    vsnprintf (line, sizeof (line), fmt, ap);
-    eol = strrchr (line, '\n');
-    if (eol) *eol='\0';
-    va_end (ap);
-    log_write (errorlog, 2, (char*)ctx, "", "%s", line);
-}
-
-
-void restart_logging (ice_config_t *config)
-{
-    if (strcmp (config->error_log, "-"))
-    {
-        char fn_error[FILENAME_MAX];
-        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
-        log_set_filename (errorlog, fn_error);
-        log_set_level (errorlog, config->loglevel);
-        log_set_trigger (errorlog, config->logsize);
-        log_set_archive_timestamp(errorlog, config->logarchive);
-        log_reopen (errorlog);
-    }
-
-    if (strcmp (config->access_log, "-"))
-    {
-        char fn_error[FILENAME_MAX];
-        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
-        log_set_filename (accesslog, fn_error);
-        log_set_trigger (errorlog, config->logsize);
-        log_set_archive_timestamp(errorlog, config->logarchive);
-        log_reopen (accesslog);
-    }
-
-    if (config->playlist_log)
-    {
-        char fn_error[FILENAME_MAX];
-        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
-        log_set_filename (playlistlog, fn_error);
-        log_set_trigger (errorlog, config->logsize);
-        log_set_archive_timestamp(errorlog, config->logarchive);
-        log_reopen (playlistlog);
-    }
-}

Copied: icecast/tags/icecast-2.3.2/src/logging.c (from rev 14223, icecast/trunk/icecast/src/logging.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/logging.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/logging.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,244 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include "thread/thread.h"
+#include "httpp/httpp.h"
+
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+
+#include "compat.h"
+#include "cfgfile.h"
+#include "logging.h"
+#include "util.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+/* the global log descriptors */
+int errorlog = 0;
+int accesslog = 0;
+int playlistlog = 0;
+
+#ifdef _WIN32
+/* Since strftime's %z option on win32 is different, we need
+   to go through a few loops to get the same info as %z */
+int get_clf_time (char *buffer, unsigned len, struct tm *t)
+{
+    char    sign;
+    char    *timezone_string;
+    struct tm gmt;
+    time_t time1 = time(NULL);
+    int time_days, time_hours, time_tz;
+    int tempnum1, tempnum2;
+    struct tm *thetime;
+    time_t now;
+
+    gmtime_r(&time1, &gmt);
+
+    time_days = t->tm_yday - gmt.tm_yday;
+
+    if (time_days < -1) {
+        tempnum1 = 24;
+    }
+    else {
+        tempnum1 = 1;
+    }
+    if (tempnum1 < time_days) {
+       tempnum2 = -24;
+    }
+    else {
+        tempnum2 = time_days*24;
+    }
+
+    time_hours = (tempnum2 + t->tm_hour - gmt.tm_hour);
+    time_tz = time_hours * 60 + t->tm_min - gmt.tm_min;
+
+    if (time_tz < 0) {
+        sign = '-';
+        time_tz = -time_tz;
+    }
+    else {
+        sign = '+';
+    }
+
+    timezone_string = calloc(1, 7);
+    snprintf(timezone_string, 7, " %c%.2d%.2d", sign, time_tz / 60, time_tz % 60);
+
+    now = time(NULL);
+
+    thetime = localtime(&now);
+    strftime (buffer, len-7, "%d/%b/%Y:%H:%M:%S", thetime);
+    strcat(buffer, timezone_string);
+	free(timezone_string);
+    return 1;
+}
+#endif
+/* 
+** ADDR IDENT USER DATE REQUEST CODE BYTES REFERER AGENT [TIME]
+**
+** ADDR = client->con->ip
+** IDENT = always - , we don't support it because it's useless
+** USER = client->username
+** DATE = _make_date(client->con->con_time)
+** REQUEST = build from client->parser
+** CODE = client->respcode
+** BYTES = client->con->sent_bytes
+** REFERER = get from client->parser
+** AGENT = get from client->parser
+** TIME = timing_get_time() - client->con->con_time
+*/
+void logging_access(client_t *client)
+{
+    char datebuf[128];
+    char reqbuf[1024];
+    struct tm thetime;
+    time_t now;
+    time_t stayed;
+    const char *referrer, *user_agent, *username;
+
+    now = time(NULL);
+
+    localtime_r (&now, &thetime);
+    /* build the data */
+#ifdef _WIN32
+    memset(datebuf, '\000', sizeof(datebuf));
+    get_clf_time(datebuf, sizeof(datebuf)-1, &thetime);
+#else
+    strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
+#endif
+    /* build the request */
+    snprintf (reqbuf, sizeof(reqbuf), "%s %s %s/%s",
+            httpp_getvar (client->parser, HTTPP_VAR_REQ_TYPE),
+            httpp_getvar (client->parser, HTTPP_VAR_URI),
+            httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL),
+            httpp_getvar (client->parser, HTTPP_VAR_VERSION));
+
+    stayed = now - client->con->con_time;
+
+    if (client->username == NULL)
+        username = "-"; 
+    else
+        username = client->username;
+
+    referrer = httpp_getvar (client->parser, "referer");
+    if (referrer == NULL)
+        referrer = "-";
+
+    user_agent = httpp_getvar (client->parser, "user-agent");
+    if (user_agent == NULL)
+        user_agent = "-";
+
+    log_write_direct (accesslog,
+            "%s - %s [%s] \"%s\" %d %" PRIu64 " \"%s\" \"%s\" %lu",
+            client->con->ip,
+            username,
+            datebuf,
+            reqbuf,
+            client->respcode,
+            client->con->sent_bytes,
+            referrer,
+            user_agent,
+            (unsigned long)stayed);
+}
+/* This function will provide a log of metadata for each
+   mountpoint.  The metadata *must* be in UTF-8, and thus
+   you can assume that the log itself is UTF-8 encoded */
+void logging_playlist(const char *mount, const char *metadata, long listeners)
+{
+    char datebuf[128];
+    struct tm thetime;
+    time_t now;
+
+    if (playlistlog == -1) {
+        return;
+    }
+
+    now = time(NULL);
+
+    localtime_r (&now, &thetime);
+    /* build the data */
+#ifdef _WIN32
+    memset(datebuf, '\000', sizeof(datebuf));
+    get_clf_time(datebuf, sizeof(datebuf)-1, &thetime);
+#else
+    strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
+#endif
+    /* This format MAY CHANGE OVER TIME.  We are looking into finding a good
+       standard format for this, if you have any ideas, please let us know */
+    log_write_direct (playlistlog, "%s|%s|%ld|%s",
+             datebuf,
+             mount,
+             listeners,
+             metadata);
+}
+
+
+void log_parse_failure (void *ctx, const char *fmt, ...)
+{
+    char line [200];
+    va_list ap;
+    char *eol;
+
+    va_start (ap, fmt);
+    vsnprintf (line, sizeof (line), fmt, ap);
+    eol = strrchr (line, '\n');
+    if (eol) *eol='\0';
+    va_end (ap);
+    log_write (errorlog, 2, (char*)ctx, "", "%s", line);
+}
+
+
+void restart_logging (ice_config_t *config)
+{
+    if (strcmp (config->error_log, "-"))
+    {
+        char fn_error[FILENAME_MAX];
+        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
+        log_set_filename (errorlog, fn_error);
+        log_set_level (errorlog, config->loglevel);
+        log_set_trigger (errorlog, config->logsize);
+        log_set_archive_timestamp(errorlog, config->logarchive);
+        log_reopen (errorlog);
+    }
+
+    if (strcmp (config->access_log, "-"))
+    {
+        char fn_error[FILENAME_MAX];
+        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
+        log_set_filename (accesslog, fn_error);
+        log_set_trigger (accesslog, config->logsize);
+        log_set_archive_timestamp (accesslog, config->logarchive);
+        log_reopen (accesslog);
+    }
+
+    if (config->playlist_log)
+    {
+        char fn_error[FILENAME_MAX];
+        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
+        log_set_filename (playlistlog, fn_error);
+        log_set_trigger (playlistlog, config->logsize);
+        log_set_archive_timestamp (playlistlog, config->logarchive);
+        log_reopen (playlistlog);
+    }
+}

Deleted: icecast/tags/icecast-2.3.2/src/main.c
===================================================================
--- icecast/trunk/icecast/src/main.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/main.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,550 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "net/sock.h"
-#include "net/resolver.h"
-#include "httpp/httpp.h"
-
-#ifdef CHUID
-#include <sys/types.h>
-#include <grp.h>
-#include <pwd.h>
-#include <errno.h>
-#endif
-
-#include "cfgfile.h"
-#include "sighandler.h"
-
-#include "global.h"
-#include "compat.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "slave.h"
-#include "stats.h"
-#include "logging.h"
-#include "xslt.h"
-#include "fserve.h"
-#include "yp.h"
-#include "auth.h"
-
-#include <libxml/xmlmemory.h>
-
-#ifdef _WIN32
-/* For getpid() */
-#include <process.h>
-#define snprintf _snprintf
-#define getpid _getpid
-#endif
-
-#undef CATMODULE
-#define CATMODULE "main"
-
-static int background;
-static char *pidfile = NULL;
-
-static void _fatal_error(char *perr)
-{
-#ifdef WIN32
-    MessageBox(NULL, perr, "Error", MB_OK);
-#else
-    fprintf(stdout, "%s\n", perr);
-#endif
-}
-
-static void _print_usage(void)
-{
-    printf("%s\n\n", ICECAST_VERSION_STRING);
-    printf("usage: icecast [-b -v] -c <file>\n");
-    printf("options:\n");
-    printf("\t-c <file>\tSpecify configuration file\n");
-    printf("\t-v\t\tDisplay version info\n");
-    printf("\t-b\t\tRun icecast in the background\n");
-    printf("\n");
-}
-
-static void _stop_logging(void)
-{
-    log_close(errorlog);
-    log_close(accesslog);
-    log_close(playlistlog);
-}
-
-static void _initialize_subsystems(void)
-{
-    log_initialize();
-    thread_initialize();
-    sock_initialize();
-    resolver_initialize();
-    config_initialize();
-    connection_initialize();
-    global_initialize();
-    refbuf_initialize();
-    xslt_initialize();
-}
-
-static void _shutdown_subsystems(void)
-{
-    fserve_shutdown();
-    xslt_shutdown();
-    refbuf_shutdown();
-    slave_shutdown();
-    auth_shutdown();
-    yp_shutdown();
-    stats_shutdown();
-
-    global_shutdown();
-    connection_shutdown();
-    config_shutdown();
-    resolver_shutdown();
-    sock_shutdown();
-    thread_shutdown();
-
-    /* Now that these are done, we can stop the loggers. */
-    _stop_logging();
-    log_shutdown();
-
-    xmlCleanupParser();
-}
-
-static int _parse_config_opts(int argc, char **argv, char *filename, int size)
-{
-    int i = 1;
-    int config_ok = 0;
-
-    background = 0;
-    if (argc < 2) return -1;
-
-    while (i < argc) {
-        if (strcmp(argv[i], "-b") == 0) {
-#ifndef WIN32
-            pid_t pid;
-            fprintf(stdout, "Starting icecast2\nDetaching from the console\n");
-
-            pid = fork();
-
-            if (pid > 0) {
-                /* exit the parent */
-                exit(0);
-            }
-            else if(pid < 0) {
-                fprintf(stderr, "FATAL: Unable to fork child!");
-                exit(1);
-            }
-            background = 1;
-#endif
-        }
-        if (strcmp(argv[i], "-v") == 0) {
-            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
-            exit(0);
-        }
-
-        if (strcmp(argv[i], "-c") == 0) {
-            if (i + 1 < argc) {
-                strncpy(filename, argv[i + 1], size-1);
-                filename[size-1] = 0;
-                config_ok = 1;
-            } else {
-                return -1;
-            }
-        }
-        i++;
-    }
-
-    if(config_ok)
-        return 1;
-    else
-        return -1;
-}
-
-static int _start_logging(void)
-{
-    char fn_error[FILENAME_MAX];
-    char fn_access[FILENAME_MAX];
-    char fn_playlist[FILENAME_MAX];
-    char buf[1024];
-    int log_to_stderr;
-
-    ice_config_t *config = config_get_config_unlocked();
-
-    if(strcmp(config->error_log, "-")) {
-        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
-        errorlog = log_open(fn_error);
-        log_to_stderr = 0;
-        if (config->logsize)
-            log_set_trigger (errorlog, config->logsize);
-        log_set_archive_timestamp(errorlog, config->logarchive);
-    } else {
-        errorlog = log_open_file(stderr);
-        log_to_stderr = 1;
-    }
-
-    if (errorlog < 0) {
-        buf[sizeof(buf)-1] = 0;
-        snprintf(buf, sizeof(buf)-1, 
-                "FATAL: could not open error logging (%s): %s",
-                log_to_stderr?"standard error":fn_error,
-                strerror(errno));
-        _fatal_error(buf);
-    }
-    log_set_level(errorlog, config->loglevel);
-
-    if(strcmp(config->access_log, "-")) {
-        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
-        accesslog = log_open(fn_access);
-        log_to_stderr = 0;
-        if (config->logsize)
-            log_set_trigger (accesslog, config->logsize);
-        log_set_archive_timestamp(accesslog, config->logarchive);
-    } else {
-        accesslog = log_open_file(stderr);
-        log_to_stderr = 1;
-    }
-
-    if (accesslog < 0) {
-        buf[sizeof(buf)-1] = 0;
-        snprintf(buf, sizeof(buf)-1, 
-                "FATAL: could not open access logging (%s): %s",
-                log_to_stderr?"standard error":fn_access,
-                strerror(errno));
-        _fatal_error(buf);
-    }
-
-    if(config->playlist_log) {
-        snprintf(fn_playlist, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
-        playlistlog = log_open(fn_playlist);
-        if (playlistlog < 0) {
-            buf[sizeof(buf)-1] = 0;
-            snprintf(buf, sizeof(buf)-1, 
-                "FATAL: could not open playlist logging (%s): %s",
-                log_to_stderr?"standard error":fn_playlist,
-                strerror(errno));
-            _fatal_error(buf);
-        }
-        log_to_stderr = 0;
-        if (config->logsize)
-            log_set_trigger (playlistlog, config->logsize);
-        log_set_archive_timestamp(playlistlog, config->logarchive);
-    } else {
-        playlistlog = -1;
-    }
-
-    log_set_level(errorlog, config->loglevel);
-    log_set_level(accesslog, 4);
-    log_set_level(playlistlog, 4);
-
-    if (errorlog >= 0 && accesslog >= 0) return 1;
-    
-    return 0;
-}
-
-static int _setup_sockets(void)
-{
-    ice_config_t *config;
-    int i = 0;
-    int ret = 0;
-    int successful = 0;
-    char pbuf[1024];
-
-    config = config_get_config_unlocked();
-
-    for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
-        if(config->listeners[i].port <= 0)
-            break;
-
-        global.serversock[i] = sock_get_server_socket(
-                config->listeners[i].port, config->listeners[i].bind_address);
-
-        if (global.serversock[i] == SOCK_ERROR) {
-            memset(pbuf, '\000', sizeof(pbuf));
-            snprintf(pbuf, sizeof(pbuf)-1, 
-                "Could not create listener socket on port %d", 
-                config->listeners[i].port);
-            _fatal_error(pbuf);
-            return 0;
-        }
-        else {
-            ret = 1;
-            successful++;
-        }
-    }
-
-    global.server_sockets = successful;
-    
-    return ret;
-}
-
-static int _start_listening(void)
-{
-    int i;
-    for(i=0; i < global.server_sockets; i++) {
-        if (sock_listen(global.serversock[i], ICE_LISTEN_QUEUE) == SOCK_ERROR)
-            return 0;
-
-        sock_set_blocking(global.serversock[i], SOCK_NONBLOCK);
-    }
-
-    return 1;
-}
-
-/* bind the socket and start listening */
-static int _server_proc_init(void)
-{
-    ice_config_t *config;
-
-    if (!_setup_sockets())
-        return 0;
-
-    if (!_start_listening()) {
-        _fatal_error("Failed trying to listen on server socket");
-        return 0;
-    }
-
-    config = config_get_config_unlocked();
-    /* recreate the pid file */
-    if (config->pidfile)
-    {
-        FILE *f;
-        pidfile = strdup (config->pidfile);
-        if (pidfile && (f = fopen (config->pidfile, "w")) != NULL)
-        {
-            fprintf (f, "%d\n", (int)getpid());
-            fclose (f);
-        }
-    }
-
-    return 1;
-}
-
-/* this is the heart of the beast */
-static void _server_proc(void)
-{
-    int i;
-
-    if (background)
-    {
-        fclose (stdin);
-        fclose (stdout);
-        fclose (stderr);
-    }
-    connection_accept_loop();
-
-    for(i=0; i < MAX_LISTEN_SOCKETS; i++)
-        sock_close(global.serversock[i]);
-}
-
-/* chroot the process. Watch out - we need to do this before starting other
- * threads. Change uid as well, after figuring out uid _first_ */
-
-static void _ch_root_uid_setup(void)
-{
-   ice_config_t *conf = config_get_config_unlocked();
-#ifdef CHUID
-   struct passwd *user;
-   struct group *group;
-   uid_t uid=-1;
-   gid_t gid=-1;
-
-   if(conf->chuid)
-   {
-       if(conf->user) {
-           user = getpwnam(conf->user);
-           if(user)
-               uid = user->pw_uid;
-           else
-               fprintf(stderr, "Couldn't find user \"%s\" in password file\n", conf->user);
-       }
-       if(conf->group) {
-           group = getgrnam(conf->group);
-
-           if(group)
-               gid = group->gr_gid;
-           else
-               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
-       }
-   }
-#endif
-
-#ifdef CHROOT
-   if (conf->chroot)
-   {
-       if(getuid()) /* root check */
-       {
-           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
-           return;
-       }
-       if(chroot(conf->base_dir))
-       {
-           fprintf(stderr,"WARNING: Couldn't change server root: %s\n", strerror(errno));
-           return;
-       }
-       else
-           fprintf(stdout, "Changed root successfully to \"%s\".\n", conf->base_dir);
-
-   }   
-#endif
-#ifdef CHUID
-
-   if(conf->chuid)
-   {
-       if(getuid()) /* root check */
-       {
-           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
-           return;
-       }
-
-       if(gid != -1) {
-           if(!setgid(gid))
-               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
-           else
-               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
-       }
-
-       if(uid != -1) {
-           if(!setuid(uid))
-               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
-           else
-               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
-       }
-   }
-#endif
-}
-
-#ifdef WIN32_SERVICE
-int mainService(int argc, char **argv)
-#else
-int main(int argc, char **argv)
-#endif
-{
-    int res, ret;
-    char filename[512];
-    char pbuf[1024];
-
-    /* parse the '-c icecast.xml' option
-    ** only, so that we can read a configfile
-    */
-    res = _parse_config_opts(argc, argv, filename, 512);
-    if (res == 1) {
-        /* startup all the modules */
-        _initialize_subsystems();
-
-        /* parse the config file */
-        config_get_config();
-        ret = config_initial_parse_file(filename);
-        config_release_config();
-        if (ret < 0) {
-            memset(pbuf, '\000', sizeof(pbuf));
-            snprintf(pbuf, sizeof(pbuf)-1, 
-                "FATAL: error parsing config file (%s)", filename);
-            _fatal_error(pbuf);
-            switch (ret) {
-            case CONFIG_EINSANE:
-                _fatal_error("filename was null or blank");
-                break;
-            case CONFIG_ENOROOT:
-                _fatal_error("no root element found");
-                break;
-            case CONFIG_EBADROOT:
-                _fatal_error("root element is not <icecast>");
-                break;
-            default:
-                _fatal_error("XML config parsing error");
-                break;
-            }
-            _shutdown_subsystems();
-            return 1;
-        }
-    } else if (res == -1) {
-        _print_usage();
-        return 1;
-    }
-    
-    /* override config file options with commandline options */
-    config_parse_cmdline(argc, argv);
-
-    /* Bind socket, before we change userid */
-    if(!_server_proc_init()) {
-        _fatal_error("Server startup failed. Exiting");
-        _shutdown_subsystems();
-        return 1;
-    }
-
-    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
-
-    stats_initialize(); /* We have to do this later on because of threading */
-    fserve_initialize(); /* This too */
-
-#ifdef CHUID 
-    /* We'll only have getuid() if we also have setuid(), it's reasonable to
-     * assume */
-    if(!getuid()) /* Running as root! Don't allow this */
-    {
-        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
-        fprintf(stderr, "Use the changeowner directive in the config file\n");
-        _shutdown_subsystems();
-        return 1;
-    }
-#endif
-
-    /* setup default signal handlers */
-    sighandler_initialize();
-
-    if (!_start_logging()) {
-        _fatal_error("FATAL: Could not start logging");
-        _shutdown_subsystems();
-        return 1;
-    }
-
-    INFO1 ("%s server started", ICECAST_VERSION_STRING);
-
-    /* REM 3D Graphics */
-
-    /* let her rip */
-    global.running = ICE_RUNNING;
-
-    /* Startup yp thread */
-    yp_initialize();
-
-    /* Do this after logging init */
-    slave_initialize();
-    auth_initialise ();
-
-    _server_proc();
-
-    INFO0("Shutting down");
-
-    _shutdown_subsystems();
-
-    if (pidfile)
-    {
-        remove (pidfile);
-        free (pidfile);
-    }
-
-    return 0;
-}
-
-

Copied: icecast/tags/icecast-2.3.2/src/main.c (from rev 14825, icecast/trunk/icecast/src/main.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/main.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/main.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,527 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_CURL
+#include <curl/curl.h>
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "net/sock.h"
+#include "net/resolver.h"
+#include "httpp/httpp.h"
+
+#ifdef CHUID
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#endif
+
+#include "cfgfile.h"
+#include "sighandler.h"
+
+#include "global.h"
+#include "compat.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "slave.h"
+#include "stats.h"
+#include "logging.h"
+#include "xslt.h"
+#include "fserve.h"
+#include "yp.h"
+#include "auth.h"
+
+#include <libxml/xmlmemory.h>
+
+#ifdef _WIN32
+/* For getpid() */
+#include <process.h>
+#include <windows.h>
+#define snprintf _snprintf
+#define getpid _getpid
+#endif
+
+#undef CATMODULE
+#define CATMODULE "main"
+
+static int background;
+static char *pidfile = NULL;
+
+static void _fatal_error(char *perr)
+{
+#ifdef WIN32
+    MessageBox(NULL, perr, "Error", MB_OK);
+#else
+    fprintf(stdout, "%s\n", perr);
+#endif
+}
+
+static void _print_usage(void)
+{
+    printf("%s\n\n", ICECAST_VERSION_STRING);
+    printf("usage: icecast [-b -v] -c <file>\n");
+    printf("options:\n");
+    printf("\t-c <file>\tSpecify configuration file\n");
+    printf("\t-v\t\tDisplay version info\n");
+    printf("\t-b\t\tRun icecast in the background\n");
+    printf("\n");
+}
+
+static void _stop_logging(void)
+{
+    log_close(errorlog);
+    log_close(accesslog);
+    log_close(playlistlog);
+}
+
+static void _initialize_subsystems(void)
+{
+    log_initialize();
+    thread_initialize();
+    sock_initialize();
+    resolver_initialize();
+    config_initialize();
+    connection_initialize();
+    global_initialize();
+    refbuf_initialize();
+#if !defined(WIN32) || defined(WIN32_SERVICE)
+    /* win32 GUI needs to do the initialise before here */
+    xslt_initialize();
+#ifdef HAVE_CURL_GLOBAL_INIT
+    curl_global_init (CURL_GLOBAL_ALL);
+#endif
+#endif
+}
+
+static void _shutdown_subsystems(void)
+{
+    fserve_shutdown();
+#if !defined(WIN32) || defined(WIN32_SERVICE)
+    /* If we do the following cleanup on the win32 GUI then the app will crash when libxml2 is
+     * initialised again as the process doesn't terminate */
+    xslt_shutdown();
+#endif
+    refbuf_shutdown();
+    slave_shutdown();
+    auth_shutdown();
+    yp_shutdown();
+    stats_shutdown();
+
+    global_shutdown();
+    connection_shutdown();
+    config_shutdown();
+    resolver_shutdown();
+    sock_shutdown();
+    thread_shutdown();
+
+#ifdef HAVE_CURL
+    curl_global_cleanup();
+#endif
+
+    /* Now that these are done, we can stop the loggers. */
+    _stop_logging();
+    log_shutdown();
+}
+
+static int _parse_config_opts(int argc, char **argv, char *filename, int size)
+{
+    int i = 1;
+    int config_ok = 0;
+
+    background = 0;
+    if (argc < 2) return -1;
+
+    while (i < argc) {
+        if (strcmp(argv[i], "-b") == 0) {
+#ifndef WIN32
+            pid_t pid;
+            fprintf(stdout, "Starting icecast2\nDetaching from the console\n");
+
+            pid = fork();
+
+            if (pid > 0) {
+                /* exit the parent */
+                exit(0);
+            }
+            else if(pid < 0) {
+                fprintf(stderr, "FATAL: Unable to fork child!");
+                exit(1);
+            }
+            background = 1;
+#endif
+        }
+        if (strcmp(argv[i], "-v") == 0) {
+            fprintf(stdout, "%s\n", ICECAST_VERSION_STRING);
+            exit(0);
+        }
+
+        if (strcmp(argv[i], "-c") == 0) {
+            if (i + 1 < argc) {
+                strncpy(filename, argv[i + 1], size-1);
+                filename[size-1] = 0;
+                config_ok = 1;
+            } else {
+                return -1;
+            }
+        }
+        i++;
+    }
+
+    if(config_ok)
+        return 1;
+    else
+        return -1;
+}
+
+static int _start_logging(void)
+{
+    char fn_error[FILENAME_MAX];
+    char fn_access[FILENAME_MAX];
+    char fn_playlist[FILENAME_MAX];
+    char buf[1024];
+    int log_to_stderr;
+
+    ice_config_t *config = config_get_config_unlocked();
+
+    if(strcmp(config->error_log, "-")) {
+        snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
+        errorlog = log_open(fn_error);
+        log_to_stderr = 0;
+        if (config->logsize)
+            log_set_trigger (errorlog, config->logsize);
+        log_set_archive_timestamp(errorlog, config->logarchive);
+    } else {
+        errorlog = log_open_file(stderr);
+        log_to_stderr = 1;
+    }
+
+    if (errorlog < 0) {
+        buf[sizeof(buf)-1] = 0;
+        snprintf(buf, sizeof(buf)-1, 
+                "FATAL: could not open error logging (%s): %s",
+                log_to_stderr?"standard error":fn_error,
+                strerror(errno));
+        _fatal_error(buf);
+    }
+    log_set_level(errorlog, config->loglevel);
+
+    if(strcmp(config->access_log, "-")) {
+        snprintf(fn_access, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
+        accesslog = log_open(fn_access);
+        log_to_stderr = 0;
+        if (config->logsize)
+            log_set_trigger (accesslog, config->logsize);
+        log_set_archive_timestamp(accesslog, config->logarchive);
+    } else {
+        accesslog = log_open_file(stderr);
+        log_to_stderr = 1;
+    }
+
+    if (accesslog < 0) {
+        buf[sizeof(buf)-1] = 0;
+        snprintf(buf, sizeof(buf)-1, 
+                "FATAL: could not open access logging (%s): %s",
+                log_to_stderr?"standard error":fn_access,
+                strerror(errno));
+        _fatal_error(buf);
+    }
+
+    if(config->playlist_log) {
+        snprintf(fn_playlist, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
+        playlistlog = log_open(fn_playlist);
+        if (playlistlog < 0) {
+            buf[sizeof(buf)-1] = 0;
+            snprintf(buf, sizeof(buf)-1, 
+                "FATAL: could not open playlist logging (%s): %s",
+                log_to_stderr?"standard error":fn_playlist,
+                strerror(errno));
+            _fatal_error(buf);
+        }
+        log_to_stderr = 0;
+        if (config->logsize)
+            log_set_trigger (playlistlog, config->logsize);
+        log_set_archive_timestamp(playlistlog, config->logarchive);
+    } else {
+        playlistlog = -1;
+    }
+
+    log_set_level(errorlog, config->loglevel);
+    log_set_level(accesslog, 4);
+    log_set_level(playlistlog, 4);
+
+    if (errorlog >= 0 && accesslog >= 0) return 1;
+    
+    return 0;
+}
+
+
+static int _start_listening(void)
+{
+    int i;
+    for(i=0; i < global.server_sockets; i++) {
+        if (sock_listen(global.serversock[i], ICE_LISTEN_QUEUE) == SOCK_ERROR)
+            return 0;
+
+        sock_set_blocking(global.serversock[i], SOCK_NONBLOCK);
+    }
+
+    return 1;
+}
+
+/* bind the socket and start listening */
+static int _server_proc_init(void)
+{
+    ice_config_t *config = config_get_config_unlocked();
+
+    if (connection_setup_sockets (config) < 1)
+        return 0;
+
+    if (!_start_listening()) {
+        _fatal_error("Failed trying to listen on server socket");
+        return 0;
+    }
+
+    /* recreate the pid file */
+    if (config->pidfile)
+    {
+        FILE *f;
+        pidfile = strdup (config->pidfile);
+        if (pidfile && (f = fopen (config->pidfile, "w")) != NULL)
+        {
+            fprintf (f, "%d\n", (int)getpid());
+            fclose (f);
+        }
+    }
+
+    return 1;
+}
+
+/* this is the heart of the beast */
+static void _server_proc(void)
+{
+    if (background)
+    {
+        fclose (stdin);
+        fclose (stdout);
+        fclose (stderr);
+    }
+    connection_accept_loop();
+
+    connection_setup_sockets (NULL);
+}
+
+/* chroot the process. Watch out - we need to do this before starting other
+ * threads. Change uid as well, after figuring out uid _first_ */
+
+static void _ch_root_uid_setup(void)
+{
+   ice_config_t *conf = config_get_config_unlocked();
+#ifdef CHUID
+   struct passwd *user;
+   struct group *group;
+   uid_t uid=-1;
+   gid_t gid=-1;
+
+   if(conf->chuid)
+   {
+       if(conf->user) {
+           user = getpwnam(conf->user);
+           if(user)
+               uid = user->pw_uid;
+           else
+               fprintf(stderr, "Couldn't find user \"%s\" in password file\n", conf->user);
+       }
+       if(conf->group) {
+           group = getgrnam(conf->group);
+
+           if(group)
+               gid = group->gr_gid;
+           else
+               fprintf(stderr, "Couldn't find group \"%s\" in groups file\n", conf->group);
+       }
+   }
+#endif
+
+#ifdef CHROOT
+   if (conf->chroot)
+   {
+       if(getuid()) /* root check */
+       {
+           fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
+           return;
+       }
+       if(chroot(conf->base_dir))
+       {
+           fprintf(stderr,"WARNING: Couldn't change server root: %s\n", strerror(errno));
+           return;
+       }
+       else
+           fprintf(stdout, "Changed root successfully to \"%s\".\n", conf->base_dir);
+
+   }   
+#endif
+#ifdef CHUID
+
+   if(conf->chuid)
+   {
+       if(getuid()) /* root check */
+       {
+           fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
+           return;
+       }
+
+       if(gid != -1) {
+           if(!setgid(gid))
+               fprintf(stdout, "Changed groupid to %i.\n", (int)gid);
+           else
+               fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
+       }
+
+       if(uid != -1) {
+           if(!setuid(uid))
+               fprintf(stdout, "Changed userid to %i.\n", (int)uid);
+           else
+               fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
+       }
+   }
+#endif
+}
+
+#ifdef WIN32_SERVICE
+int mainService(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+    int res, ret;
+    char filename[512];
+    char pbuf[1024];
+
+    /* parse the '-c icecast.xml' option
+    ** only, so that we can read a configfile
+    */
+    res = _parse_config_opts(argc, argv, filename, 512);
+    if (res == 1) {
+        /* startup all the modules */
+        _initialize_subsystems();
+
+        /* parse the config file */
+        config_get_config();
+        ret = config_initial_parse_file(filename);
+        config_release_config();
+        if (ret < 0) {
+            memset(pbuf, '\000', sizeof(pbuf));
+            snprintf(pbuf, sizeof(pbuf)-1, 
+                "FATAL: error parsing config file (%s)", filename);
+            _fatal_error(pbuf);
+            switch (ret) {
+            case CONFIG_EINSANE:
+                _fatal_error("filename was null or blank");
+                break;
+            case CONFIG_ENOROOT:
+                _fatal_error("no root element found");
+                break;
+            case CONFIG_EBADROOT:
+                _fatal_error("root element is not <icecast>");
+                break;
+            default:
+                _fatal_error("XML config parsing error");
+                break;
+            }
+            _shutdown_subsystems();
+            return 1;
+        }
+    } else if (res == -1) {
+        _print_usage();
+        return 1;
+    }
+    
+    /* override config file options with commandline options */
+    config_parse_cmdline(argc, argv);
+
+    /* Bind socket, before we change userid */
+    if(!_server_proc_init()) {
+        _fatal_error("Server startup failed. Exiting");
+        _shutdown_subsystems();
+        return 1;
+    }
+
+    _ch_root_uid_setup(); /* Change user id and root if requested/possible */
+
+    stats_initialize(); /* We have to do this later on because of threading */
+    fserve_initialize(); /* This too */
+
+#ifdef CHUID 
+    /* We'll only have getuid() if we also have setuid(), it's reasonable to
+     * assume */
+    if(!getuid()) /* Running as root! Don't allow this */
+    {
+        fprintf(stderr, "ERROR: You should not run icecast2 as root\n");
+        fprintf(stderr, "Use the changeowner directive in the config file\n");
+        _shutdown_subsystems();
+        return 1;
+    }
+#endif
+
+    /* setup default signal handlers */
+    sighandler_initialize();
+
+    if (!_start_logging()) {
+        _fatal_error("FATAL: Could not start logging");
+        _shutdown_subsystems();
+        return 1;
+    }
+
+    INFO1 ("%s server started", ICECAST_VERSION_STRING);
+
+    /* REM 3D Graphics */
+
+    /* let her rip */
+    global.running = ICE_RUNNING;
+
+    /* Startup yp thread */
+    yp_initialize();
+
+    /* Do this after logging init */
+    slave_initialize();
+    auth_initialise ();
+
+    _server_proc();
+
+    INFO0("Shutting down");
+
+    _shutdown_subsystems();
+
+    if (pidfile)
+    {
+        remove (pidfile);
+        free (pidfile);
+    }
+
+    return 0;
+}
+
+

Deleted: icecast/tags/icecast-2.3.2/src/refbuf.c
===================================================================
--- icecast/trunk/icecast/src/refbuf.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/refbuf.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,83 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* refbuf.c
-**
-** reference counting buffer implementation
-**
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "refbuf.h"
-
-void refbuf_initialize(void)
-{
-}
-
-void refbuf_shutdown(void)
-{
-}
-
-refbuf_t *refbuf_new(unsigned long size)
-{
-    refbuf_t *refbuf;
-
-    refbuf = (refbuf_t *)malloc(sizeof(refbuf_t));
-    if (refbuf == NULL)
-        return NULL;
-    refbuf->data = NULL;
-    if (size)
-    {
-        refbuf->data = malloc (size);
-        if (refbuf->data == NULL)
-        {
-            free (refbuf);
-            return NULL;
-        }
-    }
-    refbuf->len = size;
-    refbuf->sync_point = 0;
-    refbuf->_count = 1;
-    refbuf->next = NULL;
-    refbuf->associated = NULL;
-
-    return refbuf;
-}
-
-void refbuf_addref(refbuf_t *self)
-{
-    self->_count++;
-}
-
-void refbuf_release(refbuf_t *self)
-{
-    if (self == NULL)
-        return;
-    self->_count--;
-    if (self->_count == 0) {
-        while (self->associated)
-        {
-            refbuf_t *ref = self->associated;
-            self->associated = ref->next;
-            refbuf_release (ref);
-        }
-        free(self->data);
-        free(self);
-    }
-}
-

Copied: icecast/tags/icecast-2.3.2/src/refbuf.c (from rev 14306, icecast/trunk/icecast/src/refbuf.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/refbuf.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/refbuf.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,83 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* refbuf.c
+**
+** reference counting buffer implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "refbuf.h"
+
+void refbuf_initialize(void)
+{
+}
+
+void refbuf_shutdown(void)
+{
+}
+
+refbuf_t *refbuf_new(unsigned int size)
+{
+    refbuf_t *refbuf;
+
+    refbuf = (refbuf_t *)malloc(sizeof(refbuf_t));
+    if (refbuf == NULL)
+        return NULL;
+    refbuf->data = NULL;
+    if (size)
+    {
+        refbuf->data = malloc (size);
+        if (refbuf->data == NULL)
+        {
+            free (refbuf);
+            return NULL;
+        }
+    }
+    refbuf->len = size;
+    refbuf->sync_point = 0;
+    refbuf->_count = 1;
+    refbuf->next = NULL;
+    refbuf->associated = NULL;
+
+    return refbuf;
+}
+
+void refbuf_addref(refbuf_t *self)
+{
+    self->_count++;
+}
+
+void refbuf_release(refbuf_t *self)
+{
+    if (self == NULL)
+        return;
+    self->_count--;
+    if (self->_count == 0) {
+        while (self->associated)
+        {
+            refbuf_t *ref = self->associated;
+            self->associated = ref->next;
+            refbuf_release (ref);
+        }
+        free(self->data);
+        free(self);
+    }
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/refbuf.h
===================================================================
--- icecast/trunk/icecast/src/refbuf.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/refbuf.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,42 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* refbuf.h
-**
-** reference counting data buffer
-**
-*/
-#ifndef __REFBUF_H__
-#define __REFBUF_H__
-
-typedef struct _refbuf_tag
-{
-    char *data;
-    unsigned long len;
-    int sync_point;
-    struct _refbuf_tag *associated;
-    struct _refbuf_tag *next;
-
-    unsigned long _count;
-} refbuf_t;
-
-void refbuf_initialize(void);
-void refbuf_shutdown(void);
-
-refbuf_t *refbuf_new(unsigned long size);
-void refbuf_addref(refbuf_t *self);
-void refbuf_release(refbuf_t *self);
-
-#define PER_CLIENT_REFBUF_SIZE  4096
-
-#endif  /* __REFBUF_H__ */
-

Copied: icecast/tags/icecast-2.3.2/src/refbuf.h (from rev 14306, icecast/trunk/icecast/src/refbuf.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/refbuf.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/refbuf.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,42 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* refbuf.h
+**
+** reference counting data buffer
+**
+*/
+#ifndef __REFBUF_H__
+#define __REFBUF_H__
+
+typedef struct _refbuf_tag
+{
+    unsigned int len;
+    unsigned int _count;
+    char *data;
+    struct _refbuf_tag *associated;
+    struct _refbuf_tag *next;
+    int sync_point;
+
+} refbuf_t;
+
+void refbuf_initialize(void);
+void refbuf_shutdown(void);
+
+refbuf_t *refbuf_new(unsigned int size);
+void refbuf_addref(refbuf_t *self);
+void refbuf_release(refbuf_t *self);
+
+#define PER_CLIENT_REFBUF_SIZE  4096
+
+#endif  /* __REFBUF_H__ */
+

Deleted: icecast/tags/icecast-2.3.2/src/slave.c
===================================================================
--- icecast/trunk/icecast/src/slave.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/slave.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,745 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-/* slave.c
- * by Ciaran Anscomb <ciaran.anscomb at 6809.org.uk>
- *
- * Periodically requests a list of streams from a master server
- * and creates source threads for any it doesn't already have.
- * */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <sys/types.h>
-
-#ifndef _WIN32
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#else
-#include <winsock2.h>
-#define snprintf _snprintf
-#define strcasecmp stricmp
-#define strncasecmp strnicmp
-#endif
-
-#include "compat.h"
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "net/sock.h"
-#include "httpp/httpp.h"
-
-#include "cfgfile.h"
-#include "global.h"
-#include "util.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "logging.h"
-#include "source.h"
-#include "format.h"
-#include "event.h"
-
-#define CATMODULE "slave"
-
-static void *_slave_thread(void *arg);
-static thread_type *_slave_thread_id;
-static int slave_running = 0;
-static volatile int update_settings = 0;
-static volatile int update_streams = 0;
-static volatile unsigned int max_interval = 0;
-
-relay_server *relay_free (relay_server *relay)
-{
-    relay_server *next = relay->next;
-    DEBUG1("freeing relay %s", relay->localmount);
-    if (relay->source)
-       source_free_source (relay->source);
-    xmlFree (relay->server);
-    xmlFree (relay->mount);
-    xmlFree (relay->localmount);
-    if (relay->username)
-        xmlFree (relay->username);
-    if (relay->password)
-        xmlFree (relay->password);
-    free (relay);
-    return next;
-}
-
-
-relay_server *relay_copy (relay_server *r)
-{
-    relay_server *copy = calloc (1, sizeof (relay_server));
-
-    if (copy)
-    {
-        copy->server = xmlStrdup (r->server);
-        copy->mount = xmlStrdup (r->mount);
-        copy->localmount = xmlStrdup (r->localmount);
-        if (r->username)
-            copy->username = xmlStrdup (r->username);
-        if (r->password)
-            copy->password = xmlStrdup (r->password);
-        copy->port = r->port;
-        copy->mp3metadata = r->mp3metadata;
-        copy->on_demand = r->on_demand;
-    }
-    return copy;
-}
-
-
-/* force a recheck of the relays. This will recheck the master server if
- * a this is a slave.
- */
-void slave_recheck_all (void)
-{
-    max_interval = 0;
-    update_streams = 1;
-    update_settings = 1;
-}
-
-
-/* Request slave thread to check the relay list for changes and to
- * update the stats for the current streams.
- */
-void slave_rebuild_mounts (void)
-{
-    update_settings = 1;
-}
-
-
-void slave_initialize(void)
-{
-    if (slave_running)
-        return;
-
-    slave_running = 1;
-    max_interval = 0;
-    _slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
-}
-
-
-void slave_shutdown(void)
-{
-    if (!slave_running)
-        return;
-    slave_running = 0;
-    DEBUG0 ("waiting for slave thread");
-    thread_join (_slave_thread_id);
-}
-
-
-/* Actually open the connection and do some http parsing, handle any 302
- * responses within here.
- */
-static client_t *open_relay_connection (relay_server *relay)
-{
-    int redirects = 0;
-    http_parser_t *parser = NULL;
-    connection_t *con=NULL;
-    char *server = strdup (relay->server);
-    char *mount = strdup (relay->mount);
-    int port = relay->port;
-    char *auth_header;
-    char header[4096];
-
-    /* build any authentication header before connecting */
-    if (relay->username && relay->password)
-    {
-        char *esc_authorisation;
-        unsigned len = strlen(relay->username) + strlen(relay->password) + 2;
-
-        auth_header = malloc (len);
-        snprintf (auth_header, len, "%s:%s", relay->username, relay->password);
-        esc_authorisation = util_base64_encode(auth_header);
-        free(auth_header);
-        len = strlen (esc_authorisation) + 24;
-        auth_header = malloc (len);
-        snprintf (auth_header, len,
-                "Authorization: Basic %s\r\n", esc_authorisation);
-        free(esc_authorisation);
-    }
-    else
-        auth_header = strdup ("");
-
-    while (redirects < 10)
-    {
-        sock_t streamsock;
-
-        INFO2 ("connecting to %s:%d", server, port);
-
-        streamsock = sock_connect_wto (server, port, 10);
-        if (streamsock == SOCK_ERROR)
-        {
-            WARN2 ("Failed to connect to %s:%d", server, port);
-            break;
-        }
-        con = connection_create (streamsock, -1, strdup (server));
-
-        /* At this point we may not know if we are relaying an mp3 or vorbis
-         * stream, but only send the icy-metadata header if the relay details
-         * state so (the typical case).  It's harmless in the vorbis case. If
-         * we don't send in this header then relay will not have mp3 metadata.
-         */
-        sock_write(streamsock, "GET %s HTTP/1.0\r\n"
-                "User-Agent: %s\r\n"
-                "%s"
-                "%s"
-                "\r\n",
-                mount,
-                ICECAST_VERSION_STRING,
-                relay->mp3metadata?"Icy-MetaData: 1\r\n":"",
-                auth_header);
-        memset (header, 0, sizeof(header));
-        if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
-        {
-            ERROR4 ("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount);
-            break;
-        }
-        parser = httpp_create_parser();
-        httpp_initialize (parser, NULL);
-        if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
-        {
-            ERROR4("Error parsing relay request for %s (%s:%d%s)", relay->localmount,
-                    server, port, mount);
-            break;
-        }
-        if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0)
-        {
-            /* better retry the connection again but with different details */
-            const char *uri, *mountpoint;
-            int len;
-
-            uri = httpp_getvar (parser, "location");
-            INFO1 ("redirect received %s", uri);
-            if (strncmp (uri, "http://", 7) != 0)
-                break;
-            uri += 7;
-            mountpoint = strchr (uri, '/');
-            free (mount);
-            if (mountpoint)
-                mount = strdup (mountpoint);
-            else
-                mount = strdup ("/");
-
-            len = strcspn (uri, ":/");
-            port = 80;
-            if (uri [len] == ':')
-                port = atoi (uri+len+1);
-            free (server);
-            server = calloc (1, len+1);
-            strncpy (server, uri, len);
-            connection_close (con);
-            httpp_destroy (parser);
-            con = NULL;
-            parser = NULL;
-        }
-        else
-        {
-            client_t *client = NULL;
-
-            if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
-            {
-                ERROR2("Error from relay request: %s (%s)", relay->localmount,
-                        httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
-                break;
-            }
-            global_lock ();
-            if (client_create (&client, con, parser) < 0)
-            {
-                global_unlock ();
-                /* make sure only the client_destory frees these */
-                con = NULL;
-                parser = NULL;
-                client_destroy (client);
-                break;
-            }
-            global_unlock ();
-            sock_set_blocking (streamsock, SOCK_NONBLOCK);
-            client_set_queue (client, NULL);
-            free (server);
-            free (mount);
-            free (auth_header);
-
-            return client;
-        }
-        redirects++;
-    }
-    /* failed, better clean up */
-    free (server);
-    free (mount);
-    free (auth_header);
-    if (con)
-        connection_close (con);
-    if (parser)
-        httpp_destroy (parser);
-    return NULL;
-}
-
-
-/* This does the actual connection for a relay. A thread is
- * started off if a connection can be acquired
- */
-static void *start_relay_stream (void *arg)
-{
-    relay_server *relay = arg;
-    source_t *src = relay->source;
-    client_t *client;
-
-    INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);
-    do
-    {
-        client = open_relay_connection (relay);
-
-        if (client == NULL)
-            continue;
-
-        src->client = client;
-        src->parser = client->parser;
-        src->con = client->con;
-
-        if (connection_complete_source (src, 0) < 0)
-        {
-            INFO0("Failed to complete source initialisation");
-            client_destroy (client);
-            src->client = NULL;
-            continue;
-        }
-        stats_event_inc(NULL, "source_relay_connections");
-        stats_event (relay->localmount, "source_ip", client->con->ip);
-
-        source_main (relay->source);
-
-        if (relay->on_demand == 0)
-        {
-            /* only keep refreshing YP entries for inactive on-demand relays */
-            yp_remove (relay->localmount);
-            relay->source->yp_public = -1;
-            relay->start = time(NULL) + 10; /* prevent busy looping if failing */
-        }
-
-        /* we've finished, now get cleaned up */
-        relay->cleanup = 1;
-        slave_rebuild_mounts();
-
-        return NULL;
-    } while (0);  /* TODO allow looping through multiple servers */
-
-    if (relay->source->fallback_mount)
-    {
-        source_t *fallback_source;
-
-        DEBUG1 ("failed relay, fallback to %s", relay->source->fallback_mount);
-        avl_tree_rlock(global.source_tree);
-        fallback_source = source_find_mount (relay->source->fallback_mount);
-
-        if (fallback_source != NULL)
-            source_move_clients (relay->source, fallback_source);
-
-        avl_tree_unlock (global.source_tree);
-    }
-
-    source_clear_source (relay->source);
-
-    /* cleanup relay, but prevent this relay from starting up again too soon */
-    relay->start = time(NULL) + max_interval;
-    relay->cleanup = 1;
-
-    return NULL;
-}
-
-
-/* wrapper for starting the provided relay stream */
-static void check_relay_stream (relay_server *relay)
-{
-    if (relay->source == NULL)
-    {
-        if (relay->localmount[0] != '/')
-        {
-            WARN1 ("relay mountpoint \"%s\" does not start with /, skipping",
-                    relay->localmount);
-            return;
-        }
-        /* new relay, reserve the name */
-        relay->source = source_reserve (relay->localmount);
-        if (relay->source)
-        {
-            ice_config_t *config;
-            DEBUG1("Adding relay source at mountpoint \"%s\"", relay->localmount);
-            config = config_get_config();
-            stats_event_args (relay->localmount, "listenurl", "http://%s:%d%s",
-                    config->hostname, config->port, relay->localmount);
-            config_release_config();
-            stats_event (relay->localmount, "listeners", "0");
-            slave_rebuild_mounts();
-        }
-        else
-            WARN1 ("new relay but source \"%s\" already exists", relay->localmount);
-    }
-    do
-    {
-        source_t *source = relay->source;
-        /* skip relay if active, not configured or just not time yet */
-        if (relay->source == NULL || relay->running || relay->start > time(NULL))
-            break;
-        if (relay->on_demand && source->on_demand_req == 0)
-        {
-            relay->source->on_demand = relay->on_demand;
-
-            if (source->fallback_mount && source->fallback_override)
-            {
-                source_t *fallback;
-                avl_tree_rlock (global.source_tree);
-                fallback = source_find_mount (source->fallback_mount);
-                if (fallback && fallback->running && fallback->listeners)
-                {
-                   DEBUG2 ("fallback running %d with %lu listeners", fallback->running, fallback->listeners);
-                   source->on_demand_req = 1;
-                }
-                avl_tree_unlock (global.source_tree);
-            }
-            if (source->on_demand_req == 0)
-                break;
-        }
-
-        relay->start = time(NULL) + 5;
-        relay->running = 1;
-        relay->thread = thread_create ("Relay Thread", start_relay_stream,
-                relay, THREAD_ATTACHED);
-        return;
-
-    } while (0);
-    /* the relay thread may of shut down itself */
-    if (relay->cleanup)
-    {
-        if (relay->thread)
-        {
-            DEBUG1 ("waiting for relay thread for \"%s\"", relay->localmount);
-            thread_join (relay->thread);
-            relay->thread = NULL;
-        }
-        relay->cleanup = 0;
-        relay->running = 0;
-
-        if (relay->on_demand && relay->source)
-        {
-            ice_config_t *config = config_get_config ();
-            mount_proxy *mountinfo = config_find_mount (config, relay->localmount);
-            source_update_settings (config, relay->source, mountinfo);
-            config_release_config ();
-            stats_event (relay->localmount, "listeners", "0");
-        }
-    }
-}
-
-
-/* compare the 2 relays to see if there are any changes, return 1 if
- * the relay needs to be restarted, 0 otherwise
- */
-static int relay_has_changed (relay_server *new, relay_server *old)
-{
-    do
-    {
-        if (strcmp (new->mount, old->mount) != 0)
-            break;
-        if (strcmp (new->server, old->server) != 0)
-            break;
-        if (new->port != old->port)
-            break;
-        if (new->mp3metadata != old->mp3metadata)
-            break;
-        if (new->on_demand != old->on_demand)
-            old->on_demand = new->on_demand;
-        return 0;
-    } while (0);
-    return 1;
-}
-
-
-/* go through updated looking for relays that are different configured. The
- * returned list contains relays that should be kept running, current contains
- * the list of relays to shutdown
- */
-static relay_server *
-update_relay_set (relay_server **current, relay_server *updated)
-{
-    relay_server *relay = updated;
-    relay_server *existing_relay, **existing_p;
-    relay_server *new_list = NULL;
-
-    while (relay)
-    {
-        existing_relay = *current;
-        existing_p = current;
-
-        while (existing_relay)
-        {
-            /* break out if keeping relay */
-            if (strcmp (relay->localmount, existing_relay->localmount) == 0)
-                if (relay_has_changed (relay, existing_relay) == 0)
-                    break;
-            existing_p = &existing_relay->next;
-            existing_relay = existing_relay->next;
-        }
-        if (existing_relay == NULL)
-        {
-            /* new one, copy and insert */
-            existing_relay = relay_copy (relay);
-        }
-        else
-        {
-            *existing_p = existing_relay->next;
-        }
-        existing_relay->next = new_list;
-        new_list = existing_relay;
-        relay = relay->next;
-    }
-    return new_list;
-}
-
-
-/* update the relay_list with entries from new_relay_list. Any new relays
- * are added to the list, and any not listed in the provided new_relay_list
- * are separated and returned in a separate list
- */
-static relay_server *
-update_relays (relay_server **relay_list, relay_server *new_relay_list)
-{
-    relay_server *active_relays, *cleanup_relays;
-
-    active_relays = update_relay_set (relay_list, new_relay_list);
-
-    cleanup_relays = *relay_list;
-    /* re-assign new set */
-    *relay_list = active_relays;
-
-    return cleanup_relays;
-}
-
-
-static void relay_check_streams (relay_server *to_start,
-        relay_server *to_free, int skip_timer)
-{
-    relay_server *relay;
-
-    while (to_free)
-    {
-        if (to_free->source)
-        {
-            if (to_free->running)
-            {
-                /* relay has been removed from xml, shut down active relay */
-                DEBUG1 ("source shutdown request on \"%s\"", to_free->localmount);
-                to_free->running = 0;
-                to_free->source->running = 0;
-                thread_join (to_free->thread);
-            }
-            else
-                stats_event (to_free->localmount, NULL, NULL);
-        }
-        to_free = relay_free (to_free);
-    }
-
-    relay = to_start;
-    while (relay)
-    {
-        if (skip_timer)
-            relay->start = 0;
-        check_relay_stream (relay);
-        relay = relay->next;
-    }
-}
-
-
-static int update_from_master(ice_config_t *config)
-{
-    char *master = NULL, *password = NULL, *username= NULL;
-    int port;
-    sock_t mastersock;
-    int ret = 0;
-    char buf[256];
-    do
-    {
-        char *authheader, *data;
-        relay_server *new_relays = NULL, *cleanup_relays;
-        int len, count = 1;
-        int on_demand;
-
-        username = strdup (config->master_username);
-        if (config->master_password)
-            password = strdup (config->master_password);
-
-        if (config->master_server)
-            master = strdup (config->master_server);
-
-        port = config->master_server_port;
-
-        if (password == NULL || master == NULL || port == 0)
-            break;
-        on_demand = config->on_demand;
-        ret = 1;
-        config_release_config();
-        mastersock = sock_connect_wto (master, port, 0);
-
-        if (mastersock == SOCK_ERROR)
-        {
-            WARN0("Relay slave failed to contact master server to fetch stream list");
-            break;
-        }
-
-        len = strlen(username) + strlen(password) + 2;
-        authheader = malloc(len);
-        snprintf (authheader, len, "%s:%s", username, password);
-        data = util_base64_encode(authheader);
-        sock_write (mastersock,
-                "GET /admin/streamlist.txt HTTP/1.0\r\n"
-                "Authorization: Basic %s\r\n"
-                "\r\n", data);
-        free(authheader);
-        free(data);
-
-        if (sock_read_line(mastersock, buf, sizeof(buf)) == 0 ||
-                strncmp (buf, "HTTP/1.0 200", 12) != 0)
-        {
-            sock_close (mastersock);
-            WARN0 ("Master rejected streamlist request");
-            break;
-        }
-
-        while (sock_read_line(mastersock, buf, sizeof(buf)))
-        {
-            if (!strlen(buf))
-                break;
-        }
-        while (sock_read_line(mastersock, buf, sizeof(buf)))
-        {
-            relay_server *r;
-            if (!strlen(buf))
-                continue;
-            DEBUG2 ("read %d from master \"%s\"", count++, buf);
-            r = calloc (1, sizeof (relay_server));
-            if (r)
-            {
-                r->server = xmlStrdup (master);
-                r->port = port;
-                r->mount = xmlStrdup (buf);
-                r->localmount = xmlStrdup (buf);
-                r->mp3metadata = 1;
-                r->on_demand = on_demand;
-                r->next = new_relays;
-                new_relays = r;
-            }
-        }
-        sock_close (mastersock);
-
-        thread_mutex_lock (&(config_locks()->relay_lock));
-        cleanup_relays = update_relays (&global.master_relays, new_relays);
-        
-        relay_check_streams (global.master_relays, cleanup_relays, 0);
-        relay_check_streams (NULL, new_relays, 0);
-
-        thread_mutex_unlock (&(config_locks()->relay_lock));
-
-    } while(0);
-
-    if (master)
-        free (master);
-    if (username)
-        free (username);
-    if (password)
-        free (password);
-
-    return ret;
-}
-
-
-static void *_slave_thread(void *arg)
-{
-    ice_config_t *config;
-    unsigned int interval = 0;
-
-    update_settings = 0;
-    update_streams = 0;
-
-    source_recheck_mounts (1);
-
-    while (1)
-    {
-        relay_server *cleanup_relays = NULL;
-        int skip_timer = 0;
-
-        /* re-read xml file if requested */
-        if (global . schedule_config_reread)
-        {
-            event_config_read (NULL);
-            global . schedule_config_reread = 0;
-        }
-
-        thread_sleep (1000000);
-        if (slave_running == 0)
-            break;
-
-        ++interval;
-
-        /* only update relays lists when required */
-        if (max_interval <= interval)
-        {
-            DEBUG0 ("checking master stream list");
-            config = config_get_config();
-
-            if (max_interval == 0)
-                skip_timer = 1;
-            interval = 0;
-            max_interval = config->master_update_interval;
-
-            /* the connection could take some time, so the lock can drop */
-            if (update_from_master (config))
-                config = config_get_config();
-
-            thread_mutex_lock (&(config_locks()->relay_lock));
-
-            cleanup_relays = update_relays (&global.relays, config->relay);
-
-            config_release_config();
-        }
-        else
-            thread_mutex_lock (&(config_locks()->relay_lock));
-
-        relay_check_streams (global.relays, cleanup_relays, skip_timer);
-        relay_check_streams (global.master_relays, NULL, skip_timer);
-        thread_mutex_unlock (&(config_locks()->relay_lock));
-
-        if (update_settings)
-        {
-            source_recheck_mounts (update_streams);
-            update_settings = 0;
-            update_streams = 0;
-        }
-    }
-    INFO0 ("shutting down current relays");
-    relay_check_streams (NULL, global.relays, 0);
-    relay_check_streams (NULL, global.master_relays, 0);
-
-    INFO0 ("Slave thread shutdown complete");
-
-    return NULL;
-}
-

Copied: icecast/tags/icecast-2.3.2/src/slave.c (from rev 14219, icecast/trunk/icecast/src/slave.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/slave.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/slave.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,751 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* slave.c
+ * by Ciaran Anscomb <ciaran.anscomb at 6809.org.uk>
+ *
+ * Periodically requests a list of streams from a master server
+ * and creates source threads for any it doesn't already have.
+ * */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#else
+#include <winsock2.h>
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#include "compat.h"
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "net/sock.h"
+#include "httpp/httpp.h"
+
+#include "cfgfile.h"
+#include "global.h"
+#include "util.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "source.h"
+#include "format.h"
+#include "event.h"
+
+#define CATMODULE "slave"
+
+static void *_slave_thread(void *arg);
+static thread_type *_slave_thread_id;
+static int slave_running = 0;
+static volatile int update_settings = 0;
+static volatile int update_all_mounts = 0;
+static volatile unsigned int max_interval = 0;
+
+relay_server *relay_free (relay_server *relay)
+{
+    relay_server *next = relay->next;
+    DEBUG1("freeing relay %s", relay->localmount);
+    if (relay->source)
+       source_free_source (relay->source);
+    xmlFree (relay->server);
+    xmlFree (relay->mount);
+    xmlFree (relay->localmount);
+    if (relay->username)
+        xmlFree (relay->username);
+    if (relay->password)
+        xmlFree (relay->password);
+    free (relay);
+    return next;
+}
+
+
+relay_server *relay_copy (relay_server *r)
+{
+    relay_server *copy = calloc (1, sizeof (relay_server));
+
+    if (copy)
+    {
+        copy->server = (char *)xmlCharStrdup (r->server);
+        copy->mount = (char *)xmlCharStrdup (r->mount);
+        copy->localmount = (char *)xmlCharStrdup (r->localmount);
+        if (r->username)
+            copy->username = (char *)xmlCharStrdup (r->username);
+        if (r->password)
+            copy->password = (char *)xmlCharStrdup (r->password);
+        copy->port = r->port;
+        copy->mp3metadata = r->mp3metadata;
+        copy->on_demand = r->on_demand;
+    }
+    return copy;
+}
+
+
+/* force a recheck of the relays. This will recheck the master server if
+ * this is a slave and rebuild all mountpoints in the stats tree
+ */
+void slave_update_all_mounts (void)
+{
+    max_interval = 0;
+    update_all_mounts = 1;
+    update_settings = 1;
+}
+
+
+/* Request slave thread to check the relay list for changes and to
+ * update the stats for the current streams.
+ */
+void slave_rebuild_mounts (void)
+{
+    update_settings = 1;
+}
+
+
+void slave_initialize(void)
+{
+    if (slave_running)
+        return;
+
+    slave_running = 1;
+    max_interval = 0;
+    _slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
+}
+
+
+void slave_shutdown(void)
+{
+    if (!slave_running)
+        return;
+    slave_running = 0;
+    DEBUG0 ("waiting for slave thread");
+    thread_join (_slave_thread_id);
+}
+
+
+/* Actually open the connection and do some http parsing, handle any 302
+ * responses within here.
+ */
+static client_t *open_relay_connection (relay_server *relay)
+{
+    int redirects = 0;
+    char *server_id = NULL;
+    ice_config_t *config;
+    http_parser_t *parser = NULL;
+    connection_t *con=NULL;
+    char *server = strdup (relay->server);
+    char *mount = strdup (relay->mount);
+    int port = relay->port;
+    char *auth_header;
+    char header[4096];
+
+    config = config_get_config ();
+    server_id = strdup (config->server_id);
+    config_release_config ();
+
+    /* build any authentication header before connecting */
+    if (relay->username && relay->password)
+    {
+        char *esc_authorisation;
+        unsigned len = strlen(relay->username) + strlen(relay->password) + 2;
+
+        auth_header = malloc (len);
+        snprintf (auth_header, len, "%s:%s", relay->username, relay->password);
+        esc_authorisation = util_base64_encode(auth_header);
+        free(auth_header);
+        len = strlen (esc_authorisation) + 24;
+        auth_header = malloc (len);
+        snprintf (auth_header, len,
+                "Authorization: Basic %s\r\n", esc_authorisation);
+        free(esc_authorisation);
+    }
+    else
+        auth_header = strdup ("");
+
+    while (redirects < 10)
+    {
+        sock_t streamsock;
+
+        INFO2 ("connecting to %s:%d", server, port);
+
+        streamsock = sock_connect_wto (server, port, 10);
+        if (streamsock == SOCK_ERROR)
+        {
+            WARN2 ("Failed to connect to %s:%d", server, port);
+            break;
+        }
+        con = connection_create (streamsock, -1, strdup (server));
+
+        /* At this point we may not know if we are relaying an mp3 or vorbis
+         * stream, but only send the icy-metadata header if the relay details
+         * state so (the typical case).  It's harmless in the vorbis case. If
+         * we don't send in this header then relay will not have mp3 metadata.
+         */
+        sock_write(streamsock, "GET %s HTTP/1.0\r\n"
+                "User-Agent: %s\r\n"
+                "%s"
+                "%s"
+                "\r\n",
+                mount,
+                server_id,
+                relay->mp3metadata?"Icy-MetaData: 1\r\n":"",
+                auth_header);
+        memset (header, 0, sizeof(header));
+        if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
+        {
+            ERROR4 ("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount);
+            break;
+        }
+        parser = httpp_create_parser();
+        httpp_initialize (parser, NULL);
+        if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
+        {
+            ERROR4("Error parsing relay request for %s (%s:%d%s)", relay->localmount,
+                    server, port, mount);
+            break;
+        }
+        if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0)
+        {
+            /* better retry the connection again but with different details */
+            const char *uri, *mountpoint;
+            int len;
+
+            uri = httpp_getvar (parser, "location");
+            INFO1 ("redirect received %s", uri);
+            if (strncmp (uri, "http://", 7) != 0)
+                break;
+            uri += 7;
+            mountpoint = strchr (uri, '/');
+            free (mount);
+            if (mountpoint)
+                mount = strdup (mountpoint);
+            else
+                mount = strdup ("/");
+
+            len = strcspn (uri, ":/");
+            port = 80;
+            if (uri [len] == ':')
+                port = atoi (uri+len+1);
+            free (server);
+            server = calloc (1, len+1);
+            strncpy (server, uri, len);
+            connection_close (con);
+            httpp_destroy (parser);
+            con = NULL;
+            parser = NULL;
+        }
+        else
+        {
+            client_t *client = NULL;
+
+            if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
+            {
+                ERROR2("Error from relay request: %s (%s)", relay->localmount,
+                        httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
+                break;
+            }
+            global_lock ();
+            if (client_create (&client, con, parser) < 0)
+            {
+                global_unlock ();
+                /* make sure only the client_destory frees these */
+                con = NULL;
+                parser = NULL;
+                client_destroy (client);
+                break;
+            }
+            global_unlock ();
+            sock_set_blocking (streamsock, SOCK_NONBLOCK);
+            client_set_queue (client, NULL);
+            free (server);
+            free (mount);
+            free (server_id);
+            free (auth_header);
+
+            return client;
+        }
+        redirects++;
+    }
+    /* failed, better clean up */
+    free (server);
+    free (mount);
+    free (server_id);
+    free (auth_header);
+    if (con)
+        connection_close (con);
+    if (parser)
+        httpp_destroy (parser);
+    return NULL;
+}
+
+
+/* This does the actual connection for a relay. A thread is
+ * started off if a connection can be acquired
+ */
+static void *start_relay_stream (void *arg)
+{
+    relay_server *relay = arg;
+    source_t *src = relay->source;
+    client_t *client;
+
+    INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);
+    do
+    {
+        client = open_relay_connection (relay);
+
+        if (client == NULL)
+            continue;
+
+        src->client = client;
+        src->parser = client->parser;
+        src->con = client->con;
+
+        if (connection_complete_source (src, 0) < 0)
+        {
+            INFO0("Failed to complete source initialisation");
+            client_destroy (client);
+            src->client = NULL;
+            continue;
+        }
+        stats_event_inc(NULL, "source_relay_connections");
+        stats_event (relay->localmount, "source_ip", client->con->ip);
+
+        source_main (relay->source);
+
+        if (relay->on_demand == 0)
+        {
+            /* only keep refreshing YP entries for inactive on-demand relays */
+            yp_remove (relay->localmount);
+            relay->source->yp_public = -1;
+            relay->start = time(NULL) + 10; /* prevent busy looping if failing */
+            slave_update_all_mounts();
+        }
+
+        /* we've finished, now get cleaned up */
+        relay->cleanup = 1;
+        slave_rebuild_mounts();
+
+        return NULL;
+    } while (0);  /* TODO allow looping through multiple servers */
+
+    if (relay->source->fallback_mount)
+    {
+        source_t *fallback_source;
+
+        DEBUG1 ("failed relay, fallback to %s", relay->source->fallback_mount);
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount (relay->source->fallback_mount);
+
+        if (fallback_source != NULL)
+            source_move_clients (relay->source, fallback_source);
+
+        avl_tree_unlock (global.source_tree);
+    }
+
+    source_clear_source (relay->source);
+
+    /* cleanup relay, but prevent this relay from starting up again too soon */
+    relay->start = time(NULL) + max_interval;
+    relay->cleanup = 1;
+
+    return NULL;
+}
+
+
+/* wrapper for starting the provided relay stream */
+static void check_relay_stream (relay_server *relay)
+{
+    if (relay->source == NULL)
+    {
+        if (relay->localmount[0] != '/')
+        {
+            WARN1 ("relay mountpoint \"%s\" does not start with /, skipping",
+                    relay->localmount);
+            return;
+        }
+        /* new relay, reserve the name */
+        relay->source = source_reserve (relay->localmount);
+        if (relay->source)
+        {
+            DEBUG1("Adding relay source at mountpoint \"%s\"", relay->localmount);
+            if (relay->on_demand)
+                slave_update_all_mounts();
+        }
+        else
+            WARN1 ("new relay but source \"%s\" already exists", relay->localmount);
+    }
+    do
+    {
+        source_t *source = relay->source;
+        /* skip relay if active, not configured or just not time yet */
+        if (relay->source == NULL || relay->running || relay->start > time(NULL))
+            break;
+        /* check if an inactive on-demand relay has a fallback that has listeners */
+        if (relay->on_demand && source->on_demand_req == 0)
+        {
+            relay->source->on_demand = relay->on_demand;
+
+            if (source->fallback_mount && source->fallback_override)
+            {
+                source_t *fallback;
+                avl_tree_rlock (global.source_tree);
+                fallback = source_find_mount (source->fallback_mount);
+                if (fallback && fallback->running && fallback->listeners)
+                {
+                   DEBUG2 ("fallback running %d with %lu listeners", fallback->running, fallback->listeners);
+                   source->on_demand_req = 1;
+                }
+                avl_tree_unlock (global.source_tree);
+            }
+            if (source->on_demand_req == 0)
+                break;
+        }
+
+        relay->start = time(NULL) + 5;
+        relay->running = 1;
+        relay->thread = thread_create ("Relay Thread", start_relay_stream,
+                relay, THREAD_ATTACHED);
+        return;
+
+    } while (0);
+    /* the relay thread may of shut down itself */
+    if (relay->cleanup)
+    {
+        if (relay->thread)
+        {
+            DEBUG1 ("waiting for relay thread for \"%s\"", relay->localmount);
+            thread_join (relay->thread);
+            relay->thread = NULL;
+        }
+        relay->cleanup = 0;
+        relay->running = 0;
+
+        if (relay->on_demand && relay->source)
+        {
+            ice_config_t *config = config_get_config ();
+            mount_proxy *mountinfo = config_find_mount (config, relay->localmount);
+            source_update_settings (config, relay->source, mountinfo);
+            config_release_config ();
+            stats_event (relay->localmount, "listeners", "0");
+        }
+    }
+}
+
+
+/* compare the 2 relays to see if there are any changes, return 1 if
+ * the relay needs to be restarted, 0 otherwise
+ */
+static int relay_has_changed (relay_server *new, relay_server *old)
+{
+    do
+    {
+        if (strcmp (new->mount, old->mount) != 0)
+            break;
+        if (strcmp (new->server, old->server) != 0)
+            break;
+        if (new->port != old->port)
+            break;
+        if (new->mp3metadata != old->mp3metadata)
+            break;
+        if (new->on_demand != old->on_demand)
+            old->on_demand = new->on_demand;
+        return 0;
+    } while (0);
+    return 1;
+}
+
+
+/* go through updated looking for relays that are different configured. The
+ * returned list contains relays that should be kept running, current contains
+ * the list of relays to shutdown
+ */
+static relay_server *
+update_relay_set (relay_server **current, relay_server *updated)
+{
+    relay_server *relay = updated;
+    relay_server *existing_relay, **existing_p;
+    relay_server *new_list = NULL;
+
+    while (relay)
+    {
+        existing_relay = *current;
+        existing_p = current;
+
+        while (existing_relay)
+        {
+            /* break out if keeping relay */
+            if (strcmp (relay->localmount, existing_relay->localmount) == 0)
+                if (relay_has_changed (relay, existing_relay) == 0)
+                    break;
+            existing_p = &existing_relay->next;
+            existing_relay = existing_relay->next;
+        }
+        if (existing_relay == NULL)
+        {
+            /* new one, copy and insert */
+            existing_relay = relay_copy (relay);
+        }
+        else
+        {
+            *existing_p = existing_relay->next;
+        }
+        existing_relay->next = new_list;
+        new_list = existing_relay;
+        relay = relay->next;
+    }
+    return new_list;
+}
+
+
+/* update the relay_list with entries from new_relay_list. Any new relays
+ * are added to the list, and any not listed in the provided new_relay_list
+ * are separated and returned in a separate list
+ */
+static relay_server *
+update_relays (relay_server **relay_list, relay_server *new_relay_list)
+{
+    relay_server *active_relays, *cleanup_relays;
+
+    active_relays = update_relay_set (relay_list, new_relay_list);
+
+    cleanup_relays = *relay_list;
+    /* re-assign new set */
+    *relay_list = active_relays;
+
+    return cleanup_relays;
+}
+
+
+static void relay_check_streams (relay_server *to_start,
+        relay_server *to_free, int skip_timer)
+{
+    relay_server *relay;
+
+    while (to_free)
+    {
+        if (to_free->source)
+        {
+            if (to_free->running)
+            {
+                /* relay has been removed from xml, shut down active relay */
+                DEBUG1 ("source shutdown request on \"%s\"", to_free->localmount);
+                to_free->running = 0;
+                to_free->source->running = 0;
+                thread_join (to_free->thread);
+            }
+            else
+                stats_event (to_free->localmount, NULL, NULL);
+        }
+        to_free = relay_free (to_free);
+    }
+
+    relay = to_start;
+    while (relay)
+    {
+        if (skip_timer)
+            relay->start = 0;
+        check_relay_stream (relay);
+        relay = relay->next;
+    }
+}
+
+
+static int update_from_master(ice_config_t *config)
+{
+    char *master = NULL, *password = NULL, *username= NULL;
+    int port;
+    sock_t mastersock;
+    int ret = 0;
+    char buf[256];
+    do
+    {
+        char *authheader, *data;
+        relay_server *new_relays = NULL, *cleanup_relays;
+        int len, count = 1;
+        int on_demand;
+
+        username = strdup (config->master_username);
+        if (config->master_password)
+            password = strdup (config->master_password);
+
+        if (config->master_server)
+            master = strdup (config->master_server);
+
+        port = config->master_server_port;
+
+        if (password == NULL || master == NULL || port == 0)
+            break;
+        on_demand = config->on_demand;
+        ret = 1;
+        config_release_config();
+        mastersock = sock_connect_wto (master, port, 0);
+
+        if (mastersock == SOCK_ERROR)
+        {
+            WARN0("Relay slave failed to contact master server to fetch stream list");
+            break;
+        }
+
+        len = strlen(username) + strlen(password) + 2;
+        authheader = malloc(len);
+        snprintf (authheader, len, "%s:%s", username, password);
+        data = util_base64_encode(authheader);
+        sock_write (mastersock,
+                "GET /admin/streamlist.txt HTTP/1.0\r\n"
+                "Authorization: Basic %s\r\n"
+                "\r\n", data);
+        free(authheader);
+        free(data);
+
+        if (sock_read_line(mastersock, buf, sizeof(buf)) == 0 ||
+                strncmp (buf, "HTTP/1.0 200", 12) != 0)
+        {
+            sock_close (mastersock);
+            WARN0 ("Master rejected streamlist request");
+            break;
+        }
+
+        while (sock_read_line(mastersock, buf, sizeof(buf)))
+        {
+            if (!strlen(buf))
+                break;
+        }
+        while (sock_read_line(mastersock, buf, sizeof(buf)))
+        {
+            relay_server *r;
+            if (!strlen(buf))
+                continue;
+            DEBUG2 ("read %d from master \"%s\"", count++, buf);
+            r = calloc (1, sizeof (relay_server));
+            if (r)
+            {
+                r->server = (char *)xmlCharStrdup (master);
+                r->port = port;
+                r->mount = (char *)xmlCharStrdup (buf);
+                r->localmount = (char *)xmlCharStrdup (buf);
+                r->mp3metadata = 1;
+                r->on_demand = on_demand;
+                r->next = new_relays;
+                new_relays = r;
+            }
+        }
+        sock_close (mastersock);
+
+        thread_mutex_lock (&(config_locks()->relay_lock));
+        cleanup_relays = update_relays (&global.master_relays, new_relays);
+        
+        relay_check_streams (global.master_relays, cleanup_relays, 0);
+        relay_check_streams (NULL, new_relays, 0);
+
+        thread_mutex_unlock (&(config_locks()->relay_lock));
+
+    } while(0);
+
+    if (master)
+        free (master);
+    if (username)
+        free (username);
+    if (password)
+        free (password);
+
+    return ret;
+}
+
+
+static void *_slave_thread(void *arg)
+{
+    ice_config_t *config;
+    unsigned int interval = 0;
+
+    update_settings = 0;
+    update_all_mounts = 0;
+
+    config = config_get_config();
+    stats_global (config);
+    config_release_config();
+    source_recheck_mounts (1);
+
+    while (1)
+    {
+        relay_server *cleanup_relays = NULL;
+        int skip_timer = 0;
+
+        /* re-read xml file if requested */
+        if (global . schedule_config_reread)
+        {
+            event_config_read (NULL);
+            global . schedule_config_reread = 0;
+        }
+
+        thread_sleep (1000000);
+        if (slave_running == 0)
+            break;
+
+        ++interval;
+
+        /* only update relays lists when required */
+        if (max_interval <= interval)
+        {
+            DEBUG0 ("checking master stream list");
+            config = config_get_config();
+
+            if (max_interval == 0)
+                skip_timer = 1;
+            interval = 0;
+            max_interval = config->master_update_interval;
+
+            /* the connection could take some time, so the lock can drop */
+            if (update_from_master (config))
+                config = config_get_config();
+
+            thread_mutex_lock (&(config_locks()->relay_lock));
+
+            cleanup_relays = update_relays (&global.relays, config->relay);
+
+            config_release_config();
+        }
+        else
+            thread_mutex_lock (&(config_locks()->relay_lock));
+
+        relay_check_streams (global.relays, cleanup_relays, skip_timer);
+        relay_check_streams (global.master_relays, NULL, skip_timer);
+        thread_mutex_unlock (&(config_locks()->relay_lock));
+
+        if (update_settings)
+        {
+            source_recheck_mounts (update_all_mounts);
+            update_settings = 0;
+            update_all_mounts = 0;
+        }
+    }
+    INFO0 ("shutting down current relays");
+    relay_check_streams (NULL, global.relays, 0);
+    relay_check_streams (NULL, global.master_relays, 0);
+
+    INFO0 ("Slave thread shutdown complete");
+
+    return NULL;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/slave.h
===================================================================
--- icecast/trunk/icecast/src/slave.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/slave.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,42 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __SLAVE_H__
-#define __SLAVE_H__
-
-#include <thread/thread.h>
-
-typedef struct _relay_server {
-    char *server;
-    int port;
-    char *mount;
-    char *username;
-    char *password;
-    char *localmount;
-    struct source_tag *source;
-    int mp3metadata;
-    int on_demand;
-    int running;
-    int cleanup;
-    time_t start;
-    thread_type *thread;
-    struct _relay_server *next;
-} relay_server;
-
-
-void slave_initialize(void);
-void slave_shutdown(void);
-void slave_recheck_all (void);
-void slave_rebuild_mounts (void);
-relay_server *relay_free (relay_server *relay);
-
-#endif  /* __SLAVE_H__ */

Copied: icecast/tags/icecast-2.3.2/src/slave.h (from rev 14206, icecast/trunk/icecast/src/slave.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/slave.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/slave.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,42 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __SLAVE_H__
+#define __SLAVE_H__
+
+#include "thread/thread.h"
+
+typedef struct _relay_server {
+    char *server;
+    int port;
+    char *mount;
+    char *username;
+    char *password;
+    char *localmount;
+    struct source_tag *source;
+    int mp3metadata;
+    int on_demand;
+    int running;
+    int cleanup;
+    time_t start;
+    thread_type *thread;
+    struct _relay_server *next;
+} relay_server;
+
+
+void slave_initialize(void);
+void slave_shutdown(void);
+void slave_update_all_mounts (void);
+void slave_rebuild_mounts (void);
+relay_server *relay_free (relay_server *relay);
+
+#endif  /* __SLAVE_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/source.c
===================================================================
--- icecast/trunk/icecast/src/source.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/source.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1397 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <ogg/ogg.h>
-#include <errno.h>
-
-#ifndef _WIN32
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#else
-#include <winsock2.h>
-#include <windows.h>
-#define snprintf _snprintf
-#endif
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "httpp/httpp.h"
-#include "net/sock.h"
-
-#include "connection.h"
-#include "global.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "logging.h"
-#include "cfgfile.h"
-#include "util.h"
-#include "source.h"
-#include "format.h"
-#include "fserve.h"
-#include "auth.h"
-#include "compat.h"
-
-#undef CATMODULE
-#define CATMODULE "source"
-
-#define MAX_FALLBACK_DEPTH 10
-
-mutex_t move_clients_mutex;
-
-/* avl tree helper */
-static int _compare_clients(void *compare_arg, void *a, void *b);
-static int _free_client(void *key);
-static void _parse_audio_info (source_t *source, const char *s);
-static void source_shutdown (source_t *source);
-#ifdef _WIN32
-#define source_run_script(x,y)  WARN0("on [dis]connect scripts disabled");
-#else
-static void source_run_script (char *command, char *mountpoint);
-#endif
-
-/* Allocate a new source with the stated mountpoint, if one already
- * exists with that mountpoint in the global source tree then return
- * NULL.
- */
-source_t *source_reserve (const char *mount)
-{
-    source_t *src = NULL;
-
-    if(mount[0] != '/')
-        WARN1("Source at \"%s\" does not start with '/', clients will be "
-                "unable to connect", mount);
-
-    do
-    {
-        avl_tree_wlock (global.source_tree);
-        src = source_find_mount_raw (mount);
-        if (src)
-        {
-            src = NULL;
-            break;
-        }
-
-        src = calloc (1, sizeof(source_t));
-        if (src == NULL)
-            break;
-
-        src->client_tree = avl_tree_new(_compare_clients, NULL);
-        src->pending_tree = avl_tree_new(_compare_clients, NULL);
-
-        /* make duplicates for strings or similar */
-        src->mount = strdup (mount);
-        src->max_listeners = -1;
-
-        avl_insert (global.source_tree, src);
-
-    } while (0);
-
-    avl_tree_unlock (global.source_tree);
-    return src;
-}
-
-
-/* Find a mount with this raw name - ignoring fallbacks. You should have the
- * global source tree locked to call this.
- */
-source_t *source_find_mount_raw(const char *mount)
-{
-    source_t *source;
-    avl_node *node;
-    int cmp;
-
-    if (!mount) {
-        return NULL;
-    }
-    /* get the root node */
-    node = global.source_tree->root->right;
-    
-    while (node) {
-        source = (source_t *)node->key;
-        cmp = strcmp(mount, source->mount);
-        if (cmp < 0) 
-            node = node->left;
-        else if (cmp > 0)
-            node = node->right;
-        else
-            return source;
-    }
-    
-    /* didn't find it */
-    return NULL;
-}
-
-
-/* Search for mount, if the mount is there but not currently running then
- * check the fallback, and so on.  Must have a global source lock to call
- * this function.
- */
-source_t *source_find_mount (const char *mount)
-{
-    source_t *source = NULL;
-    ice_config_t *config;
-    mount_proxy *mountinfo;
-    int depth = 0;
-
-    config = config_get_config();
-    while (mount && depth < MAX_FALLBACK_DEPTH)
-    {
-        source = source_find_mount_raw(mount);
-
-        if (source)
-        {
-            if (source->running || source->on_demand)
-                break;
-        }
-
-        /* we either have a source which is not active (relay) or no source
-         * at all. Check the mounts list for fallback settings
-         */
-        mountinfo = config_find_mount (config, mount);
-        source = NULL;
-
-        if (mountinfo == NULL)
-            break;
-        mount = mountinfo->fallback_mount;
-        depth++;
-    }
-
-    config_release_config();
-    return source;
-}
-
-
-int source_compare_sources(void *arg, void *a, void *b)
-{
-    source_t *srca = (source_t *)a;
-    source_t *srcb = (source_t *)b;
-
-    return strcmp(srca->mount, srcb->mount);
-}
-
-
-void source_clear_source (source_t *source)
-{
-    DEBUG1 ("clearing source \"%s\"", source->mount);
-
-    avl_tree_wlock (source->client_tree);
-    client_destroy(source->client);
-    source->client = NULL;
-    source->parser = NULL;
-    source->con = NULL;
-
-    /* log bytes read in access log */
-    if (source->client && source->format)
-        source->client->con->sent_bytes = source->format->read_bytes;
-
-    if (source->dumpfile)
-    {
-        INFO1 ("Closing dumpfile for %s", source->mount);
-        fclose (source->dumpfile);
-        source->dumpfile = NULL;
-    }
-
-    /* lets kick off any clients that are left on here */
-    while (avl_get_first (source->client_tree))
-    {
-        avl_delete (source->client_tree,
-                avl_get_first (source->client_tree)->key, _free_client);
-    }
-
-    avl_tree_wlock (source->pending_tree);
-    while (avl_get_first (source->pending_tree))
-    {
-        avl_delete (source->pending_tree,
-                avl_get_first(source->pending_tree)->key, _free_client);
-    }
-    avl_tree_unlock (source->pending_tree);
-
-    if (source->format && source->format->free_plugin)
-        source->format->free_plugin (source->format);
-    source->format = NULL;
-
-    /* Lets clear out the source queue too */
-    while (source->stream_data)
-    {
-        refbuf_t *p = source->stream_data;
-        source->stream_data = p->next;
-        /* can be referenced by burst handler as well */
-        while (p->_count > 1)
-            refbuf_release (p);
-        refbuf_release (p);
-    }
-    source->stream_data_tail = NULL;
-
-    source->burst_point = NULL;
-    source->burst_size = 0;
-    source->burst_offset = 0;
-    source->queue_size = 0;
-    source->queue_size_limit = 0;
-    source->listeners = 0;
-    source->max_listeners = -1;
-    source->prev_listeners = 0;
-    source->hidden = 0;
-    source->shoutcast_compat = 0;
-    source->client_stats_update = 0;
-    util_dict_free (source->audio_info);
-    source->audio_info = NULL;
-
-    free(source->fallback_mount);
-    source->fallback_mount = NULL;
-
-    free(source->dumpfilename);
-    source->dumpfilename = NULL;
-
-    if (source->intro_file)
-    {
-        fclose (source->intro_file);
-        source->intro_file = NULL;
-    }
-
-    source->on_demand_req = 0;
-    avl_tree_unlock (source->client_tree);
-}
-
-
-/* Remove the provided source from the global tree and free it */
-void source_free_source (source_t *source)
-{
-    DEBUG1 ("freeing source \"%s\"", source->mount);
-    avl_tree_wlock (global.source_tree);
-    avl_delete (global.source_tree, source, NULL);
-    avl_tree_unlock (global.source_tree);
-
-    avl_tree_free(source->pending_tree, _free_client);
-    avl_tree_free(source->client_tree, _free_client);
-
-    /* make sure all YP entries have gone */
-    yp_remove (source->mount);
-
-    free (source->mount);
-    free (source);
-
-    return;
-}
-
-
-client_t *source_find_client(source_t *source, int id)
-{
-    client_t fakeclient;
-    void *result;
-    connection_t fakecon;
-
-    fakeclient.con = &fakecon;
-    fakeclient.con->id = id;
-
-    avl_tree_rlock(source->client_tree);
-    if(avl_get_by_key(source->client_tree, &fakeclient, &result) == 0)
-    {
-        avl_tree_unlock(source->client_tree);
-        return result;
-    }
-
-    avl_tree_unlock(source->client_tree);
-    return NULL;
-}
-
-
-/* Move clients from source to dest provided dest is running
- * and that the stream format is the same.
- * The only lock that should be held when this is called is the
- * source tree lock
- */
-void source_move_clients (source_t *source, source_t *dest)
-{
-    unsigned long count = 0;
-    if (strcmp (source->mount, dest->mount) == 0)
-    {
-        WARN1 ("src and dst are the same \"%s\", skipping", source->mount);
-        return;
-    }
-    /* we don't want the two write locks to deadlock in here */
-    thread_mutex_lock (&move_clients_mutex);
-
-    /* if the destination is not running then we can't move clients */
-
-    if (dest->running == 0 && dest->on_demand == 0)
-    {
-        WARN1 ("destination mount %s not running, unable to move clients ", dest->mount);
-        thread_mutex_unlock (&move_clients_mutex);
-        return;
-    }
-
-    avl_tree_wlock (dest->pending_tree);
-    do
-    {
-        client_t *client;
-
-        /* we need to move the client and pending trees - we must take the
-         * locks in this order to avoid deadlocks */
-        avl_tree_wlock (source->client_tree);
-        avl_tree_wlock (source->pending_tree);
-
-        if (source->on_demand == 0 && source->format == NULL)
-        {
-            INFO1 ("source mount %s is not available", source->mount);
-            break;
-        }
-        if (source->format && dest->format)
-        {
-            if (source->format->type != dest->format->type)
-            {
-                WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
-                break;
-            }
-        }
-
-        while (1)
-        {
-            avl_node *node = avl_get_first (source->pending_tree);
-            if (node == NULL)
-                break;
-            client = (client_t *)(node->key);
-            avl_delete (source->pending_tree, client, NULL);
-
-            /* when switching a client to a different queue, be wary of the 
-             * refbuf it's referring to, if it's http headers then we need
-             * to write them so don't release it.
-             */
-            if (client->check_buffer != format_check_http_buffer)
-            {
-                client_set_queue (client, NULL);
-                client->check_buffer = format_check_file_buffer;
-                if (source->con == NULL)
-                    client->intro_offset = -1;
-            }
-
-            avl_insert (dest->pending_tree, (void *)client);
-            count++;
-        }
-
-        while (1)
-        {
-            avl_node *node = avl_get_first (source->client_tree);
-            if (node == NULL)
-                break;
-
-            client = (client_t *)(node->key);
-            avl_delete (source->client_tree, client, NULL);
-
-            /* when switching a client to a different queue, be wary of the 
-             * refbuf it's referring to, if it's http headers then we need
-             * to write them so don't release it.
-             */
-            if (client->check_buffer != format_check_http_buffer)
-            {
-                client_set_queue (client, NULL);
-                client->check_buffer = format_check_file_buffer;
-                if (source->con == NULL)
-                    client->intro_offset = -1;
-            }
-            avl_insert (dest->pending_tree, (void *)client);
-            count++;
-        }
-        INFO2 ("passing %lu listeners to \"%s\"", count, dest->mount);
-
-        source->listeners = 0;
-        stats_event (source->mount, "listeners", "0");
-
-    } while (0);
-
-    avl_tree_unlock (source->pending_tree);
-    avl_tree_unlock (source->client_tree);
-
-    /* see if we need to wake up an on-demand relay */
-    if (dest->running == 0 && dest->on_demand && count)
-        dest->on_demand_req = 1;
-
-    avl_tree_unlock (dest->pending_tree);
-    thread_mutex_unlock (&move_clients_mutex);
-}
-
-
-/* get some data from the source. The stream data is placed in a refbuf
- * and sent back, however NULL is also valid as in the case of a short
- * timeout and there's no data pending.
- */
-static refbuf_t *get_next_buffer (source_t *source)
-{
-    refbuf_t *refbuf = NULL;
-    int delay = 250;
-
-    if (source->short_delay)
-        delay = 0;
-    while (global.running == ICE_RUNNING && source->running)
-    {
-        int fds = 0;
-        time_t current = time (NULL);
-
-        if (source->client)
-            fds = util_timed_wait_for_fd (source->con->sock, delay);
-        else
-        {
-            thread_sleep (delay*1000);
-            source->last_read = current;
-        }
-
-        if (current >= source->client_stats_update)
-        {
-            stats_event_args (source->mount, "total_bytes_read",
-                    FORMAT_UINT64, source->format->read_bytes);
-            stats_event_args (source->mount, "total_bytes_sent",
-                    FORMAT_UINT64, source->format->sent_bytes);
-            source->client_stats_update = current + 5;
-        }
-        if (fds < 0)
-        {
-            if (! sock_recoverable (sock_error()))
-            {
-                WARN0 ("Error while waiting on socket, Disconnecting source");
-                source->running = 0;
-            }
-            break;
-        }
-        if (fds == 0)
-        {
-            if (source->last_read + (time_t)source->timeout < current)
-            {
-                DEBUG3 ("last %ld, timeout %d, now %ld", (long)source->last_read,
-                        source->timeout, (long)current);
-                WARN0 ("Disconnecting source due to socket timeout");
-                source->running = 0;
-            }
-            break;
-        }
-        source->last_read = current;
-        refbuf = source->format->get_buffer (source);
-        if (source->client->con && source->client->con->error)
-        {
-            INFO1 ("End of Stream %s", source->mount);
-            source->running = 0;
-            continue;
-        }
-        if (refbuf)
-            break;
-    }
-
-    return refbuf;
-}
-
-
-/* general send routine per listener.  The deletion_expected tells us whether
- * the last in the queue is about to disappear, so if this client is still
- * referring to it after writing then drop the client as it's fallen too far
- * behind 
- */ 
-static void send_to_listener (source_t *source, client_t *client, int deletion_expected)
-{
-    int bytes;
-    int loop = 10;   /* max number of iterations in one go */
-    int total_written = 0;
-
-    while (1)
-    {
-        /* check for limited listener time */
-        if (client->con->discon_time)
-            if (time(NULL) >= client->con->discon_time)
-            {
-                INFO1 ("time limit reached for client #%lu", client->con->id);
-                client->con->error = 1;
-            }
-
-        /* jump out if client connection has died */
-        if (client->con->error)
-            break;
-
-        /* lets not send too much to one client in one go, but don't
-           sleep for too long if more data can be sent */
-        if (total_written > 20000 || loop == 0)
-        {
-            if (client->check_buffer != format_check_file_buffer)
-                source->short_delay = 1;
-            break;
-        }
-
-        loop--;
-
-        if (client->check_buffer (source, client) < 0)
-            break;
-
-        bytes = client->write_to_client (client);
-        if (bytes <= 0)
-            break;  /* can't write any more */
-
-        total_written += bytes;
-    }
-    source->format->sent_bytes += total_written;
-
-    /* the refbuf referenced at head (last in queue) may be marked for deletion
-     * if so, check to see if this client is still referring to it */
-    if (deletion_expected && client->refbuf && client->refbuf == source->stream_data)
-    {
-        INFO2 ("Client %lu (%s) has fallen too far behind, removing",
-                client->con->id, client->con->ip);
-        stats_event_inc (source->mount, "slow_listeners");
-        client->con->error = 1;
-    }
-}
-
-
-/* Perform any initialisation just before the stream data is processed, the header
- * info is processed by now and the format details are setup
- */
-static void source_init (source_t *source)
-{
-    ice_config_t *config = config_get_config();
-    char *listenurl, *str;
-    int listen_url_size;
-    mount_proxy *mountinfo;
-
-    /* 6 for max size of port */
-    listen_url_size = strlen("http://") + strlen(config->hostname) +
-        strlen(":") + 6 + strlen(source->mount) + 1;
-
-    listenurl = malloc (listen_url_size);
-    memset (listenurl, '\000', listen_url_size);
-    snprintf (listenurl, listen_url_size, "http://%s:%d%s",
-            config->hostname, config->port, source->mount);
-    config_release_config();
-
-    str = httpp_getvar(source->parser, "ice-audio-info");
-    source->audio_info = util_dict_new();
-    if (str)
-    {
-        _parse_audio_info (source, str);
-        stats_event (source->mount, "audio_info", str);
-    }
-
-    stats_event (source->mount, "listenurl", listenurl);
-
-    free(listenurl);
-
-    if (source->dumpfilename != NULL)
-    {
-        source->dumpfile = fopen (source->dumpfilename, "ab");
-        if (source->dumpfile == NULL)
-        {
-            WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.",
-                    source->dumpfilename, strerror(errno));
-        }
-    }
-
-    /* grab a read lock, to make sure we get a chance to cleanup */
-    thread_rwlock_rlock (source->shutdown_rwlock);
-
-    /* start off the statistics */
-    source->listeners = 0;
-    stats_event_inc (NULL, "source_total_connections");
-    stats_event (source->mount, "slow_listeners", "0");
-    stats_event_args (source->mount, "listeners", "%lu", source->listeners);
-    stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
-    stats_event_time (source->mount, "stream_start");
-
-    DEBUG0("Source creation complete");
-    source->last_read = time (NULL);
-    source->prev_listeners = -1;
-    source->running = 1;
-
-    mountinfo = config_find_mount (config_get_config(), source->mount);
-    if (mountinfo)
-    {
-        if (mountinfo->on_connect)
-            source_run_script (mountinfo->on_connect, source->mount);
-        auth_stream_start (mountinfo, source->mount);
-    }
-    config_release_config();
-
-    /*
-    ** Now, if we have a fallback source and override is on, we want
-    ** to steal its clients, because it means we've come back online
-    ** after a failure and they should be gotten back from the waiting
-    ** loop or jingle track or whatever the fallback is used for
-    */
-
-    if (source->fallback_override && source->fallback_mount)
-    {
-        source_t *fallback_source;
-
-        avl_tree_rlock(global.source_tree);
-        fallback_source = source_find_mount(source->fallback_mount);
-
-        if (fallback_source)
-            source_move_clients (fallback_source, source);
-
-        avl_tree_unlock(global.source_tree);
-    }
-}
-
-
-void source_main (source_t *source)
-{
-    refbuf_t *refbuf;
-    client_t *client;
-    avl_node *client_node;
-
-    source_init (source);
-
-    while (global.running == ICE_RUNNING && source->running) {
-        int remove_from_q;
-
-        refbuf = get_next_buffer (source);
-
-        remove_from_q = 0;
-        source->short_delay = 0;
-
-        if (refbuf)
-        {
-            /* append buffer to the in-flight data queue,  */
-            if (source->stream_data == NULL)
-            {
-                source->stream_data = refbuf;
-                source->burst_point = refbuf;
-            }
-            if (source->stream_data_tail)
-                source->stream_data_tail->next = refbuf;
-            source->stream_data_tail = refbuf;
-            source->queue_size += refbuf->len;
-            /* new buffer is referenced for burst */
-            refbuf_addref (refbuf);
-
-            /* new data on queue, so check the burst point */
-            source->burst_offset += refbuf->len;
-            while (source->burst_offset > source->burst_size)
-            {
-                refbuf_t *to_release = source->burst_point;
-
-                if (to_release->next)
-                {
-                    source->burst_point = to_release->next;
-                    source->burst_offset -= to_release->len;
-                    refbuf_release (to_release);
-                    continue;
-                }
-                break;
-            }
-
-            /* save stream to file */
-            if (source->dumpfile && source->format->write_buf_to_file)
-                source->format->write_buf_to_file (source, refbuf);
-        }
-        /* lets see if we have too much data in the queue, but don't remove it until later */
-        if (source->queue_size > source->queue_size_limit)
-            remove_from_q = 1;
-
-        /* acquire write lock on client_tree */
-        avl_tree_wlock(source->client_tree);
-
-        client_node = avl_get_first(source->client_tree);
-        while (client_node) {
-            client = (client_t *)client_node->key;
-
-            send_to_listener (source, client, remove_from_q);
-
-            if (client->con->error) {
-                client_node = avl_get_next(client_node);
-                avl_delete(source->client_tree, (void *)client, _free_client);
-                source->listeners--;
-                DEBUG0("Client removed");
-                continue;
-            }
-            client_node = avl_get_next(client_node);
-        }
-
-        /* acquire write lock on pending_tree */
-        avl_tree_wlock(source->pending_tree);
-
-        /** add pending clients **/
-        client_node = avl_get_first(source->pending_tree);
-        while (client_node) {
-
-            if(source->max_listeners != -1 && 
-                    source->listeners >= (unsigned long)source->max_listeners) 
-            {
-                /* The common case is caught in the main connection handler,
-                 * this deals with rarer cases (mostly concerning fallbacks)
-                 * and doesn't give the listening client any information about
-                 * why they were disconnected
-                 */
-                client = (client_t *)client_node->key;
-                client_node = avl_get_next(client_node);
-                avl_delete(source->pending_tree, (void *)client, _free_client);
-
-                INFO0("Client deleted, exceeding maximum listeners for this "
-                        "mountpoint.");
-                continue;
-            }
-            
-            /* Otherwise, the client is accepted, add it */
-            avl_insert(source->client_tree, client_node->key);
-
-            source->listeners++;
-            DEBUG0("Client added");
-            stats_event_inc(source->mount, "connections");
-
-            client_node = avl_get_next(client_node);
-        }
-
-        /** clear pending tree **/
-        while (avl_get_first(source->pending_tree)) {
-            avl_delete(source->pending_tree, 
-                    avl_get_first(source->pending_tree)->key, 
-                    source_remove_client);
-        }
-
-        /* release write lock on pending_tree */
-        avl_tree_unlock(source->pending_tree);
-
-        /* update the stats if need be */
-        if (source->listeners != source->prev_listeners)
-        {
-            source->prev_listeners = source->listeners;
-            INFO2("listener count on %s now %lu", source->mount, source->listeners);
-            if (source->listeners > source->peak_listeners)
-            {
-                source->peak_listeners = source->listeners;
-                stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
-            }
-            stats_event_args (source->mount, "listeners", "%lu", source->listeners);
-            if (source->listeners == 0 && source->on_demand)
-                source->running = 0;
-        }
-
-        /* lets reduce the queue, any lagging clients should of been
-         * terminated by now
-         */
-        if (source->stream_data)
-        {
-            /* normal unreferenced queue data will have a refcount 1, but
-             * burst queue data will be at least 2, active clients will also
-             * increase refcount */
-            while (source->stream_data->_count == 1)
-            {
-                refbuf_t *to_go = source->stream_data;
-
-                if (to_go->next == NULL || source->burst_point == to_go)
-                {
-                    /* this should not happen */
-                    ERROR0 ("queue state is unexpected");
-                    source->running = 0;
-                    break;
-                }
-                source->stream_data = to_go->next;
-                source->queue_size -= to_go->len;
-                refbuf_release (to_go);
-            }
-        }
-
-        /* release write lock on client_tree */
-        avl_tree_unlock(source->client_tree);
-    }
-    source_shutdown (source);
-}
-
-
-static void source_shutdown (source_t *source)
-{
-    mount_proxy *mountinfo;
-
-    source->running = 0;
-    INFO1("Source \"%s\" exiting", source->mount);
-
-    mountinfo = config_find_mount (config_get_config(), source->mount);
-    if (mountinfo)
-    {
-        if (mountinfo->on_disconnect)
-            source_run_script (mountinfo->on_disconnect, source->mount);
-        auth_stream_end (mountinfo, source->mount);
-    }
-    config_release_config();
-
-    /* we have de-activated the source now, so no more clients will be
-     * added, now move the listeners we have to the fallback (if any)
-     */
-    if (source->fallback_mount)
-    {
-        source_t *fallback_source;
-
-        avl_tree_rlock(global.source_tree);
-        fallback_source = source_find_mount (source->fallback_mount);
-
-        if (fallback_source != NULL)
-            source_move_clients (source, fallback_source);
-
-        avl_tree_unlock (global.source_tree);
-    }
-
-    /* delete this sources stats */
-    stats_event(source->mount, NULL, NULL);
-
-    /* we don't remove the source from the tree here, it may be a relay and
-       therefore reserved */
-    source_clear_source (source);
-
-    global_lock();
-    global.sources--;
-    stats_event_args (NULL, "sources", "%d", global.sources);
-    global_unlock();
-
-    /* release our hold on the lock so the main thread can continue cleaning up */
-    thread_rwlock_unlock(source->shutdown_rwlock);
-}
-
-
-static int _compare_clients(void *compare_arg, void *a, void *b)
-{
-    client_t *clienta = (client_t *)a;
-    client_t *clientb = (client_t *)b;
-
-    connection_t *cona = clienta->con;
-    connection_t *conb = clientb->con;
-
-    if (cona->id < conb->id) return -1;
-    if (cona->id > conb->id) return 1;
-
-    return 0;
-}
-
-int source_remove_client(void *key)
-{
-    return 1;
-}
-
-static int _free_client(void *key)
-{
-    client_t *client = (client_t *)key;
-
-    /* if no response has been sent then send a 404 */
-    if (client->respcode == 0)
-        client_send_404 (client, "Mount unavailable");
-    else
-        client_destroy(client);
-    
-    return 1;
-}
-
-static void _parse_audio_info (source_t *source, const char *s)
-{
-    const char *start = s;
-    unsigned int len;
-
-    while (start != NULL && *start != '\0')
-    {
-        if ((s = strchr (start, ';')) == NULL)
-            len = strlen (start);
-        else
-        {
-            len = (int)(s - start);
-            s++; /* skip passed the ';' */
-        }
-        if (len)
-        {
-            char name[100], value[200];
-            char *esc;
-
-            sscanf (start, "%99[^=]=%199[^;\r\n]", name, value);
-            esc = util_url_unescape (value);
-            if (esc)
-            {
-                util_dict_set (source->audio_info, name, esc);
-                stats_event (source->mount, name, esc);
-                free (esc);
-            }
-        }
-        start = s;
-    }
-}
-
-
-/* Apply the mountinfo details to the source */
-static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
-{
-    char *str;
-    int val;
-    http_parser_t *parser = NULL;
-
-    DEBUG1("Applying mount information for \"%s\"", source->mount);
-    avl_tree_rlock (source->client_tree);
-    if (mountinfo)
-    {
-        source->max_listeners = mountinfo->max_listeners;
-        source->fallback_override = mountinfo->fallback_override;
-        source->hidden = mountinfo->hidden;
-    }
-
-    /* if a setting is available in the mount details then use it, else
-     * check the parser details. */
-
-    if (source->client)
-        parser = source->client->parser;
-
-    /* public */
-    if (mountinfo && mountinfo->yp_public >= 0)
-        val = mountinfo->yp_public;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-public");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-pub");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-public");
-            if (str) break;
-            /* handle header from icecast v2 release */
-            str = httpp_getvar (parser, "icy-public");
-            if (str) break;
-            str = "0";
-        } while (0);
-        val = atoi (str);
-    }
-    stats_event_args (source->mount, "public", "%d", val);
-    if (source->yp_public != val)
-    {
-        DEBUG1 ("YP changed to %d", val);
-        if (val)
-            yp_add (source->mount);
-        else
-            yp_remove (source->mount);
-        source->yp_public = val;
-    }
-
-    /* stream name */
-    if (mountinfo && mountinfo->stream_name)
-        str = mountinfo->stream_name;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-name");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-name");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-name");
-            if (str) break;
-            str = "Unspecified name";
-        } while (0);
-    }
-    stats_event (source->mount, "server_name", str);
-
-    /* stream description */
-    if (mountinfo && mountinfo->stream_description)
-        str = mountinfo->stream_description;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-description");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-description");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-description");
-            if (str) break;
-            str = "Unspecified description";
-        } while (0);
-    }
-    stats_event (source->mount, "server_description", str);
-
-    /* stream URL */
-    if (mountinfo && mountinfo->stream_url)
-        str = mountinfo->stream_url;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-url");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-url");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-url");
-            if (str) break;
-        } while (0);
-    }
-    stats_event (source->mount, "server_url", str);
-
-    /* stream genre */
-    if (mountinfo && mountinfo->stream_genre)
-        str = mountinfo->stream_genre;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-genre");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-genre");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-genre");
-            if (str) break;
-            str = "various";
-        } while (0);
-    }
-    stats_event (source->mount, "genre", str);
-
-    /* stream bitrate */
-    if (mountinfo && mountinfo->bitrate)
-        str = mountinfo->bitrate;
-    else
-    {
-        do {
-            str = httpp_getvar (parser, "ice-bitrate");
-            if (str) break;
-            str = httpp_getvar (parser, "icy-br");
-            if (str) break;
-            str = httpp_getvar (parser, "x-audiocast-bitrate");
-        } while (0);
-    }
-    stats_event (source->mount, "bitrate", str);
-
-    /* handle MIME-type */
-    if (mountinfo && mountinfo->type)
-        stats_event (source->mount, "server_type", mountinfo->type);
-    else
-        if (source->format)
-            stats_event (source->mount, "server_type", source->format->contenttype);
-
-    if (mountinfo && mountinfo->subtype)
-        stats_event (source->mount, "subtype", mountinfo->subtype);
-
-    if (mountinfo && mountinfo->auth)
-        stats_event (source->mount, "authenticator", mountinfo->auth->type);
-    else
-        stats_event (source->mount, "authenticator", NULL);
-
-    if (mountinfo && mountinfo->fallback_mount)
-    {
-        char *mount = source->fallback_mount;
-        source->fallback_mount = strdup (mountinfo->fallback_mount);
-        free (mount);
-    }
-    else
-        source->fallback_mount = NULL;
-
-    if (mountinfo && mountinfo->dumpfile)
-    {
-        char *filename = source->dumpfilename;
-        source->dumpfilename = strdup (mountinfo->dumpfile);
-        free (filename);
-    }
-    else
-        source->dumpfilename = NULL;
-
-    if (source->intro_file)
-    {
-        fclose (source->intro_file);
-        source->intro_file = NULL;
-    }
-    if (mountinfo && mountinfo->intro_filename)
-    {
-        ice_config_t *config = config_get_config_unlocked ();
-        unsigned int len  = strlen (config->webroot_dir) +
-            strlen (mountinfo->intro_filename) + 2;
-        char *path = malloc (len);
-        if (path)
-        {
-            FILE *f;
-            snprintf (path, len, "%s" PATH_SEPARATOR "%s", config->webroot_dir,
-                    mountinfo->intro_filename);
-
-            f = fopen (path, "rb");
-            if (f)
-                source->intro_file = f;
-            else
-                WARN2 ("Cannot open intro file \"%s\": %s", path, strerror(errno));
-            free (path);
-        }
-    }
-
-    if (mountinfo && mountinfo->queue_size_limit)
-        source->queue_size_limit = mountinfo->queue_size_limit;
-
-    if (mountinfo && mountinfo->source_timeout)
-        source->timeout = mountinfo->source_timeout;
-
-    if (mountinfo && mountinfo->burst_size >= 0)
-        source->burst_size = (unsigned int)mountinfo->burst_size;
-
-    if (mountinfo && mountinfo->fallback_when_full)
-        source->fallback_when_full = mountinfo->fallback_when_full;
-
-    if (source->format && source->format->apply_settings)
-        source->format->apply_settings (source->client, source->format, mountinfo);
-    avl_tree_unlock (source->client_tree);
-}
-
-
-/* update the specified source with details from the config or mount.
- * mountinfo can be NULL in which case default settings should be taken
- */
-void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo)
-{
-    /*  skip if source is a fallback to file */
-    if (source->running && source->client == NULL)
-    {
-        stats_event_hidden (source->mount, NULL, 1);
-        return;
-    }
-    /* set global settings first */
-    source->queue_size_limit = config->queue_size_limit;
-    source->timeout = config->source_timeout;
-    source->burst_size = config->burst_size;
-
-    source_apply_mount (source, mountinfo);
-
-    if (source->fallback_mount)
-        DEBUG1 ("fallback %s", source->fallback_mount);
-    if (mountinfo && mountinfo->intro_filename)
-        DEBUG1 ("intro file is %s", mountinfo->intro_filename);
-    if (source->dumpfilename)
-        DEBUG1 ("Dumping stream to %s", source->dumpfilename);
-    if (mountinfo && mountinfo->on_connect)
-        DEBUG1 ("connect script \"%s\"", mountinfo->on_connect);
-    if (mountinfo && mountinfo->on_disconnect)
-        DEBUG1 ("disconnect script \"%s\"", mountinfo->on_disconnect);
-    if (source->on_demand)
-    {
-        DEBUG0 ("on_demand set");
-        stats_event (source->mount, "on_demand", "1");
-    }
-    else
-        stats_event (source->mount, "on_demand", NULL);
-
-    if (source->hidden)
-    {
-        stats_event_hidden (source->mount, NULL, 1);
-        DEBUG0 ("hidden from xsl");
-    }
-    else
-        stats_event_hidden (source->mount, NULL, 0);
-
-    if (source->max_listeners == -1)
-        stats_event (source->mount, "max_listeners", "unlimited");
-    else
-    {
-        char buf [10];
-        snprintf (buf, sizeof (buf), "%ld", source->max_listeners);
-        stats_event (source->mount, "max_listeners", buf);
-    }
-    DEBUG1 ("public set to %d", source->yp_public);
-    DEBUG1 ("max listeners to %ld", source->max_listeners);
-    DEBUG1 ("queue size to %u", source->queue_size_limit);
-    DEBUG1 ("burst size to %u", source->burst_size);
-    DEBUG1 ("source timeout to %u", source->timeout);
-    DEBUG1 ("fallback_when_full to %u", source->fallback_when_full);
-}
-
-
-void *source_client_thread (void *arg)
-{
-    source_t *source = arg;
-
-    stats_event_inc(NULL, "source_client_connections");
-    stats_event (source->mount, "listeners", "0");
-
-    source_main (source);
-
-    source_free_source (source);
-    slave_rebuild_mounts();
-
-    return NULL;
-}
-
-
-void source_client_callback (client_t *client, void *arg)
-{
-    const char *agent;
-    source_t *source = arg;
-    refbuf_t *old_data = client->refbuf;
-
-    if (client->con->error)
-    {
-        global_lock();
-        global.sources--;
-        global_unlock();
-        source_clear_source (source);
-        source_free_source (source);
-        return;
-    }
-    client->refbuf = old_data->associated;
-    old_data->associated = NULL;
-    refbuf_release (old_data);
-    stats_event (source->mount, "source_ip", source->client->con->ip);
-    agent = httpp_getvar (source->client->parser, "user-agent");
-    if (agent)
-        stats_event (source->mount, "user_agent", agent);
-
-    thread_create ("Source Thread", source_client_thread,
-            source, THREAD_DETACHED);
-}
-
-
-#ifndef _WIN32
-static void source_run_script (char *command, char *mountpoint)
-{
-    pid_t pid, external_pid;
-
-    /* do a fork twice so that the command has init as parent */
-    external_pid = fork();
-    switch (external_pid)
-    {
-        case 0:
-            switch (pid = fork ())
-            {
-                case -1:
-                    ERROR2 ("Unable to fork %s (%s)", command, strerror (errno));
-                    break;
-                case 0:  /* child */
-                    DEBUG1 ("Starting command %s", command);
-                    execl (command, command, mountpoint, (char *)NULL);
-                    ERROR2 ("Unable to run command %s (%s)", command, strerror (errno));
-                    exit(0);
-                default: /* parent */
-                    break;
-            }
-            exit (0);
-        case -1:
-            ERROR1 ("Unable to fork %s", strerror (errno));
-            break;
-        default: /* parent */
-            waitpid (external_pid, NULL, 0);
-            break;
-    }
-}
-#endif
-
-
-static void *source_fallback_file (void *arg)
-{
-    char *mount = arg;
-    char *type;
-    char *path;
-    unsigned int len;
-    FILE *file = NULL;
-    source_t *source = NULL;
-    ice_config_t *config;
-    http_parser_t *parser;
-
-    do
-    {
-        if (mount == NULL || mount[0] != '/')
-            break;
-        config = config_get_config();
-        len  = strlen (config->webroot_dir) + strlen (mount) + 1;
-        path = malloc (len);
-        if (path)
-            snprintf (path, len, "%s%s", config->webroot_dir, mount);
-        
-        config_release_config ();
-        if (path == NULL)
-            break;
-
-        file = fopen (path, "rb");
-        if (file == NULL)
-        {
-            DEBUG1 ("unable to open file \"%s\"", path);
-            free (path);
-            break;
-        }
-        free (path);
-        source = source_reserve (mount);
-        if (source == NULL)
-        {
-            DEBUG1 ("mountpoint \"%s\" already reserved", mount);
-            break;
-        }
-        type = fserve_content_type (mount);
-        parser = httpp_create_parser();
-        httpp_initialize (parser, NULL);
-        httpp_setvar (parser, "content-type", type);
-
-        source->hidden = 1;
-        source->yp_public = 0;
-        source->intro_file = file;
-        source->parser = parser;
-        file = NULL;
-
-        if (connection_complete_source (source, 0) < 0)
-            break;
-        source_client_thread (source);
-        httpp_destroy (parser);
-    } while (0);
-    if (file)
-        fclose (file);
-    free (mount);
-    return NULL;
-}
-
-
-/* rescan the mount list, so that xsl files are updated to show
- * unconnected but active fallback mountpoints
- */
-void source_recheck_mounts (int update_all)
-{
-    ice_config_t *config = config_get_config();
-    mount_proxy *mount = config->mounts;
-
-    avl_tree_rlock (global.source_tree);
-
-    stats_clear_virtual_mounts ();
-
-    while (mount)
-    {
-        source_t *source = source_find_mount (mount->mountname);
-
-        if (source)
-        {
-            source = source_find_mount_raw (mount->mountname);
-            stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s",
-                    config->hostname, config->port, mount->mountname);
-            if (source && update_all)
-            {
-                mount_proxy *mountinfo = config_find_mount (config, source->mount);
-                source_update_settings (config, source, mountinfo);
-            }
-            else
-            {
-                stats_event_hidden (mount->mountname, NULL, mount->hidden);
-                stats_event (mount->mountname, "listeners", "0");
-                if (mount->max_listeners < 0)
-                    stats_event (mount->mountname, "max_listeners", "unlimited");
-                else
-                    stats_event_args (mount->mountname, "max_listeners", "%d", mount->max_listeners);
-            }
-        }
-        else
-            stats_event (mount->mountname, NULL, NULL);
-
-        /* check for fallback to file */
-        if (global.running == ICE_RUNNING && mount->fallback_mount)
-        {
-            source_t *fallback = source_find_mount (mount->fallback_mount);
-            if (fallback == NULL)
-            {
-                thread_create ("Fallback file thread", source_fallback_file,
-                        strdup (mount->fallback_mount), THREAD_DETACHED);
-            }
-        }
-
-        mount = mount->next;
-    }
-    avl_tree_unlock (global.source_tree);
-    config_release_config();
-}
-

Copied: icecast/tags/icecast-2.3.2/src/source.c (from rev 14934, icecast/trunk/icecast/src/source.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/source.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/source.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1417 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ogg/ogg.h>
+#include <errno.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#else
+#include <winsock2.h>
+#include <windows.h>
+#define snprintf _snprintf
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "cfgfile.h"
+#include "util.h"
+#include "source.h"
+#include "format.h"
+#include "fserve.h"
+#include "auth.h"
+#include "compat.h"
+
+#undef CATMODULE
+#define CATMODULE "source"
+
+#define MAX_FALLBACK_DEPTH 10
+
+mutex_t move_clients_mutex;
+
+/* avl tree helper */
+static int _compare_clients(void *compare_arg, void *a, void *b);
+static int _free_client(void *key);
+static void _parse_audio_info (source_t *source, const char *s);
+static void source_shutdown (source_t *source);
+#ifdef _WIN32
+#define source_run_script(x,y)  WARN0("on [dis]connect scripts disabled");
+#else
+static void source_run_script (char *command, char *mountpoint);
+#endif
+
+/* Allocate a new source with the stated mountpoint, if one already
+ * exists with that mountpoint in the global source tree then return
+ * NULL.
+ */
+source_t *source_reserve (const char *mount)
+{
+    source_t *src = NULL;
+
+    if(mount[0] != '/')
+        WARN1("Source at \"%s\" does not start with '/', clients will be "
+                "unable to connect", mount);
+
+    do
+    {
+        avl_tree_wlock (global.source_tree);
+        src = source_find_mount_raw (mount);
+        if (src)
+        {
+            src = NULL;
+            break;
+        }
+
+        src = calloc (1, sizeof(source_t));
+        if (src == NULL)
+            break;
+
+        src->client_tree = avl_tree_new(_compare_clients, NULL);
+        src->pending_tree = avl_tree_new(_compare_clients, NULL);
+
+        /* make duplicates for strings or similar */
+        src->mount = strdup (mount);
+        src->max_listeners = -1;
+
+        avl_insert (global.source_tree, src);
+
+    } while (0);
+
+    avl_tree_unlock (global.source_tree);
+    return src;
+}
+
+
+/* Find a mount with this raw name - ignoring fallbacks. You should have the
+ * global source tree locked to call this.
+ */
+source_t *source_find_mount_raw(const char *mount)
+{
+    source_t *source;
+    avl_node *node;
+    int cmp;
+
+    if (!mount) {
+        return NULL;
+    }
+    /* get the root node */
+    node = global.source_tree->root->right;
+    
+    while (node) {
+        source = (source_t *)node->key;
+        cmp = strcmp(mount, source->mount);
+        if (cmp < 0) 
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return source;
+    }
+    
+    /* didn't find it */
+    return NULL;
+}
+
+
+/* Search for mount, if the mount is there but not currently running then
+ * check the fallback, and so on.  Must have a global source lock to call
+ * this function.
+ */
+source_t *source_find_mount (const char *mount)
+{
+    source_t *source = NULL;
+    ice_config_t *config;
+    mount_proxy *mountinfo;
+    int depth = 0;
+
+    config = config_get_config();
+    while (mount && depth < MAX_FALLBACK_DEPTH)
+    {
+        source = source_find_mount_raw(mount);
+
+        if (source)
+        {
+            if (source->running || source->on_demand)
+                break;
+        }
+
+        /* we either have a source which is not active (relay) or no source
+         * at all. Check the mounts list for fallback settings
+         */
+        mountinfo = config_find_mount (config, mount);
+        source = NULL;
+
+        if (mountinfo == NULL)
+            break;
+        mount = mountinfo->fallback_mount;
+        depth++;
+    }
+
+    config_release_config();
+    return source;
+}
+
+
+int source_compare_sources(void *arg, void *a, void *b)
+{
+    source_t *srca = (source_t *)a;
+    source_t *srcb = (source_t *)b;
+
+    return strcmp(srca->mount, srcb->mount);
+}
+
+
+void source_clear_source (source_t *source)
+{
+    DEBUG1 ("clearing source \"%s\"", source->mount);
+
+    avl_tree_wlock (source->client_tree);
+    client_destroy(source->client);
+    source->client = NULL;
+    source->parser = NULL;
+    source->con = NULL;
+
+    /* log bytes read in access log */
+    if (source->client && source->format)
+        source->client->con->sent_bytes = source->format->read_bytes;
+
+    if (source->dumpfile)
+    {
+        INFO1 ("Closing dumpfile for %s", source->mount);
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+    }
+
+    if (source->listeners)
+        stats_event_sub (NULL, "listeners", source->listeners);
+
+    /* lets kick off any clients that are left on here */
+    while (avl_get_first (source->client_tree))
+    {
+        avl_delete (source->client_tree,
+                avl_get_first (source->client_tree)->key, _free_client);
+    }
+
+    avl_tree_wlock (source->pending_tree);
+    while (avl_get_first (source->pending_tree))
+    {
+        avl_delete (source->pending_tree,
+                avl_get_first(source->pending_tree)->key, _free_client);
+    }
+    avl_tree_unlock (source->pending_tree);
+
+    if (source->format && source->format->free_plugin)
+        source->format->free_plugin (source->format);
+    source->format = NULL;
+
+    /* Lets clear out the source queue too */
+    while (source->stream_data)
+    {
+        refbuf_t *p = source->stream_data;
+        source->stream_data = p->next;
+        /* can be referenced by burst handler as well */
+        while (p->_count > 1)
+            refbuf_release (p);
+        refbuf_release (p);
+    }
+    source->stream_data_tail = NULL;
+
+    source->burst_point = NULL;
+    source->burst_size = 0;
+    source->burst_offset = 0;
+    source->queue_size = 0;
+    source->queue_size_limit = 0;
+    source->listeners = 0;
+    source->max_listeners = -1;
+    source->prev_listeners = 0;
+    source->hidden = 0;
+    source->shoutcast_compat = 0;
+    source->client_stats_update = 0;
+    util_dict_free (source->audio_info);
+    source->audio_info = NULL;
+
+    free(source->fallback_mount);
+    source->fallback_mount = NULL;
+
+    free(source->dumpfilename);
+    source->dumpfilename = NULL;
+
+    if (source->intro_file)
+    {
+        fclose (source->intro_file);
+        source->intro_file = NULL;
+    }
+
+    source->on_demand_req = 0;
+    avl_tree_unlock (source->client_tree);
+}
+
+
+/* Remove the provided source from the global tree and free it */
+void source_free_source (source_t *source)
+{
+    DEBUG1 ("freeing source \"%s\"", source->mount);
+    avl_tree_wlock (global.source_tree);
+    avl_delete (global.source_tree, source, NULL);
+    avl_tree_unlock (global.source_tree);
+
+    avl_tree_free(source->pending_tree, _free_client);
+    avl_tree_free(source->client_tree, _free_client);
+
+    /* make sure all YP entries have gone */
+    yp_remove (source->mount);
+
+    free (source->mount);
+    free (source);
+
+    return;
+}
+
+
+client_t *source_find_client(source_t *source, int id)
+{
+    client_t fakeclient;
+    void *result;
+    connection_t fakecon;
+
+    fakeclient.con = &fakecon;
+    fakeclient.con->id = id;
+
+    avl_tree_rlock(source->client_tree);
+    if(avl_get_by_key(source->client_tree, &fakeclient, &result) == 0)
+    {
+        avl_tree_unlock(source->client_tree);
+        return result;
+    }
+
+    avl_tree_unlock(source->client_tree);
+    return NULL;
+}
+
+
+/* Move clients from source to dest provided dest is running
+ * and that the stream format is the same.
+ * The only lock that should be held when this is called is the
+ * source tree lock
+ */
+void source_move_clients (source_t *source, source_t *dest)
+{
+    unsigned long count = 0;
+    if (strcmp (source->mount, dest->mount) == 0)
+    {
+        WARN1 ("src and dst are the same \"%s\", skipping", source->mount);
+        return;
+    }
+    /* we don't want the two write locks to deadlock in here */
+    thread_mutex_lock (&move_clients_mutex);
+
+    /* if the destination is not running then we can't move clients */
+
+    if (dest->running == 0 && dest->on_demand == 0)
+    {
+        WARN1 ("destination mount %s not running, unable to move clients ", dest->mount);
+        thread_mutex_unlock (&move_clients_mutex);
+        return;
+    }
+
+    avl_tree_wlock (dest->pending_tree);
+    do
+    {
+        client_t *client;
+
+        /* we need to move the client and pending trees - we must take the
+         * locks in this order to avoid deadlocks */
+        avl_tree_wlock (source->client_tree);
+        avl_tree_wlock (source->pending_tree);
+
+        if (source->on_demand == 0 && source->format == NULL)
+        {
+            INFO1 ("source mount %s is not available", source->mount);
+            break;
+        }
+        if (source->format && dest->format)
+        {
+            if (source->format->type != dest->format->type)
+            {
+                WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
+                break;
+            }
+        }
+
+        while (1)
+        {
+            avl_node *node = avl_get_first (source->pending_tree);
+            if (node == NULL)
+                break;
+            client = (client_t *)(node->key);
+            avl_delete (source->pending_tree, client, NULL);
+
+            /* when switching a client to a different queue, be wary of the 
+             * refbuf it's referring to, if it's http headers then we need
+             * to write them so don't release it.
+             */
+            if (client->check_buffer != format_check_http_buffer)
+            {
+                client_set_queue (client, NULL);
+                client->check_buffer = format_check_file_buffer;
+                if (source->con == NULL)
+                    client->intro_offset = -1;
+            }
+
+            avl_insert (dest->pending_tree, (void *)client);
+            count++;
+        }
+
+        while (1)
+        {
+            avl_node *node = avl_get_first (source->client_tree);
+            if (node == NULL)
+                break;
+
+            client = (client_t *)(node->key);
+            avl_delete (source->client_tree, client, NULL);
+
+            /* when switching a client to a different queue, be wary of the 
+             * refbuf it's referring to, if it's http headers then we need
+             * to write them so don't release it.
+             */
+            if (client->check_buffer != format_check_http_buffer)
+            {
+                client_set_queue (client, NULL);
+                client->check_buffer = format_check_file_buffer;
+                if (source->con == NULL)
+                    client->intro_offset = -1;
+            }
+            avl_insert (dest->pending_tree, (void *)client);
+            count++;
+        }
+        INFO2 ("passing %lu listeners to \"%s\"", count, dest->mount);
+
+        source->listeners = 0;
+        stats_event (source->mount, "listeners", "0");
+
+    } while (0);
+
+    avl_tree_unlock (source->pending_tree);
+    avl_tree_unlock (source->client_tree);
+
+    /* see if we need to wake up an on-demand relay */
+    if (dest->running == 0 && dest->on_demand && count)
+        dest->on_demand_req = 1;
+
+    avl_tree_unlock (dest->pending_tree);
+    thread_mutex_unlock (&move_clients_mutex);
+}
+
+
+/* get some data from the source. The stream data is placed in a refbuf
+ * and sent back, however NULL is also valid as in the case of a short
+ * timeout and there's no data pending.
+ */
+static refbuf_t *get_next_buffer (source_t *source)
+{
+    refbuf_t *refbuf = NULL;
+    int delay = 250;
+
+    if (source->short_delay)
+        delay = 0;
+    while (global.running == ICE_RUNNING && source->running)
+    {
+        int fds = 0;
+        time_t current = time (NULL);
+
+        if (source->client)
+            fds = util_timed_wait_for_fd (source->con->sock, delay);
+        else
+        {
+            thread_sleep (delay*1000);
+            source->last_read = current;
+        }
+
+        if (current >= source->client_stats_update)
+        {
+            stats_event_args (source->mount, "total_bytes_read",
+                    "%"PRIu64, source->format->read_bytes);
+            stats_event_args (source->mount, "total_bytes_sent",
+                    "%"PRIu64, source->format->sent_bytes);
+            source->client_stats_update = current + 5;
+        }
+        if (fds < 0)
+        {
+            if (! sock_recoverable (sock_error()))
+            {
+                WARN0 ("Error while waiting on socket, Disconnecting source");
+                source->running = 0;
+            }
+            break;
+        }
+        if (fds == 0)
+        {
+            if (source->last_read + (time_t)source->timeout < current)
+            {
+                DEBUG3 ("last %ld, timeout %d, now %ld", (long)source->last_read,
+                        source->timeout, (long)current);
+                WARN0 ("Disconnecting source due to socket timeout");
+                source->running = 0;
+            }
+            break;
+        }
+        source->last_read = current;
+        refbuf = source->format->get_buffer (source);
+        if (source->client->con && source->client->con->error)
+        {
+            INFO1 ("End of Stream %s", source->mount);
+            source->running = 0;
+            continue;
+        }
+        if (refbuf)
+            break;
+    }
+
+    return refbuf;
+}
+
+
+/* general send routine per listener.  The deletion_expected tells us whether
+ * the last in the queue is about to disappear, so if this client is still
+ * referring to it after writing then drop the client as it's fallen too far
+ * behind 
+ */ 
+static void send_to_listener (source_t *source, client_t *client, int deletion_expected)
+{
+    int bytes;
+    int loop = 10;   /* max number of iterations in one go */
+    int total_written = 0;
+
+    while (1)
+    {
+        /* check for limited listener time */
+        if (client->con->discon_time)
+            if (time(NULL) >= client->con->discon_time)
+            {
+                INFO1 ("time limit reached for client #%lu", client->con->id);
+                client->con->error = 1;
+            }
+
+        /* jump out if client connection has died */
+        if (client->con->error)
+            break;
+
+        /* lets not send too much to one client in one go, but don't
+           sleep for too long if more data can be sent */
+        if (total_written > 20000 || loop == 0)
+        {
+            if (client->check_buffer != format_check_file_buffer)
+                source->short_delay = 1;
+            break;
+        }
+
+        loop--;
+
+        if (client->check_buffer (source, client) < 0)
+            break;
+
+        bytes = client->write_to_client (client);
+        if (bytes <= 0)
+            break;  /* can't write any more */
+
+        total_written += bytes;
+    }
+    source->format->sent_bytes += total_written;
+
+    /* the refbuf referenced at head (last in queue) may be marked for deletion
+     * if so, check to see if this client is still referring to it */
+    if (deletion_expected && client->refbuf && client->refbuf == source->stream_data)
+    {
+        INFO2 ("Client %lu (%s) has fallen too far behind, removing",
+                client->con->id, client->con->ip);
+        stats_event_inc (source->mount, "slow_listeners");
+        client->con->error = 1;
+    }
+}
+
+
+/* Perform any initialisation just before the stream data is processed, the header
+ * info is processed by now and the format details are setup
+ */
+static void source_init (source_t *source)
+{
+    ice_config_t *config = config_get_config();
+    char *listenurl;
+    const char *str;
+    int listen_url_size;
+    mount_proxy *mountinfo;
+
+    /* 6 for max size of port */
+    listen_url_size = strlen("http://") + strlen(config->hostname) +
+        strlen(":") + 6 + strlen(source->mount) + 1;
+
+    listenurl = malloc (listen_url_size);
+    memset (listenurl, '\000', listen_url_size);
+    snprintf (listenurl, listen_url_size, "http://%s:%d%s",
+            config->hostname, config->port, source->mount);
+    config_release_config();
+
+    str = httpp_getvar(source->parser, "ice-audio-info");
+    source->audio_info = util_dict_new();
+    if (str)
+    {
+        _parse_audio_info (source, str);
+        stats_event (source->mount, "audio_info", str);
+    }
+
+    stats_event (source->mount, "listenurl", listenurl);
+
+    free(listenurl);
+
+    if (source->dumpfilename != NULL)
+    {
+        source->dumpfile = fopen (source->dumpfilename, "ab");
+        if (source->dumpfile == NULL)
+        {
+            WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.",
+                    source->dumpfilename, strerror(errno));
+        }
+    }
+
+    /* grab a read lock, to make sure we get a chance to cleanup */
+    thread_rwlock_rlock (source->shutdown_rwlock);
+
+    /* start off the statistics */
+    source->listeners = 0;
+    stats_event_inc (NULL, "source_total_connections");
+    stats_event (source->mount, "slow_listeners", "0");
+    stats_event_args (source->mount, "listeners", "%lu", source->listeners);
+    stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
+    stats_event_time (source->mount, "stream_start");
+
+    DEBUG0("Source creation complete");
+    source->last_read = time (NULL);
+    source->prev_listeners = -1;
+    source->running = 1;
+
+    mountinfo = config_find_mount (config_get_config(), source->mount);
+    if (mountinfo)
+    {
+        if (mountinfo->on_connect)
+            source_run_script (mountinfo->on_connect, source->mount);
+        auth_stream_start (mountinfo, source->mount);
+    }
+    config_release_config();
+
+    /*
+    ** Now, if we have a fallback source and override is on, we want
+    ** to steal its clients, because it means we've come back online
+    ** after a failure and they should be gotten back from the waiting
+    ** loop or jingle track or whatever the fallback is used for
+    */
+
+    if (source->fallback_override && source->fallback_mount)
+    {
+        source_t *fallback_source;
+
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount(source->fallback_mount);
+
+        if (fallback_source)
+            source_move_clients (fallback_source, source);
+
+        avl_tree_unlock(global.source_tree);
+    }
+}
+
+
+void source_main (source_t *source)
+{
+    refbuf_t *refbuf;
+    client_t *client;
+    avl_node *client_node;
+
+    source_init (source);
+
+    while (global.running == ICE_RUNNING && source->running) {
+        int remove_from_q;
+
+        refbuf = get_next_buffer (source);
+
+        remove_from_q = 0;
+        source->short_delay = 0;
+
+        if (refbuf)
+        {
+            /* append buffer to the in-flight data queue,  */
+            if (source->stream_data == NULL)
+            {
+                source->stream_data = refbuf;
+                source->burst_point = refbuf;
+            }
+            if (source->stream_data_tail)
+                source->stream_data_tail->next = refbuf;
+            source->stream_data_tail = refbuf;
+            source->queue_size += refbuf->len;
+            /* new buffer is referenced for burst */
+            refbuf_addref (refbuf);
+
+            /* new data on queue, so check the burst point */
+            source->burst_offset += refbuf->len;
+            while (source->burst_offset > source->burst_size)
+            {
+                refbuf_t *to_release = source->burst_point;
+
+                if (to_release->next)
+                {
+                    source->burst_point = to_release->next;
+                    source->burst_offset -= to_release->len;
+                    refbuf_release (to_release);
+                    continue;
+                }
+                break;
+            }
+
+            /* save stream to file */
+            if (source->dumpfile && source->format->write_buf_to_file)
+                source->format->write_buf_to_file (source, refbuf);
+        }
+        /* lets see if we have too much data in the queue, but don't remove it until later */
+        if (source->queue_size > source->queue_size_limit)
+            remove_from_q = 1;
+
+        /* acquire write lock on client_tree */
+        avl_tree_wlock(source->client_tree);
+
+        client_node = avl_get_first(source->client_tree);
+        while (client_node) {
+            client = (client_t *)client_node->key;
+
+            send_to_listener (source, client, remove_from_q);
+
+            if (client->con->error) {
+                client_node = avl_get_next(client_node);
+                avl_delete(source->client_tree, (void *)client, _free_client);
+                source->listeners--;
+                stats_event_dec (NULL, "listeners");
+                DEBUG0("Client removed");
+                continue;
+            }
+            client_node = avl_get_next(client_node);
+        }
+
+        /* acquire write lock on pending_tree */
+        avl_tree_wlock(source->pending_tree);
+
+        /** add pending clients **/
+        client_node = avl_get_first(source->pending_tree);
+        while (client_node) {
+
+            if(source->max_listeners != -1 && 
+                    source->listeners >= (unsigned long)source->max_listeners) 
+            {
+                /* The common case is caught in the main connection handler,
+                 * this deals with rarer cases (mostly concerning fallbacks)
+                 * and doesn't give the listening client any information about
+                 * why they were disconnected
+                 */
+                client = (client_t *)client_node->key;
+                client_node = avl_get_next(client_node);
+                avl_delete(source->pending_tree, (void *)client, _free_client);
+
+                INFO0("Client deleted, exceeding maximum listeners for this "
+                        "mountpoint.");
+                continue;
+            }
+            
+            /* Otherwise, the client is accepted, add it */
+            avl_insert(source->client_tree, client_node->key);
+
+            source->listeners++;
+            DEBUG0("Client added");
+            stats_event_inc(source->mount, "connections");
+
+            client_node = avl_get_next(client_node);
+        }
+
+        /** clear pending tree **/
+        while (avl_get_first(source->pending_tree)) {
+            avl_delete(source->pending_tree, 
+                    avl_get_first(source->pending_tree)->key, 
+                    source_remove_client);
+        }
+
+        /* release write lock on pending_tree */
+        avl_tree_unlock(source->pending_tree);
+
+        /* update the stats if need be */
+        if (source->listeners != source->prev_listeners)
+        {
+            source->prev_listeners = source->listeners;
+            INFO2("listener count on %s now %lu", source->mount, source->listeners);
+            if (source->listeners > source->peak_listeners)
+            {
+                source->peak_listeners = source->listeners;
+                stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
+            }
+            stats_event_args (source->mount, "listeners", "%lu", source->listeners);
+            if (source->listeners == 0 && source->on_demand)
+                source->running = 0;
+        }
+
+        /* lets reduce the queue, any lagging clients should of been
+         * terminated by now
+         */
+        if (source->stream_data)
+        {
+            /* normal unreferenced queue data will have a refcount 1, but
+             * burst queue data will be at least 2, active clients will also
+             * increase refcount */
+            while (source->stream_data->_count == 1)
+            {
+                refbuf_t *to_go = source->stream_data;
+
+                if (to_go->next == NULL || source->burst_point == to_go)
+                {
+                    /* this should not happen */
+                    ERROR0 ("queue state is unexpected");
+                    source->running = 0;
+                    break;
+                }
+                source->stream_data = to_go->next;
+                source->queue_size -= to_go->len;
+                refbuf_release (to_go);
+            }
+        }
+
+        /* release write lock on client_tree */
+        avl_tree_unlock(source->client_tree);
+    }
+    source_shutdown (source);
+}
+
+
+static void source_shutdown (source_t *source)
+{
+    mount_proxy *mountinfo;
+
+    source->running = 0;
+    INFO1("Source \"%s\" exiting", source->mount);
+
+    mountinfo = config_find_mount (config_get_config(), source->mount);
+    if (mountinfo)
+    {
+        if (mountinfo->on_disconnect)
+            source_run_script (mountinfo->on_disconnect, source->mount);
+        auth_stream_end (mountinfo, source->mount);
+    }
+    config_release_config();
+
+    /* we have de-activated the source now, so no more clients will be
+     * added, now move the listeners we have to the fallback (if any)
+     */
+    if (source->fallback_mount)
+    {
+        source_t *fallback_source;
+
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount (source->fallback_mount);
+
+        if (fallback_source != NULL)
+            source_move_clients (source, fallback_source);
+
+        avl_tree_unlock (global.source_tree);
+    }
+
+    /* delete this sources stats */
+    stats_event(source->mount, NULL, NULL);
+
+    /* we don't remove the source from the tree here, it may be a relay and
+       therefore reserved */
+    source_clear_source (source);
+
+    global_lock();
+    global.sources--;
+    stats_event_args (NULL, "sources", "%d", global.sources);
+    global_unlock();
+
+    /* release our hold on the lock so the main thread can continue cleaning up */
+    thread_rwlock_unlock(source->shutdown_rwlock);
+}
+
+
+static int _compare_clients(void *compare_arg, void *a, void *b)
+{
+    client_t *clienta = (client_t *)a;
+    client_t *clientb = (client_t *)b;
+
+    connection_t *cona = clienta->con;
+    connection_t *conb = clientb->con;
+
+    if (cona->id < conb->id) return -1;
+    if (cona->id > conb->id) return 1;
+
+    return 0;
+}
+
+int source_remove_client(void *key)
+{
+    return 1;
+}
+
+static int _free_client(void *key)
+{
+    client_t *client = (client_t *)key;
+
+    /* if no response has been sent then send a 404 */
+    if (client->respcode == 0)
+        client_send_404 (client, "Mount unavailable");
+    else
+        client_destroy(client);
+    
+    return 1;
+}
+
+static void _parse_audio_info (source_t *source, const char *s)
+{
+    const char *start = s;
+    unsigned int len;
+
+    while (start != NULL && *start != '\0')
+    {
+        if ((s = strchr (start, ';')) == NULL)
+            len = strlen (start);
+        else
+        {
+            len = (int)(s - start);
+            s++; /* skip passed the ';' */
+        }
+        if (len)
+        {
+            char name[100], value[200];
+            char *esc;
+
+            sscanf (start, "%99[^=]=%199[^;\r\n]", name, value);
+            esc = util_url_unescape (value);
+            if (esc)
+            {
+                util_dict_set (source->audio_info, name, esc);
+                stats_event (source->mount, name, esc);
+                free (esc);
+            }
+        }
+        start = s;
+    }
+}
+
+
+/* Apply the mountinfo details to the source */
+static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
+{
+    const char *str;
+    int val;
+    http_parser_t *parser = NULL;
+
+    DEBUG1("Applying mount information for \"%s\"", source->mount);
+    avl_tree_rlock (source->client_tree);
+    stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
+
+    if (mountinfo)
+    {
+        source->max_listeners = mountinfo->max_listeners;
+        source->fallback_override = mountinfo->fallback_override;
+        source->hidden = mountinfo->hidden;
+    }
+
+    /* if a setting is available in the mount details then use it, else
+     * check the parser details. */
+
+    if (source->client)
+        parser = source->client->parser;
+
+    /* to be done before possible non-utf8 stats */
+    if (source->format && source->format->apply_settings)
+        source->format->apply_settings (source->client, source->format, mountinfo);
+
+    /* public */
+    if (mountinfo && mountinfo->yp_public >= 0)
+        val = mountinfo->yp_public;
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-public");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-pub");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-public");
+            if (str) break;
+            /* handle header from icecast v2 release */
+            str = httpp_getvar (parser, "icy-public");
+            if (str) break;
+            str = "0";
+        } while (0);
+        val = atoi (str);
+    }
+    stats_event_args (source->mount, "public", "%d", val);
+    if (source->yp_public != val)
+    {
+        DEBUG1 ("YP changed to %d", val);
+        if (val)
+            yp_add (source->mount);
+        else
+            yp_remove (source->mount);
+        source->yp_public = val;
+    }
+
+    /* stream name */
+    if (mountinfo && mountinfo->stream_name)
+        stats_event (source->mount, "server_name", mountinfo->stream_name);
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-name");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-name");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-name");
+            if (str) break;
+            str = "Unspecified name";
+        } while (0);
+        if (source->format)
+            stats_event_conv (source->mount, "server_name", str, source->format->charset);
+    }
+
+    /* stream description */
+    if (mountinfo && mountinfo->stream_description)
+        stats_event (source->mount, "server_description", mountinfo->stream_description);
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-description");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-description");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-description");
+            if (str) break;
+            str = "Unspecified description";
+        } while (0);
+        if (source->format)
+            stats_event_conv (source->mount, "server_description", str, source->format->charset);
+    }
+
+    /* stream URL */
+    if (mountinfo && mountinfo->stream_url)
+        stats_event (source->mount, "server_url", mountinfo->stream_url);
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-url");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-url");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-url");
+            if (str) break;
+        } while (0);
+        if (str && source->format)
+            stats_event_conv (source->mount, "server_url", str, source->format->charset);
+    }
+
+    /* stream genre */
+    if (mountinfo && mountinfo->stream_genre)
+        stats_event (source->mount, "genre", mountinfo->stream_genre);
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-genre");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-genre");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-genre");
+            if (str) break;
+            str = "various";
+        } while (0);
+        if (source->format)
+            stats_event_conv (source->mount, "genre", str, source->format->charset);
+    }
+
+    /* stream bitrate */
+    if (mountinfo && mountinfo->bitrate)
+        str = mountinfo->bitrate;
+    else
+    {
+        do {
+            str = httpp_getvar (parser, "ice-bitrate");
+            if (str) break;
+            str = httpp_getvar (parser, "icy-br");
+            if (str) break;
+            str = httpp_getvar (parser, "x-audiocast-bitrate");
+        } while (0);
+    }
+    stats_event (source->mount, "bitrate", str);
+
+    /* handle MIME-type */
+    if (mountinfo && mountinfo->type)
+        stats_event (source->mount, "server_type", mountinfo->type);
+    else
+        if (source->format)
+            stats_event (source->mount, "server_type", source->format->contenttype);
+
+    if (mountinfo && mountinfo->subtype)
+        stats_event (source->mount, "subtype", mountinfo->subtype);
+
+    if (mountinfo && mountinfo->auth)
+        stats_event (source->mount, "authenticator", mountinfo->auth->type);
+    else
+        stats_event (source->mount, "authenticator", NULL);
+
+    if (mountinfo && mountinfo->fallback_mount)
+    {
+        char *mount = source->fallback_mount;
+        source->fallback_mount = strdup (mountinfo->fallback_mount);
+        free (mount);
+    }
+    else
+        source->fallback_mount = NULL;
+
+    if (mountinfo && mountinfo->dumpfile)
+    {
+        char *filename = source->dumpfilename;
+        source->dumpfilename = strdup (mountinfo->dumpfile);
+        free (filename);
+    }
+    else
+        source->dumpfilename = NULL;
+
+    if (source->intro_file)
+    {
+        fclose (source->intro_file);
+        source->intro_file = NULL;
+    }
+    if (mountinfo && mountinfo->intro_filename)
+    {
+        ice_config_t *config = config_get_config_unlocked ();
+        unsigned int len  = strlen (config->webroot_dir) +
+            strlen (mountinfo->intro_filename) + 2;
+        char *path = malloc (len);
+        if (path)
+        {
+            FILE *f;
+            snprintf (path, len, "%s" PATH_SEPARATOR "%s", config->webroot_dir,
+                    mountinfo->intro_filename);
+
+            f = fopen (path, "rb");
+            if (f)
+                source->intro_file = f;
+            else
+                WARN2 ("Cannot open intro file \"%s\": %s", path, strerror(errno));
+            free (path);
+        }
+    }
+
+    if (mountinfo && mountinfo->queue_size_limit)
+        source->queue_size_limit = mountinfo->queue_size_limit;
+
+    if (mountinfo && mountinfo->source_timeout)
+        source->timeout = mountinfo->source_timeout;
+
+    if (mountinfo && mountinfo->burst_size >= 0)
+        source->burst_size = (unsigned int)mountinfo->burst_size;
+
+    if (mountinfo && mountinfo->fallback_when_full)
+        source->fallback_when_full = mountinfo->fallback_when_full;
+
+    avl_tree_unlock (source->client_tree);
+}
+
+
+/* update the specified source with details from the config or mount.
+ * mountinfo can be NULL in which case default settings should be taken
+ */
+void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo)
+{
+    /*  skip if source is a fallback to file */
+    if (source->running && source->client == NULL)
+    {
+        stats_event_hidden (source->mount, NULL, 1);
+        return;
+    }
+    /* set global settings first */
+    source->queue_size_limit = config->queue_size_limit;
+    source->timeout = config->source_timeout;
+    source->burst_size = config->burst_size;
+
+    stats_event_args (source->mount, "listenurl", "http://%s:%d%s",
+            config->hostname, config->port, source->mount);
+
+    source_apply_mount (source, mountinfo);
+
+    if (source->fallback_mount)
+        DEBUG1 ("fallback %s", source->fallback_mount);
+    if (mountinfo && mountinfo->intro_filename)
+        DEBUG1 ("intro file is %s", mountinfo->intro_filename);
+    if (source->dumpfilename)
+        DEBUG1 ("Dumping stream to %s", source->dumpfilename);
+    if (mountinfo && mountinfo->on_connect)
+        DEBUG1 ("connect script \"%s\"", mountinfo->on_connect);
+    if (mountinfo && mountinfo->on_disconnect)
+        DEBUG1 ("disconnect script \"%s\"", mountinfo->on_disconnect);
+    if (source->on_demand)
+    {
+        DEBUG0 ("on_demand set");
+        stats_event (source->mount, "on_demand", "1");
+        stats_event_args (source->mount, "listeners", "%ld", source->listeners);
+    }
+    else
+        stats_event (source->mount, "on_demand", NULL);
+
+    if (source->hidden)
+    {
+        stats_event_hidden (source->mount, NULL, 1);
+        DEBUG0 ("hidden from public");
+    }
+    else
+        stats_event_hidden (source->mount, NULL, 0);
+
+    if (source->max_listeners == -1)
+        stats_event (source->mount, "max_listeners", "unlimited");
+    else
+    {
+        char buf [10];
+        snprintf (buf, sizeof (buf), "%ld", source->max_listeners);
+        stats_event (source->mount, "max_listeners", buf);
+    }
+    DEBUG1 ("public set to %d", source->yp_public);
+    DEBUG1 ("max listeners to %ld", source->max_listeners);
+    DEBUG1 ("queue size to %u", source->queue_size_limit);
+    DEBUG1 ("burst size to %u", source->burst_size);
+    DEBUG1 ("source timeout to %u", source->timeout);
+    DEBUG1 ("fallback_when_full to %u", source->fallback_when_full);
+}
+
+
+void *source_client_thread (void *arg)
+{
+    source_t *source = arg;
+
+    stats_event_inc(NULL, "source_client_connections");
+    stats_event (source->mount, "listeners", "0");
+
+    source_main (source);
+
+    source_free_source (source);
+    slave_rebuild_mounts();
+
+    return NULL;
+}
+
+
+void source_client_callback (client_t *client, void *arg)
+{
+    const char *agent;
+    source_t *source = arg;
+    refbuf_t *old_data = client->refbuf;
+
+    if (client->con->error)
+    {
+        global_lock();
+        global.sources--;
+        global_unlock();
+        source_clear_source (source);
+        source_free_source (source);
+        return;
+    }
+    client->refbuf = old_data->associated;
+    old_data->associated = NULL;
+    refbuf_release (old_data);
+    stats_event (source->mount, "source_ip", source->client->con->ip);
+    agent = httpp_getvar (source->client->parser, "user-agent");
+    if (agent)
+        stats_event (source->mount, "user_agent", agent);
+
+    thread_create ("Source Thread", source_client_thread,
+            source, THREAD_DETACHED);
+}
+
+
+#ifndef _WIN32
+static void source_run_script (char *command, char *mountpoint)
+{
+    pid_t pid, external_pid;
+
+    /* do a fork twice so that the command has init as parent */
+    external_pid = fork();
+    switch (external_pid)
+    {
+        case 0:
+            switch (pid = fork ())
+            {
+                case -1:
+                    ERROR2 ("Unable to fork %s (%s)", command, strerror (errno));
+                    break;
+                case 0:  /* child */
+                    DEBUG1 ("Starting command %s", command);
+                    execl (command, command, mountpoint, (char *)NULL);
+                    ERROR2 ("Unable to run command %s (%s)", command, strerror (errno));
+                    exit(0);
+                default: /* parent */
+                    break;
+            }
+            exit (0);
+        case -1:
+            ERROR1 ("Unable to fork %s", strerror (errno));
+            break;
+        default: /* parent */
+            waitpid (external_pid, NULL, 0);
+            break;
+    }
+}
+#endif
+
+
+static void *source_fallback_file (void *arg)
+{
+    char *mount = arg;
+    char *type;
+    char *path;
+    unsigned int len;
+    FILE *file = NULL;
+    source_t *source = NULL;
+    ice_config_t *config;
+    http_parser_t *parser;
+
+    do
+    {
+        if (mount == NULL || mount[0] != '/')
+            break;
+        config = config_get_config();
+        len  = strlen (config->webroot_dir) + strlen (mount) + 1;
+        path = malloc (len);
+        if (path)
+            snprintf (path, len, "%s%s", config->webroot_dir, mount);
+        
+        config_release_config ();
+        if (path == NULL)
+            break;
+
+        file = fopen (path, "rb");
+        if (file == NULL)
+        {
+            WARN1 ("unable to open file \"%s\"", path);
+            free (path);
+            break;
+        }
+        free (path);
+        source = source_reserve (mount);
+        if (source == NULL)
+        {
+            WARN1 ("mountpoint \"%s\" already reserved", mount);
+            break;
+        }
+        INFO1 ("mountpoint %s is reserved", mount);
+        type = fserve_content_type (mount);
+        parser = httpp_create_parser();
+        httpp_initialize (parser, NULL);
+        httpp_setvar (parser, "content-type", type);
+        free (type);
+
+        source->hidden = 1;
+        source->yp_public = 0;
+        source->intro_file = file;
+        source->parser = parser;
+        file = NULL;
+
+        if (connection_complete_source (source, 0) < 0)
+            break;
+        source_client_thread (source);
+        httpp_destroy (parser);
+    } while (0);
+    if (file)
+        fclose (file);
+    free (mount);
+    return NULL;
+}
+
+
+/* rescan the mount list, so that xsl files are updated to show
+ * unconnected but active fallback mountpoints
+ */
+void source_recheck_mounts (int update_all)
+{
+    ice_config_t *config = config_get_config();
+    mount_proxy *mount = config->mounts;
+
+    avl_tree_rlock (global.source_tree);
+
+    if (update_all)
+        stats_clear_virtual_mounts ();
+
+    while (mount)
+    {
+        source_t *source = source_find_mount (mount->mountname);
+
+        if (source)
+        {
+            source = source_find_mount_raw (mount->mountname);
+            if (source)
+            {
+                mount_proxy *mountinfo = config_find_mount (config, source->mount);
+                source_update_settings (config, source, mountinfo);
+            }
+            else if (update_all)
+            {
+                stats_event_hidden (mount->mountname, NULL, mount->hidden);
+                stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s",
+                        config->hostname, config->port, mount->mountname);
+                stats_event (mount->mountname, "listeners", "0");
+                if (mount->max_listeners < 0)
+                    stats_event (mount->mountname, "max_listeners", "unlimited");
+                else
+                    stats_event_args (mount->mountname, "max_listeners", "%d", mount->max_listeners);
+            }
+        }
+        else
+            stats_event (mount->mountname, NULL, NULL);
+
+        /* check for fallback to file */
+        if (global.running == ICE_RUNNING && mount->fallback_mount)
+        {
+            source_t *fallback = source_find_mount (mount->fallback_mount);
+            if (fallback == NULL)
+            {
+                thread_create ("Fallback file thread", source_fallback_file,
+                        strdup (mount->fallback_mount), THREAD_DETACHED);
+            }
+        }
+
+        mount = mount->next;
+    }
+    avl_tree_unlock (global.source_tree);
+    config_release_config();
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/stats.c
===================================================================
--- icecast/trunk/icecast/src/stats.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/stats.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1102 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-#include <thread/thread.h>
-#include <avl/avl.h>
-#include <httpp/httpp.h>
-#include <net/sock.h>
-
-#include "connection.h"
-
-#include "source.h"
-#include "global.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "xslt.h"
-#include "util.h"
-#define CATMODULE "stats"
-#include "logging.h"
-
-#ifdef _WIN32
-#define vsnprintf _vsnprintf
-#define snprintf _snprintf
-#endif
-
-#define STATS_EVENT_SET     0
-#define STATS_EVENT_INC     1
-#define STATS_EVENT_DEC     2
-#define STATS_EVENT_ADD     3
-#define STATS_EVENT_REMOVE  4
-#define STATS_EVENT_HIDDEN  5
-
-typedef struct _event_queue_tag
-{
-    volatile stats_event_t *head;
-    volatile stats_event_t **tail;
-} event_queue_t;
-
-#define event_queue_init(qp)    { (qp)->head = NULL; (qp)->tail = &(qp)->head; }
-
-typedef struct _event_listener_tag
-{
-    event_queue_t queue;
-    mutex_t mutex;
-
-    struct _event_listener_tag *next;
-} event_listener_t;
-
-static volatile int _stats_running = 0;
-static thread_type *_stats_thread_id;
-static volatile int _stats_threads = 0;
-
-static stats_t _stats;
-static mutex_t _stats_mutex;
-
-static event_queue_t _global_event_queue;
-mutex_t _global_event_mutex;
-
-static volatile event_listener_t *_event_listeners;
-
-
-static void *_stats_thread(void *arg);
-static int _compare_stats(void *a, void *b, void *arg);
-static int _compare_source_stats(void *a, void *b, void *arg);
-static int _free_stats(void *key);
-static int _free_source_stats(void *key);
-static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
-static stats_node_t *_find_node(avl_tree *tree, char *name);
-static stats_source_t *_find_source(avl_tree *tree, char *source);
-static void _free_event(stats_event_t *event);
-static stats_event_t *_get_event_from_queue (event_queue_t *queue);
-
-
-/* simple helper function for creating an event */
-static stats_event_t *build_event (const char *source, const char *name, const char *value)
-{
-    stats_event_t *event;
-
-    event = (stats_event_t *)calloc(1, sizeof(stats_event_t));
-    if (event)
-    {
-        if (source)
-            event->source = (char *)strdup(source);
-        if (name)
-            event->name = (char *)strdup(name);
-        if (value)
-            event->value = (char *)strdup(value);
-        else
-            event->action = STATS_EVENT_REMOVE;
-    }
-    return event;
-}
-
-static void queue_global_event (stats_event_t *event)
-{
-    thread_mutex_lock(&_global_event_mutex);
-    _add_event_to_queue (event, &_global_event_queue);
-    thread_mutex_unlock(&_global_event_mutex);
-}
-
-void stats_initialize(void)
-{
-    _event_listeners = NULL;
-
-    /* set up global struct */
-    _stats.global_tree = avl_tree_new(_compare_stats, NULL);
-    _stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
-
-    /* set up global mutex */
-    thread_mutex_create(&_stats_mutex);
-
-    /* set up stats queues */
-    event_queue_init (&_global_event_queue);
-    thread_mutex_create(&_global_event_mutex);
-
-    /* fire off the stats thread */
-    _stats_running = 1;
-    _stats_thread_id = thread_create("Stats Thread", _stats_thread, NULL, THREAD_ATTACHED);
-}
-
-void stats_shutdown(void)
-{
-    int n;
-
-    if(!_stats_running) /* We can't shutdown if we're not running. */
-        return;
-
-    /* wait for thread to exit */
-    _stats_running = 0;
-    thread_join(_stats_thread_id);
-
-    /* wait for other threads to shut down */
-    do {
-        thread_sleep(300000);
-        thread_mutex_lock(&_stats_mutex);
-        n = _stats_threads;
-        thread_mutex_unlock(&_stats_mutex);
-    } while (n > 0);
-    INFO0("stats thread finished");
-
-    /* free the queues */
-
-    /* destroy the queue mutexes */
-    thread_mutex_destroy(&_global_event_mutex);
-
-    thread_mutex_destroy(&_stats_mutex);
-    avl_tree_free(_stats.source_tree, _free_source_stats);
-    avl_tree_free(_stats.global_tree, _free_stats);
-
-    while (1)
-    {
-        stats_event_t *event = _get_event_from_queue (&_global_event_queue);
-        if (event == NULL) break;
-        if(event->source)
-            free(event->source);
-        if(event->value)
-            free(event->value);
-        if(event->name)
-            free(event->name);
-        free(event);
-    }
-}
-
-stats_t *stats_get_stats(void)
-{
-    /* lock global stats
-    
-     copy stats
-
-     unlock global stats
-
-     return copied stats */
-
-    return NULL;
-}
-
-/* simple name=tag stat create/update */
-void stats_event(const char *source, const char *name, const char *value)
-{
-    stats_event_t *event;
-
-    event = build_event (source, name, value);
-    if (event)
-        queue_global_event (event);
-}
-
-/* make stat hidden (non-zero). name can be NULL if it applies to a whole
- * source stats tree. */
-void stats_event_hidden (const char *source, const char *name, int hidden)
-{
-    stats_event_t *event;
-    const char *str = NULL;
-
-    if (hidden)
-        str = "";
-    event = build_event (source, name, str);
-    if (event)
-    {
-        event->action = STATS_EVENT_HIDDEN;
-        queue_global_event (event);
-    }
-}
-
-/* printf style formatting for stat create/update */
-void stats_event_args(const char *source, char *name, char *format, ...)
-{
-    char buf[1024];
-    va_list val;
-    int ret;
-
-    if (name == NULL)
-        return;
-    va_start(val, format);
-    ret = vsnprintf(buf, 1024, format, val);
-    va_end(val);
-
-    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
-    {
-        WARN2 ("problem with formatting %s stat %s",
-                source==NULL ? "global" : source, name);
-        return;
-    }
-    stats_event(source, name, buf);
-}
-
-static char *_get_stats(char *source, char *name)
-{
-    stats_node_t *stats = NULL;
-    stats_source_t *src = NULL;
-    char *value = NULL;
-
-    thread_mutex_lock(&_stats_mutex);
-
-    if (source == NULL) {
-        stats = _find_node(_stats.global_tree, name);
-    } else {
-        src = _find_source(_stats.source_tree, source);
-        if (src) {
-            stats = _find_node(src->stats_tree, name);
-        }
-    }
-
-    if (stats) value = (char *)strdup(stats->value);
-
-    thread_mutex_unlock(&_stats_mutex);
-
-    return value;
-}
-
-char *stats_get_value(char *source, char *name)
-{
-    return(_get_stats(source, name));
-}
-
-/* increase the value in the provided stat by 1 */
-void stats_event_inc(const char *source, const char *name)
-{
-    stats_event_t *event = build_event (source, name, NULL);
-    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
-    if (event)
-    {
-        event->action = STATS_EVENT_INC;
-        queue_global_event (event);
-    }
-}
-
-void stats_event_add(const char *source, const char *name, unsigned long value)
-{
-    stats_event_t *event = build_event (source, name, NULL);
-    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
-    if (event)
-    {
-        event->value = malloc (16);
-        snprintf (event->value, 16, "%ld", value);
-        event->action = STATS_EVENT_ADD;
-        queue_global_event (event);
-    }
-}
-
-/* decrease the value in the provided stat by 1 */
-void stats_event_dec(const char *source, const char *name)
-{
-    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
-    stats_event_t *event = build_event (source, name, NULL);
-    if (event)
-    {
-        event->action = STATS_EVENT_DEC;
-        queue_global_event (event);
-    }
-}
-
-/* note: you must call this function only when you have exclusive access
-** to the avl_tree
-*/
-static stats_node_t *_find_node(avl_tree *stats_tree, char *name)
-{
-    stats_node_t *stats;
-    avl_node *node;
-    int cmp;
-
-    /* get the root node */
-    node = stats_tree->root->right;
-    
-    while (node) {
-        stats = (stats_node_t *)node->key;
-        cmp = strcmp(name, stats->name);
-        if (cmp < 0) 
-            node = node->left;
-        else if (cmp > 0)
-            node = node->right;
-        else
-            return stats;
-    }
-    
-    /* didn't find it */
-    return NULL;
-}
-
-/* note: you must call this function only when you have exclusive access
-** to the avl_tree
-*/
-static stats_source_t *_find_source(avl_tree *source_tree, char *source)
-{
-    stats_source_t *stats;
-    avl_node *node;
-    int cmp;
-
-    /* get the root node */
-    node = source_tree->root->right;
-    while (node) {
-        stats = (stats_source_t *)node->key;
-        cmp = strcmp(source, stats->source);
-        if (cmp < 0)
-            node = node->left;
-        else if (cmp > 0)
-            node = node->right;
-        else
-            return stats;
-    }
-
-    /* didn't find it */
-    return NULL;
-}
-
-static stats_event_t *_copy_event(stats_event_t *event)
-{
-    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
-    if (event->source) 
-        copy->source = (char *)strdup(event->source);
-    else
-        copy->source = NULL;
-    if (event->name)
-        copy->name = (char *)strdup(event->name);
-    if (event->value)
-        copy->value = (char *)strdup(event->value);
-    else
-        copy->value = NULL;
-    copy->hidden = event->hidden;
-    copy->next = NULL;
-
-    return copy;
-}
-
-
-/* helper to apply specialised changes to a stats node */
-static void modify_node_event (stats_node_t *node, stats_event_t *event)
-{
-    char *str;
-
-    if (event->action == STATS_EVENT_HIDDEN)
-    {
-        if (event->value)
-            node->hidden = 1;
-        else
-            node->hidden = 0;
-        return;
-    }
-    if (event->action != STATS_EVENT_SET)
-    {
-        int value = 0;
-
-        switch (event->action)
-        {
-            case STATS_EVENT_INC:
-                value = atoi (node->value)+1;
-                break;
-            case STATS_EVENT_DEC:
-                value = atoi (node->value)-1;
-                break;
-            case STATS_EVENT_ADD:
-                value = atoi (node->value)+atoi (event->value);
-                break;
-            default:
-                break;
-        }
-        str = malloc (16);
-        snprintf (str, 16, "%d", value);
-        if (event->value == NULL)
-            event->value = strdup (str);
-    }
-    else
-        str = (char *)strdup (event->value);
-    free (node->value);
-    node->value = str;
-    DEBUG2 ("update node %s (%s)", node->name, node->value);
-}
-
-
-static void process_global_event (stats_event_t *event)
-{
-    stats_node_t *node;
-
-    /* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
-    if (event->action == STATS_EVENT_REMOVE)
-    {
-        /* we're deleting */
-        node = _find_node(_stats.global_tree, event->name);
-        if (node != NULL)
-            avl_delete(_stats.global_tree, (void *)node, _free_stats);
-        return;
-    }
-    node = _find_node(_stats.global_tree, event->name);
-    if (node)
-    {
-        modify_node_event (node, event);
-    }
-    else
-    {
-        /* add node */
-        node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
-        node->name = (char *)strdup(event->name);
-        node->value = (char *)strdup(event->value);
-
-        avl_insert(_stats.global_tree, (void *)node);
-    }
-}
-
-
-static void process_source_event (stats_event_t *event)
-{
-    stats_source_t *snode = _find_source(_stats.source_tree, event->source);
-    if (snode == NULL)
-    {
-        if (event->action == STATS_EVENT_REMOVE)
-            return;
-        snode = (stats_source_t *)calloc(1,sizeof(stats_source_t));
-        if (snode == NULL)
-            return;
-        DEBUG1 ("new source stat %s", event->source);
-        snode->source = (char *)strdup(event->source);
-        snode->stats_tree = avl_tree_new(_compare_stats, NULL);
-        if (event->action == STATS_EVENT_HIDDEN)
-            snode->hidden = 1;
-        else
-            snode->hidden = 0;
-
-        avl_insert(_stats.source_tree, (void *)snode);
-    }
-    if (event->name)
-    {
-        stats_node_t *node = _find_node(snode->stats_tree, event->name);
-        if (node == NULL)
-        {
-            if (event->action == STATS_EVENT_REMOVE)
-                return;
-            /* adding node */
-            if (event->value)
-            {
-                DEBUG2 ("new node %s (%s)", event->name, event->value);
-                node = (stats_node_t *)calloc(1,sizeof(stats_node_t));
-                node->name = (char *)strdup(event->name);
-                node->value = (char *)strdup(event->value);
-                node->hidden = snode->hidden;
-
-                avl_insert(snode->stats_tree, (void *)node);
-            }
-            return;
-        }
-        if (event->action == STATS_EVENT_REMOVE)
-        {
-            DEBUG1 ("delete node %s", event->name);
-            avl_delete(snode->stats_tree, (void *)node, _free_stats);
-            return;
-        }
-        modify_node_event (node, event);
-        return;
-    }
-    if (event->action == STATS_EVENT_HIDDEN)
-    {
-        avl_node *node = avl_get_first (snode->stats_tree);
-
-        if (event->value)
-            snode->hidden = 1;
-        else
-            snode->hidden = 0;
-        while (node)
-        {
-            stats_node_t *stats = (stats_node_t*)node->key;
-            stats->hidden = snode->hidden;
-            node = avl_get_next (node);
-        }
-        return;
-    }
-    if (event->action == STATS_EVENT_REMOVE)
-    {
-        DEBUG1 ("delete source node %s", event->source);
-        avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
-    }
-}
-
-
-void stats_event_time (const char *mount, const char *name)
-{
-    time_t now = time(NULL);
-    struct tm local;
-    char buffer[100];
-
-    localtime_r (&now, &local);
-    strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S %z", &local);
-    stats_event (mount, name, buffer);
-}
-
-
-static void *_stats_thread(void *arg)
-{
-    stats_event_t *event;
-    stats_event_t *copy;
-    event_listener_t *listener;
-
-    stats_event (NULL, "server", ICECAST_VERSION_STRING);
-    stats_event_time (NULL, "server_start");
-
-    /* global currently active stats */
-    stats_event (NULL, "clients", "0");
-    stats_event (NULL, "connections", "0");
-    stats_event (NULL, "sources", "0");
-    stats_event (NULL, "stats", "0");
-
-    /* global accumulating stats */
-    stats_event (NULL, "client_connections", "0");
-    stats_event (NULL, "source_client_connections", "0");
-    stats_event (NULL, "source_relay_connections", "0");
-    stats_event (NULL, "source_total_connections", "0");
-    stats_event (NULL, "stats_connections", "0");
-    stats_event (NULL, "listener_connections", "0");
-
-    INFO0 ("stats thread started");
-    while (_stats_running) {
-        if (_global_event_queue.head != NULL) {
-            /* grab the next event from the queue */
-            thread_mutex_lock(&_global_event_mutex);
-            event = _get_event_from_queue (&_global_event_queue);
-            thread_mutex_unlock(&_global_event_mutex);
-
-            event->next = NULL;
-
-            thread_mutex_lock(&_stats_mutex);
-
-            /* check if we are dealing with a global or source event */
-            if (event->source == NULL)
-                process_global_event (event);
-            else
-                process_source_event (event);
-            
-            /* now we have an event that's been processed into the running stats */
-            /* this event should get copied to event listeners' queues */
-            listener = (event_listener_t *)_event_listeners;
-            while (listener) {
-                copy = _copy_event(event);
-                thread_mutex_lock (&listener->mutex);
-                _add_event_to_queue (copy, &listener->queue);
-                thread_mutex_unlock (&listener->mutex);
-
-                listener = listener->next;
-            }
-
-            /* now we need to destroy the event */
-            _free_event(event);
-
-            thread_mutex_unlock(&_stats_mutex);
-            continue;
-        }
-
-        thread_sleep(300000);
-    }
-
-    return NULL;
-}
-
-/* you must have the _stats_mutex locked here */
-static void _unregister_listener(event_listener_t *listener)
-{
-    event_listener_t **prev = (event_listener_t **)&_event_listeners,
-                     *current = *prev;
-    while (current)
-    {
-        if (current == listener)
-        {
-            *prev = current->next;
-            break;
-        }
-        prev = &current->next;
-        current = *prev;
-    }
-}
-
-
-static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
-{
-    stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
-    
-    if (source != NULL)
-        event->source = (char *)strdup(source);
-    else
-        event->source = NULL;
-    event->name = (char *)strdup(node->name);
-    event->value = (char *)strdup(node->value);
-    event->hidden = node->hidden;
-    event->action = STATS_EVENT_SET;
-    event->next = NULL;
-
-    return event;
-}
-
-
-static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue)
-{
-    *queue->tail = event;
-    queue->tail = (volatile stats_event_t **)&event->next;
-}
-
-
-static stats_event_t *_get_event_from_queue (event_queue_t *queue)
-{
-    stats_event_t *event = NULL;
-
-    if (queue && queue->head)
-    {
-        event = (stats_event_t *)queue->head;
-        queue->head = event->next;
-        if (queue->head == NULL)
-            queue->tail = &queue->head;
-    }
-
-    return event;
-}
-
-static int _send_event_to_client(stats_event_t *event, client_t *client)
-{
-    int len;
-    char buf [200];
-
-    /* send data to the client!!!! */
-    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
-            (event->source != NULL) ? event->source : "global",
-            event->name ? event->name : "null",
-            event->value ? event->value : "null");
-    if (len > 0 && len < (int)sizeof (buf))
-    {
-        client_send_bytes (client, buf, len);
-        if (client->con->error)
-            return -1;
-    }
-    return 0;
-}
-
-void _dump_stats_to_queue (event_queue_t *queue)
-{
-    avl_node *node;
-    avl_node *node2;
-    stats_event_t *event;
-    stats_source_t *source;
-
-    thread_mutex_lock(&_stats_mutex);
-    /* first we fill our queue with the current stats */
-    /* start with the global stats */
-    node = avl_get_first(_stats.global_tree);
-    while (node) {
-        event = _make_event_from_node((stats_node_t *)node->key, NULL);
-        _add_event_to_queue(event, queue);
-
-        node = avl_get_next(node);
-    }
-
-    /* now the stats for each source */
-    node = avl_get_first(_stats.source_tree);
-    while (node) {
-        source = (stats_source_t *)node->key;
-        node2 = avl_get_first(source->stats_tree);
-        while (node2) {
-            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
-            _add_event_to_queue(event, queue);
-
-            node2 = avl_get_next(node2);
-        }
-        
-        node = avl_get_next(node);
-    }
-    thread_mutex_unlock(&_stats_mutex);
-}
-
-/* factoring out code for stats loops
-** this function copies all stats to queue, and registers 
-** the queue for all new events atomically.
-** note: mutex must already be created!
-*/
-static void _register_listener (event_listener_t *listener)
-{
-    avl_node *node;
-    avl_node *node2;
-    stats_event_t *event;
-    stats_source_t *source;
-
-    thread_mutex_lock(&_stats_mutex);
-
-    /* first we fill our queue with the current stats */
-    
-    /* start with the global stats */
-    node = avl_get_first(_stats.global_tree);
-    while (node) {
-        event = _make_event_from_node((stats_node_t *)node->key, NULL);
-        _add_event_to_queue (event, &listener->queue);
-
-        node = avl_get_next(node);
-    }
-
-    /* now the stats for each source */
-    node = avl_get_first(_stats.source_tree);
-    while (node) {
-        source = (stats_source_t *)node->key;
-        node2 = avl_get_first(source->stats_tree);
-        while (node2) {
-            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
-            _add_event_to_queue (event, &listener->queue);
-
-            node2 = avl_get_next(node2);
-        }
-        
-        node = avl_get_next(node);
-    }
-
-    /* now we register to receive future event notices */
-    listener->next = (event_listener_t *)_event_listeners;
-    _event_listeners = listener;
-
-    thread_mutex_unlock(&_stats_mutex);
-}
-
-void *stats_connection(void *arg)
-{
-    client_t *client = (client_t *)arg;
-    stats_event_t *event;
-    event_listener_t listener;
-
-    INFO0 ("stats client starting");
-
-    event_queue_init (&listener.queue);
-    /* increment the thread count */
-    thread_mutex_lock(&_stats_mutex);
-    _stats_threads++;
-    stats_event_args (NULL, "stats", "%d", _stats_threads);
-    thread_mutex_unlock(&_stats_mutex);
-
-    thread_mutex_create (&(listener.mutex));
-
-    _register_listener (&listener);
-
-    while (_stats_running) {
-        thread_mutex_lock (&listener.mutex);
-        event = _get_event_from_queue (&listener.queue);
-        thread_mutex_unlock (&listener.mutex);
-        if (event != NULL) {
-            if (_send_event_to_client(event, client) < 0) {
-                _free_event(event);
-                break;
-            }
-            _free_event(event);
-            continue;
-        }
-        thread_sleep (500000);
-    }
-
-    thread_mutex_lock(&_stats_mutex);
-    _unregister_listener (&listener);
-    _stats_threads--;
-    stats_event_args (NULL, "stats", "%d", _stats_threads);
-    thread_mutex_unlock(&_stats_mutex);
-
-    thread_mutex_destroy (&listener.mutex);
-    client_destroy (client);
-    INFO0 ("stats client finished");
-
-    return NULL;
-}
-
-
-void stats_callback (client_t *client, void *notused)
-{
-    if (client->con->error)
-    {
-        client_destroy (client);
-        return;
-    }
-    client_set_queue (client, NULL);
-    thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
-}
-
-
-typedef struct _source_xml_tag {
-    char *mount;
-    xmlNodePtr node;
-
-    struct _source_xml_tag *next;
-} source_xml_t;
-
-static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
-{
-    source_xml_t *node, *node2;
-    int found = 0;
-
-    /* search for existing node */
-    node = *list;
-    while (node) {
-        if (strcmp(node->mount, mount) == 0) {
-            found = 1;
-            break;
-        }
-        node = node->next;
-    }
-
-    if (found) return node->node;
-
-    /* if we didn't find it, we must build it and add it to the list */
-
-    /* build node */
-    node = (source_xml_t *)malloc(sizeof(source_xml_t));
-    node->mount = strdup(mount);
-    node->node = xmlNewChild(root, NULL, "source", NULL);
-    xmlSetProp(node->node, "mount", mount);
-    node->next = NULL;
-
-    /* add node */
-    if (*list == NULL) {
-        *list = node;
-    } else {
-        node2 = *list;
-        while (node2->next) node2 = node2->next;
-        node2->next = node;
-    }
-
-    return node->node;
-}
-
-void stats_transform_xslt(client_t *client, const char *uri)
-{
-    xmlDocPtr doc;
-    char *xslpath = util_get_path_from_normalised_uri (uri);
-
-    stats_get_xml(&doc, 0, NULL);
-
-    xslt_transform(doc, xslpath, client);
-
-    xmlFreeDoc(doc);
-    free (xslpath);
-}
-
-void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
-{
-    stats_event_t *event;
-    event_queue_t queue;
-    xmlNodePtr node, srcnode;
-    source_xml_t *src_nodes = NULL;
-    source_xml_t *next;
-
-    event_queue_init (&queue);
-    _dump_stats_to_queue (&queue);
-
-    *doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
-    xmlDocSetRootElement(*doc, node);
-
-    event = _get_event_from_queue(&queue);
-    while (event)
-    {
-        if (event->hidden <= show_hidden)
-        {
-            do
-            {
-                xmlChar *name, *value;
-                name = xmlEncodeEntitiesReentrant (*doc, event->name);
-                value = xmlEncodeEntitiesReentrant (*doc, event->value);
-                srcnode = node;
-                if (event->source)
-                {
-                    if (show_mount && strcmp (event->source, show_mount) != 0)
-                        break;
-                    srcnode = _find_xml_node(event->source, &src_nodes, node);
-                }
-                else
-                    srcnode = node;
-                xmlNewChild(srcnode, NULL, name, value);
-                xmlFree (value);
-                xmlFree (name);
-            } while (0);
-        }
-
-        _free_event(event);
-        event = _get_event_from_queue(&queue);
-    }
-
-    while (src_nodes) {
-        next = src_nodes->next;
-        free(src_nodes->mount);
-        free(src_nodes);
-        src_nodes = next;
-    }
-}
-void stats_sendxml(client_t *client)
-{
-    int bytes;
-    stats_event_t *event;
-    event_queue_t queue;
-    xmlDocPtr doc;
-    xmlNodePtr node, srcnode;
-    int len;
-    xmlChar *buff = NULL;
-    source_xml_t *snd;
-    source_xml_t *src_nodes = NULL;
-
-    event_queue_init (&queue);
-    _dump_stats_to_queue (&queue);
-
-    doc = xmlNewDoc("1.0");
-    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
-    xmlDocSetRootElement(doc, node);
-
-
-    event = _get_event_from_queue(&queue);
-    while (event) {
-        if (event->source == NULL) {
-            xmlNewChild(node, NULL, event->name, event->value);
-        } else {
-            srcnode = _find_xml_node(event->source, &src_nodes, node);
-            xmlNewChild(srcnode, NULL, event->name, event->value);
-        }
-
-        _free_event(event);
-        event = _get_event_from_queue(&queue);
-    }
-
-    xmlDocDumpMemory(doc, &buff, &len);
-    xmlFreeDoc(doc);
-    
-    client->respcode = 200;
-    bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n"
-               "Content-Length: %d\r\n"
-               "Content-Type: text/xml\r\n"
-               "\r\n", len);
-    if (bytes > 0) client->con->sent_bytes += bytes;
-    else goto send_error;
-
-    bytes = client_send_bytes (client, buff, (unsigned)len);
-
- send_error:
-    while (src_nodes) {
-        snd = src_nodes->next;
-        free(src_nodes->mount);
-        free(src_nodes);
-        src_nodes = snd;
-    }
-    if (buff) xmlFree(buff);
-}
-
-static int _compare_stats(void *arg, void *a, void *b)
-{
-    stats_node_t *nodea = (stats_node_t *)a;
-    stats_node_t *nodeb = (stats_node_t *)b;
-
-    return strcmp(nodea->name, nodeb->name);
-}
-
-static int _compare_source_stats(void *arg, void *a, void *b)
-{
-    stats_source_t *nodea = (stats_source_t *)a;
-    stats_source_t *nodeb = (stats_source_t *)b;
-
-    return strcmp(nodea->source, nodeb->source);
-}
-
-static int _free_stats(void *key)
-{
-    stats_node_t *node = (stats_node_t *)key;
-    free(node->value);
-    free(node->name);
-    free(node);
-    
-    return 1;
-}
-
-static int _free_source_stats(void *key)
-{
-    stats_source_t *node = (stats_source_t *)key;
-    avl_tree_free(node->stats_tree, _free_stats);
-    free(node->source);
-    free(node);
-
-    return 1;
-}
-
-static void _free_event(stats_event_t *event)
-{
-    if (event->source) free(event->source);
-    if (event->name) free(event->name);
-    if (event->value) free(event->value);
-    free(event);
-}
-
-
-/* get a list of mountpoints that are in the stats but are not marked as hidden */
-void stats_get_streamlist (char *buffer, size_t remaining)
-{
-    avl_node *node;
-
-    /* now the stats for each source */
-    thread_mutex_lock (&_stats_mutex);
-    node = avl_get_first(_stats.source_tree);
-    while (node)
-    {
-        int ret;
-        stats_source_t *source = (stats_source_t *)node->key;
-
-        if (source->hidden == 0)
-        {
-            if (remaining <= strlen (source->source)+2)
-            {
-                WARN0 ("streamlist was truncated");
-                break;
-            }
-            ret = snprintf (buffer, remaining, "%s\r\n", source->source);
-            if (ret > 0)
-            {
-                buffer += ret;
-                remaining -= ret;
-            }
-        }
-
-        node = avl_get_next(node);
-    }
-    thread_mutex_unlock (&_stats_mutex);
-}
-
-/* This removes any source stats from virtual mountpoints, ie mountpoints
- * where no source_t exists. This function requires the global sources lock
- * to be held before calling.
- */
-void stats_clear_virtual_mounts (void)
-{
-    avl_node *snode;
-
-    thread_mutex_lock (&_stats_mutex);
-    snode = avl_get_first(_stats.source_tree);
-    while (snode)
-    {
-        stats_source_t *src = (stats_source_t *)snode->key;
-        source_t *source = source_find_mount_raw (src->source);
-
-        if (source == NULL)
-        {
-            /* no source_t is reserved so remove them now */
-            snode = avl_get_next (snode);
-            DEBUG1 ("releasing %s stats", src->source);
-            avl_delete (_stats.source_tree, src, _free_source_stats);
-            continue;
-        }
-
-        snode = avl_get_next (snode);
-    }
-    thread_mutex_unlock (&_stats_mutex);
-}
-

Copied: icecast/tags/icecast-2.3.2/src/stats.c (from rev 14867, icecast/trunk/icecast/src/stats.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/stats.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/stats.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1169 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+
+#include "source.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "xslt.h"
+#include "util.h"
+#define CATMODULE "stats"
+#include "logging.h"
+
+#ifdef _WIN32
+#define vsnprintf _vsnprintf
+#define snprintf _snprintf
+#endif
+
+#define STATS_EVENT_SET     0
+#define STATS_EVENT_INC     1
+#define STATS_EVENT_DEC     2
+#define STATS_EVENT_ADD     3
+#define STATS_EVENT_SUB     4
+#define STATS_EVENT_REMOVE  5
+#define STATS_EVENT_HIDDEN  6
+
+typedef struct _event_queue_tag
+{
+    volatile stats_event_t *head;
+    volatile stats_event_t **tail;
+} event_queue_t;
+
+#define event_queue_init(qp)    { (qp)->head = NULL; (qp)->tail = &(qp)->head; }
+
+typedef struct _event_listener_tag
+{
+    event_queue_t queue;
+    mutex_t mutex;
+
+    struct _event_listener_tag *next;
+} event_listener_t;
+
+static volatile int _stats_running = 0;
+static thread_type *_stats_thread_id;
+static volatile int _stats_threads = 0;
+
+static stats_t _stats;
+static mutex_t _stats_mutex;
+
+static event_queue_t _global_event_queue;
+mutex_t _global_event_mutex;
+
+static volatile event_listener_t *_event_listeners;
+
+
+static void *_stats_thread(void *arg);
+static int _compare_stats(void *a, void *b, void *arg);
+static int _compare_source_stats(void *a, void *b, void *arg);
+static int _free_stats(void *key);
+static int _free_source_stats(void *key);
+static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
+static stats_node_t *_find_node(avl_tree *tree, const char *name);
+static stats_source_t *_find_source(avl_tree *tree, const char *source);
+static void _free_event(stats_event_t *event);
+static stats_event_t *_get_event_from_queue (event_queue_t *queue);
+
+
+/* simple helper function for creating an event */
+static stats_event_t *build_event (const char *source, const char *name, const char *value)
+{
+    stats_event_t *event;
+
+    event = (stats_event_t *)calloc(1, sizeof(stats_event_t));
+    if (event)
+    {
+        if (source)
+            event->source = (char *)strdup(source);
+        if (name)
+            event->name = (char *)strdup(name);
+        if (value)
+            event->value = (char *)strdup(value);
+        else
+            event->action = STATS_EVENT_REMOVE;
+    }
+    return event;
+}
+
+static void queue_global_event (stats_event_t *event)
+{
+    thread_mutex_lock(&_global_event_mutex);
+    _add_event_to_queue (event, &_global_event_queue);
+    thread_mutex_unlock(&_global_event_mutex);
+}
+
+void stats_initialize(void)
+{
+    _event_listeners = NULL;
+
+    /* set up global struct */
+    _stats.global_tree = avl_tree_new(_compare_stats, NULL);
+    _stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
+
+    /* set up global mutex */
+    thread_mutex_create(&_stats_mutex);
+
+    /* set up stats queues */
+    event_queue_init (&_global_event_queue);
+    thread_mutex_create(&_global_event_mutex);
+
+    /* fire off the stats thread */
+    _stats_running = 1;
+    _stats_thread_id = thread_create("Stats Thread", _stats_thread, NULL, THREAD_ATTACHED);
+}
+
+void stats_shutdown(void)
+{
+    int n;
+
+    if(!_stats_running) /* We can't shutdown if we're not running. */
+        return;
+
+    /* wait for thread to exit */
+    _stats_running = 0;
+    thread_join(_stats_thread_id);
+
+    /* wait for other threads to shut down */
+    do {
+        thread_sleep(300000);
+        thread_mutex_lock(&_stats_mutex);
+        n = _stats_threads;
+        thread_mutex_unlock(&_stats_mutex);
+    } while (n > 0);
+    INFO0("stats thread finished");
+
+    /* free the queues */
+
+    /* destroy the queue mutexes */
+    thread_mutex_destroy(&_global_event_mutex);
+
+    thread_mutex_destroy(&_stats_mutex);
+    avl_tree_free(_stats.source_tree, _free_source_stats);
+    avl_tree_free(_stats.global_tree, _free_stats);
+
+    while (1)
+    {
+        stats_event_t *event = _get_event_from_queue (&_global_event_queue);
+        if (event == NULL) break;
+        if(event->source)
+            free(event->source);
+        if(event->value)
+            free(event->value);
+        if(event->name)
+            free(event->name);
+        free(event);
+    }
+}
+
+stats_t *stats_get_stats(void)
+{
+    /* lock global stats
+    
+     copy stats
+
+     unlock global stats
+
+     return copied stats */
+
+    return NULL;
+}
+
+/* simple name=tag stat create/update */
+void stats_event(const char *source, const char *name, const char *value)
+{
+    stats_event_t *event;
+
+    if (value && xmlCheckUTF8 ((unsigned char *)value) == 0)
+    {
+        WARN2 ("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value);
+        return;
+    }
+    event = build_event (source, name, value);
+    if (event)
+        queue_global_event (event);
+}
+
+
+/* wrapper for stats_event, this takes a charset to convert from */
+void stats_event_conv(const char *mount, const char *name, const char *value, const char *charset)
+{
+    const char *metadata = value;
+    xmlBufferPtr conv = xmlBufferCreate ();
+
+    if (charset)
+    {
+        xmlCharEncodingHandlerPtr handle = xmlFindCharEncodingHandler (charset);
+
+        if (handle)
+        {
+            xmlBufferPtr raw = xmlBufferCreate ();
+            xmlBufferAdd (raw, (const xmlChar *)value, strlen (value));
+            if (xmlCharEncInFunc (handle, conv, raw) > 0)
+                metadata = (char *)xmlBufferContent (conv);
+            xmlBufferFree (raw);
+            xmlCharEncCloseFunc (handle);
+        }
+        else
+            WARN1 ("No charset found for \"%s\"", charset);
+    }
+
+    stats_event (mount, name, metadata);
+    xmlBufferFree (conv);
+}
+
+/* make stat hidden (non-zero). name can be NULL if it applies to a whole
+ * source stats tree. */
+void stats_event_hidden (const char *source, const char *name, int hidden)
+{
+    stats_event_t *event;
+    const char *str = NULL;
+
+    if (hidden)
+        str = "";
+    event = build_event (source, name, str);
+    if (event)
+    {
+        event->action = STATS_EVENT_HIDDEN;
+        queue_global_event (event);
+    }
+}
+
+/* printf style formatting for stat create/update */
+void stats_event_args(const char *source, char *name, char *format, ...)
+{
+    char buf[1024];
+    va_list val;
+    int ret;
+
+    if (name == NULL)
+        return;
+    va_start(val, format);
+    ret = vsnprintf(buf, 1024, format, val);
+    va_end(val);
+
+    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
+    {
+        WARN2 ("problem with formatting %s stat %s",
+                source==NULL ? "global" : source, name);
+        return;
+    }
+    stats_event(source, name, buf);
+}
+
+static char *_get_stats(const char *source, const char *name)
+{
+    stats_node_t *stats = NULL;
+    stats_source_t *src = NULL;
+    char *value = NULL;
+
+    thread_mutex_lock(&_stats_mutex);
+
+    if (source == NULL) {
+        stats = _find_node(_stats.global_tree, name);
+    } else {
+        src = _find_source(_stats.source_tree, source);
+        if (src) {
+            stats = _find_node(src->stats_tree, name);
+        }
+    }
+
+    if (stats) value = (char *)strdup(stats->value);
+
+    thread_mutex_unlock(&_stats_mutex);
+
+    return value;
+}
+
+char *stats_get_value(const char *source, const char *name)
+{
+    return(_get_stats(source, name));
+}
+
+/* increase the value in the provided stat by 1 */
+void stats_event_inc(const char *source, const char *name)
+{
+    stats_event_t *event = build_event (source, name, NULL);
+    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
+    if (event)
+    {
+        event->action = STATS_EVENT_INC;
+        queue_global_event (event);
+    }
+}
+
+void stats_event_add(const char *source, const char *name, unsigned long value)
+{
+    stats_event_t *event = build_event (source, name, NULL);
+    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
+    if (event)
+    {
+        event->value = malloc (16);
+        snprintf (event->value, 16, "%ld", value);
+        event->action = STATS_EVENT_ADD;
+        queue_global_event (event);
+    }
+}
+
+void stats_event_sub(const char *source, const char *name, unsigned long value)
+{
+    stats_event_t *event = build_event (source, name, NULL);
+    if (event)
+    {
+        event->value = malloc (16);
+        snprintf (event->value, 16, "%ld", value);
+        event->action = STATS_EVENT_SUB;
+        queue_global_event (event);
+    }
+}
+
+/* decrease the value in the provided stat by 1 */
+void stats_event_dec(const char *source, const char *name)
+{
+    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
+    stats_event_t *event = build_event (source, name, NULL);
+    if (event)
+    {
+        event->action = STATS_EVENT_DEC;
+        queue_global_event (event);
+    }
+}
+
+/* note: you must call this function only when you have exclusive access
+** to the avl_tree
+*/
+static stats_node_t *_find_node(avl_tree *stats_tree, const char *name)
+{
+    stats_node_t *stats;
+    avl_node *node;
+    int cmp;
+
+    /* get the root node */
+    node = stats_tree->root->right;
+    
+    while (node) {
+        stats = (stats_node_t *)node->key;
+        cmp = strcmp(name, stats->name);
+        if (cmp < 0) 
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return stats;
+    }
+    
+    /* didn't find it */
+    return NULL;
+}
+
+/* note: you must call this function only when you have exclusive access
+** to the avl_tree
+*/
+static stats_source_t *_find_source(avl_tree *source_tree, const char *source)
+{
+    stats_source_t *stats;
+    avl_node *node;
+    int cmp;
+
+    /* get the root node */
+    node = source_tree->root->right;
+    while (node) {
+        stats = (stats_source_t *)node->key;
+        cmp = strcmp(source, stats->source);
+        if (cmp < 0)
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return stats;
+    }
+
+    /* didn't find it */
+    return NULL;
+}
+
+static stats_event_t *_copy_event(stats_event_t *event)
+{
+    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
+    if (event->source) 
+        copy->source = (char *)strdup(event->source);
+    else
+        copy->source = NULL;
+    if (event->name)
+        copy->name = (char *)strdup(event->name);
+    if (event->value)
+        copy->value = (char *)strdup(event->value);
+    else
+        copy->value = NULL;
+    copy->hidden = event->hidden;
+    copy->next = NULL;
+
+    return copy;
+}
+
+
+/* helper to apply specialised changes to a stats node */
+static void modify_node_event (stats_node_t *node, stats_event_t *event)
+{
+    char *str;
+
+    if (event->action == STATS_EVENT_HIDDEN)
+    {
+        if (event->value)
+            node->hidden = 1;
+        else
+            node->hidden = 0;
+        return;
+    }
+    if (event->action != STATS_EVENT_SET)
+    {
+        int value = 0;
+
+        switch (event->action)
+        {
+            case STATS_EVENT_INC:
+                value = atoi (node->value)+1;
+                break;
+            case STATS_EVENT_DEC:
+                value = atoi (node->value)-1;
+                break;
+            case STATS_EVENT_ADD:
+                value = atoi (node->value)+atoi (event->value);
+                break;
+            default:
+                break;
+        }
+        str = malloc (16);
+        snprintf (str, 16, "%d", value);
+        if (event->value == NULL)
+            event->value = strdup (str);
+    }
+    else
+        str = (char *)strdup (event->value);
+    free (node->value);
+    node->value = str;
+    DEBUG2 ("update node %s (%s)", node->name, node->value);
+}
+
+
+static void process_global_event (stats_event_t *event)
+{
+    stats_node_t *node;
+
+    /* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
+    if (event->action == STATS_EVENT_REMOVE)
+    {
+        /* we're deleting */
+        node = _find_node(_stats.global_tree, event->name);
+        if (node != NULL)
+            avl_delete(_stats.global_tree, (void *)node, _free_stats);
+        return;
+    }
+    node = _find_node(_stats.global_tree, event->name);
+    if (node)
+    {
+        modify_node_event (node, event);
+    }
+    else
+    {
+        /* add node */
+        node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
+        node->name = (char *)strdup(event->name);
+        node->value = (char *)strdup(event->value);
+
+        avl_insert(_stats.global_tree, (void *)node);
+    }
+}
+
+
+static void process_source_event (stats_event_t *event)
+{
+    stats_source_t *snode = _find_source(_stats.source_tree, event->source);
+    if (snode == NULL)
+    {
+        if (event->action == STATS_EVENT_REMOVE)
+            return;
+        snode = (stats_source_t *)calloc(1,sizeof(stats_source_t));
+        if (snode == NULL)
+            return;
+        DEBUG1 ("new source stat %s", event->source);
+        snode->source = (char *)strdup(event->source);
+        snode->stats_tree = avl_tree_new(_compare_stats, NULL);
+        if (event->action == STATS_EVENT_HIDDEN)
+            snode->hidden = 1;
+        else
+            snode->hidden = 0;
+
+        avl_insert(_stats.source_tree, (void *)snode);
+    }
+    if (event->name)
+    {
+        stats_node_t *node = _find_node(snode->stats_tree, event->name);
+        if (node == NULL)
+        {
+            if (event->action == STATS_EVENT_REMOVE)
+                return;
+            /* adding node */
+            if (event->value)
+            {
+                DEBUG2 ("new node %s (%s)", event->name, event->value);
+                node = (stats_node_t *)calloc(1,sizeof(stats_node_t));
+                node->name = (char *)strdup(event->name);
+                node->value = (char *)strdup(event->value);
+                node->hidden = snode->hidden;
+
+                avl_insert(snode->stats_tree, (void *)node);
+            }
+            return;
+        }
+        if (event->action == STATS_EVENT_REMOVE)
+        {
+            DEBUG1 ("delete node %s", event->name);
+            avl_delete(snode->stats_tree, (void *)node, _free_stats);
+            return;
+        }
+        modify_node_event (node, event);
+        return;
+    }
+    if (event->action == STATS_EVENT_HIDDEN)
+    {
+        avl_node *node = avl_get_first (snode->stats_tree);
+
+        if (event->value)
+            snode->hidden = 1;
+        else
+            snode->hidden = 0;
+        while (node)
+        {
+            stats_node_t *stats = (stats_node_t*)node->key;
+            stats->hidden = snode->hidden;
+            node = avl_get_next (node);
+        }
+        return;
+    }
+    if (event->action == STATS_EVENT_REMOVE)
+    {
+        DEBUG1 ("delete source node %s", event->source);
+        avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
+    }
+}
+
+
+void stats_event_time (const char *mount, const char *name)
+{
+    time_t now = time(NULL);
+    struct tm local;
+    char buffer[100];
+
+    localtime_r (&now, &local);
+    strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S %z", &local);
+    stats_event (mount, name, buffer);
+}
+
+
+void stats_global (ice_config_t *config)
+{
+    stats_event (NULL, "server_id", config->server_id);
+    stats_event (NULL, "host", config->hostname);
+    stats_event (NULL, "location", config->location);
+    stats_event (NULL, "admin", config->admin);
+}
+
+
+static void *_stats_thread(void *arg)
+{
+    stats_event_t *event;
+    stats_event_t *copy;
+    event_listener_t *listener;
+
+    stats_event_time (NULL, "server_start");
+
+    /* global currently active stats */
+    stats_event (NULL, "clients", "0");
+    stats_event (NULL, "connections", "0");
+    stats_event (NULL, "sources", "0");
+    stats_event (NULL, "stats", "0");
+    stats_event (NULL, "listeners", "0");
+
+    /* global accumulating stats */
+    stats_event (NULL, "client_connections", "0");
+    stats_event (NULL, "source_client_connections", "0");
+    stats_event (NULL, "source_relay_connections", "0");
+    stats_event (NULL, "source_total_connections", "0");
+    stats_event (NULL, "stats_connections", "0");
+    stats_event (NULL, "listener_connections", "0");
+
+    INFO0 ("stats thread started");
+    while (_stats_running) {
+        if (_global_event_queue.head != NULL) {
+            /* grab the next event from the queue */
+            thread_mutex_lock(&_global_event_mutex);
+            event = _get_event_from_queue (&_global_event_queue);
+            thread_mutex_unlock(&_global_event_mutex);
+
+            if (event == NULL)
+                continue;
+            event->next = NULL;
+
+            thread_mutex_lock(&_stats_mutex);
+
+            /* check if we are dealing with a global or source event */
+            if (event->source == NULL)
+                process_global_event (event);
+            else
+                process_source_event (event);
+            
+            /* now we have an event that's been processed into the running stats */
+            /* this event should get copied to event listeners' queues */
+            listener = (event_listener_t *)_event_listeners;
+            while (listener) {
+                copy = _copy_event(event);
+                thread_mutex_lock (&listener->mutex);
+                _add_event_to_queue (copy, &listener->queue);
+                thread_mutex_unlock (&listener->mutex);
+
+                listener = listener->next;
+            }
+
+            /* now we need to destroy the event */
+            _free_event(event);
+
+            thread_mutex_unlock(&_stats_mutex);
+            continue;
+        }
+
+        thread_sleep(300000);
+    }
+
+    return NULL;
+}
+
+/* you must have the _stats_mutex locked here */
+static void _unregister_listener(event_listener_t *listener)
+{
+    event_listener_t **prev = (event_listener_t **)&_event_listeners,
+                     *current = *prev;
+    while (current)
+    {
+        if (current == listener)
+        {
+            *prev = current->next;
+            break;
+        }
+        prev = &current->next;
+        current = *prev;
+    }
+}
+
+
+static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
+{
+    stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
+    
+    if (source != NULL)
+        event->source = (char *)strdup(source);
+    else
+        event->source = NULL;
+    event->name = (char *)strdup(node->name);
+    event->value = (char *)strdup(node->value);
+    event->hidden = node->hidden;
+    event->action = STATS_EVENT_SET;
+    event->next = NULL;
+
+    return event;
+}
+
+
+static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue)
+{
+    *queue->tail = event;
+    queue->tail = (volatile stats_event_t **)&event->next;
+}
+
+
+static stats_event_t *_get_event_from_queue (event_queue_t *queue)
+{
+    stats_event_t *event = NULL;
+
+    if (queue && queue->head)
+    {
+        event = (stats_event_t *)queue->head;
+        queue->head = event->next;
+        if (queue->head == NULL)
+            queue->tail = &queue->head;
+    }
+
+    return event;
+}
+
+static int _send_event_to_client(stats_event_t *event, client_t *client)
+{
+    int len;
+    char buf [200];
+
+    /* send data to the client!!!! */
+    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
+            (event->source != NULL) ? event->source : "global",
+            event->name ? event->name : "null",
+            event->value ? event->value : "null");
+    if (len > 0 && len < (int)sizeof (buf))
+    {
+        client_send_bytes (client, buf, len);
+        if (client->con->error)
+            return -1;
+    }
+    return 0;
+}
+
+void _dump_stats_to_queue (event_queue_t *queue)
+{
+    avl_node *node;
+    avl_node *node2;
+    stats_event_t *event;
+    stats_source_t *source;
+
+    thread_mutex_lock(&_stats_mutex);
+    /* first we fill our queue with the current stats */
+    /* start with the global stats */
+    node = avl_get_first(_stats.global_tree);
+    while (node) {
+        event = _make_event_from_node((stats_node_t *)node->key, NULL);
+        _add_event_to_queue(event, queue);
+
+        node = avl_get_next(node);
+    }
+
+    /* now the stats for each source */
+    node = avl_get_first(_stats.source_tree);
+    while (node) {
+        source = (stats_source_t *)node->key;
+        node2 = avl_get_first(source->stats_tree);
+        while (node2) {
+            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
+            _add_event_to_queue(event, queue);
+
+            node2 = avl_get_next(node2);
+        }
+        
+        node = avl_get_next(node);
+    }
+    thread_mutex_unlock(&_stats_mutex);
+}
+
+/* factoring out code for stats loops
+** this function copies all stats to queue, and registers 
+** the queue for all new events atomically.
+** note: mutex must already be created!
+*/
+static void _register_listener (event_listener_t *listener)
+{
+    avl_node *node;
+    avl_node *node2;
+    stats_event_t *event;
+    stats_source_t *source;
+
+    thread_mutex_lock(&_stats_mutex);
+
+    /* first we fill our queue with the current stats */
+    
+    /* start with the global stats */
+    node = avl_get_first(_stats.global_tree);
+    while (node) {
+        event = _make_event_from_node((stats_node_t *)node->key, NULL);
+        _add_event_to_queue (event, &listener->queue);
+
+        node = avl_get_next(node);
+    }
+
+    /* now the stats for each source */
+    node = avl_get_first(_stats.source_tree);
+    while (node) {
+        source = (stats_source_t *)node->key;
+        node2 = avl_get_first(source->stats_tree);
+        while (node2) {
+            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
+            _add_event_to_queue (event, &listener->queue);
+
+            node2 = avl_get_next(node2);
+        }
+        
+        node = avl_get_next(node);
+    }
+
+    /* now we register to receive future event notices */
+    listener->next = (event_listener_t *)_event_listeners;
+    _event_listeners = listener;
+
+    thread_mutex_unlock(&_stats_mutex);
+}
+
+void *stats_connection(void *arg)
+{
+    client_t *client = (client_t *)arg;
+    stats_event_t *event;
+    event_listener_t listener;
+
+    INFO0 ("stats client starting");
+
+    event_queue_init (&listener.queue);
+    /* increment the thread count */
+    thread_mutex_lock(&_stats_mutex);
+    _stats_threads++;
+    stats_event_args (NULL, "stats", "%d", _stats_threads);
+    thread_mutex_unlock(&_stats_mutex);
+
+    thread_mutex_create (&(listener.mutex));
+
+    _register_listener (&listener);
+
+    while (_stats_running) {
+        thread_mutex_lock (&listener.mutex);
+        event = _get_event_from_queue (&listener.queue);
+        thread_mutex_unlock (&listener.mutex);
+        if (event != NULL) {
+            if (_send_event_to_client(event, client) < 0) {
+                _free_event(event);
+                break;
+            }
+            _free_event(event);
+            continue;
+        }
+        thread_sleep (500000);
+    }
+
+    thread_mutex_lock(&_stats_mutex);
+    _unregister_listener (&listener);
+    _stats_threads--;
+    stats_event_args (NULL, "stats", "%d", _stats_threads);
+    thread_mutex_unlock(&_stats_mutex);
+
+    thread_mutex_destroy (&listener.mutex);
+    client_destroy (client);
+    INFO0 ("stats client finished");
+
+    return NULL;
+}
+
+
+void stats_callback (client_t *client, void *notused)
+{
+    if (client->con->error)
+    {
+        client_destroy (client);
+        return;
+    }
+    client_set_queue (client, NULL);
+    thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
+}
+
+
+typedef struct _source_xml_tag {
+    char *mount;
+    xmlNodePtr node;
+
+    struct _source_xml_tag *next;
+} source_xml_t;
+
+static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
+{
+    source_xml_t *node, *node2;
+    int found = 0;
+
+    /* search for existing node */
+    node = *list;
+    while (node) {
+        if (strcmp(node->mount, mount) == 0) {
+            found = 1;
+            break;
+        }
+        node = node->next;
+    }
+
+    if (found) return node->node;
+
+    /* if we didn't find it, we must build it and add it to the list */
+
+    /* build node */
+    node = (source_xml_t *)malloc(sizeof(source_xml_t));
+    node->mount = strdup(mount);
+    node->node = xmlNewChild (root, NULL, XMLSTR("source"), NULL);
+    xmlSetProp (node->node, XMLSTR("mount"), XMLSTR(mount));
+    node->next = NULL;
+
+    /* add node */
+    if (*list == NULL) {
+        *list = node;
+    } else {
+        node2 = *list;
+        while (node2->next) node2 = node2->next;
+        node2->next = node;
+    }
+
+    return node->node;
+}
+
+void stats_transform_xslt(client_t *client, const char *uri)
+{
+    xmlDocPtr doc;
+    char *xslpath = util_get_path_from_normalised_uri (uri);
+    const char *mount = httpp_get_query_param (client->parser, "mount");
+
+    stats_get_xml(&doc, 0, mount);
+
+    xslt_transform(doc, xslpath, client);
+
+    xmlFreeDoc(doc);
+    free (xslpath);
+}
+
+void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
+{
+    stats_event_t *event;
+    event_queue_t queue;
+    xmlNodePtr node, srcnode;
+    source_xml_t *src_nodes = NULL;
+    source_xml_t *next;
+
+    event_queue_init (&queue);
+    _dump_stats_to_queue (&queue);
+
+    *doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode(*doc, NULL, XMLSTR("icestats"), NULL);
+    xmlDocSetRootElement(*doc, node);
+
+    event = _get_event_from_queue(&queue);
+    while (event)
+    {
+        if (event->hidden <= show_hidden)
+        {
+            do
+            {
+                xmlChar *name, *value;
+                name = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->name));
+                value = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->value));
+                srcnode = node;
+                if (event->source)
+                {
+                    if (show_mount && strcmp (event->source, show_mount) != 0)
+                        break;
+                    srcnode = _find_xml_node(event->source, &src_nodes, node);
+                }
+                else
+                    srcnode = node;
+                xmlNewChild(srcnode, NULL, XMLSTR(name), XMLSTR(value));
+                xmlFree (value);
+                xmlFree (name);
+            } while (0);
+        }
+
+        _free_event(event);
+        event = _get_event_from_queue(&queue);
+    }
+
+    while (src_nodes) {
+        next = src_nodes->next;
+        free(src_nodes->mount);
+        free(src_nodes);
+        src_nodes = next;
+    }
+}
+void stats_sendxml(client_t *client)
+{
+    int bytes;
+    stats_event_t *event;
+    event_queue_t queue;
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode;
+    int len;
+    xmlChar *buff = NULL;
+    source_xml_t *snd;
+    source_xml_t *src_nodes = NULL;
+
+    event_queue_init (&queue);
+    _dump_stats_to_queue (&queue);
+
+    doc = xmlNewDoc (XMLSTR("1.0"));
+    node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
+    xmlDocSetRootElement(doc, node);
+
+
+    event = _get_event_from_queue(&queue);
+    while (event) {
+        if (event->source == NULL) {
+            xmlNewChild (node, NULL, XMLSTR(event->name), XMLSTR(event->value));
+        } else {
+            srcnode = _find_xml_node(event->source, &src_nodes, node);
+            xmlNewChild (srcnode, NULL, XMLSTR(event->name), XMLSTR(event->value));
+        }
+
+        _free_event(event);
+        event = _get_event_from_queue(&queue);
+    }
+
+    xmlDocDumpMemory(doc, &buff, &len);
+    xmlFreeDoc(doc);
+    
+    client->respcode = 200;
+    bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n"
+               "Content-Length: %d\r\n"
+               "Content-Type: text/xml\r\n"
+               "\r\n", len);
+    if (bytes > 0) client->con->sent_bytes += bytes;
+    else goto send_error;
+
+    bytes = client_send_bytes (client, buff, (unsigned)len);
+
+ send_error:
+    while (src_nodes) {
+        snd = src_nodes->next;
+        free(src_nodes->mount);
+        free(src_nodes);
+        src_nodes = snd;
+    }
+    if (buff) xmlFree(buff);
+}
+
+static int _compare_stats(void *arg, void *a, void *b)
+{
+    stats_node_t *nodea = (stats_node_t *)a;
+    stats_node_t *nodeb = (stats_node_t *)b;
+
+    return strcmp(nodea->name, nodeb->name);
+}
+
+static int _compare_source_stats(void *arg, void *a, void *b)
+{
+    stats_source_t *nodea = (stats_source_t *)a;
+    stats_source_t *nodeb = (stats_source_t *)b;
+
+    return strcmp(nodea->source, nodeb->source);
+}
+
+static int _free_stats(void *key)
+{
+    stats_node_t *node = (stats_node_t *)key;
+    free(node->value);
+    free(node->name);
+    free(node);
+    
+    return 1;
+}
+
+static int _free_source_stats(void *key)
+{
+    stats_source_t *node = (stats_source_t *)key;
+    avl_tree_free(node->stats_tree, _free_stats);
+    free(node->source);
+    free(node);
+
+    return 1;
+}
+
+static void _free_event(stats_event_t *event)
+{
+    if (event->source) free(event->source);
+    if (event->name) free(event->name);
+    if (event->value) free(event->value);
+    free(event);
+}
+
+
+refbuf_t *stats_get_streams (void)
+{
+#define STREAMLIST_BLKSIZE  4096
+    avl_node *node;
+    unsigned int remaining = STREAMLIST_BLKSIZE;
+    refbuf_t *start = refbuf_new (remaining), *cur = start;
+    char *buffer = cur->data;
+
+    /* now the stats for each source */
+    thread_mutex_lock (&_stats_mutex);
+    node = avl_get_first(_stats.source_tree);
+    while (node)
+    {
+        int ret;
+        stats_source_t *source = (stats_source_t *)node->key;
+
+        if (source->hidden == 0)
+        {
+            if (remaining <= strlen (source->source) + 3)
+            {
+                cur->len = STREAMLIST_BLKSIZE - remaining;
+                cur->next = refbuf_new (STREAMLIST_BLKSIZE);
+                remaining = STREAMLIST_BLKSIZE;
+                cur = cur->next;
+                buffer = cur->data;
+            }
+            ret = snprintf (buffer, remaining, "%s\r\n", source->source);
+            if (ret > 0)
+            {
+                buffer += ret;
+                remaining -= ret;
+            }
+        }
+        node = avl_get_next(node);
+    }
+    thread_mutex_unlock (&_stats_mutex);
+    cur->len = STREAMLIST_BLKSIZE - remaining;
+    return start;
+}
+
+
+
+/* This removes any source stats from virtual mountpoints, ie mountpoints
+ * where no source_t exists. This function requires the global sources lock
+ * to be held before calling.
+ */
+void stats_clear_virtual_mounts (void)
+{
+    avl_node *snode;
+
+    thread_mutex_lock (&_stats_mutex);
+    snode = avl_get_first(_stats.source_tree);
+    while (snode)
+    {
+        stats_source_t *src = (stats_source_t *)snode->key;
+        source_t *source = source_find_mount_raw (src->source);
+
+        if (source == NULL)
+        {
+            /* no source_t is reserved so remove them now */
+            snode = avl_get_next (snode);
+            DEBUG1 ("releasing %s stats", src->source);
+            avl_delete (_stats.source_tree, src, _free_source_stats);
+            continue;
+        }
+
+        snode = avl_get_next (snode);
+    }
+    thread_mutex_unlock (&_stats_mutex);
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/stats.h
===================================================================
--- icecast/trunk/icecast/src/stats.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/stats.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,101 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __STATS_H__
-#define __STATS_H__
-
-#include "connection.h"
-#include "httpp/httpp.h"
-#include "client.h"
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-
-typedef struct _stats_node_tag
-{
-    char *name;
-    char *value;
-    int hidden;
-} stats_node_t;
-
-typedef struct _stats_event_tag
-{
-    char *source;
-    char *name;
-    char *value;
-    int  hidden;
-    int  action;
-
-    struct _stats_event_tag *next;
-} stats_event_t;
-
-typedef struct _stats_source_tag
-{
-    char *source;
-    int  hidden;
-    avl_tree *stats_tree;
-} stats_source_t;
-
-typedef struct _stats_tag
-{
-    avl_tree *global_tree;
-
-    /* global stats
-    start_time
-    total_users
-    max_users
-    total_sources
-    max_sources
-    total_user_connections
-    total_source_connections
-    */
-
-    avl_tree *source_tree;
-
-    /* stats by source, and for stats
-    start_time
-    total_users
-    max_users
-    */
-
-} stats_t;
-
-void stats_initialize(void);
-void stats_shutdown(void);
-
-stats_t *stats_get_stats(void);
-void stats_get_streamlist (char *buffer, size_t remaining);
-void stats_clear_virtual_mounts (void);
-
-void stats_event(const char *source, const char *name, const char *value);
-void stats_event_args(const char *source, char *name, char *format, ...);
-void stats_event_inc(const char *source, const char *name);
-void stats_event_add(const char *source, const char *name, unsigned long value);
-void stats_event_dec(const char *source, const char *name);
-void stats_event_hidden (const char *source, const char *name, int hidden);
-void stats_event_time (const char *mount, const char *name);
-
-void *stats_connection(void *arg);
-void stats_callback (client_t *client, void *notused);
-
-void stats_transform_xslt(client_t *client, const char *uri);
-void stats_sendxml(client_t *client);
-void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount);
-char *stats_get_value(char *source, char *name);
-
-#endif  /* __STATS_H__ */
-
-
-
-
-

Copied: icecast/tags/icecast-2.3.2/src/stats.h (from rev 14812, icecast/trunk/icecast/src/stats.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/stats.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/stats.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,102 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __STATS_H__
+#define __STATS_H__
+
+#include "cfgfile.h"
+#include "connection.h"
+#include "httpp/httpp.h"
+#include "client.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+
+typedef struct _stats_node_tag
+{
+    char *name;
+    char *value;
+    int hidden;
+} stats_node_t;
+
+typedef struct _stats_event_tag
+{
+    char *source;
+    char *name;
+    char *value;
+    int  hidden;
+    int  action;
+
+    struct _stats_event_tag *next;
+} stats_event_t;
+
+typedef struct _stats_source_tag
+{
+    char *source;
+    int  hidden;
+    avl_tree *stats_tree;
+} stats_source_t;
+
+typedef struct _stats_tag
+{
+    avl_tree *global_tree;
+
+    /* global stats
+    start_time
+    total_users
+    max_users
+    total_sources
+    max_sources
+    total_user_connections
+    total_source_connections
+    */
+
+    avl_tree *source_tree;
+
+    /* stats by source, and for stats
+    start_time
+    total_users
+    max_users
+    */
+
+} stats_t;
+
+void stats_initialize(void);
+void stats_shutdown(void);
+
+void stats_global(ice_config_t *config);
+stats_t *stats_get_stats(void);
+refbuf_t *stats_get_streams (void);
+void stats_clear_virtual_mounts (void);
+
+void stats_event(const char *source, const char *name, const char *value);
+void stats_event_conv(const char *mount, const char *name,
+        const char *value, const char *charset);
+void stats_event_args(const char *source, char *name, char *format, ...);
+void stats_event_inc(const char *source, const char *name);
+void stats_event_add(const char *source, const char *name, unsigned long value);
+void stats_event_sub(const char *source, const char *name, unsigned long value);
+void stats_event_dec(const char *source, const char *name);
+void stats_event_hidden (const char *source, const char *name, int hidden);
+void stats_event_time (const char *mount, const char *name);
+
+void *stats_connection(void *arg);
+void stats_callback (client_t *client, void *notused);
+
+void stats_transform_xslt(client_t *client, const char *uri);
+void stats_sendxml(client_t *client);
+void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount);
+char *stats_get_value(const char *source, const char *name);
+
+#endif  /* __STATS_H__ */
+

Deleted: icecast/tags/icecast-2.3.2/src/util.c
===================================================================
--- icecast/trunk/icecast/src/util.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/util.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,634 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifndef _WIN32
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#ifdef HAVE_POLL
-#include <sys/poll.h>
-#endif
-#else
-#include <winsock2.h>
-#include <windows.h>
-#include <stdio.h>
-#define snprintf _snprintf
-#define strcasecmp stricmp
-#define strncasecmp strnicmp
-#endif
-
-#include "net/sock.h"
-#include "thread/thread.h"
-
-#include "cfgfile.h"
-#include "util.h"
-#include "compat.h"
-#include "refbuf.h"
-#include "connection.h"
-#include "client.h"
-
-#define CATMODULE "util"
-
-#include "logging.h"
-
-/* Abstract out an interface to use either poll or select depending on which
- * is available (poll is preferred) to watch a single fd.
- *
- * timeout is in milliseconds.
- *
- * returns > 0 if activity on the fd occurs before the timeout.
- *           0 if no activity occurs
- *         < 0 for error.
- */
-int util_timed_wait_for_fd(int fd, int timeout)
-{
-#ifdef HAVE_POLL
-    struct pollfd ufds;
-
-    ufds.fd = fd;
-    ufds.events = POLLIN;
-    ufds.revents = 0;
-
-    return poll(&ufds, 1, timeout);
-#else
-    fd_set rfds;
-    struct timeval tv, *p=NULL;
-
-    FD_ZERO(&rfds);
-    FD_SET(fd, &rfds);
-
-    if(timeout >= 0) {
-        tv.tv_sec = timeout/1000;
-        tv.tv_usec = (timeout % 1000)*1000;
-        p = &tv;
-    }
-    return select(fd+1, &rfds, NULL, NULL, p);
-#endif
-}
-
-int util_read_header(int sock, char *buff, unsigned long len, int entire)
-{
-    int read_bytes, ret;
-    unsigned long pos;
-    char c;
-    ice_config_t *config;
-    int header_timeout;
-
-    config = config_get_config();
-    header_timeout = config->header_timeout;
-    config_release_config();
-
-    read_bytes = 1;
-    pos = 0;
-    ret = 0;
-
-    while ((read_bytes == 1) && (pos < (len - 1))) {
-        read_bytes = 0;
-
-        if (util_timed_wait_for_fd(sock, header_timeout*1000) > 0) {
-
-            if ((read_bytes = recv(sock, &c, 1, 0))) {
-                if (c != '\r') buff[pos++] = c;
-                if (entire) {
-                    if ((pos > 1) && (buff[pos - 1] == '\n' && 
-                                      buff[pos - 2] == '\n')) {
-                        ret = 1;
-                        break;
-                    }
-                }
-                else {
-                    if ((pos > 1) && (buff[pos - 1] == '\n')) {
-                        ret = 1;
-                        break;
-                    }
-                }
-            }
-        } else {
-            break;
-        }
-    }
-
-    if (ret) buff[pos] = '\0';
-    
-    return ret;
-}
-
-char *util_get_extension(const char *path) {
-    char *ext = strrchr(path, '.');
-
-    if(ext == NULL)
-        return "";
-    else
-        return ext+1;
-}
-
-int util_check_valid_extension(char *uri) {
-    int    ret = 0;
-    char    *p2;
-
-    if (uri) {
-        p2 = strrchr(uri, '.');
-        if (p2) {
-            p2++;
-            if (strncmp(p2, "xsl", strlen("xsl")) == 0) {
-                /* Build the full path for the request, concatenating the webroot from the config.
-                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
-                */
-                ret = XSLT_CONTENT;
-            }
-            if (strncmp(p2, "htm", strlen("htm")) == 0) {
-                /* Build the full path for the request, concatenating the webroot from the config.
-                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
-                */
-                ret = HTML_CONTENT;
-            }
-            if (strncmp(p2, "html", strlen("html")) == 0) {
-                /* Build the full path for the request, concatenating the webroot from the config.
-                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
-                */
-                ret = HTML_CONTENT;
-            }
-
-        }
-    }
-    return ret;
-}
-
-static int hex(char c)
-{
-    if(c >= '0' && c <= '9')
-        return c - '0';
-    else if(c >= 'A' && c <= 'F')
-        return c - 'A' + 10;
-    else if(c >= 'a' && c <= 'f')
-        return c - 'a' + 10;
-    else
-        return -1;
-}
-
-static int verify_path(char *path) {
-    int dir = 0, indotseq = 0;
-
-    while(*path) {
-        if(*path == '/' || *path == '\\') {
-            if(indotseq)
-                return 0;
-            if(dir)
-                return 0;
-            dir = 1;
-            path++;
-            continue;
-        }
-
-        if(dir || indotseq) {
-            if(*path == '.')
-                indotseq = 1;
-            else
-                indotseq = 0;
-        }
-        
-        dir = 0;
-        path++;
-    }
-
-    return 1;
-}
-
-char *util_get_path_from_uri(char *uri) {
-    char *path = util_normalise_uri(uri);
-    char *fullpath;
-
-    if(!path)
-        return NULL;
-    else {
-        fullpath = util_get_path_from_normalised_uri(path);
-        free(path);
-        return fullpath;
-    }
-}
-
-char *util_get_path_from_normalised_uri(const char *uri) {
-    char *fullpath;
-    char *webroot;
-    ice_config_t *config = config_get_config();
-
-    webroot = config->webroot_dir;
-
-    fullpath = malloc(strlen(uri) + strlen(webroot) + 1);
-    if (fullpath)
-        sprintf (fullpath, "%s%s", webroot, uri);
-    config_release_config();
-
-    return fullpath;
-}
-
-static char hexchars[16] = {
-    '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
-};
-
-static char safechars[256] = {
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
-      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
-      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
-      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
-      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-};
-
-char *util_url_escape(char *src)
-{
-    int len = strlen(src);
-    /* Efficiency not a big concern here, keep the code simple/conservative */
-    char *dst = calloc(1, len*3 + 1); 
-    unsigned char *source = src;
-    int i,j=0;
-
-    for(i=0; i < len; i++) {
-        if(safechars[source[i]]) {
-            dst[j++] = source[i];
-        }
-        else {
-            dst[j] = '%';
-            dst[j+1] = hexchars[ (source[i] >> 4) & 0xf ];
-            dst[j+2] = hexchars[ source[i] & 0xf ];
-            j+= 3;
-        }
-    }
-
-    dst[j] = 0;
-    return dst;
-}
-
-char *util_url_unescape(char *src)
-{
-    int len = strlen(src);
-    unsigned char *decoded;
-    int i;
-    char *dst;
-    int done = 0;
-
-    decoded = calloc(1, len + 1);
-
-    dst = decoded;
-
-    for(i=0; i < len; i++) {
-        switch(src[i]) {
-            case '%':
-                if(i+2 >= len) {
-                    free(decoded);
-                    return NULL;
-                }
-                if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
-                    free(decoded);
-                    return NULL;
-                }
-
-                *dst++ = hex(src[i+1]) * 16  + hex(src[i+2]);
-                i+= 2;
-                break;
-            case '#':
-                done = 1;
-                break;
-            case 0:
-                ERROR0("Fatal internal logic error in util_url_unescape()");
-                free(decoded);
-                return NULL;
-                break;
-            default:
-                *dst++ = src[i];
-                break;
-        }
-        if(done)
-            break;
-    }
-
-    *dst = 0; /* null terminator */
-
-    return decoded;
-}
-
-/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
- * path contains 'disallowed' sequences like foo/../ (which could be used to
- * escape from the webroot) or if it cannot be URI-decoded.
- * Caller should free the path.
- */
-char *util_normalise_uri(char *uri) {
-    char *path;
-
-    if(uri[0] != '/')
-        return NULL;
-
-    path = util_url_unescape(uri);
-
-    if(path == NULL) {
-        WARN1("Error decoding URI: %s\n", uri);
-        return NULL;
-    }
-
-    /* We now have a full URI-decoded path. Check it for allowability */
-    if(verify_path(path))
-        return path;
-    else {
-        WARN1("Rejecting invalid path \"%s\"", path);
-        free(path);
-        return NULL;
-    }
-}
-
-static char base64table[64] = {
-    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
-    'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
-    'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
-    'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
-};
-
-static signed char base64decode[256] = {
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
-     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
-     -2,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
-     -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
-};
-
-char *util_bin_to_hex(unsigned char *data, int len)
-{
-    char *hex = malloc(len*2 + 1);
-    int i;
-
-    for(i = 0; i < len; i++) {
-        hex[i*2] = hexchars[(data[i]&0xf0) >> 4];
-        hex[i*2+1] = hexchars[data[i]&0x0f];
-    }
-
-    hex[len*2] = 0;
-
-    return hex;
-}
-
-/* This isn't efficient, but it doesn't need to be */
-char *util_base64_encode(char *data)
-{
-    int len = strlen(data);
-    char *out = malloc(len*4/3 + 4);
-    char *result = out;
-    int chunk;
-
-    while(len > 0) {
-        chunk = (len >3)?3:len;
-        *out++ = base64table[(*data & 0xFC)>>2];
-        *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
-        switch(chunk) {
-            case 3:
-                *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
-                *out++ = base64table[(*(data+2)) & 0x3F];
-                break;
-            case 2:
-                *out++ = base64table[((*(data+1) & 0x0F)<<2)];
-                *out++ = '=';
-                break;
-            case 1:
-                *out++ = '=';
-                *out++ = '=';
-                break;
-        }
-        data += chunk;
-        len -= chunk;
-    }
-    *out = 0;
-
-    return result;
-}
-
-char *util_base64_decode(unsigned char *input)
-{
-    int len = strlen(input);
-    char *out = malloc(len*3/4 + 5);
-    char *result = out;
-    signed char vals[4];
-
-    while(len > 0) {
-        if(len < 4)
-        {
-            free(result);
-            return NULL; /* Invalid Base64 data */
-        }
-
-        vals[0] = base64decode[*input++];
-        vals[1] = base64decode[*input++];
-        vals[2] = base64decode[*input++];
-        vals[3] = base64decode[*input++];
-
-        if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
-            len -= 4;
-            continue;
-        }
-
-        *out++ = vals[0]<<2 | vals[1]>>4;
-        /* vals[3] and (if that is) vals[2] can be '=' as padding, which is
-           looked up in the base64decode table as '-1'. Check for this case,
-           and output zero-terminators instead of characters if we've got
-           padding. */
-        if(vals[2] >= 0)
-            *out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
-        else
-            *out++ = 0;
-
-        if(vals[3] >= 0)
-            *out++ = ((vals[2]&0x03)<<6) | (vals[3]);
-        else
-            *out++ = 0;
-
-        len -= 4;
-    }
-    *out = 0;
-
-    return result;
-}
-
-util_dict *util_dict_new(void)
-{
-    return (util_dict *)calloc(1, sizeof(util_dict));
-}
-
-void util_dict_free(util_dict *dict)
-{
-    util_dict *next;
-
-    while (dict) {
-        next = dict->next;
-
-        if (dict->key)
-            free (dict->key);
-        if (dict->val)
-            free (dict->val);
-        free (dict);
-
-        dict = next;
-    }
-}
-
-const char *util_dict_get(util_dict *dict, const char *key)
-{
-    while (dict) {
-        if (!strcmp(key, dict->key))
-            return dict->val;
-        dict = dict->next;
-    }
-    return NULL;
-}
-
-int util_dict_set(util_dict *dict, const char *key, const char *val)
-{
-    util_dict *prev;
-
-    if (!dict || !key) {
-        ERROR0("NULL values passed to util_dict_set()");
-        return 0;
-    }
-
-    prev = NULL;
-    while (dict) {
-        if (!dict->key || !strcmp(dict->key, key))
-            break;
-        prev = dict;
-        dict = dict->next;
-    }
-
-    if (!dict) {
-        dict = util_dict_new();
-        if (!dict) {
-            ERROR0("unable to allocate new dictionary");
-            return 0;
-        }
-        if (prev)
-            prev->next = dict;
-    }
-
-    if (dict->key)
-        free (dict->val);
-    else if (!(dict->key = strdup(key))) {
-        if (prev)
-            prev->next = NULL;
-        util_dict_free (dict);
-
-        ERROR0("unable to allocate new dictionary key");
-        return 0;
-    }
-
-    dict->val = strdup(val);
-    if (!dict->val) {
-        ERROR0("unable to allocate new dictionary value");
-        return 0;
-    }
-
-    return 1;
-}
-
-/* given a dictionary, URL-encode each val and 
-   stringify it in order as key=val&key=val... if val 
-   is set, or just key&key if val is NULL.
-  TODO: Memory management needs overhaul. */
-char *util_dict_urlencode(util_dict *dict, char delim)
-{
-    char *res, *tmp;
-    char *enc;
-    int start = 1;
-
-    for (res = NULL; dict; dict = dict->next) {
-        /* encode key */
-        if (!dict->key)
-            continue;
-        if (start) {
-            if (!(res = malloc(strlen(dict->key) + 1))) {
-                return NULL;
-            }
-            sprintf(res, "%s", dict->key);
-            start = 0;
-        } else {
-            if (!(tmp = realloc(res, strlen(res) + strlen(dict->key) + 2))) {
-                free(res);
-                return NULL;
-            } else
-                res = tmp;
-            sprintf(res + strlen(res), "%c%s", delim, dict->key);
-        }
-
-        /* encode value */
-        if (!dict->val)
-            continue;
-        if (!(enc = util_url_escape(dict->val))) {
-            free(res);
-            return NULL;
-        }
-
-        if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
-            free(enc);
-            free(res);
-            return NULL;
-        } else
-            res = tmp;
-        sprintf(res + strlen(res), "=%s", enc);
-        free(enc);
-    }
-
-    return res;
-}
-
-#ifndef HAVE_LOCALTIME_R
-struct tm *localtime_r (const time_t *timep, struct tm *result)
-{
-     static mutex_t localtime_lock;
-     static int initialised = 0;
-     struct tm *tm;
-
-     if (initialised == 0)
-     {
-         thread_mutex_create (&localtime_lock);
-         initialised = 1;
-     }
-     thread_mutex_lock (&localtime_lock);
-     tm = localtime (timep);
-     memcpy (result, tm, sizeof (*result));
-     thread_mutex_unlock (&localtime_lock);
-     return result;
-}
-#endif

Copied: icecast/tags/icecast-2.3.2/src/util.c (from rev 14043, icecast/trunk/icecast/src/util.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/util.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/util.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,690 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+#else
+#include <winsock2.h>
+#include <windows.h>
+#include <stdio.h>
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#include "net/sock.h"
+#include "thread/thread.h"
+
+#include "cfgfile.h"
+#include "util.h"
+#include "compat.h"
+#include "refbuf.h"
+#include "connection.h"
+#include "client.h"
+
+#define CATMODULE "util"
+
+#include "logging.h"
+
+/* Abstract out an interface to use either poll or select depending on which
+ * is available (poll is preferred) to watch a single fd.
+ *
+ * timeout is in milliseconds.
+ *
+ * returns > 0 if activity on the fd occurs before the timeout.
+ *           0 if no activity occurs
+ *         < 0 for error.
+ */
+int util_timed_wait_for_fd(sock_t fd, int timeout)
+{
+#ifdef HAVE_POLL
+    struct pollfd ufds;
+
+    ufds.fd = fd;
+    ufds.events = POLLIN;
+    ufds.revents = 0;
+
+    return poll(&ufds, 1, timeout);
+#else
+    fd_set rfds;
+    struct timeval tv, *p=NULL;
+
+    FD_ZERO(&rfds);
+    FD_SET(fd, &rfds);
+
+    if(timeout >= 0) {
+        tv.tv_sec = timeout/1000;
+        tv.tv_usec = (timeout % 1000)*1000;
+        p = &tv;
+    }
+    return select(fd+1, &rfds, NULL, NULL, p);
+#endif
+}
+
+int util_read_header(sock_t sock, char *buff, unsigned long len, int entire)
+{
+    int read_bytes, ret;
+    unsigned long pos;
+    char c;
+    ice_config_t *config;
+    int header_timeout;
+
+    config = config_get_config();
+    header_timeout = config->header_timeout;
+    config_release_config();
+
+    read_bytes = 1;
+    pos = 0;
+    ret = 0;
+
+    while ((read_bytes == 1) && (pos < (len - 1))) {
+        read_bytes = 0;
+
+        if (util_timed_wait_for_fd(sock, header_timeout*1000) > 0) {
+
+            if ((read_bytes = recv(sock, &c, 1, 0))) {
+                if (c != '\r') buff[pos++] = c;
+                if (entire) {
+                    if ((pos > 1) && (buff[pos - 1] == '\n' && 
+                                      buff[pos - 2] == '\n')) {
+                        ret = 1;
+                        break;
+                    }
+                }
+                else {
+                    if ((pos > 1) && (buff[pos - 1] == '\n')) {
+                        ret = 1;
+                        break;
+                    }
+                }
+            }
+        } else {
+            break;
+        }
+    }
+
+    if (ret) buff[pos] = '\0';
+    
+    return ret;
+}
+
+char *util_get_extension(const char *path) {
+    char *ext = strrchr(path, '.');
+
+    if(ext == NULL)
+        return "";
+    else
+        return ext+1;
+}
+
+int util_check_valid_extension(const char *uri) {
+    int    ret = 0;
+    char    *p2;
+
+    if (uri) {
+        p2 = strrchr(uri, '.');
+        if (p2) {
+            p2++;
+            if (strncmp(p2, "xsl", strlen("xsl")) == 0) {
+                /* Build the full path for the request, concatenating the webroot from the config.
+                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
+                */
+                ret = XSLT_CONTENT;
+            }
+            if (strncmp(p2, "htm", strlen("htm")) == 0) {
+                /* Build the full path for the request, concatenating the webroot from the config.
+                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
+                */
+                ret = HTML_CONTENT;
+            }
+            if (strncmp(p2, "html", strlen("html")) == 0) {
+                /* Build the full path for the request, concatenating the webroot from the config.
+                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
+                */
+                ret = HTML_CONTENT;
+            }
+
+        }
+    }
+    return ret;
+}
+
+static int hex(char c)
+{
+    if(c >= '0' && c <= '9')
+        return c - '0';
+    else if(c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    else if(c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    else
+        return -1;
+}
+
+static int verify_path(char *path) {
+    int dir = 0, indotseq = 0;
+
+    while(*path) {
+        if(*path == '/' || *path == '\\') {
+            if(indotseq)
+                return 0;
+            if(dir)
+                return 0;
+            dir = 1;
+            path++;
+            continue;
+        }
+
+        if(dir || indotseq) {
+            if(*path == '.')
+                indotseq = 1;
+            else
+                indotseq = 0;
+        }
+        
+        dir = 0;
+        path++;
+    }
+
+    return 1;
+}
+
+char *util_get_path_from_uri(char *uri) {
+    char *path = util_normalise_uri(uri);
+    char *fullpath;
+
+    if(!path)
+        return NULL;
+    else {
+        fullpath = util_get_path_from_normalised_uri(path);
+        free(path);
+        return fullpath;
+    }
+}
+
+char *util_get_path_from_normalised_uri(const char *uri) {
+    char *fullpath;
+    char *webroot;
+    ice_config_t *config = config_get_config();
+
+    webroot = config->webroot_dir;
+
+    fullpath = malloc(strlen(uri) + strlen(webroot) + 1);
+    if (fullpath)
+        sprintf (fullpath, "%s%s", webroot, uri);
+    config_release_config();
+
+    return fullpath;
+}
+
+static char hexchars[16] = {
+    '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
+};
+
+static char safechars[256] = {
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
+      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
+      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+};
+
+char *util_url_escape (const char *src)
+{
+    int len = strlen(src);
+    /* Efficiency not a big concern here, keep the code simple/conservative */
+    char *dst = calloc(1, len*3 + 1); 
+    unsigned char *source = (unsigned char *)src;
+    int i,j=0;
+
+    for(i=0; i < len; i++) {
+        if(safechars[source[i]]) {
+            dst[j++] = source[i];
+        }
+        else {
+            dst[j] = '%';
+            dst[j+1] = hexchars[ (source[i] >> 4) & 0xf ];
+            dst[j+2] = hexchars[ source[i] & 0xf ];
+            j+= 3;
+        }
+    }
+
+    dst[j] = 0;
+    return dst;
+}
+
+char *util_url_unescape (const char *src)
+{
+    int len = strlen(src);
+    char *decoded;
+    int i;
+    char *dst;
+    int done = 0;
+
+    decoded = calloc(1, len + 1);
+
+    dst = decoded;
+
+    for(i=0; i < len; i++) {
+        switch(src[i]) {
+            case '%':
+                if(i+2 >= len) {
+                    free(decoded);
+                    return NULL;
+                }
+                if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
+                    free(decoded);
+                    return NULL;
+                }
+
+                *dst++ = hex(src[i+1]) * 16  + hex(src[i+2]);
+                i+= 2;
+                break;
+            case '#':
+                done = 1;
+                break;
+            case 0:
+                ERROR0("Fatal internal logic error in util_url_unescape()");
+                free(decoded);
+                return NULL;
+                break;
+            default:
+                *dst++ = src[i];
+                break;
+        }
+        if(done)
+            break;
+    }
+
+    *dst = 0; /* null terminator */
+
+    return decoded;
+}
+
+/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
+ * path contains 'disallowed' sequences like foo/../ (which could be used to
+ * escape from the webroot) or if it cannot be URI-decoded.
+ * Caller should free the path.
+ */
+char *util_normalise_uri(const char *uri) {
+    char *path;
+
+    if(uri[0] != '/')
+        return NULL;
+
+    path = util_url_unescape(uri);
+
+    if(path == NULL) {
+        WARN1("Error decoding URI: %s\n", uri);
+        return NULL;
+    }
+
+    /* We now have a full URI-decoded path. Check it for allowability */
+    if(verify_path(path))
+        return path;
+    else {
+        WARN1("Rejecting invalid path \"%s\"", path);
+        free(path);
+        return NULL;
+    }
+}
+
+static char base64table[64] = {
+    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+    'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+    'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+    'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+static signed char base64decode[256] = {
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
+     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
+     -2,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
+     -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
+};
+
+char *util_bin_to_hex(unsigned char *data, int len)
+{
+    char *hex = malloc(len*2 + 1);
+    int i;
+
+    for(i = 0; i < len; i++) {
+        hex[i*2] = hexchars[(data[i]&0xf0) >> 4];
+        hex[i*2+1] = hexchars[data[i]&0x0f];
+    }
+
+    hex[len*2] = 0;
+
+    return hex;
+}
+
+/* This isn't efficient, but it doesn't need to be */
+char *util_base64_encode(const char *data)
+{
+    int len = strlen(data);
+    char *out = malloc(len*4/3 + 4);
+    char *result = out;
+    int chunk;
+
+    while(len > 0) {
+        chunk = (len >3)?3:len;
+        *out++ = base64table[(*data & 0xFC)>>2];
+        *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
+        switch(chunk) {
+            case 3:
+                *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
+                *out++ = base64table[(*(data+2)) & 0x3F];
+                break;
+            case 2:
+                *out++ = base64table[((*(data+1) & 0x0F)<<2)];
+                *out++ = '=';
+                break;
+            case 1:
+                *out++ = '=';
+                *out++ = '=';
+                break;
+        }
+        data += chunk;
+        len -= chunk;
+    }
+    *out = 0;
+
+    return result;
+}
+
+char *util_base64_decode(const char *data)
+{
+    const unsigned char *input = (const unsigned char *)data;
+    int len = strlen (data);
+    char *out = malloc(len*3/4 + 5);
+    char *result = out;
+    signed char vals[4];
+
+    while(len > 0) {
+        if(len < 4)
+        {
+            free(result);
+            return NULL; /* Invalid Base64 data */
+        }
+
+        vals[0] = base64decode[*input++];
+        vals[1] = base64decode[*input++];
+        vals[2] = base64decode[*input++];
+        vals[3] = base64decode[*input++];
+
+        if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
+            len -= 4;
+            continue;
+        }
+
+        *out++ = vals[0]<<2 | vals[1]>>4;
+        /* vals[3] and (if that is) vals[2] can be '=' as padding, which is
+           looked up in the base64decode table as '-1'. Check for this case,
+           and output zero-terminators instead of characters if we've got
+           padding. */
+        if(vals[2] >= 0)
+            *out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
+        else
+            *out++ = 0;
+
+        if(vals[3] >= 0)
+            *out++ = ((vals[2]&0x03)<<6) | (vals[3]);
+        else
+            *out++ = 0;
+
+        len -= 4;
+    }
+    *out = 0;
+
+    return result;
+}
+
+util_dict *util_dict_new(void)
+{
+    return (util_dict *)calloc(1, sizeof(util_dict));
+}
+
+void util_dict_free(util_dict *dict)
+{
+    util_dict *next;
+
+    while (dict) {
+        next = dict->next;
+
+        if (dict->key)
+            free (dict->key);
+        if (dict->val)
+            free (dict->val);
+        free (dict);
+
+        dict = next;
+    }
+}
+
+const char *util_dict_get(util_dict *dict, const char *key)
+{
+    while (dict) {
+        if (!strcmp(key, dict->key))
+            return dict->val;
+        dict = dict->next;
+    }
+    return NULL;
+}
+
+int util_dict_set(util_dict *dict, const char *key, const char *val)
+{
+    util_dict *prev;
+
+    if (!dict || !key) {
+        ERROR0("NULL values passed to util_dict_set()");
+        return 0;
+    }
+
+    prev = NULL;
+    while (dict) {
+        if (!dict->key || !strcmp(dict->key, key))
+            break;
+        prev = dict;
+        dict = dict->next;
+    }
+
+    if (!dict) {
+        dict = util_dict_new();
+        if (!dict) {
+            ERROR0("unable to allocate new dictionary");
+            return 0;
+        }
+        if (prev)
+            prev->next = dict;
+    }
+
+    if (dict->key)
+        free (dict->val);
+    else if (!(dict->key = strdup(key))) {
+        if (prev)
+            prev->next = NULL;
+        util_dict_free (dict);
+
+        ERROR0("unable to allocate new dictionary key");
+        return 0;
+    }
+
+    dict->val = strdup(val);
+    if (!dict->val) {
+        ERROR0("unable to allocate new dictionary value");
+        return 0;
+    }
+
+    return 1;
+}
+
+/* given a dictionary, URL-encode each val and 
+   stringify it in order as key=val&key=val... if val 
+   is set, or just key&key if val is NULL.
+  TODO: Memory management needs overhaul. */
+char *util_dict_urlencode(util_dict *dict, char delim)
+{
+    char *res, *tmp;
+    char *enc;
+    int start = 1;
+
+    for (res = NULL; dict; dict = dict->next) {
+        /* encode key */
+        if (!dict->key)
+            continue;
+        if (start) {
+            if (!(res = malloc(strlen(dict->key) + 1))) {
+                return NULL;
+            }
+            sprintf(res, "%s", dict->key);
+            start = 0;
+        } else {
+            if (!(tmp = realloc(res, strlen(res) + strlen(dict->key) + 2))) {
+                free(res);
+                return NULL;
+            } else
+                res = tmp;
+            sprintf(res + strlen(res), "%c%s", delim, dict->key);
+        }
+
+        /* encode value */
+        if (!dict->val)
+            continue;
+        if (!(enc = util_url_escape(dict->val))) {
+            free(res);
+            return NULL;
+        }
+
+        if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
+            free(enc);
+            free(res);
+            return NULL;
+        } else
+            res = tmp;
+        sprintf(res + strlen(res), "=%s", enc);
+        free(enc);
+    }
+
+    return res;
+}
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r (const time_t *timep, struct tm *result)
+{
+     static mutex_t localtime_lock;
+     static int initialised = 0;
+     struct tm *tm;
+
+     if (initialised == 0)
+     {
+         thread_mutex_create (&localtime_lock);
+         initialised = 1;
+     }
+     thread_mutex_lock (&localtime_lock);
+     tm = localtime (timep);
+     memcpy (result, tm, sizeof (*result));
+     thread_mutex_unlock (&localtime_lock);
+     return result;
+}
+#endif
+
+
+/* helper function for converting a passed string in one character set to another
+ * we use libxml2 for this
+ */
+char *util_conv_string (const char *string, const char *in_charset, const char *out_charset)
+{
+    xmlCharEncodingHandlerPtr in, out;
+    char *ret = NULL;
+
+    if (string == NULL || in_charset == NULL || out_charset == NULL)
+        return NULL;
+
+    in  = xmlFindCharEncodingHandler (in_charset);
+    out = xmlFindCharEncodingHandler (out_charset);
+
+    if (in && out)
+    {
+        xmlBufferPtr orig = xmlBufferCreate ();
+        xmlBufferPtr utf8 = xmlBufferCreate ();
+        xmlBufferPtr conv = xmlBufferCreate ();
+
+        INFO2 ("converting metadata from %s to %s", in_charset, out_charset);
+        xmlBufferCCat (orig, string);
+        if (xmlCharEncInFunc (in, utf8, orig) > 0)
+        {
+            xmlCharEncOutFunc (out, conv, NULL);
+            if (xmlCharEncOutFunc (out, conv, utf8) >= 0)
+                ret = strdup ((const char *)xmlBufferContent (conv));
+        }
+        xmlBufferFree (orig);
+        xmlBufferFree (utf8);
+        xmlBufferFree (conv);
+    }
+    xmlCharEncCloseFunc (in);
+    xmlCharEncCloseFunc (out);
+
+    return ret;
+}
+
+
+int get_line(FILE *file, char *buf, size_t siz)
+{
+    if(fgets(buf, (int)siz, file)) {
+        size_t len = strlen(buf);
+        if(len > 0 && buf[len-1] == '\n') {
+            buf[--len] = 0;
+            if(len > 0 && buf[len-1] == '\r')
+                buf[--len] = 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/util.h
===================================================================
--- icecast/trunk/icecast/src/util.h	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/util.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,55 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifndef __UTIL_H__
-#define __UTIL_H__
-
-#define XSLT_CONTENT 1
-#define HTML_CONTENT 2
-
-#define READ_ENTIRE_HEADER 1
-#define READ_LINE 0
-
-int util_timed_wait_for_fd(int fd, int timeout);
-int util_read_header(int sock, char *buff, unsigned long len, int entire);
-int util_check_valid_extension(char *uri);
-char *util_get_extension(const char *path);
-char *util_get_path_from_uri(char *uri);
-char *util_get_path_from_normalised_uri(const char *uri);
-char *util_normalise_uri(char *uri);
-char *util_base64_encode(char *data);
-char *util_base64_decode(unsigned char *input);
-char *util_bin_to_hex(unsigned char *data, int len);
-
-char *util_url_unescape(char *src);
-char *util_url_escape(char *src);
-
-/* String dictionary type, without support for NULL keys, or multiple
- * instances of the same key */
-typedef struct _util_dict {
-  char *key;
-  char *val;
-  struct _util_dict *next;
-} util_dict;
-
-util_dict *util_dict_new(void);
-void util_dict_free(util_dict *dict);
-/* dict, key must not be NULL. */
-int util_dict_set(util_dict *dict, const char *key, const char *val);
-const char *util_dict_get(util_dict *dict, const char *key);
-char *util_dict_urlencode(util_dict *dict, char delim);
-
-#ifndef HAVE_LOCALTIME_R
-struct tm *localtime_r (const time_t *timep, struct tm *result);
-#endif
-
-#endif  /* __UTIL_H__ */

Copied: icecast/tags/icecast-2.3.2/src/util.h (from rev 14043, icecast/trunk/icecast/src/util.h)
===================================================================
--- icecast/tags/icecast-2.3.2/src/util.h	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/util.h	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,59 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#define XSLT_CONTENT 1
+#define HTML_CONTENT 2
+
+#define READ_ENTIRE_HEADER 1
+#define READ_LINE 0
+
+#define MAX_LINE_LEN 512
+
+int util_timed_wait_for_fd(sock_t fd, int timeout);
+int util_read_header(sock_t sock, char *buff, unsigned long len, int entire);
+int util_check_valid_extension(const char *uri);
+char *util_get_extension(const char *path);
+char *util_get_path_from_uri(char *uri);
+char *util_get_path_from_normalised_uri(const char *uri);
+char *util_normalise_uri(const char *uri);
+char *util_base64_encode(const char *data);
+char *util_base64_decode(const char *input);
+char *util_bin_to_hex(unsigned char *data, int len);
+
+char *util_url_unescape(const char *src);
+char *util_url_escape(const char *src);
+
+/* String dictionary type, without support for NULL keys, or multiple
+ * instances of the same key */
+typedef struct _util_dict {
+  char *key;
+  char *val;
+  struct _util_dict *next;
+} util_dict;
+
+util_dict *util_dict_new(void);
+void util_dict_free(util_dict *dict);
+/* dict, key must not be NULL. */
+int util_dict_set(util_dict *dict, const char *key, const char *val);
+const char *util_dict_get(util_dict *dict, const char *key);
+char *util_dict_urlencode(util_dict *dict, char delim);
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r (const time_t *timep, struct tm *result);
+#endif
+char *util_conv_string (const char *string, const char *in_charset, const char *out_charset);
+
+int get_line(FILE *file, char *buf, size_t siz);
+#endif  /* __UTIL_H__ */

Deleted: icecast/tags/icecast-2.3.2/src/xslt.c
===================================================================
--- icecast/trunk/icecast/src/xslt.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/xslt.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,254 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#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>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-
-#ifdef  HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#ifdef WIN32
-#define snprintf _snprintf
-#endif
-
-#include "thread/thread.h"
-#include "avl/avl.h"
-#include "httpp/httpp.h"
-#include "net/sock.h"
-
-#include "connection.h"
-
-#include "global.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-#include "fserve.h"
-
-#define CATMODULE "xslt"
-
-#include "logging.h"
-
-typedef struct {
-    char              *filename;
-    time_t             last_modified;
-    time_t             cache_age;
-    xsltStylesheetPtr  stylesheet;
-} stylesheet_cache_t;
-
-#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
-
-/* Keep it small... */
-#define CACHESIZE 3
-
-static stylesheet_cache_t cache[CACHESIZE];
-static mutex_t xsltlock;
-
-void xslt_initialize(void)
-{
-    xmlSubstituteEntitiesDefault(1);
-    xmlLoadExtDtdDefaultValue = 1;
-
-    memset(cache, 0, sizeof(stylesheet_cache_t)*CACHESIZE);
-    thread_mutex_create(&xsltlock);
-    xmlSubstituteEntitiesDefault(1);
-    xmlLoadExtDtdDefaultValue = 1;
-}
-
-void xslt_shutdown(void) {
-    int i;
-
-    for(i=0; i < CACHESIZE; i++) {
-        if(cache[i].filename)
-            free(cache[i].filename);
-        if(cache[i].stylesheet)
-            xsltFreeStylesheet(cache[i].stylesheet);
-    }
-
-    thread_mutex_destroy (&xsltlock);
-    xsltCleanupGlobals();
-}
-
-static int evict_cache_entry(void) {
-    int i, age=0, oldest=0;
-
-    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;
-}
-
-static xsltStylesheetPtr xslt_get_stylesheet(const char *fn) {
-    int i;
-    int empty = -1;
-    struct stat file;
-
-    if(stat(fn, &file)) {
-        WARN2("Error checking for stylesheet file \"%s\": %s", fn, 
-                strerror(errno));
-        return NULL;
-    }
-
-    for(i=0; i < CACHESIZE; i++) {
-        if(cache[i].filename)
-        {
-#ifdef _WIN32
-            if(!stricmp(fn, cache[i].filename))
-#else
-            if(!strcmp(fn, cache[i].filename))
-#endif
-            {
-                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);
-                }
-                DEBUG1("Using cached sheet %i", i);
-                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;
-}
-
-void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
-{
-    xmlDocPtr    res;
-    xsltStylesheetPtr cur;
-    xmlChar *string;
-    int len, problem = 0;
-    const char *mediatype = NULL;
-
-    xmlSetGenericErrorFunc ("", log_parse_failure);
-    xsltSetGenericErrorFunc ("", log_parse_failure);
-
-    thread_mutex_lock(&xsltlock);
-    cur = xslt_get_stylesheet(xslfilename);
-
-    if (cur == NULL)
-    {
-        thread_mutex_unlock(&xsltlock);
-        ERROR1 ("problem reading stylesheet \"%s\"", xslfilename);
-        client_send_404 (client, "Could not parse XSLT file");
-        return;
-    }
-
-    res = xsltApplyStylesheet(cur, doc, NULL);
-
-    if (xsltSaveResultToString (&string, &len, res, cur) < 0)
-        problem = 1;
-
-    /* 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";
-    }
-    if (problem == 0)
-    {
-        /* the 100 is to allow for the hardcoded headers */
-        unsigned int full_len = strlen (mediatype) + len + 100;
-        refbuf_t *refbuf = refbuf_new (full_len);
-
-        if (string == NULL)
-            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);
-
-        client->respcode = 200;
-        client_set_queue (client, NULL);
-        client->refbuf = refbuf;
-        refbuf->len = strlen (refbuf->data);
-        fserve_add_client (client, NULL);
-        xmlFree (string);
-    }
-    else
-    {
-        WARN1 ("problem applying stylesheet \"%s\"", xslfilename);
-        client_send_404 (client, "XSLT problem");
-    }
-    thread_mutex_unlock (&xsltlock);
-    xmlFreeDoc(res);
-}
-

Copied: icecast/tags/icecast-2.3.2/src/xslt.c (from rev 14465, icecast/trunk/icecast/src/xslt.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/xslt.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/xslt.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,254 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#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>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifdef  HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "fserve.h"
+
+#define CATMODULE "xslt"
+
+#include "logging.h"
+
+typedef struct {
+    char              *filename;
+    time_t             last_modified;
+    time_t             cache_age;
+    xsltStylesheetPtr  stylesheet;
+} stylesheet_cache_t;
+
+#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
+
+/* Keep it small... */
+#define CACHESIZE 3
+
+static stylesheet_cache_t cache[CACHESIZE];
+static mutex_t xsltlock;
+
+void xslt_initialize(void)
+{
+    memset(cache, 0, sizeof(stylesheet_cache_t)*CACHESIZE);
+    thread_mutex_create(&xsltlock);
+    xmlInitParser();
+    LIBXML_TEST_VERSION
+    xmlSubstituteEntitiesDefault(1);
+    xmlLoadExtDtdDefaultValue = 1;
+}
+
+void xslt_shutdown(void) {
+    int i;
+
+    for(i=0; i < CACHESIZE; i++) {
+        if(cache[i].filename)
+            free(cache[i].filename);
+        if(cache[i].stylesheet)
+            xsltFreeStylesheet(cache[i].stylesheet);
+    }
+
+    thread_mutex_destroy (&xsltlock);
+    xmlCleanupParser();
+    xsltCleanupGlobals();
+}
+
+static int evict_cache_entry(void) {
+    int i, age=0, oldest=0;
+
+    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;
+}
+
+static xsltStylesheetPtr xslt_get_stylesheet(const char *fn) {
+    int i;
+    int empty = -1;
+    struct stat file;
+
+    if(stat(fn, &file)) {
+        WARN2("Error checking for stylesheet file \"%s\": %s", fn, 
+                strerror(errno));
+        return NULL;
+    }
+
+    for(i=0; i < CACHESIZE; i++) {
+        if(cache[i].filename)
+        {
+#ifdef _WIN32
+            if(!stricmp(fn, cache[i].filename))
+#else
+            if(!strcmp(fn, cache[i].filename))
+#endif
+            {
+                if(file.st_mtime > cache[i].last_modified)
+                {
+                    xsltFreeStylesheet(cache[i].stylesheet);
+
+                    cache[i].last_modified = file.st_mtime;
+                    cache[i].stylesheet = xsltParseStylesheetFile (XMLSTR(fn));
+                    cache[i].cache_age = time(NULL);
+                }
+                DEBUG1("Using cached sheet %i", i);
+                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 (XMLSTR(fn));
+    cache[i].cache_age = time(NULL);
+    return cache[i].stylesheet;
+}
+
+void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
+{
+    xmlDocPtr    res;
+    xsltStylesheetPtr cur;
+    xmlChar *string;
+    int len, problem = 0;
+    const char *mediatype = NULL;
+
+    xmlSetGenericErrorFunc ("", log_parse_failure);
+    xsltSetGenericErrorFunc ("", log_parse_failure);
+
+    thread_mutex_lock(&xsltlock);
+    cur = xslt_get_stylesheet(xslfilename);
+
+    if (cur == NULL)
+    {
+        thread_mutex_unlock(&xsltlock);
+        ERROR1 ("problem reading stylesheet \"%s\"", xslfilename);
+        client_send_404 (client, "Could not parse XSLT file");
+        return;
+    }
+
+    res = xsltApplyStylesheet(cur, doc, NULL);
+
+    if (xsltSaveResultToString (&string, &len, res, cur) < 0)
+        problem = 1;
+
+    /* 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, XMLSTR("html")) == 0)
+            mediatype = "text/html";
+        else
+            if (cur->method && xmlStrcmp (cur->method, XMLSTR("text")) == 0)
+                mediatype = "text/plain";
+            else
+                mediatype = "text/xml";
+    }
+    if (problem == 0)
+    {
+        /* the 100 is to allow for the hardcoded headers */
+        unsigned int full_len = strlen (mediatype) + len + 100;
+        refbuf_t *refbuf = refbuf_new (full_len);
+
+        if (string == NULL)
+            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);
+
+        client->respcode = 200;
+        client_set_queue (client, NULL);
+        client->refbuf = refbuf;
+        refbuf->len = strlen (refbuf->data);
+        fserve_add_client (client, NULL);
+        xmlFree (string);
+    }
+    else
+    {
+        WARN1 ("problem applying stylesheet \"%s\"", xslfilename);
+        client_send_404 (client, "XSLT problem");
+    }
+    thread_mutex_unlock (&xsltlock);
+    xmlFreeDoc(res);
+}
+

Deleted: icecast/tags/icecast-2.3.2/src/yp.c
===================================================================
--- icecast/trunk/icecast/src/yp.c	2007-08-11 19:59:10 UTC (rev 13540)
+++ icecast/tags/icecast-2.3.2/src/yp.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,930 +0,0 @@
-/* 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 at xiph.org, 
- *                      Michael Smith <msmith at xiph.org>,
- *                      oddsock <oddsock at xiph.org>,
- *                      Karl Heyes <karl at xiph.org>
- *                      and others (see AUTHORS for details).
- */
-
-/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <curl/curl.h>
-
-#include <thread/thread.h>
-
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "logging.h"
-#include "format.h"
-#include "source.h"
-#include "cfgfile.h"
-#include "stats.h"
-
-#ifdef WIN32
-#define snprintf _snprintf
-#endif
-
-#define CATMODULE "yp" 
-
-struct yp_server
-{
-    char        *url;
-    unsigned    url_timeout;
-    unsigned    touch_interval;
-    int         remove;
-
-    CURL *curl;
-    struct ypdata_tag *mounts, *pending_mounts;
-    struct yp_server *next;
-    char curl_error[CURL_ERROR_SIZE];
-};
-
-
-
-typedef struct ypdata_tag
-{
-    int remove;
-    int release;
-    int cmd_ok;
-
-    char *sid;
-    char *mount;
-    char *url;
-    char *listen_url;
-    char *server_name;
-    char *server_desc;
-    char *server_genre;
-    char *cluster_password;
-    char *bitrate;
-    char *audio_info;
-    char *server_type;
-    char *current_song;
-    char *subtype;
-
-    struct yp_server *server;
-    time_t      next_update;
-    unsigned    touch_interval;
-    char        *error_msg;
-    unsigned    (*process)(struct ypdata_tag *yp, char *s, unsigned len);
-
-    struct ypdata_tag *next;
-} ypdata_t;
-
-
-static rwlock_t yp_lock;
-static mutex_t yp_pending_lock;
-
-static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
-static volatile int yp_update = 0;
-static int yp_running;
-static time_t now;
-static thread_type *yp_thread;
-static volatile unsigned client_limit = 0;
-
-static void *yp_update_thread(void *arg);
-static void add_yp_info (ypdata_t *yp, void *info, int type);
-static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len);
-static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len);
-static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len);
-static void yp_destroy_ypdata(ypdata_t *ypdata);
-
-
-/* curl callback used to parse headers coming back from the YP server */
-static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
-{
-    ypdata_t *yp = stream;
-    unsigned bytes = size * nmemb;
-
-    /* DEBUG2 ("header from YP is \"%.*s\"", bytes, ptr); */
-    if (strncmp (ptr, "YPResponse: 1", 13) == 0)
-        yp->cmd_ok = 1;
-
-    if (strncmp (ptr, "YPMessage: ", 11) == 0)
-    {
-        unsigned len = bytes - 11;
-        free (yp->error_msg);
-        yp->error_msg = calloc (1, len);
-        if (yp->error_msg)
-            sscanf (ptr, "YPMessage: %[^\r\n]", yp->error_msg);
-    }
-
-    if (yp->process == do_yp_add)
-    {
-        if (strncmp (ptr, "SID: ", 5) == 0)
-        {
-            unsigned len = bytes - 5;
-            free (yp->sid);
-            yp->sid = calloc (1, len);
-            if (yp->sid)
-                sscanf (ptr, "SID: %[^\r\n]", yp->sid);
-        }
-    }
-    if (strncmp (ptr, "TouchFreq: ", 11) == 0)
-    {
-        unsigned secs;
-        sscanf (ptr, "TouchFreq: %u", &secs);
-        if (secs < 30)
-            secs = 30;
-        DEBUG1 ("server touch interval is %u", secs);
-        yp->touch_interval = secs;
-    }
-    return (int)bytes;
-}
-
-
-/* capture returned data, but don't do anything with it, shouldn't be any */
-static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
-{
-    return (int)(size*nmemb);
-}
-
-
-/* search the active and pending YP server lists */
-static struct yp_server *find_yp_server (const char *url)
-{
-    struct yp_server *server;
-
-    server = (struct yp_server *)active_yps;
-    while (server)
-    {
-        if (strcmp (server->url, url) == 0)
-            return server;
-        server = server->next;
-    }
-    server = (struct yp_server *)pending_yps;
-    while (server)
-    {
-        if (strcmp (server->url, url) == 0)
-            break;
-        server = server->next;
-    }
-    return server;
-}
-
-
-static void destroy_yp_server (struct yp_server *server)
-{
-    if (server == NULL)
-        return;
-    DEBUG1 ("Removing YP server entry for %s", server->url);
-    if (server->curl)
-        curl_easy_cleanup (server->curl);
-    if (server->mounts) WARN0 ("active ypdata not freed up");
-    if (server->pending_mounts) WARN0 ("pending ypdata not freed up");
-    free (server->url);
-    free (server);
-}
-
-
-
-/* search for a ypdata entry corresponding to a specific mountpoint */
-static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
-{
-    ypdata_t *yp = mounts;
-    while (yp)
-    {
-        if (strcmp (yp->mount, mount) == 0)
-            break;
-        yp = yp->next;
-    }
-    return yp;
-}
-
-
-void yp_recheck_config (ice_config_t *config)
-{
-    int i;
-    struct yp_server *server;
-
-    DEBUG0("Updating YP configuration");
-    thread_rwlock_rlock (&yp_lock);
-
-    server = (struct yp_server *)active_yps;
-    while (server)
-    {
-        server->remove = 1;
-        server = server->next;
-    }
-    client_limit = config->client_limit;
-    /* for each yp url in config, check to see if one exists 
-       if not, then add it. */
-    for (i=0 ; i < config->num_yp_directories; i++)
-    {
-        server = find_yp_server (config->yp_url[i]);
-        if (server == NULL)
-        {
-            server = calloc (1, sizeof (struct yp_server));
-
-            if (server == NULL)
-            {
-                destroy_yp_server (server);
-                break;
-            }
-            server->url = strdup (config->yp_url[i]);
-            server->url_timeout = config->yp_url_timeout[i];
-            server->touch_interval = config->yp_touch_interval[i];
-            server->curl = curl_easy_init();
-            if (server->curl == NULL)
-            {
-                destroy_yp_server (server);
-                break;
-            }
-            if (server->touch_interval < 30)
-                server->touch_interval = 30;
-            curl_easy_setopt (server->curl, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
-            curl_easy_setopt (server->curl, CURLOPT_URL, server->url);
-            curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
-            curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
-            curl_easy_setopt (server->curl, CURLOPT_WRITEDATA, server->curl);
-            curl_easy_setopt (server->curl, CURLOPT_TIMEOUT, server->url_timeout);
-            curl_easy_setopt (server->curl, CURLOPT_NOSIGNAL, 1L);
-            curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
-            server->next = (struct yp_server *)pending_yps;
-            pending_yps = server;
-            INFO3 ("Adding new YP server \"%s\" (timeout %ds, default interval %ds)",
-                    server->url, server->url_timeout, server->touch_interval);
-        }
-        else
-        {
-            server->remove = 0;
-        }
-    }
-    thread_rwlock_unlock (&yp_lock);
-    yp_update = 1;
-}
-
-
-void yp_initialize(void)
-{
-    ice_config_t *config = config_get_config();
-    thread_rwlock_create (&yp_lock);
-    thread_mutex_create (&yp_pending_lock);
-    yp_recheck_config (config);
-    config_release_config ();
-    yp_thread = thread_create("YP Touch Thread", yp_update_thread,
-                            (void *)NULL, THREAD_ATTACHED);
-}
-
-
-
-/* handler for curl, checks if successful handling occurred */
-static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
-{
-    int curlcode;
-    struct yp_server *server = yp->server;
-
-    /* DEBUG2 ("send YP (%s):%s", cmd, post); */
-    yp->cmd_ok = 0;
-    curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post);
-    curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp);
-    curlcode = curl_easy_perform (server->curl);
-    if (curlcode)
-    {
-        yp->process = do_yp_add;
-        yp->next_update += 300;
-        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
-        return -1;
-    }
-    if (yp->cmd_ok == 0)
-    {
-        if (yp->error_msg == NULL)
-            yp->error_msg = strdup ("no response from server");
-        yp->process = do_yp_add;
-        yp->next_update += 300;
-        ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
-        return -1;
-    }
-    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
-    return 0;
-}
-
-
-/* routines for building and issues requests to the YP server */
-static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len)
-{
-    if (yp->sid)
-    {
-        int ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
-        if (ret >= (signed)len)
-            return ret+1;
-
-        INFO1 ("clearing up YP entry for %s", yp->mount);
-        send_to_yp ("remove", yp, s);
-        free (yp->sid);
-        yp->sid = NULL;
-    }
-    yp_update = 1;
-    yp->remove = 1;
-    yp->process = do_yp_add;
-
-    return 0;
-}
-
-
-static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len)
-{
-    int ret;
-    char *value;
-
-    value = stats_get_value (yp->mount, "server_type");
-    add_yp_info (yp, value, YP_SERVER_TYPE);
-    free (value);
-
-    value = stats_get_value (yp->mount, "server_name");
-    add_yp_info (yp, value, YP_SERVER_NAME);
-    free (value);
-
-    value = stats_get_value (yp->mount, "server_url");
-    add_yp_info (yp, value, YP_SERVER_URL);
-    free (value);
-
-    value = stats_get_value (yp->mount, "genre");
-    add_yp_info (yp, value, YP_SERVER_GENRE);
-    free (value);
-
-    value = stats_get_value (yp->mount, "bitrate");
-    add_yp_info (yp, value, YP_BITRATE);
-    free (value);
-
-    value = stats_get_value (yp->mount, "server_description");
-    add_yp_info (yp, value, YP_SERVER_DESC);
-    free (value);
-
-    value = stats_get_value (yp->mount, "subtype");
-    add_yp_info (yp, value, YP_SUBTYPE);
-    free (value);
-
-    value = stats_get_value (yp->mount, "audio_info");
-    add_yp_info (yp, value, YP_AUDIO_INFO);
-    free (value);
-
-    ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
-                    "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
-                    yp->server_name, yp->server_genre, yp->cluster_password,
-                    yp->server_desc, yp->url, yp->listen_url,
-                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
-    if (ret >= (signed)len)
-        return ret+1;
-    if (send_to_yp ("add", yp, s) == 0)
-    {
-        yp->process = do_yp_touch;
-        /* force first touch in 5 secs */
-        yp->next_update = time(NULL) + 5;
-    }
-
-    return 0;
-}
-
-
-static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len)
-{
-    unsigned listeners = 0, max_listeners = 1;
-    char *val, *artist, *title;
-    int ret;
-
-    artist = (char *)stats_get_value (yp->mount, "artist");
-    title = (char *)stats_get_value (yp->mount, "title");
-    if (artist || title)
-    {
-         char *song;
-         char *separator = " - ";
-         if (artist == NULL)
-         {
-             artist = strdup("");
-             separator = "";
-         }
-         if (title == NULL) title = strdup("");
-         song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1);
-         if (song)
-         {
-             sprintf (song, "%s%s%s", artist, separator, title);
-             add_yp_info(yp, song, YP_CURRENT_SONG);
-             stats_event (yp->mount, "yp_currently_playing", song);
-             free (song);
-         }
-    }
-    free (artist);
-    free (title);
-
-    val = (char *)stats_get_value (yp->mount, "listeners");
-    if (val)
-    {
-        listeners = atoi (val);
-        free (val);
-    }
-    val = stats_get_value (yp->mount, "max_listeners");
-    if (val == NULL || strcmp (val, "unlimited") == 0)
-    {
-        free (val);
-        max_listeners = client_limit;
-    }
-    else
-        max_listeners = atoi (val);
-
-    val = stats_get_value (yp->mount, "subtype");
-    if (val)
-    {
-        add_yp_info (yp, val, YP_SUBTYPE);
-        free (val);
-    }
-
-    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
-            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
-            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
-
-    if (ret >= (signed)len)
-        return ret+1; /* space required for above text and nul*/
-
-    send_to_yp ("touch", yp, s);
-    return 0;
-}
-
-
-
-static void process_ypdata (struct yp_server *server, ypdata_t *yp)
-{
-    unsigned len = 512;
-    char *s = NULL, *tmp;
-
-    if (now < yp->next_update)
-        return;
-    yp->next_update = now + yp->touch_interval;
-
-    /* loop just in case the memory area isn't big enough */
-    while (1)
-    {
-        unsigned ret;
-        if ((tmp = realloc (s, len)) == NULL)
-            return;
-        s = tmp;
-
-        if (yp->release)
-        {
-            yp->process = do_yp_remove;
-            yp->next_update = 0;
-        }
-
-        ret = yp->process (yp, s, len);
-        if (ret == 0)
-        {
-           free (s);
-           return;
-        }
-        len = ret;
-    }
-}
-
-
-static void yp_process_server (struct yp_server *server)
-{
-    ypdata_t *yp;
-
-    /* DEBUG1("processing yp server %s", server->url); */
-    yp = server->mounts;
-    while (yp)
-    {
-        now = time (NULL);
-        process_ypdata (server, yp);
-        yp = yp->next;
-    }
-}
-
-
-
-static ypdata_t *create_yp_entry (const char *mount)
-{
-    ypdata_t *yp;
-    char *s;
-
-    yp = calloc (1, sizeof (ypdata_t));
-    do
-    {
-        unsigned len = 512;
-        int ret;
-        char *url;
-        mount_proxy *mountproxy = NULL;
-        ice_config_t *config;
-
-        if (yp == NULL)
-            break;
-        yp->mount = strdup (mount);
-        yp->server_name = strdup ("");
-        yp->server_desc = strdup ("");
-        yp->server_genre = strdup ("");
-        yp->bitrate = strdup ("");
-        yp->server_type = strdup ("");
-        yp->cluster_password = strdup ("");
-        yp->url = strdup ("");
-        yp->current_song = strdup ("");
-        yp->audio_info = strdup ("");
-        yp->subtype = strdup ("");
-        yp->process = do_yp_add;
-
-        url = malloc (len);
-        if (url == NULL)
-            break;
-        config = config_get_config();
-        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
-        if (ret >= (signed)len)
-        {
-            s = realloc (url, ++ret);
-            if (s) url = s;
-            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
-        }
-
-        mountproxy = config_find_mount (config, mount);
-        if (mountproxy && mountproxy->cluster_password)
-            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
-        config_release_config();
-
-        yp->listen_url = util_url_escape (url);
-        free (url);
-        if (yp->listen_url == NULL)
-            break;
-
-        return yp;
-    } while (0);
-
-    yp_destroy_ypdata (yp);
-    return NULL;
-}
-
-
-/* Check for changes in the YP servers configured */
-static void check_servers (void)
-{
-    struct yp_server *server = (struct yp_server *)active_yps,
-                     **server_p = (struct yp_server **)&active_yps;
-
-    while (server)
-    {
-        if (server->remove)
-        {
-            struct yp_server *to_go = server;
-            DEBUG1 ("YP server \"%s\"removed", server->url);
-            *server_p = server->next;
-            server = server->next;
-            destroy_yp_server (to_go);
-            continue;
-        }
-        server_p = &server->next;
-        server = server->next;
-    }
-    /* add new server entries */
-    while (pending_yps)
-    {
-        avl_node *node;
-
-        server = (struct yp_server *)pending_yps;
-        pending_yps = server->next;
-
-        DEBUG1("Add pending yps %s", server->url);
-        server->next = (struct yp_server *)active_yps;
-        active_yps = server;
-
-        /* new YP server configured, need to populate with existing sources */
-        avl_tree_rlock (global.source_tree);
-        node = avl_get_first (global.source_tree);
-        while (node)
-        {
-            ypdata_t *yp;
-
-            source_t *source = node->key;
-            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
-            {
-                DEBUG1 ("Adding existing mount %s", source->mount);
-                yp->server = server;
-                yp->touch_interval = server->touch_interval;
-                yp->next = server->mounts;
-                server->mounts = yp;
-            }
-            node = avl_get_next (node);
-        }
-        avl_tree_unlock (global.source_tree);
-    }
-}
-
-
-static void add_pending_yp (struct yp_server *server)
-{
-    ypdata_t *current, *yp;
-    unsigned count = 0;
-
-    if (server->pending_mounts == NULL)
-        return;
-    current = server->mounts;
-    server->mounts = server->pending_mounts;
-    server->pending_mounts = NULL;
-    yp = server->mounts;
-    while (1)
-    {
-        count++;
-        if (yp->next == NULL)
-            break;
-        yp = yp->next;
-    }
-    yp->next = current;
-    DEBUG2 ("%u YP entries added to %s", count, server->url);
-}
-
-
-static void delete_marked_yp (struct yp_server *server)
-{
-    ypdata_t *yp = server->mounts, **prev = &server->mounts;
-
-    while (yp)
-    {
-        if (yp->remove)
-        {
-            ypdata_t *to_go = yp;
-            DEBUG2 ("removed %s from YP server %s", yp->mount, server->url);
-            *prev = yp->next;
-            yp = yp->next;
-            yp_destroy_ypdata (to_go);
-            continue;
-        }
-        prev = &yp->next;
-        yp = yp->next;
-    }
-}
-
-
-static void *yp_update_thread(void *arg)
-{
-    INFO0("YP update thread started");
-
-    yp_running = 1;
-    while (yp_running)
-    {
-        struct yp_server *server;
-
-        thread_sleep (200000);
-
-        /* do the YP communication */
-        thread_rwlock_rlock (&yp_lock);
-        server = (struct yp_server *)active_yps;
-        while (server)
-        {
-            /* DEBUG1 ("trying %s", server->url); */
-            yp_process_server (server);
-            server = server->next;
-        }
-        thread_rwlock_unlock (&yp_lock);
-
-        /* update the local YP structure */
-        if (yp_update)
-        {
-            thread_rwlock_wlock (&yp_lock);
-            check_servers ();
-            server = (struct yp_server *)active_yps;
-            while (server)
-            {
-                /* DEBUG1 ("Checking yps %s", server->url); */
-                add_pending_yp (server);
-                delete_marked_yp (server);
-                server = server->next;
-            }
-            yp_update = 0;
-            thread_rwlock_unlock (&yp_lock);
-        }
-    }
-    thread_rwlock_destroy (&yp_lock);
-    thread_mutex_destroy (&yp_pending_lock);
-    /* free server and ypdata left */
-    while (active_yps)
-    {
-        struct yp_server *server = (struct yp_server *)active_yps;
-        active_yps = server->next;
-        destroy_yp_server (server);
-    }
-
-    return NULL;
-}
-
-
-
-static void yp_destroy_ypdata(ypdata_t *ypdata)
-{
-    if (ypdata) {
-        if (ypdata->mount) {
-            free (ypdata->mount);
-        }
-        if (ypdata->url) {
-            free (ypdata->url);
-        }
-        if (ypdata->sid) {
-            free(ypdata->sid);
-        }
-        if (ypdata->server_name) {
-            free(ypdata->server_name);
-        }
-        if (ypdata->server_desc) {
-            free(ypdata->server_desc);
-        }
-        if (ypdata->server_genre) {
-            free(ypdata->server_genre);
-        }
-        if (ypdata->cluster_password) {
-            free(ypdata->cluster_password);
-        }
-        if (ypdata->listen_url) {
-            free(ypdata->listen_url);
-        }
-        if (ypdata->current_song) {
-            free(ypdata->current_song);
-        }
-        if (ypdata->bitrate) {
-            free(ypdata->bitrate);
-        }
-        if (ypdata->server_type) {
-            free(ypdata->server_type);
-        }
-        if (ypdata->audio_info) {
-            free(ypdata->audio_info);
-        }
-        free (ypdata->subtype);
-        free (ypdata->error_msg);
-        free (ypdata);
-    }
-}
-
-static void add_yp_info (ypdata_t *yp, void *info, int type)
-{
-    char *escaped;
-
-    if (!info)
-        return;
-
-    escaped = util_url_escape(info);
-    if (escaped == NULL)
-        return;
-
-    switch (type)
-    {
-        case YP_SERVER_NAME:
-            free (yp->server_name);
-            yp->server_name = escaped;
-            break;
-        case YP_SERVER_DESC:
-            free (yp->server_desc);
-            yp->server_desc = escaped;
-            break;
-        case YP_SERVER_GENRE:
-            free (yp->server_genre);
-            yp->server_genre = escaped;
-            break;
-        case YP_SERVER_URL:
-            free (yp->url);
-            yp->url = escaped;
-            break;
-        case YP_BITRATE:
-            free (yp->bitrate);
-            yp->bitrate = escaped;
-            break;
-        case YP_AUDIO_INFO:
-            free (yp->audio_info);
-            yp->audio_info = escaped;
-            break;
-        case YP_SERVER_TYPE:
-            free (yp->server_type);
-            yp->server_type = escaped;
-            break;
-        case YP_CURRENT_SONG:
-            free (yp->current_song);
-            yp->current_song = escaped;
-            break;
-        case YP_CLUSTER_PASSWORD:
-            free (yp->cluster_password);
-            yp->cluster_password = escaped;
-            break;
-        case YP_SUBTYPE:
-            free (yp->subtype);
-            yp->subtype = escaped;
-            break;
-        default:
-            free (escaped);
-    }
-}
-
-
-/* Add YP entries to active servers */
-void yp_add (const char *mount)
-{
-    struct yp_server *server;
-
-    /* make sure YP thread is not modifying the lists */
-    thread_rwlock_rlock (&yp_lock);
-
-    /* make sure we don't race against another yp_add */
-    thread_mutex_lock (&yp_pending_lock);
-    server = (struct yp_server *)active_yps;
-    while (server)
-    {
-        ypdata_t *yp;
-
-        /* on-demand relays may already have a YP entry */
-        yp = find_yp_mount (server->mounts, mount);
-        if (yp == NULL)
-        {
-            /* add new ypdata to each servers pending yp */
-            yp = create_yp_entry (mount);
-            if (yp)
-            {
-                DEBUG2 ("Adding %s to %s", mount, server->url);
-                yp->server = server;
-                yp->touch_interval = server->touch_interval;
-                yp->next = server->pending_mounts;
-                yp->next_update = time(NULL) + 5;
-                server->pending_mounts = yp;
-                yp_update = 1;
-            }
-        }
-        else
-            DEBUG1 ("YP entry %s already exists", mount);
-        server = server->next;
-    }
-    thread_mutex_unlock (&yp_pending_lock);
-    thread_rwlock_unlock (&yp_lock);
-}
-
-
-
-/* Mark an existing entry in the YP list as to be marked for deletion */
-void yp_remove (const char *mount)
-{
-    struct yp_server *server = (struct yp_server *)active_yps;
-
-    thread_rwlock_rlock (&yp_lock);
-    while (server)
-    {
-        ypdata_t *yp = find_yp_mount (server->mounts, mount);
-        if (yp)
-        {
-            DEBUG2 ("release %s on YP %s", mount, server->url);
-            yp->release = 1;
-            yp->next_update = 0;
-        }
-        server = server->next;
-    }
-    thread_rwlock_unlock (&yp_lock);
-}
-
-
-/* This is similar to yp_remove, but we force a touch
- * attempt */
-void yp_touch (const char *mount)
-{
-    struct yp_server *server = (struct yp_server *)active_yps;
-    time_t trigger;
-    ypdata_t *search_list = NULL;
-
-    thread_rwlock_rlock (&yp_lock);
-    /* do update in 3 secs, give stats chance to update */
-    trigger = time(NULL) + 3;
-    if (server)
-        search_list = server->mounts;
-
-    while (server)
-    {
-        ypdata_t *yp = find_yp_mount (search_list, mount);
-        if (yp)
-        {
-            /* we may of found old entries not purged yet, so skip them */
-            if (yp->release != 0 || yp->remove != 0)
-            {
-                search_list = yp->next;
-                continue;
-            }
-            /* only force if touch */
-            if (yp->process == do_yp_touch)
-                yp->next_update = trigger;
-        }
-        server = server->next;
-        if (server)
-            search_list = server->mounts;
-    }
-    thread_rwlock_unlock (&yp_lock);
-}
-
-
-void yp_shutdown (void)
-{
-    yp_running = 0;
-    yp_update = 1;
-    if (yp_thread)
-        thread_join (yp_thread);
-    curl_global_cleanup();
-    INFO0 ("YP thread down");
-}
-

Copied: icecast/tags/icecast-2.3.2/src/yp.c (from rev 14934, icecast/trunk/icecast/src/yp.c)
===================================================================
--- icecast/tags/icecast-2.3.2/src/yp.c	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/src/yp.c	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,990 @@
+/* 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 at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <curl/curl.h>
+
+#include "thread/thread.h"
+
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h"
+#include "format.h"
+#include "source.h"
+#include "cfgfile.h"
+#include "stats.h"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "yp" 
+
+struct yp_server
+{
+    char        *url;
+    char        *server_id;
+    unsigned    url_timeout;
+    unsigned    touch_interval;
+    int         remove;
+
+    CURL *curl;
+    struct ypdata_tag *mounts, *pending_mounts;
+    struct yp_server *next;
+    char curl_error[CURL_ERROR_SIZE];
+};
+
+
+
+typedef struct ypdata_tag
+{
+    int remove;
+    int release;
+    int cmd_ok;
+
+    char *sid;
+    char *mount;
+    char *url;
+    char *listen_url;
+    char *server_name;
+    char *server_desc;
+    char *server_genre;
+    char *cluster_password;
+    char *bitrate;
+    char *audio_info;
+    char *server_type;
+    char *current_song;
+    char *subtype;
+
+    struct yp_server *server;
+    time_t      next_update;
+    unsigned    touch_interval;
+    char        *error_msg;
+    int    (*process)(struct ypdata_tag *yp, char *s, unsigned len);
+
+    struct ypdata_tag *next;
+} ypdata_t;
+
+
+static rwlock_t yp_lock;
+static mutex_t yp_pending_lock;
+
+static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
+static volatile int yp_update = 0;
+static int yp_running;
+static time_t now;
+static thread_type *yp_thread;
+static volatile unsigned client_limit = 0;
+static volatile char *server_version = NULL;
+
+static void *yp_update_thread(void *arg);
+static void add_yp_info (ypdata_t *yp, void *info, int type);
+static int do_yp_remove (ypdata_t *yp, char *s, unsigned len);
+static int do_yp_add (ypdata_t *yp, char *s, unsigned len);
+static int do_yp_touch (ypdata_t *yp, char *s, unsigned len);
+static void yp_destroy_ypdata(ypdata_t *ypdata);
+
+
+/* curl callback used to parse headers coming back from the YP server */
+static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    ypdata_t *yp = stream;
+    unsigned bytes = size * nmemb;
+
+    /* DEBUG2 ("header from YP is \"%.*s\"", bytes, ptr); */
+    if (strncmp (ptr, "YPResponse: 1", 13) == 0)
+        yp->cmd_ok = 1;
+
+    if (strncmp (ptr, "YPMessage: ", 11) == 0)
+    {
+        unsigned len = bytes - 11;
+        free (yp->error_msg);
+        yp->error_msg = calloc (1, len);
+        if (yp->error_msg)
+            sscanf (ptr, "YPMessage: %[^\r\n]", yp->error_msg);
+    }
+
+    if (yp->process == do_yp_add)
+    {
+        if (strncmp (ptr, "SID: ", 5) == 0)
+        {
+            unsigned len = bytes - 5;
+            free (yp->sid);
+            yp->sid = calloc (1, len);
+            if (yp->sid)
+                sscanf (ptr, "SID: %[^\r\n]", yp->sid);
+        }
+    }
+    if (strncmp (ptr, "TouchFreq: ", 11) == 0)
+    {
+        unsigned secs;
+        sscanf (ptr, "TouchFreq: %u", &secs);
+        if (secs < 30)
+            secs = 30;
+        DEBUG1 ("server touch interval is %u", secs);
+        yp->touch_interval = secs;
+    }
+    return (int)bytes;
+}
+
+
+/* capture returned data, but don't do anything with it, shouldn't be any */
+static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    return (int)(size*nmemb);
+}
+
+
+/* search the active and pending YP server lists */
+static struct yp_server *find_yp_server (const char *url)
+{
+    struct yp_server *server;
+
+    server = (struct yp_server *)active_yps;
+    while (server)
+    {
+        if (strcmp (server->url, url) == 0)
+            return server;
+        server = server->next;
+    }
+    server = (struct yp_server *)pending_yps;
+    while (server)
+    {
+        if (strcmp (server->url, url) == 0)
+            break;
+        server = server->next;
+    }
+    return server;
+}
+
+
+static void destroy_yp_server (struct yp_server *server)
+{
+    if (server == NULL)
+        return;
+    DEBUG1 ("Removing YP server entry for %s", server->url);
+    if (server->curl)
+        curl_easy_cleanup (server->curl);
+    if (server->mounts) WARN0 ("active ypdata not freed up");
+    if (server->pending_mounts) WARN0 ("pending ypdata not freed up");
+    free (server->url);
+    free (server->server_id);
+    free (server);
+}
+
+
+
+/* search for a ypdata entry corresponding to a specific mountpoint */
+static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
+{
+    ypdata_t *yp = mounts;
+    while (yp)
+    {
+        if (strcmp (yp->mount, mount) == 0)
+            break;
+        yp = yp->next;
+    }
+    return yp;
+}
+
+
+void yp_recheck_config (ice_config_t *config)
+{
+    int i;
+    struct yp_server *server;
+
+    DEBUG0("Updating YP configuration");
+    thread_rwlock_rlock (&yp_lock);
+
+    server = (struct yp_server *)active_yps;
+    while (server)
+    {
+        server->remove = 1;
+        server = server->next;
+    }
+    client_limit = config->client_limit;
+    free ((char*)server_version);
+    server_version = strdup (config->server_id);
+    /* for each yp url in config, check to see if one exists 
+       if not, then add it. */
+    for (i=0 ; i < config->num_yp_directories; i++)
+    {
+        server = find_yp_server (config->yp_url[i]);
+        if (server == NULL)
+        {
+            server = calloc (1, sizeof (struct yp_server));
+
+            if (server == NULL)
+            {
+                destroy_yp_server (server);
+                break;
+            }
+            server->server_id = strdup ((char *)server_version);
+            server->url = strdup (config->yp_url[i]);
+            server->url_timeout = config->yp_url_timeout[i];
+            server->touch_interval = config->yp_touch_interval[i];
+            server->curl = curl_easy_init();
+            if (server->curl == NULL)
+            {
+                destroy_yp_server (server);
+                break;
+            }
+            if (server->url_timeout > 10 || server->url_timeout < 1)
+                server->url_timeout = 6;
+            if (server->touch_interval < 30)
+                server->touch_interval = 30;
+            curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
+            curl_easy_setopt (server->curl, CURLOPT_URL, server->url);
+            curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
+            curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
+            curl_easy_setopt (server->curl, CURLOPT_WRITEDATA, server->curl);
+            curl_easy_setopt (server->curl, CURLOPT_TIMEOUT, server->url_timeout);
+            curl_easy_setopt (server->curl, CURLOPT_NOSIGNAL, 1L);
+            curl_easy_setopt (server->curl, CURLOPT_FOLLOWLOCATION, 1L);
+            curl_easy_setopt (server->curl, CURLOPT_MAXREDIRS, 3L);
+            curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
+            server->next = (struct yp_server *)pending_yps;
+            pending_yps = server;
+            INFO3 ("Adding new YP server \"%s\" (timeout %ds, default interval %ds)",
+                    server->url, server->url_timeout, server->touch_interval);
+        }
+        else
+        {
+            server->remove = 0;
+        }
+    }
+    thread_rwlock_unlock (&yp_lock);
+    yp_update = 1;
+}
+
+
+void yp_initialize(void)
+{
+    ice_config_t *config = config_get_config();
+    thread_rwlock_create (&yp_lock);
+    thread_mutex_create (&yp_pending_lock);
+    yp_recheck_config (config);
+    config_release_config ();
+    yp_thread = thread_create("YP Touch Thread", yp_update_thread,
+                            (void *)NULL, THREAD_ATTACHED);
+}
+
+
+
+/* handler for curl, checks if successful handling occurred
+ * return 0 for ok, -1 for this entry failed, -2 for server fail.
+ * On failure case, update and process are modified
+ */
+static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
+{
+    int curlcode;
+    struct yp_server *server = yp->server;
+
+    /* DEBUG2 ("send YP (%s):%s", cmd, post); */
+    yp->cmd_ok = 0;
+    curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp);
+    curlcode = curl_easy_perform (server->curl);
+    if (curlcode)
+    {
+        yp->process = do_yp_add;
+        yp->next_update = now + 1200;
+        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
+        return -2;
+    }
+    if (yp->cmd_ok == 0)
+    {
+        if (yp->error_msg == NULL)
+            yp->error_msg = strdup ("no response from server");
+        if (yp->process == do_yp_add)
+        {
+            ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
+            yp->next_update = now + 7200;
+        }
+        if (yp->process == do_yp_touch)
+        {
+            /* At this point the touch request failed, either because they rejected our session
+             * or the server isn't accessible. This means we have to wait before doing another
+             * add request. We have a minimum delay but we could allow the directory server to
+             * give us a wait time using the TouchFreq header. This time could be given in such
+             * cases as a firewall block or incorrect listenurl.
+             */
+            if (yp->touch_interval < 1200)
+                yp->next_update = now + 1200;
+            else
+                yp->next_update = now + yp->touch_interval;
+            INFO3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
+        }
+        yp->process = do_yp_add;
+        free (yp->sid);
+        yp->sid = NULL;
+        return -1;
+    }
+    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
+    return 0;
+}
+
+
+/* routines for building and issues requests to the YP server */
+static int do_yp_remove (ypdata_t *yp, char *s, unsigned len)
+{
+    int ret = 0;
+
+    if (yp->sid)
+    {
+        ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
+        if (ret >= (signed)len)
+            return ret+1;
+
+        INFO1 ("clearing up YP entry for %s", yp->mount);
+        ret = send_to_yp ("remove", yp, s);
+        free (yp->sid);
+        yp->sid = NULL;
+    }
+    yp->remove = 1;
+    yp->process = do_yp_add;
+    yp_update = 1;
+
+    return ret;
+}
+
+
+static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
+{
+    int ret;
+    char *value;
+
+    value = stats_get_value (yp->mount, "server_type");
+    add_yp_info (yp, value, YP_SERVER_TYPE);
+    free (value);
+
+    value = stats_get_value (yp->mount, "server_name");
+    add_yp_info (yp, value, YP_SERVER_NAME);
+    free (value);
+
+    value = stats_get_value (yp->mount, "server_url");
+    add_yp_info (yp, value, YP_SERVER_URL);
+    free (value);
+
+    value = stats_get_value (yp->mount, "genre");
+    add_yp_info (yp, value, YP_SERVER_GENRE);
+    free (value);
+
+    value = stats_get_value (yp->mount, "bitrate");
+    if (value == NULL)
+        value = stats_get_value (yp->mount, "ice-bitrate");
+    add_yp_info (yp, value, YP_BITRATE);
+    free (value);
+
+    value = stats_get_value (yp->mount, "server_description");
+    add_yp_info (yp, value, YP_SERVER_DESC);
+    free (value);
+
+    value = stats_get_value (yp->mount, "subtype");
+    add_yp_info (yp, value, YP_SUBTYPE);
+    free (value);
+
+    value = stats_get_value (yp->mount, "audio_info");
+    add_yp_info (yp, value, YP_AUDIO_INFO);
+    free (value);
+
+    ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
+                    "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
+                    yp->server_name, yp->server_genre, yp->cluster_password,
+                    yp->server_desc, yp->url, yp->listen_url,
+                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
+    if (ret >= (signed)len)
+        return ret+1;
+    ret = send_to_yp ("add", yp, s);
+    if (ret == 0)
+    {
+        yp->process = do_yp_touch;
+        /* force first touch in 5 secs */
+        yp->next_update = time(NULL) + 5;
+    }
+    return ret;
+}
+
+
+static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
+{
+    unsigned listeners = 0, max_listeners = 1;
+    char *val, *artist, *title;
+    int ret;
+
+    artist = (char *)stats_get_value (yp->mount, "artist");
+    title = (char *)stats_get_value (yp->mount, "title");
+    if (artist || title)
+    {
+         char *song;
+         char *separator = " - ";
+         if (artist == NULL)
+         {
+             artist = strdup("");
+             separator = "";
+         }
+         if (title == NULL) title = strdup("");
+         song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1);
+         if (song)
+         {
+             sprintf (song, "%s%s%s", artist, separator, title);
+             add_yp_info(yp, song, YP_CURRENT_SONG);
+             stats_event (yp->mount, "yp_currently_playing", song);
+             free (song);
+         }
+    }
+    free (artist);
+    free (title);
+
+    val = (char *)stats_get_value (yp->mount, "listeners");
+    if (val)
+    {
+        listeners = atoi (val);
+        free (val);
+    }
+    val = stats_get_value (yp->mount, "max_listeners");
+    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
+        max_listeners = client_limit;
+    else
+        max_listeners = atoi (val);
+    free (val);
+
+    val = stats_get_value (yp->mount, "subtype");
+    if (val)
+    {
+        add_yp_info (yp, val, YP_SUBTYPE);
+        free (val);
+    }
+
+    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
+            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
+            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
+
+    if (ret >= (signed)len)
+        return ret+1; /* space required for above text and nul*/
+
+    if (send_to_yp ("touch", yp, s) == 0)
+    {
+        yp->next_update = now + yp->touch_interval;
+        return 0;
+    }
+    return -1;
+}
+
+
+
+static int process_ypdata (struct yp_server *server, ypdata_t *yp)
+{
+    unsigned len = 1024;
+    char *s = NULL, *tmp;
+
+    if (now < yp->next_update)
+        return 0;
+
+    /* loop just in case the memory area isn't big enough */
+    while (1)
+    {
+        int ret;
+        if ((tmp = realloc (s, len)) == NULL)
+            return 0;
+        s = tmp;
+
+        if (yp->release)
+        {
+            yp->process = do_yp_remove;
+            yp->next_update = 0;
+        }
+
+        ret = yp->process (yp, s, len);
+        if (ret <= 0)
+        {
+           free (s);
+           return ret;
+        }
+        len = ret;
+    }
+    return 0;
+}
+
+
+static void yp_process_server (struct yp_server *server)
+{
+    ypdata_t *yp;
+    int state = 0;
+
+    /* DEBUG1("processing yp server %s", server->url); */
+    yp = server->mounts;
+    while (yp)
+    {
+        now = time (NULL);
+        /* if one of the streams shows that the server cannot be contacted then mark the
+         * other entries for an update later. Assume YP server is dead and skip it for now
+         */
+        if (state == -2)
+        {
+            DEBUG2 ("skiping %s on %s", yp->mount, server->url);
+            yp->process = do_yp_add;
+            yp->next_update += 900;
+        }
+        else
+            state = process_ypdata (server, yp);
+        yp = yp->next;
+    }
+}
+
+
+
+static ypdata_t *create_yp_entry (const char *mount)
+{
+    ypdata_t *yp;
+    char *s;
+
+    yp = calloc (1, sizeof (ypdata_t));
+    do
+    {
+        unsigned len = 512;
+        int ret;
+        char *url;
+        mount_proxy *mountproxy = NULL;
+        ice_config_t *config;
+
+        if (yp == NULL)
+            break;
+        yp->mount = strdup (mount);
+        yp->server_name = strdup ("");
+        yp->server_desc = strdup ("");
+        yp->server_genre = strdup ("");
+        yp->bitrate = strdup ("");
+        yp->server_type = strdup ("");
+        yp->cluster_password = strdup ("");
+        yp->url = strdup ("");
+        yp->current_song = strdup ("");
+        yp->audio_info = strdup ("");
+        yp->subtype = strdup ("");
+        yp->process = do_yp_add;
+
+        url = malloc (len);
+        if (url == NULL)
+            break;
+        config = config_get_config();
+        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
+        if (ret >= (signed)len)
+        {
+            s = realloc (url, ++ret);
+            if (s) url = s;
+            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
+        }
+
+        mountproxy = config_find_mount (config, mount);
+        if (mountproxy && mountproxy->cluster_password)
+            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
+        config_release_config();
+
+        yp->listen_url = util_url_escape (url);
+        free (url);
+        if (yp->listen_url == NULL)
+            break;
+
+        return yp;
+    } while (0);
+
+    yp_destroy_ypdata (yp);
+    return NULL;
+}
+
+
+/* Check for changes in the YP servers configured */
+static void check_servers (void)
+{
+    struct yp_server *server = (struct yp_server *)active_yps,
+                     **server_p = (struct yp_server **)&active_yps;
+
+    while (server)
+    {
+        if (server->remove)
+        {
+            struct yp_server *to_go = server;
+            DEBUG1 ("YP server \"%s\"removed", server->url);
+            *server_p = server->next;
+            server = server->next;
+            destroy_yp_server (to_go);
+            continue;
+        }
+        server_p = &server->next;
+        server = server->next;
+    }
+    /* add new server entries */
+    while (pending_yps)
+    {
+        avl_node *node;
+
+        server = (struct yp_server *)pending_yps;
+        pending_yps = server->next;
+
+        DEBUG1("Add pending yps %s", server->url);
+        server->next = (struct yp_server *)active_yps;
+        active_yps = server;
+
+        /* new YP server configured, need to populate with existing sources */
+        avl_tree_rlock (global.source_tree);
+        node = avl_get_first (global.source_tree);
+        while (node)
+        {
+            ypdata_t *yp;
+
+            source_t *source = node->key;
+            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
+            {
+                DEBUG1 ("Adding existing mount %s", source->mount);
+                yp->server = server;
+                yp->touch_interval = server->touch_interval;
+                yp->next = server->mounts;
+                server->mounts = yp;
+            }
+            node = avl_get_next (node);
+        }
+        avl_tree_unlock (global.source_tree);
+    }
+}
+
+
+static void add_pending_yp (struct yp_server *server)
+{
+    ypdata_t *current, *yp;
+    unsigned count = 0;
+
+    if (server->pending_mounts == NULL)
+        return;
+    current = server->mounts;
+    server->mounts = server->pending_mounts;
+    server->pending_mounts = NULL;
+    yp = server->mounts;
+    while (1)
+    {
+        count++;
+        if (yp->next == NULL)
+            break;
+        yp = yp->next;
+    }
+    yp->next = current;
+    DEBUG2 ("%u YP entries added to %s", count, server->url);
+}
+
+
+static void delete_marked_yp (struct yp_server *server)
+{
+    ypdata_t *yp = server->mounts, **prev = &server->mounts;
+
+    while (yp)
+    {
+        if (yp->remove)
+        {
+            ypdata_t *to_go = yp;
+            DEBUG2 ("removed %s from YP server %s", yp->mount, server->url);
+            *prev = yp->next;
+            yp = yp->next;
+            yp_destroy_ypdata (to_go);
+            continue;
+        }
+        prev = &yp->next;
+        yp = yp->next;
+    }
+}
+
+
+static void *yp_update_thread(void *arg)
+{
+    INFO0("YP update thread started");
+
+    yp_running = 1;
+    while (yp_running)
+    {
+        struct yp_server *server;
+
+        thread_sleep (200000);
+
+        /* do the YP communication */
+        thread_rwlock_rlock (&yp_lock);
+        server = (struct yp_server *)active_yps;
+        while (server)
+        {
+            /* DEBUG1 ("trying %s", server->url); */
+            yp_process_server (server);
+            server = server->next;
+        }
+        thread_rwlock_unlock (&yp_lock);
+
+        /* update the local YP structure */
+        if (yp_update)
+        {
+            thread_rwlock_wlock (&yp_lock);
+            check_servers ();
+            server = (struct yp_server *)active_yps;
+            while (server)
+            {
+                /* DEBUG1 ("Checking yps %s", server->url); */
+                add_pending_yp (server);
+                delete_marked_yp (server);
+                server = server->next;
+            }
+            yp_update = 0;
+            thread_rwlock_unlock (&yp_lock);
+        }
+    }
+    thread_rwlock_destroy (&yp_lock);
+    thread_mutex_destroy (&yp_pending_lock);
+    /* free server and ypdata left */
+    while (active_yps)
+    {
+        struct yp_server *server = (struct yp_server *)active_yps;
+        active_yps = server->next;
+        destroy_yp_server (server);
+    }
+
+    return NULL;
+}
+
+
+
+static void yp_destroy_ypdata(ypdata_t *ypdata)
+{
+    if (ypdata) {
+        if (ypdata->mount) {
+            free (ypdata->mount);
+        }
+        if (ypdata->url) {
+            free (ypdata->url);
+        }
+        if (ypdata->sid) {
+            free(ypdata->sid);
+        }
+        if (ypdata->server_name) {
+            free(ypdata->server_name);
+        }
+        if (ypdata->server_desc) {
+            free(ypdata->server_desc);
+        }
+        if (ypdata->server_genre) {
+            free(ypdata->server_genre);
+        }
+        if (ypdata->cluster_password) {
+            free(ypdata->cluster_password);
+        }
+        if (ypdata->listen_url) {
+            free(ypdata->listen_url);
+        }
+        if (ypdata->current_song) {
+            free(ypdata->current_song);
+        }
+        if (ypdata->bitrate) {
+            free(ypdata->bitrate);
+        }
+        if (ypdata->server_type) {
+            free(ypdata->server_type);
+        }
+        if (ypdata->audio_info) {
+            free(ypdata->audio_info);
+        }
+        free (ypdata->subtype);
+        free (ypdata->error_msg);
+        free (ypdata);
+    }
+}
+
+static void add_yp_info (ypdata_t *yp, void *info, int type)
+{
+    char *escaped;
+
+    if (!info)
+        return;
+
+    escaped = util_url_escape(info);
+    if (escaped == NULL)
+        return;
+
+    switch (type)
+    {
+        case YP_SERVER_NAME:
+            free (yp->server_name);
+            yp->server_name = escaped;
+            break;
+        case YP_SERVER_DESC:
+            free (yp->server_desc);
+            yp->server_desc = escaped;
+            break;
+        case YP_SERVER_GENRE:
+            free (yp->server_genre);
+            yp->server_genre = escaped;
+            break;
+        case YP_SERVER_URL:
+            free (yp->url);
+            yp->url = escaped;
+            break;
+        case YP_BITRATE:
+            free (yp->bitrate);
+            yp->bitrate = escaped;
+            break;
+        case YP_AUDIO_INFO:
+            free (yp->audio_info);
+            yp->audio_info = escaped;
+            break;
+        case YP_SERVER_TYPE:
+            free (yp->server_type);
+            yp->server_type = escaped;
+            break;
+        case YP_CURRENT_SONG:
+            free (yp->current_song);
+            yp->current_song = escaped;
+            break;
+        case YP_CLUSTER_PASSWORD:
+            free (yp->cluster_password);
+            yp->cluster_password = escaped;
+            break;
+        case YP_SUBTYPE:
+            free (yp->subtype);
+            yp->subtype = escaped;
+            break;
+        default:
+            free (escaped);
+    }
+}
+
+
+/* Add YP entries to active servers */
+void yp_add (const char *mount)
+{
+    struct yp_server *server;
+
+    /* make sure YP thread is not modifying the lists */
+    thread_rwlock_rlock (&yp_lock);
+
+    /* make sure we don't race against another yp_add */
+    thread_mutex_lock (&yp_pending_lock);
+    server = (struct yp_server *)active_yps;
+    while (server)
+    {
+        ypdata_t *yp;
+
+        /* on-demand relays may already have a YP entry */
+        yp = find_yp_mount (server->mounts, mount);
+        if (yp == NULL)
+        {
+            /* add new ypdata to each servers pending yp */
+            yp = create_yp_entry (mount);
+            if (yp)
+            {
+                DEBUG2 ("Adding %s to %s", mount, server->url);
+                yp->server = server;
+                yp->touch_interval = server->touch_interval;
+                yp->next = server->pending_mounts;
+                yp->next_update = time(NULL) + 60;
+                server->pending_mounts = yp;
+                yp_update = 1;
+            }
+        }
+        else
+            DEBUG1 ("YP entry %s already exists", mount);
+        server = server->next;
+    }
+    thread_mutex_unlock (&yp_pending_lock);
+    thread_rwlock_unlock (&yp_lock);
+}
+
+
+
+/* Mark an existing entry in the YP list as to be marked for deletion */
+void yp_remove (const char *mount)
+{
+    struct yp_server *server = (struct yp_server *)active_yps;
+
+    thread_rwlock_rlock (&yp_lock);
+    while (server)
+    {
+        ypdata_t *list = server->mounts;
+
+        while (1)
+        {
+            ypdata_t *yp = find_yp_mount (list, mount);
+            if (yp == NULL)
+                break;
+            if (yp->release || yp->remove)
+            {
+                list = yp->next;
+                continue;   /* search again these are old entries */
+            }
+            DEBUG2 ("release %s on YP %s", mount, server->url);
+            yp->release = 1;
+            yp->next_update = 0;
+        }
+        server = server->next;
+    }
+    thread_rwlock_unlock (&yp_lock);
+}
+
+
+/* This is similar to yp_remove, but we force a touch
+ * attempt */
+void yp_touch (const char *mount)
+{
+    struct yp_server *server = (struct yp_server *)active_yps;
+    time_t trigger;
+    ypdata_t *search_list = NULL;
+
+    thread_rwlock_rlock (&yp_lock);
+    /* do update in 3 secs, give stats chance to update */
+    trigger = time(NULL) + 3;
+    if (server)
+        search_list = server->mounts;
+
+    while (server)
+    {
+        ypdata_t *yp = find_yp_mount (search_list, mount);
+        if (yp)
+        {
+            /* we may of found old entries not purged yet, so skip them */
+            if (yp->release != 0 || yp->remove != 0)
+            {
+                search_list = yp->next;
+                continue;
+            }
+            /* only force if touch */
+            if (yp->process == do_yp_touch)
+                yp->next_update = trigger;
+        }
+        server = server->next;
+        if (server)
+            search_list = server->mounts;
+    }
+    thread_rwlock_unlock (&yp_lock);
+}
+
+
+void yp_shutdown (void)
+{
+    yp_running = 0;
+    yp_update = 1;
+    if (yp_thread)
+        thread_join (yp_thread);
+    curl_global_cleanup();
+    free ((char*)server_version);
+    server_version = NULL;
+    INFO0 ("YP thread down");
+}
+

Copied: icecast/tags/icecast-2.3.2/web (from rev 11194, icecast/trunk/icecast/web)

Deleted: icecast/tags/icecast-2.3.2/web/auth.xsl
===================================================================
--- icecast/trunk/icecast/web/auth.xsl	2006-04-20 22:50:21 UTC (rev 11194)
+++ icecast/tags/icecast-2.3.2/web/auth.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,57 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<table border="0" width="100%%">
-<tr>
-<td width="50"></td>
-<td>
-<img align="left" src="/icecast.png" /><h2><center>Authorization Page</center></h2>
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<xsl:for-each select="source">
-<xsl:choose>
-<xsl:when test="listeners">
-<xsl:if test="authenticator">
-<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
-<h3>(<xsl:value-of select="@mount" />)</h3>
-<form method="GET" action="/admin/buildm3u">
-<table border="0" cellpadding="4">
-<tr><td>Username : <input type="text" name="username"/></td></tr>
-<tr><td>Password : <input type="password" name="password"/></td></tr>
-<tr><td><input type="Submit" value="Login"/></td></tr>
-</table>
-<input type="hidden" name="mount" value="{@mount}"/>
-</form>
-</xsl:if>
-</xsl:when>
-<xsl:otherwise>
-<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
-</xsl:otherwise>
-</xsl:choose>
-<br></br>
-<br></br>
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br></br><br></br>
-</td>
-<td width="25"></td></tr>
-</table>
-<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/web/auth.xsl (from rev 14850, icecast/trunk/icecast/web/auth.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/web/auth.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/web/auth.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,57 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<table border="0" width="100%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>Authorization Page</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<xsl:choose>
+<xsl:when test="listeners">
+<xsl:if test="authenticator">
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+<h3>(<xsl:value-of select="@mount" />)</h3>
+<form method="GET" action="/admin/buildm3u">
+<table border="0" cellpadding="4">
+<tr><td>Username : <input type="text" name="username"/></td></tr>
+<tr><td>Password : <input type="password" name="password"/></td></tr>
+<tr><td><input type="Submit" value="Login"/></td></tr>
+</table>
+<input type="hidden" name="mount" value="{@mount}"/>
+</form>
+</xsl:if>
+</xsl:when>
+<xsl:otherwise>
+<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
+</xsl:otherwise>
+</xsl:choose>
+<br></br>
+<br></br>
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="25"></td></tr>
+</table>
+<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/web/server_version.xsl
===================================================================
--- icecast/trunk/icecast/web/server_version.xsl	2006-04-20 22:50:21 UTC (rev 11194)
+++ icecast/tags/icecast-2.3.2/web/server_version.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,91 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Status (Version Info)</center></h2>
-<br />
-<!--index header menu -->
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<table border="0" width="100%" id="table1" cellspacing="0" cellpadding="4">
-	<tr>
-		<td bgcolor="#656565">
-	    <a class="nav" href="admin/">Administration</a> |
-		<a class="nav" href="status.xsl">Server Status</a> |
-		<a class="nav" href="server_version.xsl">Version</a></td>
-	</tr>
-</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-<!--end index header menu -->
-
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>Version Information</h3>
-<table border="0" cellpadding="4">
-<xsl:for-each select="/icestats">
-<xsl:for-each select="server">
-<xsl:if test = "name()!='source'"> 
-<tr>
-	<td width="130">Build</td>
-	<td class="streamdata"><xsl:value-of select="." /></td>
-</tr>
-</xsl:if>
-</xsl:for-each>
-<xsl:for-each select="server_start">
-<xsl:if test = "name()!='source'"> 
-<tr>
-	<td width="130">Server Uptime</td>
-	<td class="streamdata"><xsl:value-of select="." /></td>
-</tr>
-</xsl:if>
-</xsl:for-each>
-</xsl:for-each>
-<tr>
-	<td width="130">Download</td>
-	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/download.php">icecast.org</a></td>
-</tr>
-<tr>
-	<td width="130">Subversion</td>
-	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/svn.php">click here</a></td>
-</tr>
-<tr>
-	<td width="130">Documentation</td>
-	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/docs.php">click here</a></td>
-</tr>
-<tr>
-	<td width="130">Stream Directory </td>
-	<td class="streamdata"><a class="nav" target="_blank" href="http://dir.xiph.org/index.php/">dir.xiph.org</a></td>
-</tr>
-<tr>
-	<td width="130">Community</td>
-	<td class="streamdata"><a class="nav" target="_blank" href="http://forum.icecast.org/">forum.icecast.org</a></td>
-</tr>
-</table>
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-
-<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>
\ No newline at end of file

Copied: icecast/tags/icecast-2.3.2/web/server_version.xsl (from rev 14850, icecast/trunk/icecast/web/server_version.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/web/server_version.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/web/server_version.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,91 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Server Information</h2>
+<br />
+<!--index header menu -->
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<table border="0" width="100%" id="table1" cellspacing="0" cellpadding="4">
+	<tr>
+		<td bgcolor="#656565">
+	    <a class="nav" href="admin/">Administration</a>
+		<a class="nav" href="status.xsl">Server Status</a>
+		<a class="nav" href="server_version.xsl">Version</a></td>
+	</tr>
+</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+<!--end index header menu -->
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Server Information</h3>
+<table border="0" cellpadding="4">
+<xsl:for-each select="/icestats">
+<tr>
+	<td width="130">Location</td>
+	<td class="streamdata"><xsl:value-of select="location" /></td>
+</tr>
+<tr>
+	<td width="130">Admin</td>
+	<td class="streamdata"><xsl:value-of select="admin" /></td>
+</tr>
+<tr>
+    <td width="130">Host</td>
+    <td class="streamdata"><xsl:value-of select="host" /></td>
+</tr>
+<tr>
+    <td width="130">Version</td>
+    <td class="streamdata"><xsl:value-of select="server_id" /></td>
+</tr>
+</xsl:for-each>
+<tr>
+	<td width="130">Download</td>
+	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/download.php">icecast.org</a></td>
+</tr>
+<tr>
+	<td width="130">Subversion</td>
+	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/svn.php">click here</a></td>
+</tr>
+<tr>
+	<td width="130">Documentation</td>
+	<td class="streamdata"><a class="nav" target="_blank" href="http://icecast.org/docs.php">click here</a></td>
+</tr>
+<tr>
+	<td width="130">Stream Directory </td>
+	<td class="streamdata"><a class="nav" target="_blank" href="http://dir.xiph.org/index.php">dir.xiph.org</a></td>
+</tr>
+<tr>
+	<td width="130">Community</td>
+	<td class="streamdata"><a class="nav" target="_blank" href="http://forum.icecast.org/">forum.icecast.org</a></td>
+</tr>
+</table>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+
+<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/web/status.xsl
===================================================================
--- icecast/trunk/icecast/web/status.xsl	2006-04-20 22:50:21 UTC (rev 11194)
+++ icecast/tags/icecast-2.3.2/web/status.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,121 +0,0 @@
-<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
-<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
-<xsl:template match = "/icestats" >
-<html>
-<head>
-<title>Icecast Streaming Media Server</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
-<img align="left" src="/icecast.png" /><h2><center>Icecast2 Status</center></h2>
-<br />
-<!--index header menu -->
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<table border="0" width="100%" id="table1" cellspacing="0" cellpadding="4">
-	<tr>
-		<td bgcolor="#656565">
-		<a class="nav" href="admin/">Administration</a> |
-		<a class="nav" href="status.xsl">Server Status</a> |
-		<a class="nav" href="server_version.xsl">Version</a> |</td>
-	</tr>
-</table>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-<!--end index header menu -->
-<!--mount point stats-->
-<xsl:for-each select="source">
-<xsl:choose>
-<xsl:when test="listeners">
-<div class="roundcont">
-<div class="roundtop">
-<img src="/corner_topleft.jpg" class="corner" style="display: none" />
-</div>
-<div class="newscontent">
-<h3>
-<xsl:choose>
-<xsl:when test="authenticator">
-<a href="/auth.xsl"><img border="0" src="/key.png"/></a>
-</xsl:when>
-<xsl:otherwise>
-<a href="{@mount}.m3u"><img border="0" src="/tunein.png"/></a>
-</xsl:otherwise>
-</xsl:choose>
-Mount Point : (<xsl:value-of select="@mount" />) : 
-<xsl:choose>
-<xsl:when test="authenticator">
-<a href="/auth.xsl">Click to Listen</a>
-</xsl:when>
-<xsl:otherwise>
-<a href="{@mount}.m3u">Click to Listen</a>
-</xsl:otherwise>
-</xsl:choose>
-</h3>
-
-<table border="0" cellpadding="4">
-<xsl:if test="server_name">
-<tr><td>Stream Title:</td><td class="streamdata"> <xsl:value-of select="server_name" /></td></tr>
-</xsl:if>
-<xsl:if test="server_description">
-<tr><td>Stream Description:</td><td class="streamdata"> <xsl:value-of select="server_description" /></td></tr>
-</xsl:if>
-<tr><td>Content Type:</td><td class="streamdata"><xsl:value-of select="server_type" /></td></tr>
-<tr><td>Mount Uptime:</td><td class="streamdata"><xsl:value-of select="stream_start" /></td></tr>
-<xsl:if test="bitrate">
-<tr><td>Bitrate:</td><td class="streamdata"> <xsl:value-of select="bitrate" /></td></tr>
-</xsl:if>
-<xsl:if test="quality">
-<tr><td>Quality:</td><td class="streamdata"> <xsl:value-of select="quality" /></td></tr>
-</xsl:if>
-<xsl:if test="video_quality">
-<tr><td>Video Quality:</td><td class="streamdata"> <xsl:value-of select="video_quality" /></td></tr>
-</xsl:if>
-<xsl:if test="frame_size">
-<tr><td>Framesize:</td><td class="streamdata"> <xsl:value-of select="frame_size" /></td></tr>
-</xsl:if>
-<xsl:if test="frame_rate">
-<tr><td>Framerate:</td><td class="streamdata"> <xsl:value-of select="frame_rate" /></td></tr>
-</xsl:if>
-<xsl:if test="listeners">
-<tr><td>Current Listeners:</td><td class="streamdata"> <xsl:value-of select="listeners" /></td></tr>
-</xsl:if>
-<xsl:if test="listener_peak">
-<tr><td>Peak Listeners:</td><td class="streamdata"> <xsl:value-of select="listener_peak" /></td></tr>
-</xsl:if>
-<xsl:if test="genre">
-<tr><td>Stream Genre:</td><td class="streamdata"> <xsl:value-of select="genre" /></td></tr>
-</xsl:if>
-<xsl:if test="server_url">
-<tr><td>Stream URL:</td><td class="streamdata"> <a target="_blank" href="{server_url}"><xsl:value-of select="server_url" /></a></td></tr>
-</xsl:if>
-<tr><td>Current Song:</td><td class="streamdata"> 
-<xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if><xsl:value-of select="title" /></td></tr>
-</table>
-</div>
-<div class="roundbottom">
-<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
-</div>
-</div>
-<br />
-<br />
-</xsl:when>
-<xsl:otherwise>
-<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
-</xsl:otherwise>
-</xsl:choose>
-
-</xsl:for-each>
-<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
-
-
-<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
-</body>
-</html>
-</xsl:template>
-</xsl:stylesheet>

Copied: icecast/tags/icecast-2.3.2/web/status.xsl (from rev 14850, icecast/trunk/icecast/web/status.xsl)
===================================================================
--- icecast/tags/icecast-2.3.2/web/status.xsl	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/web/status.xsl	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,122 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
+<h2>Icecast2 Status</h2>
+<br />
+<!--index header menu -->
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<table border="0" width="100%" id="table1" cellspacing="0" cellpadding="4">
+	<tr>
+		<td bgcolor="#656565">
+		<a class="nav" href="admin/">Administration</a>
+		<a class="nav" href="status.xsl">Server Status</a>
+		<a class="nav" href="server_version.xsl">Version</a></td>
+	</tr>
+</table>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+<!--end index header menu -->
+<!--mount point stats-->
+<xsl:for-each select="source">
+<xsl:choose>
+<xsl:when test="listeners">
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+    <div class="streamheader">
+        <table cellspacing="0" cellpadding="0">
+            <colgroup align="left" />
+            <colgroup align="right" width="300" />
+            <tr>
+                <td><h3>Mount Point <xsl:value-of select="@mount" /></h3></td>
+                <xsl:choose>
+                    <xsl:when test="authenticator">
+                        <td align="right"><a class="auth" href="/auth.xsl">Login</a></td>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <td align="right"> <a href="{@mount}.m3u">M3U</a> <a href="{@mount}.xspf">XSPF</a></td>
+                    </xsl:otherwise>
+                </xsl:choose>
+        </tr></table>
+    </div>
+
+<table border="0" cellpadding="4">
+<xsl:if test="server_name">
+<tr><td>Stream Title:</td><td class="streamdata"> <xsl:value-of select="server_name" /></td></tr>
+</xsl:if>
+<xsl:if test="server_description">
+<tr><td>Stream Description:</td><td class="streamdata"> <xsl:value-of select="server_description" /></td></tr>
+</xsl:if>
+<xsl:if test="server_type">
+<tr><td>Content Type:</td><td class="streamdata"><xsl:value-of select="server_type" /></td></tr>
+</xsl:if>
+<xsl:if test="stream_start">
+<tr><td>Mount started:</td><td class="streamdata"><xsl:value-of select="stream_start" /></td></tr>
+</xsl:if>
+<xsl:if test="bitrate">
+<tr><td>Bitrate:</td><td class="streamdata"> <xsl:value-of select="bitrate" /></td></tr>
+</xsl:if>
+<xsl:if test="quality">
+<tr><td>Quality:</td><td class="streamdata"> <xsl:value-of select="quality" /></td></tr>
+</xsl:if>
+<xsl:if test="video_quality">
+<tr><td>Video Quality:</td><td class="streamdata"> <xsl:value-of select="video_quality" /></td></tr>
+</xsl:if>
+<xsl:if test="frame_size">
+<tr><td>Framesize:</td><td class="streamdata"> <xsl:value-of select="frame_size" /></td></tr>
+</xsl:if>
+<xsl:if test="frame_rate">
+<tr><td>Framerate:</td><td class="streamdata"> <xsl:value-of select="frame_rate" /></td></tr>
+</xsl:if>
+<xsl:if test="listeners">
+<tr><td>Current Listeners:</td><td class="streamdata"> <xsl:value-of select="listeners" /></td></tr>
+</xsl:if>
+<xsl:if test="listener_peak">
+<tr><td>Peak Listeners:</td><td class="streamdata"> <xsl:value-of select="listener_peak" /></td></tr>
+</xsl:if>
+<xsl:if test="genre">
+<tr><td>Stream Genre:</td><td class="streamdata"> <xsl:value-of select="genre" /></td></tr>
+</xsl:if>
+<xsl:if test="server_url">
+<tr><td>Stream URL:</td><td class="streamdata"> <a target="_blank" href="{server_url}"><xsl:value-of select="server_url" /></a></td></tr>
+</xsl:if>
+<tr><td>Current Song:</td><td class="streamdata"> 
+<xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if><xsl:value-of select="title" /></td></tr>
+</table>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br />
+<br />
+</xsl:when>
+<xsl:otherwise>
+<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
+</xsl:otherwise>
+</xsl:choose>
+
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+
+
+<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>

Deleted: icecast/tags/icecast-2.3.2/web/style.css
===================================================================
--- icecast/trunk/icecast/web/style.css	2006-04-20 22:50:21 UTC (rev 11194)
+++ icecast/tags/icecast-2.3.2/web/style.css	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,214 +0,0 @@
-/******************************************************************************
-
-	This file styles the bar that goes across the top of all Xiph.Org
-	pages.
-	
-	The style that comes from this was first (to my knowledge) at
-	http://alistapart.com/stories/practicalcss/ in the
-	"Splitting the Difference" section.
-
-******************************************************************************/
-
-/* This effect doesn't work at all if all content is pinched in a bit. */
-html, body {
-	margin: 0;
-	padding: 0;
-}
-
-body {
-	margin-left: 50px;
-	margin-right: 25px;
-	background-color: #000;
-}
-
-.xiphnav {
-	font-family: Verdana, sans-serif;
-	font-weight: normal;
-	padding:    .25em;
-	margin-bottom:  .5em;
-	border-bottom:  1px solid #000;
-	color: #000;
-	background: #aaa;
-}
-h2 {
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	font-size: 250%;
-	color: #fff;
-	margin-top:3px;
-}
-h1 {
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	font-size: 100%;
-	color: #fff;
-	margin-top:3px;
-}
-.nav {
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	font-size: 110%;
-	color: #fff;
-}
-.nav:hover {
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	color: #f8ef64;
-}
-.xiphnav_a {
-    text-decoration: none;
-    font-weight: normal;
-    color: #000;
-}
-.news {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: normal;
-    color: #fff;
-}
-.newsheader {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: normal;
-    font-size: 110%;
-    color: #f8ef64;
-    background: #444;
-}
-.streamtd {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: normal;
-    font-size: 85%;
-    color: #fff;
-    padding:15px;
-}
-.streamtd_alt {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: normal;
-    font-size: 85%;
-    color: #fff;
-}
-
-.streamtd_alt_2 {
-    font-family: Verdana, sans-serif;
-    text-decoration: underline;
-    font-weight: normal;
-    font-size: 85%;
-    color: #fff;
-}
-
-td {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: normal;
-    color: #fff;
-}
-.roundcont {
-	width: 90%;
-	background-color: #656565;
-	color: #fff;
-}
-
-.newscontent {
-	margin: 0 20px;
-}
-.newscontent h3 {
-	margin: 10px 0px;
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	font-size: 110%;
-	color: #f8ef64;
-        border-bottom:  1px groove #ACACAC;
-}
-.newscontent h4 {
-	margin: 10px 0px;
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: bold;
-	font-size: 110%;
-	color: #f8ef64;
-}
-.newscontent p {
-	margin: 0 0;
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: none;
-	font-size: 90%;
-}
-.newscontent td {
-	margin: 0 0;
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: none;
-	font-size: 90%;
-}
-.newscontent td.streamdata {
-	margin: 0 0;
-	font-family: Verdana, sans-serif;
-	text-decoration: none;
-	font-weight: none;
-	font-size: 90%;
-        color: #f8ef64;
-}
-.newscontent a {
-    font-family: Verdana, sans-serif;
-    text-decoration: underline;
-    font-weight: bold;
-    color: #f8ef64;
-}
-.newscontent a:hover {
-    font-family: Verdana, sans-serif;
-    text-decoration: underline;
-    font-weight: bold;
-    color: #fff;
-}
-.newscontent a.nav2 {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    font-weight: bold;
-    background: #444;
-    color: #f8ef64;
-}
-.newscontent a.nav2:hover {
-    font-family: Verdana, sans-serif;
-    text-decoration: none;
-    background: #777;
-    font-weight: bold;
-    color: #fff;
-}
-.poster {
-	font-family: Verdana, sans-serif;
-	margin: 0px;
-	margin-top: 50px;
-	display: block;
-	text-decoration: none;
-	font-size: 100%;
-	color: #f8ef64;
-	border-top:  3px dashed #000;
-}
-.roundcont p {
-	margin: 10px 50px;
-}
-
-.roundtop { 
-	background: url(/corner_topright.jpg) no-repeat top right; 
-}
-
-.roundbottom {
-	background: url(/corner_bottomright.jpg) no-repeat top right; 
-}
-
-img.corner {
-	width: 15px;
-	height: 15px;
-	border: none;
-	display: block !important;
-}
-
-

Copied: icecast/tags/icecast-2.3.2/web/style.css (from rev 14850, icecast/trunk/icecast/web/style.css)
===================================================================
--- icecast/tags/icecast-2.3.2/web/style.css	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/web/style.css	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,250 @@
+/******************************************************************************
+
+	This file styles the bar that goes across the top of all Xiph.Org
+	pages.
+	
+	The style that comes from this was first (to my knowledge) at
+	http://alistapart.com/stories/practicalcss/ in the
+	"Splitting the Difference" section.
+
+******************************************************************************/
+
+/* This effect doesn't work at all if all content is pinched in a bit. */
+html, body {
+	margin: 0;
+	padding: 0;
+}
+
+body {
+	margin-left: 50px;
+	margin-right: 25px;
+	background-color: #000;
+}
+
+.xiphnav {
+	font-family: Verdana, sans-serif;
+	font-weight: normal;
+	padding:    .25em;
+	margin-bottom:  .5em;
+	border-bottom:  1px solid #000;
+	color: #000;
+	background: #aaa;
+}
+h2 {
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+    font-size: 3em;
+	color: #fff;
+    padding: 10px 0px 10px 80px;
+	margin-top:3px;
+    background: transparent url(/icecast.png) no-repeat scroll left center
+}
+h1 {
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 100%;
+	color: #fff;
+	margin-top:3px;
+}
+.nav {
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 110%;
+	color: #fff;
+}
+.nav:hover {
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	color: #f8ef64;
+}
+.xiphnav_a {
+    text-decoration: none;
+    font-weight: normal;
+    color: #000;
+}
+.news {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    color: #fff;
+}
+.newsheader {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 110%;
+    color: #f8ef64;
+    background: #444;
+}
+.streamtd {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 85%;
+    color: #fff;
+    padding:15px;
+}
+.streamtd_alt {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 85%;
+    color: #fff;
+}
+
+.streamtd_alt_2 {
+    font-family: Verdana, sans-serif;
+    text-decoration: underline;
+    font-weight: normal;
+    font-size: 85%;
+    color: #fff;
+}
+
+td {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    color: #fff;
+}
+.roundcont {
+	width: 90%;
+	background-color: #656565;
+	color: #fff;
+}
+.roundcont a {
+    margin: 0px 10px;
+}
+.newscontent {
+	margin: 0 20px;
+}
+h3 {
+	margin: 0px;
+    padding: 0px;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 110%;
+	color: #f8ef64;
+}
+.newscontent h3 {
+    margin-bottom: 10px;
+    border-bottom: 1px groove #ACACAC;
+}
+.newscontent h4 {
+	margin: 10px 0px;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 110%;
+	color: #f8ef64;
+}
+.newscontent p {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+}
+.newscontent td {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+}
+.newscontent td.streamdata {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+        color: #f8ef64;
+}
+.streamheader table {
+    width: 100%;
+    margin-bottom: 5px;
+    border-bottom:  1px groove #ACACAC;
+}
+.streamheader td {
+    margin: 0px;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    padding: 10 5 10 5;
+    border: 0px solid white;
+}
+.streamheader h3 {
+    border: 0px;
+}
+.streamheader a {
+    padding: 8px 5px 3px 30px;
+    text-decoration: none;
+    background: transparent url("/tunein.png") no-repeat left center;
+}
+.streamheader a.auth {
+    padding-top: 10px;
+    padding-bottom: 10px;
+    padding-left: 32px;
+    background: transparent url("/key.png") no-repeat left center;
+}
+.newscontent a {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    margin: 0px;
+    color: #f8ef64;
+}
+.newscontent a:hover {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    color: #fff;
+}
+.newscontent a.nav2 {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    padding: 2px 9px;
+    background: #444;
+    color: #f8ef64;
+}
+.newscontent a.nav2:hover {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    background: #777;
+    font-weight: bold;
+    color: #fff;
+}
+.poster {
+	font-family: Verdana, sans-serif;
+    margin: 50px 120px 20px 0px;
+	display: block;
+	text-decoration: none;
+	font-size: 100%;
+	color: #f8ef64;
+    padding: 5px;
+    border-top:  1px groove #ACACAC;
+}
+.roundcont p {
+	margin: 10px 50px;
+}
+
+.roundtop { 
+	background: url(/corner_topright.jpg) no-repeat top right; 
+}
+
+.roundbottom {
+	background: url(/corner_bottomright.jpg) no-repeat top right; 
+}
+
+img.corner {
+	width: 15px;
+	height: 15px;
+	border: none;
+	display: block !important;
+}
+
+

Deleted: icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp
===================================================================
--- icecast/trunk/icecast/win32/Icecast2win.dsp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,270 +0,0 @@
-# Microsoft Developer Studio Project File - Name="Icecast2win" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Application" 0x0101
-
-CFG=Icecast2win - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE 
-!MESSAGE NMAKE /f "Icecast2win.mak".
-!MESSAGE 
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE 
-!MESSAGE NMAKE /f "Icecast2win.mak" CFG="Icecast2win - Win32 Debug"
-!MESSAGE 
-!MESSAGE Possible choices for configuration are:
-!MESSAGE 
-!MESSAGE "Icecast2win - Win32 Release" (based on "Win32 (x86) Application")
-!MESSAGE "Icecast2win - Win32 Debug" (based on "Win32 (x86) Application")
-!MESSAGE 
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-MTL=midl.exe
-RSC=rc.exe
-
-!IF  "$(CFG)" == "Icecast2win - Win32 Release"
-
-# PROP BASE Use_MFC 5
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "Release"
-# PROP BASE Intermediate_Dir "Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 5
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c
-# ADD CPP /nologo /MT /W3 /GX /O2 /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
-# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
-# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
-# ADD LINK32 Releaseicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib ../../theora/win32/Static_Release/theora_static.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /out:"Release/Icecast2.exe"
-# SUBTRACT LINK32 /pdb:none
-
-!ELSEIF  "$(CFG)" == "Icecast2win - Win32 Debug"
-
-# PROP BASE Use_MFC 5
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "Debug"
-# PROP BASE Intermediate_Dir "Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 6
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "Debug"
-# PROP Intermediate_Dir "Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_WIN32" /D "_AFXDLL" /FD /GZ /c
-# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
-# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 Debugicecast\icecast.lib ..\..\curl\lib\Debug\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static_d.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static_d.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib ../../theora/win32/Static_Debug/theora_static_d.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /nodefaultlib:"libcd.lib" /pdbtype:sept
-# SUBTRACT LINK32 /pdb:none
-
-!ENDIF 
-
-# Begin Target
-
-# Name "Icecast2win - Win32 Release"
-# Name "Icecast2win - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=.\ConfigTab.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2win.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2win.rc
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2winDlg.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\StatsTab.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\Status.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\StdAfx.cpp
-# ADD CPP /Yc"stdafx.h"
-# End Source File
-# Begin Source File
-
-SOURCE=.\TabCtrlSSL.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\TabCtrlSSL.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\TabPageSSL.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\TabPageSSL.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\Traynot.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\TRAYNOT.H
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=.\colors.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\ConfigTab.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2win.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2winDlg.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\Resource.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\StatsTab.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\Status.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\StdAfx.h
-# End Source File
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# Begin Source File
-
-SOURCE=.\bitmap1.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\bitmap2.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\black.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\credits.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\cursor1.cur
-# End Source File
-# Begin Source File
-
-SOURCE=.\cursor2.cur
-# End Source File
-# Begin Source File
-
-SOURCE=.\green1.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\icecast.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\Icecast2.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\res\Icecast2.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\icecast2logo2.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\icecast2title.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\res\Icecast2win.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\res\Icecast2win.rc2
-# End Source File
-# Begin Source File
-
-SOURCE=.\ico00001.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\icon1.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\icon2.ico
-# End Source File
-# Begin Source File
-
-SOURCE=.\running.bmp
-# End Source File
-# Begin Source File
-
-SOURCE=.\stopped.bmp
-# End Source File
-# End Group
-# Begin Source File
-
-SOURCE=.\ReadMe.txt
-# End Source File
-# End Target
-# End Project

Copied: icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp (from rev 14985, icecast/trunk/icecast/win32/Icecast2win.dsp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/Icecast2win.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,274 @@
+# Microsoft Developer Studio Project File - Name="Icecast2win" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=Icecast2win - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Icecast2win.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Icecast2win.mak" CFG="Icecast2win - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Icecast2win - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "Icecast2win - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Icecast2win - Win32 Release"
+
+# PROP BASE Use_MFC 5
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 5
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "releasewin_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D PACKAGE_VERSION=\"2.3.2\" /D "USE_YP" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
+# ADD LINK32 libcurl.lib ogg_static_d.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib winmm.lib theora_static_d.lib libspeex.lib /nologo /version:2.3 /subsystem:windows /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "Icecast2win - Win32 Debug"
+
+# PROP BASE Use_MFC 5
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 5
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "debugwin_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../src" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_WIN32" /D PACKAGE_VERSION=\"2.3.2\" /D "USE_YP" /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libcurl.lib ogg_static_d.lib vorbis_static_d.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib winmm.lib theora_static_d.lib libspeex.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /nodefaultlib:"libcd.lib" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Icecast2win - Win32 Release"
+# Name "Icecast2win - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ConfigTab.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2win.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2win.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2winDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=\xiph\icecast\trunk\icecast\src\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\StatsTab.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Status.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\TabCtrlSSL.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TabCtrlSSL.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TabPageSSL.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TabPageSSL.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Traynot.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TRAYNOT.H
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\colors.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConfigTab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2win.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2winDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StatsTab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Status.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\bitmap1.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\bitmap2.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\black.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\credits.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\cursor1.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\cursor2.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\green1.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icecast.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Icecast2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Icecast2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icecast2logo2.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\icecast2title.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Icecast2win.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Icecast2win.rc2
+# End Source File
+# Begin Source File
+
+SOURCE=.\ico00001.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icon1.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icon2.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\running.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\stopped.bmp
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project

Deleted: icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp
===================================================================
--- icecast/trunk/icecast/win32/Icecast2winDlg.cpp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,1200 +0,0 @@
-// Icecast2winDlg.cpp : implementation file
-//
-
-#include "stdafx.h"
-#include "Icecast2win.h"
-#include "Icecast2winDlg.h"
-#include <process.h>
-
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <stdlib.h>
-
-extern "C" {
-#include "thread.h"
-#include "avl.h"
-#include "log.h"
-#include "global.h"
-#include "httpp.h"
-#include "sock.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-}
-
-#include <afxinet.h>
-
-#ifdef _DEBUG
-#define new DEBUG_NEW
-#undef THIS_FILE
-static char THIS_FILE[] = __FILE__;
-#endif
-
-#define ICECAST_VERSION "2.x"
-CEdit	*g_accessControl;
-CEdit	*g_errorControl;
-CIcecast2winDlg	*g_mainDialog;
-bool	g_tailAccess = false;
-bool	g_tailError = false;
-CString gConfigurationSave;
-
-char	gTitleSource[1024] = "";
-char	gTitleName[1024] = "";
-
-#define MAXSTATSPERSOURCE 30
-#define MAXSOURCES 1024
-
-typedef struct tagElement {
-	CString	name;
-	CString	value;
-	int		titleFlag;
-} Element;
-
-typedef struct tagElementAdditional {
-	CString source;
-	CString	name;
-	CString	value;
-	int		titleFlag;
-} ElementAdditional;
-
-
-typedef struct tagMainElement {
-	CString	source;
-	long	numStats;
-	Element	stats[MAXSTATSPERSOURCE];
-	int		populated;
-} MainElement;
-
-typedef struct tagMainElementAdditional {
-	long	numStats;
-	ElementAdditional	stats[MAXSTATSPERSOURCE];
-} MainElementAdditional;
-
-
-MainElement gStats[MAXSOURCES];
-MainElement gGlobalStats;
-MainElementAdditional	gAdditionalGlobalStats;
-
-long	numMainStats;
-
-extern "C" {
-	int main(int argc, char **argv);
-}
-
-
-void AddToAdditionalGlobalStats(CString source, CString name) {
-	int foundit = 0;
-	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
-		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
-			foundit = 1;
-			break;
-		}
-	}
-	if (!foundit) {
-		gAdditionalGlobalStats.stats[gAdditionalGlobalStats.numStats].source = source;
-		gAdditionalGlobalStats.stats[gAdditionalGlobalStats.numStats].name = name;
-		gAdditionalGlobalStats.numStats++;
-	}
-	g_mainDialog->UpdateStatsLists();
-}
-
-void ClearTitleAdditionalGlobalStats(CString source, CString name) {
-	int foundit = 0;
-	int i,j;
-	for (i=0;i<gAdditionalGlobalStats.numStats;i++) {
-		gAdditionalGlobalStats.stats[i].titleFlag = 0;
-	}
-	for (i=0;i<numMainStats;i++) {
-		for (j=0;j<gStats[i].numStats;j++) {
-			if ((gStats[i].source == source) && (gStats[i].stats[j].name == name)) {
-				gStats[i].stats[j].titleFlag = 0;
-			}
-		}
-	}
-	g_mainDialog->UpdateStatsLists();
-}
-void AddToTitleAdditionalGlobalStats(CString source, CString name) {
-	int foundit = 0;
-	int i,j;
-	for (i=0;i<gAdditionalGlobalStats.numStats;i++) {
-		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
-			ClearTitleAdditionalGlobalStats(source, name);
-			gAdditionalGlobalStats.stats[i].titleFlag = 1;
-			strcpy(gTitleSource, source);
-			strcpy(gTitleName, name);
-			foundit = 1;
-			break;
-		}
-	}
-	if (!foundit) {
-		for (i=0;i<numMainStats;i++) {
-			for (j=0;j<gStats[i].numStats;j++) {
-				if ((gStats[i].source == source) && (gStats[i].stats[j].name == name)) {
-					ClearTitleAdditionalGlobalStats(source, name);
-					gStats[i].stats[j].titleFlag = 1;
-					strcpy(gTitleSource, source);
-					strcpy(gTitleName, name);
-					foundit = 1;
-					break;
-				}
-			}
-		}
-	}
-	g_mainDialog->UpdateStatsLists();
-}
-
-void RemoveFromAdditionalGlobalStats(CString source, CString name) {
-	int foundit = 0;
-	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
-		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
-			for (int j=i+1;j < gAdditionalGlobalStats.numStats;j++) {
-				gAdditionalGlobalStats.stats[j-1].name = gAdditionalGlobalStats.stats[j].name;
-				gAdditionalGlobalStats.stats[j-1].value = gAdditionalGlobalStats.stats[j].value;
-				gAdditionalGlobalStats.stats[j-1].source = gAdditionalGlobalStats.stats[j].source;
-			}
-			gAdditionalGlobalStats.numStats--;
-			break;
-		}
-	}
-	g_mainDialog->UpdateStatsLists();
-}
-/////////////////////////////////////////////////////////////////////////////
-// CAboutDlg dialog used for App About
-
-class CAboutDlg : public CDialog
-{
-public:
-	CAboutDlg();
-
-// Dialog Data
-	//{{AFX_DATA(CAboutDlg)
-	enum { IDD = IDD_ABOUTBOX };
-	//}}AFX_DATA
-
-	// ClassWizard generated virtual function overrides
-	//{{AFX_VIRTUAL(CAboutDlg)
-	protected:
-	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
-	//}}AFX_VIRTUAL
-
-// Implementation
-protected:
-	//{{AFX_MSG(CAboutDlg)
-	//}}AFX_MSG
-	DECLARE_MESSAGE_MAP()
-};
-
-CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
-{
-	//{{AFX_DATA_INIT(CAboutDlg)
-	//}}AFX_DATA_INIT
-}
-
-void CAboutDlg::DoDataExchange(CDataExchange* pDX)
-{
-	CDialog::DoDataExchange(pDX);
-	//{{AFX_DATA_MAP(CAboutDlg)
-	//}}AFX_DATA_MAP
-}
-
-BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
-	//{{AFX_MSG_MAP(CAboutDlg)
-		// No message handlers
-	//}}AFX_MSG_MAP
-END_MESSAGE_MAP()
-
-
-/////////////////////////////////////////////////////////////////////////////
-// CIcecast2winDlg dialog
-
-CIcecast2winDlg::CIcecast2winDlg(CWnd* pParent /*=NULL*/)
-	: CDialog(CIcecast2winDlg::IDD, pParent)
-{
-	//{{AFX_DATA_INIT(CIcecast2winDlg)
-	m_AccessEdit = _T("");
-	m_ErrorEdit = _T("");
-	m_ConfigEdit = _T("");
-	m_ServerStatus = _T("");
-	m_SourcesConnected = _T("");
-	m_NumClients = _T("");
-	m_StatsEdit = _T("");
-	m_Autostart = FALSE;
-	//}}AFX_DATA_INIT
-	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
-	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
-	m_pTray = NULL;
-}
-
-void CIcecast2winDlg::DoDataExchange(CDataExchange* pDX)
-{
-	CDialog::DoDataExchange(pDX);
-	//{{AFX_DATA_MAP(CIcecast2winDlg)
-	DDX_Control(pDX, IDC_STATIC_SS, m_SS);
-	DDX_Control(pDX, IDC_SERVERSTATUS, m_ServerStatusBitmap);
-	DDX_Control(pDX, IDC_START, m_StartButton);
-	DDX_Control(pDX, IDC_MAINTAB, m_MainTab);
-	DDX_Check(pDX, IDC_AUTOSTART, m_Autostart);
-	//}}AFX_DATA_MAP
-}
-
-BEGIN_MESSAGE_MAP(CIcecast2winDlg, CDialog)
-	//{{AFX_MSG_MAP(CIcecast2winDlg)
-	ON_WM_SYSCOMMAND()
-	ON_WM_PAINT()
-	ON_WM_QUERYDRAGICON()
-	ON_NOTIFY(TCN_SELCHANGE, IDC_MAINTAB, OnSelchangeMaintab)
-	ON_COMMAND(ID_FILE_EXIT, OnFileExit)
-	ON_WM_TIMER()
-	ON_COMMAND(ID_FILE_STARTSERVER, OnFileStartserver)
-	ON_COMMAND(ID_FILE_STOPSERVER, OnFileStopserver)
-	ON_BN_CLICKED(IDC_START, OnStart)
-	ON_WM_CLOSE()
-	ON_WM_SIZE()
-	ON_BN_CLICKED(IDC_HIDESYSTRAY, OnHidesystray)
-	ON_COMMAND(ID_BLANK_RESTORE, OnBlankRestore)
-	ON_MESSAGE(WM_TRAY_NOTIFY, OnTrayNotify)
-	ON_WM_DESTROY()
-	ON_COMMAND(ID_FILE_EDITCONFIGURATION, OnFileEditconfiguration)
-	ON_COMMAND(ID_ABOUT_HELP, OnAboutHelp)
-	ON_COMMAND(ID_ABOUT_CREDITS, OnAboutCredits)
-	//}}AFX_MSG_MAP
-END_MESSAGE_MAP()
-
-/////////////////////////////////////////////////////////////////////////////
-// CIcecast2winDlg message handlers
-
-#include "colors.h"
-
-
-BOOL CIcecast2winDlg::OnInitDialog()
-{
-	CDialog::OnInitDialog();
-
-	// Add "About..." menu item to system menu.
-
-	// IDM_ABOUTBOX must be in the system command range.
-	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
-	ASSERT(IDM_ABOUTBOX < 0xF000);
-
-	CMenu* pSysMenu = GetSystemMenu(FALSE);
-	if (pSysMenu != NULL)
-	{
-		CString strAboutMenu;
-		strAboutMenu.LoadString(IDS_ABOUTBOX);
-		if (!strAboutMenu.IsEmpty())
-		{
-			pSysMenu->AppendMenu(MF_SEPARATOR);
-			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
-		}
-	}
-
-	g_mainDialog = this;
-
-	// Set the icon for this dialog.  The framework does this automatically
-	//  when the application's main window is not a dialog
-	SetIcon(m_hIcon, TRUE);			// Set big icon
-	SetIcon(m_hIcon, FALSE);		// Set small icon
-	
-	// TODO: Add extra initialization here
-	config_read();
-
-	statsTab.m_colSource0Width = m_colSource0Width;
-	statsTab.m_colStats0Width = m_colStats0Width;
-	statsTab.m_colStats1Width = m_colStats1Width;
-	statusTab.m_colStats0Width = m_colGStats0Width;
-	statusTab.m_colStats1Width = m_colGStats1Width;
-	statusTab.m_colStats2Width = m_colGStats2Width;
-	
-	statsTab.Create(IDD_STATSDIALOG, this);
-	statusTab.Create(IDD_SSTATUS, this);
-
-	int nPageID = 0;
-	m_MainTab.AddSSLPage (_T("Server Status"), nPageID, (CTabPageSSL *)&statusTab);
-	nPageID++;
-	m_MainTab.AddSSLPage (_T("Source Level Stats"), nPageID, (CTabPageSSL *)&statsTab);
-	nPageID++;
-
-	
-	labelFont.CreateFont(24,0, 0, 0, FW_BOLD, 0, 0, 0, 0, OUT_TT_PRECIS, 0, PROOF_QUALITY, 0, "Arial");
-	
-	runningBitmap.LoadBitmap(IDB_BITMAP6);
-	stoppedBitmap.LoadBitmap(IDB_BITMAP5);
-
-	UpdateData(FALSE);
-
-	LoadConfig();
-
-//	AddAnchor(IDC_MAINTAB, TOP_LEFT, BOTTOM_RIGHT);
-//	AddAnchor(IDC_STATICBLACK, TOP_LEFT, TOP_RIGHT);
-
-//	EnableSaveRestore("icecast2win", "positions");
-
-	m_pTray = NULL;
-
-	char	version[255] = "";
-	sprintf(version, "Icecast2 Version %s", ICECAST_VERSION);
-	SetWindowText(version);
-
-
-	if (m_Autostart) {
-		OnStart();
-	}
-	return TRUE;  // return TRUE  unless you set the focus to a control
-}
-
-void CIcecast2winDlg::OnSysCommand(UINT nID, LPARAM lParam)
-{
-	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
-	{
-		CAboutDlg dlgAbout;
-		dlgAbout.DoModal();
-	}
-	else
-	{
-		CDialog::OnSysCommand(nID, lParam);
-	}
-}
-
-// If you add a minimize button to your dialog, you will need the code below
-//  to draw the icon.  For MFC applications using the document/view model,
-//  this is automatically done for you by the framework.
-
-void CIcecast2winDlg::OnPaint() 
-{
-	if (IsIconic())
-	{
-		CPaintDC dc(this); // device context for painting
-
-		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
-
-		// Center icon in client rectangle
-		int cxIcon = GetSystemMetrics(SM_CXICON);
-		int cyIcon = GetSystemMetrics(SM_CYICON);
-		CRect rect;
-		GetClientRect(&rect);
-		int x = (rect.Width() - cxIcon + 1) / 2;
-		int y = (rect.Height() - cyIcon + 1) / 2;
-
-		// Draw the icon
-		dc.DrawIcon(x, y, m_hIcon);
-	}
-	else
-	{
-		CDialog::OnPaint();
-	}
-}
-
-
-// The system calls this to obtain the cursor to display while the user drags
-//  the minimized window.
-HCURSOR CIcecast2winDlg::OnQueryDragIcon()
-{
-	return (HCURSOR) m_hIcon;
-}
-
-void CIcecast2winDlg::OnSelchangeMaintab(NMHDR* pNMHDR, LRESULT* pResult) 
-{
-	// TODO: Add your control notification handler code here
-
-	*pResult = 0;
-}
-
-void CIcecast2winDlg::LoadConfig()
-{
-	FILE	*filep;
-	char	buffer[2046] = "";
-	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
-
-	configTab.m_Config = "";
-	filep = fopen(myApp->m_configFile, "r");
-	if (filep) {
-		fclose(filep);
-	}
-	else {
-		MessageBox("Unable to load config file (" + CString(myApp->m_configFile) + ") unable to start", NULL, MB_OK);
-	}
-
-}
-
-
-void CIcecast2winDlg::OnFileExit() 
-{
-	// TODO: Add your command handler code here
-	DestroyWindow();
-}
-
-
-
-void CIcecast2winDlg::getTag(char *pbuf, char *ptag, char *dest)
-{
-	char	openTag[256] = "";
-	char	closeTag[256] = "";
-
-	sprintf(openTag, "<%s>", ptag);
-	sprintf(closeTag, "</%s>", ptag);
-	
-	char *p1;
-	p1 = strstr(pbuf, openTag);
-	if (p1) {
-		p1 = p1 + strlen(openTag);
-		char *p2;
-		p2 = strstr(p1, closeTag);
-		if (p2) {
-			strncpy(dest, p1, p2-p1);
-		}
-	}
-}
-
-
-void CIcecast2winDlg::EnableControl(UINT control)
-{
-	CWnd	*pWnd;
-	pWnd = GetDlgItem(control);
-	::ShowWindow(pWnd->GetSafeHwnd(), SW_SHOW);
-}
-
-void CIcecast2winDlg::DisableControl(UINT control)
-{
-	CWnd	*pWnd;
-	pWnd = GetDlgItem(control);
-	::ShowWindow(pWnd->GetSafeHwnd(), SW_HIDE);
-}
-
-
-void AddUpdateStatistic(int sourceIndex, char *name, char *value)
-{
-	for (int j=0;j<gStats[sourceIndex].numStats;j++) {
-		if (gStats[sourceIndex].stats[j].name == name) {
-			gStats[sourceIndex].stats[j].value = value;
-			return;
-		}
-	}
-	int numStats = gStats[sourceIndex].numStats;
-	/* If we get here, we haven't found the stat, so add it */
-	gStats[sourceIndex].stats[numStats].name = name;
-	gStats[sourceIndex].stats[numStats].value = value;
-	gStats[sourceIndex].numStats++;
-
-}
-int GetSourceIndex(char *sourceName)
-{
-	if (sourceName == NULL) {
-		return 0;
-	}
-	for (int i=1;i<numMainStats+1;i++) {
-		if (!strcmp(gStats[i].source, sourceName)) {
-			return i;
-		}
-	}
-	/* This means we haven't seen the source, so lets add it */
-	numMainStats++;
-	gStats[numMainStats].source = sourceName;
-	gStats[numMainStats].populated = 1;
-	gStats[numMainStats].numStats = 0;
-	return numMainStats;
-
-}
-void UpdateSourceData(xmlDocPtr doc, xmlNodePtr cur, char *sourceName) {
-	xmlNodePtr children;
-	char	*ls_xmlContentPtr = NULL;
-	int sourceIndex = GetSourceIndex(sourceName);
-	int listenerInd = 0;
-
-	children = cur->xmlChildrenNode;
-	while (children != NULL) {
-		if (!strcmp((char *)children->name, "listeners")) {
-			listenerInd = 1;
-		}
-		ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, children->xmlChildrenNode, 1);
-		AddUpdateStatistic(sourceIndex, (char *)children->name, ls_xmlContentPtr);
-		xmlFree(ls_xmlContentPtr);
-		children = children->next;
-	}
-	if (!listenerInd) {
-		/* If no listeners, then the source has been disconnected */
-		gStats[sourceIndex].populated = 0;
-		gStats[sourceIndex].numStats = 0;
-		g_mainDialog->statsTab.m_SourceListCtrl.DeleteAllItems();
-		g_mainDialog->statsTab.m_StatsListCtrl.DeleteAllItems();
-	}
-}
-
-bool g_collectingStats = false;
-
-void StartStats(void *dummy)
-{
-	while (global.running != ICE_RUNNING) {
-		Sleep(500);
-	}
-	while (global.running == ICE_RUNNING) {
-		if (global.running == ICE_RUNNING) {
-			for (int j=0;j<MAXSOURCES;j++) {
-				gStats[j].numStats = 0;
-			}
-			numMainStats = 0;
-
-			xmlDocPtr doc;
-
-			stats_get_xml(&doc, 0);
-			xmlNodePtr cur;
-		    cur = xmlDocGetRootElement(doc); 
-
-		    if (cur == NULL) {
-				MessageBox(NULL, "empty XML document", "Error", MB_OK);
-				xmlFreeDoc(doc);
-				return;
-			}
-
-			cur = cur->xmlChildrenNode;
-			char* ls_xmlContentPtr2 = NULL;
-
-		    while (cur != NULL) {
-				if ((!xmlStrcmp(cur->name, (const xmlChar *)"source"))) {
-					ls_xmlContentPtr2 = (char *)xmlGetProp(cur, (unsigned char *)"mount");
-					UpdateSourceData(doc, cur, ls_xmlContentPtr2);
-				}
-				else {
-					/* A Global stat */
-					ls_xmlContentPtr2 = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-					AddUpdateStatistic(0, (char *)cur->name, ls_xmlContentPtr2);
-				}
-				if (ls_xmlContentPtr2) {
-					xmlFree(ls_xmlContentPtr2);
-				}
-
-				cur = cur->next;
-			}
-			xmlFreeDoc(doc);
-			xmlCleanupParser();
-			g_mainDialog->UpdateStatsLists();
-			Sleep(5000);
-		}
-		if (global.running != ICE_RUNNING) {
-			numMainStats = 0;
-			g_mainDialog->statusTab.m_GlobalStatList.DeleteAllItems();
-			g_mainDialog->statsTab.m_SourceListCtrl.DeleteAllItems();
-			g_mainDialog->statsTab.m_StatsListCtrl.DeleteAllItems();
-			_endthread();
-		}
-	}
-	_endthread();
-}
-void CIcecast2winDlg::OnTimer(UINT nIDEvent) 
-{
-	// TODO: Add your message handler code here and/or call default
-	if (nIDEvent == 0) {
-		if (global.running == ICE_RUNNING) {
-			char	buffer[255] = "";
-			CString	tmp;
-			// Get info from stats...
-			m_ServerStatusBitmap.SetBitmap(HBITMAP(runningBitmap));
-			sprintf(buffer, "%d", global.sources);
-			tmp = buffer;
-			if (tmp != statusTab.m_Sources) {
-				statusTab.m_Sources = tmp;
-				statusTab.UpdateData(FALSE);
-			}
-			sprintf(buffer, "%d", global.clients);
-			tmp = buffer;
-			if (tmp != statusTab.m_Clients) {
-				statusTab.m_Clients = tmp;
-				statusTab.UpdateData(FALSE);
-			}
-
-			m_StartButton.GetWindowText(tmp);
-
-			if (tmp == "Start Server") {
-				m_StartButton.SetWindowText("Stop Server");
-				m_StartButton.SetState(0);
-			}
-			time_t	currentTime;
-			time(&currentTime);
-			time_t  runningTime = currentTime - serverStart;
-
-			CTimeSpan runningFor(runningTime);
-
-			char	timespan[1024] = "";
-			sprintf(timespan, "%d Days, %d Hours, %d Minutes, %d Seconds", runningFor.GetDays(), runningFor.GetHours(), runningFor.GetMinutes(), runningFor.GetSeconds());
-			statusTab.m_RunningFor = timespan;
-			statusTab.UpdateData(FALSE);
-
-			SetTimer(0, 500, NULL);
-		}
-		else {
-			statusTab.m_Sources = "0";
-			statusTab.m_Clients = "0";
-			m_ServerStatusBitmap.SetBitmap(HBITMAP(stoppedBitmap));
-			m_StartButton.SetWindowText("Start Server");
-			m_StartButton.SetState(0);
-			UpdateData(FALSE);
-			statusTab.m_RunningFor = "Not running";
-			statusTab.UpdateData(FALSE);
-		}
-	}
-	
-	CDialog::OnTimer(nIDEvent);
-}
-
-char	g_configFile[1024] = "";
-char	g_progName[255] = "icecast2";
-
-void StartServer(void *configfile)
-{
-	int		argc = 3;
-	char*	argv[3];
-
-	strcpy(g_configFile, (char *)configfile);
-
-	argv[0] = g_progName;
-	argv[1] = "-c";
-	argv[2] = g_configFile;
-	time(&(g_mainDialog->serverStart));
-
-	int ret = main(argc, (char **)argv);
-	if (ret) {
-		MessageBox(NULL, "Unable to start server", NULL, MB_OK);
-	}
-	global.running = ICE_HALTING;
-	_endthread();
-
-
-}
-void CIcecast2winDlg::OnFileStartserver() 
-{
-	// TODO: Add your command handler code here
-	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
-
-	if (gConfigurationSave == "") {
-		gConfigurationSave = m_ConfigEdit;
-	}
-
-	if (global.running == ICE_RUNNING) {
-		MessageBox("Server already running", "Error", MB_OK);
-	}
-	else {
-		m_ConfigEditCtrl.SetReadOnly(TRUE);
-		LoadConfig();
-		SetTimer(0, 500, NULL);
-		_beginthread(StartServer, 0, (void *)(LPCSTR)myApp->m_configFile);
-		_beginthread(StartStats, 0, (void *)NULL);
-	}
-}
-
-void CIcecast2winDlg::OnFileStopserver() 
-{
-	// TODO: Add your command handler code here
-	;
-}
-
-bool infocus = false;
-
-void CIcecast2winDlg::StopServer()
-{
-	KillTimer(0);
-	global.running = ICE_HALTING;
-	m_StartButton.SetWindowText("Start Server");
-	m_StartButton.SetState(0);
-	m_ServerStatusBitmap.SetBitmap(HBITMAP(stoppedBitmap));
-	statusTab.m_RunningFor = "Not running";
-	statusTab.UpdateData(FALSE);
-
-
-
-}
-
-
-void CIcecast2winDlg::OnStart() 
-{
-	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
-
-	// TODO: Add your control notification handler code here
-
-	if (global.running == ICE_RUNNING) {
-		StopServer();
-	}
-	else {
-		SetTimer(0, 500, NULL);
-		_beginthread(StartServer, 0, (void *)(LPCSTR)myApp->m_configFile);
-		_beginthread(StartStats, 0, (void *)NULL);
-	}
-	
-}
-
-void CIcecast2winDlg::UpdateStatsLists()
-{
-	char	item[1024] = "";
-	int l = 0;
-
-	// Global Stats are index of 0
-	for (int k=0;k < gStats[0].numStats;k++) {
-		int inthere = 0;
-		for (l=0;l < statusTab.m_GlobalStatList.GetItemCount();l++) {
-
-			statusTab.m_GlobalStatList.GetItemText(l, 1, item, sizeof(item));
-			if (!strcmp(gStats[0].stats[k].name, item)) {
-				inthere = 1;
-				break;
-			}
-		}
-		if (!inthere) {
-			LVITEM	lvi;
-
-			lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
-			lvi.iItem = statsTab.m_SourceListCtrl.GetItemCount();
-			lvi.iSubItem = 0;
-			//lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].source;
-			lvi.pszText = "Global Stat";
-			statusTab.m_GlobalStatList.InsertItem(&lvi);
-			lvi.iSubItem = 1;
-			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].name;
-			statusTab.m_GlobalStatList.SetItem(&lvi);
-			lvi.iSubItem = 2;
-			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].value;
-			statusTab.m_GlobalStatList.SetItem(&lvi);
-			if ((!strcmp(gTitleSource, gStats[0].source)) && 
-				(!strcmp(gTitleName, gStats[0].stats[k].name))) {
-				gStats[0].stats[k].titleFlag = 1;
-			}
-
-		}
-		else {
-			LVITEM	lvi;
-
-			lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
-			lvi.iItem = l;
-			lvi.iSubItem = 2;
-			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].value;
-			statusTab.m_GlobalStatList.SetItem(&lvi);
-		}
-		if (gStats[0].stats[k].titleFlag) {
-			CString	windowTitle = CString("Global Stat") + " - " + gStats[0].stats[k].name + " - " + gStats[0].stats[k].value;
-			SetWindowText(windowTitle);
-			if (m_pTray) {
-				m_pTray->SetTIP((LPSTR)(LPCSTR)windowTitle);
-			}
-		}
-	}
-
-	for (int i=1;i<numMainStats+1;i++) {
-		int inthere = 0;
-		int k = 0;
-		if (gStats[i].populated) {
-			for (l=0;l < gAdditionalGlobalStats.numStats;l++) {
-				for (int m=0;m < gStats[i].numStats;m++) {
-					if ((gAdditionalGlobalStats.stats[l].source == gStats[i].source) &&
-						(gAdditionalGlobalStats.stats[l].name == gStats[i].stats[m].name)) {
-						gAdditionalGlobalStats.stats[l].value = gStats[i].stats[m].value;
-						break;
-					}
-				}
-			}
-			if (strcmp(gStats[i].source, "Global Stat")) {
-				// If Not Global STat
-				for (k=0;k < statsTab.m_SourceListCtrl.GetItemCount();k++) {
-
-					statsTab.m_SourceListCtrl.GetItemText(k, 0, item, sizeof(item));
-					if (!strcmp(gStats[i].source, item)) {
-						inthere = 1;
-						break;
-					}
-				}
-				if (!inthere) {
-					if (gStats[i].source != "") {
-						LVITEM	lvi;
-
-						lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
-						lvi.iItem = statsTab.m_SourceListCtrl.GetItemCount();
-						lvi.iSubItem = 0;
-						lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].source;
-						statsTab.m_SourceListCtrl.InsertItem(&lvi);
-					}
-				}
-				int nItemSelected = statsTab.m_SourceListCtrl.GetSelectionMark();
-				if (nItemSelected != -1) {
-					memset(item, '\000', sizeof(item));
-					statsTab.m_SourceListCtrl.GetItemText(nItemSelected, 0, item, sizeof(item));
-					if (!strcmp(gStats[i].source, item)) {
-						for (int l=0;l<gStats[i].numStats;l++) {
-							int inthere2 = 0;
-							char	item2[1024] = "";
-							for (int m=0;m < statsTab.m_StatsListCtrl.GetItemCount();m++) {
-								statsTab.m_StatsListCtrl.GetItemText(m, 0, item2, sizeof(item2));
-								if (!strcmp(gStats[i].stats[l].name, item2)) {
-									LVITEM	lvi;
-
-									lvi.mask =  LVIF_TEXT;
-									lvi.iItem = m;
-									lvi.iSubItem = 1;
-									lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].value;
-									statsTab.m_StatsListCtrl.SetItem(&lvi);
-									inthere2 = 1;
-									break;
-								}
-							}
-							if (!inthere2) {
-								LVITEM	lvi;
-
-								lvi.mask =  LVIF_TEXT;
-								lvi.iItem = statsTab.m_StatsListCtrl.GetItemCount();
-								lvi.iSubItem = 0;
-								lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].name;
-								statsTab.m_StatsListCtrl.InsertItem(&lvi);
-								lvi.iSubItem = 1;
-								lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].value;
-								statsTab.m_StatsListCtrl.SetItem(&lvi);
-							}
-						}
-					}
-				}
-				for (l=0;l < gAdditionalGlobalStats.numStats;l++) {
-					int inthere2 = 0;
-					char	item2[1024] = "";
-					char	item3[1024] = "";
-					CString	itemSource;
-					CString itemName;
-					for (int m=0;m < statusTab.m_GlobalStatList.GetItemCount();m++) {
-						statusTab.m_GlobalStatList.GetItemText(m, 0, item2, sizeof(item2));
-						statusTab.m_GlobalStatList.GetItemText(m, 1, item3, sizeof(item3));
-						itemSource = item2;
-						itemName = item3;
-						if ((gAdditionalGlobalStats.stats[l].source == itemSource) &&
-							(gAdditionalGlobalStats.stats[l].name == itemName)) {
-							LVITEM	lvi;
-
-							lvi.mask =  LVIF_TEXT;
-							lvi.iItem = m;
-							lvi.iSubItem = 2;
-							lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].value;
-							statusTab.m_GlobalStatList.SetItem(&lvi);
-							inthere2 = 1;
-							break;
-						}
-					}
-					if (!inthere2) {
-						LVITEM	lvi;
-
-
-						lvi.mask =  LVIF_TEXT;
-						lvi.iItem = statusTab.m_GlobalStatList.GetItemCount();
-						lvi.iSubItem = 0;
-						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].source;
-						statusTab.m_GlobalStatList.InsertItem(&lvi);
-						lvi.iSubItem = 1;
-						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].name;
-						statusTab.m_GlobalStatList.SetItem(&lvi);
-						lvi.iSubItem = 2;
-						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].value;
-						statusTab.m_GlobalStatList.SetItem(&lvi);
-						if ((!strcmp(gTitleSource, gAdditionalGlobalStats.stats[l].source)) && 
-							(!strcmp(gTitleName, gAdditionalGlobalStats.stats[l].name))) {
-							gAdditionalGlobalStats.stats[l].titleFlag = 1;
-						}
-							
-
-					}
-
-					if (gAdditionalGlobalStats.stats[l].titleFlag) {
-						CString	windowTitle = gAdditionalGlobalStats.stats[l].source + " - " + gAdditionalGlobalStats.stats[l].name + " - " + gAdditionalGlobalStats.stats[l].value;
-						SetWindowText(windowTitle);
-						if (m_pTray) {
-							m_pTray->SetTIP((LPSTR)(LPCSTR)windowTitle);
-						}
-					}
-				}
-			}
-			else {
-			}
-		}
-	}
-}
-
-char	gAppName[255] = "icecast2";
-char	gConfigFile[255] = "icecast2.ini";
-
-void CIcecast2winDlg::config_write()
-{
-	char	buf[255] = "";
-	char	buf2[1024] = "";
-
-	UpdateData(TRUE);
-
-	m_colSource0Width = statsTab.m_SourceListCtrl.GetColumnWidth(0);
-	m_colStats0Width = statsTab.m_StatsListCtrl.GetColumnWidth(0);
-	m_colStats1Width = statsTab.m_StatsListCtrl.GetColumnWidth(1);
-	m_colGStats0Width = statusTab.m_GlobalStatList.GetColumnWidth(0);
-	m_colGStats1Width = statusTab.m_GlobalStatList.GetColumnWidth(1);
-	m_colGStats2Width = statusTab.m_GlobalStatList.GetColumnWidth(2);
-
-
-	sprintf(buf, "%d", m_colSource0Width);
-	WritePrivateProfileString(gAppName, "col0SourceWidth", buf, gConfigFile);
-	sprintf(buf, "%d", m_colStats0Width);
-	WritePrivateProfileString(gAppName, "col0StatsWidth", buf, gConfigFile);
-	sprintf(buf, "%d", m_colStats1Width);
-	WritePrivateProfileString(gAppName, "col1StatsWidth", buf, gConfigFile);
-	sprintf(buf, "%d", m_colGStats0Width);
-	WritePrivateProfileString(gAppName, "col0GStatsWidth", buf, gConfigFile);
-	sprintf(buf, "%d", m_colGStats1Width);
-	WritePrivateProfileString(gAppName, "col1GStatsWidth", buf, gConfigFile);
-	sprintf(buf, "%d", m_colGStats2Width);
-	WritePrivateProfileString(gAppName, "col2GStatsWidth", buf, gConfigFile);
-
-	if (m_Autostart) {
-		WritePrivateProfileString(gAppName, "AutoStart", "1", gConfigFile);
-	}
-	else {
-		WritePrivateProfileString(gAppName, "AutoStart", "0", gConfigFile);
-	}
-
-	sprintf(buf, "%d", gAdditionalGlobalStats.numStats);
-	WritePrivateProfileString(gAppName, "numAdditionalStats", buf, gConfigFile);
-
-	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
-		memset(buf, '\000', sizeof(buf));
-		sprintf(buf2, "AdditionalStatsSource%d", i);
-		WritePrivateProfileString(gAppName, buf2, gAdditionalGlobalStats.stats[i].source, gConfigFile);
-
-		memset(buf, '\000', sizeof(buf));
-		sprintf(buf2, "AdditionalStatsName%d", i);
-		WritePrivateProfileString(gAppName, buf2, gAdditionalGlobalStats.stats[i].name, gConfigFile);
-
-		if (gAdditionalGlobalStats.stats[i].titleFlag) {
-			sprintf(buf2, "%s|%s", gAdditionalGlobalStats.stats[i].source, gAdditionalGlobalStats.stats[i].name);
-			WritePrivateProfileString(gAppName, "TitleName", buf2, gConfigFile);
-		}
-	}
-	for (i=0;i<numMainStats;i++) {
-		for (int k=0;k < gStats[i].numStats;k++) {
-			if (gStats[i].stats[k].titleFlag) {
-				sprintf(buf2, "%s|%s", gStats[i].source, gStats[i].stats[k].name);
-				WritePrivateProfileString(gAppName, "TitleName", buf2, gConfigFile);
-			}
-		}
-	}
-
-}
-
-void CIcecast2winDlg::config_read()
-{
-	char	buf2[1024] = "";
-	char	buf[1024] = "";
-	CString	tempString;
-
-	m_colSource0Width = GetPrivateProfileInt(gAppName, "col0SourceWidth", 163, gConfigFile);
-	m_colStats0Width = GetPrivateProfileInt(gAppName, "col0StatsWidth", 100, gConfigFile);
-	m_colStats1Width = GetPrivateProfileInt(gAppName, "col1StatsWidth", 150, gConfigFile);
-	m_colGStats0Width = GetPrivateProfileInt(gAppName, "col0GStatsWidth", 150, gConfigFile);
-	m_colGStats1Width = GetPrivateProfileInt(gAppName, "col1GStatsWidth", 150, gConfigFile);
-	m_colGStats2Width = GetPrivateProfileInt(gAppName, "col2GStatsWidth", 150, gConfigFile);
-
-	GetPrivateProfileString(gAppName, "AutoStart", "0", buf, sizeof(buf), gConfigFile);
-	if (!strcmp(buf, "1")) {
-		m_Autostart = true;
-	}
-	else{
-		m_Autostart = false;
-	}
-	int numAdditionalGlobalStats = GetPrivateProfileInt(gAppName, "numAdditionalStats", 0, gConfigFile);
-	for (int i=0;i<numAdditionalGlobalStats;i++) {
-		memset(buf, '\000', sizeof(buf));
-		sprintf(buf2, "AdditionalStatsSource%d", i);
-		GetPrivateProfileString(gAppName, buf2, "", buf, sizeof(buf), gConfigFile);
-		gAdditionalGlobalStats.stats[i].source = buf;
-
-		memset(buf, '\000', sizeof(buf));
-		sprintf(buf2, "AdditionalStatsName%d", i);
-		GetPrivateProfileString(gAppName, buf2, "", buf, sizeof(buf), gConfigFile);
-		gAdditionalGlobalStats.stats[i].name = buf;
-		gAdditionalGlobalStats.numStats++;
-	}
-	GetPrivateProfileString(gAppName, "TitleName", "", buf, sizeof(buf), gConfigFile);
-
-	if (strlen(buf) > 0) {
-		char *p1 = strchr(buf, '|');
-		if (p1) {
-			char tmpSource[1024] = "";
-			char tmpName[1024] = "";
-			memset(tmpSource, '\000', sizeof(tmpSource));
-			memset(tmpName, '\000', sizeof(tmpName));
-			
-
-			strncpy(tmpSource, buf, p1-buf);
-			p1++;
-			strcpy(tmpName, p1);
-
-			
-			strcpy(gTitleSource, tmpSource);
-			strcpy(gTitleName, tmpName);
-		}
-	}
-
-}
-
-void CIcecast2winDlg::OnClose() 
-{
-	// TODO: Add your message handler code here and/or call default
-	config_write();
-	CDialog::OnClose();
-}
-
-void CIcecast2winDlg::OnSize(UINT nType, int cx, int cy) 
-{
-	CDialog::OnSize(nType, cx, cy);
-	
-	int border1 = 0;
-	int border2 = 78;
-	// TODO: Add your message handler code here
-	if (m_MainTab.m_hWnd) {
-		CRect rect;
-		GetClientRect (&rect);
-		int x = rect.Width()-border1;
-		int y = rect.Width()-border2;
-
-		statusTab.SetWindowPos(NULL, 4, 22, cx, cy, SWP_NOZORDER);
-		statsTab.SetWindowPos(NULL, 4, 22, cx, cy, SWP_NOZORDER);
-		statusTab.m_GlobalStatList.SetWindowPos(NULL, 14, 55, cx-40, cy-180, SWP_NOZORDER);
-		statsTab.m_StatsListCtrl.SetWindowPos(NULL, 213, 55, cx-243, cy-180, SWP_NOZORDER);
-		statsTab.m_SourceListCtrl.SetWindowPos(NULL, 14, 55, 166, cy-180, SWP_NOZORDER);
-//			CListCtrl	m_StatsListCtrl;
-//	CListCtrl	m_SourceListCtrl;
-		m_MainTab.SetWindowPos(NULL, 0, 80, cx, cy, SWP_NOZORDER);
-
-		//m_MainTab.ResizeDialog(0, rect.Width()-border1, rect.Height()-border2);
-		//m_MainTab.ResizeDialog(1, rect.Width()-border1, rect.Height()-border2);
-	}
-
-}
-
-
-LONG CIcecast2winDlg::OnTrayNotify ( WPARAM wParam, LPARAM lParam )
-{
-	switch (lParam) {
-	case WM_RBUTTONDOWN:
-			{
-			CMenu menu ;
-			// Load and Verify Menu
-			VERIFY(menu.LoadMenu(IDR_TRAY));
-			CMenu* pPopup = menu.GetSubMenu (0) ;
-			ASSERT(pPopup != NULL);
-
-			// Get the cursor position
-			POINT pt ;
-			GetCursorPos (&pt) ;
-
-			// Fix Microsofts' BUG!!!!
-			SetForegroundWindow();
-
-			///////////////////////////////////
-			// Display The Menu
-			pPopup->TrackPopupMenu(TPM_LEFTALIGN |
-			TPM_RIGHTBUTTON,pt.x, pt.y, AfxGetMainWnd());
-			break ;
-			}
-	case WM_LBUTTONDBLCLK:
-	//////////////////////////////////
-	// Unhide our Window
-		if (m_bHidden) {
-			ShowWindow (SW_RESTORE);
-		}
-	//OnUnHide() ;
-		break ;
-	}
-
-	return (0) ;
-}
-
-void CIcecast2winDlg::OnHidesystray() 
-{
-	// TODO: Add your control notification handler code here
-	OnHide();
-	theApp.HideApplication();	
-}
-void CIcecast2winDlg::OnHide() 
-{
-	// TODO: Add your control notification handler code here
-	if (m_pTray == NULL) {
-		m_pTray = new CTrayNot (this,WM_TRAY_NOTIFY, NULL,theApp.m_pIconList);
-	}
-	m_pTray->SetState(0);
-	m_bHidden = TRUE;
-	
-}
-
-void CIcecast2winDlg::OnBlankRestore() 
-{
-	// TODO: Add your command handler code here
-		if (m_bHidden) {
-			ShowWindow (SW_RESTORE);
-		}
-	
-}
-
-void CIcecast2winDlg::OnDestroy() 
-{
-	CDialog::OnDestroy();
-	
-	if (m_pTray) {
-		delete m_pTray ;
-		m_pTray = NULL ;
-	} 
-	// TODO: Add your message handler code here
-	
-}
-
-void CIcecast2winDlg::OnFileEditconfiguration() 
-{
-	// TODO: Add your command handler code here
-	
-    STARTUPINFO si;
-    PROCESS_INFORMATION pi;
-
-    ZeroMemory( &si, sizeof(si) );
-    si.cb = sizeof(si);
-    ZeroMemory( &pi, sizeof(pi) );
-
-
-	int ok = 1;
-	if (global.running == ICE_RUNNING) {
-		MessageBox("I'm sorry, but you cannot edit the configuration file while the server is running", NULL, MB_OK);
-	}
-	else {
-		// Start the child process. 
-		if( !CreateProcess( NULL, // No module name (use command line). 
-			"notepad icecast.xml", // Command line. 
-			NULL,             // Process handle not inheritable. 
-			NULL,             // Thread handle not inheritable. 
-			FALSE,            // Set handle inheritance to FALSE. 
-			0,                // No creation flags. 
-			NULL,             // Use parent's environment block. 
-			NULL,             // Use parent's starting directory. 
-			&si,              // Pointer to STARTUPINFO structure.
-			&pi )             // Pointer to PROCESS_INFORMATION structure.
-		) 
-		{
-		   ok = 0;
-		}
-
-		// Wait until child process exits.
-		WaitForSingleObject( pi.hProcess, INFINITE );
-
-		// Close process and thread handles. 
-		CloseHandle( pi.hProcess );
-		CloseHandle( pi.hThread );
-	}
-
-}
-
-void CIcecast2winDlg::OnAboutHelp() 
-{
-	// TODO: Add your command handler code here
-	ShellExecute(NULL, "open", "doc\\icecast2.chm", NULL, NULL, SW_SHOWNORMAL);	
-}
-
-void CIcecast2winDlg::OnAboutCredits() 
-{
-	// TODO: Add your command handler code here
-	CAboutDlg	about;
-	about.DoModal();
-}

Copied: icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp (from rev 14465, icecast/trunk/icecast/win32/Icecast2winDlg.cpp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/Icecast2winDlg.cpp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,1207 @@
+// Icecast2winDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Icecast2win.h"
+#include "Icecast2winDlg.h"
+#include <process.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <stdlib.h>
+#include <curl/curl.h>
+
+extern "C" {
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "log/log.h"
+#include "global.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "xslt.h"
+}
+
+#include <afxinet.h>
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define ICECAST_VERSION "2.x"
+CEdit	*g_accessControl;
+CEdit	*g_errorControl;
+CIcecast2winDlg	*g_mainDialog;
+bool	g_tailAccess = false;
+bool	g_tailError = false;
+CString gConfigurationSave;
+
+char	gTitleSource[1024] = "";
+char	gTitleName[1024] = "";
+
+#define MAXSTATSPERSOURCE 60
+#define MAXSOURCES 1024
+
+typedef struct tagElement {
+	CString	name;
+	CString	value;
+	int		titleFlag;
+} Element;
+
+typedef struct tagElementAdditional {
+	CString source;
+	CString	name;
+	CString	value;
+	int		titleFlag;
+} ElementAdditional;
+
+
+typedef struct tagMainElement {
+	CString	source;
+	long	numStats;
+	Element	stats[MAXSTATSPERSOURCE];
+	int		populated;
+} MainElement;
+
+typedef struct tagMainElementAdditional {
+	long	numStats;
+	ElementAdditional	stats[MAXSTATSPERSOURCE];
+} MainElementAdditional;
+
+
+MainElement gStats[MAXSOURCES];
+MainElement gGlobalStats;
+MainElementAdditional	gAdditionalGlobalStats;
+
+long	numMainStats;
+
+extern "C" {
+	int main(int argc, char **argv);
+}
+
+
+void AddToAdditionalGlobalStats(CString source, CString name) {
+	int foundit = 0;
+	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
+		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
+			foundit = 1;
+			break;
+		}
+	}
+	if (!foundit) {
+		gAdditionalGlobalStats.stats[gAdditionalGlobalStats.numStats].source = source;
+		gAdditionalGlobalStats.stats[gAdditionalGlobalStats.numStats].name = name;
+		gAdditionalGlobalStats.numStats++;
+	}
+	g_mainDialog->UpdateStatsLists();
+}
+
+void ClearTitleAdditionalGlobalStats(CString source, CString name) {
+	int foundit = 0;
+	int i,j;
+	for (i=0;i<gAdditionalGlobalStats.numStats;i++) {
+		gAdditionalGlobalStats.stats[i].titleFlag = 0;
+	}
+	for (i=0;i<numMainStats;i++) {
+		for (j=0;j<gStats[i].numStats;j++) {
+			if ((gStats[i].source == source) && (gStats[i].stats[j].name == name)) {
+				gStats[i].stats[j].titleFlag = 0;
+			}
+		}
+	}
+	g_mainDialog->UpdateStatsLists();
+}
+void AddToTitleAdditionalGlobalStats(CString source, CString name) {
+	int foundit = 0;
+	int i,j;
+	for (i=0;i<gAdditionalGlobalStats.numStats;i++) {
+		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
+			ClearTitleAdditionalGlobalStats(source, name);
+			gAdditionalGlobalStats.stats[i].titleFlag = 1;
+			strcpy(gTitleSource, source);
+			strcpy(gTitleName, name);
+			foundit = 1;
+			break;
+		}
+	}
+	if (!foundit) {
+		for (i=0;i<numMainStats;i++) {
+			for (j=0;j<gStats[i].numStats;j++) {
+				if ((gStats[i].source == source) && (gStats[i].stats[j].name == name)) {
+					ClearTitleAdditionalGlobalStats(source, name);
+					gStats[i].stats[j].titleFlag = 1;
+					strcpy(gTitleSource, source);
+					strcpy(gTitleName, name);
+					foundit = 1;
+					break;
+				}
+			}
+		}
+	}
+	g_mainDialog->UpdateStatsLists();
+}
+
+void RemoveFromAdditionalGlobalStats(CString source, CString name) {
+	int foundit = 0;
+	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
+		if ((gAdditionalGlobalStats.stats[i].source == source) && (gAdditionalGlobalStats.stats[i].name == name)) {
+			for (int j=i+1;j < gAdditionalGlobalStats.numStats;j++) {
+				gAdditionalGlobalStats.stats[j-1].name = gAdditionalGlobalStats.stats[j].name;
+				gAdditionalGlobalStats.stats[j-1].value = gAdditionalGlobalStats.stats[j].value;
+				gAdditionalGlobalStats.stats[j-1].source = gAdditionalGlobalStats.stats[j].source;
+			}
+			gAdditionalGlobalStats.numStats--;
+			break;
+		}
+	}
+	g_mainDialog->UpdateStatsLists();
+}
+/////////////////////////////////////////////////////////////////////////////
+// CAboutDlg dialog used for App About
+
+class CAboutDlg : public CDialog
+{
+public:
+	CAboutDlg();
+
+// Dialog Data
+	//{{AFX_DATA(CAboutDlg)
+	enum { IDD = IDD_ABOUTBOX };
+	//}}AFX_DATA
+
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CAboutDlg)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+	//{{AFX_MSG(CAboutDlg)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
+{
+	//{{AFX_DATA_INIT(CAboutDlg)
+	//}}AFX_DATA_INIT
+}
+
+void CAboutDlg::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CAboutDlg)
+	//}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
+	//{{AFX_MSG_MAP(CAboutDlg)
+		// No message handlers
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winDlg dialog
+
+CIcecast2winDlg::CIcecast2winDlg(CWnd* pParent /*=NULL*/)
+	: CDialog(CIcecast2winDlg::IDD, pParent)
+{
+	//{{AFX_DATA_INIT(CIcecast2winDlg)
+	m_AccessEdit = _T("");
+	m_ErrorEdit = _T("");
+	m_ConfigEdit = _T("");
+	m_ServerStatus = _T("");
+	m_SourcesConnected = _T("");
+	m_NumClients = _T("");
+	m_StatsEdit = _T("");
+	m_Autostart = FALSE;
+	//}}AFX_DATA_INIT
+	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
+	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+	m_pTray = NULL;
+}
+
+void CIcecast2winDlg::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CIcecast2winDlg)
+	DDX_Control(pDX, IDC_STATIC_SS, m_SS);
+	DDX_Control(pDX, IDC_SERVERSTATUS, m_ServerStatusBitmap);
+	DDX_Control(pDX, IDC_START, m_StartButton);
+	DDX_Control(pDX, IDC_MAINTAB, m_MainTab);
+	DDX_Check(pDX, IDC_AUTOSTART, m_Autostart);
+	//}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CIcecast2winDlg, CDialog)
+	//{{AFX_MSG_MAP(CIcecast2winDlg)
+	ON_WM_SYSCOMMAND()
+	ON_WM_PAINT()
+	ON_WM_QUERYDRAGICON()
+	ON_NOTIFY(TCN_SELCHANGE, IDC_MAINTAB, OnSelchangeMaintab)
+	ON_COMMAND(ID_FILE_EXIT, OnFileExit)
+	ON_WM_TIMER()
+	ON_COMMAND(ID_FILE_STARTSERVER, OnFileStartserver)
+	ON_COMMAND(ID_FILE_STOPSERVER, OnFileStopserver)
+	ON_BN_CLICKED(IDC_START, OnStart)
+	ON_WM_CLOSE()
+	ON_WM_SIZE()
+	ON_BN_CLICKED(IDC_HIDESYSTRAY, OnHidesystray)
+	ON_COMMAND(ID_BLANK_RESTORE, OnBlankRestore)
+	ON_MESSAGE(WM_TRAY_NOTIFY, OnTrayNotify)
+	ON_WM_DESTROY()
+	ON_COMMAND(ID_FILE_EDITCONFIGURATION, OnFileEditconfiguration)
+	ON_COMMAND(ID_ABOUT_HELP, OnAboutHelp)
+	ON_COMMAND(ID_ABOUT_CREDITS, OnAboutCredits)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winDlg message handlers
+
+#include "colors.h"
+
+
+BOOL CIcecast2winDlg::OnInitDialog()
+{
+	CDialog::OnInitDialog();
+
+	// Add "About..." menu item to system menu.
+
+	// IDM_ABOUTBOX must be in the system command range.
+	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
+	ASSERT(IDM_ABOUTBOX < 0xF000);
+
+	CMenu* pSysMenu = GetSystemMenu(FALSE);
+	if (pSysMenu != NULL)
+	{
+		CString strAboutMenu;
+		strAboutMenu.LoadString(IDS_ABOUTBOX);
+		if (!strAboutMenu.IsEmpty())
+		{
+			pSysMenu->AppendMenu(MF_SEPARATOR);
+			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
+		}
+	}
+
+	g_mainDialog = this;
+
+	// Set the icon for this dialog.  The framework does this automatically
+	//  when the application's main window is not a dialog
+	SetIcon(m_hIcon, TRUE);			// Set big icon
+	SetIcon(m_hIcon, FALSE);		// Set small icon
+	
+	// TODO: Add extra initialization here
+	config_read();
+
+	statsTab.m_colSource0Width = m_colSource0Width;
+	statsTab.m_colStats0Width = m_colStats0Width;
+	statsTab.m_colStats1Width = m_colStats1Width;
+	statusTab.m_colStats0Width = m_colGStats0Width;
+	statusTab.m_colStats1Width = m_colGStats1Width;
+	statusTab.m_colStats2Width = m_colGStats2Width;
+	
+	statsTab.Create(IDD_STATSDIALOG, this);
+	statusTab.Create(IDD_SSTATUS, this);
+
+	int nPageID = 0;
+	m_MainTab.AddSSLPage (_T("Server Status"), nPageID, (CTabPageSSL *)&statusTab);
+	nPageID++;
+	m_MainTab.AddSSLPage (_T("Source Level Stats"), nPageID, (CTabPageSSL *)&statsTab);
+	nPageID++;
+
+	
+	labelFont.CreateFont(24,0, 0, 0, FW_BOLD, 0, 0, 0, 0, OUT_TT_PRECIS, 0, PROOF_QUALITY, 0, "Arial");
+	
+	runningBitmap.LoadBitmap(IDB_BITMAP6);
+	stoppedBitmap.LoadBitmap(IDB_BITMAP5);
+
+	UpdateData(FALSE);
+
+	LoadConfig();
+
+//	AddAnchor(IDC_MAINTAB, TOP_LEFT, BOTTOM_RIGHT);
+//	AddAnchor(IDC_STATICBLACK, TOP_LEFT, TOP_RIGHT);
+
+//	EnableSaveRestore("icecast2win", "positions");
+
+	m_pTray = NULL;
+
+	char	version[255] = "";
+	sprintf(version, "Icecast2 Version %s", ICECAST_VERSION);
+	SetWindowText(version);
+
+    xslt_initialize();
+    curl_global_init (CURL_GLOBAL_ALL);
+
+	if (m_Autostart) {
+		OnStart();
+	}
+	return TRUE;  // return TRUE  unless you set the focus to a control
+}
+
+void CIcecast2winDlg::OnSysCommand(UINT nID, LPARAM lParam)
+{
+	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
+	{
+		CAboutDlg dlgAbout;
+		dlgAbout.DoModal();
+	}
+	else
+	{
+		CDialog::OnSysCommand(nID, lParam);
+	}
+}
+
+// If you add a minimize button to your dialog, you will need the code below
+//  to draw the icon.  For MFC applications using the document/view model,
+//  this is automatically done for you by the framework.
+
+void CIcecast2winDlg::OnPaint() 
+{
+	if (IsIconic())
+	{
+		CPaintDC dc(this); // device context for painting
+
+		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
+
+		// Center icon in client rectangle
+		int cxIcon = GetSystemMetrics(SM_CXICON);
+		int cyIcon = GetSystemMetrics(SM_CYICON);
+		CRect rect;
+		GetClientRect(&rect);
+		int x = (rect.Width() - cxIcon + 1) / 2;
+		int y = (rect.Height() - cyIcon + 1) / 2;
+
+		// Draw the icon
+		dc.DrawIcon(x, y, m_hIcon);
+	}
+	else
+	{
+		CDialog::OnPaint();
+	}
+}
+
+
+// The system calls this to obtain the cursor to display while the user drags
+//  the minimized window.
+HCURSOR CIcecast2winDlg::OnQueryDragIcon()
+{
+	return (HCURSOR) m_hIcon;
+}
+
+void CIcecast2winDlg::OnSelchangeMaintab(NMHDR* pNMHDR, LRESULT* pResult) 
+{
+	// TODO: Add your control notification handler code here
+
+	*pResult = 0;
+}
+
+void CIcecast2winDlg::LoadConfig()
+{
+	FILE	*filep;
+	char	buffer[2046] = "";
+	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
+
+	configTab.m_Config = "";
+	filep = fopen(myApp->m_configFile, "r");
+	if (filep) {
+		fclose(filep);
+	}
+	else {
+		MessageBox("Unable to load config file (" + CString(myApp->m_configFile) + ") unable to start", NULL, MB_OK);
+	}
+
+}
+
+
+void CIcecast2winDlg::OnFileExit() 
+{
+	// TODO: Add your command handler code here
+	DestroyWindow();
+}
+
+
+
+void CIcecast2winDlg::getTag(char *pbuf, char *ptag, char *dest)
+{
+	char	openTag[256] = "";
+	char	closeTag[256] = "";
+
+	sprintf(openTag, "<%s>", ptag);
+	sprintf(closeTag, "</%s>", ptag);
+	
+	char *p1;
+	p1 = strstr(pbuf, openTag);
+	if (p1) {
+		p1 = p1 + strlen(openTag);
+		char *p2;
+		p2 = strstr(p1, closeTag);
+		if (p2) {
+			strncpy(dest, p1, p2-p1);
+		}
+	}
+}
+
+
+void CIcecast2winDlg::EnableControl(UINT control)
+{
+	CWnd	*pWnd;
+	pWnd = GetDlgItem(control);
+	::ShowWindow(pWnd->GetSafeHwnd(), SW_SHOW);
+}
+
+void CIcecast2winDlg::DisableControl(UINT control)
+{
+	CWnd	*pWnd;
+	pWnd = GetDlgItem(control);
+	::ShowWindow(pWnd->GetSafeHwnd(), SW_HIDE);
+}
+
+
+void AddUpdateStatistic(int sourceIndex, char *name, char *value)
+{
+	for (int j=0;j<gStats[sourceIndex].numStats;j++) {
+		if (gStats[sourceIndex].stats[j].name == name) {
+			gStats[sourceIndex].stats[j].value = value;
+			return;
+		}
+	}
+	int numStats = gStats[sourceIndex].numStats;
+    if (numStats >= MAXSTATSPERSOURCE)
+        return;
+	/* If we get here, we haven't found the stat, so add it */
+	gStats[sourceIndex].stats[numStats].name = name;
+	gStats[sourceIndex].stats[numStats].value = value;
+	gStats[sourceIndex].numStats++;
+
+}
+int GetSourceIndex(char *sourceName)
+{
+	if (sourceName == NULL) {
+		return 0;
+	}
+	for (int i=1;i<numMainStats+1;i++) {
+		if (!strcmp(gStats[i].source, sourceName)) {
+			return i;
+		}
+	}
+    if (numMainStats >= MAXSOURCES)
+        return 0;
+	/* This means we haven't seen the source, so lets add it */
+	numMainStats++;
+	gStats[numMainStats].source = sourceName;
+	gStats[numMainStats].populated = 1;
+	gStats[numMainStats].numStats = 0;
+	return numMainStats;
+
+}
+void UpdateSourceData(xmlDocPtr doc, xmlNodePtr cur, char *sourceName) {
+	xmlNodePtr children;
+	char	*ls_xmlContentPtr = NULL;
+	int sourceIndex = GetSourceIndex(sourceName);
+	int listenerInd = 0;
+
+	children = cur->xmlChildrenNode;
+	while (children != NULL) {
+		if (!strcmp((char *)children->name, "listeners")) {
+			listenerInd = 1;
+		}
+		ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, children->xmlChildrenNode, 1);
+		AddUpdateStatistic(sourceIndex, (char *)children->name, ls_xmlContentPtr);
+		xmlFree(ls_xmlContentPtr);
+		children = children->next;
+	}
+	if (!listenerInd) {
+		/* If no listeners, then the source has been disconnected */
+		gStats[sourceIndex].populated = 0;
+		gStats[sourceIndex].numStats = 0;
+		g_mainDialog->statsTab.m_SourceListCtrl.DeleteAllItems();
+		g_mainDialog->statsTab.m_StatsListCtrl.DeleteAllItems();
+	}
+}
+
+bool g_collectingStats = false;
+
+void StartStats(void *dummy)
+{
+	while (global.running != ICE_RUNNING) {
+		Sleep(500);
+	}
+	while (global.running == ICE_RUNNING) {
+		if (global.running == ICE_RUNNING) {
+			for (int j=0;j<MAXSOURCES;j++) {
+				gStats[j].numStats = 0;
+			}
+			numMainStats = 0;
+
+			xmlDocPtr doc;
+
+			stats_get_xml(&doc, 0, NULL);
+			xmlNodePtr cur;
+		    cur = xmlDocGetRootElement(doc); 
+
+		    if (cur == NULL) {
+				MessageBox(NULL, "empty XML document", "Error", MB_OK);
+				xmlFreeDoc(doc);
+				return;
+			}
+
+			cur = cur->xmlChildrenNode;
+			char* ls_xmlContentPtr2 = NULL;
+
+		    while (cur != NULL) {
+				if ((!xmlStrcmp(cur->name, (const xmlChar *)"source"))) {
+					ls_xmlContentPtr2 = (char *)xmlGetProp(cur, (unsigned char *)"mount");
+					UpdateSourceData(doc, cur, ls_xmlContentPtr2);
+				}
+				else {
+					/* A Global stat */
+					ls_xmlContentPtr2 = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+					AddUpdateStatistic(0, (char *)cur->name, ls_xmlContentPtr2);
+				}
+				if (ls_xmlContentPtr2) {
+					xmlFree(ls_xmlContentPtr2);
+				}
+
+				cur = cur->next;
+			}
+			xmlFreeDoc(doc);
+			g_mainDialog->UpdateStatsLists();
+			Sleep(5000);
+		}
+		if (global.running != ICE_RUNNING) {
+			numMainStats = 0;
+			g_mainDialog->statusTab.m_GlobalStatList.DeleteAllItems();
+			g_mainDialog->statsTab.m_SourceListCtrl.DeleteAllItems();
+			g_mainDialog->statsTab.m_StatsListCtrl.DeleteAllItems();
+			_endthread();
+		}
+	}
+	_endthread();
+}
+void CIcecast2winDlg::OnTimer(UINT nIDEvent) 
+{
+	// TODO: Add your message handler code here and/or call default
+	if (nIDEvent == 0) {
+		if (global.running == ICE_RUNNING) {
+			char	buffer[255] = "";
+			CString	tmp;
+			// Get info from stats...
+			m_ServerStatusBitmap.SetBitmap(HBITMAP(runningBitmap));
+			sprintf(buffer, "%d", global.sources);
+			tmp = buffer;
+			if (tmp != statusTab.m_Sources) {
+				statusTab.m_Sources = tmp;
+				statusTab.UpdateData(FALSE);
+			}
+			sprintf(buffer, "%d", global.clients);
+			tmp = buffer;
+			if (tmp != statusTab.m_Clients) {
+				statusTab.m_Clients = tmp;
+				statusTab.UpdateData(FALSE);
+			}
+
+			m_StartButton.GetWindowText(tmp);
+
+			if (tmp == "Start Server") {
+				m_StartButton.SetWindowText("Stop Server");
+				m_StartButton.SetState(0);
+			}
+			time_t	currentTime;
+			time(&currentTime);
+			time_t  runningTime = currentTime - serverStart;
+
+			CTimeSpan runningFor(runningTime);
+
+			char	timespan[1024] = "";
+			sprintf(timespan, "%d Days, %d Hours, %d Minutes, %d Seconds", runningFor.GetDays(), runningFor.GetHours(), runningFor.GetMinutes(), runningFor.GetSeconds());
+			statusTab.m_RunningFor = timespan;
+			statusTab.UpdateData(FALSE);
+
+			SetTimer(0, 500, NULL);
+		}
+		else {
+			statusTab.m_Sources = "0";
+			statusTab.m_Clients = "0";
+			m_ServerStatusBitmap.SetBitmap(HBITMAP(stoppedBitmap));
+			m_StartButton.SetWindowText("Start Server");
+			m_StartButton.SetState(0);
+			UpdateData(FALSE);
+			statusTab.m_RunningFor = "Not running";
+			statusTab.UpdateData(FALSE);
+		}
+	}
+	
+	CDialog::OnTimer(nIDEvent);
+}
+
+char	g_configFile[1024] = "";
+char	g_progName[255] = "icecast2";
+
+void StartServer(void *configfile)
+{
+	int		argc = 3;
+	char*	argv[3];
+
+	strcpy(g_configFile, (char *)configfile);
+
+	argv[0] = g_progName;
+	argv[1] = "-c";
+	argv[2] = g_configFile;
+	time(&(g_mainDialog->serverStart));
+
+	int ret = main(argc, (char **)argv);
+	if (ret) {
+		MessageBox(NULL, "Unable to start server", NULL, MB_OK);
+	}
+	global.running = ICE_HALTING;
+	_endthread();
+
+
+}
+void CIcecast2winDlg::OnFileStartserver() 
+{
+	// TODO: Add your command handler code here
+	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
+
+	if (gConfigurationSave == "") {
+		gConfigurationSave = m_ConfigEdit;
+	}
+
+	if (global.running == ICE_RUNNING) {
+		MessageBox("Server already running", "Error", MB_OK);
+	}
+	else {
+		m_ConfigEditCtrl.SetReadOnly(TRUE);
+		LoadConfig();
+		SetTimer(0, 500, NULL);
+		_beginthread(StartServer, 0, (void *)(LPCSTR)myApp->m_configFile);
+		_beginthread(StartStats, 0, (void *)NULL);
+	}
+}
+
+void CIcecast2winDlg::OnFileStopserver() 
+{
+	// TODO: Add your command handler code here
+	;
+}
+
+bool infocus = false;
+
+void CIcecast2winDlg::StopServer()
+{
+	KillTimer(0);
+	global.running = ICE_HALTING;
+	m_StartButton.SetWindowText("Start Server");
+	m_StartButton.SetState(0);
+	m_ServerStatusBitmap.SetBitmap(HBITMAP(stoppedBitmap));
+	statusTab.m_RunningFor = "Not running";
+	statusTab.UpdateData(FALSE);
+
+
+
+}
+
+
+void CIcecast2winDlg::OnStart() 
+{
+	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
+
+	// TODO: Add your control notification handler code here
+
+	if (global.running == ICE_RUNNING) {
+		StopServer();
+	}
+	else {
+		SetTimer(0, 500, NULL);
+		_beginthread(StartServer, 0, (void *)(LPCSTR)myApp->m_configFile);
+		_beginthread(StartStats, 0, (void *)NULL);
+	}
+	
+}
+
+void CIcecast2winDlg::UpdateStatsLists()
+{
+	char	item[1024] = "";
+	int l = 0;
+
+	// Global Stats are index of 0
+	for (int k=0;k < gStats[0].numStats;k++) {
+		int inthere = 0;
+		for (l=0;l < statusTab.m_GlobalStatList.GetItemCount();l++) {
+
+			statusTab.m_GlobalStatList.GetItemText(l, 1, item, sizeof(item));
+			if (!strcmp(gStats[0].stats[k].name, item)) {
+				inthere = 1;
+				break;
+			}
+		}
+		if (!inthere) {
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
+			lvi.iItem = statsTab.m_SourceListCtrl.GetItemCount();
+			lvi.iSubItem = 0;
+			//lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].source;
+			lvi.pszText = "Global Stat";
+			statusTab.m_GlobalStatList.InsertItem(&lvi);
+			lvi.iSubItem = 1;
+			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].name;
+			statusTab.m_GlobalStatList.SetItem(&lvi);
+			lvi.iSubItem = 2;
+			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].value;
+			statusTab.m_GlobalStatList.SetItem(&lvi);
+			if ((!strcmp(gTitleSource, gStats[0].source)) && 
+				(!strcmp(gTitleName, gStats[0].stats[k].name))) {
+				gStats[0].stats[k].titleFlag = 1;
+			}
+
+		}
+		else {
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
+			lvi.iItem = l;
+			lvi.iSubItem = 2;
+			lvi.pszText = (LPTSTR)(LPCTSTR)gStats[0].stats[k].value;
+			statusTab.m_GlobalStatList.SetItem(&lvi);
+		}
+		if (gStats[0].stats[k].titleFlag) {
+			CString	windowTitle = CString("Global Stat") + " - " + gStats[0].stats[k].name + " - " + gStats[0].stats[k].value;
+			SetWindowText(windowTitle);
+			if (m_pTray) {
+				m_pTray->SetTIP((LPSTR)(LPCSTR)windowTitle);
+			}
+		}
+	}
+
+	for (int i=1;i<numMainStats+1;i++) {
+		int inthere = 0;
+		int k = 0;
+		if (gStats[i].populated) {
+			for (l=0;l < gAdditionalGlobalStats.numStats;l++) {
+				for (int m=0;m < gStats[i].numStats;m++) {
+					if ((gAdditionalGlobalStats.stats[l].source == gStats[i].source) &&
+						(gAdditionalGlobalStats.stats[l].name == gStats[i].stats[m].name)) {
+						gAdditionalGlobalStats.stats[l].value = gStats[i].stats[m].value;
+						break;
+					}
+				}
+			}
+			if (strcmp(gStats[i].source, "Global Stat")) {
+				// If Not Global STat
+				for (k=0;k < statsTab.m_SourceListCtrl.GetItemCount();k++) {
+
+					statsTab.m_SourceListCtrl.GetItemText(k, 0, item, sizeof(item));
+					if (!strcmp(gStats[i].source, item)) {
+						inthere = 1;
+						break;
+					}
+				}
+				if (!inthere) {
+					if (gStats[i].source != "") {
+						LVITEM	lvi;
+
+						lvi.mask =  LVIF_IMAGE | LVIF_TEXT;
+						lvi.iItem = statsTab.m_SourceListCtrl.GetItemCount();
+						lvi.iSubItem = 0;
+						lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].source;
+						statsTab.m_SourceListCtrl.InsertItem(&lvi);
+					}
+				}
+				int nItemSelected = statsTab.m_SourceListCtrl.GetSelectionMark();
+				if (nItemSelected != -1) {
+					memset(item, '\000', sizeof(item));
+					statsTab.m_SourceListCtrl.GetItemText(nItemSelected, 0, item, sizeof(item));
+					if (!strcmp(gStats[i].source, item)) {
+						for (int l=0;l<gStats[i].numStats;l++) {
+							int inthere2 = 0;
+							char	item2[1024] = "";
+							for (int m=0;m < statsTab.m_StatsListCtrl.GetItemCount();m++) {
+								statsTab.m_StatsListCtrl.GetItemText(m, 0, item2, sizeof(item2));
+								if (!strcmp(gStats[i].stats[l].name, item2)) {
+									LVITEM	lvi;
+
+									lvi.mask =  LVIF_TEXT;
+									lvi.iItem = m;
+									lvi.iSubItem = 1;
+									lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].value;
+									statsTab.m_StatsListCtrl.SetItem(&lvi);
+									inthere2 = 1;
+									break;
+								}
+							}
+							if (!inthere2) {
+								LVITEM	lvi;
+
+								lvi.mask =  LVIF_TEXT;
+								lvi.iItem = statsTab.m_StatsListCtrl.GetItemCount();
+								lvi.iSubItem = 0;
+								lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].name;
+								statsTab.m_StatsListCtrl.InsertItem(&lvi);
+								lvi.iSubItem = 1;
+								lvi.pszText = (LPTSTR)(LPCTSTR)gStats[i].stats[l].value;
+								statsTab.m_StatsListCtrl.SetItem(&lvi);
+							}
+						}
+					}
+				}
+				for (l=0;l < gAdditionalGlobalStats.numStats;l++) {
+					int inthere2 = 0;
+					char	item2[1024] = "";
+					char	item3[1024] = "";
+					CString	itemSource;
+					CString itemName;
+					for (int m=0;m < statusTab.m_GlobalStatList.GetItemCount();m++) {
+						statusTab.m_GlobalStatList.GetItemText(m, 0, item2, sizeof(item2));
+						statusTab.m_GlobalStatList.GetItemText(m, 1, item3, sizeof(item3));
+						itemSource = item2;
+						itemName = item3;
+						if ((gAdditionalGlobalStats.stats[l].source == itemSource) &&
+							(gAdditionalGlobalStats.stats[l].name == itemName)) {
+							LVITEM	lvi;
+
+							lvi.mask =  LVIF_TEXT;
+							lvi.iItem = m;
+							lvi.iSubItem = 2;
+							lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].value;
+							statusTab.m_GlobalStatList.SetItem(&lvi);
+							inthere2 = 1;
+							break;
+						}
+					}
+					if (!inthere2) {
+						LVITEM	lvi;
+
+
+						lvi.mask =  LVIF_TEXT;
+						lvi.iItem = statusTab.m_GlobalStatList.GetItemCount();
+						lvi.iSubItem = 0;
+						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].source;
+						statusTab.m_GlobalStatList.InsertItem(&lvi);
+						lvi.iSubItem = 1;
+						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].name;
+						statusTab.m_GlobalStatList.SetItem(&lvi);
+						lvi.iSubItem = 2;
+						lvi.pszText = (LPTSTR)(LPCTSTR)gAdditionalGlobalStats.stats[l].value;
+						statusTab.m_GlobalStatList.SetItem(&lvi);
+						if ((!strcmp(gTitleSource, gAdditionalGlobalStats.stats[l].source)) && 
+							(!strcmp(gTitleName, gAdditionalGlobalStats.stats[l].name))) {
+							gAdditionalGlobalStats.stats[l].titleFlag = 1;
+						}
+							
+
+					}
+
+					if (gAdditionalGlobalStats.stats[l].titleFlag) {
+						CString	windowTitle = gAdditionalGlobalStats.stats[l].source + " - " + gAdditionalGlobalStats.stats[l].name + " - " + gAdditionalGlobalStats.stats[l].value;
+						SetWindowText(windowTitle);
+						if (m_pTray) {
+							m_pTray->SetTIP((LPSTR)(LPCSTR)windowTitle);
+						}
+					}
+				}
+			}
+			else {
+			}
+		}
+	}
+}
+
+char	gAppName[255] = "icecast2";
+char	gConfigFile[255] = "icecast2.ini";
+
+void CIcecast2winDlg::config_write()
+{
+	char	buf[255] = "";
+	char	buf2[1024] = "";
+
+	UpdateData(TRUE);
+
+	m_colSource0Width = statsTab.m_SourceListCtrl.GetColumnWidth(0);
+	m_colStats0Width = statsTab.m_StatsListCtrl.GetColumnWidth(0);
+	m_colStats1Width = statsTab.m_StatsListCtrl.GetColumnWidth(1);
+	m_colGStats0Width = statusTab.m_GlobalStatList.GetColumnWidth(0);
+	m_colGStats1Width = statusTab.m_GlobalStatList.GetColumnWidth(1);
+	m_colGStats2Width = statusTab.m_GlobalStatList.GetColumnWidth(2);
+
+
+	sprintf(buf, "%d", m_colSource0Width);
+	WritePrivateProfileString(gAppName, "col0SourceWidth", buf, gConfigFile);
+	sprintf(buf, "%d", m_colStats0Width);
+	WritePrivateProfileString(gAppName, "col0StatsWidth", buf, gConfigFile);
+	sprintf(buf, "%d", m_colStats1Width);
+	WritePrivateProfileString(gAppName, "col1StatsWidth", buf, gConfigFile);
+	sprintf(buf, "%d", m_colGStats0Width);
+	WritePrivateProfileString(gAppName, "col0GStatsWidth", buf, gConfigFile);
+	sprintf(buf, "%d", m_colGStats1Width);
+	WritePrivateProfileString(gAppName, "col1GStatsWidth", buf, gConfigFile);
+	sprintf(buf, "%d", m_colGStats2Width);
+	WritePrivateProfileString(gAppName, "col2GStatsWidth", buf, gConfigFile);
+
+	if (m_Autostart) {
+		WritePrivateProfileString(gAppName, "AutoStart", "1", gConfigFile);
+	}
+	else {
+		WritePrivateProfileString(gAppName, "AutoStart", "0", gConfigFile);
+	}
+
+	sprintf(buf, "%d", gAdditionalGlobalStats.numStats);
+	WritePrivateProfileString(gAppName, "numAdditionalStats", buf, gConfigFile);
+
+	for (int i=0;i<gAdditionalGlobalStats.numStats;i++) {
+		memset(buf, '\000', sizeof(buf));
+		sprintf(buf2, "AdditionalStatsSource%d", i);
+		WritePrivateProfileString(gAppName, buf2, gAdditionalGlobalStats.stats[i].source, gConfigFile);
+
+		memset(buf, '\000', sizeof(buf));
+		sprintf(buf2, "AdditionalStatsName%d", i);
+		WritePrivateProfileString(gAppName, buf2, gAdditionalGlobalStats.stats[i].name, gConfigFile);
+
+		if (gAdditionalGlobalStats.stats[i].titleFlag) {
+			sprintf(buf2, "%s|%s", gAdditionalGlobalStats.stats[i].source, gAdditionalGlobalStats.stats[i].name);
+			WritePrivateProfileString(gAppName, "TitleName", buf2, gConfigFile);
+		}
+	}
+	for (i=0;i<numMainStats;i++) {
+		for (int k=0;k < gStats[i].numStats;k++) {
+			if (gStats[i].stats[k].titleFlag) {
+				sprintf(buf2, "%s|%s", gStats[i].source, gStats[i].stats[k].name);
+				WritePrivateProfileString(gAppName, "TitleName", buf2, gConfigFile);
+			}
+		}
+	}
+
+}
+
+void CIcecast2winDlg::config_read()
+{
+	char	buf2[1024] = "";
+	char	buf[1024] = "";
+	CString	tempString;
+
+	m_colSource0Width = GetPrivateProfileInt(gAppName, "col0SourceWidth", 163, gConfigFile);
+	m_colStats0Width = GetPrivateProfileInt(gAppName, "col0StatsWidth", 100, gConfigFile);
+	m_colStats1Width = GetPrivateProfileInt(gAppName, "col1StatsWidth", 150, gConfigFile);
+	m_colGStats0Width = GetPrivateProfileInt(gAppName, "col0GStatsWidth", 150, gConfigFile);
+	m_colGStats1Width = GetPrivateProfileInt(gAppName, "col1GStatsWidth", 150, gConfigFile);
+	m_colGStats2Width = GetPrivateProfileInt(gAppName, "col2GStatsWidth", 150, gConfigFile);
+
+	GetPrivateProfileString(gAppName, "AutoStart", "0", buf, sizeof(buf), gConfigFile);
+	if (!strcmp(buf, "1")) {
+		m_Autostart = true;
+	}
+	else{
+		m_Autostart = false;
+	}
+	int numAdditionalGlobalStats = GetPrivateProfileInt(gAppName, "numAdditionalStats", 0, gConfigFile);
+	for (int i=0;i<numAdditionalGlobalStats;i++) {
+		memset(buf, '\000', sizeof(buf));
+		sprintf(buf2, "AdditionalStatsSource%d", i);
+		GetPrivateProfileString(gAppName, buf2, "", buf, sizeof(buf), gConfigFile);
+		gAdditionalGlobalStats.stats[i].source = buf;
+
+		memset(buf, '\000', sizeof(buf));
+		sprintf(buf2, "AdditionalStatsName%d", i);
+		GetPrivateProfileString(gAppName, buf2, "", buf, sizeof(buf), gConfigFile);
+		gAdditionalGlobalStats.stats[i].name = buf;
+		gAdditionalGlobalStats.numStats++;
+	}
+	GetPrivateProfileString(gAppName, "TitleName", "", buf, sizeof(buf), gConfigFile);
+
+	if (strlen(buf) > 0) {
+		char *p1 = strchr(buf, '|');
+		if (p1) {
+			char tmpSource[1024] = "";
+			char tmpName[1024] = "";
+			memset(tmpSource, '\000', sizeof(tmpSource));
+			memset(tmpName, '\000', sizeof(tmpName));
+			
+
+			strncpy(tmpSource, buf, p1-buf);
+			p1++;
+			strcpy(tmpName, p1);
+
+			
+			strcpy(gTitleSource, tmpSource);
+			strcpy(gTitleName, tmpName);
+		}
+	}
+
+}
+
+void CIcecast2winDlg::OnClose() 
+{
+	// TODO: Add your message handler code here and/or call default
+	config_write();
+	CDialog::OnClose();
+}
+
+void CIcecast2winDlg::OnSize(UINT nType, int cx, int cy) 
+{
+	CDialog::OnSize(nType, cx, cy);
+	
+	int border1 = 0;
+	int border2 = 78;
+	// TODO: Add your message handler code here
+	if (m_MainTab.m_hWnd) {
+		CRect rect;
+		GetClientRect (&rect);
+		int x = rect.Width()-border1;
+		int y = rect.Width()-border2;
+
+		statusTab.SetWindowPos(NULL, 4, 22, cx, cy, SWP_NOZORDER);
+		statsTab.SetWindowPos(NULL, 4, 22, cx, cy, SWP_NOZORDER);
+		statusTab.m_GlobalStatList.SetWindowPos(NULL, 14, 55, cx-40, cy-180, SWP_NOZORDER);
+		statsTab.m_StatsListCtrl.SetWindowPos(NULL, 213, 55, cx-243, cy-180, SWP_NOZORDER);
+		statsTab.m_SourceListCtrl.SetWindowPos(NULL, 14, 55, 166, cy-180, SWP_NOZORDER);
+//			CListCtrl	m_StatsListCtrl;
+//	CListCtrl	m_SourceListCtrl;
+		m_MainTab.SetWindowPos(NULL, 0, 80, cx, cy, SWP_NOZORDER);
+
+		//m_MainTab.ResizeDialog(0, rect.Width()-border1, rect.Height()-border2);
+		//m_MainTab.ResizeDialog(1, rect.Width()-border1, rect.Height()-border2);
+	}
+
+}
+
+
+LONG CIcecast2winDlg::OnTrayNotify ( WPARAM wParam, LPARAM lParam )
+{
+	switch (lParam) {
+	case WM_RBUTTONDOWN:
+			{
+			CMenu menu ;
+			// Load and Verify Menu
+			VERIFY(menu.LoadMenu(IDR_TRAY));
+			CMenu* pPopup = menu.GetSubMenu (0) ;
+			ASSERT(pPopup != NULL);
+
+			// Get the cursor position
+			POINT pt ;
+			GetCursorPos (&pt) ;
+
+			// Fix Microsofts' BUG!!!!
+			SetForegroundWindow();
+
+			///////////////////////////////////
+			// Display The Menu
+			pPopup->TrackPopupMenu(TPM_LEFTALIGN |
+			TPM_RIGHTBUTTON,pt.x, pt.y, AfxGetMainWnd());
+			break ;
+			}
+	case WM_LBUTTONDBLCLK:
+	//////////////////////////////////
+	// Unhide our Window
+		if (m_bHidden) {
+			ShowWindow (SW_RESTORE);
+		}
+	//OnUnHide() ;
+		break ;
+	}
+
+	return (0) ;
+}
+
+void CIcecast2winDlg::OnHidesystray() 
+{
+	// TODO: Add your control notification handler code here
+	OnHide();
+	theApp.HideApplication();	
+}
+void CIcecast2winDlg::OnHide() 
+{
+	// TODO: Add your control notification handler code here
+	if (m_pTray == NULL) {
+		m_pTray = new CTrayNot (this,WM_TRAY_NOTIFY, NULL,theApp.m_pIconList);
+	}
+	m_pTray->SetState(0);
+	m_bHidden = TRUE;
+	
+}
+
+void CIcecast2winDlg::OnBlankRestore() 
+{
+	// TODO: Add your command handler code here
+		if (m_bHidden) {
+			ShowWindow (SW_RESTORE);
+		}
+	
+}
+
+void CIcecast2winDlg::OnDestroy() 
+{
+	CDialog::OnDestroy();
+	
+	if (m_pTray) {
+		delete m_pTray ;
+		m_pTray = NULL ;
+	} 
+	// TODO: Add your message handler code here
+	
+}
+
+void CIcecast2winDlg::OnFileEditconfiguration() 
+{
+	// TODO: Add your command handler code here
+	
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+
+    ZeroMemory( &si, sizeof(si) );
+    si.cb = sizeof(si);
+    ZeroMemory( &pi, sizeof(pi) );
+
+
+	int ok = 1;
+	if (global.running == ICE_RUNNING) {
+		MessageBox("I'm sorry, but you cannot edit the configuration file while the server is running", NULL, MB_OK);
+	}
+	else {
+		// Start the child process. 
+		if( !CreateProcess( NULL, // No module name (use command line). 
+			"notepad icecast.xml", // Command line. 
+			NULL,             // Process handle not inheritable. 
+			NULL,             // Thread handle not inheritable. 
+			FALSE,            // Set handle inheritance to FALSE. 
+			0,                // No creation flags. 
+			NULL,             // Use parent's environment block. 
+			NULL,             // Use parent's starting directory. 
+			&si,              // Pointer to STARTUPINFO structure.
+			&pi )             // Pointer to PROCESS_INFORMATION structure.
+		) 
+		{
+		   ok = 0;
+		}
+
+		// Wait until child process exits.
+		WaitForSingleObject( pi.hProcess, INFINITE );
+
+		// Close process and thread handles. 
+		CloseHandle( pi.hProcess );
+		CloseHandle( pi.hThread );
+	}
+
+}
+
+void CIcecast2winDlg::OnAboutHelp() 
+{
+	// TODO: Add your command handler code here
+	ShellExecute(NULL, "open", "doc\\icecast2.chm", NULL, NULL, SW_SHOWNORMAL);	
+}
+
+void CIcecast2winDlg::OnAboutCredits() 
+{
+	// TODO: Add your command handler code here
+	CAboutDlg	about;
+	about.DoModal();
+}

Deleted: icecast/tags/icecast-2.3.2/win32/icecast.dsp
===================================================================
--- icecast/trunk/icecast/win32/icecast.dsp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/icecast.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,380 +0,0 @@
-# Microsoft Developer Studio Project File - Name="icecast" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Static Library" 0x0104
-
-CFG=icecast - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE 
-!MESSAGE NMAKE /f "icecast.mak".
-!MESSAGE 
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE 
-!MESSAGE NMAKE /f "icecast.mak" CFG="icecast - Win32 Debug"
-!MESSAGE 
-!MESSAGE Possible choices for configuration are:
-!MESSAGE 
-!MESSAGE "icecast - Win32 Release" (based on "Win32 (x86) Static Library")
-!MESSAGE "icecast - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE 
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF  "$(CFG)" == "icecast - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "Release"
-# PROP BASE Intermediate_Dir "Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Releaseicecast"
-# PROP Intermediate_Dir "Releaseicecast"
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../curl/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo
-
-!ELSEIF  "$(CFG)" == "icecast - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "Debug"
-# PROP BASE Intermediate_Dir "Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "Debugicecast"
-# PROP Intermediate_Dir "Debugicecast"
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../curl/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "_WIN32" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /FD /D /GZ /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo
-
-!ENDIF 
-
-# Begin Target
-
-# Name "icecast - Win32 Release"
-# Name "icecast - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=..\src\admin.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\admin.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth_htpasswd.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth_htpasswd.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth_url.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\auth_url.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\avl\avl.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\avl\avl.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\cfgfile.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\cfgfile.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\client.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\client.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\compat.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\connection.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\connection.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\event.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\event.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_flac.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_flac.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_midi.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_midi.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_mp3.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_mp3.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_ogg.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_ogg.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_speex.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_speex.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_theora.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_theora.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_vorbis.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\format_vorbis.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\fserve.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\fserve.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\global.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\global.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\httpp\httpp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\httpp\httpp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\log\log.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\log\log.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\logging.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\logging.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\main.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\md5.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\md5.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\os.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\refbuf.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\refbuf.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\net\resolver.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\net\resolver.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\sighandler.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\sighandler.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\slave.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\net\sock.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\net\sock.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\source.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\source.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\stats.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\stats.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\thread\thread.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\thread\thread.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\timing\timing.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\util.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\util.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\xslt.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\yp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\yp.h
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=..\src\timing\timing.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\xslt.h
-# End Source File
-# End Group
-# End Target
-# End Project

Copied: icecast/tags/icecast-2.3.2/win32/icecast.dsp (from rev 14985, icecast/trunk/icecast/win32/icecast.dsp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/icecast.dsp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/icecast.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,392 @@
+# Microsoft Developer Studio Project File - Name="icecast" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=icecast - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "icecast.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "icecast.mak" CFG="icecast - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "icecast - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "icecast - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "icecast - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "releaselib_tmp"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../curl/include" /I "..\src" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /D sock_t=SOCKET /D "HAVE_WINSOCK2_H" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "icecast - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "debuglib_tmp"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../curl/include" /I "..\src" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "_WIN32" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /FD /D /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "icecast - Win32 Release"
+# Name "icecast - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\admin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth_htpasswd.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth_url.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\avl\avl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\cfgfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\client.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\connection.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\event.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_flac.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_kate.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_midi.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_mp3.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_ogg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_skeleton.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_speex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_theora.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_vorbis.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\fserve.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\global.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\httpp\httpp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\log\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\logging.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\os.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\refbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\net\resolver.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\sighandler.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\slave.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\net\sock.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\source.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\stats.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\thread\thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\timing\timing.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\xslt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\yp.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\admin.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth_htpasswd.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\auth_url.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\avl\avl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\cfgfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\client.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\compat.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\connection.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\event.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_flac.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_kate.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_midi.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_mp3.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_ogg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_skeleton.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_speex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_theora.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\format_vorbis.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\fserve.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\httpp\httpp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\log\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\logging.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\refbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\net\resolver.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\sighandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\net\sock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\source.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\stats.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\thread\thread.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\timing\timing.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\xslt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\yp.h
+# End Source File
+# End Group
+# End Target
+# End Project

Deleted: icecast/tags/icecast-2.3.2/win32/icecast2.iss
===================================================================
--- icecast/trunk/icecast/win32/icecast2.iss	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/icecast2.iss	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,61 +0,0 @@
-; Script generated by the Inno Setup Script Wizard.
-; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
-
-[Setup]
-AppName=Icecast2 Win32
-AppVerName=Icecast v2.3.1
-AppPublisherURL=http://www.icecast.org
-AppSupportURL=http://www.icecast.org
-AppUpdatesURL=http://www.icecast.org
-DefaultDirName={pf}\Icecast2 Win32
-DefaultGroupName=Icecast2 Win32
-AllowNoIcons=yes
-LicenseFile=..\COPYING
-InfoAfterFile=..\README
-OutputDir=.
-OutputBaseFilename=icecast2_win32_v2.3.1_setup
-WizardImageFile=icecast2logo2.bmp
-WizardImageStretch=no
-; uncomment the following line if you want your installation to run on NT 3.51 too.
-; MinVersion=4,3.51
-
-[Tasks]
-Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"; MinVersion: 4,4
-
-[Dirs]
-Name: "{app}\web"
-Name: "{app}\admin"
-Name: "{app}\doc"
-Name: "{app}\logs"
-Name: "{app}\examples"
-
-
-[Files]
-Source: "Release\Icecast2.exe"; DestDir: "{app}"; Flags: ignoreversion
-Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
-Source: "Release\icecastService.exe"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\examples\*"; DestDir: "{app}\examples"; Flags: ignoreversion
-Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
-Source: "..\web\*.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
-Source: "..\web\*.png"; DestDir: "{app}\web"; Flags: ignoreversion
-Source: "..\web\*.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
-Source: "..\web\*.css"; DestDir: "{app}\web"; Flags: ignoreversion
-Source: "..\admin\*.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
-Source: "..\..\pthreads\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\conf\*.xml"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\iconv\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\libxslt\lib\libxslt.dll"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\libxml2\lib\libxml2.dll"; DestDir: "{app}"; Flags: ignoreversion
-Source: "..\..\curl\lib\Release\libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion
-
-[Icons]
-
-Name: "{group}\Icecast2 Win32"; Filename: "{app}\Icecast2.exe";WorkingDir: "{app}";
-Name: "{userdesktop}\Icecast2 Win32"; Filename: "{app}\Icecast2.exe"; MinVersion: 4,4; Tasks: desktopicon;WorkingDir: "{app}";
-
-[Run]
-Filename: "{app}\icecastService.exe"; Parameters: "install ""{app}""";Description: "Install Icecast as a windows service.";Flags: postinstall
-
-[UninstallRun]
-Filename: "{app}\icecastService.exe"; Parameters: "remove"
-

Copied: icecast/tags/icecast-2.3.2/win32/icecast2.iss (from rev 14985, icecast/trunk/icecast/win32/icecast2.iss)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/icecast2.iss	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/icecast2.iss	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,64 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+
+[Setup]
+AppName=Icecast2 Win32
+AppVerName=Icecast 2.3.2
+AppPublisherURL=http://www.icecast.org
+AppSupportURL=http://www.icecast.org
+AppUpdatesURL=http://www.icecast.org
+DefaultDirName={pf}\Icecast2 Win32
+DefaultGroupName=Icecast2 Win32
+AllowNoIcons=yes
+LicenseFile=..\COPYING
+InfoAfterFile=..\README
+OutputDir=.
+OutputBaseFilename=icecast2_win32_2.3.2_setup
+WizardImageFile=icecast2logo2.bmp
+WizardImageStretch=no
+; uncomment the following line if you want your installation to run on NT 3.51 too.
+; MinVersion=4,3.51
+
+[Tasks]
+Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"; MinVersion: 4,4
+
+[Dirs]
+Name: "{app}\web"
+Name: "{app}\admin"
+Name: "{app}\doc"
+Name: "{app}\logs"
+Name: "{app}\examples"
+
+
+[Files]
+Source: "Release\Icecast2win.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "Release\icecastService.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\examples\*"; DestDir: "{app}\examples"; Flags: ignoreversion
+; Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
+Source: "..\web\*.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\*.png"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\*.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\*.css"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\admin\*.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "c:\xiph\lib\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "icecast.xml"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\libxslt.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\libxml2.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\libeay32.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\ssleay32.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "c:\xiph\lib\zlib1.dll"; DestDir: "{app}"; Flags: ignoreversion
+
+[Icons]
+
+Name: "{group}\Icecast2 Win32"; Filename: "{app}\Icecast2win.exe";WorkingDir: "{app}";
+Name: "{userdesktop}\Icecast2 Win32"; Filename: "{app}\Icecast2win.exe"; MinVersion: 4,4; Tasks: desktopicon;WorkingDir: "{app}";
+
+[Run]
+Filename: "{app}\icecastService.exe"; Parameters: "install ""{app}""";Description: "Install Icecast as a windows service.";Flags: postinstall
+
+[UninstallRun]
+Filename: "{app}\icecastService.exe"; Parameters: "remove"
+

Deleted: icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp
===================================================================
--- icecast/trunk/icecast/win32/icecast2_console.dsp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,102 +0,0 @@
-# Microsoft Developer Studio Project File - Name="icecast2 console" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=icecast2 console - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE 
-!MESSAGE NMAKE /f "icecast2_console.mak".
-!MESSAGE 
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE 
-!MESSAGE NMAKE /f "icecast2_console.mak" CFG="icecast2 console - Win32 Debug"
-!MESSAGE 
-!MESSAGE Possible choices for configuration are:
-!MESSAGE 
-!MESSAGE "icecast2 console - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "icecast2 console - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE 
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF  "$(CFG)" == "icecast2 console - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "icecast2_console___Win32_Release"
-# PROP BASE Intermediate_Dir "icecast2_console___Win32_Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MT /W3 /GX /O2 /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Releaseicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Release\theora_static.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /out:"Release/icecast2console.exe"
-
-!ELSEIF  "$(CFG)" == "icecast2 console - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "icecast2_console___Win32_Debug"
-# PROP BASE Intermediate_Dir "icecast2_console___Win32_Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "icecast2_console___Win32_Debug"
-# PROP Intermediate_Dir "icecast2_console___Win32_Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /GZ /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debugicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Debug\theora_static_d.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/icecast2console.exe" /pdbtype:sept
-
-!ENDIF 
-
-# Begin Target
-
-# Name "icecast2 console - Win32 Release"
-# Name "icecast2 console - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=..\src\main.c
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# End Target
-# End Project

Copied: icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp (from rev 14985, icecast/trunk/icecast/win32/icecast2_console.dsp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/icecast2_console.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,103 @@
+# Microsoft Developer Studio Project File - Name="icecast2 console" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=icecast2 console - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "icecast2_console.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "icecast2_console.mak" CFG="icecast2 console - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "icecast2 console - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "icecast2 console - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "icecast2 console - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "icecast2_console___Win32_Release"
+# PROP BASE Intermediate_Dir "icecast2_console___Win32_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "releaseconsole_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_THEORA" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcurl.lib ogg_static_d.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib theora_static_d.lib libspeex.lib /nologo /version:2.3 /subsystem:console /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /out:"Release/icecast2console.exe"
+
+!ELSEIF  "$(CFG)" == "icecast2 console - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "icecast2_console___Win32_Debug"
+# PROP BASE Intermediate_Dir "icecast2_console___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "debugconsole_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_THEORA" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcurl.lib ogg_static_d.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVC.lib ws2_32.lib theora_static_d.lib libspeex.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmt.lib" /out:"Debug/icecast2console.exe" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "icecast2 console - Win32 Release"
+# Name "icecast2 console - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\main.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project

Deleted: icecast/tags/icecast-2.3.2/win32/icecastService.cpp
===================================================================
--- icecast/trunk/icecast/win32/icecastService.cpp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/icecastService.cpp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,170 +0,0 @@
-#include <windows.h>
-#include <stdio.h>
-#include <errno.h>
-#include <direct.h>
-extern "C" {
-#include "thread.h"
-#include "avl.h"
-#include "log.h"
-#include "global.h"
-#include "httpp.h"
-#include "sock.h"
-#include "connection.h"
-#include "refbuf.h"
-#include "client.h"
-#include "stats.h"
-}
-
-
-SERVICE_STATUS          ServiceStatus; 
-SERVICE_STATUS_HANDLE   hStatus; 
- 
-void  ServiceMain(int argc, char** argv); 
-void  ControlHandler(DWORD request); 
-int InitService();
-extern "C" int mainService(int argc, char **argv);
-
-int InitService() 
-{ 
-   int result = 0;
-   return(result); 
-}
-
-void installService(char *path)
-{
-	if (path) {
-		char	fullPath[8096*2] = "";
-
-		sprintf(fullPath, "\"%s\\icecastService.exe\" \"%s\"", path, path);
-		SC_HANDLE handle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
-		SC_HANDLE service = CreateService(
-			handle,
-			"Icecast",
-			"Icecast Media Server",
-			GENERIC_READ | GENERIC_EXECUTE,
-			SERVICE_WIN32_OWN_PROCESS,
-			SERVICE_AUTO_START,
-			SERVICE_ERROR_IGNORE,
-			fullPath,
-			NULL,
-			NULL,
-			NULL,
-			NULL,
-			NULL
-		);
-		printf("Service Installed\n");
-	}
-}
-void removeService()
-{
-	SC_HANDLE handle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
-	SC_HANDLE service = OpenService(handle, "Icecast", DELETE);
-	if (service) {
-		DeleteService(service);
-	}
-	printf("Service Removed\n");
-}
-void ControlHandler(DWORD request) 
-{ 
-   switch(request) { 
-      case SERVICE_CONTROL_STOP: 
-		global.running = ICE_HALTING;
-		ServiceStatus.dwWin32ExitCode = 0; 
-		ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
-		SetServiceStatus (hStatus, &ServiceStatus);
-		return; 
- 
-      case SERVICE_CONTROL_SHUTDOWN: 
-		global.running = ICE_HALTING;
-		ServiceStatus.dwWin32ExitCode = 0; 
-		ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
-		SetServiceStatus (hStatus, &ServiceStatus);
-		return; 
-      default:
-		break;
-    } 
- 
-    // Report current status
-    SetServiceStatus (hStatus, &ServiceStatus);
- 
-    return; 
-}
-
-void ServiceMain(int argc, char** argv) 
-{ 
-   int error; 
- 
-   ServiceStatus.dwServiceType = SERVICE_WIN32; 
-   ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
-   ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
-   ServiceStatus.dwWin32ExitCode = 0; 
-   ServiceStatus.dwServiceSpecificExitCode = 0; 
-   ServiceStatus.dwCheckPoint = 0; 
-   ServiceStatus.dwWaitHint = 0; 
- 
-   hStatus = RegisterServiceCtrlHandler("Icecast", (LPHANDLER_FUNCTION)ControlHandler); 
-   if (hStatus == (SERVICE_STATUS_HANDLE)0) { 
-      // Registering Control Handler failed
-      return; 
-   }  
-   // Initialize Service 
-   error = InitService(); 
-   if (error) {
-      // Initialization failed
-      ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
-      ServiceStatus.dwWin32ExitCode = -1; 
-      SetServiceStatus(hStatus, &ServiceStatus); 
-      return; 
-   } 
-   // We report the running status to SCM. 
-   ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
-   SetServiceStatus (hStatus, &ServiceStatus);
- 
-   /* Here we do the work */
-
-   	int		argc2 = 3;
-	char*	argv2[3];
-
-	argv2[0] = "icecastService.exe";
-	argv2[1] = "-c";
-	argv2[2] = "icecast.xml";
-
-	int ret = mainService(argc2, (char **)argv2);
-
-	ServiceStatus.dwCurrentState = SERVICE_STOPPED;
-	ServiceStatus.dwWin32ExitCode = -1;
-	SetServiceStatus(hStatus, &ServiceStatus);
-	return; 
-}
-
-
-void main(int argc, char **argv) 
-{
-
-	bool matched  = false;
-	if (argv[0]) {
-		if (argv[1]) {
-			if (!strcmp(argv[1], "install")) {
-				installService(argv[2]);
-				matched = true;
-			}
-			if (!strcmp(argv[1], "remove")) {
-				removeService();
-				matched = true;
-			}
-		}
-	}
-	if (matched) {
-		return;
-	}
-	_chdir(argv[1]);
-
-	SERVICE_TABLE_ENTRY ServiceTable[2];
-	ServiceTable[0].lpServiceName = "Icecast Server";
-	ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
-
-	ServiceTable[1].lpServiceName = NULL;
-	ServiceTable[1].lpServiceProc = NULL;
-	// Start the control dispatcher thread for our service
-	StartServiceCtrlDispatcher(ServiceTable);  
-}
\ No newline at end of file

Copied: icecast/tags/icecast-2.3.2/win32/icecastService.cpp (from rev 14867, icecast/trunk/icecast/win32/icecastService.cpp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/icecastService.cpp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/icecastService.cpp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,261 @@
+#include <windows.h>
+#include <stdio.h>
+#include <direct.h>
+extern "C" {
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "log/log.h"
+#include "global.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+}
+
+// Issues to be wary of. Careful of the runtime you use, I've had printf and similar routines
+// crash because of this on apparently valid strings. some weird thing related to checking for
+// multiple byte characters.  DeleteService only marks a service for deletion, and the docs
+// are unclear on the cases that lead to purging however a reboot should do it.
+
+SERVICE_STATUS          ServiceStatus; 
+SERVICE_STATUS_HANDLE   hStatus; 
+ 
+void  ServiceMain(int argc, char** argv); 
+void  ControlHandler(DWORD request); 
+int InitService();
+extern "C" int mainService(int argc, char **argv);
+
+int InitService() 
+{ 
+   int result = 0;
+   return(result); 
+}
+
+void installService (const char *path)
+{
+    if (path) {
+        char	fullPath[8096*2] = "\"";
+        int len = GetModuleFileName (NULL, fullPath+1, sizeof (fullPath)-1);
+
+         _snprintf(fullPath+len+1, sizeof (fullPath)-len, "\" \"%s\"", path);
+
+        SC_HANDLE manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+        if (manager == NULL)
+		{
+			LPVOID lpMsgBuf;
+			FormatMessage( 
+					FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+					FORMAT_MESSAGE_FROM_SYSTEM | 
+					FORMAT_MESSAGE_IGNORE_INSERTS,
+					NULL,
+					GetLastError(),
+					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+					(LPTSTR) &lpMsgBuf,
+					0,
+					NULL 
+					);
+
+			printf ("OpenSCManager: %s\n", (LPCTSTR)lpMsgBuf);
+			LocalFree( lpMsgBuf );
+			return;
+		}
+
+		SC_HANDLE service = CreateService(
+				manager,
+                PACKAGE_STRING,
+                PACKAGE_STRING " Streaming Media Server",
+			GENERIC_READ | GENERIC_EXECUTE,
+			SERVICE_WIN32_OWN_PROCESS,
+			SERVICE_AUTO_START,
+			SERVICE_ERROR_IGNORE,
+			fullPath,
+			NULL,
+			NULL,
+			NULL,
+			NULL,
+			NULL
+		);
+		if (service == NULL)
+		{
+			LPVOID lpMsgBuf;
+			FormatMessage( 
+					FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+					FORMAT_MESSAGE_FROM_SYSTEM | 
+					FORMAT_MESSAGE_IGNORE_INSERTS,
+					NULL,
+					GetLastError(),
+					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+					(LPTSTR) &lpMsgBuf,
+					0,
+					NULL 
+					);
+
+			printf ("CreateService: %s\n", (LPCTSTR)lpMsgBuf);
+			LocalFree( lpMsgBuf );
+			CloseServiceHandle (manager);
+			return;
+		}
+
+		printf("Service Installed\n");
+		CloseServiceHandle (service);
+		CloseServiceHandle (manager);
+	}
+}
+void removeService()
+{
+	SC_HANDLE manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+	if (manager == NULL)
+	{
+		LPVOID lpMsgBuf;
+		FormatMessage( 
+				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+				FORMAT_MESSAGE_FROM_SYSTEM | 
+				FORMAT_MESSAGE_IGNORE_INSERTS,
+				NULL,
+				GetLastError(),
+				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+				(LPTSTR) &lpMsgBuf,
+				0,
+				NULL 
+				);
+
+		printf ("OpenSCManager: %s\n", (LPCTSTR)lpMsgBuf);
+		LocalFree( lpMsgBuf );
+		return;
+	}
+
+	SC_HANDLE service = OpenService (manager, PACKAGE_STRING, DELETE);
+	if (service) {
+		DeleteService(service);
+		printf("Service Removed\n");
+        CloseServiceHandle (service);
+	}
+	else
+		printf("Service not found\n");
+    CloseServiceHandle (manager);
+}
+void ControlHandler(DWORD request) 
+{ 
+   switch(request) { 
+      case SERVICE_CONTROL_STOP: 
+		global.running = ICE_HALTING;
+		ServiceStatus.dwWin32ExitCode = 0; 
+		ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
+		SetServiceStatus (hStatus, &ServiceStatus);
+		return; 
+ 
+      case SERVICE_CONTROL_SHUTDOWN: 
+		global.running = ICE_HALTING;
+		ServiceStatus.dwWin32ExitCode = 0; 
+		ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
+		SetServiceStatus (hStatus, &ServiceStatus);
+		return; 
+      default:
+		break;
+    } 
+ 
+    // Report current status
+    SetServiceStatus (hStatus, &ServiceStatus);
+ 
+    return; 
+}
+
+void ServiceMain(int argc, char** argv) 
+{ 
+   int error; 
+ 
+   ServiceStatus.dwServiceType = SERVICE_WIN32; 
+   ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
+   ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+   ServiceStatus.dwWin32ExitCode = 0; 
+   ServiceStatus.dwServiceSpecificExitCode = 0; 
+   ServiceStatus.dwCheckPoint = 0; 
+   ServiceStatus.dwWaitHint = 0; 
+ 
+   hStatus = RegisterServiceCtrlHandler(PACKAGE_STRING, (LPHANDLER_FUNCTION)ControlHandler); 
+   if (hStatus == (SERVICE_STATUS_HANDLE)0) { 
+      // Registering Control Handler failed
+      return; 
+   }  
+   // Initialize Service 
+   error = InitService(); 
+   if (error) {
+      // Initialization failed
+      ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
+      ServiceStatus.dwWin32ExitCode = -1; 
+      SetServiceStatus(hStatus, &ServiceStatus); 
+      return; 
+   } 
+   // We report the running status to SCM. 
+   ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
+   SetServiceStatus (hStatus, &ServiceStatus);
+ 
+   /* Here we do the work */
+
+   	int		argc2 = 3;
+	char*	argv2 [4];
+
+    argv2 [0] = argv[0];
+    argv2 [1] = "-c";
+    if (argc < 2)
+        argv2 [2] = "icecast.xml";
+    else
+        argv2 [2] = argv[1];
+    argv2[3] = NULL;
+
+	int ret = mainService(argc2, (char **)argv2);
+
+	ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+	ServiceStatus.dwWin32ExitCode = -1;
+	SetServiceStatus(hStatus, &ServiceStatus);
+	return; 
+}
+
+
+int main(int argc, char **argv) 
+{
+    if (argc < 2)
+    {
+        printf ("Usage:\n %s  remove\n %s  install path\n", argv[0], argv[0]);
+        return 0;
+    }
+    if (!strcmp(argv[1], "install"))
+    {
+        if (argc > 2)
+        {
+            printf ("Installing service from %s\n", argv[2]);
+            installService(argv[2]);
+        }
+        else
+            printf ("install requires a path arg as well\n");
+        Sleep (2000);
+        return 0;
+    }
+    if (!strcmp(argv[1], "remove") || !strcmp(argv[1], "uninstall"))
+    {
+        printf ("removing service\n");
+        removeService();
+        Sleep (2000);
+        return 0;
+    }
+
+    if (_chdir(argv[1]) < 0)
+    {
+        printf ("unable to change to directory %s\n", argv[1]);
+        Sleep (2000);
+        return 0;
+    }
+
+	SERVICE_TABLE_ENTRY ServiceTable[2];
+	ServiceTable[0].lpServiceName = PACKAGE_STRING;
+	ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
+
+	ServiceTable[1].lpServiceName = NULL;
+	ServiceTable[1].lpServiceProc = NULL;
+	// Start the control dispatcher thread for our service
+	StartServiceCtrlDispatcher(ServiceTable);  
+
+    return 0;
+}

Deleted: icecast/tags/icecast-2.3.2/win32/icecastService.dsp
===================================================================
--- icecast/trunk/icecast/win32/icecastService.dsp	2006-01-23 19:28:04 UTC (rev 10752)
+++ icecast/tags/icecast-2.3.2/win32/icecastService.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -1,107 +0,0 @@
-# Microsoft Developer Studio Project File - Name="icecastService" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=icecastService - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE 
-!MESSAGE NMAKE /f "icecastService.mak".
-!MESSAGE 
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE 
-!MESSAGE NMAKE /f "icecastService.mak" CFG="icecastService - Win32 Debug"
-!MESSAGE 
-!MESSAGE Possible choices for configuration are:
-!MESSAGE 
-!MESSAGE "icecastService - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "icecastService - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE 
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF  "$(CFG)" == "icecastService - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "Release"
-# PROP BASE Intermediate_Dir "Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MT /W3 /GX /O2 /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "WIN32_SERVICE" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Releaseicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Release\theora_static.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib"
-# SUBTRACT LINK32 /pdb:none
-
-!ELSEIF  "$(CFG)" == "icecastService - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "icecastService___Win32_Debug"
-# PROP BASE Intermediate_Dir "icecastService___Win32_Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "icecastService___Win32_Debug"
-# PROP Intermediate_Dir "icecastService___Win32_Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "WIN32_SERVICE" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /GZ /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 Debugicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Debug\theora_static_d.lib ../../speex/win32/libspeex/Release/libspeex.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcd.lib" /pdbtype:sept
-
-!ENDIF 
-
-# Begin Target
-
-# Name "icecastService - Win32 Release"
-# Name "icecastService - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=.\icecastService.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\main.c
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# End Target
-# End Project

Copied: icecast/tags/icecast-2.3.2/win32/icecastService.dsp (from rev 14985, icecast/trunk/icecast/win32/icecastService.dsp)
===================================================================
--- icecast/tags/icecast-2.3.2/win32/icecastService.dsp	                        (rev 0)
+++ icecast/tags/icecast-2.3.2/win32/icecastService.dsp	2008-06-02 03:19:35 UTC (rev 14987)
@@ -0,0 +1,109 @@
+# Microsoft Developer Studio Project File - Name="icecastService" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=icecastService - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "icecastService.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "icecastService.mak" CFG="icecastService - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "icecastService - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "icecastService - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "icecastService - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "releaseservice_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "WIN32_SERVICE" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_THEORA" /YX /FD /D PACKAGE_STRING=\"Icecast-trunk\" /c
+# SUBTRACT CPP /X
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcurl.lib ogg_static_d.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib theora_static_d.lib libspeex.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "icecastService - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "icecastService___Win32_Debug"
+# PROP BASE Intermediate_Dir "icecastService___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "debugservice_tmp"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "WIN32_SERVICE" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.2\" /D "HAVE_THEORA" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libcurl.lib ogg_static_d.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVC.lib ws2_32.lib theora_static_d.lib libspeex.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmt.lib" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "icecastService - Win32 Release"
+# Name "icecastService - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\icecastService.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\main.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project



More information about the commits mailing list