[xiph-commits] r7095 - in icecast/branches/kh: . icecast

j at dactyl.lonelymoon.com j
Sun Jul 11 11:09:07 PDT 2004


icecast/conf icecast/doc icecast/src icecast/win32
Message-ID: <20040711180907.88E739AAAB at dactyl.lonelymoon.com>

Author: j
Date: Sun Jul 11 11:09:07 2004
New Revision: 7095

Added:
icecast/branches/kh/icecast/
Modified:
icecast/branches/kh/icecast/AUTHORS
icecast/branches/kh/icecast/NEWS
icecast/branches/kh/icecast/README
icecast/branches/kh/icecast/TODO
icecast/branches/kh/icecast/conf/icecast.xml.in
icecast/branches/kh/icecast/configure.in
icecast/branches/kh/icecast/doc/icecast2_config_file.html
icecast/branches/kh/icecast/src/Makefile.am
icecast/branches/kh/icecast/src/admin.c
icecast/branches/kh/icecast/src/auth.c
icecast/branches/kh/icecast/src/auth.h
icecast/branches/kh/icecast/src/cfgfile.c
icecast/branches/kh/icecast/src/cfgfile.h
icecast/branches/kh/icecast/src/client.c
icecast/branches/kh/icecast/src/client.h
icecast/branches/kh/icecast/src/connection.c
icecast/branches/kh/icecast/src/format.c
icecast/branches/kh/icecast/src/format.h
icecast/branches/kh/icecast/src/format_mp3.c
icecast/branches/kh/icecast/src/format_mp3.h
icecast/branches/kh/icecast/src/format_vorbis.c
icecast/branches/kh/icecast/src/format_vorbis.h
icecast/branches/kh/icecast/src/fserve.c
icecast/branches/kh/icecast/src/global.c
icecast/branches/kh/icecast/src/logging.c
icecast/branches/kh/icecast/src/main.c
icecast/branches/kh/icecast/src/md5.c
icecast/branches/kh/icecast/src/refbuf.c
icecast/branches/kh/icecast/src/refbuf.h
icecast/branches/kh/icecast/src/sighandler.c
icecast/branches/kh/icecast/src/slave.c
icecast/branches/kh/icecast/src/slave.h
icecast/branches/kh/icecast/src/source.c
icecast/branches/kh/icecast/src/source.h
icecast/branches/kh/icecast/src/stats.c
icecast/branches/kh/icecast/src/util.c
icecast/branches/kh/icecast/src/xslt.c
icecast/branches/kh/icecast/src/yp.c
icecast/branches/kh/icecast/win32/icecast.dsp
icecast/branches/kh/icecast/win32/icecast2.iss
Log:
reimport icecast-kh to branches/kh/icecast; this time as a branch of trunk/icecast

Copied: icecast/branches/kh/icecast (from rev 7094, icecast/trunk/icecast)


Property changes on: icecast/branches/kh/icecast
___________________________________________________________________
Name: svn:ignore
+ Makefile
Makefile.in
aclocal.m4
autom4te.cache
compile
config.cache
config.guess
config.sub
config.h.in
config.log
config.status
configure
depcomp
install-sh
libtool
ltconfig
ltmain.sh
missing
mkinstalldirs
*.tar.gz

Name: branch-point
+ 7094
Name: svn:externals
+ m4		http://svn.xiph.org/icecast/trunk/m4
src/avl		http://svn.xiph.org/icecast/trunk/avl
src/httpp	http://svn.xiph.org/icecast/trunk/httpp
src/log		http://svn.xiph.org/icecast/trunk/log
src/timing	http://svn.xiph.org/icecast/trunk/timing
src/net         http://svn.xiph.org/icecast/branches/kh/net
src/thread      http://svn.xiph.org/icecast/branches/kh/thread


Modified: icecast/branches/kh/icecast/AUTHORS
===================================================================
--- icecast/trunk/icecast/AUTHORS	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/AUTHORS	2004-07-11 18:09:05 UTC (rev 7095)
@@ -1,4 +1,4 @@
Jack Moffitt <jack at icecast.org>
Michael Smith <msmith at icecast.org>
-oddsock <oddsock at xiph.org>
+oddsock <oddsock at oddsock.org>
Karl Heyes <karl at xiph.org>

Modified: icecast/branches/kh/icecast/NEWS
===================================================================
--- icecast/trunk/icecast/NEWS	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/NEWS	2004-07-11 18:09:05 UTC (rev 7095)
@@ -1,3 +1,332 @@
+feature differences from CVS
+
+fast pre-buffering - burst a certain amount of data at connection time.
+mp3 accepts artist and title separately on the url.
+vorbis can accept url updates if module compiled in
+program invocation at stream start and end, per mount based.
+on-demand relays, activated on first listener, disconnected when listeners
+falls to 0
+initial implementation of multiple Ogg codec streaming, theora, vorbis and
+speex. more work needed to start at theora key frame.
+Added URL and command based authentication
+
+kh47
+. per mount burst size was not being referred to properly
+. added sync markers to queue for client starts, the buffer with a sync mark
+  from the burst point is where a client starts from.
+. theora marks a sync marker on a page which has the start of a keyframe.
+. vorbis and speex mark sync on all buffers unless theora is used. mp3 sync
+  marks on every buffer.
+. some code re-org for queue management, allows old refbuf removal even
+  on socket read timeout
+. place clients that can take more pending data at the beginning of the client
+  queue. This allows for reducing client scanning when there's a short timeout
+
+kh46
+. New listeners actually drop to fallback mount, silly inverse bug
+. typo in mp3 metadata handling, affected error case, rare
+. make short delay timeout work when reading from source, minor handling
+  case from previous update.
+. make the ogg vorbis module use 1000 not 1024 as a factor for stats.
+. if a YP server is up but not responding with an error message then show
+  a default one.
+. fixed a fallback problem and improved the log messages for when fallback
+  is triggered.
+
+kh45
+. fix minor YP cleanup case when YP server fails and source disconnects
+. cleanup from previous patch exposed a potential source reading bug, which
+  had shown up because sources could disconnect for no reason.
+
+kh44
+. fix segv case for non-auth streams, unchecked in last release.
+. only send ICY 200 OK if realplayer else send HTTP/1.0 200 OK
+. allow for setting username/password for auth URLs
+. still apply timeout to recoverable source read
+
+kh43
+. add <username> <password> tags for <relay>, default none, both need to
+  be provided for auth to be attempted.
+. add url based auth module. example in conf, you can state add and
+  remove urls to run for each client, each add is passed id, user, pass, ip,
+  user agent. The remove is passed id, user, pass, duration
+. make sure the global client count is in sync when auth is used
+
+kh42
+. drop the use of thread conditional variables in the connection threads,
+  they we not working as they should
+. minor cleanups, log messages etc
+. re-work auth subsystem to allow for slow auth mechanisms
+. added command auth, do that listener auth can be done externally. The
+  external command should take a mount, user and pass on separate lines and
+  return a status code of 0 (authenticated) or non-zero (reject)
+
+kh41
+. fixed 401 reporting on missing urls, should be 404. Handle a very rare
+  long lock held case as well
+
+kh40
+. make sure public is 0 in the stats tree
+. move parse_audio_info back to source.c and make it thread safe
+. add debug for YP ok case, debug is supposed to be rarely used after all
+. up the per-listener max write threshold
+. report 401 for failed auth connections
+. add audio/x-mpeg mime type
+
+kh39
+. mp3 streams without metadata were not having their metadata initialised
+  properly, causing problems when clients connected with metadata capable
+  clients.
+. send ice-audio-info to clients, provides YP info in relays situations
+
+kh38
+. re-sync with svn codebase, update listener auth and docs
+. send ICY OK instead of HTTP OK for realplayer test
+. minor cleanups
+
+kh37
+. add the previous vorbis specific handler back in, it allowed for updates
+  via url. enabled via configure --enable-vorbis-updates
+. updated admin pages to those in the main tree
+. plug some minor memory leaks in yp
+
+kh36
+. force yp_remove, log message when found, fixed a race as well
+. fix mp3 segv when on-demand relays are used
+. fix debug messaging in yp.c, helps to track any YP related problems
+. emit icy-br instead of icy-bitrate, allow relays to be on YP listings, but
+  handle icy-bitrate as well
+. <no-yp> (default 0) in <mount> prevents a mount from going on YP listings
+. fixes to ice-audio-info handling.
+. make yp thread wait longer than it does at shutdown, allows for final
+  yp_remove on closing streams
+
+kh35
+. handle YP error conditions better.
+. moving clients via url allows on-demand relays to startup/shutdown
+. segv bug fixed from previous release, http headers were being prepared
+  on mount switchover.
+
+kh34
+. minor fixes for YP, fix missing bits from rewrite
+  if any YP request fails go back to add
+  do a yp touch on inline mp3 updates as well as url metadata updates
+. sync with main tree for minor patches.
+. set default stream burst size to 64k (typical player prebuffer size)
+. fix some missing web stats
+. add <fallback-when-full> (0 default) to <mount>, put new listeners
+  on next available fallback, 404 otherwise
+
+kh33
+. schedule a YP touch on admin metadata updates
+. build fix relating to geturl files.
+
+kh32
+. minor compile cleanups from CVS
+. make sure a failed script does exit
+. report system message on initial log open failure
+. update YP thread code, big change.
+
+kh31
+. fix for bug when adding clients from pending list.
+
+kh30
+. add make static for those who want it
+. changed code on list processing for active and pending clients
+. changed when format-specific client data gets freed up
+. shuffle code around, OK response to source client only source_client_thread
+. 404 sent to client on failed on-demand relay
+. more re-sync work + various non-functional cleanups
+
+kh29
+. revert test harness code left from before which advertised private streams
+  and cleanup unwanted messages
+. more cleanups, more sync work with CVS
+. source_t wasn't going after source client disconnection
+
+kh28
+. more re-sync work
+. fixed a yp deadlock case for stuffed yp servers.
+
+kh27
+. fixed 2 possible deadlock cases
+. fixed race wrt to alias lookup after HUP change
+. reset ogg stream type to "Ogg Vorbis" even if it isn't, so that YP
+  shows icons correctly.
+
+kh26
+. fixed YP related bugs, leaks + bad pointer access
+. fixed rare segv case in format_mp3
+. more cleanup work in source shutdown
+. Changed how client http headers are sent back
+. Implement on demand relay, enabled with <on-demand>1</on-demand> in <relay>
+
+kh25
+. Changed Ogg module
+  - added Ogg multiplexing
+  - added theora and speex streaming
+  - URL updates removed from vorbis, due to multiple codec support, maybe added later.
+. continue with more re-sync to CVS work
+. fixed a few rare races that had shown up with the re-sync work.
+
+kh24
+. more re-sync work with CVS.
+. Identified a couple more locking issues via the admin interface
+. remove errno use from stream dump routines
+
+kh23
+. re-sync with CVS tree, fallback override, no-mount, initial work on
+  listener authentication.
+. locking updates
+. on-connect/disconnect scripts are started and not waited on.
+. icy/ice headers cleanup
+. initialise config correctly
+
+kh22
+. fix some rare lock races
+. page samples fix on EOS flush, reset granulepos to 0 on new stream
+. add /admin/streamlist.txt from beta3
+. big update of the relay code, supports master relay now
+. added yp-log-headers tag in <logging> to disable YP header logging, it
+  can make for lots of noise in the logs
+
+kh21
+. only send a 200 OK for source connections that are from source clientts
+  and not relays
+. on failed source init, some setup was not cleared and the source count
+  was not decremented
+. Check that writes to clients have actual data available
+. fix deadlock case with yp
+. fix locking for url metdata updates generically
+. fix use of non thread-safe function localtime in yp
+
+kh20
+. fixed slave/stats shutdown race
+. re-applying avl fix for null free function
+. fix for vorbis, API changes caused stalls when sending to client
+. relay updating/restarting code fixed.
+. changes to vorbis input to allow for url updates
+. allow for stating artist and title on url
+
+kh19
+. fix a few more signed/unsigned problems, affecting mp3 mainly
+. re-worked pthread configure option
+. update various bits to bring in line with v2.0 beta2
+
+kh18
+. another signed/unsigned int bug fixed
+. added a short delay trigger for waiting clients, improves bursting
+. added --disable-log-ip for the access log
+. fix segv when adding clients to inactive relays
+
+kh17
+. cleanup some api bits
+. fix mp3 bug causing glitches in the audio
+. added pidfile support
+
+kh16
+. made outgoing serial numbers unique, helps in fallback situations
+. revert date field to where it was in access.log. That way it conforms
+  to the common log format for analysers.
+. changed interal api of the client write function so that handling of
+  burst limits is better. Also allow the queue to be more format specific.
+
+kh15
+. Updated vorbis input to rebuild stream, forcing pages to contain around
+  1 second worth of audio.
+
+kh14
+. use localtime_r when available
+. compile time switch for enabling/disabling logging IP in access log
+. handle source counts better.
+. relays retain the source struct so source clients don't steal inactive
+  relay mountpoints.
+
+kh13
+. update to latest CVS docs
+. fixup source timeout, internally it's in milliseconds, and can be
+  stated per mount as well as globally.
+
+kh12
+. terminate stream if inline metadata does not contain "StreamTitle"
+. Added docs by oddsock, and other updates from CVS
+. wait for source start/stop run scripts to finish, linuxthreads
+  were leaving zombies.
+. missing initialiser for relay connection, caused segv on failed
+  connections
+. fix 3 mp3 metadata mis-alignment bugs on stalled links
+
+kh11
+. fix a bad memory reference from kh9
+. don't free finished clients too early, bad memory reference
+
+kh10
+. when reading from straight mp3 stream (no shoutcast metadata)
+  updates via url were not getting to clients. fixed
+
+kh9
+. per-mount queue and burst size options
+. pre-mount on-connect/on-disconnect scripts
+. more re-sync with cvs updates
+. force relay re-check on xml update
+
+kh8
+. timeout value had multiple applied
+. more dead code removal
+. make initial mp3 metadata block blank
+. fix potential leak in mp3 reading code.
+
+kh7
+. added stream type check for client moving
+. various cleanups, dead code removal
+. minor relay structure mem leak fix
+. fix source count check.
+. enable the fileserve thread again
+
+kh6
+. stream dumping added
+. mp3 title update via url fixed up
+. fixed up listclients, moveclients.
+. re-implemented source fallbacks
+
+kh5
+. 2 lots of ;; caused compile problems on windows
+. enable curl for YP access
+. allow the format specific get buffer routine to return
+  NULL for retry, needs to set running to 0 now
+. add vorbis header parsing for artist title stats
+. add mp3 title parsing for the stats
+
+kh4
+. handle relay start and shutdown better.
+. apply avl rwlock leak fix
+. apply stats thread sleep avoidance fix
+. update sock.c errno check
+. enable stats/YP thread, add various stat triggers
+
+kh3
+. fix burst size larger than queue size case
+. prevent relay connections initiating if connection is running
+. send server package string from autoconf
+. fixed a few segvs from previous update
+
+kh2
+. fix mp3 handling, metadata handling wasn't correct.
+. fix flow control, locks/source counts with relay
+. Added <burst-size> tag default 0 (in bytes)
+. Use queue size field, was hardcoded before
+. Don't filter refbuf if 0 length but have associated data
+. handle EOF/error from socket in format_mp3
+
+kh1 - core update
+. removed many locks
+. used single queue for source data.
+. clients start at end of queue and allowed to repeat writes, thus
+  giving bursts at connection. capped at 8 writes
+. many secondary things not working. stream dumping, YP, stats,
+  web interface
+. mp3 incl shoutcast meta and ogg should work
+
2003-10-12
Added documentation


Modified: icecast/branches/kh/icecast/README
===================================================================
--- icecast/trunk/icecast/README	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/README	2004-07-11 18:09:05 UTC (rev 7095)
@@ -1,41 +1,8 @@
-icecast 2.x - README
----------------------------------------------------------------------
+Icecast2 Beta 1.

-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.
+This is an beta release. Not all functionality is fully implemented, though
+we believe it to be very stable for all the currently available features.

-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
@@ -45,11 +12,9 @@

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
+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.
+Documentation for icecast is available in the doc directory, by viewing doc/icecast2_TOC.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.

Modified: icecast/branches/kh/icecast/TODO
===================================================================
--- icecast/trunk/icecast/TODO	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/TODO	2004-07-11 18:09:05 UTC (rev 7095)
@@ -1,13 +1,33 @@
+2.0 CRITICAL - These are the things without which 2.0 cannot be released
+____________
+
+- Should icecast automatically (i.e. without needing -c) look for the config
+  file in /etc/icecast.xml or something?
+
+- libshout 2.0 and ices 2.0 releases, also an ices 0.x release that works with
+  this. Without source clients, icecast isn't much use...
+
+- integrate/include all the documentation done by external groups.
+
+- generally we don't do proper checking for the correct versions of various
+  libraries (this is probably more of an issue with ices2, but it also affects
+  icecast)
+
BUGS
----
+- stats get off?  this needs testing more testing.
+
+- some stuff (like 'genre') isn't making it into the stats dump
+
- logging - bytes send and time listening may both be broken?

+- slave servers don't work. relay user is not respected by the source (only
+  admin can read /admin/streamlist), and the slave can't parse the xml result
+  of streamlist anyway (it expects a simple mountpoint per line)
+
FEATURES
--------

-- Should icecast automatically (i.e. without needing -c) look for the config
-  file in /etc/icecast.xml or something?
-
- pull out vorbis comments.  and send to stats. This seems to be being
done, but it isn't working right.

@@ -27,6 +47,17 @@

- stats to list currently connected clients: ip and hostname

+- stream switching (drop clients to another stream on disconnect of source)
+  - a) fallbacks from named location to new mountpoint
+  - OR b) fallbacks for connected clients to new mountpoint (so newly-connecting
+       clients just get a 404 on the old path)
+  - OR c) combination - first one, plus generic alias ability?
+
+- /admin/* for all admin functionality
+  - configuring fallbacks
+  - mp3 metadata injection
+  - remote shutdown?
+
- general registerable url-handlers in connection.c rather than hard-coded list
(already getting unmaintainable)

@@ -39,11 +70,16 @@
commands through that.
Use this for alternative admin interfaces (GUI? telnet interface?)

+- listener authentication (per mountpoint?)
+
- all timer-based functionality (yp updates, slave/relay checks) should have a
single timer thread which dispatches events through the normal event
mechanism (to worker threads from the main pool). This will reduce the
extraneous thread count.

+- atomic admin function to: set fallback from A->B, remove A, move mountpoint
+  B to A. Needs forced-source removal first.
+
- race condition between avl_tree_unlock(pending_tree) and
thread_cond_wait(&fserv_cond) in fserv.c, it's a pain to fix but should be.


Modified: icecast/branches/kh/icecast/conf/icecast.xml.in
===================================================================
--- icecast/trunk/icecast/conf/icecast.xml.in	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/conf/icecast.xml.in	2004-07-11 18:09:05 UTC (rev 7095)
@@ -7,12 +7,8 @@
<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, so it's disabled
-             by default -->
-        <burst-on-connect>0</burst-on-connect>
+        <!-- global setting for burst on connection, default is 64k for all sources -->
+        <burst-size>65535</burst-size>
</limits>

<authentication>
@@ -38,9 +34,6 @@
</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 listener -->
@@ -62,12 +55,18 @@
<!--<master-server-port>8001</master-server-port>-->
<!--<master-update-interval>120</master-update-interval>-->
<!--<master-password>hackme</master-password>-->
+
+    <!-- Relays. State connection information, and by default
+         request inline metadata for mp3 streams if available.
+         An on-demand relay will only retrieve the stream if
+         there are listeners connected -->
<!--
<relay>
<server>127.0.0.1</server>
<port>8001</port>
<mount>/example.ogg</mount>
<local-mount>/different.ogg</local-mount>
+        <on-demand>1</on-demand>

<relay-shoutcast-metadata>0</relay-shoutcast-metadata>
</relay>
@@ -83,18 +82,48 @@

<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>
+        <no-yp>1</no-yp>
<authentication type="htpasswd">
-                <option name="filename" value="myauth"/>
-                <option name="allow_duplicate_users" value="0"/>
+            <option name="filename" value="myauth"/>
</authentication>
</mount>
-->
+    <!-- other auth possibilities include running a command
+         to do the auth, mount, user and pass are passed via
+         stdin to the program
+    <mount>
+    ....
+        <authentication type="command">
+             <option name="filename" value="auth_verify"/>
+        </authentication>
+
+        or

+        for url auth, the add url needs to return a "icecast-auth-user: 1" http
+        header for a user to authenicate. Both urls are sent params via POST,
+        add is sent id, mount, user, pass, ip, useragent
+        remove is passed id, mount, user, pass, duration
+
+        <authentication type="url">
+
+             state username/password if url requires it
+
+             <option name="username" value="admin"/>
+             <option name="password" value="hackme"/>
+             <option name="add"    value="http://myauthserver.com/scripts/add_listener.php"/>
+             <option name="remove" value="http://myauthserver.com/scripts/del_listener.php"/>
+        </authentication>
+    </mount -->
+
+
<fileserve>1</fileserve>

<paths>
-		<!-- basedir is only used if chroot is enabled -->
+        <!-- basedir is only used if chroot is enabled -->
<basedir>@pkgdatadir@</basedir>

<!-- Note that if <chroot> is turned on below, these paths must both

Modified: icecast/branches/kh/icecast/configure.in
===================================================================
--- icecast/trunk/icecast/configure.in	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/configure.in	2004-07-11 18:09:05 UTC (rev 7095)
@@ -1,4 +1,4 @@
-AC_INIT([Icecast], [2.0.0], [icecast at xiph.org])
+AC_INIT([Icecast], [2.0-kh47], [karl at xiph.org])

AC_PREREQ(2.54)
AC_CONFIG_SRCDIR(src/main.c)
@@ -34,7 +34,8 @@
esac

case "$host" in
-        *openbsd* | *irix*)
+        # for system header breakage
+        *bsd* | *irix*)
;;
*) AC_DEFINE([_XOPEN_SOURCE], 600, [Define if you have POSIX and XPG specifications])
;;
@@ -63,8 +64,8 @@

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]))
+AC_SEARCH_LIBS(nanosleep, rt posix4,
+        AC_DEFINE(HAVE_NANOSLEEP, 1, [Define if you have nanosleep]))
XIPH_NET

dnl -- configure options --
@@ -77,33 +78,65 @@
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])

+XIPH_PATH_THEORA(, AC_MSG_WARN([Theora support disabled!]))
+XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$THEORA_CFLAGS])
+XIPH_VAR_PREPEND([XIPH_LIBS],[$THEORA_LIBS])
+
+XIPH_PATH_SPEEX(, AC_MSG_WARN([Speex support disabled!]))
+XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$SPEEX_CFLAGS])
+XIPH_VAR_PREPEND([XIPH_LIBS],[$SPEEX_LIBS])
+
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])

-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"
-then
XIPH_PATH_CURL([
AC_CHECK_DECL([CURLOPT_NOSIGNAL],
-        [ AC_DEFINE([USE_YP], 1, [Define to compile in YP support code])
-        ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o"
+        [ 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), YP disabled])
+        ], [ 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, YP disabled])
+    ],[ 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
+# don't log ip's in the access log
+AC_ARG_ENABLE([log-ip],
+        AC_HELP_STRING([--disable-log-ip],[disable logging of IP's in access log]),
+        enable_logging_ip="$enableval",
+        enable_logging_ip="yes"
+        )
+if test x$enable_logging_ip = xyes; then
+        AC_DEFINE([HAVE_LOGGING_IP],,[Define to log IP to access log])
+fi

+# use the older format_vorbis with metadata updates
+AC_ARG_ENABLE([vorbis-updates],
+        AC_HELP_STRING([--enable-vorbis-updates],[Allow for metadata via URL, only vorbis supported]),
+        vorbis_updates="$enableval",
+        vorbis_updates="no"
+        )
+if test x$vorbis_updates = xyes; then
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_vorbis.o"
+else
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_ogg.o"
+fi
+
dnl Make substitutions

AC_SUBST(XIPH_CPPFLAGS)

Modified: icecast/branches/kh/icecast/doc/icecast2_config_file.html
===================================================================
--- icecast/trunk/icecast/doc/icecast2_config_file.html	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/doc/icecast2_config_file.html	2004-07-11 18:09:05 UTC (rev 7095)
@@ -31,7 +31,6 @@
&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;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.
@@ -64,10 +63,6 @@
<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>
<br>
<br>
<br>
@@ -273,7 +268,6 @@
&lt;fallback-mount&gt;example2.ogg&lt;fallback-mount&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;
@@ -306,7 +300,7 @@
</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>.
+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.
</div>
<br>
<br>

Modified: icecast/branches/kh/icecast/src/Makefile.am
===================================================================
--- icecast/trunk/icecast/src/Makefile.am	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/Makefile.am	2004-07-11 18:09:05 UTC (rev 7095)
@@ -6,13 +6,14 @@

bin_PROGRAMS = icecast

-noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h global.h\
-	 util.h slave.h source.h stats.h refbuf.h client.h format.h format_vorbis.h\
-	 compat.h format_mp3.h fserve.h xslt.h yp.h event.h auth.h md5.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 format.c format_vorbis.c\
-	 format_mp3.c xslt.c fserve.c event.c admin.c auth.c md5.c
-EXTRA_icecast_SOURCES = yp.c
+noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h \
+	global.h util.h slave.h source.h stats.h refbuf.h client.h format.h \
+	format_ogg.h format_vorbis.h compat.h format_mp3.h fserve.h xslt.h yp.h \
+	event.h auth.h auth_htpasswd.h auth_cmd.h auth_url.h md5.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 format.c format_mp3.c \
+     xslt.c fserve.c event.c admin.c auth.c auth_htpasswd.c auth_cmd.c md5.c
+EXTRA_icecast_SOURCES = yp.c format_vorbis.c format_ogg.c auth_url.c

icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \
httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la

Modified: icecast/branches/kh/icecast/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/admin.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -34,7 +34,6 @@
#include "xslt.h"

#include "format.h"
-#include "format_mp3.h"

#include "logging.h"
#include "auth.h"
@@ -104,6 +103,7 @@
#define RAW         1
#define TRANSFORMED 2
#define PLAINTEXT   3
+
int admin_get_command(char *command)
{
if(!strcmp(command, FALLBACK_RAW_REQUEST))
@@ -140,14 +140,14 @@
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, BUILDM3U_RAW_REQUEST))
+        return COMMAND_BUILDM3U;
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_MANAGEAUTH;
-    else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
-        return COMMAND_BUILDM3U;
+    else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_KILL_SOURCE;
else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_STATS;
else if(!strcmp(command, DEFAULT_RAW_REQUEST))
@@ -199,26 +199,32 @@
node = avl_get_first(global.source_tree);
while(node) {
source = (source_t *)node->key;
-        if (source->running)
+        thread_mutex_lock (&source->lock);
+        if (source->running || source->on_demand)
{
-            srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
-            xmlSetProp(srcnode, "mount", source->mount);
+            srcnode = xmlNewChild (xmlnode, NULL, "source", NULL);
+            xmlSetProp (srcnode, "mount", source->mount);

-            xmlNewChild(srcnode, NULL, "fallback",
+            xmlNewChild (srcnode, NULL, "fallback",
(source->fallback_mount != NULL)?
source->fallback_mount:"");
-            snprintf(buf, sizeof(buf), "%ld", source->listeners);
-            xmlNewChild(srcnode, NULL, "listeners", buf);
-            snprintf(buf, sizeof(buf), "%lu",
-                    (unsigned long)(now - source->con->con_time));
-            xmlNewChild(srcnode, NULL, "Connected", buf);
-            xmlNewChild(srcnode, NULL, "Format",
-                    source->format->format_description);
-            if (source->authenticator) {
-                xmlNewChild(srcnode, NULL, "authenticator",
-                    source->authenticator->type);
+            snprintf (buf, sizeof(buf), "%ld", source->listeners);
+            xmlNewChild (srcnode, NULL, "listeners", buf);
+            if (source->running)
+            {
+                snprintf (buf, sizeof(buf), "%lu",
+                        (unsigned long)(now - source->con->con_time));
+                xmlNewChild (srcnode, NULL, "Connected", buf);
+                xmlNewChild (srcnode, NULL, "Format",
+                        source->format->format_description);
+                if (source->authenticator)
+                {
+                    xmlNewChild(srcnode, NULL, "authenticator",
+                            source->authenticator->type);
+                }
}
}
+        thread_mutex_unlock (&source->lock);
node = avl_get_next(node);
}
return(doc);
@@ -301,7 +307,7 @@
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);
+                            "request (command: %s)", command_string);
client_send_401(client);
return;
}
@@ -316,7 +322,7 @@
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");
+            client_send_400 (client, "Source does not exist");
}
else
{
@@ -330,16 +336,15 @@
}
INFO2("Received admin command %s on mount \"%s\"",
command_string, mount);
-            admin_handle_mount_request(client, source, command);
+            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 */
+            /* 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);
@@ -348,7 +353,7 @@
}
}
else {
-            if(!connection_check_admin_pass(client->parser)) {
+            if(!connection_check_admin_pass (client->parser)) {
INFO1("Bad or missing password on admin command "
"request (command: %s)", command_string);
client_send_401(client);
@@ -492,11 +497,9 @@
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,
@@ -520,12 +523,14 @@
return;
}

-    if (dest->running == 0)
+    if (dest->running == 0 && dest->on_demand == 0)
{
client_send_400 (client, "Destination not running");
return;
}

+    DEBUG2("source is \"%s\", destination is \"%s\"", source->mount, dest->mount);
+
doc = xmlNewDoc("1.0");
node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
xmlDocSetRootElement(doc, node);
@@ -548,16 +553,19 @@
int response)
{
xmlDocPtr doc;
-    xmlNodePtr node, srcnode, listenernode;
-    avl_node *client_node;
+    xmlNodePtr node, srcnode;
+    char *userAgent = NULL;
+    xmlNodePtr listenernode;
client_t *current;
+    time_t now = time(NULL);
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);
+
+    thread_mutex_lock (&source->lock);
+
xmlSetProp(srcnode, "mount", source->mount);
xmlDocSetRootElement(doc, node);

@@ -565,11 +573,9 @@
snprintf(buf, sizeof(buf)-1, "%ld", 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;
+    current = source->active_clients;
+    while (current)
+    {
listenernode = xmlNewChild(srcnode, NULL, "listener", NULL);
xmlNewChild(listenernode, NULL, "IP", current->con->ip);
userAgent = httpp_getvar(current->parser, "user-agent");
@@ -585,13 +591,14 @@
memset(buf, '\000', sizeof(buf));
snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
xmlNewChild(listenernode, NULL, "ID", buf);
-        if (current->username) {
+        if (current->username)
xmlNewChild(listenernode, NULL, "username", current->username);
-        }
-        client_node = avl_get_next(client_node);
+
+        current = current->next;
}

-    avl_tree_unlock(source->client_tree);
+    thread_mutex_unlock (&source->lock);
+
admin_send_response(doc, client, response,
LISTCLIENTS_TRANSFORMED_REQUEST);
xmlFreeDoc(doc);
@@ -631,8 +638,10 @@
free(host);
client_destroy(client);
}
+
+
static void command_manageauth(client_t *client, source_t *source,
-    int response)
+        int response)
{
xmlDocPtr doc;
xmlNodePtr node, srcnode, msgnode;
@@ -646,7 +655,7 @@
if (!strcmp(action, "add")) {
COMMAND_REQUIRE(client, "username", username);
COMMAND_REQUIRE(client, "password", password);
-            ret = auth_adduser(source, username, password);
+            ret = source->authenticator->adduser(source->authenticator, username, password);
if (ret == AUTH_FAILED) {
message = strdup("User add failed - check the icecast error log");
}
@@ -659,7 +668,7 @@
}
if (!strcmp(action, "delete")) {
COMMAND_REQUIRE(client, "username", username);
-            ret = auth_deleteuser(source, username);
+            ret = source->authenticator->deleteuser(source->authenticator, username);
if (ret == AUTH_FAILED) {
message = strdup("User delete failed - check the icecast error log");
}
@@ -681,10 +690,10 @@

xmlDocSetRootElement(doc, node);

-    auth_get_userlist(source, srcnode);
+    source->authenticator->listuser(source->authenticator, srcnode);

admin_send_response(doc, client, response,
-        MANAGEAUTH_TRANSFORMED_REQUEST);
+            MANAGEAUTH_TRANSFORMED_REQUEST);
if (message) {
free(message);
}
@@ -692,8 +701,9 @@
client_destroy(client);
}

+
static void command_kill_source(client_t *client, source_t *source,
-    int response)
+        int response)
{
xmlDocPtr doc;
xmlNodePtr node;
@@ -726,6 +736,7 @@

id = atoi(idtext);

+    thread_mutex_lock (&source->lock);
listener = source_find_client(source, id);

doc = xmlNewDoc("1.0");
@@ -751,6 +762,7 @@
xmlNewChild(node, NULL, "message", buf);
xmlNewChild(node, NULL, "return", "0");
}
+    thread_mutex_unlock (&source->lock);
admin_send_response(doc, client, response,
ADMIN_XSL_RESPONSE);
xmlFreeDoc(doc);
@@ -767,9 +779,11 @@

COMMAND_REQUIRE(client, "fallback", fallback);

+    thread_mutex_lock (&source->lock);
old = source->fallback_mount;
source->fallback_mount = strdup(fallback);
free(old);
+    thread_mutex_unlock (&source->lock);

html_success(client, "Fallback configured");
}
@@ -777,43 +791,51 @@
static void command_metadata(client_t *client, source_t *source)
{
char *action;
-    char *value;
-    mp3_state *state;
+    char *song, *title, *artist;
+    format_plugin_t *plugin;

DEBUG0("Got metadata update request");

COMMAND_REQUIRE(client, "mode", action);
-    COMMAND_REQUIRE(client, "song", value);
+    COMMAND_OPTIONAL(client, "song", song);
+    COMMAND_OPTIONAL(client, "title", title);
+    COMMAND_OPTIONAL(client, "artist", artist);

-    if (source->format->type != FORMAT_TYPE_MP3)
+    if (strcmp(action, "updinfo") != 0)
{
-        client_send_400 (client, "Not mp3, cannot update metadata");
+        client_send_400(client, "No such action");
return;
}

-    if (strcmp (action, "updinfo") != 0)
+    thread_mutex_lock (&source->lock);
+
+    plugin = source->format;
+    if (plugin && plugin->set_tag)
{
-        client_send_400 (client, "No such action");
-        return;
+        if (song)
+        {
+            plugin->set_tag (plugin, "title", song);
+            INFO2("Metadata on mountpoint %s changed to \"%s\"", source->mount, song);
+        }
+        else
+        {
+            if (artist && title)
+            {
+                plugin->set_tag (plugin, "artist", artist);
+                plugin->set_tag (plugin, "title", title);
+                INFO3("Metadata on mountpoint %s changed to \"%s - %s\"",
+                        source->mount, artist, title);
+            }
+        }
+
+        thread_mutex_unlock (&source->lock);
+        html_success(client, "Metadata update successful");
}
-
-    state = source->format->_state;
-
-    thread_mutex_lock(&(state->lock));
-    free(state->metadata);
-    state->metadata = strdup(value);
-    state->metadata_age++;
-    thread_mutex_unlock(&(state->lock));
-
-    DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
-        source->mount, value);
-    stats_event(source->mount, "title", value);
-
-    /* If we get an update on the mountpoint, force a
-       yp touch */
-    yp_touch (source->mount);
-
-    html_success(client, "Metadata update successful");
+    else
+    {
+        thread_mutex_unlock (&source->lock);
+        client_send_400 (client, "source will not accept URL updates");
+    }
}

static void command_stats(client_t *client, int response) {
@@ -830,9 +852,6 @@

static void command_list_mounts(client_t *client, int response)
{
-    avl_node *node;
-    source_t *source;
-
DEBUG0("List mounts request");

avl_tree_rlock (global.source_tree);
@@ -843,18 +862,18 @@
int ret = sprintf (buffer,
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");

-        node = avl_get_first(global.source_tree);
-        while (node && ret > 0 && ret < remaining)
+        avl_node *node = avl_get_first(global.source_tree);
+        while (node && ret > 0 && (unsigned)ret < remaining)
{
+            source_t *source = (source_t *)node->key;
remaining -= ret;
buf += ret;
-            source = (source_t *)node->key;
ret = snprintf (buf, remaining, "%s\n", source->mount);
node = avl_get_next(node);
}
avl_tree_unlock (global.source_tree);
/* handle last line */
-        if (ret > 0 && ret < remaining)
+        if (ret > 0 && (unsigned)ret < remaining)
{
remaining -= ret;
buf += ret;
@@ -867,7 +886,7 @@
avl_tree_unlock (global.source_tree);

admin_send_response(doc, client, response,
-            LISTMOUNTS_TRANSFORMED_REQUEST);
+                LISTMOUNTS_TRANSFORMED_REQUEST);
xmlFreeDoc(doc);
}
client_destroy(client);

Modified: icecast/branches/kh/icecast/src/auth.c
===================================================================
--- icecast/trunk/icecast/src/auth.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/auth.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -24,6 +24,9 @@
#include <stdio.h>

#include "auth.h"
+#include "auth_htpasswd.h"
+#include "auth_cmd.h"
+#include "auth_url.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
@@ -34,30 +37,7 @@
#define CATMODULE "auth"


-int auth_is_listener_connected(source_t *source, char *username)
-{
-    client_t *client;
-    avl_node *client_node;

-    avl_tree_rlock(source->client_tree);
-
-    client_node = avl_get_first(source->client_tree);
-    while(client_node) {
-        client = (client_t *)client_node->key;
-        if (client->username) {
-            if (!strcmp(client->username, username)) {
-                avl_tree_unlock(source->client_tree);
-                return 1;
-            }
-        }
-        client_node = avl_get_next(client_node);
-    }
-
-    avl_tree_unlock(source->client_tree);
-    return 0;
-
-}
-
auth_result auth_check_client(source_t *source, client_t *client)
{
auth_t *authenticator = source->authenticator;
@@ -94,26 +74,54 @@
username = userpass;
password = tmp+1;

-        result = authenticator->authenticate(
-                authenticator, source, username, password);
+        client->username = strdup (username);
+        client->password = strdup (password);

-        if(result == AUTH_OK)
-            client->username = strdup(username);
+        result = authenticator->authenticate (source, client);

free(userpass);

return result;
}
else
-        return AUTH_FAILED;
+    {
+        /* just add the client */
+        add_authenticated_client (source, client);
+        return AUTH_OK;
+    }
}

-static auth_t *auth_get_htpasswd_auth(config_options_t *options);

+void auth_clear(auth_t *authenticator)
+{
+    if (authenticator == NULL)
+        return;
+    authenticator->free (authenticator);
+    free (authenticator->type);
+    free (authenticator);
+}
+
+
auth_t *auth_get_authenticator(char *type, config_options_t *options)
{
auth_t *auth = NULL;
-    if(!strcmp(type, "htpasswd")) {
+#ifdef HAVE_AUTH_URL
+    if(!strcmp(type, "url")) {
+        auth = auth_get_url_auth(options);
+        auth->type = strdup(type);
+    }
+    else
+#endif
+    if(!strcmp(type, "command")) {
+#ifdef WIN32
+        ERROR1("Authenticator type: \"%s\" not supported on win32 platform", type);
+        return NULL;
+#else
+        auth = auth_get_cmd_auth(options);
+        auth->type = strdup(type);
+#endif
+    }
+    else if(!strcmp(type, "htpasswd")) {
auth = auth_get_htpasswd_auth(options);
auth->type = strdup(type);
}
@@ -128,369 +136,40 @@
return auth;
}

-typedef struct {
-    char *filename;
-    int allow_duplicate_users;
-    rwlock_t file_rwlock;
-} htpasswd_auth_state;

-static void htpasswd_clear(auth_t *self) {
-    htpasswd_auth_state *state = self->state;
-    free(state->filename);
-    thread_rwlock_destroy(&state->file_rwlock);
-    free(state);
-    free(self->type);
-    free(self);
-}
-
-static int get_line(FILE *file, char *buf, int len)
+/* place authenticated client on named source */
+int auth_postprocess_client (const char *mount, client_t *client)
{
-    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;
+    int ret = -1;
+    source_t *source;
+    avl_tree_unlock (global.source_tree);
+    source = source_find_mount (mount);
+    if (source)
+    {
+        ret = 0;
+        thread_mutex_lock (&source->lock);
+        if (source->running)
+            add_authenticated_client (source, client);
+        else
+            ret = -1;
+        thread_mutex_unlock (&source->lock);
}
-    return 0;
-}
-
-/* md5 hash */
-static char *get_hash(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
-
-/* Not efficient; opens and scans the entire file for every request */
-static auth_result htpasswd_auth(auth_t *auth, source_t *source, char *username, char *password)
-{
-    htpasswd_auth_state *state = auth->state;
-    FILE *passwdfile = NULL;
-    char line[MAX_LINE_LEN];
-    char *sep;
-
-    thread_rwlock_rlock(&state->file_rwlock);
-    if (!state->allow_duplicate_users) {
-        if (auth_is_listener_connected(source, username)) {
-            thread_rwlock_unlock(&state->file_rwlock);
-            return AUTH_FORBIDDEN;
-        }
-    }
-    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;
-    }
-
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
-
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No seperator in line");
-            continue;
-        }
-
-        *sep = 0;
-        if(!strcmp(username, line)) {
-            /* Found our user, now: does the hash of password match hash? */
-            char *hash = sep+1;
-            char *hashed_password = get_hash(password, strlen(password));
-            if(!strcmp(hash, hashed_password)) {
-                fclose(passwdfile);
-                free(hashed_password);
-                thread_rwlock_unlock(&state->file_rwlock);
-                return AUTH_OK;
-            }
-            free(hashed_password);
-            /* We don't keep searching through the file */
-            break;
-        }
-    }
-
-    fclose(passwdfile);
-
-    thread_rwlock_unlock(&state->file_rwlock);
-    return AUTH_FAILED;
-}
-
-static auth_t *auth_get_htpasswd_auth(config_options_t *options)
-{
-    auth_t *authenticator = calloc(1, sizeof(auth_t));
-    htpasswd_auth_state *state;
-
-    authenticator->authenticate = htpasswd_auth;
-    authenticator->free = htpasswd_clear;
-
-    state = calloc(1, sizeof(htpasswd_auth_state));
-
-    state->allow_duplicate_users = 1;
-    while(options) {
-        if(!strcmp(options->name, "filename"))
-            state->filename = strdup(options->value);
-        if(!strcmp(options->name, "allow_duplicate_users"))
-            state->allow_duplicate_users = atoi(options->value);
-        options = options->next;
-    }
-
-    if(!state->filename) {
-        free(state);
-        free(authenticator);
-        ERROR0("No filename given in options for authenticator.");
-        return NULL;
-    }
-
-    authenticator->state = state;
-    DEBUG1("Configured htpasswd authentication using password file %s",
-            state->filename);
-
-    thread_rwlock_create(&state->file_rwlock);
-
-    return authenticator;
-}
-
-int auth_htpasswd_existing_user(auth_t *auth, char *username)
-{
-    FILE *passwdfile;
-    htpasswd_auth_state *state;
-    int ret = AUTH_OK;
-    char line[MAX_LINE_LEN];
-    char *sep;
-
-    state = auth->state;
-    passwdfile = fopen(state->filename, "rb");
-
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s",
-                state->filename, strerror(errno));
-        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 seperator in line");
-            continue;
-        }
-        *sep = 0;
-        if (!strcmp(username, line)) {
-            /* We found the user, break out of the loop */
-            ret = AUTH_USEREXISTS;
-            break;
-        }
-    }
-
-    fclose(passwdfile);
+    avl_tree_unlock (global.source_tree);
+    if (ret < 0)
+        source_free_client (NULL, client);
return ret;
-
}
-int auth_htpasswd_adduser(auth_t *auth, char *username, char *password)
-{
-    FILE *passwdfile;
-    char *hashed_password = NULL;
-    htpasswd_auth_state *state;

-    if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) {
-        return AUTH_USEREXISTS;
-    }
-    state = auth->state;
-    passwdfile = fopen(state->filename, "ab");

-    if(passwdfile == NULL) {
-        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);
-    return AUTH_USERADDED;
-}
-
-int auth_adduser(source_t *source, char *username, char *password)
+void auth_close_client (client_t *client)
{
-    int ret = 0;
-    htpasswd_auth_state *state;
-
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_wlock(&state->file_rwlock);
-            ret = auth_htpasswd_adduser(source->authenticator, username, password);
-            thread_rwlock_unlock(&state->file_rwlock);
-        }
-    }
-    return ret;
+    /* failed client, drop global count */
+    global_lock();
+    global.clients--;
+    global_unlock();
+    if (client->respcode)
+        client_destroy (client);
+    else
+        client_send_401 (client);
}

-int auth_htpasswd_deleteuser(auth_t *auth, 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;
-
-    state = auth->state;
-    passwdfile = fopen(state->filename, "rb");
-
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s",
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
-    }
-    tmpfile_len = strlen(state->filename) + 6;
-    tmpfile = calloc(1, tmpfile_len);
-    sprintf(tmpfile, ".%s.tmp", state->filename);
-
-    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);
-        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 seperator 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 */
-#ifdef _WIN32
-    /* 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 {
-#endif
-        if (rename(tmpfile, state->filename) != 0) {
-            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
-                tmpfile, state->filename, strerror(errno));
-	}
-#ifdef _WIN32
-    }
-#endif
-
-    free(tmpfile);
-
-    return AUTH_USERDELETED;
-}
-int auth_deleteuser(source_t *source, char *username)
-{
-    htpasswd_auth_state *state;
-
-    int ret = 0;
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_wlock(&state->file_rwlock);
-            ret = auth_htpasswd_deleteuser(source->authenticator, username);
-            thread_rwlock_unlock(&state->file_rwlock);
-        }
-    }
-    return ret;
-}
-
-int auth_get_htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
-{
-    htpasswd_auth_state *state;
-    FILE *passwdfile;
-    char line[MAX_LINE_LEN];
-    char *sep;
-    char *passwd;
-    xmlNodePtr newnode;
-
-    state = auth->state;
-
-    passwdfile = fopen(state->filename, "rb");
-
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s",
-                state->filename, strerror(errno));
-        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 seperator in line");
-            continue;
-        }
-
-        *sep = 0;
-        newnode = xmlNewChild(srcnode, NULL, "User", NULL);
-        xmlNewChild(newnode, NULL, "username", line);
-        passwd = sep+1;
-        xmlNewChild(newnode, NULL, "password", passwd);
-    }
-
-    fclose(passwdfile);
-    return AUTH_OK;
-}
-
-int auth_get_userlist(source_t *source, xmlNodePtr srcnode)
-{
-    int ret = 0;
-    htpasswd_auth_state *state;
-
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_rlock(&state->file_rwlock);
-            ret = auth_get_htpasswd_userlist(source->authenticator, srcnode);
-            thread_rwlock_unlock(&state->file_rwlock);
-        }
-    }
-    return ret;
-}
-

Modified: icecast/branches/kh/icecast/src/auth.h
===================================================================
--- icecast/trunk/icecast/src/auth.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/auth.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -24,7 +24,6 @@
{
AUTH_OK,
AUTH_FAILED,
-    AUTH_FORBIDDEN,
AUTH_USERADDED,
AUTH_USEREXISTS,
AUTH_USERDELETED,
@@ -33,9 +32,14 @@
typedef struct auth_tag
{
/* Authenticate using the given username and password */
-    auth_result (*authenticate)(struct auth_tag *self,
-            source_t *source, char *username, char *password);
+    auth_result (*authenticate)(source_t *source, client_t *client);
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);
+    void (*release_client)(struct source_tag *source, client_t *client);
+    int (*checkuser)(source_t *source, client_t *client);
+
void *state;
void *type;
} auth_t;
@@ -43,10 +47,9 @@
auth_result auth_check_client(source_t *source, client_t *client);

auth_t *auth_get_authenticator(char *type, config_options_t *options);
-void *auth_clear(auth_t *authenticator);
-int auth_get_userlist(source_t *source, xmlNodePtr srcnode);
-int auth_adduser(source_t *source, char *username, char *password);
-int auth_deleteuser(source_t *source, char *username);
+void auth_clear(auth_t *authenticator);
+int auth_postprocess_client (const char *mount, client_t *client);
+void auth_close_client (client_t *client);

#endif


Modified: icecast/branches/kh/icecast/src/cfgfile.c
===================================================================
--- icecast/trunk/icecast/src/cfgfile.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/cfgfile.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -32,6 +32,7 @@
#define CONFIG_DEFAULT_CLIENT_LIMIT 256
#define CONFIG_DEFAULT_SOURCE_LIMIT 16
#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (100*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
@@ -83,9 +84,9 @@
static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);

static void create_locks() {
-    thread_mutex_create(&_locks.relay_lock);
-    thread_mutex_create(&_locks.mounts_lock);
-    thread_mutex_create(&_locks.config_lock);
+    thread_mutex_create("relay lock", &_locks.relay_lock);
+    thread_mutex_create("mounts lock", &_locks.mounts_lock);
+    thread_mutex_create("config lock", &_locks.config_lock);
}

static void release_locks() {
@@ -157,6 +158,7 @@
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);
@@ -181,6 +183,8 @@
xmlFree(mount->username);
xmlFree(mount->password);
xmlFree(mount->dumpfile);
+        xmlFree(mount->on_connect);
+        xmlFree(mount->on_disconnect);
xmlFree(mount->fallback_mount);

xmlFree(mount->auth_type);
@@ -217,7 +221,7 @@
}
#ifdef HAVE_YP
i = 0;
-    while (i < c->num_yp_directories)
+    while (i < c->num_yp_directories)
{
xmlFree (c->yp_url[i]);
i++;
@@ -308,6 +312,7 @@
configuration->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
configuration->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
configuration->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
+    configuration->burst_size_limit = CONFIG_DEFAULT_BURST_SIZE;
configuration->threadpool_size = CONFIG_DEFAULT_THREADPOOL_SIZE;
configuration->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
@@ -324,6 +329,7 @@
configuration->master_server = NULL;
configuration->master_server_port = 0;
configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
+    configuration->master_username = NULL;
configuration->master_password = NULL;
configuration->base_dir = CONFIG_DEFAULT_BASE_DIR;
configuration->log_dir = CONFIG_DEFAULT_LOG_DIR;
@@ -339,7 +345,6 @@
configuration->num_yp_directories = 0;
configuration->relay_username = NULL;
configuration->relay_password = NULL;
-    configuration->burst_on_connect = 0;
}

static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
@@ -395,6 +400,9 @@
} 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);
@@ -441,6 +449,10 @@
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->source_limit = atoi(tmp);
if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "burst-size") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->burst_size_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);
@@ -461,10 +473,6 @@
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);
-            configuration->burst_on_connect = atoi(tmp);
-            if (tmp) xmlFree(tmp);
}
} while ((node = node->next));
}
@@ -516,6 +524,11 @@
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);
@@ -531,6 +544,11 @@
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->no_yp = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
else if (strcmp(node->name, "authentication") == 0) {
mount->auth_type = xmlGetProp(node, "type");
option = node->xmlChildrenNode;
@@ -562,11 +580,27 @@
option = option->next;
}
}
+        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, "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, "burst-size") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if (tmp)
+            {
+                mount->burst_size = atoi (tmp);
+                xmlFree(tmp);
+            }
+        }
else if (strcmp(node->name, "source-timeout") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if (tmp)
@@ -625,6 +659,19 @@
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);
@@ -734,7 +781,8 @@
_add_server(doc, node->xmlChildrenNode, configuration);
} else if (strcmp(node->name, "touch-interval") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
-            configuration->touch_interval = atoi(tmp);
+            configuration->yp_touch_interval[configuration->num_yp_directories]
+                = atoi(tmp);
if (tmp) xmlFree(tmp);
}
} while ((node = node->next));

Modified: icecast/branches/kh/icecast/src/cfgfile.h
===================================================================
--- icecast/trunk/icecast/src/cfgfile.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/cfgfile.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -46,6 +46,8 @@

char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
to not dump. */
+    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 */
@@ -54,11 +56,16 @@
clients from the fallback? */
int no_mount; /* Do we permit direct requests of this mountpoint? (or only
indirect, through fallbacks) */
+    int no_yp; /* Do we prevent YP on this mount */
unsigned queue_size_limit;
unsigned source_timeout;  /* source timeout in seconds */
+    unsigned burst_size;

char *auth_type; /* Authentication type */
config_options_t *auth_options; /* Options for this type */
+    char *on_connect;
+    char *on_disconnect;
+
struct _mount_proxy *next;
} mount_proxy;

@@ -85,6 +92,7 @@
int client_limit;
int source_limit;
unsigned queue_size_limit;
+    unsigned burst_size_limit;
int threadpool_size;
int client_timeout;
int header_timeout;
@@ -109,6 +117,7 @@
char *master_server;
int master_server_port;
int master_update_interval;
+    char *master_username;
char *master_password;

relay_server *relay;
@@ -132,8 +141,8 @@
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;
-    int burst_on_connect;
} ice_config_t;

typedef struct {

Modified: icecast/branches/kh/icecast/src/client.c
===================================================================
--- icecast/trunk/icecast/src/client.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/client.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -33,23 +33,26 @@
#include "client.h"
#include "logging.h"

+#undef CATMODULE
+#define CATMODULE "client"
+
+#ifdef HAVE_AIO
+#include <errno.h>
+#endif
+
client_t *client_create(connection_t *con, http_parser_t *parser)
{
client_t *client = (client_t *)calloc(1, sizeof(client_t));

client->con = con;
client->parser = parser;
-    client->queue = NULL;
client->pos = 0;
-    client->burst_sent = 0;

return client;
}

void client_destroy(client_t *client)
{
-    refbuf_t *refbuf;
-
if (client == NULL)
return;
/* write log entry if ip is set (some things don't set it, like outgoing
@@ -57,18 +60,23 @@
*/
if(client->con->ip)
logging_access(client);
-
+#ifdef HAVE_AIO
+    if (aio_cancel (client->con->sock, NULL) == AIO_NOTCANCELED)
+    {
+        const struct aiocb *list = &client->aio;
+        INFO0 ("having to wait for aio cancellation");
+        while (aio_suspend (&list, 1, NULL) < 0)
+            ;
+    }
+#endif
connection_close(client->con);
httpp_destroy(client->parser);

-    while ((refbuf = refbuf_queue_remove(&client->queue)))
-        refbuf_release(refbuf);
-
-    /* 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);
}
@@ -116,12 +124,48 @@
client_destroy(client);
}

-void client_send_403(client_t *client) {
-    int bytes = sock_write(client->con->sock,
-            "HTTP/1.0 403 Forbidden\r\n"
-            "\r\n"
-            "Access restricted.\r\n");
-    if(bytes > 0) client->con->sent_bytes = bytes;
-    client->respcode = 403;
-    client_destroy(client);
+
+/* helper function for sending the data to a client */
+int client_send_bytes (client_t *client, const void *buf, unsigned len)
+{
+    int ret;
+#ifdef HAVE_AIO
+    int err;
+    struct aiocb *aiocbp = &client->aio;
+
+    if (client->pending_io == 0)
+    {
+        memset (aiocbp, 0 , sizeof (struct aiocb));
+        aiocbp->aio_fildes = client->con->sock;
+        aiocbp->aio_buf = (void*)buf; /* only read from */
+        aiocbp->aio_nbytes = len;
+
+        if (aio_write (aiocbp) < 0)
+            return -1;
+        client->pending_io = 1;
+    }
+    if ((err = aio_error (aiocbp)) == EINPROGRESS)
+        return -1;
+    ret = aio_return (aiocbp);
+    if (ret < 0)
+       sock_set_error (err); /* make sure errno gets set */
+
+    client->pending_io = 0;
+
+#else
+    ret = sock_write_bytes (client->con->sock, buf, len);
+#endif
+
+    if (ret < 0)
+    {
+        if (! sock_recoverable (sock_error()))
+        {
+            DEBUG0 ("Client connection died");
+            client->con->error = 1;
+        }
+    }
+    if (ret > 0)
+        client->con->sent_bytes += ret;
+    return ret;
}
+

Modified: icecast/branches/kh/icecast/src/client.h
===================================================================
--- icecast/trunk/icecast/src/client.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/client.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -17,10 +17,13 @@
*/
#ifndef __CLIENT_H__
#define __CLIENT_H__
+
+#ifndef WIN32
+#include <aio.h>
+#endif

#include "connection.h"
#include "refbuf.h"
-#include "httpp/httpp.h"

typedef struct _client_tag
{
@@ -32,20 +35,39 @@
/* http response code for this client */
int respcode;

-    /* buffer queue */
-    refbuf_queue_t *queue;
+    /* auth completed, 0 not yet, 1 passed, 2 failed  */
+    int authenticated;
+
+    /* where in the queue the client is */
+    refbuf_t *refbuf;
+
/* position in first buffer */
unsigned long pos;

/* Client username, if authenticated */
char *username;

+    /* Client password, if authenticated */
+    char *password;
+
+#ifdef HAVE_AIO
+    /* for handling async IO */
+    struct aiocb aio;
+    int pending_io;
+#endif
+
/* 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);
-    int burst_sent;
+
+    char *predata;
+    unsigned predata_size;
+    unsigned predata_len;
+    unsigned predata_offset;
+
+    struct _client_tag *next;
} client_t;

client_t *client_create(connection_t *con, http_parser_t *parser);
@@ -53,7 +75,7 @@
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);
void client_send_400(client_t *client, char *message);
+int client_send_bytes (client_t *client, const void *buf, unsigned len);

#endif  /* __CLIENT_H__ */

Modified: icecast/branches/kh/icecast/src/connection.c
===================================================================
--- icecast/trunk/icecast/src/connection.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/connection.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -61,7 +61,6 @@
#include "format_mp3.h"
#include "event.h"
#include "admin.h"
-#include "auth.h"

#define CATMODULE "connection"

@@ -78,7 +77,6 @@
static mutex_t _connection_mutex;
static unsigned long _current_id = 0;
static int _initialized = 0;
-static cond_t _pool_cond;

static con_queue_t *_queue = NULL;
static mutex_t _queue_mutex;
@@ -93,11 +91,10 @@
{
if (_initialized) return;

-    thread_mutex_create(&_connection_mutex);
-    thread_mutex_create(&_queue_mutex);
-    thread_mutex_create(&move_clients_mutex);
+    thread_mutex_create("connection", &_connection_mutex);
+    thread_mutex_create("connection q", &_queue_mutex);
+    thread_mutex_create("move_clients", &move_clients_mutex);
thread_rwlock_create(&_source_shutdown_rwlock);
-    thread_cond_create(&_pool_cond);
thread_cond_create(&global.shutdown_cond);

_initialized = 1;
@@ -108,7 +105,6 @@
if (!_initialized) return;

thread_cond_destroy(&global.shutdown_cond);
-    thread_cond_destroy(&_pool_cond);
thread_rwlock_destroy(&_source_shutdown_rwlock);
thread_mutex_destroy(&_queue_mutex);
thread_mutex_destroy(&_connection_mutex);
@@ -249,7 +245,11 @@
}

if (!sock_recoverable(sock_error()))
+    {
WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
+        abort();
+        /* global.running = ICE_HALTING; */
+    }

free(ip);

@@ -270,11 +270,6 @@

}

-static void _signal_pool(void)
-{
-    thread_cond_signal(&_pool_cond);
-}
-
static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
{
/* create item */
@@ -342,13 +337,12 @@

i = 0;

-    thread_cond_broadcast(&_pool_cond);
id = _pop_thread(&_conhands);
while (id != NULL) {
thread_join(id);
-        _signal_pool();
id = _pop_thread(&_conhands);
}
+    INFO0("All connection threads down");
}

void connection_accept_loop(void)
@@ -372,7 +366,6 @@

if (con) {
_add_connection(con);
-            _signal_pool();
}
}

@@ -423,7 +416,6 @@
con->event = event_data;

_add_connection(con);
-    _signal_pool();
}


@@ -465,9 +457,8 @@
"for icecast 1.x relays. Assuming content is mp3.");
format_type = FORMAT_TYPE_MP3;
}
-        source->format = format_get_plugin (format_type, source->mount, source->parser);

-        if (source->format == NULL)
+        if (format_get_plugin (format_type, source) < 0)
{
global_unlock();
config_release_config();
@@ -483,6 +474,8 @@
/* set global settings first */
source->queue_size_limit = config->queue_size_limit;
source->timeout = config->source_timeout;
+        // source->burst_size = config->burst_size_limit;
+        source->burst_size_limit = config->burst_size_limit;

/* for relays, we don't yet have a client, however we do require one
* to retrieve the stream from.  This is created here, quite late,
@@ -610,7 +603,7 @@
int ret;
ice_config_t *config = config_get_config();
char *pass = config->relay_password;
-    char *user = "relay";
+    char *user = config->relay_username;

if(!pass || !user) {
config_release_config();
@@ -622,6 +615,7 @@
return ret;
}

+
int connection_check_source_pass(http_parser_t *parser, char *mount)
{
ice_config_t *config = config_get_config();
@@ -680,8 +674,9 @@
client = client_create(con, parser);

INFO1("Source logging in at mountpoint \"%s\"", uri);
-
-    if (!connection_check_source_pass(parser, uri)) {
+
+    if (!connection_check_source_pass(parser, uri))
+    {
/* We commonly get this if the source client is using the wrong
* protocol: attempt to diagnose this and return an error
*/
@@ -719,7 +714,8 @@

stats_event_inc(NULL, "stats_connections");

-    if (!connection_check_admin_pass(parser)) {
+    if (!connection_check_admin_pass(parser))
+    {
ERROR0("Bad password for stats connection");
connection_close(con);
httpp_destroy(parser);
@@ -727,7 +723,7 @@
}

stats_event_inc(NULL, "stats");
-
+
/* create stats connection and create stats handler thread */
stats = (stats_connection_t *)malloc(sizeof(stats_connection_t));
stats->parser = parser;
@@ -737,7 +733,7 @@
}

static void _handle_get_request(connection_t *con,
-        http_parser_t *parser, char *uri)
+        http_parser_t *parser, char *passed_uri)
{
char *fullpath;
client_t *client;
@@ -753,8 +749,9 @@
aliases *alias;
ice_config_t *config;
int client_limit;
-    int ret;
+    char *uri = passed_uri;

+    DEBUG1("start with %s", passed_uri);
config = config_get_config();
fileserve = config->fileserve;
host = config->hostname;
@@ -768,13 +765,8 @@
}
alias = config->aliases;
client_limit = config->client_limit;
-    config_release_config();


-    DEBUG0("Client connected");
-
-    /* make a client */
-    client = client_create(con, parser);
stats_event_inc(NULL, "client_connections");

/* there are several types of HTTP GET clients
@@ -790,15 +782,21 @@
/* 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 = alias->destination;
+            uri = strdup (alias->destination);
+            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
break;
}
alias = alias->next;
}
+    config_release_config();

+    /* make a client */
+    client = client_create(con, parser);
+
/* Dispatch all admin requests */
if (strncmp(uri, "/admin/", 7) == 0) {
admin_handle_request(client, uri);
+        if (uri != passed_uri) free (uri);
return;
}

@@ -822,6 +820,7 @@
client_send_404(client, "The file you requested could not be found");
}
free(fullpath);
+        if (uri != passed_uri) free (uri);
return;
}
else if(fileserve && stat(fullpath, &statbuf) == 0 &&
@@ -833,6 +832,7 @@
{
fserve_client_create(client, fullpath);
free(fullpath);
+        if (uri != passed_uri) free (uri);
return;
}
free(fullpath);
@@ -881,107 +881,25 @@
}
avl_tree_unlock(global.source_tree);
free(sourceuri);
+        if (uri != passed_uri) free (uri);
return;
}

global_lock();
-    if (global.clients >= client_limit) {
+    if (global.clients >= client_limit)
+    {
global_unlock();
client_send_404(client,
"The server is already full. Try again later.");
+        if (uri != passed_uri) free (uri);
return;
}
+    global.clients++;
global_unlock();
-
-    avl_tree_rlock(global.source_tree);
-    source = source_find_mount(uri);
-    if (source) {
-        DEBUG0("Source found for client");

-        /* The source may not be the requested source - it might have gone
-         * via one or more fallbacks. We only reject it for no-mount if it's
-         * the originally requested source
-         */
-        if(strcmp(uri, source->mount) == 0 && source->no_mount) {
-            avl_tree_unlock(global.source_tree);
-            client_send_404(client, "This mount is unavailable.");
-            return;
-        }
-        if (source->running == 0)
-        {
-            avl_tree_unlock(global.source_tree);
-            DEBUG0("inactive source, client dropped");
-            client_send_404(client, "This mount is unavailable.");
-            return;
-        }
+    add_client (uri, client);

-        /* Check for any required authentication first */
-        if(source->authenticator != NULL) {
-            ret = auth_check_client(source, client);
-            if(ret != AUTH_OK) {
-                avl_tree_unlock(global.source_tree);
-                if (ret == AUTH_FORBIDDEN) {
-                    INFO1("Client attempted to log multiple times to source "
-                        "(\"%s\")", uri);
-                    client_send_403(client);
-                }
-                else {
-                /* If not FORBIDDEN, default to 401 */
-                    INFO1("Client attempted to log in to source (\"%s\")with "
-                        "incorrect or missing password", uri);
-                    client_send_401(client);
-                }
-                return;
-            }
-        }
-
-        /* And then check that there's actually room in the server... */
-        global_lock();
-        if (global.clients >= client_limit) {
-            global_unlock();
-            avl_tree_unlock(global.source_tree);
-            client_send_404(client,
-                    "The server is already full. Try again later.");
-            return;
-        }
-        /* Early-out for per-source max listeners. This gets checked again
-         * by the source itself, later. This route gives a useful message to
-         * the client, also.
-         */
-        else if(source->max_listeners != -1 &&
-                source->listeners >= source->max_listeners)
-        {
-            global_unlock();
-            avl_tree_unlock(global.source_tree);
-            client_send_404(client,
-                    "Too many clients on this mountpoint. Try again later.");
-            return;
-        }
-        global.clients++;
-        global_unlock();
-
-        client->format_data = source->format->create_client_data(
-                source->format, source, client);
-
-        source->format->client_send_headers(source->format, source, client);
-
-        bytes = sock_write(client->con->sock, "\r\n");
-        if(bytes > 0) client->con->sent_bytes += bytes;
-
-        sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
-        sock_set_nodelay(client->con->sock);
-
-        avl_tree_wlock(source->pending_tree);
-        avl_insert(source->pending_tree, (void *)client);
-        avl_tree_unlock(source->pending_tree);
-    }
-
-    avl_tree_unlock(global.source_tree);
-
-    if (!source) {
-        DEBUG0("Source not found for client");
-        client_send_404(client, "The source you requested could not be found.");
-    }
+    if (uri != passed_uri) free (uri);
}

static void *_handle_connection(void *arg)
@@ -995,7 +913,7 @@
while (global.running == ICE_RUNNING) {
memset(header, 0, 4096);

-        thread_cond_wait(&_pool_cond);
+        thread_sleep (100000);
if (global.running != ICE_RUNNING) break;

/* grab a connection and set the socket to blocking */
@@ -1088,6 +1006,7 @@
}
}
}
+    DEBUG0 ("Connection thread done");

return NULL;
}

Modified: icecast/branches/kh/icecast/src/format.c
===================================================================
--- icecast/trunk/icecast/src/format.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -36,7 +36,7 @@
#include "global.h"
#include "httpp/httpp.h"

-#include "format_vorbis.h"
+#include "format_ogg.h"
#include "format_mp3.h"

#include "logging.h"
@@ -44,19 +44,20 @@

#ifdef WIN32
#define strcasecmp stricmp
-#define strncasecmp strnicmp
+#define strncasecmp strnicmp
+#define snprintf _snprintf
#endif

format_type_t format_get_type(char *contenttype)
{
if(strcmp(contenttype, "application/x-ogg") == 0)
-        return FORMAT_TYPE_VORBIS; /* Backwards compatibility */
+        return FORMAT_TYPE_OGG; /* Backwards compatibility */
else if(strcmp(contenttype, "application/ogg") == 0)
-        return FORMAT_TYPE_VORBIS; /* Now blessed by IANA */
+        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
else if(strcmp(contenttype, "audio/mpeg") == 0)
return FORMAT_TYPE_MP3;
else if(strcmp(contenttype, "audio/x-mpeg") == 0)
-        return FORMAT_TYPE_MP3; /* Relay-compatibility for some servers */
+        return FORMAT_TYPE_MP3;
else
return FORMAT_ERROR;
}
@@ -64,7 +65,7 @@
char *format_get_mimetype(format_type_t type)
{
switch(type) {
-        case FORMAT_TYPE_VORBIS:
+        case FORMAT_TYPE_OGG:
return "application/ogg";
break;
case FORMAT_TYPE_MP3:
@@ -75,105 +76,137 @@
}
}

-format_plugin_t *format_get_plugin(format_type_t type, char *mount,
-        http_parser_t *parser)
+int format_get_plugin(format_type_t type, source_t *source)
{
-    format_plugin_t *plugin;
+    int ret;

-    switch (type) {
-    case FORMAT_TYPE_VORBIS:
-        plugin = format_vorbis_get_plugin();
-        if (plugin) plugin->mount = mount;
+    switch (type)
+    {
+    case FORMAT_TYPE_OGG:
+        ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_MP3:
-        plugin = format_mp3_get_plugin(parser);
-        if (plugin) plugin->mount = mount;
+        ret = format_mp3_get_plugin (source);
break;
default:
-        plugin = NULL;
+        ret = -1;
break;
}

-    return plugin;
+    return ret;
}

+
int format_generic_write_buf_to_client(format_plugin_t *format,
client_t *client, unsigned char *buf, int len)
{
int ret;

-    ret = sock_write_bytes(client->con->sock, buf, len);
+    ret = client_send_bytes (client, buf, len);
+    if (ret < 0 && client->con->error == 0)
+        ret = 0;

-    if(ret < 0) {
-        if(sock_recoverable(sock_error())) {
-            DEBUG1("Client had recoverable error %ld", ret);
-            ret = 0;
-        }
-    }
-    else
-        client->con->sent_bytes += ret;
-
return ret;
}

-void format_send_general_headers(format_plugin_t *format,
-        source_t *source, client_t *client)
+
+void format_prepare_headers (source_t *source, client_t *client)
{
-    http_var_t *var;
+    unsigned remaining;
+    char *ptr;
+    int bytes;
+    int bitrate_filtered = 0;
avl_node *node;
-    int bytes;
+    char *agent;

+    remaining = client->predata_size;
+    ptr = client->predata;
+    client->respcode = 200;
+
+    /* ugly hack, but send ICY OK header when client is realplayer */
+    agent = httpp_getvar (client->parser, "user-agent");
+    if (agent && strstr (agent, "RealMedia") != NULL)
+        bytes = snprintf (ptr, remaining, "ICY 200 OK\r\nContent-Type: %s\r\n",
+                format_get_mimetype (source->format->type));
+    else
+        bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n",
+                format_get_mimetype (source->format->type));
+
+    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);
+    avl_tree_rlock (source->parser->vars);
+    node = avl_get_first (source->parser->vars);
while (node)
{
-        var = (http_var_t *)node->key;
-        if (!strcasecmp(var->name, "ice-audio-info")) {
+        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;
+            char *brfield = NULL;
unsigned int bitrate;

-            brfield = strstr(var->value, "bitrate=");
-            if (brfield && sscanf(var->value, "bitrate=%u", &bitrate)) {
-                bytes = sock_write(client->con->sock, "icy-br:%u\r\n", bitrate);
-                if (bytes > 0)
-                    client->con->sent_bytes += bytes;
+            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 (strcasecmp (var->name, "ice-password") &&
+                strcasecmp (var->name, "icy-metaint"))
{
-                bytes = 0;
-                if (!strncasecmp("ice-", var->name, 4))
+                if (!strncasecmp ("ice-", var->name, 4))
{
-                    if (!strcasecmp("ice-bitrate", var->name))
-                        bytes += sock_write(client->con->sock, "icy-br:%s\r\n", var->value);
+                    if (!strcasecmp ("ice-public", var->name))
+                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
else
-                        if (!strcasecmp("ice-public", var->name))
-                            bytes += sock_write(client->con->sock,
-                                "icy-pub:%s\r\n", var->value);
+                        if (!strcasecmp ("ice-bitrate", var->name))
+                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
else
-                            bytes = sock_write(client->con->sock, "icy%s:%s\r\n",
+                            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);
-
-                }
-                if (!strncasecmp("icy-", var->name, 4))
-                {
-                    bytes = sock_write(client->con->sock, "icy%s:%s\r\n",
-                            var->name + 3, var->value);
-                }
-                if (bytes > 0)
-                    client->con->sent_bytes += bytes;
+                    }
}
}
-        node = avl_get_next(node);
+
+        remaining -= bytes;
+        ptr += bytes;
+        if (next)
+            node = avl_get_next (node);
}
-    avl_tree_unlock(source->parser->vars);
-    bytes = sock_write(client->con->sock,
-            "Server: %s\r\n", ICECAST_VERSION_STRING);
-    if(bytes > 0) client->con->sent_bytes += bytes;
+    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->predata_len = client->predata_size - remaining;
}

+
+void format_initialise ()
+{
+    format_ogg_initialise ();
+}
+

Modified: icecast/branches/kh/icecast/src/format.h
===================================================================
--- icecast/trunk/icecast/src/format.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -26,9 +26,10 @@

typedef enum _format_type_tag
{
+    FORMAT_ERROR, /* No format, source not processable */
+    FORMAT_TYPE_OGG,
FORMAT_TYPE_VORBIS,
-    FORMAT_TYPE_MP3,
-    FORMAT_ERROR /* No format, source not processable */
+    FORMAT_TYPE_MP3
} format_type_t;

typedef struct _format_plugin_tag
@@ -43,18 +44,14 @@
/* set this is the data format has a header that
** we must send before regular data
*/
-    int has_predata;

-    int (*get_buffer)(struct _format_plugin_tag *self, char *data, unsigned long
-            len, refbuf_t **buffer);
-    refbuf_queue_t *(*get_predata)(struct _format_plugin_tag *self);
-    int (*write_buf_to_client)(struct _format_plugin_tag *format,
-            client_t *client, unsigned char *buf, int len);
-    void *(*create_client_data)(struct _format_plugin_tag *format,
-            struct source_tag *source, client_t *client);
-    void (*client_send_headers)(struct _format_plugin_tag *format,
-            struct source_tag *source, client_t *client);
+    refbuf_t *(*get_buffer)(struct source_tag *);
+    int (*write_buf_to_client)(struct _format_plugin_tag *format, 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 (*prerelease)(struct source_tag *source, refbuf_t *refbuf);

/* for internal state management */
void *_state;
@@ -62,19 +59,14 @@

format_type_t format_get_type(char *contenttype);
char *format_get_mimetype(format_type_t type);
-format_plugin_t *format_get_plugin(format_type_t type, char *mount,
-        http_parser_t *parser);
+int format_get_plugin(format_type_t type, struct source_tag *source);

int format_generic_write_buf_to_client(format_plugin_t *format,
client_t *client, unsigned char *buf, int len);
void format_send_general_headers(format_plugin_t *format,
struct source_tag *source, client_t *client);
+void format_prepare_headers (struct source_tag *source, client_t *client);
+void format_initialise();

#endif  /* __FORMAT_H__ */

-
-
-
-
-
-

Modified: icecast/branches/kh/icecast/src/format_mp3.c
===================================================================
--- icecast/trunk/icecast/src/format_mp3.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format_mp3.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -51,358 +51,503 @@
/* 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
+#define ICY_METADATA_INTERVAL 8192

-static void format_mp3_free_plugin(format_plugin_t *self);
-static int format_mp3_get_buffer(format_plugin_t *self, char *data,
-        unsigned long len, refbuf_t **buffer);
-static refbuf_queue_t *format_mp3_get_predata(format_plugin_t *self);
-static void *format_mp3_create_client_data(format_plugin_t *self,
-        source_t *source, client_t *client);
+static void format_mp3_free_plugin(format_plugin_t *plugin);
+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(format_plugin_t *self,
-        client_t *client, unsigned char *buf, int len);
-static void format_mp3_send_headers(format_plugin_t *self,
-        source_t *source, client_t *client);
+static int format_mp3_write_buf_to_client(format_plugin_t *self, 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);

+
typedef struct {
int use_metadata;
-   int interval;
-   int offset;
-   int metadata_age;
int metadata_offset;
+   unsigned since_meta_block;
+   int in_metadata;
+   refbuf_t *associated;
} mp3_client_data;

-format_plugin_t *format_mp3_get_plugin(http_parser_t *parser)
+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 *)malloc(sizeof(format_plugin_t));

plugin->type = FORMAT_TYPE_MP3;
-    plugin->has_predata = 0;
-    plugin->get_buffer = format_mp3_get_buffer;
-    plugin->get_predata = format_mp3_get_predata;
+    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->client_send_headers = format_mp3_send_headers;
plugin->free_plugin = format_mp3_free_plugin;
+    plugin->set_tag = mp3_set_tag;
+    plugin->prerelease = NULL;
plugin->format_description = "MP3 audio";

plugin->_state = state;

-    state->metadata_age = 0;
-    state->metadata = strdup("");
-    thread_mutex_create(&(state->lock));
+    meta = refbuf_new (1);
+    memcpy (meta->data, "", 1);
+    meta->len = 1;
+    state->metadata = meta;
+    state->interval = ICY_METADATA_INTERVAL;

-    metadata = httpp_getvar(parser, "icy-metaint");
-    if(metadata)
-        state->inline_metadata_interval = atoi(metadata);
+    metadata = httpp_getvar (source->parser, "icy-metaint");
+    if (metadata)
+    {
+        state->inline_metadata_interval = atoi (metadata);
+        state->offset = 0;
+        plugin->get_buffer = mp3_get_filter_meta;
+    }
+    source->format = plugin;

-    return plugin;
+    return 0;
}


-static int send_metadata(client_t *client, mp3_client_data *client_state,
-        mp3_state *source_state)
+static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value)
{
-    int len_byte;
-    int len;
-    int ret = -1;
-    unsigned char *buf;
-    int source_age;
-    char *fullmetadata = NULL;
-    int  fullmetadata_size = 0;
-    const char meta_fmt[] = "StreamTitle='';";
+    mp3_state *source_mp3 = plugin->_state;
+    unsigned len;
+    const char meta[] = "StreamTitle='";
+    int size = sizeof (meta) + 1;

-    do
+    if (tag==NULL || value == NULL)
+        return;
+
+    len = strlen (value)+1;
+    size += len;
+    if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
{
-        thread_mutex_lock (&(source_state->lock));
-        if (source_state->metadata == NULL)
-            break; /* Shouldn't be possible */
+        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;
+        }
+        return;
+    }
+    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;
+}

-        fullmetadata_size = strlen (source_state->metadata) + sizeof (meta_fmt);

-        if (fullmetadata_size > 4079)
+static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned meta_len)
+{
+    if (metadata)
+    {
+        char *end, *p;
+        int len;
+
+        do
{
-            fullmetadata_size = 4079;
-        }
-        fullmetadata = malloc (fullmetadata_size);
-        if (fullmetadata == NULL)
-            break;
+            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);
+                stats_event (source->mount, "title", p);
+                yp_touch (source->mount);
+                free (p);
+            }
+        } while (0);
+    }
+}

-        fullmetadata_size = snprintf (fullmetadata, fullmetadata_size,
-                "StreamTitle='%.*s';", fullmetadata_size-(sizeof (meta_fmt)-1), source_state->metadata);

-        source_age = source_state->metadata_age;
+void mp3_set_title (source_t *source)
+{
+    const char meta[] = "StreamTitle='";
+    int size;
+    unsigned char len_byte;
+    refbuf_t *p;
+    unsigned len = sizeof(meta) + 6;
+    mp3_state *source_mp3 = source->format->_state;

-        if (fullmetadata_size > 0 && source_age != client_state->metadata_age)
-        {
-            len_byte = (fullmetadata_size)/16 + 1; /* to give 1-255 */
-            client_state->metadata_offset = 0;
-        }
+    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
+    size  = sizeof (meta) + len + 2;
+    if (len > MAX_META_LEN-(sizeof(meta)+3))
+    {
+        WARN1 ("Metadata too long at %d chars", len);
+        return;
+    }
+    len_byte = size / 16 + 1;
+    size = len_byte * 16 + 1;
+    p = refbuf_new (size);
+    p->len = 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
-            len_byte = 0;
-        len = 1 + len_byte*16;
-        buf = calloc (1, len);
-        if (buf == NULL)
-            break;
+            snprintf (p->data, size, "%c%s%.*s';", len_byte, meta, len, source_mp3->url_title);
+        filter_shoutcast_metadata (source, p->data, size);
+        source_mp3->metadata = p;
+    }
+}

-        buf[0] = len_byte;

-        if (len > 1) {
-            strncpy (buf+1, fullmetadata, len-1);
-            buf[len-1] = '\0';
-        }
+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;

-        thread_mutex_unlock (&(source_state->lock));
+    if (associated == client_mp3->associated)
+    {
+        metadata = "\0";
+        meta_len = 1;
+    }
+    else
+    {
+        metadata = associated->data + client_mp3->metadata_offset;
+        meta_len = associated->len - client_mp3->metadata_offset;
+    }
+    ret = client_send_bytes (client, metadata, meta_len);

-        /* only write what hasn't been written already */
-        ret = sock_write_bytes (client->con->sock, buf+client_state->metadata_offset, len-client_state->metadata_offset);
-
-        if (ret > 0 && ret < len) {
-            client_state->metadata_offset += ret;
-        }
-        else if (ret == len) {
-            client_state->metadata_age = source_age;
-            client_state->offset = 0;
-            client_state->metadata_offset = 0;
-        }
-        free (buf);
-        free (fullmetadata);
+    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;
+    client_mp3->in_metadata = 1;

-    } while (0);
-
-    thread_mutex_unlock(&(source_state->lock));
-    free (fullmetadata);
-    return -1;
+    return ret;
}

-static int format_mp3_write_buf_to_client(format_plugin_t *self,
-    client_t *client, unsigned char *buf, int len)
+
+/* return bytes actually written, -1 for error or 0 for no more data to write */
+
+static int format_mp3_write_buf_to_client (format_plugin_t *self, client_t *client)
{
-    int ret;
-    mp3_client_data *mp3data = client->format_data;
-
-    if(((mp3_state *)self->_state)->metadata && mp3data->use_metadata)
-    {
-        mp3_client_data *state = client->format_data;
-        int max = state->interval - state->offset;
+    int ret, written = 0;
+    mp3_client_data *client_mp3 = client->format_data;
+    mp3_state *source_mp3 = self->_state;
+    refbuf_t *refbuf = client->refbuf;
+    char *buf;
+    unsigned len;

-        if(len == 0) /* Shouldn't happen */
-            return 0;
+    if (refbuf == NULL)
+        return 0;  /* no data yet */
+    if (refbuf->next == NULL && client->pos == refbuf->len)
+        return 0;
+    buf = refbuf->data + client->pos;
+    len = refbuf->len - client->pos;

-        if(max > len)
-            max = len;
+    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(max > 0) {
-            ret = sock_write_bytes(client->con->sock, buf, max);
-            if(ret > 0)
-                state->offset += ret;
+            if (ret < (int)associated->len)
+                break;
+            written += ret;
}
-        else {
-            ret = send_metadata(client, state, self->_state);
-            if(ret > 0)
-                client->con->sent_bytes += ret;
-            ret = 0;
-        }
+        /* see if we need to send the current metadata to the client */
+        if (client_mp3->use_metadata)
+        {
+            unsigned remaining = source_mp3->interval - client_mp3->since_meta_block;

-    }
-    else {
-        ret = sock_write_bytes(client->con->sock, buf, len);
-    }
+            /* 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) {
-        if(sock_recoverable(sock_error())) {
-            DEBUG1("Client had recoverable error %ld", ret);
-            ret = 0;
+                    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;
+            }
}
-    }
-    else
-        client->con->sent_bytes += ret;
+        /* write any mp3, maybe after the metadata block */
+        if (len)
+        {
+            ret = client_send_bytes (client, buf, len);

-    return ret;
+            if (ret > 0)
+            {
+                client_mp3->since_meta_block += ret;
+                client->pos += ret;
+            }
+            if (ret < (int)len)
+                break;
+            written += ret;
+        }
+        ret = 0;
+        /* we have now written what we need to in here */
+        if (refbuf->next)
+        {
+            client->refbuf = refbuf->next;
+            client->pos = 0;
+        }
+    } while (0);
+
+    if (ret > 0)
+        written += ret;
+    return written ? written : -1;
}

-static void format_mp3_free_plugin(format_plugin_t *self)
+static void format_mp3_free_plugin (format_plugin_t *plugin)
{
/* free the plugin instance */
-    mp3_state *state = self->_state;
-    thread_mutex_destroy(&(state->lock));
+    mp3_state *state = plugin->_state;

-    free(state->metadata);
free(state);
-    free(self);
+    free(plugin);
}

-static int format_mp3_get_buffer(format_plugin_t *self, char *data,
-    unsigned long len, refbuf_t **buffer)
+
+static refbuf_t *mp3_get_no_meta (source_t *source)
{
+    int bytes;
refbuf_t *refbuf;
-    mp3_state *state = self->_state;
+    mp3_state *source_mp3 = source->format->_state;

-    /* Set this to NULL in case it doesn't get set to a valid buffer later */
-    *buffer = NULL;
+    if ((refbuf = refbuf_new (4096)) == NULL)
+        return NULL;
+    bytes = sock_read_bytes (source->con->sock, refbuf->data, 4096);

-    if(!data)
-        return 0;
+    if (bytes == 0)
+    {
+        INFO1 ("End of stream %s", source->mount);
+        source->running = 0;
+        refbuf_release (refbuf);
+        return NULL;
+    }
+    if (source_mp3->update_metadata)
+    {
+        mp3_set_title (source);
+        source_mp3->update_metadata = 0;
+    }
+    if (bytes > 0)
+    {
+        refbuf->len  = bytes;
+        refbuf->associated = source_mp3->metadata;
+        refbuf->sync_point = 1;
+        return refbuf;
+    }
+    refbuf_release (refbuf);

-    if(state->inline_metadata_interval) {
-        /* Source is sending metadata, handle it... */
+    if (!sock_recoverable (sock_error()))
+        source->running = 0;

-        while(len > 0) {
-            int to_read = state->inline_metadata_interval - state->offset;
-            if(to_read > 0) {
-                refbuf_t *old_refbuf = *buffer;
+    return NULL;
+}

-                if(to_read > len)
-                    to_read = len;

-                if(old_refbuf) {
-                    refbuf = refbuf_new(to_read + old_refbuf->len);
-                    memcpy(refbuf->data, old_refbuf->data, old_refbuf->len);
-                    memcpy(refbuf->data+old_refbuf->len, data, to_read);
+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 bytes, mp3_block;
+    int ret;

-                    refbuf_release(old_refbuf);
-                }
-                else {
-                    refbuf = refbuf_new(to_read);
-                    memcpy(refbuf->data, data, to_read);
-                }
+    refbuf = refbuf_new (2048);
+    src = refbuf->data;

-                *buffer = refbuf;
+    ret = sock_read_bytes (source->con->sock, refbuf->data, 2048);

-                state->offset += to_read;
-                data += to_read;
-                len -= to_read;
-            }
-            else if(!state->metadata_length) {
-                /* Next up is the metadata byte... */
-                unsigned char byte = data[0];
-                data++;
-                len--;
+    if (ret == 0)
+    {
+        INFO1 ("End of stream %s", source->mount);
+        source->running = 0;
+        refbuf_release (refbuf);
+        return NULL;
+    }
+    if (source_mp3->update_metadata)
+    {
+        mp3_set_title (source);
+        source_mp3->update_metadata = 0;
+    }
+    if (ret < 0)
+    {
+        refbuf_release (refbuf);
+        if (sock_recoverable (sock_error()))
+            return NULL; /* go back to waiting */
+        INFO0 ("Error on connection from source");
+        source->running = 0;
+        return NULL;
+    }
+    /* fill the buffer with the read data */
+    bytes = (unsigned)ret;
+    while (bytes > 0)
+    {
+        unsigned metadata_remaining;

-                /* According to the "spec"... this byte * 16 */
-                state->metadata_length = byte * 16;
+        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;

-                if(state->metadata_length) {
-                    state->metadata_buffer =
-                        calloc(state->metadata_length + 1, 1);
+        /* 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;
+        }

-                    /* Ensure we have a null-terminator even if the source
-                     * stream is invalid.
-                     */
-                    state->metadata_buffer[state->metadata_length] = 0;
-                }
-                else {
-                    state->offset = 0;
-                }
+        /* are we processing the inline metadata, len == 0 indicates not seen any */
+        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);
+        }

-                state->metadata_offset = 0;
-            }
-            else {
-                /* Metadata to read! */
-                int readable = state->metadata_length - state->metadata_offset;
+        /* 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;
+        }
+        memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset,
+                src, metadata_remaining);

-                if(readable > len)
-                    readable = len;
+        /* overwrite metadata in the buffer */
+        bytes -= metadata_remaining;
+        memmove (src, src+metadata_remaining, bytes);

-                memcpy(state->metadata_buffer + state->metadata_offset,
-                        data, readable);
+        /* assign metadata if it's not 1 byte, as that indicates a change */
+        if (source_mp3->build_metadata_len > 1)
+        {
+            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
+            memcpy (meta->data, source_mp3->build_metadata, source_mp3->build_metadata_len);
+            meta->len = source_mp3->build_metadata_len;

-                state->metadata_offset += readable;
-
-                data += readable;
-                len -= readable;
-
-                if(state->metadata_offset == state->metadata_length)
-                {
-                    if(state->metadata_length)
-                    {
-                        thread_mutex_lock(&(state->lock));
-                        free(state->metadata);
-                        /* Now, reformat state->metadata_buffer to strip off
-                           StreamTitle=' and the closing '; (but only if there's
-                           enough data for it to be correctly formatted) */
-                        if(state->metadata_length >= 15) {
-                            /* This is overly complex because the
-                               metadata_length is the length of the actual raw
-                               data, but the (null-terminated) string is going
-                               to be shorter than this, and we can't trust that
-                               the raw data doesn't have other embedded-nulls */
-                            int stringlength;
-
-                            state->metadata = malloc(state->metadata_length -
-                                    12);
-                            memcpy(state->metadata,
-                                    state->metadata_buffer + 13,
-                                    state->metadata_length - 13);
-                            /* Make sure we've got a null-terminator of some
-                               sort */
-                            state->metadata[state->metadata_length - 13] = 0;
-
-                            /* Now figure out the _right_ one */
-                            stringlength = strlen(state->metadata);
-                            if(stringlength > 2)
-                                state->metadata[stringlength - 2] = 0;
-                            free(state->metadata_buffer);
-                        }
-                        else
-                            state->metadata = state->metadata_buffer;
-
-                        stats_event(self->mount, "title", state->metadata);
-                        state->metadata_buffer = NULL;
-                        state->metadata_age++;
-                        thread_mutex_unlock(&(state->lock));
-                        yp_touch (self->mount);
-                    }
-
-                    state->offset = 0;
-                    state->metadata_length = 0;
-                }
+            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);
+                source_mp3->metadata = meta;
}
+            else
+            {
+                ERROR0 ("Incorrect metadata format, ending stream");
+                source->running = 0;
+                refbuf_release (refbuf);
+                return NULL;
+            }
}
-
-        /* Either we got a buffer above (in which case it can be used), or
-         * we set *buffer to NULL in the prologue, so the return value is
-         * correct anyway...
-         */
-        return 0;
+        source_mp3->offset = 0;
+        source_mp3->build_metadata_len = 0;
}
-    else {
-        /* Simple case - no metadata, just dump data directly to a buffer */
-        refbuf = refbuf_new(len);
+    refbuf->associated = source_mp3->metadata;
+    refbuf->sync_point = 1;

-        memcpy(refbuf->data, data, len);
-
-        *buffer = refbuf;
-        return 0;
-    }
+    return refbuf;
}

-static refbuf_queue_t *format_mp3_get_predata(format_plugin_t *self)
+
+static void mp3_set_predata (source_t *source, client_t *client)
{
-    return NULL;
+    mp3_client_data *mp3data = client->format_data;
+
+    if (mp3data->use_metadata)
+    {
+        unsigned remaining = client->predata_size - client->predata_len + 2;
+        char *ptr = client->predata + client->predata_len - 2;
+        int bytes;
+
+        bytes = snprintf (ptr, remaining, "icy-metaint:%u\r\n\r\n",
+                ICY_METADATA_INTERVAL);
+        if (bytes > 0)
+            client->predata_len += bytes - 2;
+    }
}

-static void *format_mp3_create_client_data(format_plugin_t *self,
-        source_t *source, client_t *client)
+
+static int format_mp3_create_client_data(source_t *source, client_t *client)
{
mp3_client_data *data = calloc(1,sizeof(mp3_client_data));
char *metadata;

-    data->interval = ICY_METADATA_INTERVAL;
-    data->offset = 0;
+    if (data == NULL)
+    {
+        ERROR0 ("malloc failed");
+        return -1;
+    }
+
+    client->format_data = data;
client->free_client_data = free_mp3_client_data;
-
metadata = httpp_getvar(client->parser, "icy-metadata");
+
if(metadata)
+    {
data->use_metadata = atoi(metadata)>0?1:0;

-    return data;
+        mp3_set_predata (source, client);
+    }
+
+    return 0;
}


@@ -413,29 +558,15 @@
}


-static void format_mp3_send_headers(format_plugin_t *self,
-        source_t *source, client_t *client)
+static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
{
-    int bytes;
-    mp3_client_data *mp3data = client->format_data;
-
-    client->respcode = 200;
-    /* TODO: This may need to be ICY/1.0 for shoutcast-compatibility? */
-    bytes = sock_write(client->con->sock,
-            "HTTP/1.0 200 OK\r\n"
-            "Content-Type: %s\r\n",
-            format_get_mimetype(source->format->type));
-
-    if (bytes > 0)
-        client->con->sent_bytes += bytes;
-
-    if (mp3data->use_metadata)
+    if (refbuf->len == 0)
+        return;
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
{
-        int bytes = sock_write(client->con->sock, "icy-metaint:%d\r\n",
-                ICY_METADATA_INTERVAL);
-        if(bytes > 0)
-            client->con->sent_bytes += bytes;
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
}
-    format_send_general_headers(self, source, client);
}


Modified: icecast/branches/kh/icecast/src/format_mp3.h
===================================================================
--- icecast/trunk/icecast/src/format_mp3.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format_mp3.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -18,19 +18,24 @@
#ifndef __FORMAT_MP3_H__
#define __FORMAT_MP3_H__

+#include "refbuf.h"
+
typedef struct {
-    char *metadata;
-    int metadata_age;
-    mutex_t lock;
-
/* These are for inline metadata */
int inline_metadata_interval;
+    unsigned interval;
int offset;
-    int metadata_length;
-    char *metadata_buffer;
-    int metadata_offset;
+    char *url_artist;
+    char *url_title;
+    int update_metadata;
+
+    refbuf_t *metadata;
+
+    unsigned build_metadata_len;
+    unsigned build_metadata_offset;
+    char build_metadata[4081];
} mp3_state;

-format_plugin_t *format_mp3_get_plugin(http_parser_t *parser);
+int format_mp3_get_plugin(struct source_tag *src);

#endif  /* __FORMAT_MP3_H__ */

Modified: icecast/branches/kh/icecast/src/format_vorbis.c
===================================================================
--- icecast/trunk/icecast/src/format_vorbis.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format_vorbis.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -37,244 +37,761 @@
#define CATMODULE "format-vorbis"
#include "logging.h"

-#define MAX_HEADER_PAGES 10
+static ogg_int64_t next_rebuild_serialno = 0;
+static mutex_t serial_lock;

typedef struct _vstate_tag
{
ogg_sync_state oy;
-    ogg_stream_state os;
+    ogg_stream_state os, out_os;
vorbis_info vi;
vorbis_comment vc;

-    ogg_page og;
-    unsigned long serialno;
-    int header;
-    refbuf_t *headbuf[MAX_HEADER_PAGES];
-    int packets;
+    ogg_packet *prev_packet;
+    refbuf_t *file_headers;
+
+    int initial_audio_packet;
+    int stream_notify;
+    int use_url_comment;
+    int to_terminate;
+    int more_headers;
+    int prev_window;
+    int page_samples_trigger;
+    ogg_int64_t granulepos;
+    ogg_int64_t samples_in_page;
+    ogg_int64_t prev_samples;
+    ogg_int64_t prev_page_samples;
+
+    refbuf_t *headers_head;
+    refbuf_t *headers_tail;
+    ogg_packet *header [3];
+    ogg_packet url_comment;
+    char *url_artist;
+    char *url_title;
+
+    int (*process_packet)(source_t *);
+    refbuf_t *(*get_buffer_page)(struct _vstate_tag *source_vorbis);
+
} vstate_t;

-static void format_vorbis_free_plugin(format_plugin_t *self);
-static int format_vorbis_get_buffer(format_plugin_t *self, char *data,
-        unsigned long len, refbuf_t **buffer);
-static refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self);
-static void *format_vorbis_create_client_data(format_plugin_t *self,
-        source_t *source, client_t *client);
-static void format_vorbis_send_headers(format_plugin_t *self,
-        source_t *source, client_t *client);
+struct client_vorbis
+{
+    refbuf_t *headers;
+    refbuf_t *header_page;
+    unsigned pos;
+    int headers_sent;
+};

-format_plugin_t *format_vorbis_get_plugin(void)
+
+static ogg_int64_t get_next_serialno ()
{
+    ogg_int64_t serialno;
+    thread_mutex_lock (&serial_lock);
+    serialno = next_rebuild_serialno++;
+    thread_mutex_unlock (&serial_lock);
+    return serialno;
+}
+
+static void format_vorbis_free_plugin (format_plugin_t *plugin);
+static int  create_vorbis_client_data(source_t *source, client_t *client);
+static void free_vorbis_client_data (client_t *client);
+
+static void write_vorbis_to_file (struct source_tag *source, refbuf_t *refbuf);
+static refbuf_t *vorbis_get_buffer (source_t *source);
+static int vorbis_write_buf_to_client (format_plugin_t *self, client_t *client);
+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);
+    }
+}
+
+
+int format_ogg_get_plugin (source_t *source)
+{
format_plugin_t *plugin;
vstate_t *state;
+    vorbis_comment vc;

plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));

-    plugin->type = FORMAT_TYPE_VORBIS;
-    plugin->has_predata = 1;
-    plugin->get_buffer = format_vorbis_get_buffer;
-    plugin->get_predata = format_vorbis_get_predata;
-    plugin->write_buf_to_client = format_generic_write_buf_to_client;
-    plugin->create_client_data = format_vorbis_create_client_data;
-    plugin->client_send_headers = format_vorbis_send_headers;
+    plugin->type = FORMAT_TYPE_OGG;
+    plugin->format_description = "Ogg Vorbis";
+    plugin->get_buffer = vorbis_get_buffer;
+    plugin->write_buf_to_client = vorbis_write_buf_to_client;
+    plugin->write_buf_to_file = write_vorbis_to_file;
+    plugin->create_client_data = create_vorbis_client_data;
plugin->free_plugin = format_vorbis_free_plugin;
-    plugin->format_description = "Ogg Vorbis";
+    plugin->set_tag = vorbis_set_tag;

state = (vstate_t *)calloc(1, sizeof(vstate_t));
ogg_sync_init(&state->oy);
+    ogg_stream_init (&state->out_os, get_next_serialno());

+    vorbis_comment_init (&vc);
+    vorbis_commentheader_out (&vc, &state->url_comment);
+    vorbis_comment_clear (&vc);
+
plugin->_state = (void *)state;
+    source->format = plugin;

-    return plugin;
+    return 0;
}

-void format_vorbis_free_plugin(format_plugin_t *self)
+void format_vorbis_free_plugin (format_plugin_t *plugin)
{
-    int i;
-    vstate_t *state = (vstate_t *)self->_state;
+    vstate_t *state = plugin->_state;

/* free memory associated with this plugin instance */

/* free state memory */
-    ogg_sync_clear(&state->oy);
-    ogg_stream_clear(&state->os);
-    vorbis_comment_clear(&state->vc);
-    vorbis_info_clear(&state->vi);
-
-    for (i = 0; i < MAX_HEADER_PAGES; i++) {
-        if (state->headbuf[i]) {
-            refbuf_release(state->headbuf[i]);
-            state->headbuf[i] = NULL;
+    ogg_sync_clear (&state->oy);
+    ogg_stream_clear (&state->os);
+    ogg_stream_clear (&state->out_os);
+    vorbis_comment_clear (&state->vc);
+    vorbis_info_clear (&state->vi);
+
+    free_ogg_packet (state->header[0]);
+    free_ogg_packet (state->header[1]);
+    free_ogg_packet (state->header[2]);
+    if (state->prev_packet)
+        free_ogg_packet (state->prev_packet);
+
+    ogg_packet_clear (&state->url_comment);
+
+    free (state);
+
+    /* free the plugin instance */
+    free (plugin);
+}
+
+
+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 (vstate_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_samples);
+        source_vorbis->prev_samples = packet->granulepos;
+        source_vorbis->granulepos += source_vorbis->prev_window;
+    }
+    /* printf ("Adding packet %lld, granulepos %lld (%ld)\n", packet->packetno,
+            packet->granulepos, vorbis_packet_blocksize (&source_vorbis->vi, packet)/4); */
+    ogg_stream_packetin (&source_vorbis->out_os, packet);
+}
+
+
+static refbuf_t *get_buffer_audio (vstate_t *source_vorbis)
+{
+    refbuf_t *refbuf = NULL;
+    ogg_page page;
+    int (*get_ogg_page)(ogg_stream_state*, ogg_page *) = ogg_stream_pageout;
+
+    /* printf ("current sample count is %lld, %ld\n", source_vorbis->samples_in_page, source_vorbis->vi.rate>>1); */
+    if (source_vorbis->samples_in_page > source_vorbis->page_samples_trigger)
+    {
+        get_ogg_page = ogg_stream_flush;
+        /* printf ("forcing flush with %lld samples\n", source_vorbis->samples_in_page); */
+    }
+    if (get_ogg_page (&source_vorbis->out_os, &page) > 0)
+    {
+        /* printf ("got audio page %lld\n", ogg_page_granulepos (&page)); */
+        /* 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 = 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);
+        refbuf->len = page.header_len + page.body_len;
+        refbuf->associated = source_vorbis->headers_head;
+        /* printf ("setting associated to %p\n", refbuf->associated); */
+    }
+    return refbuf;
+}
+
+
+static refbuf_t *get_buffer_header (vstate_t *source_vorbis)
+{
+    int headers_flushed = 0;
+    ogg_page page;
+
+    /* printf ("in buffer_header\n"); */
+    while (ogg_stream_flush (&source_vorbis->out_os, &page) > 0)
+    {
+        refbuf_t *refbuf;
+        /* squeeze a page copy into a buffer */
+        /* printf ("Stored vorbis header\n"); */
+        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);
+        refbuf->len = page.header_len + page.body_len;
+
+        /* store header page for associated list */
+        if (source_vorbis->headers_tail)
+            source_vorbis->headers_tail->next = refbuf;
+        if (source_vorbis->headers_head == NULL)
+            source_vorbis->headers_head = refbuf;
+        source_vorbis->headers_tail = refbuf;
+        headers_flushed = 1;
+    }
+    if (headers_flushed)
+    {
+        /* printf ("headers have now been handled\n"); */
+        source_vorbis->get_buffer_page = get_buffer_audio;
+    }
+    return NULL;
+}
+
+
+static refbuf_t *get_buffer_finished (vstate_t *source_vorbis)
+{
+    ogg_page page;
+
+    if (ogg_stream_flush (&source_vorbis->out_os, &page) > 0)
+    {
+        refbuf_t *refbuf;
+        /* printf ("EOS stream flush %lld\n", ogg_page_granulepos (&page)); */
+
+        source_vorbis->samples_in_page -= (ogg_page_granulepos (&page) - source_vorbis->prev_page_samples);
+        source_vorbis->prev_page_samples = ogg_page_granulepos (&page);
+
+        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);
+        refbuf->len = page.header_len + page.body_len;
+        refbuf->associated = source_vorbis->headers_head;
+        return refbuf;
+    }
+    ogg_stream_clear (&source_vorbis->out_os);
+    ogg_stream_init (&source_vorbis->out_os, get_next_serialno());
+    source_vorbis->headers_head = NULL;
+    source_vorbis->headers_tail = NULL;
+    source_vorbis->get_buffer_page = get_buffer_header;
+    /* printf ("stream cleared\n"); */
+    return NULL;
+}
+
+
+/* pushed last packet into stream marked with eos */
+static void initiate_flush (vstate_t *source_vorbis)
+{
+    if (source_vorbis->prev_packet)
+    {
+        /* insert prev_packet with eos */
+        source_vorbis->prev_packet->e_o_s = 1;
+        /* printf ("adding stored packet marked as EOS\n"); */
+        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;
+}
+
+/* just deal with ogg vorbis streams at the moment */
+
+static int process_vorbis_audio (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;
+    int result = 0;
+
+    while (1)
+    {
+        int window;
+        ogg_packet packet;
+
+        /* now, lets extract what packets we can */
+        if (ogg_stream_packetout (&source_vorbis->os, &packet) <= 0)
+            return result;
+
+        result = 1;
+
+        /* 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;
+            if (packet.b_o_s)
+                prev_packet->e_o_s = 1;
+            add_audio_packet (source_vorbis, prev_packet);
+            /* printf ("Adding prev packet %lld, granulepos %lld (%d) samples %lld\n", prev_packet->packetno,
+                    prev_packet->granulepos, source_vorbis->prev_window, source_vorbis->samples_in_page); */
+            free_ogg_packet (prev_packet);
+
+            packet . granulepos = source_vorbis->granulepos;
}
+        else
+        {
+            packet . granulepos = 0;
+        }
+        source_vorbis->prev_window = window;
+
+        /* copy the next packet */
+        source_vorbis->prev_packet = copy_ogg_packet (&packet);
+
+        /* 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;
}
+}

-    free(state);
+/* handle the headers we want going to the clients */
+static int process_vorbis_headers (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;

-    /* free the plugin instance */
-    free(self);
+    /* trap for missing initial header, this means we're expecting
+       headers coming in, so jump out and try in a short while */
+    if (source_vorbis->header [0] == NULL)
+        return 0;
+    /* printf ("Adding the 3 header packets\n"); */
+    ogg_stream_packetin (&source_vorbis->out_os, source_vorbis->header [0]);
+    /* NOTE: we could build a separate comment packet each time */
+    if (source_vorbis->use_url_comment)
+        ogg_stream_packetin (&source_vorbis->out_os, &source_vorbis->url_comment);
+    else
+        ogg_stream_packetin (&source_vorbis->out_os, source_vorbis->header [1]);
+    ogg_stream_packetin (&source_vorbis->out_os, source_vorbis->header [2]);
+    source_vorbis->use_url_comment = 0;
+
+    source_vorbis->process_packet = process_vorbis_audio;
+    source_vorbis->granulepos = 0;
+    source_vorbis->initial_audio_packet = 1;
+    return 1;
}

-int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer)
+
+/* this is called with the first page after the initial header */
+/* it processes any headers that have come in on the stream */
+static int process_vorbis_incoming_hdrs (source_t *source)
{
-    char *buf;
-    int i, result;
-    ogg_packet op;
char *tag;
-    refbuf_t *refbuf, *source_refbuf;
-    vstate_t *state = (vstate_t *)self->_state;
-    source_t *source;
+    ogg_packet header;
+    vstate_t *source_vorbis = source->format->_state;

-    if (data) {
-        /* write the data to the buffer */
-        buf = ogg_sync_buffer(&state->oy, len);
-        memcpy(buf, data, len);
-        ogg_sync_wrote(&state->oy, len);
+    /* printf ("processing incoming header packet\n"); */
+    while (source_vorbis->more_headers)
+    {
+        /* now, lets extract the packets */
+        int result = ogg_stream_packetout (&source_vorbis->os, &header);
+
+        if (result <= 0)
+            return result;   /* need more pages */
+
+        /* change comments here if need be */
+        if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0)
+        {
+            WARN0 ("Problem parsing ogg vorbis header");
+            return -1;
+        }
+        header.granulepos = 0;
+        /* printf ("Parsing [%d] vorbis header %lld,  %lld\n", source_vorbis->more_headers, header.packetno, header.granulepos); */
+        source_vorbis->header [3-source_vorbis->more_headers] = copy_ogg_packet (&header);
+        source_vorbis->more_headers--;
}

-    refbuf = NULL;
-    if (ogg_sync_pageout(&state->oy, &state->og) == 1) {
-        refbuf = refbuf_new(state->og.header_len + state->og.body_len);
-        memcpy(refbuf->data, state->og.header, state->og.header_len);
-        memcpy(&refbuf->data[state->og.header_len], state->og.body, state->og.body_len);
+    /* we have all headers */

-        if (state->serialno != ogg_page_serialno(&state->og)) {
-            /* this is a new logical bitstream */
-            state->header = 0;
-            state->packets = 0;
+    /* put known comments in the stats, this could be site specific */
+    tag = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0);
+    if (tag == NULL)
+        tag = "unknown";
+    stats_event (source->mount, "title", tag);

-            /* release old headers, stream state, vorbis data */
-            for (i = 0; i < MAX_HEADER_PAGES; i++) {
-                if (state->headbuf[i]) {
-                    refbuf_release(state->headbuf[i]);
-                    state->headbuf[i] = NULL;
-                }
-            }
-            /* Clear old stuff. Rarely but occasionally needed. */
-            ogg_stream_clear(&state->os);
-            vorbis_comment_clear(&state->vc);
-            vorbis_info_clear(&state->vi);
+    tag = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
+    if (tag == NULL)
+        tag = "unknown";
+    stats_event (source->mount, "artist", tag);

-            state->serialno = ogg_page_serialno(&state->og);
-            ogg_stream_init(&state->os, state->serialno);
-            vorbis_info_init(&state->vi);
-            vorbis_comment_init(&state->vc);
-        }
+    stats_event_args (source->mount, "ice-samplerate", "%ld", (long)source_vorbis->vi.rate);
+    stats_event_args (source->mount, "ice-channels", "%ld", (long)source_vorbis->vi.channels);
+    stats_event_args (source->mount, "ice-bitrate", "%ld", (long)source_vorbis->vi.bitrate_nominal/1024);
+    /* set queued pages to contain a 1/4 of a second worth of samples */
+    source_vorbis->page_samples_trigger = source_vorbis->vi.rate / 4;

-        if (state->header >= 0) {
-            /* FIXME: In some streams (non-vorbis ogg streams), this could get
-             * extras pages beyond the header. We need to collect the pages
-             * here anyway, but they may have to be discarded later.
-             */
-            if (ogg_page_granulepos(&state->og) <= 0) {
-                state->header++;
-            } else {
-                /* we're done caching headers */
-                state->header = -1;
+    /* printf ("finished with incoming header packets\n"); */
+    source_vorbis->process_packet = process_vorbis_headers;

-                /* put known comments in the stats */
-                tag = vorbis_comment_query(&state->vc, "TITLE", 0);
-                if (tag) stats_event(self->mount, "title", tag);
-                else stats_event(self->mount, "title", "unknown");
-                tag = vorbis_comment_query(&state->vc, "ARTIST", 0);
-                if (tag) stats_event(self->mount, "artist", tag);
-                else stats_event(self->mount, "artist", "unknown");
+    return 1;
+}

-                /* don't need these now */
-                ogg_stream_clear(&state->os);
-                vorbis_comment_clear(&state->vc);
-                vorbis_info_clear(&state->vi);

-                /* Drain the source queue on metadata update otherwise you
-                   could have a mismatch between what is on the source queue
-                   and what is in the state->headbuf */
-                avl_tree_rlock(global.source_tree);
-                source = source_find_mount_raw(self->mount);
-                avl_tree_unlock(global.source_tree);

-                thread_mutex_lock(&source->queue_mutex);
-                while ((source_refbuf = refbuf_queue_remove(&source->queue))) {
-                    refbuf_release(source_refbuf);
-                }
-                thread_mutex_unlock(&source->queue_mutex);
+static int initial_vorbis_page (vstate_t *source_vorbis, ogg_packet *packet)
+{
+    /* init vi and vc */
+    vorbis_comment_clear (&source_vorbis->vc);
+    vorbis_info_clear (&source_vorbis->vi);

-                yp_touch (self->mount);
-            }
+    vorbis_info_init (&source_vorbis->vi);
+    vorbis_comment_init (&source_vorbis->vc);
+
+    /* printf ("processing initial page\n"); */
+    if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, packet) < 0)
+    {
+        /* printf ("not a vorbis packet\n"); */
+        return -1;
+    }
+
+    /* printf ("Handling ogg vorbis header\n"); */
+    free_ogg_packet (source_vorbis->header[0]);
+    free_ogg_packet (source_vorbis->header[1]);
+    free_ogg_packet (source_vorbis->header[2]);
+    memset (source_vorbis->header, 0, sizeof (source_vorbis->header));
+    source_vorbis->header [0] = copy_ogg_packet (packet);
+    source_vorbis->more_headers = 2;
+
+    initiate_flush (source_vorbis);
+    source_vorbis->process_packet = process_vorbis_incoming_hdrs;
+    /* free previous audio packet, it maybe in a different format */
+    free_ogg_packet (source_vorbis->prev_packet);
+    source_vorbis->prev_packet = NULL;
+    source_vorbis->prev_window = 0;
+
+    source_vorbis->headers_head = NULL;
+    source_vorbis->headers_tail = NULL;
+    source_vorbis->initial_audio_packet = 1;
+
+    return 0;
+}
+
+
+static int process_initial_page (source_t *source, ogg_page *page)
+{
+    vstate_t *source_vorbis = source->format->_state;
+    int ret = -1;
+    ogg_packet packet;
+
+    ogg_stream_clear (&source_vorbis->os);
+    ogg_stream_init (&source_vorbis->os, ogg_page_serialno (page));
+
+    ogg_stream_pagein (&source_vorbis->os, page);
+    do
+    {
+        if (ogg_stream_packetout (&source_vorbis->os, &packet) <= 0)
+            break;
+        ret = 0;
+        if (initial_vorbis_page (source_vorbis, &packet) == 0)
+            break;
+        /* any others */
+        ret = -1;
+    } while (0);
+    /* printf ("processed initial page\n"); */
+    return ret;
+}
+
+
+static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value)
+{
+    vstate_t *source_vorbis = plugin->_state;
+    int change = 0;
+    if (strcmp (tag, "artist") == 0)
+    {
+        char *p = strdup (value);
+        if (p)
+        {
+            free (source_vorbis->url_artist);
+            source_vorbis->url_artist = p;
+            change = 1;
}
+    }
+    if (strcmp (tag, "title") == 0)
+    {
+        char *p = strdup (value);
+        if (p)
+        {
+            free (source_vorbis->url_title);
+            source_vorbis->url_title = p;
+            change = 1;
+        }
+    }
+    if (change)
+        source_vorbis->stream_notify = 1;
+}

-        /* cache header pages */
-        if (state->header > 0 && state->packets < 3) {
-            if(state->header > MAX_HEADER_PAGES) {
-                refbuf_release(refbuf);
-                ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES);

-                return -1;
-            }
-            refbuf_addref(refbuf);
-            state->headbuf[state->header - 1] = refbuf;
+static void update_comments (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;
+    vorbis_comment vc;
+    ogg_packet header;

-            if (state->packets >= 0 && state->packets < 3) {
-                ogg_stream_pagein(&state->os, &state->og);
-                while (state->packets < 3) {
-                    result = ogg_stream_packetout(&state->os, &op);
-                    if (result == 0) break; /* need more data */
-                    if (result < 0) {
-                        state->packets = -1;
-                        break;
-                    }
+    initiate_flush (source_vorbis);

-                    state->packets++;
+    /* printf ("updated comment header\n"); */
+    vorbis_comment_init (&vc);
+    if (source_vorbis->url_artist)
+        vorbis_comment_add_tag (&vc, "artist", source_vorbis->url_artist);
+    if (source_vorbis->url_title)
+        vorbis_comment_add_tag (&vc, "title", source_vorbis->url_title);
+    vorbis_comment_add (&vc, "server=" ICECAST_VERSION_STRING);
+    ogg_packet_clear (&source_vorbis->url_comment);
+    vorbis_commentheader_out (&vc, &source_vorbis->url_comment);
+    vorbis_comment_clear (&vc);
+    header.packetno = 1;
+    source_vorbis->use_url_comment = 1;
+    source_vorbis->process_packet = process_vorbis_headers;
+}

-                    if (vorbis_synthesis_headerin(&state->vi, &state->vc, &op) < 0) {
-                        state->packets = -1;
-                        break;
-                    }
+static refbuf_t *vorbis_get_buffer (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;
+    char *data = NULL;
+    int bytes = 1;
+    ogg_page page;
+    refbuf_t *refbuf = NULL;
+
+    while (1)
+    {
+        while (1)
+        {
+            if (source_vorbis->get_buffer_page)
+                refbuf = source_vorbis->get_buffer_page (source_vorbis);
+            if (refbuf)
+               return refbuf;
+
+            /* printf ("check for processed packets\n"); */
+            if (source_vorbis->process_packet && source_vorbis->process_packet (source) > 0)
+                continue;
+            /* printf ("Checking for more in-pages\n"); */
+            if (ogg_sync_pageout (&source_vorbis->oy, &page) > 0)
+            {
+                /* lets see what we do with it */
+                if (ogg_page_bos (&page))
+                {
+                    process_initial_page (source, &page);
+                    return NULL;
}
+                /* printf ("Adding in page to out_os\n"); */
+                ogg_stream_pagein (&source_vorbis->os, &page);
+                continue;
}
+            break;
}
+        if (source_vorbis->to_terminate)
+        {
+            /* normal exit path */
+            source->running = 0;
+            source_vorbis->to_terminate = 0;
+            return NULL;
+        }
+        /* see if any non-stream updates are requested */
+        if (source_vorbis->stream_notify)
+        {
+            update_comments (source);
+            source_vorbis->stream_notify = 0;
+            continue;
+        }
+        if (data == NULL)
+            data = ogg_sync_buffer (&source_vorbis->oy, 4096);
+        /* printf ("reading data in\n"); */
+        bytes = sock_read_bytes (source->con->sock, data, 4096);
+        if (bytes < 0)
+        {
+            if (sock_recoverable (sock_error()))
+                return NULL;
+            WARN0 ("source connection has died");
+            ogg_sync_wrote (&source_vorbis->oy, 0);
+            source_vorbis->to_terminate = 1;
+            initiate_flush (source_vorbis);
+            return NULL;
+        }
+        if (bytes == 0)
+        {
+            INFO1 ("End of Stream %s", source->mount);
+            ogg_sync_wrote (&source_vorbis->oy, 0);
+            source_vorbis->to_terminate = 1;
+            initiate_flush (source_vorbis);
+            return NULL;
+        }
+        ogg_sync_wrote (&source_vorbis->oy, bytes);
+        data = NULL;
}
+}

-    *buffer = refbuf;
+
+static int create_vorbis_client_data (source_t *source, client_t *client)
+{
+    struct client_vorbis *client_data = calloc (1, sizeof (struct client_vorbis));
+    if (client_data == NULL)
+    {
+        ERROR0("malloc failed");
+        return -1;
+    }
+    client_data->headers_sent = 1;
+    client->format_data = client_data;
+    client->free_client_data = free_vorbis_client_data;
return 0;
}

-refbuf_queue_t *format_vorbis_get_predata(format_plugin_t *self)
+static void free_vorbis_client_data (client_t *client)
{
-    refbuf_queue_t *queue;
-    int i;
-    vstate_t *state = (vstate_t *)self->_state;
+    free (client->format_data);
+    client->format_data = NULL;
+}

-    queue = NULL;
-    for (i = 0; i < MAX_HEADER_PAGES; i++) {
-        if (state->headbuf[i]) {
-            refbuf_addref(state->headbuf[i]);
-            refbuf_queue_add(&queue, state->headbuf[i]);
-        } else {
-            break;
+
+static int send_vorbis_headers (client_t *client, refbuf_t *headers)
+{
+    struct client_vorbis *client_data = client->format_data;
+    refbuf_t *refbuf;
+    int written = 0;
+
+    if (client_data->headers_sent)
+    {
+        /* printf ("setting client_data header to %p\n", headers); */
+        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;
+
+        /* printf ("....sending header at %p\n", refbuf); */
+        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;
+}

-    return queue;
+
+static int vorbis_write_buf_to_client (format_plugin_t *self, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+    char *buf;
+    unsigned len;
+    struct client_vorbis *client_data = client->format_data;
+    int ret, written = 0;
+
+    /* rare but the listener could connect before audio is ready */
+    if (refbuf == NULL)
+        return 0;
+    /* printf ("client %p (%p) @ %lu\n", refbuf, refbuf->next,  client->pos); */
+    if (refbuf->next == NULL && client->pos == refbuf->len)
+        return 0;
+
+    if (refbuf->next && client->pos == refbuf->len)
+    {
+        client->refbuf = refbuf->next;
+        client->pos = 0;
+    }
+    refbuf = client->refbuf;
+    buf = refbuf->data + client->pos;
+    len = refbuf->len - client->pos;
+    do
+    {
+        if (client_data->headers != refbuf->associated)
+        {
+            /* printf ("sending header data %p\n", refbuf->associated); */
+            ret = send_vorbis_headers (client, refbuf->associated);
+            if (client_data->headers_sent == 0)
+                break;
+            written += ret;
+        }
+        /* printf ("sending audio data\n"); */
+        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 header page(s) */
+        ret = 0;
+    } while (0);
+
+    if (ret > 0)
+       written += ret;
+    return written ? written : -1;
}

-static void *format_vorbis_create_client_data(format_plugin_t *self,
-        source_t *source, client_t *client)
+
+static int write_vorbis_data (struct source_tag *source, refbuf_t *refbuf)
{
-    return NULL;
+    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 format_vorbis_send_headers(format_plugin_t *self,
-        source_t *source, client_t *client)
+
+static void write_vorbis_to_file (struct source_tag *source, refbuf_t *refbuf)
{
-    int bytes;
-
-    client->respcode = 200;
-    bytes = sock_write(client->con->sock,
-            "HTTP/1.0 200 OK\r\n"
-            "Content-Type: %s\r\n",
-            format_get_mimetype(source->format->type));
+    vstate_t *source_vorbis = source->format->_state;

-    if(bytes > 0) client->con->sent_bytes += bytes;
+    if (source_vorbis->file_headers != refbuf->associated)
+    {
+        refbuf_t *header = refbuf->associated;
+        while (header)
+        {
+            if (write_vorbis_data (source, header) == 0)
+                return;
+            header = header->next;
+        }
+        source_vorbis->file_headers = refbuf->associated;
+    }
+    write_vorbis_data (source, refbuf);
+}

-    format_send_general_headers(self, source, client);
+
+void format_ogg_initialise (void)
+{
+    next_rebuild_serialno = 1;
+    thread_mutex_create ("serial", &serial_lock);
}

-

Modified: icecast/branches/kh/icecast/src/format_vorbis.h
===================================================================
--- icecast/trunk/icecast/src/format_vorbis.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/format_vorbis.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -18,6 +18,7 @@
#ifndef __FORMAT_VORBIS_H__
#define __FORMAT_VORBIS_H__

-format_plugin_t *format_vorbis_get_plugin(void);
+int format_ogg_get_plugin (source_t *source);
+void format_ogg_initialise (void);

#endif  /* __FORMAT_VORBIS_H__ */

Modified: icecast/branches/kh/icecast/src/fserve.c
===================================================================
--- icecast/trunk/icecast/src/fserve.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/fserve.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -247,23 +247,14 @@
}

/* Now try and send current chunk. */
-            sbytes = sock_write_bytes(client->client->con->sock,
+            sbytes = client_send_bytes (client->client,
&client->buf[client->offset],
client->datasize - client->offset);

/* TODO: remove clients if they take too long. */
-            if(sbytes >= 0) {
+            if(sbytes > 0) {
client->offset += sbytes;
-                client->client->con->sent_bytes += sbytes;
}
-            else if(!sock_recoverable(sock_error())) {
-                DEBUG0("Fileserving client had fatal error, disconnecting");
-                client->client->con->error = 1;
-            }
-            /*
-            else
-                DEBUG0("Fileserving client had recoverable error");
-             */

avl_node_unlock(client_node);
client_node = avl_get_next(client_node);

Modified: icecast/branches/kh/icecast/src/global.c
===================================================================
--- icecast/trunk/icecast/src/global.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/global.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -43,7 +43,7 @@
global.clients = 0;
global.sources = 0;
global.source_tree = avl_tree_new(source_compare_sources, NULL);
-    thread_mutex_create(&_global_mutex);
+    thread_mutex_create("global", &_global_mutex);
}

void global_shutdown(void)

Modified: icecast/branches/kh/icecast/src/logging.c
===================================================================
--- icecast/trunk/icecast/src/logging.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/logging.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -85,15 +85,16 @@
if (user_agent == NULL)
user_agent = "-";

-    log_write_direct (accesslog, "%s - - [%s] \"%s\" %d %lld \"%s\" \"%s\" %u",
+#ifdef HAVE_LOGGING_IP
+    log_write_direct (accesslog, "%s - - [%s] \"%s\" %d %lld \"%s\" \"%s\" %d",
client->con->ip,
-             datebuf,
-             reqbuf,
-             client->respcode,
-             client->con->sent_bytes,
-             referrer,
-             user_agent,
-             stayed);
+             datebuf, reqbuf, client->respcode, client->con->sent_bytes,
+             referrer, user_agent, (int)stayed);
+#else
+    log_write_direct (accesslog, "- - - [%s] \"%s\" %d %lld \"%s\" \"%s\" %d",
+             datebuf, reqbuf, client->respcode, client->con->sent_bytes,
+             referrer, user_agent, (int)stayed);
+#endif
}



Modified: icecast/branches/kh/icecast/src/main.c
===================================================================
--- icecast/trunk/icecast/src/main.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/main.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -49,6 +49,7 @@
#include "xslt.h"
#include "fserve.h"
#include "yp.h"
+#include "format.h"

#include <libxml/xmlmemory.h>

@@ -100,6 +101,7 @@
global_initialize();
refbuf_initialize();
xslt_initialize();
+    format_initialise();
}

static void _shutdown_subsystems(void)
@@ -142,7 +144,7 @@
/* exit the parent */
exit(0);
}
-            else {
+            else if (processID < 0) {
fprintf(stderr, "FATAL: Unable to fork child!");
exit(1);
}
@@ -186,13 +188,11 @@
errorlog = log_open_file(stderr);
}

-    if (errorlog < 0) {
-        buf[sizeof(buf)-1] = 0;
-        snprintf(buf, sizeof(buf)-1, "FATAL: could not open error logging: %s",
-                strerror(errno));
-        _fatal_error(buf);
+    if (errorlog < 0)
+    {
+        snprintf (buf, sizeof(buf), "FATAL: could not open error logging: %s", 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);
@@ -201,13 +201,13 @@
accesslog = log_open_file(stderr);
}

-    if (accesslog < 0) {
-        buf[sizeof(buf)-1] = 0;
-        snprintf(buf, sizeof(buf)-1, "FATAL: could not open access logging: %s",
+    if (accesslog < 0)
+    {
+        snprintf (buf, sizeof(buf), "FATAL: could not open access logging: %s",
strerror(errno));
_fatal_error(buf);
}
-
+
log_set_level(errorlog, config->loglevel);
log_set_level(accesslog, 4);


Modified: icecast/branches/kh/icecast/src/md5.c
===================================================================
--- icecast/trunk/icecast/src/md5.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/md5.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -185,7 +185,7 @@
# define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
-# define MD5STEP(f, w, x, y, z, data, s) w += f(x, y, z) + data;  w = (w<<s) | (w>>(32-s));  w += x
+# define MD5STEP(f, w, x, y, z, data, s) do { w += f(x, y, z) + data;  w = (w<<s) | (w>>(32-s));  w += x; }while (0)

/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to

Modified: icecast/branches/kh/icecast/src/refbuf.c
===================================================================
--- icecast/trunk/icecast/src/refbuf.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/refbuf.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -33,121 +33,48 @@
{
}

-refbuf_t *refbuf_new(unsigned long size)
-{
-    refbuf_t *refbuf;

-    refbuf = (refbuf_t *)malloc(sizeof(refbuf_t));
-    refbuf->data = (void *)malloc(size);
-    refbuf->len = size;
-    refbuf->_count = 1;
-
-    return refbuf;
-}
-
-void refbuf_addref(refbuf_t *self)
+void refbuf_free (refbuf_t *refbuf)
{
-    self->_count++;
+    free (refbuf->data);
+    free (refbuf);
}

-void refbuf_release(refbuf_t *self)
-{
-    self->_count--;
-    if (self->_count == 0) {
-        free(self->data);
-        free(self);
-        return;
-    }
-}

-void refbuf_queue_add(refbuf_queue_t **queue, refbuf_t *refbuf)
+refbuf_t *refbuf_new(unsigned long size)
{
-    refbuf_queue_t *node;
-    refbuf_queue_t *item = (refbuf_queue_t *)malloc(sizeof(refbuf_queue_t));
-
-    item->refbuf = refbuf;
-    item->next = NULL;
-
-    if (*queue == NULL) {
-        *queue = item;
-        (*queue)->total_length = item->refbuf->len;
-    } else {
-        node = *queue;
-        while (node->next) node = node->next;
-        node->next = item;
-        (*queue)->total_length += item->refbuf->len;
-    }
-}
-
-refbuf_t *refbuf_queue_remove(refbuf_queue_t **queue)
-{
-    refbuf_queue_t *item;
refbuf_t *refbuf;

-    if (*queue == NULL) return NULL;
-
-    item = *queue;
-    *queue = item->next;
-    item->next = NULL;
-
-    refbuf = item->refbuf;
-    item->refbuf = NULL;
-
-    if(*queue)
-        (*queue)->total_length = item->total_length - refbuf->len;
-
-    free(item);
-
-
-    return refbuf;
-}
-refbuf_t * refbuf_queue_get(refbuf_queue_t **queue, int item)
-{
-    refbuf_queue_t *node = *queue;
-    int size = 0;
-    while (node) {
-        if (size == item) {
-            return node->refbuf;
+    refbuf = malloc (sizeof(refbuf_t));
+    if (refbuf)
+    {
+        refbuf->data = NULL;
+        if (size && (refbuf->data = malloc (size)) == NULL)
+        {
+            free (refbuf);
+            return NULL;
}
-        node = node->next;
-        size++;
+        refbuf->len = 0;
+        refbuf->sync_point = 0;
+        refbuf->allocated = size;
+        refbuf->next = NULL;
+        refbuf->associated = NULL;
+        refbuf->refbuf_associated_release = refbuf_free;
+        refbuf->refbuf_release = refbuf_free;
}
-    return NULL;
-}

-
-void refbuf_queue_insert(refbuf_queue_t **queue, refbuf_t *refbuf)
-{
-    refbuf_queue_t *item = (refbuf_queue_t *)malloc(sizeof(refbuf_queue_t));
-
-    item->refbuf = refbuf;
-    item->next = *queue;
-    if(item->next)
-        item->total_length = item->next->total_length + item->refbuf->len;
-    else
-        item->total_length = item->refbuf->len;
-    *queue = item;
+    return refbuf;
}

-int refbuf_queue_size(refbuf_queue_t **queue)
-{
-    refbuf_queue_t *node = *queue;
-    int size = 0;

-    while (node) {
-        node = node->next;
-        size++;
+void refbuf_release(refbuf_t *refbuf)
+{
+    while (refbuf->associated)
+    {
+        refbuf_t *ref = refbuf->associated;
+        refbuf->associated = ref->next;
+        refbuf->refbuf_associated_release (ref);
}
-
-    return size;
+    refbuf->refbuf_release (refbuf);
}

-int refbuf_queue_length(refbuf_queue_t **queue)
-{
-    if(*queue)
-        return (*queue)->total_length;
-    else
-        return 0;
-}
-
-

Modified: icecast/branches/kh/icecast/src/refbuf.h
===================================================================
--- icecast/trunk/icecast/src/refbuf.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/refbuf.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -21,36 +21,25 @@
typedef struct _refbuf_tag
{
char *data;
-    long len;
+    unsigned len;
+    unsigned allocated;
+    int idx;
+    int sync_point;
+    struct _refbuf_tag *associated;
+    void (*refbuf_associated_release)(struct _refbuf_tag *);
+    void (*refbuf_release)(struct _refbuf_tag *);

-    unsigned long _count;
+    struct _refbuf_tag *next;
} refbuf_t;

-typedef struct _refbuf_queue_tag
-{
-    refbuf_t *refbuf;
-    long total_length;
-
-    struct _refbuf_queue_tag *next;
-} refbuf_queue_t;
-
void refbuf_initialize(void);
void refbuf_shutdown(void);

+void refbuf_free (refbuf_t *refbuf);
refbuf_t *refbuf_new(unsigned long size);
-void refbuf_addref(refbuf_t *self);
void refbuf_release(refbuf_t *self);

-void refbuf_queue_add(refbuf_queue_t **queue, refbuf_t *refbuf);
-refbuf_t *refbuf_queue_remove(refbuf_queue_t **queue);
-void refbuf_queue_insert(refbuf_queue_t **queue, refbuf_t *refbuf);

-/* Size in buffers */
-int refbuf_queue_size(refbuf_queue_t **queue);
-/* Size in bytes */
-int refbuf_queue_length(refbuf_queue_t **queue);
-refbuf_t * refbuf_queue_get(refbuf_queue_t **queue, int item);
-
#endif  /* __REFBUF_H__ */



Modified: icecast/branches/kh/icecast/src/sighandler.c
===================================================================
--- icecast/trunk/icecast/src/sighandler.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/sighandler.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -32,6 +32,7 @@
#ifndef _WIN32
void _sig_hup(int signo);
void _sig_die(int signo);
+void _sig_ignore(int signo);
#endif

void sighandler_initialize(void)
@@ -41,10 +42,15 @@
signal(SIGINT, _sig_die);
signal(SIGTERM, _sig_die);
signal(SIGPIPE, SIG_IGN);
+    signal(SIGCHLD, _sig_ignore);
#endif
}

#ifndef _WIN32
+void _sig_ignore(int signo)
+{
+    signal(signo, _sig_ignore);
+}

void _sig_hup(int signo)
{

Modified: icecast/branches/kh/icecast/src/slave.c
===================================================================
--- icecast/trunk/icecast/src/slave.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/slave.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -62,7 +62,7 @@
static void *_slave_thread(void *arg);
thread_type *_slave_thread_id;
static int slave_running = 0;
-static int max_interval = 0;
+static unsigned max_interval = 0;

relay_server *relay_free (relay_server *relay)
{
@@ -73,6 +73,8 @@
xmlFree (relay->server);
xmlFree (relay->mount);
xmlFree (relay->localmount);
+    xmlFree (relay->username);
+    xmlFree (relay->password);
xmlFree (relay);
return next;
}
@@ -87,8 +89,11 @@
copy->server = xmlStrdup (r->server);
copy->mount = xmlStrdup (r->mount);
copy->localmount = xmlStrdup (r->localmount);
+        copy->username = xmlStrdup (r->username);
+        copy->password = xmlStrdup (r->password);
copy->port = r->port;
copy->mp3metadata = r->mp3metadata;
+        copy->on_demand = r->on_demand;
}
return copy;
}
@@ -159,9 +164,14 @@
connection_t *con=NULL;
char header[4096];

+    if (relay->on_demand && src->on_demand_req == 0)
+        return;
+
INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);
do
{
+        char *auth_header;
+
streamsock = sock_connect_wto (relay->server, relay->port, 30);
if (streamsock == SOCK_ERROR)
{
@@ -171,6 +181,22 @@
}
con = create_connection (streamsock, -1, NULL);

+        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);
+            auth_header = malloc (len + 24);
+            snprintf (auth_header, len+24,
+                    "Authorization: Basic %s\r\n", esc_authorisation);
+            free(esc_authorisation);
+        }
+        else
+            auth_header = strdup ("");
+
/* 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
@@ -179,8 +205,11 @@
sock_write(streamsock, "GET %s HTTP/1.0\r\n"
"User-Agent: " ICECAST_VERSION_STRING "\r\n"
"%s"
+                "%s"
"\r\n",
-                relay->mount, relay->mp3metadata?"Icy-MetaData: 1\r\n":"");
+                relay->mount, relay->mp3metadata?"Icy-MetaData: 1\r\n":"",
+                auth_header);
+        free (auth_header);
memset (header, 0, sizeof(header));
if (util_read_header (con->sock, header, 4096) == 0)
{
@@ -231,6 +260,12 @@
/* new relay, reserve the name */
DEBUG1("Adding relay source at mountpoint \"%s\"", relay->localmount);
relay->source = source_reserve (relay->localmount);
+        if (relay->source)
+        {
+            if (relay->on_demand)
+                DEBUG0 ("setting on_demand");
+            relay->source->on_demand = relay->on_demand;
+        }
}
if (relay->source && !relay->running)
{
@@ -324,7 +359,10 @@
relay_server *relays = NULL, *relay;
int len, count = 1;

-        username = strdup ("relay");
+        if (config->master_username)
+            username = strdup (config->master_password);
+        else
+            username = strdup ("relay");
if (config->master_password)
password = strdup (config->master_password);


Modified: icecast/branches/kh/icecast/src/slave.h
===================================================================
--- icecast/trunk/icecast/src/slave.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/slave.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -17,9 +17,12 @@
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;
struct _relay_server *next;

Modified: icecast/branches/kh/icecast/src/source.c
===================================================================
--- icecast/trunk/icecast/src/source.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/source.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -25,11 +25,12 @@
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
-#include <sys/socket.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
#else
#include <winsock2.h>
#include <windows.h>
-#define snprintf _snprintf
+#define snprintf _snprintf
#endif

#include "thread/thread.h"
@@ -58,8 +59,12 @@

/* 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 parse_audio_info (source_t *source, const char *str);
+#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
@@ -83,13 +88,15 @@
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;

+        src->active_clients_tail = &src->active_clients;
+        src->pending_clients_tail = &src->pending_clients;
+
+        thread_mutex_create (src->mount, &src->lock);
+
avl_insert (global.source_tree, src);

} while (0);
@@ -152,10 +159,14 @@
}

source = source_find_mount_raw(mount);
+        if (source)
+        {
+            if (source->running)
+                break;
+            if (source->on_demand)
+                break;
+        }

-        if (source != NULL && source->running)
-            break;
-
/* source is not running, meaning that the fallback is not configured
within the source, we need to check the mount list */
mountinfo = config->mounts;
@@ -189,7 +200,6 @@

void source_clear_source (source_t *source)
{
-    refbuf_t *refbuf;
DEBUG1 ("clearing source \"%s\"", source->mount);
client_destroy(source->client);
source->client = NULL;
@@ -203,48 +213,65 @@
source->dumpfile = NULL;
}

-    /* lets kick off any clients that are left on here */
-    avl_tree_rlock (source->client_tree);
-    while (avl_get_first (source->client_tree))
+    /* lets drop any clients still connected */
+    while (source->active_clients)
{
-        avl_delete (source->client_tree,
-                avl_get_first (source->client_tree)->key, _free_client);
+        client_t *client = source->active_clients;
+        source->active_clients = client->next;
+        source_free_client (source, client);
}
-    avl_tree_unlock (source->client_tree);
+    source->active_clients_tail = &source->active_clients;
+    while (source->pending_clients)
+    {
+        client_t *client = source->pending_clients;
+        source->pending_clients = client->next;
+        source_free_client (source, client);
+    }
+    source->pending_clients_tail = &source->pending_clients;

-    avl_tree_rlock (source->pending_tree);
-    while (avl_get_first (source->pending_tree))
+    /* flush out the stream data, we don't want any left over */
+    while (source->stream_data)
{
-        avl_delete (source->pending_tree,
-                avl_get_first(source->pending_tree)->key, _free_client);
+        refbuf_t *p = source->stream_data;
+        source->stream_data = p->next;
+        if (source->stream_data && p->associated == source->stream_data->associated)
+            p->associated = NULL;
+        refbuf_release (p);
}
-    avl_tree_unlock (source->pending_tree);
+    source->stream_data_tail = NULL;

if (source->format && source->format->free_plugin)
{
source->format->free_plugin (source->format);
}
source->format = NULL;
-    if (source->yp_public)
-        yp_remove (source->mount);

+    auth_clear (source->authenticator);
+
+    source->burst_point = NULL;
+    source->first_normal_client = NULL;
+    source->queue_size = 0;
source->queue_size_limit = 0;
source->listeners = 0;
source->no_mount = 0;
source->max_listeners = -1;
source->yp_public = 0;
+    free (source->audio_info);
+    source->audio_info = NULL;

free(source->fallback_mount);
source->fallback_mount = NULL;

free(source->dumpfilename);
source->dumpfilename = NULL;
-    /* Lets clear out the source queue too */
-    while ((refbuf = refbuf_queue_remove(&source->queue)))
-        refbuf_release(refbuf);
-    source->queue = NULL;
-    source->burst_on_connect = 1;
-    thread_mutex_destroy(&source->queue_mutex);
+
+    free (source->on_connect);
+    source->on_connect = NULL;
+
+    free (source->on_disconnect);
+    source->on_disconnect = NULL;
+
+    source->on_demand_req = 0;
}


@@ -256,9 +283,15 @@
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);
+    /* There should be no listeners on this mount */
+    if (source->active_clients)
+        WARN1("active listeners on mountpoint %s", source->mount);

+    if (source->pending_clients)
+        WARN1("pending listeners on mountpoint %s", source->mount);
+
+    thread_mutex_destroy (&source->lock);
+
free (source->mount);
free (source);

@@ -268,23 +301,23 @@

client_t *source_find_client(source_t *source, int id)
{
-    client_t fakeclient;
-    void *result;
+    client_t fakeclient, *client = NULL;
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)
+    client = source->active_clients;
+    while (client)
{
-        avl_tree_unlock(source->client_tree);
-        return result;
+        if (_compare_clients (NULL, client, &fakeclient) == 0)
+            break;
+        client = client->next;
}

-    avl_tree_unlock(source->client_tree);
-    return NULL;
+    return client;
}
+

/* Move clients from source to dest provided dest is running
* and that the stream format is the same.
@@ -295,76 +328,324 @@
{
/* we don't want the two write locks to deadlock in here */
thread_mutex_lock (&move_clients_mutex);
+    thread_mutex_lock (&dest->lock);

/* if the destination is not running then we can't move clients */
-
-    if (dest->running == 0)
+    if (dest->running == 0 && dest->on_demand == 0)
{
WARN1 ("destination mount %s not running, unable to move clients ", dest->mount);
+        thread_mutex_unlock (&dest->lock);
thread_mutex_unlock (&move_clients_mutex);
return;
}

-    avl_tree_wlock (dest->pending_tree);
do
{
-        client_t *client;
+        long count = 0;
+        thread_mutex_lock (&source->lock);

-        /* we need to move the client and pending trees */
-        avl_tree_wlock (source->pending_tree);
-
if (source->format == NULL)
{
INFO1 ("source mount %s is not available", source->mount);
break;
}
-        if (source->format->type != dest->format->type)
+        if (dest->format)
{
-            WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
+            if (source->format->type != dest->format->type)
+            {
+                WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
+                break;
+            }
+        }
+
+        /* we need to move the client and pending trees */
+        while (source->active_clients)
+        {
+            client_t *client = source->active_clients;
+            source->active_clients = client->next;
+            client->refbuf = dest->stream_data_tail;
+            client->pos = 0;
+            *dest->pending_clients_tail = client;
+            dest->pending_clients_tail = &client->next;
+            count++;
+        }
+        source->active_clients_tail = &source->active_clients;
+        if (count != source->listeners)
+            WARN2 ("count %u, listeners %u", count, source->listeners);
+        count = 0;
+        while (source->pending_clients)
+        {
+            client_t *client = source->pending_clients;
+            source->pending_clients = client->next;
+            *dest->pending_clients_tail = client;
+            dest->pending_clients_tail = &client->next;
+            count++;
+        }
+        source->pending_clients_tail = &source->pending_clients;
+        if (count != source->new_listeners)
+            WARN2 ("count %u, new listeners %u", count, source->new_listeners);
+
+        INFO2 ("passing %d listeners to \"%s\"",
+                source->listeners + source->new_listeners, dest->mount);
+
+        dest->new_listeners += source->listeners + source->new_listeners;
+        dest->check_pending = 1;
+        source->listeners = 0;
+        source->new_listeners = 0;
+        stats_event (source->mount, "listeners", "0");
+
+    } while (0);
+
+    thread_mutex_unlock (&source->lock);
+    /* see if we need to wake up an on-demand relay */
+    if (dest->running == 0 && dest->on_demand)
+    {
+        dest->on_demand_req = 1;
+        slave_recheck();
+    }
+    thread_mutex_unlock (&dest->lock);
+    thread_mutex_unlock (&move_clients_mutex);
+}
+
+
+/* clients need to be start from somewhere in the queue
+ * so we will look for a refbuf which has been previous
+ * marked as a sync point */
+static void find_client_start (source_t *source, client_t *client)
+{
+    refbuf_t *refbuf = source->burst_point;
+
+    while (refbuf)
+    {
+        if (refbuf->sync_point)
+        {
break;
}
+        refbuf = refbuf->next;
+    }
+#if 0
+    if (refbuf == NULL)
+        DEBUG1 ("no start point for client %u", client->con->id);
+#endif
+    client->refbuf = refbuf;
+}

-        while (1)
+
+/* 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.
+ *
+ * return 1 for client should be specially handled, either removed or placed
+ *          elsewhere
+ *        0 for normal case.
+ */
+static int 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;
+    int ret = 1;
+
+    if (client->predata)
+    {
+        char *ptr = client->predata + client->predata_offset;
+        unsigned len  = client->predata_len - client->predata_offset;
+        bytes = client_send_bytes (client, ptr, len);
+        if (bytes > 0 && (unsigned)bytes < len)
{
-            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);
+            client->predata_offset += bytes;
+            return 0;
+        }
+        free (client->predata);
+        client->predata_size = client->predata_len = client->predata_offset = 0;
+        client->predata = NULL;
+    }

-            /* TODO: reset client local format data?  */
-            avl_insert (dest->pending_tree, (void *)client);
+    /* new users need somewhere to start from */
+    if (client->refbuf == NULL)
+        find_client_start (source, client);
+
+    while (1)
+    {
+        /* jump out if client has error'd */
+        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 > 15000 || loop == 0)
+            break;
+
+        loop--;
+
+        bytes = source->format->write_buf_to_client (source->format, client);
+        if (bytes <= 0)
+        {
+            ret = 0;
+            break;  /* can't write any more */
}

-        avl_tree_wlock (source->client_tree);
-        while (1)
+        total_written += bytes;
+    }
+
+    /* 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 == source->stream_data)
+    {
+        DEBUG0("Client has fallen too far behind, removing");
+        client->con->error = 1;
+        ret = 1;
+    }
+    return ret;
+}
+
+
+static void process_listeners (source_t *source, int fast_clients_only, int deletion_expected)
+{
+    client_t *sentinel = NULL, *client, **client_p;
+
+    if (fast_clients_only)
+    {
+        sentinel = source->first_normal_client;
+    }
+
+    source->first_normal_client = source->active_clients;
+
+    client = source->active_clients;
+    client_p = &source->active_clients;
+    while (client && client != sentinel)
+    {
+        int move_it = send_to_listener (source, client, deletion_expected);
+
+        if (move_it)
{
-            avl_node *node = avl_get_first (source->client_tree);
-            if (node == NULL)
-                break;
+            client_t *to_go = client;

-            client = (client_t *)(node->key);
-            avl_delete (source->client_tree, client, NULL);
+            *client_p = client->next;
+            if (client->next == NULL)
+                source->active_clients_tail = client_p;
+            client = client->next;

-            /* TODO: reset client local format data?  */
-            avl_insert (dest->pending_tree, (void *)client);
+            if (source->first_normal_client == to_go)
+            {
+                source->first_normal_client = to_go->next;
+            }
+
+            if (to_go->con->error)
+            {
+                source_free_client (source, to_go);
+                source->listeners--;
+                DEBUG0("Client removed");
+            }
+            else
+            {
+                /* move fast clients to beginning of list */
+                if (client_p == &source->active_clients)
+                {
+                    source->active_clients_tail = &to_go->next;
+                    client_p = &to_go->next;
+                }
+                to_go->next = source->active_clients;
+                source->active_clients = to_go;
+            }
}
-        source->listeners = 0;
-        stats_event (source->mount, "listeners", "0");
-        avl_tree_unlock (source->client_tree);
+        else
+        {
+            client_p = &client->next;
+            client = client->next;
+        }
+    }
+}

-    } while (0);

-    avl_tree_unlock (source->pending_tree);
-    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 void get_next_buffer (source_t *source)
+{
+    refbuf_t *refbuf = NULL;
+
+    while (global.running == ICE_RUNNING && source->running)
+    {
+        int fds;
+        time_t current = time(NULL);
+        int delay = 200;
+
+        if (source->active_clients != source->first_normal_client)
+            delay = 0;
+
+        thread_mutex_unlock (&source->lock);
+
+        fds = util_timed_wait_for_fd (source->con->sock, delay);
+
+        /* take the lock */
+        thread_mutex_lock (&source->lock);
+
+        if (fds < 0)
+        {
+            if (! sock_recoverable (sock_error()))
+            {
+                WARN0 ("Problem while waiting on socket, Disconnecting source");
+                source->running = 0;
+            }
+            continue;
+        }
+
+        if (fds == 0)
+        {
+            if (source->last_read + (time_t)source->timeout < current)
+            {
+                WARN0 ("Disconnecting source due to socket timeout");
+                source->running = 0;
+                break;
+            }
+            if (delay == 0)
+            {
+                process_listeners (source, 1, 0);
+                continue;
+            }
+            break;
+        }
+        source->last_read = current;
+        refbuf = source->format->get_buffer (source);
+        if (refbuf)
+        {
+            /* append buffer to the in-flight data queue,  */
+            if (source->stream_data == NULL)
+            {
+                source->stream_data = refbuf;
+                source->burst_point = refbuf;
+                source->burst_size = 0;
+            }
+            if (source->stream_data_tail)
+                source->stream_data_tail->next = refbuf;
+            source->stream_data_tail = refbuf;
+            source->queue_size += refbuf->len;
+
+            /* move the starting point for new listeners */
+            source->burst_size += refbuf->len;
+            if (source->burst_size > source->burst_size_limit)
+            {
+                // DEBUG2 ("starting counts are %d, %d", source->burst_size, source->burst_size_limit);
+                source->burst_size -= source->burst_point->len;
+                source->burst_point = source->burst_point->next;
+            }
+
+            /* save stream to file */
+            if (source->dumpfile && source->format->write_buf_to_file)
+                source->format->write_buf_to_file (source, refbuf);
+        }
+        break;
+    }
}


static void source_init (source_t *source)
{
ice_config_t *config = config_get_config();
-    char *listenurl, *str;
+    char *listenurl, *str = NULL;
int listen_url_size;

/* 6 for max size of port */
@@ -372,35 +653,57 @@
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);
-    source->burst_on_connect = config->burst_on_connect;
-    config_release_config();
+    if (listenurl)
+    {
+        snprintf (listenurl, listen_url_size, "http://%s:%d%s",
+                config->hostname, config->port, source->mount);
+        config_release_config();

-    /* maybe better in connection.c */
-    if ((str = httpp_getvar(source->parser, "ice-public")))
-        source->yp_public = atoi(str);
-    if ((str = httpp_getvar(source->parser, "icy-pub")))
-        source->yp_public = atoi(str);
+        stats_event (source->mount, "listenurl", listenurl);
+
+        free(listenurl);
+    }
+    else
+        config_release_config();
+
+    thread_mutex_lock (&source->lock);
+    if (source->yp_prevent == 0)
+    {
+        if ((str = httpp_getvar (source->parser, "ice-public")))
+            source->yp_public = atoi (str);
+        if ((str = httpp_getvar (source->parser, "icy-pub")))
+            source->yp_public = atoi (str);
+        /* handle header from icecast v2 release */
+        if ((str = httpp_getvar (source->parser, "icy-public")))
+            source->yp_public = atoi (str);
+        if (str == NULL)
+            str = "0";
+        stats_event (source->mount, "public", str);
+    }
+
+    str = httpp_getvar(source->parser, "ice-genre");
if (str == NULL)
-       str = "0";
-    stats_event (source->mount, "public", str);
+        str = httpp_getvar(source->parser, "icy-genre");
+    stats_event (source->mount, "genre", str);

+    str = httpp_getvar(source->parser, "ice-description");
+    if (str == NULL)
+        str = httpp_getvar(source->parser, "icy-description");
+    stats_event (source->mount, "server_description", str);
+
+    str = httpp_getvar(source->parser, "ice-name");
+    if (str == NULL)
+        str = httpp_getvar(source->parser, "icy-name");
+    stats_event (source->mount, "server_name", str);
+
+    source->audio_info = util_dict_new();
str = httpp_getvar(source->parser, "ice-audio-info");
-    source->audio_info = util_dict_new();
if (str)
{
-        _parse_audio_info (source, str);
+        parse_audio_info(source, str);
stats_event (source->mount, "audio_info", str);
}

-    stats_event (source->mount, "listenurl", listenurl);
-
-    if (listenurl) {
-        free(listenurl);
-    }
-
if (source->dumpfilename != NULL)
{
source->dumpfile = fopen (source->dumpfilename, "ab");
@@ -423,14 +726,17 @@

sock_set_blocking (source->con->sock, SOCK_NONBLOCK);

-    thread_mutex_create(&source->queue_mutex);
-
DEBUG0("Source creation complete");
source->running = 1;
+    source->last_read = time (NULL);
+    thread_mutex_unlock (&source->lock);

+    if (source->on_connect)
+        source_run_script (source->on_connect, source->mount);
+
/*
** 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
+    ** to steal it's 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
*/
@@ -447,355 +753,299 @@

avl_tree_unlock(global.source_tree);
}
+    thread_mutex_lock (&source->lock);
if (source->yp_public)
yp_add (source);
}


-void source_main (source_t *source)
+static void source_shutdown (source_t *source)
{
-    char buffer[4096];
-    long bytes, sbytes;
-    int ret, i;
-    client_t *client;
-    avl_node *client_node;
+    INFO1("Source \"%s\" exiting", source->mount);
+    source->running = 0;

-    refbuf_t *refbuf, *abuf, *stale_refbuf;
-    int data_done;
+    yp_remove (source->mount);

-    source_init (source);
+    if (source->on_disconnect)
+        source_run_script (source->on_disconnect, source->mount);

-    while (global.running == ICE_RUNNING && source->running) {
-        ret = source->format->get_buffer(source->format, NULL, 0, &refbuf);
-        if(ret < 0) {
-            WARN0("Bad data from source");
-            break;
-        }
-        if (source->burst_on_connect) {
-            thread_mutex_lock(&source->queue_mutex);
-            /* Add to the source buffer */
-            if (refbuf) {
-                refbuf_addref(refbuf);
-                refbuf_queue_add(&(source->queue), refbuf);
-                /* We derive the size of the source buffer queue based off the
-                   setting for queue_size_limit (client buffer queue size).
-                   This is because the source buffer queue size should be a
-                   percentage of the client buffer size (definately cannot
-                   be larger). Why 50% ? Because > 75% does not give the
-                   client enough leeway for lagging on initial connection
-                   and < 25% does not provide a good enough burst on connect. */
-                if (refbuf_queue_length(&(source->queue)) >
-                    source->queue_size_limit/2) {
-                    stale_refbuf = refbuf_queue_remove(&(source->queue));
-                    refbuf_release(stale_refbuf);
-                }
-            }
-            thread_mutex_unlock(&source->queue_mutex);
-        }
-        bytes = 1; /* Set to > 0 so that the post-loop check won't be tripped */
-        while (refbuf == NULL) {
-            bytes = 0;
-            while (bytes <= 0) {
-                ret = util_timed_wait_for_fd(source->con->sock, source->timeout*1000);
+    if (source->fallback_mount)
+    {
+        source_t *fallback_source;

-                if (ret < 0 && sock_recoverable (sock_error()))
-                   continue;
-                if (ret <= 0) { /* timeout expired */
-                    WARN1("Disconnecting source: socket timeout (%d s) expired",
-                           source->timeout);
-                    bytes = 0;
-                    break;
-                }
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount (source->fallback_mount);

-                bytes = sock_read_bytes(source->con->sock, buffer, 4096);
-                if (bytes == 0 ||
-                        (bytes < 0 && !sock_recoverable(sock_error())))
-                {
-                    DEBUG1("Disconnecting source due to socket read error: %s",
-                            strerror(sock_error()));
-                    break;
-                }
-            }
-            if (bytes <= 0) break;
-            source->client->con->sent_bytes += bytes;
-            ret = source->format->get_buffer(source->format, buffer, bytes,
-                    &refbuf);
-            if(ret < 0) {
-                WARN0("Bad data from source");
-                goto done;
-            }
-            if (source->burst_on_connect) {
-                /* Add to the source buffer */
-                thread_mutex_lock(&source->queue_mutex);
-                if (refbuf) {
-                    refbuf_addref(refbuf);
-                    refbuf_queue_add(&(source->queue), refbuf);
-                    if (refbuf_queue_length(&(source->queue)) >
-                        source->queue_size_limit/2) {
-                        stale_refbuf = refbuf_queue_remove(&(source->queue));
-                        refbuf_release(stale_refbuf);
-                    }
-                }
-                thread_mutex_unlock(&source->queue_mutex);
-            }
+        if (fallback_source != NULL)
+        {
+            /* be careful wrt to deadlocking */
+            thread_mutex_unlock (&source->lock);
+            source_move_clients (source, fallback_source);
+            thread_mutex_lock (&source->lock);
}

-        if (bytes <= 0) {
-            INFO0("Removing source following disconnection");
-            break;
-        }
+        avl_tree_unlock (global.source_tree);
+    }

-        /* we have a refbuf buffer, which a data block to be sent to
-        ** all clients.  if a client is not able to send the buffer
-        ** immediately, it should store it on its queue for the next
-        ** go around.
-        **
-        ** instead of sending the current block, a client should send
-        ** all data in the queue, plus the current block, until either
-        ** it runs out of data, or it hits a recoverable error like
-        ** EAGAIN.  this will allow a client that got slightly lagged
-        ** to catch back up if it can
-        */
+    /* delete this sources stats */
+    stats_event_dec (NULL, "sources");
+    stats_event (source->mount, "listeners", NULL);

-        /* First, stream dumping, if enabled */
-        if(source->dumpfile) {
-            if(fwrite(refbuf->data, 1, refbuf->len, source->dumpfile) !=
-                    refbuf->len)
-            {
-                WARN1("Write to dump file failed, disabling: %s",
-                        strerror(errno));
-                fclose(source->dumpfile);
-                source->dumpfile = NULL;
-            }
-        }
+    /* we don't remove the source from the tree here, it may be a relay and
+       therefore reserved */
+    source_clear_source (source);

-        /* acquire read lock on client_tree */
-        avl_tree_rlock(source->client_tree);
+    thread_mutex_unlock (&source->lock);

-        client_node = avl_get_first(source->client_tree);
-        while (client_node) {
-            /* acquire read lock on node */
-            avl_node_wlock(client_node);
+    global_lock();
+    global.sources--;
+    global_unlock();

-            client = (client_t *)client_node->key;
-
-            data_done = 0;
+    /* release our hold on the lock so the main thread can continue cleaning up */
+    thread_rwlock_unlock(source->shutdown_rwlock);
+}

-            /* do we have any old buffers? */
-            abuf = refbuf_queue_remove(&client->queue);
-            while (abuf) {
-                if (client->pos > 0)
-                    bytes = abuf->len - client->pos;
-                else
-                    bytes = abuf->len;

-                sbytes = source->format->write_buf_to_client(source->format,
-                        client, &abuf->data[client->pos], bytes);
-                if (sbytes >= 0) {
-                    if(sbytes != bytes) {
-                        /* We didn't send the entire buffer. Leave it for
-                         * the moment, handle it in the next iteration.
-                         */
-                        client->pos += sbytes;
-                        refbuf_queue_insert(&client->queue, abuf);
-                        data_done = 1;
-                        break;
-                    }
-                }
-                else {
-                    DEBUG0("Client has unrecoverable error catching up. "
-                            "Client has probably disconnected");
-                    client->con->error = 1;
-                    data_done = 1;
-                    refbuf_release(abuf);
-                    break;
-                }
-
-                /* we're done with that refbuf, release it and reset the pos */
-                refbuf_release(abuf);
-                client->pos = 0;
+void add_authenticated_client (source_t *source, client_t *client)
+{
+    /* lets add the client to the pending list */
+    client->next = source->pending_clients;
+    source->pending_clients = client;

-                abuf = refbuf_queue_remove(&client->queue);
-            }
-
-            /* now send or queue the new data */
-            if (data_done) {
-                refbuf_addref(refbuf);
-                refbuf_queue_add(&client->queue, refbuf);
-            } else {
-                sbytes = source->format->write_buf_to_client(source->format,
-                        client, refbuf->data, refbuf->len);
-                if (sbytes >= 0) {
-                    if(sbytes != refbuf->len) {
-                        /* Didn't send the entire buffer, queue it */
-                        client->pos = sbytes;
-                        refbuf_addref(refbuf);
-                        refbuf_queue_insert(&client->queue, refbuf);
-                    }
-                }
-                else {
-                    DEBUG0("Client had unrecoverable error with new data, "
-                            "probably due to client disconnection");
-                    client->con->error = 1;
-                }
-            }
+    client->predata_size = 4096;
+    client->predata = calloc (1, client->predata_size);
+    sock_set_blocking (client->con->sock, SOCK_NONBLOCK);
+    sock_set_nodelay (client->con->sock);
+    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;
+        slave_recheck();
+    }
+    DEBUG1 ("Added client to pending on %s", source->mount);
+    source->check_pending = 1;
+    stats_event_inc (NULL, "clients");
+    stats_event_inc (source->mount, "clients");
+}

-            /* if the client is too slow, its queue will slowly build up.
-            ** we need to make sure the client is keeping up with the
-            ** data, so we'll kick any client who's queue gets to large.
-            */
-            if (refbuf_queue_length(&client->queue) > source->queue_size_limit) {
-                DEBUG0("Client has fallen too far behind, removing");
-                client->con->error = 1;
-            }

-            /* release read lock on node */
-            avl_node_unlock(client_node);
+/* try to add client to a pending list.  return
+ *  0 for success
+ *  -1 too many clients
+ *  -2 mount needs authentication
+ *  -3 mount is unavailable
+ */
+static int _add_client (char *passed_mount, client_t *client, int initial_connection)
+{
+    source_t *source;
+    char *mount = passed_mount;
+    int ret;

-            /* get the next node */
-            client_node = avl_get_next(client_node);
-        }
-        /* release read lock on client_tree */
-        avl_tree_unlock(source->client_tree);
+    while (1)
+    {
+        source = source_find_mount (mount);
+        if (passed_mount != mount)
+            free (mount);
+        if (source == NULL)
+            return -3;
+        if (initial_connection && source->no_mount
+                && strcmp (source->mount, passed_mount) == 0)
+            return -3;
+        thread_mutex_lock (&source->lock);

-        /* Only release the refbuf if we didn't add it to the source queue */
-        if (!source->burst_on_connect) {
-            refbuf_release(refbuf);
-        }
+        if (source->running || source->on_demand)
+        {
+            DEBUG2 ("max on %s is %d", source->mount, source->max_listeners);
+            DEBUG2 ("pending %d, current %d", source->new_listeners, source->listeners);
+            if (source->max_listeners == -1)
+                break;
+            if (source->new_listeners + source->listeners < source->max_listeners)
+                break;

-        /* acquire write lock on client_tree */
-        avl_tree_wlock(source->client_tree);
-
-        /** delete bad clients **/
-        client_node = avl_get_first(source->client_tree);
-        while (client_node) {
-            client = (client_t *)client_node->key;
-            if (client->con->error) {
-                client_node = avl_get_next(client_node);
-                avl_delete(source->client_tree, (void *)client, _free_client);
-                source->listeners--;
-                stats_event_args(source->mount, "listeners", "%d",
-                        source->listeners);
-                DEBUG0("Client removed");
-                continue;
+            INFO2 ("max listeners (%d) reached on %s", source->max_listeners, source->mount);
+            if (source->fallback_when_full == 0 || source->fallback_mount == NULL)
+            {
+                thread_mutex_unlock (&source->lock);
+                return -1;
}
-            client_node = avl_get_next(client_node);
+            if (source->fallback_mount)
+                mount = strdup (source->fallback_mount);
+            else
+                mount = NULL;
}

-        /* acquire write lock on pending_tree */
-        avl_tree_wlock(source->pending_tree);
+        thread_mutex_unlock (&source->lock);
+    }

-        /** add pending clients **/
-        client_node = avl_get_first(source->pending_tree);
-        while (client_node) {
-            if(source->max_listeners != -1 &&
-                    source->listeners >= 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);
+    if (auth_check_client (source, client) != AUTH_OK)
+    {
+        thread_mutex_unlock (&source->lock);
+        INFO0 ("listener failed to authenticate");
+        return -2;
+    }
+    source->new_listeners++;

-                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);
+    thread_mutex_unlock (&source->lock);
+    return ret;
+}

-            source->listeners++;
-            DEBUG0("Client added");
-            stats_event_inc(NULL, "clients");
-            stats_event_inc(source->mount, "connections");
-            stats_event_args(source->mount, "listeners", "%d",
-                    source->listeners);

-            /* we have to send cached headers for some data formats
-            ** this is where we queue up the buffers to send
-            */
-            client = (client_t *)client_node->key;
-            if (source->format->has_predata) {
-                client->queue = source->format->get_predata(source->format);
-            }
-            if (source->burst_on_connect) {
-                /* here is where we fill up the new client with refbufs from
-                   the source buffer.  this will allow an initial burst of
-                   audio data to be sent to the client, and allow for a faster
-                   startup time (from listener perspective) for the stream */
-                if (!client->burst_sent) {
-                    thread_mutex_lock(&source->queue_mutex);
-                    for (i=0;i<refbuf_queue_size(&(source->queue));i++) {
-                        refbuf_queue_add(&(client->queue),
-                            refbuf_queue_get(&(source->queue), i));
-                    }
-                    thread_mutex_unlock(&source->queue_mutex);
-                    client->burst_sent = 1;
-                    DEBUG1("Added %d buffers to initial client queue",
-                            refbuf_queue_length(&(source->queue)));
-                }
-            }
+void add_client (char *mount, client_t *client)
+{
+    int added = -3;

-            client_node = avl_get_next(client_node);
-        }
+    if (mount)
+    {
+        thread_mutex_lock (&move_clients_mutex);
+        avl_tree_rlock (global.source_tree);
+        added = _add_client (mount, client, 1);
+        avl_tree_unlock (global.source_tree);
+        thread_mutex_unlock (&move_clients_mutex);
+    }
+    switch (added)
+    {
+    case -1:
+        client_send_404 (client, "Too many clients on this mountpoint. Try again later.");
+        DEBUG1 ("max clients on %s", mount);
+        break;
+    case -2:
+        client_send_401 (client);
+        break;
+    case -3:
+        client_send_404 (client, "The file you requested could not be found");
+        break;
+    default:
+        return;
+    }
+    /* failed client, drop global count */
+    global_lock();
+    global.clients--;
+    global_unlock();
+}

-        /** 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);
+static void process_pending_clients (source_t *source)
+{
+    unsigned count = 0;
+    client_t *client = source->pending_clients;

-        /* release write lock on client_tree */
-        avl_tree_unlock(source->client_tree);
+    while (client)
+    {
+        client_t *to_go = client;
+        int drop_client = 0;
+
+        client = client->next;
+        /* do we need to handle http style headers */
+        if (to_go->respcode == 0)
+        {
+            DEBUG0("processing pending client headers");
+
+            format_prepare_headers (source, to_go);
+            if (source->format->create_client_data &&
+                    source->format->create_client_data (source, to_go) < 0)
+                drop_client = 1;
+        }
+        if (drop_client)
+        {
+            /* shouldn't happen, but don't stall */
+            ERROR0 ("dropping pending client");
+            to_go->respcode = 200;
+            source_free_client (source, to_go);
+        }
+        else
+        {
+            to_go->next = source->active_clients;
+            source->active_clients = to_go;
+            if (*source->active_clients_tail == to_go)
+                source->active_clients_tail = &to_go->next;
+//            *source->active_clients_tail = to_go;
+            //source->active_clients_tail = &to_go->next;
+            count++;
+        }
+        source->new_listeners--;
}
+    source->pending_clients = NULL;
+    source->pending_clients_tail = &source->pending_clients;
+    source->check_pending = 0;

-done:
+    if (count)
+    {
+        DEBUG1("Adding %d client(s)", count);
+        source->listeners += count;
+    }
+}

-    source->running = 0;
-    INFO1("Source \"%s\" exiting", source->mount);

-    /* 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)
+void source_main(source_t *source)
+{
+    long bytes;
+    int listeners = 0;
+
+    source_init (source);
+
+    bytes = 0;
+    listeners = 0;
+
+    while (global.running == ICE_RUNNING && source->running)
{
-        source_t *fallback_source;
+        int remove_from_q;

-        avl_tree_rlock(global.source_tree);
-        fallback_source = source_find_mount (source->fallback_mount);
+        get_next_buffer (source);

-        if (fallback_source != NULL)
-            source_move_clients (source, fallback_source);
+        remove_from_q = 0;

-        avl_tree_unlock (global.source_tree);
-    }
+        /* lets see if we have too much data in the queue, but do not
+           remove it until later */
+        if (source->queue_size > source->queue_size_limit)
+            remove_from_q = 1;

-    /* delete this sources stats */
-    stats_event_dec(NULL, "sources");
-    stats_event(source->mount, "listeners", NULL);
+        /* add pending clients */
+        if (source->check_pending)
+            process_pending_clients (source);

-    /* we don't remove the source from the tree here, it may be a relay and
-       therefore reserved */
-    source_clear_source (source);
+        process_listeners (source, 0, remove_from_q);

-    global_lock();
-    global.sources--;
-    global_unlock();
+        /* has the listener count changed */
+        if (source->listeners != listeners)
+        {
+            INFO2("listener count on %s now %d", source->mount, source->listeners);
+            stats_event_args (source->mount, "listeners", "%d", source->listeners);
+            if (source->listeners == 0 && source->on_demand)
+                source->running = 0;
+            listeners = source->listeners;
+        }

-    /* release our hold on the lock so the main thread can continue cleaning up */
-    thread_rwlock_unlock(source->shutdown_rwlock);
+        if (remove_from_q)
+        {
+            refbuf_t *to_go = source->stream_data;
+            /* associated data is shared so don't release it if the next refbuf refers to it */
+            if (to_go->next)
+            {
+                source->stream_data = to_go->next;
+                if (to_go->associated == source->stream_data->associated)
+                    to_go->associated = NULL;
+                source->queue_size -= to_go->len;
+                if (source->burst_point == to_go)
+                {
+                    source->burst_point = to_go->next;
+                    source->burst_size -= to_go->len;
+                }
+                if (source->format->prerelease)
+                    source->format->prerelease (source, to_go);
+                refbuf_release (to_go);
+                // DEBUG1 ("releasing %p", to_go);
+            }
+            else
+                WARN0("possible queue length error");
+        }
+    }
+    source->running = 0;

-    return;
+    source_shutdown (source);
}

+
static int _compare_clients(void *compare_arg, void *a, void *b)
{
client_t *clienta = (client_t *)a;
@@ -810,26 +1060,30 @@
return 0;
}

-int source_remove_client(void *key)
-{
-    return 1;
-}

-static int _free_client(void *key)
+int source_free_client (source_t *source, client_t *client)
{
-    client_t *client = (client_t *)key;
-
global_lock();
global.clients--;
global_unlock();
stats_event_dec(NULL, "clients");
+
+    if (source && source->authenticator && source->authenticator->release_client)
+    {
+        source->authenticator->release_client (source, client);
+        return 0;
+    }
+    /* if no response has been sent then send a 404 */
+    if (client->respcode == 0)
+        client_send_404 (client, "Mount unavailable");
+    else
+        client_destroy (client);

-    client_destroy(client);
-
return 1;
}

-static void _parse_audio_info (source_t *source, const char *s)
+
+static void parse_audio_info (source_t *source, const char *s)
{
const char *start = s;
unsigned len;
@@ -841,15 +1095,15 @@
else
{
len = (int)(s - start);
-            s++; /* skip passed the ';' */
+            s++; /* skip passed the ';' */
}
if (len)
{
-            char name[100], value[100];
+            char name[200], value[200];
char *esc;

sscanf (start, "%199[^=]=%199[^;\r\n]", name, value);
-            esc = util_url_unescape (value);
+            esc = util_url_unescape (value);
if (esc)
{
util_dict_set (source->audio_info, name, esc);
@@ -894,6 +1148,31 @@
source->timeout = mountinfo->source_timeout;
DEBUG1 ("source timeout to %u", source->timeout);
}
+    if (mountinfo->burst_size)
+    {
+        source->burst_size_limit = mountinfo->burst_size;
+        DEBUG1 ("burst size to %u", source->burst_size_limit);
+    }
+    if (mountinfo->fallback_when_full)
+    {
+        source->fallback_when_full = mountinfo->fallback_when_full;
+        DEBUG1 ("fallback_when_full to %u", source->fallback_when_full);
+    }
+    if (mountinfo->no_yp)
+    {
+        source->yp_prevent = 1;
+        DEBUG0("prevent YP listings");
+    }
+    if (mountinfo->on_connect)
+    {
+        source->on_connect = strdup(mountinfo->on_connect);
+        DEBUG1 ("connect script \"%s\"", source->on_connect);
+    }
+    if (mountinfo->on_disconnect)
+    {
+        source->on_disconnect = strdup(mountinfo->on_disconnect);
+        DEBUG1 ("disconnect script \"%s\"", source->on_disconnect);
+    }
}


@@ -905,7 +1184,7 @@

source->client->respcode = 200;
bytes = sock_write_bytes (source->client->con->sock, ok_msg, sizeof (ok_msg)-1);
-    if (bytes < sizeof (ok_msg)-1)
+    if (bytes < (int)sizeof (ok_msg)-1)
{
global_lock();
global.sources--;
@@ -923,3 +1202,37 @@
return NULL;
}

+
+#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, 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

Modified: icecast/branches/kh/icecast/src/source.h
===================================================================
--- icecast/trunk/icecast/src/source.h	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/source.h	2004-07-11 18:09:05 UTC (rev 7095)
@@ -39,27 +39,47 @@

struct _format_plugin_tag *format;

-    avl_tree *client_tree;
-    avl_tree *pending_tree;
+    client_t *active_clients;
+    client_t **active_clients_tail;
+    client_t *first_normal_client;

+    int check_pending;
+    client_t *pending_clients;
+    client_t **pending_clients_tail;
+    long new_listeners;
+
rwlock_t *shutdown_rwlock;
util_dict *audio_info;

char *dumpfilename; /* Name of a file to dump incoming stream to */
FILE *dumpfile;

-    int    num_yp_directories;
long listeners;
long max_listeners;
int yp_public;
+    int yp_prevent;
struct auth_tag *authenticator;
int fallback_override;
+    int fallback_when_full;
int no_mount;
unsigned queue_size_limit;
unsigned timeout;  /* source timeout in seconds */
-    refbuf_queue_t *queue;
-    mutex_t queue_mutex;
-    int burst_on_connect;
+
+    int on_demand;
+    int on_demand_req;
+
+    time_t last_read;
+    char *on_connect;
+    char *on_disconnect;
+
+    mutex_t lock;
+    unsigned queue_size;
+    unsigned burst_size;
+    unsigned burst_size_limit;
+    refbuf_t *burst_point;
+
+    refbuf_t *stream_data, *stream_data_tail;
+
} source_t;

source_t *source_reserve (const char *mount);
@@ -74,6 +94,9 @@
void source_move_clients (source_t *source, source_t *dest);
int source_remove_client(void *key);
void source_main(source_t *source);
+void add_client (char *mount, client_t *client);
+void add_authenticated_client (source_t *source, client_t *client);
+int source_free_client (source_t *source, client_t *client);

extern mutex_t move_clients_mutex;


Modified: icecast/branches/kh/icecast/src/stats.c
===================================================================
--- icecast/trunk/icecast/src/stats.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/stats.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -83,14 +83,14 @@
_stats.source_tree = avl_tree_new(_compare_source_stats, NULL);

/* set up global mutex */
-    thread_mutex_create(&_stats_mutex);
+    thread_mutex_create("stats", &_stats_mutex);

/* set up event signaler */
thread_cond_create(&_event_signal_cond);

/* set up stats queues */
_global_event_queue = NULL;
-    thread_mutex_create(&_global_event_mutex);
+    thread_mutex_create("stats_global_event", &_global_event_mutex);

/* fire off the stats thread */
_stats_running = 1;
@@ -633,7 +633,7 @@
_stats_threads++;
thread_mutex_unlock(&_stats_mutex);

-    thread_mutex_create(&local_event_mutex);
+    thread_mutex_create("stats local event", &local_event_mutex);

_atomic_get_and_register(&local_event_queue, &local_event_mutex);

@@ -801,8 +801,7 @@
if (bytes > 0) client->con->sent_bytes += bytes;
else goto send_error;

-    bytes = sock_write_bytes(client->con->sock, buff, len);
-    if (bytes > 0) client->con->sent_bytes += bytes;
+    bytes = client_send_bytes (client, buff, (unsigned)len);

send_error:
while (src_nodes) {

Modified: icecast/branches/kh/icecast/src/util.c
===================================================================
--- icecast/trunk/icecast/src/util.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/util.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -450,7 +450,7 @@
vals[3] = base64decode[*input++];

if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
-            len -= 4;
+            len -=4;
continue;
}


Modified: icecast/branches/kh/icecast/src/xslt.c
===================================================================
--- icecast/trunk/icecast/src/xslt.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/xslt.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -68,7 +68,7 @@
void xslt_initialize()
{
memset(cache, 0, sizeof(stylesheet_cache_t)*CACHESIZE);
-    thread_mutex_create(&xsltlock);
+    thread_mutex_create("xslt", &xsltlock);
}

void xslt_shutdown() {

Modified: icecast/branches/kh/icecast/src/yp.c
===================================================================
--- icecast/trunk/icecast/src/yp.c	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/src/yp.c	2004-07-11 18:09:05 UTC (rev 7095)
@@ -229,7 +229,7 @@
}
server->url = strdup (config->yp_url[i]);
server->url_timeout = config->yp_url_timeout[i];
-            server->touch_interval = config->touch_interval;
+            server->touch_interval = config->yp_touch_interval[i];
server->curl = curl_easy_init();
if (server->curl == NULL)
{
@@ -264,7 +264,7 @@
{
ice_config_t *config = config_get_config();
thread_rwlock_create (&yp_lock);
-    thread_mutex_create (&yp_pending_lock);
+    thread_mutex_create ("yp", &yp_pending_lock);
yp_recheck_config (config);
config_release_config ();
yp_thread = thread_create("YP Touch Thread", yp_update_thread,
@@ -287,7 +287,7 @@
if (curlcode)
{
yp->process = do_yp_add;
-        yp->next_update += 300;
+        yp->next_update += 60;
ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
return -1;
}
@@ -296,7 +296,7 @@
if (yp->error_msg == NULL)
yp->error_msg = strdup ("no response from server");
yp->process = do_yp_add;
-        yp->next_update += 300;
+        yp->next_update += 60;
ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
return -1;
}
@@ -322,13 +322,16 @@
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 = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
+    int ret;
+
+    ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
"%s&url=%s&listenurl=%s&type=%s&b=%s&%s\r\n",
yp->server_name, yp->server_genre, yp->cluster_password,
yp->server_desc, yp->url, yp->listen_url,
@@ -374,7 +377,6 @@
}
free (artist);
free (title);
-
val = (char *)stats_get_value (yp->mount, "listeners");
if (val)
{
@@ -564,6 +566,7 @@
ypdata_t *yp;

source_t *source = node->key;
+            thread_mutex_lock (&source->lock);
if ((yp = create_yp_entry (source)) != NULL)
{
DEBUG1 ("Adding existing mount %s", source->mount);
@@ -572,6 +575,7 @@
yp->next = server->mounts;
server->mounts = yp;
}
+            thread_mutex_unlock (&source->lock);
node = avl_get_next (node);
}
avl_tree_unlock (global.source_tree);
@@ -671,7 +675,6 @@
active_yps = server->next;
destroy_yp_server (server);
}
-
return NULL;
}

@@ -728,6 +731,7 @@
if (!info)
return;

+    /* DEBUG2 ("stat %s with %s", stat_name, info); */
switch (type)
{
case YP_SERVER_NAME:

Modified: icecast/branches/kh/icecast/win32/icecast.dsp
===================================================================
--- icecast/trunk/icecast/win32/icecast.dsp	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/win32/icecast.dsp	2004-07-11 18:09:05 UTC (rev 7095)
@@ -64,7 +64,7 @@
# 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 /MTd /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" /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.0.0\" /D "HAVE_LOCALTIME_R" /FD /D /GZ /c
+# ADD CPP /nologo /MTd /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" /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.0.0\" /FD /D /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@@ -101,6 +101,14 @@
# 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
@@ -161,11 +169,11 @@
# End Source File
# Begin Source File

-SOURCE=..\src\format_vorbis.c
+SOURCE=..\src\format_ogg.c
# End Source File
# Begin Source File

-SOURCE=..\src\format_vorbis.h
+SOURCE=..\src\format_ogg.h
# End Source File
# Begin Source File

@@ -313,6 +321,18 @@
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File

+SOURCE=..\src\auth_cmd.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\timing\timing.h
# End Source File
# Begin Source File

Modified: icecast/branches/kh/icecast/win32/icecast2.iss
===================================================================
--- icecast/trunk/icecast/win32/icecast2.iss	2004-07-11 17:25:14 UTC (rev 7094)
+++ icecast/branches/kh/icecast/win32/icecast2.iss	2004-07-11 18:09:05 UTC (rev 7095)
@@ -2,8 +2,8 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
-AppName=Icecast2 Win32
-AppVerName=Icecast v2.0.0
+AppName=Icecast2 KH47 Win32
+AppVerName=Icecast v2.0.0 KH47
AppPublisherURL=http://www.icecast.org
AppSupportURL=http://www.icecast.org
AppUpdatesURL=http://www.icecast.org
@@ -13,7 +13,7 @@
LicenseFile=..\COPYING
InfoAfterFile=..\README
OutputDir=.
-OutputBaseFilename=icecast2_win32_2.0.0_setup
+OutputBaseFilename=icecast2_win32_2.0.0_KH47_setup
WizardImageFile=icecast2logo2.bmp
; uncomment the following line if you want your installation to run on NT 3.51 too.
; MinVersion=4,3.51



More information about the commits mailing list