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

j at dactyl.lonelymoon.com j
Fri Jun 25 13:50:13 PDT 2004


icecast-kh/admin icecast-kh/conf icecast-kh/debian
icecast-kh/doc icecast-kh/src icecast-kh/web icecast-kh/win32
icecast-kh/win32/res
Message-ID: <20040625205013.855C69AAAB at dactyl.lonelymoon.com>

Author: j
Date: Fri Jun 25 13:50:13 2004
New Revision: 6848

Added:
icecast/branches/icecast-kh/
icecast/branches/icecast-kh/AUTHORS
icecast/branches/icecast-kh/COPYING
icecast/branches/icecast-kh/HACKING
icecast/branches/icecast-kh/Makefile.am
icecast/branches/icecast-kh/NEWS
icecast/branches/icecast-kh/README
icecast/branches/icecast-kh/TODO
icecast/branches/icecast-kh/admin/
icecast/branches/icecast-kh/admin/Makefile.am
icecast/branches/icecast-kh/admin/listclients.xsl
icecast/branches/icecast-kh/admin/listmounts.xsl
icecast/branches/icecast-kh/admin/manageauth.xsl
icecast/branches/icecast-kh/admin/moveclients.xsl
icecast/branches/icecast-kh/admin/response.xsl
icecast/branches/icecast-kh/admin/stats.xsl
icecast/branches/icecast-kh/autogen.sh
icecast/branches/icecast-kh/conf/
icecast/branches/icecast-kh/conf/Makefile.am
icecast/branches/icecast-kh/conf/icecast.xml.in
icecast/branches/icecast-kh/configure.in
icecast/branches/icecast-kh/debian/
icecast/branches/icecast-kh/debian/Makefile.am
icecast/branches/icecast-kh/debian/README.Debian
icecast/branches/icecast-kh/debian/changelog
icecast/branches/icecast-kh/debian/compat
icecast/branches/icecast-kh/debian/control
icecast/branches/icecast-kh/debian/copyright
icecast/branches/icecast-kh/debian/icecast2.1
icecast/branches/icecast-kh/debian/icecast2.default
icecast/branches/icecast-kh/debian/icecast2.init
icecast/branches/icecast-kh/debian/icecast2.manpages
icecast/branches/icecast-kh/debian/icecast2.postinst
icecast/branches/icecast-kh/debian/icecast2.postrm
icecast/branches/icecast-kh/debian/icecast2.preinst
icecast/branches/icecast-kh/debian/rules
icecast/branches/icecast-kh/debian/watch
icecast/branches/icecast-kh/doc/
icecast/branches/icecast-kh/doc/Index.hhk
icecast/branches/icecast-kh/doc/Makefile.am
icecast/branches/icecast-kh/doc/icecast2.chm
icecast/branches/icecast-kh/doc/icecast2.hhc
icecast/branches/icecast-kh/doc/icecast2.hhp
icecast/branches/icecast-kh/doc/icecast2_admin.html
icecast/branches/icecast-kh/doc/icecast2_basicsetup.html
icecast/branches/icecast-kh/doc/icecast2_config_file.html
icecast/branches/icecast-kh/doc/icecast2_faq.html
icecast/branches/icecast-kh/doc/icecast2_glossary.html
icecast/branches/icecast-kh/doc/icecast2_introduction.html
icecast/branches/icecast-kh/doc/icecast2_relay.html
icecast/branches/icecast-kh/doc/icecast2_stats.html
icecast/branches/icecast-kh/doc/icecast2_win32.html
icecast/branches/icecast-kh/doc/icecast2_yp.html
icecast/branches/icecast-kh/doc/index.html
icecast/branches/icecast-kh/doc/index_win32.html
icecast/branches/icecast-kh/doc/stats1.jpg
icecast/branches/icecast-kh/doc/style.css
icecast/branches/icecast-kh/doc/win32_section1.html
icecast/branches/icecast-kh/doc/win32_section2.html
icecast/branches/icecast-kh/doc/win32_section3.html
icecast/branches/icecast-kh/doc/windowtitle.jpg
icecast/branches/icecast-kh/icecast.spec
icecast/branches/icecast-kh/src/
icecast/branches/icecast-kh/src/Makefile.am
icecast/branches/icecast-kh/src/TODO
icecast/branches/icecast-kh/src/admin.c
icecast/branches/icecast-kh/src/admin.h
icecast/branches/icecast-kh/src/auth.c
icecast/branches/icecast-kh/src/auth.h
icecast/branches/icecast-kh/src/auth_cmd.c
icecast/branches/icecast-kh/src/auth_cmd.h
icecast/branches/icecast-kh/src/auth_htpasswd.c
icecast/branches/icecast-kh/src/auth_htpasswd.h
icecast/branches/icecast-kh/src/auth_url.c
icecast/branches/icecast-kh/src/auth_url.h
icecast/branches/icecast-kh/src/cfgfile.c
icecast/branches/icecast-kh/src/cfgfile.h
icecast/branches/icecast-kh/src/client.c
icecast/branches/icecast-kh/src/client.h
icecast/branches/icecast-kh/src/compat.h
icecast/branches/icecast-kh/src/connection.c
icecast/branches/icecast-kh/src/connection.h
icecast/branches/icecast-kh/src/event.c
icecast/branches/icecast-kh/src/event.h
icecast/branches/icecast-kh/src/format.c
icecast/branches/icecast-kh/src/format.h
icecast/branches/icecast-kh/src/format_mp3.c
icecast/branches/icecast-kh/src/format_mp3.h
icecast/branches/icecast-kh/src/format_ogg.c
icecast/branches/icecast-kh/src/format_ogg.h
icecast/branches/icecast-kh/src/format_vorbis.c
icecast/branches/icecast-kh/src/format_vorbis.h
icecast/branches/icecast-kh/src/fserve.c
icecast/branches/icecast-kh/src/fserve.h
icecast/branches/icecast-kh/src/global.c
icecast/branches/icecast-kh/src/global.h
icecast/branches/icecast-kh/src/logging.c
icecast/branches/icecast-kh/src/logging.h
icecast/branches/icecast-kh/src/main.c
icecast/branches/icecast-kh/src/md5.c
icecast/branches/icecast-kh/src/md5.h
icecast/branches/icecast-kh/src/os.h
icecast/branches/icecast-kh/src/refbuf.c
icecast/branches/icecast-kh/src/refbuf.h
icecast/branches/icecast-kh/src/sighandler.c
icecast/branches/icecast-kh/src/sighandler.h
icecast/branches/icecast-kh/src/slave.c
icecast/branches/icecast-kh/src/slave.h
icecast/branches/icecast-kh/src/source.c
icecast/branches/icecast-kh/src/source.h
icecast/branches/icecast-kh/src/stats.c
icecast/branches/icecast-kh/src/stats.h
icecast/branches/icecast-kh/src/util.c
icecast/branches/icecast-kh/src/util.h
icecast/branches/icecast-kh/src/xslt.c
icecast/branches/icecast-kh/src/xslt.h
icecast/branches/icecast-kh/src/yp.c
icecast/branches/icecast-kh/src/yp.h
icecast/branches/icecast-kh/web/
icecast/branches/icecast-kh/web/Makefile.am
icecast/branches/icecast-kh/web/corner_bottomleft.jpg
icecast/branches/icecast-kh/web/corner_bottomright.jpg
icecast/branches/icecast-kh/web/corner_topleft.jpg
icecast/branches/icecast-kh/web/corner_topright.jpg
icecast/branches/icecast-kh/web/icecast.png
icecast/branches/icecast-kh/web/key.gif
icecast/branches/icecast-kh/web/status.xsl
icecast/branches/icecast-kh/web/status2.xsl
icecast/branches/icecast-kh/web/style.css
icecast/branches/icecast-kh/win32/
icecast/branches/icecast-kh/win32/ConfigTab.cpp
icecast/branches/icecast-kh/win32/ConfigTab.h
icecast/branches/icecast-kh/win32/Icecast2win.clw
icecast/branches/icecast-kh/win32/Icecast2win.cpp
icecast/branches/icecast-kh/win32/Icecast2win.dsp
icecast/branches/icecast-kh/win32/Icecast2win.dsw
icecast/branches/icecast-kh/win32/Icecast2win.h
icecast/branches/icecast-kh/win32/Icecast2win.rc
icecast/branches/icecast-kh/win32/Icecast2winDlg.cpp
icecast/branches/icecast-kh/win32/Icecast2winDlg.h
icecast/branches/icecast-kh/win32/Makefile.am
icecast/branches/icecast-kh/win32/ResizableDialog.cpp
icecast/branches/icecast-kh/win32/ResizableDialog.h
icecast/branches/icecast-kh/win32/StatsTab.cpp
icecast/branches/icecast-kh/win32/StatsTab.h
icecast/branches/icecast-kh/win32/Status.cpp
icecast/branches/icecast-kh/win32/Status.h
icecast/branches/icecast-kh/win32/StdAfx.cpp
icecast/branches/icecast-kh/win32/StdAfx.h
icecast/branches/icecast-kh/win32/TRAYNOT.h
icecast/branches/icecast-kh/win32/TabCtrlSSL.cpp
icecast/branches/icecast-kh/win32/TabCtrlSSL.h
icecast/branches/icecast-kh/win32/TabPageSSL.cpp
icecast/branches/icecast-kh/win32/TabPageSSL.h
icecast/branches/icecast-kh/win32/Traynot.cpp
icecast/branches/icecast-kh/win32/black.bmp
icecast/branches/icecast-kh/win32/colors.h
icecast/branches/icecast-kh/win32/icecast.dsp
icecast/branches/icecast-kh/win32/icecast.ico
icecast/branches/icecast-kh/win32/icecast2.iss
icecast/branches/icecast-kh/win32/icecast2_console.dsp
icecast/branches/icecast-kh/win32/icecast2_console.dsw
icecast/branches/icecast-kh/win32/icecast2logo2.bmp
icecast/branches/icecast-kh/win32/res/
icecast/branches/icecast-kh/win32/res/Icecast2win.rc2
icecast/branches/icecast-kh/win32/res/Makefile.am
icecast/branches/icecast-kh/win32/resource.h
icecast/branches/icecast-kh/win32/running.bmp
icecast/branches/icecast-kh/win32/stopped.bmp
Log:
import of icecast-kh

Added: icecast/branches/icecast-kh/AUTHORS
===================================================================
--- icecast/branches/icecast-kh/AUTHORS	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/AUTHORS	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,4 @@
+Jack Moffitt <jack at icecast.org>
+Michael Smith <msmith at icecast.org>
+oddsock <oddsock at oddsock.org>
+Karl Heyes <karl at xiph.org>

Added: icecast/branches/icecast-kh/COPYING
===================================================================
--- icecast/branches/icecast-kh/COPYING	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/COPYING	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: icecast/branches/icecast-kh/HACKING
===================================================================
--- icecast/branches/icecast-kh/HACKING	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/HACKING	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,48 @@
+Note that these instructions are *not* necessary for distribution
+tarballs; they have separate configure/build instructions.
+
+Building this package from CVS is mainly intended for developers.
+General users should obtain official distribution packages; both
+source and binary distributions are available at
+http://www.icecast.org/
+
+-----
+
+These are *brief* instructions on how to build this package from CVS.
+Yes, there are details left out.
+
+There are generally four steps necessary when building from CVS (i.e.,
+a developer's copy):
+
+1. cvs checkout of the sources, or cvs update.  RTFM from your
+   favorite flavor of CVS documentation; information on the xiph.org
+   CVS repository can be found at http://www.xiph.org/cvs.html.
+
+2. [re-]generate files such as "configure" and "Makefile.in" with the
+   GNU autoconf/automake tools.  Run the "autogen.sh" script to
+   perform this step.
+
+   *** IF YOU ARE NOT BUILDING WITH GNU MAKE *AND* GCC: you must set
+   the AUTOMAKE_FLAGS environment variable to "--include-deps"
+   before running autogen.sh.  For example:
+
+   csh% setenv AUTOMAKE_FLAGS --include-deps
+   csh% ./autogen.sh
+     or
+   sh% AUTOMAKE_FLAGS=--include-deps ./autogen.sh
+
+3. Run configure.  There are several options available; see
+   "./configure --help" for more information.
+
+4. Run "make" to build the source.
+
+In general, steps 2 and 3 need to be re-run every time any of the
+following files are modified (either manually or by a cvs update):
+
+	  configure.in
+	  acinclude.m4
+
+Running "make clean" after running steps 2 and 3 is generally also
+advisable before running step 4.  It isn't *always* necessary, but
+unless you understand the workings of autoconf/automake, it's safest
+to just do it.

Added: icecast/branches/icecast-kh/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = 1.6 foreign dist-zip
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src conf debian doc web admin win32
+
+EXTRA_DIST = HACKING m4/acx_pthread.m4 m4/ogg.m4 m4/vorbis.m4 \
+    m4/xiph_compiler.m4 m4/xiph_curl.m4 m4/xiph_net.m4 \
+    m4/xiph_types.m4 m4/xiph_xml2.m4 icecast.spec
+
+docdir = $(datadir)/doc/$(PACKAGE)
+doc_DATA = README AUTHORS COPYING NEWS TODO
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+
+static:
+	$(MAKE) all LDFLAGS="${LDFLAGS} -all-static"

Added: icecast/branches/icecast-kh/NEWS
===================================================================
--- icecast/branches/icecast-kh/NEWS	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/NEWS	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,416 @@
+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
+
+2003-04-23
+    Support aliases
+
+2003-03-09
+    Support listening on multiple sockets.
+
+2003-03-08
+    Support for shoutcast source protocol added.
+
+2003-03-08
+    Started implementing generic admin interface. Supports (so far):
+    - dynamic configuration of mount fallbacks
+        /admin/fallbacks?mount=/mount&fallback=/fallback
+    - setting of mp3 metadata
+        /admin/metadata?mount=/mount&mode=updinfo&song=New%20Title
+    - dumping raw xml stats
+        /admin/rawstats
+    - listing all connected clients on a mountpoint:
+        /admin/listclients?mount=/mountname
+
+2003-03-05
+    Implemented the ability to reread the config file on SIGHUP. For now, this
+    does not affect configuration for currently running sources (only new
+    sources and global parameters like max-listeners)
+
+2003-03-02
+    More features:
+    -- per mountpoint listener maxima
+    -- static configuration of mountpoint fallbacks
+    -- stream dumping (write incoming stream to disk)
+
+2003-02-27
+    Fix log buffering on win32 - previously, logs were never flushed, so they
+    only got output every few tens or hundreds of lines.
+
+2003-02-27
+	Support new icy-audio-info header, to communicate various parameters to
+    clients and yp servers, including sample rate, quality, channels, bitrate
+
+2003-02-25
+    Full support for relaying mp3 metadata (if turned on in config file)
+
+2003-02-25
+    Allow configuration of maximum client queue length (in bytes)
+
+2003-02-14
+    Finished full IPv6 support.
+
+2003-02-12
+	Allow configuring local mountpoint seperately from remote mountpoint for
+    relays
+
+2003-02-12
+	Per mountpoint usernames and passwords (for sources)
+
+2003-02-11
+	Now that it's been officially assigned, use application/ogg instead of
+	application/x-ogg
+
+2003-02-07
+    Allow relaying of mp3 streams from icecast 1.x and shoutcast
+
+2003-02-07
+    Added ability to configure individual relays (rather than just all streams
+    from a single server).
+
+2003-02-03
+    Added support for YP directory services listings
+	are only used by the yp listing routines
+
+2003-02-03
+    Support command line parameter -b to run in the background (not supported
+    on win32)
+
+2002-12-31
+    Implement configurable mountpoint fallbacks (on source exit, clients are
+    transferred to another mountpoint automatically, without disconnecting
+    them)
+
+2002-12-31
+    Implemented full mp3 metadata support.
+
+(older stuff is missing from here)
+

Added: icecast/branches/icecast-kh/README
===================================================================
--- icecast/branches/icecast-kh/README	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/README	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,21 @@
+Icecast2 Beta 1.
+
+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.
+
+To build icecast on a Unix platform, perform the following :
+
+Run
+   ./configure
+   make
+   make install
+
+To build and install this release.
+
+A sample config file will be placed in /usr/local/etc (on UNIX) or in the current working directory (on Win32) and is called icecast.xml
+
+Documentation for icecast is available in the doc directory, by viewing doc/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.
+

Added: icecast/branches/icecast-kh/TODO
===================================================================
--- icecast/branches/icecast-kh/TODO	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/TODO	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,89 @@
+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
+--------
+
+- pull out vorbis comments.  and send to stats. This seems to be being
+  done, but it isn't working right.
+
+- directory server GUID checks
+	directory server does GET /GUID-asldjfasldfjalsdkfjasldkfj HTTP/1.0
+	and either gets a 404 if it's wrong, or a 200 if it's correct.
+
+- adding new stats type, event.  events don't modify the global stats tree,
+	ie, source /1234.ogg disconnected
+
+- support W3C Extended Logging (http://www.w3.org/TR/WD-logfile.html)
+	toggle between this and Apache Combined Log Format in the config file.
+	default to apache style.
+
+- allow using get_predata() stuff to send an "intro" to any newly-connected
+  user?
+
+- 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)
+
+- httpp - split out query string for further processing
+
+- option to use ipv6 (equiv to using <bind-address>::</bindaddress>, I think.
+
+- abstract all admin functionality to a set of commands, and command handlers.
+  Make /admin/* just parse according to a set of rules, and dispatch generic
+  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.
+
+- do we need to use locks on the avl client_trees in source.c and fserv.c?
+
+
+

Added: icecast/branches/icecast-kh/admin/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/admin/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,8 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+admindir = $(pkgdatadir)/admin
+dist_admin_DATA = listclients.xsl listmounts.xsl moveclients.xsl response.xsl \
+	stats.xsl manageauth.xsl
+

Added: icecast/branches/icecast-kh/admin/listclients.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/listclients.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/listclients.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,79 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>List Connected Listeners</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)</h3>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>
+	    <td align="center">
+		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> |
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> |
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+	    </td></tr>
+	</table>
+<br></br>
+<table cellpadding="2" cellspacing="4" border="0" >
+		<tr>
+				<td ><b>IP</b></td>
+				<td ><b>Connected For</b></td>
+				<td ><b>User Agent</b></td>
+				<td ></td>
+		</tr>
+<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
+<xsl:for-each select="listener">
+		<tr>
+				<td><xsl:value-of select="IP" /><xsl:if test="username"> (<xsl:value-of select="username" />)</xsl:if></td>
+				<td><xsl:value-of select="Connected" /> seconds</td>
+				<td><xsl:value-of select="UserAgent" /></td>
+				<td><a class="nav2" href="killclient.xsl?mount={$themount}&amp;id={ID}">kill</a></td>
+		</tr>
+</xsl:for-each>
+</table>
+<br></br>
+<br></br>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/listclients.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/admin/listmounts.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/listmounts.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/listmounts.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,65 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>List Mountpoints</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)
+<xsl:if test="authenticator"> <a href="manageauth.xsl?mount={@mount}"><img border="0" src="/key.gif"/></a> </xsl:if>
+</h3>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>
+	    <td align="center">
+		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> |
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> |
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+                <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
+	    </td></tr>
+	</table>
+<br></br>
+<p><xsl:value-of select="listeners" /> Listener(s)</p>
+<br></br>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/listmounts.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/admin/manageauth.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/manageauth.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/manageauth.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,91 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>Show defined users</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="iceresponse">
+<xsl:value-of select="message" />
+</xsl:for-each>
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)</h3>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>
+	    <td align="center">
+		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> |
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> |
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+	    </td></tr>
+	</table>
+<br></br>
+<form method="GET" action="manageauth.xsl">
+<table cellpadding="2" cellspacing="4" border="0" >
+		<tr>
+				<td ><b>User Id</b></td>
+				<td ><b>Password</b></td>
+				<td ></td>
+		</tr>
+<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
+<xsl:for-each select="User">
+		<tr>
+				<td><xsl:value-of select="username" /></td>
+				<td><xsl:value-of select="password" /></td>
+				<td><a class="nav2" href="manageauth.xsl?mount={$themount}&amp;username={username}&amp;action=delete">delete</a></td>
+		</tr>
+</xsl:for-each>
+		<tr>
+				<td ><input type="text" name="username" /></td>
+				<td ><input type="text" name="password" /></td>
+		</tr>
+		<tr>
+				<td colspan="2"><input type="Submit" name="Submit" value="Add New User" /></td>
+		</tr>
+</table>
+<input type="hidden" name="mount" value="{@mount}"/>
+<input type="hidden" name="action" value="add"/>
+</form>
+<br></br>
+<br></br>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/manageauth.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/admin/moveclients.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/moveclients.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/moveclients.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,61 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<xsl:variable name = "currentmount" ><xsl:value-of select="current_source" /></xsl:variable>
+<h2>Move Clients from (<xsl:value-of select="current_source" />)</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Move to which mountpoint ?</h3>
+<xsl:for-each select="source">
+	<table border="0" cellpadding="1" cellspacing="5" >
+	<tr>
+		<td>Move from (<xsl:copy-of select="$currentmount" />) to (<xsl:value-of select="@mount" />)</td>
+		<td><xsl:value-of select="listeners" /> Listeners</td>
+		<td><a class="nav2" href="moveclients.xsl?mount={$currentmount}&amp;destination={@mount}">Move Clients</a></td>
+	</tr>
+	</table>
+<br></br>
+<br></br>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/moveclients.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/admin/response.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/response.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/response.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,54 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/iceresponse" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>Icecast Server Response</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Response</h3>
+<xsl:for-each select="/iceresponse">
+Message : <xsl:value-of select="message" /><br></br>
+Return Code: <xsl:value-of select="return" /><br></br>
+</xsl:for-each>
+<br></br>
+<br></br>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/response.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/admin/stats.xsl
===================================================================
--- icecast/branches/icecast-kh/admin/stats.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/admin/stats.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,101 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body bgcolor="black">
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List Mountpoints</a> |
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> |
+        	<a class="nav" href="stats.xsl">Stats</a> |
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<table border="0" width="90%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>Icecast Status Page</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<h3>Global Server Stats</h3>
+<table border="0" cellpadding="4">
+<xsl:for-each select="/icestats">
+<xsl:for-each select="*">
+<xsl:if test = "name()!='source'">
+<tr>
+	<td width="130"><xsl:value-of select="name()" /></td>
+	<td class="streamdata"><xsl:value-of select="." /></td>
+</tr>
+</xsl:if>
+</xsl:for-each>
+</xsl:for-each>
+</table>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br>
+<br></br>
+
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<xsl:if test = "listeners!=''">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)
+<xsl:if test="authenticator"> <a href="manageauth.xsl?mount={@mount}"><img border="0" src="/key.gif"/></a> </xsl:if>
+</h3>
+	<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
+	<tr>
+	    <td align="center">
+		<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> |
+        	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a> |
+        	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
+                <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
+	    </td></tr>
+	</table>
+<br></br>
+<table cellpadding="5" cellspacing="0" border="0">
+	<xsl:for-each select="*">
+	<tr>
+		<td width="130"><xsl:value-of select="name()" /></td>
+		<td class="streamdata"><xsl:value-of select="." /></td>
+	</tr>
+	</xsl:for-each>
+</table>
+<br></br>
+<br></br>
+</xsl:if>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="50"></td>
+</tr>
+</table>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/admin/stats.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/autogen.sh
===================================================================
--- icecast/branches/icecast-kh/autogen.sh	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/autogen.sh	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,113 @@
+#!/bin/sh
+# Run this to set up the build system: configure, makefiles, etc.
+# (based on the version in enlightenment's cvs)
+
+package="icecast"
+
+olddir=`pwd`
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+cd "$srcdir"
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have autoconf installed to compile $package."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+        DIE=1
+}
+VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/"
+
+# do we need automake?
+if test -r Makefile.am; then
+    echo Checking for automake version
+    options=`fgrep AUTOMAKE_OPTIONS Makefile.am`
+    AM_NEEDED=`echo "$options" | $VERSIONGREP`
+    AM_PROGS=automake
+    AC_PROGS=aclocal
+    if test -n "$AM_NEEDED" && test "x$AM_NEEDED" != "x$options"
+    then
+        AM_PROGS="automake-$AM_NEEDED automake$AM_NEEDED $AM_PROGS"
+        AC_PROGS="aclocal-$AM_NEEDED aclocal$AM_NEEDED $AC_PROGS"
+    else
+        AM_NEEDED=""
+    fi
+    AM_PROGS="$AUTOMAKE $AM_PROGS"
+    AC_PROGS="$ACLOCAL $AC_PROGS"
+    for am in $AM_PROGS; do
+      ($am --version > /dev/null 2>&1) 2>/dev/null || continue
+      ver=`$am --version | head -1 | $VERSIONGREP`
+      AWK_RES=`echo $ver $AM_NEEDED | awk '{ if ( $1 >= $2 ) print "yes"; else print "no" }'`
+      if test "$AWK_RES" = "yes"; then
+        AUTOMAKE=$am
+        echo "  found $AUTOMAKE"
+        break
+      fi
+    done
+    for ac in $AC_PROGS; do
+      ($ac --version > /dev/null 2>&1) 2>/dev/null || continue
+      ver=`$ac --version < /dev/null | head -1 | $VERSIONGREP`
+      AWK_RES=`echo $ver $AM_NEEDED | awk '{ if ( $1 >= $2 ) print "yes"; else print "no" }'`
+      if test "$AWK_RES" = "yes"; then
+        ACLOCAL=$ac
+        echo "  found $ACLOCAL"
+        break
+      fi
+    done
+    test -z $AUTOMAKE || test -z $ACLOCAL && {
+        echo
+        if test -n "$AM_NEEDED"; then
+            echo "You must have automake version $AM_NEEDED installed"
+            echo "to compile $package."
+        else
+            echo "You must have automake installed to compile $package."
+        fi
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+        DIE=1
+      }
+fi
+
+(libtoolize --version)  > /dev/null 2>&1 || {
+	echo
+	echo "You must have libtool installed to compile $package."
+	echo "Download the appropriate package for your system,"
+	echo "or get the source from one of the GNU ftp sites"
+	echo "listed in http://www.gnu.org/order/ftp.html"
+	DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+echo "Generating configuration files for $package, please wait...."
+
+ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I m4"
+if test -n "$ACLOCAL"; then
+  echo "  $ACLOCAL $ACLOCAL_FLAGS"
+  $ACLOCAL $ACLOCAL_FLAGS
+fi
+
+echo "  autoheader"
+autoheader
+
+echo "  libtoolize --automake"
+libtoolize --automake
+
+if test -n "$AUTOMAKE"; then
+  echo "  $AUTOMAKE --add-missing"
+  $AUTOMAKE --add-missing
+fi
+
+echo "  autoconf"
+autoconf
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+cd $olddir
+$srcdir/configure "$@" && echo


Property changes on: icecast/branches/icecast-kh/autogen.sh
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/conf/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/conf/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/conf/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,28 @@
+## Process this with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = icecast.xml.in
+DISTCLEANFILES = icecast.xml.dist
+
+docdir = $(datadir)/$(PACKAGE)/doc
+doc_DATA = icecast.xml.dist
+
+install-data-hook:
+	$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
+	test -f $(DESTDIR)$(sysconfdir)/icecast.xml || \
+	$(INSTALL_DATA) icecast.xml.dist $(DESTDIR)$(sysconfdir)/icecast.xml
+
+edit = sed -e 's, at pkgdatadir\@,$(pkgdatadir),g' \
+	-e 's, at localstatedir\@,$(localstatedir),g' \
+	-e 's, at PACKAGE\@,$(PACKAGE),g'
+
+icecast.xml.dist: $(srcdir)/icecast.xml.in
+	$(edit) $(srcdir)/icecast.xml.in > icecast.xml.dist
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+

Added: icecast/branches/icecast-kh/conf/icecast.xml.in
===================================================================
--- icecast/branches/icecast-kh/conf/icecast.xml.in	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/conf/icecast.xml.in	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,165 @@
+<icecast>
+    <limits>
+        <clients>100</clients>
+        <sources>2</sources>
+        <threadpool>5</threadpool>
+        <queue-size>102400</queue-size>
+        <client-timeout>30</client-timeout>
+        <header-timeout>15</header-timeout>
+        <source-timeout>10</source-timeout>
+        <!-- global setting for burst on connection, default is 64k for all sources -->
+        <burst-size>65535</burst-size>
+    </limits>
+
+    <authentication>
+        <!-- Sources log in with username 'source' -->
+        <source-password>hackme</source-password>
+        <!-- Relays log in username 'relay' -->
+        <relay-password>hackme</relay-password>
+
+        <!-- Admin logs in with the username given below -->
+        <admin-user>admin</admin-user>
+        <admin-password>hackme</admin-password>
+    </authentication>
+
+    <!-- Uncomment this if you want directory listings -->
+    <!--
+    <directory>
+        <yp-url-timeout>15</yp-url-timeout>
+        <yp-url>http://dir.xiph.org/cgi-bin/yp-cgi</yp-url>
+    </directory>
+    <directory>
+        <yp-url-timeout>15</yp-url-timeout>
+        <yp-url>http://www.oddsock.org/cgi-bin/yp-cgi</yp-url>
+    </directory>
+     -->
+
+    <hostname>localhost</hostname>
+
+    <!-- You can use these two if you only want a single listener -->
+    <!--<port>8000</port> -->
+    <!--<bind-address>127.0.0.1</bind-address>-->
+
+    <!-- You may have multiple <listener> elements -->
+    <listen-socket>
+        <port>8000</port>
+        <!-- <bind-address>127.0.0.1</bind-address> -->
+    </listen-socket>
+    <!--
+    <listen-socket>
+        <port>8001</port>
+    </listen-socket>
+    -->
+
+    <!--<master-server>127.0.0.1</master-server>-->
+    <!--<master-server-port>8001</master-server-port>-->
+    <!--<master-update-interval>120</master-update-interval>-->
+    <!--<master-password>hackme</master-password>-->
+
+    <!-- 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>
+    -->
+
+    <!-- Only define a <mount> section if you want to use advanced options,
+         like alternative usernames or passwords
+    <mount>
+        <mount-name>/example-complex.ogg</mount-name>
+
+        <username>othersource</username>
+        <password>hackmemore</password>
+
+        <max-listeners>1</max-listeners>
+        <dump-file>/tmp/dump-example1.ogg</dump-file>
+        <burst-size>65536</burst-size>
+        <fallback-mount>/example2.ogg</fallback-mount>
+        <fallback-override>1</fallback-override>
+        <fallback-when-full>1</fallback-when-full>
+        <no-yp>1</no-yp>
+        <authentication type="htpasswd">
+            <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>@pkgdatadir@</basedir>
+
+        <!-- Note that if <chroot> is turned on below, these paths must both
+             be relative to the new root, not the original root -->
+        <logdir>@localstatedir@/log/@PACKAGE@</logdir>
+        <webroot>@pkgdatadir@/web</webroot>
+        <adminroot>@pkgdatadir@/admin</adminroot>
+        <!-- <pidfile>@pkgdatadir@/icecast.pid</pidfile> -->
+
+        <!-- Aliases: treat requests for 'source' path as being for 'dest' path
+             May be made specific to a port or bound address using the "port"
+             and "bind-address" attributes.
+          -->
+        <!--
+        <alias source="/foo" dest="/bar"/>
+          -->
+        <!-- Aliases: can also be used for simple redirections as well,
+             this example will redirect all requests for http://server:port/ to
+             the status page
+          -->
+        <alias source="/" dest="/status.xsl"/>
+    </paths>
+
+    <logging>
+        <accesslog>access.log</accesslog>
+        <errorlog>error.log</errorlog>
+      	<loglevel>4</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
+    </logging>
+
+    <security>
+        <chroot>0</chroot>
+        <!--
+        <changeowner>
+            <user>nobody</user>
+            <group>nogroup</group>
+        </changeowner>
+        -->
+    </security>
+</icecast>

Added: icecast/branches/icecast-kh/configure.in
===================================================================
--- icecast/branches/icecast-kh/configure.in	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/configure.in	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,158 @@
+AC_INIT([Icecast], [2.0-kh47], [karl at xiph.org])
+
+AC_PREREQ(2.54)
+AC_CONFIG_SRCDIR(src/main.c)
+dnl Process this file with autoconf to produce a configure script.
+
+AM_INIT_AUTOMAKE
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_CANONICAL_HOST
+AC_PROG_LIBTOOL
+
+dnl Set some options based on environment
+
+DEBUG="-g"
+if test -z "$GCC"; then
+    XIPH_CPPFLAGS="-D_REENTRANT"
+    case $host in
+        *-*-irix*)
+                XIPH_CPPFLAGS="$XIPH_CPPFLAGS -w -signed"
+                PROFILE="-p -g3 -O2 -signed -D_REENTRANT"
+                ;;
+        *-*-solaris*)
+                XIPH_CFLAGS="-xO4 -xcg92"
+                XIPH_CPPFLAGS="$XIPH_CPPFLAGS -v -w -fsimple -fast"
+                PROFILE="-xpg -g -Dsuncc"
+                ;;
+        *)
+                XIPH_CFLAGS="-O"
+                PROFILE="-g -p"
+                ;;
+    esac
+
+    case "$host" in
+        # for system header breakage
+        *bsd* | *irix*)
+        ;;
+        *) AC_DEFINE([_XOPEN_SOURCE], 600, [Define if you have POSIX and XPG specifications])
+        ;;
+    esac
+
+else
+    XIPH_CPPFLAGS="-Wall -ffast-math -fsigned-char"
+    PROFILE="-pg -g"
+    AC_DEFINE([_GNU_SOURCE], 1, [Define to include GNU extensions to POSIX])
+fi
+
+dnl Checks for programs.
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+
+AC_CHECK_HEADERS([alloca.h])
+AC_CHECK_HEADERS(pwd.h, AC_DEFINE(CHUID, 1, [Define if you have pwd.h]),,)
+AC_CHECK_HEADERS(unistd.h, AC_DEFINE(CHROOT, 1, [Define if you have unistd.h]),,)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+dnl Check for types
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(localtime_r poll)
+AC_SEARCH_LIBS(nanosleep, rt posix4,
+        AC_DEFINE(HAVE_NANOSLEEP, 1, [Define if you have nanosleep]))
+XIPH_NET
+
+dnl -- configure options --
+
+XIPH_PATH_XSLT
+XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS])
+XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS])
+
+XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!]))
+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])
+
+XIPH_PATH_CURL([
+    AC_CHECK_DECL([CURLOPT_NOSIGNAL],
+        [ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code])
+        ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o"
+        enable_curl="yes"
+        XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS])
+        XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS])
+        ], [ AC_MSG_NOTICE([Your curl dev files are too old (7.10 or above required)])
+        ], [#include <curl/curl.h>
+        ])
+    ],[ AC_MSG_NOTICE([libcurl not found])
+    ])
+dnl -- YP support --
+AC_ARG_ENABLE([yp],
+        AC_HELP_STRING([--disable-yp],[disable YP directory support]),
+        enable_yp="$enableval",
+        enable_yp="yes")
+if test "x$enable_yp" = "xyes" -a "x$enable_curl" = xyes
+then
+    AC_DEFINE([USE_YP], 1, [Define to compile in YP support code])
+    ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o"
+else
+    AC_MSG_NOTICE([YP support disabled])
+fi
+# 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)
+AC_SUBST(XIPH_CFLAGS)
+AC_SUBST(XIPH_LIBS)
+AC_SUBST(PTHREAD_CPPFLAGS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(LIBTOOL_DEPS)
+AC_SUBST(LIBS)
+AC_SUBST(DEBUG)
+AC_SUBST(CFLAGS)
+AC_SUBST(PROFILE)
+AC_SUBST(ICECAST_OPTIONAL)
+
+AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile
+src/httpp/Makefile src/thread/Makefile src/log/Makefile
+src/net/Makefile src/timing/Makefile doc/Makefile web/Makefile
+admin/Makefile win32/Makefile win32/res/Makefile])

Added: icecast/branches/icecast-kh/debian/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/debian/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,8 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = 1.6 foreign
+
+EXTRA_DIST = README.Debian changelog compat control copyright \
+	icecast2.1 icecast2.default icecast2.init icecast2.manpages \
+	icecast2.postinst icecast2.postrm icecast2.preinst rules watch
+

Added: icecast/branches/icecast-kh/debian/README.Debian
===================================================================
--- icecast/branches/icecast-kh/debian/README.Debian	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/README.Debian	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,12 @@
+icecast2 for Debian
+-------------------
+
+It is recommended to run icecast under a dedicated user account, which only
+has access to write the log files.  The Debian package creates such an
+account, named 'icecast', and uses it by default, but you are free to
+reconfigure it and remove the account.
+
+Edit /etc/default/icecast2 to change the init-script configuration.
+
+ -- Keegan Quinn <ice at thebasement.org>
+

Added: icecast/branches/icecast-kh/debian/changelog
===================================================================
--- icecast/branches/icecast-kh/debian/changelog	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/changelog	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,198 @@
+icecast2 (1.9+2.0alphasnap2+20030802-1) unstable; urgency=low
+
+  * Added a 'watch' file to automate tracking of updates.
+  * Now uses dh-buildinfo to store information about the package build
+    environment.  Added a Build-Dependancy to dh-buildinfo.
+  * Removed cdbs/autotools-vars.mk, from cdbs CVS, because a new release
+    was made.
+  * Enabled curl during configure stage, since --disable-curl was recently
+    broken upstream, and potentially broken YP support can be disabled at
+    runtime.
+  * Trimmed ancient upgrade nodes and other cruft, left over from
+    pre-Debian versions, from README.Debian.
+  * Removed some autotools build cruft, since bugs were fixed upstream.
+  * Normalized {preinst,postinst,postrm} filenames to
+    icecast2.{preinst,postinst,postrm}.  Thanks to Emmanuel le Chevoir
+    for this suggestion.
+  * Fixed preinst; was stopping /usr/bin/icecast instead of
+    /usr/bin/icecast2.  Thanks to Emmanuel le Chevoir for this suggestion.
+  * Removed prerm, since it was not serving any purpose.  Thanks to
+    Emmanuel le Chevoir for this suggestion.
+  * Cleaned up postinst: removed old comments, fixed a path typo in the
+    configuration file location change message.
+  * Cleaned up postrm: removed old comments, fixed a typo in the group
+    removal test.
+  * Thanks to Jonas Smedegaard for sponsoring this package, and
+    providing many good suggestions.
+
+ -- Keegan Quinn <ice at thebasement.org>  Sat,  2 Aug 2003 20:28:13 -0700
+
+icecast2 (1.9+2.0alphasnap2+20030720-1.1) unstable; urgency=low
+
+  * NMU by sponsor (still closes: Bug#178160).
+
+ -- Jonas Smedegaard <dr at jones.dk>  Wed, 23 Jul 2003 06:03:42 +0200
+
+icecast2 (1.9+2.0alphasnap2+20030720-1) unstable; urgency=low
+
+  * New daily snapshot build.
+  * Added Build-Dependancy to cdbs, and increased debhelper version
+    requirement as recommended by cdbs README.
+  * Corrected Standards-Version to 3.6.0.  This package now generates
+    no lintian errors.
+  * Updated Recommends for ices to ices2; it was renamed.
+  * Added more information to the long description.
+  * Thanks to Jonas Smedegaard for sponsoring this package.
+  * This revision still closes: #178160 - the last was not uploaded.
+
+ -- Keegan Quinn <ice at thebasement.org>  Mon, 21 Jul 2003 08:55:27 -0700
+
+icecast2 (1.9+2.0alphasnap2+20030714-0.2) unstable; urgency=low
+
+  * Sponsored upload. Closes: Bug#178160.
+  * Switch to cdbs (agreed with maintainer).
+  * Use cdbs autotools-vars.mk from CVS to avoid cross-compiling on same
+    host.
+  * Add build-dependency on libxml2-dev.
+  * Explicitly configure without curl support to avoid building broken
+    YP stuff.
+  * Hack src/Makefile.am to use AM_CFLAGS instead of CFLAGS (which is
+    overridden by cdbs), and add clean rule to avoid invoking automake.
+  * Disable daemon by default and hint about changing passwords before
+    enabling.
+  * Avoid moving config files from pre-Debian times - instead just print
+    a warning if config exists in old location (better mess as little as
+    possible with files not ever claimed to be ours).
+  * Update README.Debian to reflect the above, include note about YP
+    support not compiled in, and remove note regarding adoption.
+  * Strip paths from packaging scripts (they may move around in the
+    future, and if PATH is wrong then something else broken anyway).
+  * Avoid removing unused /usr/share/icecast2 on purge.
+  * Remove icecast group on purge only if empty.
+  * Standards-version 3.6 (no changes needed).
+  * Fix wrong escaping of sed vars in conf/Makefile.
+  * Symlink public files from /usr/share/icecast2 to /etc/icecast2
+    (instead of pointing public root dirs below /etc).
+  * Use upstream config (paths are properly included now).
+
+ -- Jonas Smedegaard <dr at jones.dk>  Sun, 20 Jul 2003 20:19:30 +0200
+
+icecast2 (1.9+2.0alphasnap2+20030714-0.1) unstable; urgency=low
+
+  * New daily snapshot build.
+  * Updated versioning scheme to reflect (as well as possible) that the
+    source is a daily snapshot now, not CVS.
+
+ -- Keegan Quinn <ice at thebasement.org>  Mon, 14 Jul 2003 19:39:58 -0700
+
+icecast2 (1.9+2.0alphacvs030704-0.1) unstable; urgency=low
+
+  * Constructed a build script to completely automate the construction
+    of the 'pristine' tarball from CVS.  This doesn't really effect the
+    contents of the package, just makes it easier for me to rebuild.
+  * New CVS source.
+  * Removed Build-Dependancy on libcurl2-dev; packages built without this
+    library present will not have YP functionality, which is okay for
+    now since it's badly broken.
+  * Updated the default configuration file, including some new options
+    recently added upstream.
+  * Added a number of tweaks to clean up and rearrange new configuration
+    and documentation added to upstream install target.
+  * Moved the configuration file from /etc/icecast.xml to
+    /etc/icecast2/icecast.xml.  See README.Debian.
+  * Nice ugly version number to reflect that upstream calls this the 2.0
+    alpha branch, without potentially introducing the need for an epoch.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Jul 2003 23:46:56 -0700
+
+icecast2 (0.00.cvs030529-0.1) unstable; urgency=low
+
+  * New CVS source.
+  * Removed unnecessary debconf stuff.
+  * Added README.Debian.
+  * Path updates:
+    - /usr/share/icecast to /usr/share/icecast2,
+    - /var/log/icecast to /var/log/icecast2,
+    - /usr/bin/icecast to /usr/bin/icecast2,
+    - /usr/share/man/man8/icecast.8.gz to /usr/share/man/man8/icecast2.8.gz.
+
+ -- Keegan Quinn <ice at thebasement.org>  Wed, 29 May 2003 22:53:21 -0700
+
+icecast2 (0.00.cvs030403-0.2) unstable; urgency=low
+
+  * Tried to make the default configuration more understandable.
+
+ -- Keegan Quinn <ice at thebasement.org>  Fri,  4 Apr 2003 10:55:27 -0800
+
+icecast2 (0.00.cvs030403-0.1) unstable; urgency=low
+
+  * New CVS source.
+  * Minor changes to postrm.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Apr 2003 16:05:09 -0800
+
+icecast2 (0.00.cvs030401-0.7) unstable; urgency=low
+
+  * Minor changes to postinst.
+  * Added --background flag to initscript, since this version of icecast
+    does not yet run detached.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Apr 2003 14:24:19 -0800
+
+icecast2 (0.00.cvs030401-0.6) unstable; urgency=low
+
+  * Added Debianized configuration file.
+  * Created and set ownership of /var/log/icecast and /usr/share/icecast.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Apr 2003 14:15:11 -0800
+
+icecast2 (0.00.cvs030401-0.5) unstable; urgency=low
+
+  * Attempt at making debconf work properly.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Apr 2003 12:07:16 -0800
+
+icecast2 (0.00.cvs030401-0.4) unstable; urgency=low
+
+  * Minor edits to init.d script.
+  * Added bits to create and remove system accounts appropriately.
+  * Typo fix in the manual page.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu,  3 Apr 2003 11:06:48 -0800
+
+icecast2 (0.00.cvs030401-0.3) unstable; urgency=low
+
+  * Finished init.d script and manual page.
+  * Updated postinst to handle rc*.d links.
+  * Package is now lintian/linda clean.
+
+ -- Keegan Quinn <ice at thebasement.org>  Wed,  2 Apr 2003 16:29:18 -0800
+
+icecast2 (0.00.cvs030401-0.2) unstable; urgency=low
+
+  * Updated copyright (replacing dh_make template).
+  * Fixed duplicate conffiles.
+
+ -- Keegan Quinn <ice at thebasement.org>  Wed,  2 Apr 2003 16:18:02 -0800
+
+icecast2 (0.00.cvs030401-0.1) unstable; urgency=low
+
+  * New CVS source.
+  * Lots of packaging cleanup.
+  * Initial stab at manual page and init.d script.
+
+ -- Keegan Quinn <ice at thebasement.org>  Wed,  2 Apr 2003 10:25:56 -0800
+
+icecast2 (0.00.cvs030320-0.1) unstable; urgency=low
+
+  * New CVS source.
+  * Automated CVS original source creation.
+
+ -- Keegan Quinn <ice at thebasement.org>  Thu, 20 Mar 2003 12:58:49 -0800
+
+icecast2 (0.00.cvs030315-0.1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Keegan Quinn <ice at thebasement.org>  Sun, 16 Mar 2003 13:45:23 -0800
+

Added: icecast/branches/icecast-kh/debian/compat
===================================================================
--- icecast/branches/icecast-kh/debian/compat	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/compat	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1 @@
+4

Added: icecast/branches/icecast-kh/debian/control
===================================================================
--- icecast/branches/icecast-kh/debian/control	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/control	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,22 @@
+Source: icecast2
+Section: sound
+Priority: optional
+Maintainer: Keegan Quinn <ice at thebasement.org>
+Build-Depends: cdbs, debhelper (>> 4.1.0), dh-buildinfo, libogg-dev (>> 1.0.0), libvorbis-dev (>> 1.0.0), libxslt1-dev, libxml2-dev
+Uploaders: Jonas Smedegaard <dr at jones.dk>
+Standards-Version: 3.6.0
+
+Package: icecast2
+Architecture: any
+Depends: ${shlibs:Depends}
+Recommends: ices2
+Description: streaming Ogg Vorbis/MP3 media server
+ Icecast is an audio broadcasting system.  It can stream music in both
+ MPEG 1 Layer 3 (MP3) and Ogg Vorbis formats, supports multiple
+ streams on a single port through the use of "mountpoints," includes
+ web-based status and management interfaces, and has many other
+ advanced features.
+ .
+ Many standard audio players can connect and listen to Icecast-hosted
+ streams, since it's based on Nullsoft's Shoutcast protocol and HTTP.
+

Added: icecast/branches/icecast-kh/debian/copyright
===================================================================
--- icecast/branches/icecast-kh/debian/copyright	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/copyright	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,26 @@
+This package was debianized by Keegan Quinn <ice at thebasement.org> on
+Sun, 16 Mar 2003 13:45:23 -0800.
+
+It was retrieved from http://www.xiph.org/~brendan/snapshots/icecast/
+
+Upstream Authors: the icecast team <team at icecast.org>
+
+Copyright (c) 1999, 2000 the icecast team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any latfer version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+On Debian systems, the complete text of the GNU General Public License
+can be found in `/usr/share/common-licenses/GPL'.
+

Added: icecast/branches/icecast-kh/debian/icecast2.1
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.1	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.1	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,24 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH ICECAST2 1 "July 28, 2003"
+.SH NAME
+icecast2 \- an MP3/Ogg Vorbis broadcast streaming media server
+.SH SYNOPSIS
+.B icecast2
+-c
+.RI config.xml
+.SH DESCRIPTION
+\fBicecast2\fP is an audio broadcasting system that streams music in
+Ogg Vorbis and/or MPEG 1 Layer III format.  It accepts stream input
+from sources like ices0 and ices2, and broadcasts to clients like xmms.
+.SH OPTIONS
+\fBicecast2\fP has no command line options, except to specify the location
+of an XML configuration file.  All operational aspects of the software
+are controlled by this XML configuration file.
+.SH SEE ALSO
+The example configuration files, provided with the package documentation
+are the only reliable source of information on this software.
+It is still under very active development;
+documentation will be written when it is possible to do so.
+.SH AUTHOR
+icecast2 was created by the icecast team <team at icecast.org>.
+This manual page was written by Keegan Quinn <ice at thebasement.org>.

Added: icecast/branches/icecast-kh/debian/icecast2.default
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.default	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.default	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,19 @@
+# Defaults for icecast2 initscript
+# sourced by /etc/init.d/icecast2
+# installed at /etc/default/icecast2 by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+# Full path to the server configuration file
+CONFIGFILE="/etc/icecast2/icecast.xml"
+
+# Name or ID of the user and group the daemon should run under
+USERID=icecast
+GROUPID=icecast
+
+# Edit /etc/icecast2/icecast.xml and change at least the passwords.
+# Change this to true when done to enable the init.d script
+ENABLE=false
+

Added: icecast/branches/icecast-kh/debian/icecast2.init
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.init	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.init	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,67 @@
+#! /bin/sh
+#
+# icecast2
+#
+#		Written by Miquel van Smoorenburg <miquels at cistron.nl>.
+#		Modified for Debian
+#		by Ian Murdock <imurdock at gnu.ai.mit.edu>.
+#
+#		Further modified by Keegan Quinn <ice at thebasement.org>
+#		for use with Icecast 2
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/bin/icecast2
+NAME=icecast2
+DESC=icecast2
+
+test -x $DAEMON || exit 0
+
+# Defaults
+CONFIGFILE="/etc/icecast2/icecast.xml"
+CONFIGDEFAULTFILE="/etc/default/icecast2"
+USERID=icecast
+GROUPID=icecast
+ENABLE="false"
+
+# Reads config file (will override defaults above)
+[ -r "$CONFIGDEFAULTFILE" ] && . $CONFIGDEFAULTFILE
+
+if [ "$ENABLE" != "true" ]; then
+	echo "$NAME daemon disabled - read $CONFIGDEFAULTFILE."
+	exit 0
+fi
+
+set -e
+
+case "$1" in
+  start)
+	echo -n "Starting $DESC: "
+	start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
+		--background --exec $DAEMON -- -c $CONFIGFILE
+	echo "$NAME."
+	;;
+  stop)
+	echo -n "Stopping $DESC: "
+	start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+	echo "$NAME."
+	;;
+  reload|force-reload)
+	echo "Reloading $DESC configuration files."
+	start-stop-daemon --stop --signal 1 --quiet --exec $DAEMON
+	;;
+  restart)
+	echo -n "Restarting $DESC: "
+	start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+	sleep 1
+	start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
+		--background --exec $DAEMON -- -c $CONFIGFILE
+	echo "$NAME."
+	;;
+  *)
+	echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2
+	exit 1
+	;;
+esac
+
+exit 0

Added: icecast/branches/icecast-kh/debian/icecast2.manpages
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.manpages	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.manpages	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1 @@
+debian/icecast2.1

Added: icecast/branches/icecast-kh/debian/icecast2.postinst
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.postinst	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.postinst	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,43 @@
+#! /bin/sh
+# postinst script for icecast2
+
+set -e
+
+case "$1" in
+    configure)
+
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+	exit 0
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# Move configuration file to current location, if an old one exists
+# and the init.d script configuration file was updated
+if [ -f /etc/icecast.xml ] && grep -q /etc/icecast2/ /etc/default/icecast2; then
+    echo "It seems you have an old configuration lying around at"
+    echo "/etc/icecast.xml. You will need to manually merge with"
+    echo "the current configuration at /etc/icecast2/icecast.xml."
+
+    echo "See /usr/share/doc/icecast2/examples for new configuration options."
+fi
+
+# Check for an account named 'icecast'
+if ! id icecast >/dev/null 2>&1; then
+    # Create the new system account
+    adduser --system --disabled-password --disabled-login \
+	--home /usr/share/icecast2 --no-create-home --group icecast
+fi
+
+chown -R icecast:icecast /var/log/icecast2
+
+#DEBHELPER#
+
+exit 0
+

Added: icecast/branches/icecast-kh/debian/icecast2.postrm
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.postrm	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.postrm	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,29 @@
+#! /bin/sh
+# postrm script for icecast2
+
+set -e
+
+case "$1" in
+    purge)
+	rm -rf /var/log/icecast2
+
+	if id icecast >/dev/null 2>&1; then
+	    deluser icecast
+	fi
+
+	# Remove group only if empty
+	if getent group icecast | awk -F: ' { print $4 } ' | egrep -cq '^$'; then
+	    groupdel icecast
+	fi
+	;;
+    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+
+	;;
+    *)
+	echo "postrm called with unknown argument \`$1'" >&2
+	exit 1
+esac
+
+#DEBHELPER#
+
+exit 0

Added: icecast/branches/icecast-kh/debian/icecast2.preinst
===================================================================
--- icecast/branches/icecast-kh/debian/icecast2.preinst	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/icecast2.preinst	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,27 @@
+#! /bin/sh
+# preinst script for icecast2
+
+set -e
+
+case "$1" in
+    install|upgrade)
+        if [ "$1" = "upgrade" ]
+        then
+            start-stop-daemon --stop --quiet --oknodo  \
+                --exec /usr/bin/icecast2 2>/dev/null || true
+        fi
+    ;;
+
+    abort-upgrade)
+    ;;
+
+    *)
+        echo "preinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+

Added: icecast/branches/icecast-kh/debian/rules
===================================================================
--- icecast/branches/icecast-kh/debian/rules	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/rules	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,34 @@
+#!/usr/bin/make -f
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+
+DEB_INSTALL_CHANGELOGS_ALL = NEWS
+DEB_CONFIGURE_SYSCONFDIR = /etc/icecast2
+DEB_MAKE_INVOKE += PACKAGE=icecast2 docdir=/usr/share/doc/icecast2 pkgdatadir=/usr/share/icecast2
+
+binary-post-install/icecast2::
+	# Debian has a central copy of the GPL, no need to distribute again
+	rm -f $(DEB_DESTDIR)/usr/share/doc/icecast2/COPYING
+
+	# Live peacefully with icecast 1
+	mv $(DEB_DESTDIR)/usr/bin/icecast $(DEB_DESTDIR)/usr/bin/icecast2
+
+	# Move XSLT templates to /etc and replace with symlinks
+	for file in `cd $(DEB_DESTDIR)/usr/share && find icecast2 -type f -name *.xsl`; do \
+		mkdir -p $(DEB_DESTDIR)/etc/`dirname $$file`; \
+		mv $(DEB_DESTDIR)/usr/share/$$file $(DEB_DESTDIR)/etc/$$file; \
+		ln -s /etc/$$file $(DEB_DESTDIR)/usr/share/$$file; \
+	done
+
+	# NEWS is ChangeLog - avoid original name
+	rm -f $(DEB_DESTDIR)/usr/share/doc/icecast2/NEWS
+
+	mkdir -p $(CURDIR)/debian/icecast2/var/log/icecast2
+
+	# Store build information
+	dh_buildinfo
+
+clean::
+	# Upstream forgot to clean this one it seems...
+	rm -f conf/icecast.xml.dist
+


Property changes on: icecast/branches/icecast-kh/debian/rules
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/debian/watch
===================================================================
--- icecast/branches/icecast-kh/debian/watch	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/debian/watch	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,3 @@
+version=2
+
+http://www.icecast.org/download.html	files/icecast/icecast-(.*)\.tar\.gz	debian	uupdate

Added: icecast/branches/icecast-kh/doc/Index.hhk
===================================================================
--- icecast/branches/icecast-kh/doc/Index.hhk	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/Index.hhk	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<UL>
+</UL>
+</BODY></HTML>

Added: icecast/branches/icecast-kh/doc/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/doc/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+docdir = $(datadir)/doc/icecast
+doc_DATA = index.html icecast2_admin.html icecast2_basicsetup.html \
+	icecast2_config_file.html icecast2_faq.html icecast2_glossary.html \
+	icecast2_introduction.html icecast2_relay.html icecast2_stats.html \
+	icecast2_win32.html icecast2_yp.html
+
+EXTRA_DIST = Index.hhk icecast2.chm icecast2.hhc icecast2.hhp index_win32.html \
+	stats1.jpg style.css win32_section1.html win32_section2.html \
+	win32_section3.html windowtitle.jpg $(doc_DATA)

Added: icecast/branches/icecast-kh/doc/icecast2.chm
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/doc/icecast2.chm
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/doc/icecast2.hhc
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2.hhc	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2.hhc	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<OBJECT type="text/site properties">
+	<param name="ImageType" value="Folder">
+</OBJECT>
+<UL>
+	<LI> <OBJECT type="text/sitemap">
+		<param name="Name" value="Icecast2 - Table of Contents">
+		<param name="Local" value="index.html">
+		</OBJECT>
+	<UL>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Introduction">
+			<param name="Local" value="icecast2_introduction.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Basic Setup">
+			<param name="Local" value="icecast2_basicsetup.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Icecast Config File">
+			<param name="Local" value="icecast2_config_file.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Admin Interface">
+			<param name="Local" value="icecast2_admin.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Server Statistics">
+			<param name="Local" value="icecast2_stats.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Relaying">
+			<param name="Local" value="icecast2_relay.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Listing in a YP Directory">
+			<param name="Local" value="icecast2_yp.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Listener Authentication">
+			<param name="Local" value="icecast2_listenerauth.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Win32 specific documentation">
+			<param name="Local" value="icecast2_win32.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="Glossary">
+			<param name="Local" value="icecast2_glossary.html">
+			</OBJECT>
+		<LI> <OBJECT type="text/sitemap">
+			<param name="Name" value="FAQ">
+			<param name="Local" value="icecast2_faq.html">
+			</OBJECT>
+	</UL>
+</UL>
+</BODY></HTML>

Added: icecast/branches/icecast-kh/doc/icecast2.hhp
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2.hhp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2.hhp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,23 @@
+[OPTIONS]
+Auto Index=Yes
+Compatibility=1.1 or later
+Compiled file=icecast2.chm
+Contents file=icecast2.hhc
+Default Font=,8,0
+Default Window=Icecast 2 Documentation
+Default topic=index.html
+Display compile progress=No
+Full-text search=Yes
+Index file=Index.hhk
+Language=0x409 English (United States)
+Title=Icecast 2
+
+[WINDOWS]
+Icecast 2 Documentation=,"icecast2.hhc","Index.hhk","index.html",,,,,,0x42520,,0x3006,[26,28,794,605],,,,,,,0
+
+
+[FILES]
+index.html
+
+[INFOTYPES]
+

Added: icecast/branches/icecast-kh/doc/icecast2_admin.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_admin.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_admin.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,119 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Admin Interface</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>Overview</h2>
+<p>This section contains information about the admin interface of icecast.  Through this interface the user can manipulate many server features.  From it you can gather statistics, mov	e listeners from mountpoint to mountpoint, disconnect connected sources, disconnect connected listeners, and many other activities.  Each function is enumerated here as well as an example usage of the function.</p>
+<p>Each of these functions requires authentication via the &lt;admin-username&gt; and &lt;admin-password&gt; specified in the icecast config file. It is also important to note that in all the examples 192.168.1.10 is used as the example host and 8000 is used as the example port for the icecast server.</p>
+<br>
+<br>
+<br>
+<h2>Admin Functions (mount specific)</h2>
+<p>All these admin functions are mount specific in that they only apply to a particular mountpoint (as opposed to applying to the entire server).  Each of these functions requires a mountpoint to be specified as input.
+<h3>Metadata Update</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function provides the ability for either a source client or any external program to update the metadata information for a particular mountpoint.  Currently the only metadata supported is song title and only for MP3 streams.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/metadata?mount=/mystream&mode=updinfo&song=ACDC+Back+In+Black
+</pre>
+<br>
+<br>
+<h3>Fallback Update</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function provides the ability for either a source client or any external program to update the "fallback mountpoint" for a particular mountpoint.  Fallback mounts are those that are used in the even of a source client disconnection.  If a source client disconnects for some reason that all currently connected clients are sent immediately to the fallback mountpoint.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/fallbacks?mount=/mystream.ogg&fallback=/myfallback.ogg
+</pre>
+<br>
+<br>
+<h3>List Clients</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function lists all the clients currently connected to a specific mountpoint.  The results are sent back in XML form.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/listclients?mount=/mystream.ogg
+</pre>
+<br>
+<br>
+<h3>Move Clients (Listeners)</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function provides the ability to migrate currently connected listeners from one mountpoint to another.  This function requires 2 mountpoints to be passed in: mount (the *from* mountpoint) and destination (the *to* mountpoint).  After processing this function all currently connected listeners on mount will be connected to destination.  Note that the destination mountpoint must exist and have a sounce client already feeding it a stream.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/moveclients?mount=/mystream.ogg&destination=/mynewstream.ogg
+</pre>
+<br>
+<br>
+<h3>Kill Client (Listener)</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function provides the ability to disconnect a specific listener of a currently connected mountpoint.  Listeners are identified by a unique id that can be retrieved by via the "List Clients" admin function.  This id must be passed in to the request.  After processing this request, the listener will no longer be connected to the mountpoint.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/killclient?mount=/mystream.ogg&id=21
+</pre>
+<br>
+<br>
+<h3>Kill Source</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This function will provide the ability to disconnect a specific mountpoint from the server.  The mountpoint to be disconnected is specified via the variable "mount".
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/killsource?mount=/mystream.ogg
+</pre>
+<br>
+<br>
+<br>
+<h2>Admin Functions (general)</h2>
+<h3>Stats</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This admin function provides the ability to query the internal statistics kept by the icecast server.  Almost all information about the internal workings of the server such as the mountpoints connected, how many client requests have been served, how many listeners for each mountpoint, etc, are available via this admin function.<br>
+Note that this admin function can also be invoked via the http://server:port/admin/stats.xml syntax, however this syntax should not be used and will eventually become deprecated.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/stats
+</pre>
+<br>
+<br>
+<h3>List Mounts</h3>
+<h4>description</h4>
+<div class=indentedbox>
+This admin function provides the ability to view all the currently connected mountpoints.
+</div>
+<h4>example</h4>
+<pre>
+http://192.168.1.10:8000/admin/listmounts
+</pre>
+<br>
+<br>
+<br>
+<h2>Web-Based Admin Interface</h2>
+<p>As an alternative to manually invoking these URLs, a web-based admin interface was developed.  This interface provides the same functions that were identified and described above but presents them in a little nicer way.  The Web-Based Admin Interface to icecast is shipped with icecast provided in the "admin" directory and comes ready to use.  All the user needs to do is set the path to this directory in the config file via the &lt;adminroot&gt; config variable.</p>
+<p>The Web-Based Admin Interface is a series of XSLT files which are used to display all the XML obtained via the URL admin interface.  This can be changed and modified to suit the user's need.  Knowledge of XSLT and transformations from XML to HTML are required in order to make changes to these scripts.</p>
+<p>The main URL for the Web-Based Admin Interface is</p>
+<pre>
+http://192.168.1.10:8000/admin/stats.xsl
+</pre>
+<p>From this URL all of the other admin functions can be exercised.</p>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_admin.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_basicsetup.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_basicsetup.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_basicsetup.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,59 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Basic Setup</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>Basic Requirements</h2>
+<p>This section will describe the essential requirements in setting up a simple Internet radio station.  It is by no means a complete list but should give you enough to get started.</p>
+<p>There are two major components involved: the streaming server (icecast in this case) and the source client.  The icecast server will be the place where all listeners of your station will connect.  The source client (in general) runs on a separate machine than icecast, but does not necessarily need to.  Source clients send the content to icecast and provide the stream data (encoded audio) that is then relayed out to listeners by icecast.</p>
+<p>It is important to note that not all source clients work with icecast2.  You will need to check to make sure that icecast2 is supported by your chosen source client.</p>
+
+<br>
+<br>
+<br>
+<h2>The Basics</h2>
+<p>Each icecast server can house multiple broadcasts (or mountpoints) each containing a separate stream of content.  A listener can only listen to a single mountpoint at a time.  This means you can have a single icecast server contain either multiple broadcasts with different content, or possibly the same broadcast but with streams of different bitrates or qualities.  In this case each broadcast or stream is a separate mountpoint.</p>
+<p>At this point, the steps outlined here related to the Unix version or Win32 console version of icecast.  Icecast is also available in a Win32 GUI version, and the steps are similar in setup, but not quite the same.</p>
+<p>The first step in the process is to find and install the icecast2 server itself.  How to do this is not contained within this documentation.  After installation you should have and icecast binary and 3 directories</p>
+<center>
+<table border=1 width=75%>
+<tr><td>conf</td><td>Contains the icecast configuration file (icecast.xml) which defines all the configuration parameters for the server.</td></tr>
+<tr><td>admin</td><td>Contains xslt files which are used by the icecast server to provide a web-based front end to the administration capabilities of the server.</td></tr>
+<tr><td>logs</td><td>This is a blank directory which (if specified in the config file) will contain all the logs (there are 2) for icecast.</td></tr>
+</table>
+</center>
+<p>The next step is to edit the icecast.xml file and set the appropriate values.  Most of the default values are fine as provided, and for a basic setup the following entries should be changed :<br><br>
+<pre>
+&lt;source-password&gt; - will be used by the source client
+&lt;admin-password&gt; - will be used to access admin features of icecast
+&lt;listen-socket&gt; (both port and bind-address)
+&lt;logdir&gt; - directory where log files will be placed
+&lt;webroot&gt; - any static content can be placed here (file serving root)
+&lt;adminroot&gt; - directory containing admin xslt files
+</pre>
+<p>Once the configuration file is modified, you should be able to start the server with the following command</p>
+<pre>
+icecast -c /path/to/icecast.xml
+</pre>
+<p>If no error messages are generated, then check the error.log file for the following message :</p>
+<pre>
+[2003-10-31  13:04:49] INFO main/main.c icecast server started
+</pre>
+<p>You can also verify that it started by visiting the following URL : http://yourip:port/admin/stats.xml.  You should be prompted for a username and password.  Enter the username "admin" and the password you entered for &lt;admin-password&gt;.  If all is well, you should see an small XML tree which represents icecast statistics (more about that later).</p>
+<p>Now that the icecast server is started you must now configure your source client.  The information you will need for the source client is the following : <br>
+<br>
+IP address and Port of the icecast server - both of these come from &lt;listen-socket&gt;<br>
+source password - from &lt;source-password&gt;<br>
+<p>Additionally, you will need to choose a mountpoint and specify this in the source client.  Icecast does not need to know about each mount point (although you can configure settings for specific mountpoint - this is covered under Advanced configuration) there are, however, some points to mention regarding mountpoints.  All Ogg Vorbis streams should have mountpoints that end in .ogg (i,e. /mystream.ogg).  This is due to the lazy way most media players infer the type of stream.  MP3 streams usually do not contain an extension (/mystream).  Mount points also should not contain any spaces or odd characters (again due to the lazy way many of the media players are coded).</p>
+<p>Once you have configured your source client, you should be able to connect it to the icecast server.  Verify that it is connected by hitting the stats.xml URL that was mentioned above.</p>
+<p>Now that you have the source connnected, listening to the stream involves simply opening the appropriate following URL in a browser: http://yourip:port/mounpointyouspecified.m3u.  So, for instance, if you attached your source client to an icecast server located at 192.168.1.10:8000 with a mountpoint of /mystream.ogg, then you would open : http://192.168.1.10:8000/mystream.ogg.m3u.  Note that the .m3u extention will serve up a link that opens most media players.  Also it is important to note that m3u need not contain only MP3 stream, it can contain streams of arbitrary content-type and is used by icecast to serve a playlist that represents your broadcast to listening clients.  Alternatively you can open up the stream URL directly within your media player (http://192.168.1.10:8000/mystream.ogg in this case)</p>
+
+<br>
+<br>
+<br>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_basicsetup.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_config_file.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_config_file.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_config_file.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,403 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Config File</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<h3>Overview</h3>
+<p>
+This section will describe each section of the config file and is grouped into the following sections:
+</p>
+<li><a href="#limits">Limits</a>
+<li><a href="#authentication">Authentication</a>
+<li><a href="#yp">YP Directory Settings</a>
+<li><a href="#misc">Misc Server settings</a>
+<li><a href="#relay">Relay settings</a>
+<li><a href="#mount">Mount Specific settings</a>
+<li><a href="#path">File path settings</a>
+<li><a href="#log">Logging</a>
+<li><a href="#security">Security</a>
+
+<br>
+<br>
+<br>
+<a name="limits"></a>
+<h2>Limits</h2>
+<pre>
+    &lt;limits&gt;
+        &lt;clients&gt;100&lt;clients&gt;
+        &lt;sources&gt;2&lt;sources&gt;
+        &lt;threadpool&gt;5&lt;threadpool&gt;
+        &lt;queue-size&gt;102400&lt;queue-size&gt;
+        &lt;client-timeout&gt;30&lt;client-timeout&gt;
+        &lt;header-timeout&gt;15&lt;header-timeout&gt;
+        &lt;source-timeout&gt;10&lt;source-timeout&gt;
+    &lt;limits&gt;
+</pre>
+<p>This section contains server level settings that, in general, do not need to be changed.  Only modify this section if you are know what you are doing.
+</p>
+<h4>clients</h4>
+<div class=indentedbox>
+Total number of concurrent clients supported by the server.  Listeners are considered clients, but so is accesses to any static content (i.e. fileserved content) and also any requests to gather stats.  These are max *concurrent* connections for the entire server (not per mountpoint).
+</div>
+<h4>sources</h4>
+<div class=indentedbox>
+Maximum number of connected sources supported by the server.
+</div>
+<h4>threadpool</h4>
+<div class=indentedbox>
+This is the number of threads that are started to handle client connections.  You may need to increase this value if you are running a high traffic stream.  This recommended value is for a small to medium traffic server.
+</div>
+<h4>queue-size</h4>
+<div class=indentedbox>
+This is the maximum size (in bytes) of a client (listener) queue.  A listener may temporarily lag behind due to network congestion and in this case an internal queue is maintained for each listener.  If the queue grows larger than this config value, then the listener will be removed from the stream.
+</div>
+<h4>client-timeout</h4>
+<div class=indentedbox>
+This does not seem to be used.
+</div>
+<h4>header-timeout</h4>
+<div class=indentedbox>
+The maximum time (in seconds) to wait for a request to come in once the client has made a connection to the server.  In general this value should not need to be tweaked.
+</div>
+<h4>source-timeout</h4>
+<div class=indentedbox>
+If a connected source does not send any data within this timeout period (in seconds), then the source connection will be removed from the server.
+</div>
+<br>
+<br>
+<br>
+<a name="authentication"></a>
+<h2>Authentication</h2>
+<pre>
+    &lt;authentication&gt;
+        &lt;source-password&gt;hackme&lt;source-password&gt;
+        &lt;relay-password&gt;hackme&lt;relay-password&gt;
+        &lt;admin-user&gt;admin&lt;admin-user&gt;
+        &lt;admin-password&gt;hackme&lt;admin-password&gt;
+    &lt;authentication&gt;
+</pre>
+<p>This section contains all the users and passwords used for administration purposes or to connect sources and relays.
+</p>
+<h4>source-password</h4>
+<div class=indentedbox>
+The unencrypted password used by sources to connect to icecast2.  Currently, the username for all source connections must be 'source'.  This is likely to change in the future.
+</div>
+<h4>relay-password</h4>
+<div class=indentedbox>
+Currently not used.
+</div>
+<h4>admin-user</h4>
+<h4>admin-password</h4>
+<div class=indentedbox>
+The username/password used for all administration functions.  This includes retrieving statistics, accessing the web-based administration screens, etc.  A list of these functions can be found in the "Administration" section of the manual.
+</div>
+<br>
+<br>
+<br>
+<a name="yp"></a>
+<h2>YP Directory Settings</h2>
+<pre>
+    &lt;directory&gt;
+        &lt;yp-url-timeout&gt;15&lt;yp-url-timeout&gt;
+        &lt;yp-url&gt;http://dir.xiph.org/cgi-bin/yp-cgi&lt;yp-url&gt;
+    &lt;directory&gt;
+</pre>
+<p>This section contains all the settings for listing a stream on any of the Icecast2 YP Directory servers.  Multiple occurances of this section can be specified in order to be listed on multiple directory servers.
+</p>
+<h4>yp-url-timeout</h4>
+<div class=indentedbox>
+This value is the maximum time icecast2 will wait for a response from a particular directory server.  The recommended value should be sufficient for most directory servers.
+</div>
+<h4>yp-url</h4>
+<div class=indentedbox>
+The URL which icecast2 uses to communicate with the Directory server.  The value for this setting is provided by the owner of the Directory server.
+</div>
+<br>
+<br>
+<br>
+<a name="misc"></a>
+<h2>Misc Server Settings</h2>
+<pre>
+    &lt;hostname&gt;localhost&lt;hostname&gt;
+
+    &lt;-- You can use these two if you only want a single listener --&gt;
+    &lt;-- &lt;port&gt;8000&lt;port&gt; --&gt;
+    &lt;-- &lt;bind-address&gt;127.0.0.1&lt;bind-address&gt; --&gt;
+
+    &lt;-- You may have multiple &lt;listener&gt; elements --&gt;
+    &lt;listen-socket&gt;
+        &lt;port&gt;8000&lt;port&gt;
+        &lt;bind-address&gt;127.0.0.1&lt;bind-address&gt;
+    &lt;listen-socket&gt;
+
+    &lt;fileserve&gt;1&lt;fileserve&gt;
+</pre>
+<p>This section contains miscellaneous server settings.  Note that multiple listen-socket sections may be configured in order to have icecast2 listen on multiple network interfaces.  If a bind-address is not specified for a particular listen-socket, then the hostname parameter will be used to specify the address that will be bound.
+</p>
+<h4>port</h4>
+<div class=indentedbox>
+The TCP port that will be used to accept client connections.
+</div>
+<h4>bind-address</h4>
+<div class=indentedbox>
+And option IP address that can be used to bind to a specific network card.  If not supplied, then &lt;hostname&gt; will be used.
+</div>
+<h4>fileserve</h4>
+<div class=indentedbox>
+This flag turns on the icecast2 fileserver from which static files can be served.  All files are served relative to the path specified in the &lt;paths&gt;&lt;webroot&gt; configuration setting.
+</div>
+<br>
+<br>
+<br>
+<a name="relay"></a>
+<h2>Relay Settings</h2>
+<pre>
+    &lt;master-server&gt;127.0.0.1&lt;master-server&gt;
+    &lt;master-server-port&gt;8001&lt;master-server-port&gt;
+    &lt;master-update-interval&gt;120&lt;master-update-interval&gt;
+    &lt;master-password&gt;hackme&lt;master-password&gt;
+
+    &lt;relay&gt;
+        &lt;server&gt;127.0.0.1&lt;server&gt;
+        &lt;port&gt;8001&lt;port&gt;
+        &lt;mount&gt;example.ogg&lt;mount&gt;
+        &lt;local-mount&gt;different.ogg&lt;local-mount&gt;
+        &lt;relay-shoutcast-metadata&gt;0&lt;relay-shoutcast-metadata&gt;
+    &lt;relay&gt;
+</pre>
+<p>This section contains the server's relay settings. There are two types of relays: a "Master server relay" or a "Specific Mountpoint relay."  A Master server relay is only supported between icecast2 servers and is used to relays all mountpoints on a remote icecast2 server.
+
+<h3>Master Relay</h3>
+The following diagram shows the basics of doing a Master relay.  Note that Server 1 is configured with the &lt;master-server&gt;, &lt;master-server-port&gt;, etc settings and Server 2 is the server from which Server 1 will pull all attached mountpoints and relay them.  Using a Master Server relay, ALL mountpoints on Server 2 will be relayed.  If only specific mountpoints need to be relayed, then you must configure Server 1 as a "Specific Mountpoint Relay".  Both Master server relays and Specific Mountpoint relays begin their "relaying" when the Server is started.
+
+<pre>
+      |-----|                       |-----|
+      |     |  all mountpoints      |     | /mount1
+      |     | &lt;-------------------  |     | /mount2.ogg
+      |-----|                       |-----| /mount3
+      Icecast 2                     Icecast 2
+      Server 1                      Server 2
+      (RELAY SERVER)                (MASTER SERVER)
+
+     configured with
+     &lt;master-server&gt;
+     settings
+
+</pre>
+
+A server is configured as a Master Server relay by specifying the &lt;master-server&gt;, &lt;master-server-port&gt;,&lt;master-update-interval&gt;,&lt;master-password&gt; values in the config file.  The server that is being relayed does not need any special configuration.
+
+</p>
+<h4>master-server</h4>
+<div class=indentedbox>
+This is the IP for the server which contains the mountpoints to be relayed (Master Server).
+</div>
+<h4>master-server-port</h4>
+<div class=indentedbox>
+This is the TCP Port for the server which contains the mountpoints to be relayed (Master Server).
+</div>
+<h4>master-update-interval</h4>
+<div class=indentedbox>
+The interval (in seconds) that the Relay Server will poll the Master Server for any new mountpoints to relay.
+</div>
+<h4>master-password</h4>
+<div class=indentedbox>
+This is the admin password on the Master server.  It is used to query the server for a list of mountpoints to relay.
+</div>
+<br>
+<h3>Specific Mountpoint Relay</h3>
+The following diagram shows the basics of doing a Specific Mountpoint relay.  Note that Server 1 is configured with the &lt;relay&gt; settings and Server 2 is the server from which Server 1 will pull the specified mountpoint(s) and relay them.  Using a Specific Mountpoint Relay, only those mountpoints specified on Server 1 will be relayed from Server 2.
+
+<pre>
+      |-----|                       |-----|
+      |     |      /mount3          |     | /mount1
+      |     | &lt;-------------------  |     | /mount2.ogg
+      |-----|                       |-----| /mount3
+      Icecast 2                     Icecast 2/Shoutcast/Icecast
+      Server 1                      Server 2
+      (RELAY SERVER)                (REMOTE SERVER)
+
+     configured with
+     &lt;relay&gt;
+     settings
+
+</pre>
+
+Specific Mountpoint Relays can be configured to relay from an Icecast 2 server, as well as Icecast 1.x and Shoutcast.
+A server is configured as a Specific Mountpoint Server relay by specifying a &lt;relay&gt; XML chunk in the config file for each mountpoint to be relayed.  The server that is being relayed does not need any special configuration.
+
+        &lt;server&gt;127.0.0.1&lt;server&gt;
+        &lt;port&gt;8001&lt;port&gt;
+        &lt;mount&gt;example.ogg&lt;mount&gt;
+        &lt;local-mount&gt;different.ogg&lt;local-mount&gt;
+        &lt;relay-shoutcast-metadata&gt;0&lt;relay-shoutcast-metadata&gt;
+
+</p>
+<h4>server</h4>
+<div class=indentedbox>
+This is the IP for the server which contains the mountpoint to be relayed.
+</div>
+<h4>port</h4>
+<div class=indentedbox>
+This is the TCP Port for the server which contains the mountpoint to be relayed.
+</div>
+<h4>mount</h4>
+<div class=indentedbox>
+The mountpoint located on the remote server.  If you are relaying a shoutcast stream, this must be '/'.
+</div>
+<h4>local-mount</h4>
+<div class=indentedbox>
+The name to use for the local mountpoint.  This is what the mount will be named on the RELAY SERVER.
+</div>
+<h4>relay-shoutcast-metadata</h4>
+<div class=indentedbox>
+If you are relaying a Shoutcast stream, you need to specify this indicator to also relay the metadata (song titles) that is part of the Shoutcast stream (1=enabled, 0=disabled).
+</div>
+<br>
+<br>
+<br>
+<a name="mount"></a>
+<h2>Mount Specific Settings</h2>
+<pre>
+    &lt;mount&gt;
+        &lt;mount-name&gt;/example-complex.ogg&lt;mount-name&gt;
+        &lt;username&gt;othersource&lt;username&gt;
+        &lt;password&gt;hackmemore&lt;password&gt;
+        &lt;max-listeners&gt;1&lt;max-listeners&gt;
+        &lt;dump-file&gt;/tmp/dump-example1.ogg&lt;dump-file&gt;
+        &lt;fallback-mount&gt;example2.ogg&lt;fallback-mount&gt;
+        &lt;authentication type="htpasswd"&gt;
+                &lt;option name="filename" value="myauth"/&gt;
+        &lt;/authentication&gt;
+
+    &lt;mount&gt;
+</pre>
+<p>This section contains settings which apply only to a specific mountpoint.  Within this section you can reserve a specific mountpoint and set a source username/password for that mountpoint (not yet implemented) as well as specify individual settings which will apply only to the supplied mountpoint.
+</p>
+<h4>mount-name</h4>
+<div class=indentedbox>
+The name of the mount point for which these settings apply.
+</div>
+<h4>username</h4>
+<div class=indentedbox>
+An optional value which will set the username that a source must use to connect using this mountpoint.
+</div>
+<h4>password</h4>
+<div class=indentedbox>
+An optional value which will set the password that a source must use to connect using this mountpoint.
+</div>
+<h4>max-listeners</h4>
+<div class=indentedbox>
+An optional value which will set the maximum number of listeners that can be attached to this mountpoint.
+</div>
+<h4>dump-file</h4>
+<div class=indentedbox>
+An optional value which will set the filename which will be a dump of the stream coming through on this mountpoint.
+</div>
+<h4>fallback-mount</h4>
+<div class=indentedbox>
+This specifies a mountpoint that is used in the case of a source disconnect.  If listeners are connected to the mount specified by the &lt;mount-name&gt; config value, then if the source is disconnected; all currently connected clients will be moved to the fallback-mount.
+</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.
+</div>
+<br>
+<br>
+<br>
+<a name="path"></a>
+<h2>Path Settings</h2>
+<pre>
+    &lt;paths&gt;
+        &lt;basedir&gt;./&lt;basedir&gt;
+        &lt;logdir&gt;./logs&lt;logdir&gt;
+        &lt;pidfile&gt;./icecast.pid&lt;pidfile&gt;
+        &lt;webroot&gt;./web&lt;webroot&gt;
+        &lt;adminroot&gt;./admin&lt;adminroot&gt;
+        &lt;alias source="/foo" dest="/bar"/&gt;
+    &lt;paths&gt;
+</pre>
+<p>This section contains paths which are used for various things within icecast.  All paths should not end in a '/'.
+</p>
+<h4>basedir</h4>
+<div class=indentedbox>
+This path is used in conjunction with the chroot settings, and specified the base directory that is chrooted to when the server is started.  This feature is not supported on win32.
+</div>
+<h4>logdir</h4>
+<div class=indentedbox>
+This path specifies the base directory used for logging. Both the error.log and access.log will be created relative to this directory.
+</div>
+<h4>pidfile</h4>
+<div class=indentedbox>
+This pathname specifies the file to write at startup and to remove at normal shutdown. The file contains the process id of the icecast process. This could be read and used for sending signals icecast.
+</div>
+<h4>webroot</h4>
+<div class=indentedbox>
+This path specifies the base directory used for all static file requests.  This directory can contain all standard file types (including mp3s and ogg vorbis files).  For example, if webroot is set to /var/share/icecast2, and a request for http://server:port/mp3/stuff.mp3 comes in, then the file /var/share/icecast2/mp3/stuff.mp3 will be served.
+</div>
+<h4>adminroot</h4>
+<div class=indentedbox>
+This path specifies the base directory used for all admin requests.  More specifically, this is used to hold the XSLT scripts used for the web-based admin interface.  The admin directory contained within the icecast distribution contains these files.
+</div>
+<h4>alias source="/foo" dest="/bar"</h4>
+<div class=indentedbox>
+Aliases are used to provide a way to create multiple mountpoints that refer to the same mountpoint.
+</div>
+<br>
+<br>
+<br>
+<a name="log"></a>
+<h2>Logging Settings</h2>
+<pre>
+    &lt;logging&gt;
+        &lt;accesslog&gt;access.log&lt;/accesslog&gt;
+        &lt;errorlog&gt;error.log&lt;/errorlog&gt;
+      	&lt;loglevel&gt;4&lt;/loglevel&gt; &lt;-- 4 Debug, 3 Info, 2 Warn, 1 Error --&gt;
+    &lt;/logging&gt;
+</pre>
+<p>This section contains information relating to logging within icecast.  There are two logfiles currently generated by icecast, an error.log (where all log messages are placed) and an access.log (where all stream/admin/http requests are logged).
+</p>
+<p>Note that on non-win32 platforms, a HUP signal can be sent to icecast in which the log files are re-opened for appending giving the ability move/remove the log files.
+<h4>accesslog</h4>
+<div class=indentedbox>
+Into this file, all requests made to the icecast2 will be logged.  This file is relative to the path specified by the &lt;logdir&gt; config value.
+</div>
+<h4>errorlog</h4>
+<div class=indentedbox>
+All icecast generated log messages will be written to this file.  If the loglevel is set too high (Debug for instance) then this file can grow fairly large over time.  Currently, there is no log-rotation implemented.
+</div>
+<h4>loglevel</h4>
+<div class=indentedbox>
+Indicates what messages are logged by icecast.  Log messages are categorized into one of 4 types, Debug, Info, Warn, and Error.<br><br>The following mapping can be used to set the appropraite value :
+</div>
+<br>
+<br>
+<li>loglevel = 4 - Debug, Info, Warn, Error messages are printed
+<li>loglevel = 3 - Info, Warn, Error messages are printed
+<li>loglevel = 2 - Warn, Error messages are printed
+<li>loglevel = 1 - Error messages only are printed
+<br>
+<a name="security"></a>
+<h2>Security Settings</h2>
+<pre>
+    &lt;security&gt;
+        &lt;chroot&gt;0&lt;/chroot&gt;
+        &lt;changeowner&gt;
+            &lt;user&gt;nobody&lt;/user&gt;
+            &lt;group&gt;nogroup&lt;/group&gt;
+	&lt;/changeowner&gt;
+    &lt;/security&gt;
+</pre>
+<p>This section contains configuration settings that can be used to secure the icecast server by performing a chroot to a secured location.  This is currently not supported on win32.
+</p>
+<h4>chroot</h4>
+<div class=indentedbox>
+An indicator which specifies whether a chroot() will be done when the server is started.  The chrooted path is specified by the &lt;basedir&gt; configuration value.
+</div>
+<h4>changeowner</h4>
+<div class=indentedbox>
+This section indicates the user and group that will own the icecast process when it is started.  These need to be valid users on the system.
+</div>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_config_file.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_faq.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_faq.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_faq.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,85 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 FAQ</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>General Questions</h2>
+<h4>What is Icecast?</h4>
+<div class=indentedbox>
+<p>
+Icecast, the project, is a collection of programs and libraries for
+streaming audio over the Internet. This includes:
+</p>
+<ul>
+<li>icecast, a program that streams audio data to listeners</li>
+<li>libshout, a library for communicating with Icecast servers</li>
+<li>IceS, a program that sends audio data to Icecast servers</li>
+</ul>
+A source client is an external program which is responsible for sending content data to icecast.  Some source clients that support icecast2 are Oddcast, ices2, ices0.3, and DarkIce.
+</div>
+
+<h4>What is icecast, the program?</h4>
+<div class=indentedbox>
+<p>
+icecast streams audio to listeners, and is compatible with Nullsoft’s Shoutcast.
+</p>
+</div>
+<h4>What is libshout ?</h4>
+<div class=indentedbox>
+<p>
+	From the README:
+</p>
+<blockquote>
+	<p>
+	libshout is a library for communicating with and sending data to an icecast server.
+	It handles the socket connection, the timing of the data, and prevents bad data from getting to the icecast server.
+	</p>
+</blockquote>
+</div>
+
+<h4>What is IceS?</h4>
+<div class=indentedbox>
+<p>
+	IceS is a program that sends audio data to an icecast server to broadcast to clients.
+	IceS can either read audio data from disk,
+	such as from Ogg Vorbis files, or sample live audio from a sound card and encode it on the fly.
+</p>
+</div>
+
+<h4>How can I view the stream status page?</h4>
+<div class=indentedbox>
+		<p>
+			Check your icecast configuration file for an element
+			called &lt;webroot&gt;. This directory contains web stuff.
+			In it, place a file called “status.xsl” that
+			transforms an <acronym>XML</acronym> file containing stream
+			data into a web page
+			(either <acronym>XHTML</acronym> or <acronym>HTML</acronym>).
+		</p>
+		<p>
+			There are sample <acronym>XSL</acronym> stylesheets available
+			in icecast/web/ in the CVS distribution
+			of icecast.
+		</p>
+
+		<p>
+			In addition, the web directory can
+			hold multiple status transforms, if you can’t decide which
+			one you want.
+		</p>
+</div>
+
+<h4>What can I use to listen to an Icecast stream?</h4>
+<div class=indentedbox>
+		<p>
+			We maintain a list of Icecast-compatible audio players at
+			http://www.icecast.org/
+		</p>
+</div>
+</div>
+</body>
+</html>
+


Property changes on: icecast/branches/icecast-kh/doc/icecast2_faq.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_glossary.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_glossary.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_glossary.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,37 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Glossary</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<a name="source client"></a>
+<h4>source client</h4>
+<div class=indentedbox>
+A source client is an external program which is responsible for sending content data to icecast.  Some source clients that support icecast2 are Oddcast, ices2, ices0.3, and DarkIce.
+</div>
+<a name="slave server"></a>
+<h4>slave server (Relay)</h4>
+<div class=indentedbox>
+The slave server in a relay configuration is the server that is pulling the data from the master server. It acts as a listening client to the master server.
+</div>
+<a name="master server"></a>
+<h4>master server (Relay)</h4>
+<div class=indentedbox>
+The master server in a relay configuration is the server that has the stream that is being relayed.
+</div>
+<a name="mountpoint"></a>
+<h4>mountpoint</h4>
+<div class=indentedbox>
+A mountpoint is a resource on the icecast server that represents a single broadcast stream.  Mountpoints are named similar to files (/mystream.ogg, /mymp3stream).  When listeners connect to icecast2, they must specify the mountpoint in the request (i.e. http://192.168.1.10:8000/mystream.ogg).  Additionally, source clients must specify a mountpoint when they connect as well.  Statistics are kept track of by mountpoint.  Mountpoints are a fundamental aspect of icecast2 and how it is organized.
+</div>
+<a name="fallback"></a>
+<h4>fallback mountpoint</h4>
+<div class=indentedbox>
+A fallback mountpoint is configured with a parent mountpoint.  In the event of the parent mountpoint losing connection with icecast, Icecast will then move all clients currently connected to the now defunct mountpoint to it's fallback mountpoint.
+</div>
+
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_glossary.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_introduction.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_introduction.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_introduction.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,43 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Introduction</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>What is Icecast ?</h2>
+<p>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.</p>
+<br>
+<p>There are two major parts to most streaming media servers: the component providing the content (what we call <b>source clients</b>) and the component which is responsible for serving that content to listeners (this is the function of icecast).
+</p>
+<br>
+<br>
+<br>
+<h2>What platforms are supported ?</h2>
+<p>Currently the following Unix platforms are supported:</p>
+<li>Linux (Most flavors including Redhat and Debian)
+<li>FreeBSD
+<li>OpenBSD
+<li>Solaris
+<p>Currently the following Windows platforms are supported:</p>
+<li>Windows NT
+<li>Windows 2000
+<li>Windows XP
+<br>
+<br>
+<br>
+<h2>Where do I go for questions?</h2>
+<p>There are many ways to contact the icecast development team</p>
+<h3>Best Ways</h3>
+<li>Icecast mailing list <a href="http://www.xiph.org/archives">http://www.xiph.org/archives</a>
+<li>Icecast Developers mailing list <a href="http://www.xiph.org/archives">http://www.xiph.org/archives</a>
+<li>Icecast IRC chat room - irc.freenode.net : #icecast
+<h3>Alternate Ways</h3>
+<li>team at icecast.org
+<br>
+<br>
+<br>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_introduction.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_relay.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_relay.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_relay.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,47 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Relaying</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>Overview</h2>
+<p>Relaying is the process by which one server mirrors one or more streams from a remote server.  The servers need not be of the same type (i.e. icecast can relay from Shoutcast).  Relaying is used primarily for large broadcasts that need to distribute listening clients across multiple physical machines.</p>
+<br>
+<br>
+<h2>Type of Relays</h2>
+<p>There are two types of relays that icecast supports.  The first type is when both master and slave servers are icecast2 servers.  In this case, a "master-slave" relay can be setup such that all that needs to be done is configure the slave server with the connection information (serverip:port) of the master server and the slave will mirror all mountpoints on the master server.  The slave will also periodically check the master server to see if any new mountpoints have attached and if so will relay those as well.  The second type of relay is a "single-broadcast" relay.  In this case, the slave server is configured with a serverip+port+mount and only the mountpoint specified is relayed.  In order to relay a broadcast stream on a Shoutcast server, you must use the "single-broadcast" relay and specify a mountpoint of "/".</p>
+<br>
+<br>
+<br>
+<h2>Setting Up A Master-Slave Relay</h2>
+<p>In order to setup a relay of this type both servers (the one you wish to relay and the one doing the relaying) need to be icecast2 servers. The following configuration snippet is used as an example:</p>
+<pre>
+    &lt;master-server&gt;192.168.1.11&lt;/master-server&gt;
+    &lt;master-server-port&gt;8001&lt;/master-server-port&gt;
+    &lt;master-update-interval&gt;120&lt;/master-update-interval&gt;
+    &lt;master-password&gt;hackme&lt;/master-password&gt;
+</pre>
+In this example, this configuration is setup in the server which will be doing the relaying (slave server).  The master server in this case need not be configured (and actually is unaware of the relaying being performed) as a relay.  When the slave server is started, it will connect to the master server located at 192.168.1.11:8001 and will begin to relay all mountpoints connected to the master server.  Additionally, every master-update-interval (120 seconds in this case) the slave server will poll the master server to see if any new mountpoints have connected, and if so, the slave server will relay those as well.  Note that the names of the mountpoints on the slave server will be identical to those on the master server.
+<br>
+<br>
+<br>
+<h2>Setting Up A Single-Broadcast Relay</h2>
+<p>In this case, the master server need not be an icecast2 server.  Supported master servers for a single-broadcast relay are Shoutcast, Icecast1.x, and of course Icecast2. The following configuration snippet is used as an example:</p>
+<pre>
+    &lt;relay&gt;
+        &lt;server&gt;192.168.1.11&lt;/server&gt;
+        &lt;port&gt;8001&lt;/port&gt;
+        &lt;mount&gt;/example.ogg&lt;/mount&gt;
+        &lt;local-mount&gt;/different.ogg&lt;/local-mount&gt;
+        &lt;relay-shoutcast-metadata&gt;0&lt;/relay-shoutcast-metadata&gt;
+    &lt;/relay&gt;
+</pre>
+<p>In this example, this configuration is also setup in the server which will be doing the relaying (slave server).  The master server in this case need not be configured (and actually is unaware of the relaying being performed) as a relay.  When the slave server is started, it will connect to the master server located at 192.168.1.11:8001 and will begin to relay only the mountpoint specified (/example.ogg in this case).  Using this type of relay, the user can override the local mountpoint name and make it something entirely different than the one on the master server.  Additionally, if the server is a Shoutcast server, then the &lt;mount&gt; must be specified as /.  And if you want the Shoutcast relay stream to have metadata contained within it (Shoutcast metadata is embedded in the stream itself) then the &lt;relay-shoutcast-metadata&gt; needs to be set to 1.</p>
+<br>
+<br>
+<br>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_relay.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_stats.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_stats.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_stats.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,83 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Server Statistics</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>Overview</h2>
+<p>This section contains information about the server statistics available from icecast.  An example stats XML tree will be shown and each element will be described.  The following example stats tree will be used:</p>
+<pre>
+&lt;?xml version="1.0"?&gt;
+&lt;icestats&gt;
+	&lt;client_connections&gt;13&lt;/client_connections&gt;
+	&lt;connections&gt;14&lt;/connections&gt;
+	&lt;source_connections&gt;1&lt;/source_connections&gt;
+	&lt;sources&gt;1&lt;/sources&gt;
+	&lt;source mount="/test.ogg"&gt;
+		&lt;artist&gt;&lt;/artist&gt;
+		&lt;audio_info&gt;ice-samplerate=32000;ice-bitrate=Quality -1;ice-channels=1&lt;/audio_info&gt;
+		&lt;ice-bitrate&gt;Quality -1&lt;/ice-bitrate&gt;
+		&lt;ice-channels&gt;1&lt;/ice-channels&gt;
+		&lt;ice-samplerate&gt;32000&lt;/ice-samplerate&gt;
+		&lt;listeners&gt;0&lt;/listeners&gt;
+		&lt;public&gt;0&lt;/public&gt;
+		&lt;title&gt;&lt;/title&gt;
+		&lt;type&gt;Ogg Vorbis&lt;/type&gt;
+	&lt;/source&gt;
+&lt;/icestats&gt;
+</pre>
+<h3>General Statistics</h3>
+<h4>client-connections</h4>
+<div class=indentedbox>
+Client connections are basically anything that is not a source connection.  These include listeners (not concurrent, but cumulative), any admin function accesses, and any static content (file serving) accesses.
+</div>
+<h4>source-connections</h4>
+<div class=indentedbox>
+Source connections are the number of times (cumulative not currently connected) a source has connected to icecast.
+</div>
+<h4>connections</h4>
+<div class=indentedbox>
+The total of client + source connections.
+</div>
+<h4>sources</h4>
+<div class=indentedbox>
+The total of currently connected sources (mountpoints).
+</div>
+<h3>Source-specific Statistics</h3>
+<h4>artist</h4>
+<div class=indentedbox>
+Artist of the current song (metadata set by source client).
+</div>
+<h4>title</h4>
+<div class=indentedbox>
+Title of the current song (metadata set by source client).
+</div>
+<h4>audio-info</h4>
+<div class=indentedbox>
+Information about the bitrate/samplerate/quality of the stream (set by source client). Also used for YP entries.
+</div>
+<h4>ice-bitrate</h4>
+<h4>ice-samplerate</h4>
+<h4>ice-channels</h4>
+<div class=indentedbox>
+Information about the bitrate/samplerate/quality of the stream (set by source client).
+</div>
+<h4>listeners</h4>
+<div class=indentedbox>
+The number of currently connected listeners.
+</div>
+<h4>public</h4>
+<div class=indentedbox>
+Flag that indicates whether this mount is being listed on a YP (sey by source client).
+</div>
+<h4>type</h4>
+<div class=indentedbox>
+Media type of the stream.
+</div>
+<br>
+<br>
+<br>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_stats.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_win32.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_win32.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_win32.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,54 @@
+<!doctype html public "-//w3c//dtd html 3.2//en">
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class="boxtest">
+<h1>Icecast 2 - Win32 Specific Documentation</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<p>
+The win32 port of icecast2 is simply a UI framework around the core icecast2 server.  The win32 version of icecast2 directly uses the main executable of icecast (statically included) and simply provides a GUI interface to icecast2.
+</p>
+<p>
+Most of the features of icecast2 are available in the win32 port.
+</p>
+<h2>Server Status Tab</h2>
+<p>
+The server status tab contains information regarding statistics that are global to the server.  There are two types of statistics in icecast2: source level and global statistics.  Global statistics are cumulative stats from all sources offered by the server.  Source level statistics are stats which apply only to a single source attached to the server.
+</p>
+<p>
+Examples of global statistics are:
+</p>
+<pre>
+	The number of current sources connected
+	The number of sources that have attempted connections
+	Total number of attempted connections to the server
+</pre>
+</p>
+<p>
+The Server Status tab contains at a minimal the global stats for the server.  Additionally, you may add source specific stats to this tab.  The intent is to provide a single "dashboard view" of what's going on in the server.  To add source statistics to the Server Status tab, see the section on the Stats tab.
+</p>
+<h3>Adding stats to the window title</h3>
+<p>
+Any stat that is contained on the Server Status tab can be displayed as the icecast2 window title.  This provides yet another mechanism by which you can view activities on the server.  To enable this feature, right click on any stat in the Server Status tab as seen below :
+</p>
+<img src="windowtitle.jpg">
+<br>
+<br>
+<br>
+<h3>Removing source level stats from the Server Status Tab</h3>
+<p>
+To remove a source level stat that you have inserted onto the Server Status Tab, simple right click that statistic and select "Delete from Global Stats".  The stat will be deleted from the Server Status tab, but will still remain on the source level Stats tab.
+</p>
+
+<h2>Editing The Icecast Config File</h2>
+<p>
+Editing the icecast2 configuration file is a very simple process.  For a description of what each field means, see the main icecast documenation.  Changes to the icecast2 configuration can only be done while the server is stopped.  To edit the current server configuration file, select "Configuration/Edit Configuration" from the main menu.
+</p>
+
+<h2>Stats Tab</h2>
+<p>
+The stats tab contains a view of all the connected mountpoints and the statistics that go along with them.  Each connected mountpoint is displayed in the left pane of the window, and all stats for the selected mountpoint are displayed in the right pane of the window.
+</p>
+<img src="stats1.jpg">
+
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_win32.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/icecast2_yp.html
===================================================================
--- icecast/branches/icecast-kh/doc/icecast2_yp.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/icecast2_yp.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,36 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 YP Directories</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<br>
+<br>
+<br>
+<h2>Overview</h2>
+<p>A YP (Yellow Pages) directory is a listing of broadcast streams.  Icecast2 has it own YP directory located at http://dir.xiph.org.  Currently icecast2 can only be listed in an icecast2-supported YP directory.  This means that you cannot list your stream in the Shoutcast YP directory.</p>
+<p>In the icecast2 configuration file are all the currently available YP directory servers.  Listing your stream in a YP is a combination of settings in the icecast configuration file and also in your source client.</p>
+<br>
+<br>
+<br>
+<h2>Configuring icecast2 for YP Support</h2>
+<p>First of all, icecast must have been built with YP support.  This is automatically done if you have libcurl installed.  If libcurl is not detected when icecats is compiled, then YP support is disabled.</p>
+<p>If icecast has been built with YP support, then the following configuration options control the YP directory settings:</p>
+<pre>
+    &lt;directory&gt;
+        &lt;yp-url-timeout&gt;15&lt;yp-url-timeout&gt;
+        &lt;yp-url&gt;http://dir.xiph.org/cgi-bin/yp-cgi&lt;yp-url&gt;
+    &lt;directory&gt;
+</pre>
+<p>Multiple directory XML chunks can be specified in order to be listed in multiple directories.</p>
+<br>
+<br>
+<br>
+<h2>Configuring Your Source Client for YP Support</h2>
+<p>This is usually covered in the source client documentation.  More specifically, the source client needs to provide the HTTP header ice-public:1 on connect in order to enable YP listing of the stream.</p>
+<p>If a mountpoint is being listed on a YP, then you will see some additional statistics relating to the YP such as last-touch, currently-playing, etc.</p>
+<br>
+<br>
+<br>
+</div>
+</body>
+</html>


Property changes on: icecast/branches/icecast-kh/doc/icecast2_yp.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/index.html
===================================================================
--- icecast/branches/icecast-kh/doc/index.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/index.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,76 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Icecast 2 Documentation Table of Contents</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+
+<li><a href="icecast2_introduction.html">Introduction</a>
+<li><a href="icecast2_basicsetup.html">Basic Setup</a>
+<li><a href="icecast2_config_file.html">Icecast Config File</a>
+<li><a href="icecast2_admin.html">Admin Interface</a>
+<li><a href="icecast2_stats.html">Server Statistics</a>
+<li><a href="icecast2_relay.html">Relaying</a>
+<li><a href="icecast2_yp.html">Listing in a YP directory</a>
+<li><a href="icecast2_listenerauth.html">Listener Authentication</a>
+<li><a href="icecast2_win32.html">Win32 specific documentation</a>
+<li><a href="icecast2_glossary.html">Glossary</a>
+<li><a href="icecast2_faq.html">FAQ</a>
+
+
+
+<pre>
+icecast 2.x - README
+---------------------------------------------------------------------
+
+Icecast is a streaming media server which currently supports Ogg
+Vorbis and MP3 audio streams. It can be used to create an Internet
+radio station or a privately running jukebox and many things in
+between. It is very versatile in that new formats can be added
+relatively easily and supports open standards for commuincation and
+interaction.
+
+
+Prerequisites
+---------------------------------------------------------------------
+icecast requires the following packages :
+
+* libxml2 - http://xmlsoft.org/downloads.html
+* libxslt - http://xmlsoft.org/XSLT/downloads.html
+* curl - http://curl.haxx.se/download.html (>= version 7.10 required)
+  NOTE: icecast may be compiled without curl, however this will
+        disable all Directory server interaction (YP).
+* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required)
+
+A Note About RPMS
+---------------------------------------------------------------------
+This section only applies to you if your operating system uses RPMS.
+
+In order to build icecast, you will need to install the "devel" RPM
+packages for each of the prerequisite packages in addition to the
+normal RPMS for each package.
+
+please check the websites for each of the prerequisite packages for
+appropriate download links for RPMS.
+
+
+Build/Install
+---------------------------------------------------------------------
+To build icecast on a Unix platform, perform the following :
+
+Run
+   ./configure
+   make
+   make install
+
+To build and install this release.
+
+A sample config file will be placed in /usr/local/etc (on UNIX) or in
+the current working directory (on Win32) and is called icecast.xml
+
+Documentation for icecast is available in the doc directory, by
+viewing doc/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.
+
+</pre>


Property changes on: icecast/branches/icecast-kh/doc/index.html
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/doc/index_win32.html
===================================================================
--- icecast/branches/icecast-kh/doc/index_win32.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/index_win32.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,19 @@
+<!doctype html public "-//w3c//dtd html 3.2//en">
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<html>
+<head>
+<title>Icecast2 Win32</title>
+</head>
+<div class="boxtest">
+<h1>Icecast 2 - Win32 Specific Documentation</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<p>
+The win32 port of icecast2 is simply a UI framework around the core icecast2 server.  The win32 version of icecast2 uses directly the main executable of icecast (statically included) and simply provides a nicer, friendlier interface to icecast2.
+</p>
+<p>
+All of the features of icecast2 are available in the win32 port.
+</p>
+</div>
+</body>
+
+</html>

Added: icecast/branches/icecast-kh/doc/stats1.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/doc/stats1.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/doc/style.css
===================================================================
--- icecast/branches/icecast-kh/doc/style.css	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/style.css	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,79 @@
+.ahem { display: none }
+body {
+	font-family:'Lucida Grande', Verdana, Geneva, Lucida, sans-serif;
+	background:#000000;
+	link:#FFFFFF;
+	text:#323232;
+	vlink:#FFFFFF;
+	alink:#FFFFFF;
+}
+a {
+    font-weight: bold;
+    text-decoration: none;
+}
+
+a:link	{	color: #ff0; }
+a:visited { color: #cc3; }
+a:hover	{	color: #f00; }
+code,pre {
+	font-size:90%;
+	color:#ffffff;
+	font-family:"Courier New",monospace;
+	background:#777777;
+	padding:0 0.5em
+}
+td {
+	color:#ffffff;
+}
+pre { padding:0.5em }
+blockquote { margin:0.5em }
+blockquote p { margin:0 }
+
+.width300 { width:300px; background:red }
+.width400 { width:400px; background:blue }
+
+p.ruletest { color:red }
+
+div.boxtest {
+ border:2px solid;
+ padding:30px;
+ background: #555555;
+ width:80%;;
+ color:#ffffff;
+}
+
+div.smallbox{
+ border:2px solid;
+ padding:40px;
+ background: #ffc;
+ width:600px;
+ text:#FFFFFF;
+}
+
+div.indentedbox {
+ border:0px solid;
+ padding:10px;
+ background: #779;
+}
+
+
+div.content {
+ border:20px solid;
+ padding:30px;
+ background: #ffc;
+}
+
+div.content {
+ width:400px;
+ voice-family: "\"}\"";
+ voice-family:inherit;
+ width:300px;
+}
+  /* CSS1 UAs should see and use 2nd width */
+
+html>body .content { width:300px }
+
+p.ruletest { color: blue }
+
+
+

Added: icecast/branches/icecast-kh/doc/win32_section1.html
===================================================================
--- icecast/branches/icecast-kh/doc/win32_section1.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/win32_section1.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,34 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Server Status Tab</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<h3>Overview</h3>
+<p>
+The server status tab contains information regarding statistics that are global to the server.  There are two types of statistics in icecast2, source level and global statistics.  Global statistics are those that are accumilations of stats from all sources offered by the server.  Source level statistics are stats which apply only to a single source attached to the server.
+</p>
+<p>
+Examples of global statistics are :
+</p>
+<pre>
+	The number of current sources connected
+	The number of sources that have attempted connections
+	Total number of attempted connections to the server
+</pre>
+</p>
+<p>
+The Server Status tab contains at a minimal the global stats for the server.  Additionally, you may add source specific stats to this tab.  The intent is to provide a single "dashboard view" of what's going on in the server.  To add source statistics to the Server Status tab, see the section on the <a href="win32_section3.html">Stats</a> tab.
+</p>
+<h3>Adding stats to the window title</h3>
+<p>
+Any stat that is contained on the Server Status tab can be displayed as the icecast2 window title.  This provides yet another mechanism by which you can view activities on the server.  To enable this feature, right click on any stat in the Server Status tab as seen below :
+</p>
+<img src="windowtitle.jpg">
+<p>
+<h3>Removing source level stats from the Server Status Tab</h3>
+<p>
+To remove a source level stat that you have inserted onto the Server Status Tab, simple right click that statistic and select "Delete from Global Stats".  The stat will be deleted from the Server Status tab, but will still remain on the source level Stats tab.
+</p>
+
+</p>
+</div>

Added: icecast/branches/icecast-kh/doc/win32_section2.html
===================================================================
--- icecast/branches/icecast-kh/doc/win32_section2.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/win32_section2.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,9 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<body bgcolor="#efefef" text="#323232" link="#0000ff" vlink="#800080" alink="#ff0000">
+<h1>Editing A Config File</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<p>
+Editing the icecast2 configuration file is a very simple process.  For a description of what each field means, see the main icecast documenation.  Changes to the icecast2 configuration can only be done while the server is stopped.  To edit the current server configuration file, select "Configuration/Edit Configuration" from the main menu.
+</p>
+</div>

Added: icecast/branches/icecast-kh/doc/win32_section3.html
===================================================================
--- icecast/branches/icecast-kh/doc/win32_section3.html	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/doc/win32_section3.html	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,9 @@
+<LINK REL=STYLESHEET TYPE= "text/css" HREF= "style.css">
+<div class=boxtest>
+<h1>Stats Tab</h1>
+<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
+<p>
+Explanation of the stats tab here
+</p>
+<img src="stats1.jpg"><br>
+</div>

Added: icecast/branches/icecast-kh/doc/windowtitle.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/doc/windowtitle.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

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

Added: icecast/branches/icecast-kh/src/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/src/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,31 @@
+## Process this with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = avl thread httpp net log timing
+
+bin_PROGRAMS = icecast
+
+noinst_HEADERS = admin.h cfgfile.h 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
+icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@
+
+AM_CFLAGS = @XIPH_CFLAGS@
+AM_CPPFLAGS = @XIPH_CPPFLAGS@
+
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+

Added: icecast/branches/icecast-kh/src/TODO
===================================================================
--- icecast/branches/icecast-kh/src/TODO	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/TODO	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1 @@
+need a shutdown function in case anything else in the code needs to have icecast gracefully shutdown.
\ No newline at end of file

Added: icecast/branches/icecast-kh/src/admin.c
===================================================================
--- icecast/branches/icecast-kh/src/admin.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/admin.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,896 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "cfgfile.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "source.h"
+#include "global.h"
+#include "event.h"
+#include "stats.h"
+#include "os.h"
+#include "xslt.h"
+
+#include "format.h"
+
+#include "logging.h"
+#include "auth.h"
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "admin"
+
+#define COMMAND_ERROR             (-1)
+
+/* Mount-specific commands */
+#define COMMAND_RAW_FALLBACK        1
+#define COMMAND_METADATA_UPDATE     2
+#define COMMAND_RAW_SHOW_LISTENERS  3
+#define COMMAND_RAW_MOVE_CLIENTS    4
+#define COMMAND_RAW_MANAGEAUTH      5
+
+#define COMMAND_TRANSFORMED_FALLBACK        50
+#define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
+#define COMMAND_TRANSFORMED_MOVE_CLIENTS    54
+#define COMMAND_TRANSFORMED_MANAGEAUTH      55
+
+/* Global commands */
+#define COMMAND_RAW_LIST_MOUNTS             101
+#define COMMAND_RAW_STATS                   102
+#define COMMAND_RAW_LISTSTREAM              103
+#define COMMAND_PLAINTEXT_LISTSTREAM        104
+#define COMMAND_TRANSFORMED_LIST_MOUNTS     201
+#define COMMAND_TRANSFORMED_STATS           202
+#define COMMAND_TRANSFORMED_LISTSTREAM      203
+
+/* Client management commands */
+#define COMMAND_RAW_KILL_CLIENT             301
+#define COMMAND_RAW_KILL_SOURCE             302
+#define COMMAND_TRANSFORMED_KILL_CLIENT     401
+#define COMMAND_TRANSFORMED_KILL_SOURCE     402
+
+/* Admin commands requiring no auth */
+#define COMMAND_BUILDM3U                    501
+
+#define FALLBACK_RAW_REQUEST "fallbacks"
+#define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
+#define METADATA_REQUEST "metadata"
+#define LISTCLIENTS_RAW_REQUEST "listclients"
+#define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
+#define STATS_RAW_REQUEST "stats"
+#define STATS_TRANSFORMED_REQUEST "stats.xsl"
+#define LISTMOUNTS_RAW_REQUEST "listmounts"
+#define LISTMOUNTS_TRANSFORMED_REQUEST "listmounts.xsl"
+#define STREAMLIST_RAW_REQUEST "streamlist"
+#define STREAMLIST_TRANSFORMED_REQUEST "streamlist.xsl"
+#define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
+#define MOVECLIENTS_RAW_REQUEST "moveclients"
+#define MOVECLIENTS_TRANSFORMED_REQUEST "moveclients.xsl"
+#define KILLCLIENT_RAW_REQUEST "killclient"
+#define KILLCLIENT_TRANSFORMED_REQUEST "killclient.xsl"
+#define KILLSOURCE_RAW_REQUEST "killsource"
+#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
+#define ADMIN_XSL_RESPONSE "response.xsl"
+#define MANAGEAUTH_RAW_REQUEST "manageauth"
+#define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
+#define DEFAULT_RAW_REQUEST ""
+#define DEFAULT_TRANSFORMED_REQUEST ""
+#define BUILDM3U_RAW_REQUEST "buildm3u"
+
+#define RAW         1
+#define TRANSFORMED 2
+#define PLAINTEXT   3
+
+int admin_get_command(char *command)
+{
+    if(!strcmp(command, FALLBACK_RAW_REQUEST))
+        return COMMAND_RAW_FALLBACK;
+    else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_FALLBACK;
+    else if(!strcmp(command, METADATA_REQUEST))
+        return COMMAND_METADATA_UPDATE;
+    else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
+        return COMMAND_RAW_SHOW_LISTENERS;
+    else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_SHOW_LISTENERS;
+    else if(!strcmp(command, STATS_RAW_REQUEST))
+        return COMMAND_RAW_STATS;
+    else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_STATS;
+    else if(!strcmp(command, "stats.xml")) /* The old way */
+        return COMMAND_RAW_STATS;
+    else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
+        return COMMAND_RAW_LIST_MOUNTS;
+    else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_LIST_MOUNTS;
+    else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
+        return COMMAND_RAW_LISTSTREAM;
+    else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
+        return COMMAND_PLAINTEXT_LISTSTREAM;
+    else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
+        return COMMAND_RAW_MOVE_CLIENTS;
+    else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_MOVE_CLIENTS;
+    else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
+        return COMMAND_RAW_KILL_CLIENT;
+    else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_KILL_CLIENT;
+    else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
+        return COMMAND_RAW_KILL_SOURCE;
+    else if(!strcmp(command, 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, 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))
+        return COMMAND_TRANSFORMED_STATS;
+    else
+        return COMMAND_ERROR;
+}
+
+static void command_fallback(client_t *client, source_t *source, int response);
+static void command_metadata(client_t *client, source_t *source);
+static void command_show_listeners(client_t *client, source_t *source,
+        int response);
+static void command_move_clients(client_t *client, source_t *source,
+        int response);
+static void command_stats(client_t *client, int response);
+static void command_list_mounts(client_t *client, int response);
+static void command_kill_client(client_t *client, source_t *source,
+        int response);
+static void command_manageauth(client_t *client, source_t *source,
+        int response);
+static void command_buildm3u(client_t *client, source_t *source,
+        int response);
+static void command_kill_source(client_t *client, source_t *source,
+        int response);
+static void admin_handle_mount_request(client_t *client, source_t *source,
+        int command);
+static void admin_handle_general_request(client_t *client, int command);
+static void admin_send_response(xmlDocPtr doc, client_t *client,
+        int response, char *xslt_template);
+static void html_write(client_t *client, char *fmt, ...);
+
+xmlDocPtr admin_build_sourcelist(char *current_source)
+{
+    avl_node *node;
+    source_t *source;
+    xmlNodePtr xmlnode, srcnode;
+    xmlDocPtr doc;
+    char buf[22];
+    time_t now = time(NULL);
+
+    doc = xmlNewDoc("1.0");
+    xmlnode = xmlNewDocNode(doc, NULL, "icestats", NULL);
+    xmlDocSetRootElement(doc, xmlnode);
+
+    if (current_source) {
+        xmlNewChild(xmlnode, NULL, "current_source", current_source);
+    }
+
+    node = avl_get_first(global.source_tree);
+    while(node) {
+        source = (source_t *)node->key;
+        thread_mutex_lock (&source->lock);
+        if (source->running || source->on_demand)
+        {
+            srcnode = xmlNewChild (xmlnode, NULL, "source", NULL);
+            xmlSetProp (srcnode, "mount", source->mount);
+
+            xmlNewChild (srcnode, NULL, "fallback",
+                    (source->fallback_mount != NULL)?
+                    source->fallback_mount:"");
+            snprintf (buf, sizeof(buf), "%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);
+}
+
+void admin_send_response(xmlDocPtr doc, client_t *client,
+        int response, char *xslt_template)
+{
+    xmlChar *buff = NULL;
+    int len = 0;
+    ice_config_t *config;
+    char *fullpath_xslt_template;
+    int fullpath_xslt_template_len;
+    char *adminwebroot;
+
+    client->respcode = 200;
+    if (response == RAW) {
+        xmlDocDumpMemory(doc, &buff, &len);
+        html_write(client, "HTTP/1.0 200 OK\r\n"
+               "Content-Length: %d\r\n"
+               "Content-Type: text/xml\r\n"
+               "\r\n", len);
+        html_write(client, buff);
+    }
+    if (response == TRANSFORMED) {
+        config = config_get_config();
+        adminwebroot = config->adminroot_dir;
+        config_release_config();
+        fullpath_xslt_template_len = strlen(adminwebroot) +
+            strlen(xslt_template) + 2;
+        fullpath_xslt_template = malloc(fullpath_xslt_template_len);
+        memset(fullpath_xslt_template, '\000', fullpath_xslt_template_len);
+        snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s",
+            adminwebroot, PATH_SEPARATOR, xslt_template);
+        html_write(client, "HTTP/1.0 200 OK\r\n"
+               "Content-Type: text/html\r\n"
+               "\r\n");
+        DEBUG1("Sending XSLT (%s)", fullpath_xslt_template);
+        xslt_transform(doc, fullpath_xslt_template, client);
+        free(fullpath_xslt_template);
+    }
+    if (buff) {
+        xmlFree(buff);
+    }
+}
+void admin_handle_request(client_t *client, char *uri)
+{
+    char *mount, *command_string;
+    int command;
+    int noauth = 0;
+
+    if(strncmp("/admin/", uri, 7)) {
+        ERROR0("Internal error: admin request isn't");
+        client_send_401(client);
+        return;
+    }
+
+    command_string = uri + 7;
+
+    DEBUG1("Got command (%s)", command_string);
+    command = admin_get_command(command_string);
+
+    if(command < 0) {
+        ERROR1("Error parsing command string or unrecognised command: %s",
+                command_string);
+        client_send_400(client, "Unrecognised command");
+        return;
+    }
+
+    mount = httpp_get_query_param(client->parser, "mount");
+
+    if(mount != NULL) {
+        source_t *source;
+
+        if (command == COMMAND_BUILDM3U) {
+            noauth = 1;
+        }
+        /* This is a mount request, handle it as such */
+        if (!noauth) {
+            if(!connection_check_admin_pass(client->parser)) {
+                if(!connection_check_source_pass(client->parser, mount)) {
+                    INFO1("Bad or missing password on mount modification admin "
+                            "request (command: %s)", command_string);
+                    client_send_401(client);
+                    return;
+                }
+            }
+        }
+
+        avl_tree_rlock(global.source_tree);
+        source = source_find_mount_raw(mount);
+
+        if (source == NULL)
+        {
+            WARN2("Admin command %s on non-existent source %s",
+                    command_string, mount);
+            avl_tree_unlock(global.source_tree);
+            client_send_400 (client, "Source does not exist");
+        }
+        else
+        {
+            if (source->running == 0)
+            {
+                INFO2("Received admin command %s on unavailable mount \"%s\"",
+                        command_string, mount);
+                avl_tree_unlock (global.source_tree);
+                client_send_400 (client, "Source is not available");
+                return;
+            }
+            INFO2("Received admin command %s on mount \"%s\"",
+                    command_string, mount);
+            admin_handle_mount_request (client, source, command);
+            avl_tree_unlock(global.source_tree);
+        }
+    }
+    else {
+        if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
+            /* this request is used by a slave relay to retrieve
+               mounts from the master, so handle this request
+               validating against the relay password */
+            if(!connection_check_relay_pass(client->parser)) {
+                INFO1("Bad or missing password on admin command "
+                      "request (command: %s)", command_string);
+                client_send_401(client);
+                return;
+            }
+        }
+        else {
+            if(!connection_check_admin_pass (client->parser)) {
+                INFO1("Bad or missing password on admin command "
+                      "request (command: %s)", command_string);
+                client_send_401(client);
+                return;
+            }
+        }
+
+        admin_handle_general_request(client, command);
+    }
+}
+
+static void admin_handle_general_request(client_t *client, int command)
+{
+    switch(command) {
+        case COMMAND_RAW_STATS:
+            command_stats(client, RAW);
+            break;
+        case COMMAND_RAW_LIST_MOUNTS:
+            command_list_mounts(client, RAW);
+            break;
+        case COMMAND_RAW_LISTSTREAM:
+            command_list_mounts(client, RAW);
+            break;
+        case COMMAND_PLAINTEXT_LISTSTREAM:
+            command_list_mounts(client, PLAINTEXT);
+            break;
+        case COMMAND_TRANSFORMED_STATS:
+            command_stats(client, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_LIST_MOUNTS:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_LISTSTREAM:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
+            command_list_mounts(client, TRANSFORMED);
+            break;
+        default:
+            WARN0("General admin request not recognised");
+            client_send_400(client, "Unknown admin request");
+            return;
+    }
+}
+
+static void admin_handle_mount_request(client_t *client, source_t *source,
+        int command)
+{
+    switch(command) {
+        case COMMAND_RAW_FALLBACK:
+            command_fallback(client, source, RAW);
+            break;
+        case COMMAND_METADATA_UPDATE:
+            command_metadata(client, source);
+            break;
+        case COMMAND_RAW_SHOW_LISTENERS:
+            command_show_listeners(client, source, RAW);
+            break;
+        case COMMAND_RAW_MOVE_CLIENTS:
+            command_move_clients(client, source, RAW);
+            break;
+        case COMMAND_RAW_KILL_CLIENT:
+            command_kill_client(client, source, RAW);
+            break;
+        case COMMAND_RAW_KILL_SOURCE:
+            command_kill_source(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_FALLBACK:
+            command_fallback(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_SHOW_LISTENERS:
+            command_show_listeners(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
+            command_move_clients(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_KILL_CLIENT:
+            command_kill_client(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_KILL_SOURCE:
+            command_kill_source(client, source, TRANSFORMED);
+            break;
+        case COMMAND_TRANSFORMED_MANAGEAUTH:
+            command_manageauth(client, source, TRANSFORMED);
+            break;
+        case COMMAND_RAW_MANAGEAUTH:
+            command_manageauth(client, source, RAW);
+            break;
+        case COMMAND_BUILDM3U:
+            command_buildm3u(client, source, RAW);
+            break;
+        default:
+            WARN0("Mount request not recognised");
+            client_send_400(client, "Mount request unknown");
+            break;
+    }
+}
+
+#define COMMAND_REQUIRE(client,name,var) \
+    do { \
+        (var) = httpp_get_query_param((client)->parser, (name)); \
+        if((var) == NULL) { \
+            client_send_400((client), "Missing parameter"); \
+            return; \
+        } \
+    } while(0);
+#define COMMAND_OPTIONAL(client,name,var) \
+    (var) = httpp_get_query_param((client)->parser, (name))
+
+static void html_success(client_t *client, char *message)
+{
+    int bytes;
+
+    client->respcode = 200;
+    bytes = sock_write(client->con->sock,
+            "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
+            "<html><head><title>Admin request successful</title></head>"
+            "<body><p>%s</p></body></html>", message);
+    if(bytes > 0) client->con->sent_bytes = bytes;
+    client_destroy(client);
+}
+
+static void html_write(client_t *client, char *fmt, ...)
+{
+    int bytes;
+    va_list ap;
+
+    va_start(ap, fmt);
+    bytes = sock_write_fmt(client->con->sock, fmt, ap);
+    va_end(ap);
+    if(bytes > 0) client->con->sent_bytes = bytes;
+}
+
+static void command_move_clients(client_t *client, source_t *source,
+    int response)
+{
+    char *dest_source;
+    source_t *dest;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    char buf[255];
+    int parameters_passed = 0;
+
+    if((COMMAND_OPTIONAL(client, "destination", dest_source))) {
+        parameters_passed = 1;
+    }
+    if (!parameters_passed) {
+        doc = admin_build_sourcelist(source->mount);
+        admin_send_response(doc, client, response,
+             MOVECLIENTS_TRANSFORMED_REQUEST);
+        xmlFreeDoc(doc);
+        client_destroy(client);
+        return;
+    }
+
+    dest = source_find_mount (dest_source);
+
+    if (dest == NULL)
+    {
+        client_send_400 (client, "No such destination");
+        return;
+    }
+
+    if (strcmp (dest->mount, source->mount) == 0)
+    {
+        client_send_400 (client, "supplied mountpoints are identical");
+        return;
+    }
+
+    if (dest->running == 0 && dest->on_demand == 0)
+    {
+        client_send_400 (client, "Destination not running");
+        return;
+    }
+
+    DEBUG2("source is \"%s\", destination is \"%s\"", source->mount, dest->mount);
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
+    xmlDocSetRootElement(doc, node);
+
+    source_move_clients (source, dest);
+
+    memset(buf, '\000', sizeof(buf));
+    snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
+            source->mount, dest_source);
+    xmlNewChild(node, NULL, "message", buf);
+    xmlNewChild(node, NULL, "return", "1");
+
+    admin_send_response(doc, client, response,
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
+
+static void command_show_listeners(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode;
+    char *userAgent = NULL;
+    xmlNodePtr listenernode;
+    client_t *current;
+    time_t now = time(NULL);
+    char buf[22];
+
+    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);
+
+    memset(buf, '\000', sizeof(buf));
+    snprintf(buf, sizeof(buf)-1, "%ld", source->listeners);
+    xmlNewChild(srcnode, NULL, "Listeners", buf);
+
+    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");
+        if (userAgent) {
+            xmlNewChild(listenernode, NULL, "UserAgent", userAgent);
+        }
+        else {
+            xmlNewChild(listenernode, NULL, "UserAgent", "Unknown");
+        }
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "%ld", now - current->con->con_time);
+        xmlNewChild(listenernode, NULL, "Connected", buf);
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
+        xmlNewChild(listenernode, NULL, "ID", buf);
+        if (current->username)
+            xmlNewChild(listenernode, NULL, "username", current->username);
+
+        current = current->next;
+    }
+
+    thread_mutex_unlock (&source->lock);
+
+    admin_send_response(doc, client, response,
+        LISTCLIENTS_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
+
+static void command_buildm3u(client_t *client, source_t *source,
+    int response)
+{
+    char *username = NULL;
+    char *password = NULL;
+    char *host = NULL;
+    int port = 0;
+    ice_config_t *config;
+
+    COMMAND_REQUIRE(client, "username", username);
+    COMMAND_REQUIRE(client, "password", password);
+
+    config = config_get_config();
+    host = strdup(config->hostname);
+    port = config->port;
+    config_release_config();
+
+    client->respcode = 200;
+    sock_write(client->con->sock,
+        "HTTP/1.0 200 OK\r\n"
+        "Content-Type: audio/x-mpegurl\r\n"
+        "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n"
+        "http://%s:%s@%s:%d%s\r\n",
+        username,
+        password,
+        host,
+        port,
+        source->mount
+    );
+
+    free(host);
+    client_destroy(client);
+}
+
+
+static void command_manageauth(client_t *client, source_t *source,
+        int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode, msgnode;
+    char *action = NULL;
+    char *username = NULL;
+    char *password = NULL;
+    char *message = NULL;
+    int ret = AUTH_OK;
+
+    if((COMMAND_OPTIONAL(client, "action", action))) {
+        if (!strcmp(action, "add")) {
+            COMMAND_REQUIRE(client, "username", username);
+            COMMAND_REQUIRE(client, "password", password);
+            ret = source->authenticator->adduser(source->authenticator, username, password);
+            if (ret == AUTH_FAILED) {
+                message = strdup("User add failed - check the icecast error log");
+            }
+            if (ret == AUTH_USERADDED) {
+                message = strdup("User added");
+            }
+            if (ret == AUTH_USEREXISTS) {
+                message = strdup("User already exists - not added");
+            }
+        }
+        if (!strcmp(action, "delete")) {
+            COMMAND_REQUIRE(client, "username", username);
+            ret = source->authenticator->deleteuser(source->authenticator, username);
+            if (ret == AUTH_FAILED) {
+                message = strdup("User delete failed - check the icecast error log");
+            }
+            if (ret == AUTH_USERDELETED) {
+                message = strdup("User deleted");
+            }
+        }
+    }
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
+    srcnode = xmlNewChild(node, NULL, "source", NULL);
+    xmlSetProp(srcnode, "mount", source->mount);
+
+    if (message) {
+        msgnode = xmlNewChild(node, NULL, "iceresponse", NULL);
+        xmlNewChild(msgnode, NULL, "message", message);
+    }
+
+    xmlDocSetRootElement(doc, node);
+
+    source->authenticator->listuser(source->authenticator, srcnode);
+
+    admin_send_response(doc, client, response,
+            MANAGEAUTH_TRANSFORMED_REQUEST);
+    if (message) {
+        free(message);
+    }
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
+
+
+static void command_kill_source(client_t *client, source_t *source,
+        int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node;
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
+    xmlNewChild(node, NULL, "message", "Source Removed");
+    xmlNewChild(node, NULL, "return", "1");
+    xmlDocSetRootElement(doc, node);
+
+    source->running = 0;
+
+    admin_send_response(doc, client, response,
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
+
+static void command_kill_client(client_t *client, source_t *source,
+    int response)
+{
+    char *idtext;
+    int id;
+    client_t *listener;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    char buf[50] = "";
+
+    COMMAND_REQUIRE(client, "id", idtext);
+
+    id = atoi(idtext);
+
+    thread_mutex_lock (&source->lock);
+    listener = source_find_client(source, id);
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
+    xmlDocSetRootElement(doc, node);
+    DEBUG1("Response is %d", response);
+
+    if(listener != NULL) {
+        INFO1("Admin request: client %d removed", id);
+
+        /* This tags it for removal on the next iteration of the main source
+         * loop
+         */
+        listener->con->error = 1;
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "Client %d removed", id);
+        xmlNewChild(node, NULL, "message", buf);
+        xmlNewChild(node, NULL, "return", "1");
+    }
+    else {
+        memset(buf, '\000', sizeof(buf));
+        snprintf(buf, sizeof(buf)-1, "Client %d not found", id);
+        xmlNewChild(node, NULL, "message", buf);
+        xmlNewChild(node, NULL, "return", "0");
+    }
+    thread_mutex_unlock (&source->lock);
+    admin_send_response(doc, client, response,
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
+
+static void command_fallback(client_t *client, source_t *source,
+    int response)
+{
+    char *fallback;
+    char *old;
+
+    DEBUG0("Got fallback request");
+
+    COMMAND_REQUIRE(client, "fallback", fallback);
+
+    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");
+}
+
+static void command_metadata(client_t *client, source_t *source)
+{
+    char *action;
+    char *song, *title, *artist;
+    format_plugin_t *plugin;
+
+    DEBUG0("Got metadata update request");
+
+    COMMAND_REQUIRE(client, "mode", action);
+    COMMAND_OPTIONAL(client, "song", song);
+    COMMAND_OPTIONAL(client, "title", title);
+    COMMAND_OPTIONAL(client, "artist", artist);
+
+    if (strcmp(action, "updinfo") != 0)
+    {
+        client_send_400(client, "No such action");
+        return;
+    }
+
+    thread_mutex_lock (&source->lock);
+
+    plugin = source->format;
+    if (plugin && plugin->set_tag)
+    {
+        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");
+    }
+    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) {
+    xmlDocPtr doc;
+
+    DEBUG0("Stats request, sending xml stats");
+
+    stats_get_xml(&doc);
+    admin_send_response(doc, client, response, STATS_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+    return;
+}
+
+static void command_list_mounts(client_t *client, int response)
+{
+    DEBUG0("List mounts request");
+
+    avl_tree_rlock (global.source_tree);
+    if (response == PLAINTEXT)
+    {
+        char buffer [4096], *buf = buffer;
+        unsigned remaining = sizeof (buffer);
+        int ret = sprintf (buffer,
+                "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
+
+        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;
+            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 && (unsigned)ret < remaining)
+        {
+            remaining -= ret;
+            buf += ret;
+        }
+        sock_write_bytes (client->con->sock, buffer, sizeof (buffer)-remaining);
+    }
+    else
+    {
+        xmlDocPtr doc = admin_build_sourcelist(NULL);
+        avl_tree_unlock (global.source_tree);
+
+        admin_send_response(doc, client, response,
+                LISTMOUNTS_TRANSFORMED_REQUEST);
+        xmlFreeDoc(doc);
+    }
+    client_destroy(client);
+
+    return;
+}
+

Added: icecast/branches/icecast-kh/src/admin.h
===================================================================
--- icecast/branches/icecast-kh/src/admin.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/admin.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,21 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __ADMIN_H__
+#define __ADMIN_H__
+
+#include "refbuf.h"
+#include "client.h"
+
+void admin_handle_request(client_t *client, char *uri);
+
+#endif  /* __ADMIN_H__ */

Added: icecast/branches/icecast-kh/src/auth.c
===================================================================
--- icecast/branches/icecast-kh/src/auth.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,170 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/**
+ * Client authentication functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "auth.h"
+#include "auth_htpasswd.h"
+#include "auth_cmd.h"
+#include "auth_url.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+#include "md5.h"
+
+#include "logging.h"
+#define CATMODULE "auth"
+
+
+
+auth_result auth_check_client(source_t *source, client_t *client)
+{
+    auth_t *authenticator = source->authenticator;
+    auth_result result;
+
+    if(authenticator) {
+        /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
+        char *header = httpp_getvar(client->parser, "authorization");
+        char *userpass, *tmp;
+        char *username, *password;
+
+        if(header == NULL)
+            return AUTH_FAILED;
+
+        if(strncmp(header, "Basic ", 6)) {
+            INFO0("Authorization not using Basic");
+            return 0;
+        }
+
+        userpass = util_base64_decode(header+6);
+        if(userpass == NULL) {
+            WARN1("Base64 decode of Authorization header \"%s\" failed",
+                    header+6);
+            return AUTH_FAILED;
+        }
+
+        tmp = strchr(userpass, ':');
+        if(!tmp) {
+            free(userpass);
+            return AUTH_FAILED;
+        }
+
+        *tmp = 0;
+        username = userpass;
+        password = tmp+1;
+
+        client->username = strdup (username);
+        client->password = strdup (password);
+
+        result = authenticator->authenticate (source, client);
+
+        free(userpass);
+
+        return result;
+    }
+    else
+    {
+        /* just add the client */
+        add_authenticated_client (source, client);
+        return AUTH_OK;
+    }
+}
+
+
+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;
+#ifdef HAVE_AUTH_URL
+    if(!strcmp(type, "url")) {
+        auth = auth_get_url_auth(options);
+        auth->type = strdup(type);
+    }
+    else
+#endif
+    if(!strcmp(type, "command")) {
+        auth = auth_get_cmd_auth(options);
+        auth->type = strdup(type);
+    }
+    else if(!strcmp(type, "htpasswd")) {
+        auth = auth_get_htpasswd_auth(options);
+        auth->type = strdup(type);
+    }
+    else {
+        ERROR1("Unrecognised authenticator type: \"%s\"", type);
+        return NULL;
+    }
+
+    if(!auth)
+        ERROR1("Couldn't configure authenticator of type \"%s\"", type);
+
+    return auth;
+}
+
+
+/* place authenticated client on named source */
+int auth_postprocess_client (const char *mount, client_t *client)
+{
+    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);
+    }
+    avl_tree_unlock (global.source_tree);
+    if (ret < 0)
+        source_free_client (NULL, client);
+    return ret;
+}
+
+
+void auth_close_client (client_t *client)
+{
+    /* failed client, drop global count */
+    global_lock();
+    global.clients--;
+    global_unlock();
+    if (client->respcode)
+        client_destroy (client);
+    else
+        client_send_401 (client);
+}
+

Added: icecast/branches/icecast-kh/src/auth.h
===================================================================
--- icecast/branches/icecast-kh/src/auth.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,56 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+typedef enum
+{
+    AUTH_OK,
+    AUTH_FAILED,
+    AUTH_USERADDED,
+    AUTH_USEREXISTS,
+    AUTH_USERDELETED,
+} auth_result;
+
+typedef struct auth_tag
+{
+    /* Authenticate using the given username and 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;
+
+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_postprocess_client (const char *mount, client_t *client);
+void auth_close_client (client_t *client);
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/auth_cmd.c
===================================================================
--- icecast/branches/icecast-kh/src/auth_cmd.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_cmd.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,183 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/**
+ * Client authentication via command functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+#define CATMODULE "auth_cmd"
+
+typedef struct {
+    char *filename;
+} auth_cmd;
+
+typedef struct {
+   char *mount;
+   client_t *client;
+   char *cmd;
+} auth_client;
+
+
+static void cmd_clear(auth_t *self)
+{
+    auth_cmd *cmd = self->state;
+    free(cmd->filename);
+    free(cmd);
+}
+
+static void *auth_cmd_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    int fd[2];
+    int ok = 0;
+    int send_fail = 404;
+    pid_t pid;
+    client_t *client = auth_user->client;
+    int status, len;
+    char str[256];
+
+    DEBUG0("starting auth thread");
+    if (pipe (fd) == 0)
+    {
+        pid = fork();
+        switch (pid)
+        {
+            case 0: /* child */
+                dup2 (fd[0], fileno(stdin));
+                close (fd[0]);
+                close (fd[1]);
+                execl (auth_user->cmd, auth_user->cmd, NULL);
+                exit (EXIT_FAILURE);
+            case -1:
+                break;
+            default: /* parent */
+                close (fd[0]);
+                len = snprintf (str, sizeof(str), "%s\n%s\n%s\n",
+                        auth_user->mount, client->username, client->password);
+                write (fd[1], str, len);
+                close (fd[1]);
+                DEBUG1 ("Waiting on pid %ld", (long)pid);
+                if (waitpid (pid, &status, 0) < 0)
+                {
+                    DEBUG1("waitpid error %s", strerror(errno));
+                    break;
+                }
+                if (WIFEXITED (status))
+                {
+                    DEBUG1("command exited normally with %d", WEXITSTATUS (status));
+                    if (WEXITSTATUS(status) == 0)
+                        ok = 1;
+                    else
+                        send_fail = 401;
+                }
+                break;
+        }
+        /* try to add authenticated client */
+        if (ok)
+        {
+            source_t *source;
+            send_fail = 0;
+            avl_tree_unlock (global.source_tree);
+            source = source_find_mount (auth_user->mount);
+            if (source)
+            {
+                thread_mutex_lock (&source->lock);
+                if (source->running)
+                    add_authenticated_client (source, auth_user->client);
+                else
+                    send_fail = 404;
+                thread_mutex_unlock (&source->lock);
+            }
+            avl_tree_unlock (global.source_tree);
+        }
+    }
+    if (send_fail == 404)
+        client_send_404 (client, "Mount not available");
+    if (send_fail == 401)
+        client_send_401 (client);
+    free (auth_user->mount);
+    free (auth_user->cmd);
+    free (auth_user);
+    return NULL;
+}
+
+
+static auth_result auth_cmd_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_cmd *cmd = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return AUTH_FAILED;
+
+    auth_user->cmd = strdup (cmd->filename);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("Auth by command thread", auth_cmd_thread, auth_user, THREAD_DETACHED);
+    return AUTH_OK;
+}
+
+static auth_result auth_cmd_adduser(auth_t *auth, const char *username, const char *password)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_cmd_deleteuser (auth_t *auth, const char *username)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_cmd_listuser (auth_t *auth, xmlNodePtr srcnode)
+{
+    return AUTH_FAILED;
+}
+
+auth_t *auth_get_cmd_auth (config_options_t *options)
+{
+    auth_t *authenticator = calloc(1, sizeof(auth_t));
+    auth_cmd *state;
+
+    authenticator->authenticate = auth_cmd_client;
+    authenticator->free = cmd_clear;
+    authenticator->adduser = auth_cmd_adduser;
+    authenticator->deleteuser = auth_cmd_deleteuser;
+    authenticator->listuser = auth_cmd_listuser;
+
+    state = calloc(1, sizeof(auth_cmd));
+
+    while(options) {
+        if(!strcmp(options->name, "filename"))
+            state->filename = strdup(options->value);
+        options = options->next;
+    }
+    authenticator->state = state;
+    INFO0("external command based authentication setup");
+    return authenticator;
+}
+

Added: icecast/branches/icecast-kh/src/auth_cmd.h
===================================================================
--- icecast/branches/icecast-kh/src/auth_cmd.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_cmd.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,27 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_CMD_H__
+#define __AUTH_CMD_H__
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_cmd_auth (config_options_t *options);
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/auth_htpasswd.c
===================================================================
--- icecast/branches/icecast-kh/src/auth_htpasswd.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_htpasswd.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,363 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/**
+ * Client authentication functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+#include "md5.h"
+
+#include "logging.h"
+#define CATMODULE "auth"
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode);
+
+typedef struct {
+    char *filename;
+    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);
+}
+
+static int get_line(FILE *file, char *buf, int len)
+{
+    if(fgets(buf, len, file)) {
+        int len = strlen(buf);
+        if(len > 0 && buf[len-1] == '\n') {
+            buf[--len] = 0;
+            if(len > 0 && buf[len-1] == '\r')
+                buf[--len] = 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/* md5 hash */
+static char *get_hash(const char *data, int len)
+{
+    struct MD5Context context;
+    unsigned char digest[16];
+
+    MD5Init(&context);
+
+    MD5Update(&context, data, len);
+
+    MD5Final(digest, &context);
+
+    return util_bin_to_hex(digest, 16);
+}
+
+#define MAX_LINE_LEN 512
+
+/* Not efficient; opens and scans the entire file for every request */
+static auth_result htpasswd_auth(source_t *source, client_t *client)
+{
+    auth_t *auth = source->authenticator;
+    htpasswd_auth_state *state = auth->state;
+    FILE *passwdfile = fopen(state->filename, "rb");
+    char line[MAX_LINE_LEN];
+    char *sep;
+
+    thread_rwlock_rlock(&state->file_rwlock);
+    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(client->username, line)) {
+            /* Found our user, now: does the hash of password match hash? */
+            char *hash = sep+1;
+            char *hashed_password = get_hash(client->password, strlen(client->password));
+            if(!strcmp(hash, hashed_password)) {
+                fclose(passwdfile);
+                free(hashed_password);
+                thread_rwlock_unlock(&state->file_rwlock);
+                add_authenticated_client (source, client);
+                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;
+}
+
+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;
+    authenticator->adduser = htpasswd_adduser;
+    authenticator->deleteuser = htpasswd_deleteuser;
+    authenticator->listuser = htpasswd_userlist;
+
+    state = calloc(1, sizeof(htpasswd_auth_state));
+
+    while(options) {
+        if(!strcmp(options->name, "filename"))
+            state->filename = strdup(options->value);
+        options = options->next;
+    }
+
+    if(!state->filename) {
+        free(state);
+        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, const 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);
+    return ret;
+
+}
+
+
+static int _auth_htpasswd_adduser(auth_t *auth, const char *username, const 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;
+}
+
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
+{
+    auth_result ret = 0;
+    htpasswd_auth_state *state;
+
+    state = auth->state;
+    thread_rwlock_wlock (&state->file_rwlock);
+    ret = _auth_htpasswd_adduser (auth, username, password);
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return ret;
+}
+
+
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
+{
+    FILE *passwdfile;
+    FILE *tmp_passwdfile;
+    htpasswd_auth_state *state;
+    char line[MAX_LINE_LEN];
+    char *sep;
+    char *tmpfile = NULL;
+    int tmpfile_len = 0;
+
+    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 */
+    /* Windows won't let us rename a file if the destination file
+       exists...so, lets remove the original first */
+    if (remove(state->filename) != 0) {
+        ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
+                tmpfile, state->filename, strerror(errno));
+    }
+    else {
+        if (rename(tmpfile, state->filename) != 0) {
+            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
+                    tmpfile, state->filename, strerror(errno));
+        }
+    }
+    free(tmpfile);
+
+    return AUTH_USERDELETED;
+}
+
+
+static auth_result 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;
+}
+

Added: icecast/branches/icecast-kh/src/auth_htpasswd.h
===================================================================
--- icecast/branches/icecast-kh/src/auth_htpasswd.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_htpasswd.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,27 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_HTPASSWD_H__
+#define __AUTH_HTPASSWD_H__
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_htpasswd_auth(config_options_t *options);
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/auth_url.c
===================================================================
--- icecast/branches/icecast-kh/src/auth_url.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_url.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,321 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org>,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/**
+ * Client authentication via URL functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include <curl/curl.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+#define CATMODULE "auth_url"
+
+typedef struct {
+    char *addurl;
+    char *removeurl;
+    char *username;
+    char *password;
+} auth_url;
+
+typedef struct {
+    char *mount;
+    client_t *client;
+    CURL *auth_server;
+    char *addurl;
+    char *removeurl;
+    int authenticated;
+    char *username;
+    char *password;
+} auth_client;
+
+
+static void auth_url_clear(auth_t *self)
+{
+    auth_url *url = self->state;
+    free(url->username);
+    free(url->password);
+    free(url->removeurl);
+    free(url->addurl);
+    free(url);
+}
+
+
+static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    auth_client *auth_user = stream;
+    unsigned bytes = size * nmemb;
+
+    if (strncmp (ptr, "icecast-auth-user: 1", 20) == 0)
+        auth_user->authenticated = 1;
+
+    return (int)bytes;
+}
+
+/* capture returned data, but don't do anything with it */
+static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    return (int)(size*nmemb);
+}
+
+
+static void *auth_removeurl_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    client_t *client = auth_user->client;
+    CURL *handle;
+    char post[1024];
+
+    if (auth_user->removeurl)
+    {
+        time_t duration = time(NULL) - client->con->con_time;
+        char *username, *password;
+
+        DEBUG0("starting auth thread");
+        username = util_url_escape (client->username);
+        password = util_url_escape (client->password);
+        snprintf (post, 1024,"id=%ld&mount=%s&user=%s&pass=%s&duration=%ld",
+                client->con->id, auth_user->mount, username, password, duration);
+        free (username);
+        free (password);
+        handle = curl_easy_init ();
+        if (handle)
+        {
+            int res;
+            char errormsg [CURL_ERROR_SIZE];
+            char *userpass = NULL;
+
+            if (auth_user->username && auth_user->password)
+            {
+                unsigned len = strlen (auth_user->username) + strlen (auth_user->password) + 1;
+                userpass = malloc (len);
+                snprintf (userpass, len, "%s:%s", auth_user->username, auth_user->password);
+                curl_easy_setopt (handle, CURLOPT_USERPWD, userpass);
+            }
+            curl_easy_setopt (handle, CURLOPT_URL, auth_user->removeurl);
+            curl_easy_setopt (handle, CURLOPT_POSTFIELDS, post);
+            curl_easy_setopt (handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
+            curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
+            curl_easy_setopt (handle, CURLOPT_WRITEHEADER, auth_user);
+            curl_easy_setopt (handle, CURLOPT_WRITEDATA, handle);
+            curl_easy_setopt (handle, CURLOPT_NOSIGNAL, 1L);
+            curl_easy_setopt (handle, CURLOPT_TIMEOUT, 15L);
+            curl_easy_setopt (handle, CURLOPT_ERRORBUFFER, errormsg);
+            res = curl_easy_perform (handle);
+            curl_easy_cleanup (handle);
+            free (userpass);
+
+            if (res)
+                WARN2 ("auth to server %s failed with %s", auth_user->removeurl, errormsg);
+        }
+    }
+    auth_close_client (client);
+
+    free (auth_user->username);
+    free (auth_user->password);
+    free (auth_user->mount);
+    free (auth_user->removeurl);
+    free (auth_user);
+    return NULL;
+}
+
+
+static void *auth_url_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    client_t *client = auth_user->client;
+    int res;
+    char *agent, *user_agent, *username, *password;
+    CURL *handle;
+    char post[1024];
+
+    DEBUG0("starting auth thread");
+    agent = httpp_getvar (client->parser, "user-agent");
+    if (agent == NULL)
+        agent = "-";
+    user_agent = util_url_escape (agent);
+    username  = util_url_escape (client->username);
+    password  = util_url_escape (client->password);
+    snprintf (post, 1024,"id=%ld&mount=%s&user=%s&pass=%s&ip=%s&agent=%s",
+            client->con->id, auth_user->mount, username, password,
+            client->con->ip, user_agent);
+    free (user_agent);
+    free (username);
+    free (password);
+
+    handle = curl_easy_init ();
+    if (handle)
+    {
+        char errormsg [CURL_ERROR_SIZE];
+        char *userpass = NULL;
+
+        if (auth_user->username && auth_user->password)
+        {
+            unsigned len = strlen (auth_user->username) + strlen (auth_user->password) + 1;
+            userpass = malloc (len);
+            snprintf (userpass, len, "%s:%s", auth_user->username, auth_user->password);
+            curl_easy_setopt (handle, CURLOPT_USERPWD, userpass);
+        }
+
+        curl_easy_setopt (handle, CURLOPT_URL, auth_user->addurl);
+        curl_easy_setopt (handle, CURLOPT_POSTFIELDS, post);
+        curl_easy_setopt (handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
+        curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
+        curl_easy_setopt (handle, CURLOPT_WRITEHEADER, auth_user);
+        curl_easy_setopt (handle, CURLOPT_WRITEDATA, handle);
+        curl_easy_setopt (handle, CURLOPT_NOSIGNAL, 1L);
+        curl_easy_setopt (handle, CURLOPT_TIMEOUT, 15L);
+        curl_easy_setopt (handle, CURLOPT_ERRORBUFFER, errormsg);
+        res = curl_easy_perform (handle);
+        curl_easy_cleanup (handle);
+        free (userpass);
+
+        if (res)
+        {
+            WARN2 ("auth to server %s failed with %s", auth_user->addurl, errormsg);
+            auth_close_client (client);
+        }
+        else
+        {
+            /* we received a response, lets see what it is */
+            if (auth_user->authenticated)
+            {
+                if (auth_postprocess_client (auth_user->mount, client) < 0)
+                {
+                    /* do cleanup, and exit as the remove does cleanup as well */
+                    free (auth_user->addurl);
+                    auth_removeurl_thread (auth_user);
+                    return NULL;
+                }
+            }
+            else
+            {
+                DEBUG0 ("client authentication failed");
+                auth_close_client (client);
+            }
+        }
+    }
+    else
+        auth_close_client (client);
+
+    free (auth_user->username);
+    free (auth_user->password);
+    free (auth_user->mount);
+    free (auth_user->addurl);
+    free (auth_user->removeurl);
+    free (auth_user);
+
+    return NULL;
+}
+
+
+static void auth_remove_url_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_url *urlinfo = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return;
+
+    if (urlinfo->username)
+        auth_user->username = strdup (urlinfo->username);
+    if (urlinfo->password)
+        auth_user->password = strdup (urlinfo->password);
+    if (urlinfo->removeurl)
+        auth_user->removeurl = strdup (urlinfo->removeurl);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("AuthRemove by URL thread", auth_removeurl_thread,
+            auth_user, THREAD_DETACHED);
+}
+
+
+static auth_result auth_url_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_url *urlinfo = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return AUTH_FAILED;
+
+    if (urlinfo->username)
+        auth_user->username = strdup (urlinfo->username);
+    if (urlinfo->password)
+        auth_user->password = strdup (urlinfo->password);
+    auth_user->addurl = strdup (urlinfo->addurl);
+    auth_user->removeurl = strdup (urlinfo->removeurl);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("Auth by URL thread", auth_url_thread, auth_user, THREAD_DETACHED);
+    return AUTH_OK;
+}
+
+static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_deleteuser (auth_t *auth, const char *username)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
+{
+    return AUTH_FAILED;
+}
+
+auth_t *auth_get_url_auth (config_options_t *options)
+{
+    auth_t *authenticator = calloc(1, sizeof(auth_t));
+    auth_url *state;
+
+    authenticator->authenticate = auth_url_client;
+    authenticator->free = auth_url_clear;
+    authenticator->adduser = auth_url_adduser;
+    authenticator->deleteuser = auth_url_deleteuser;
+    authenticator->listuser = auth_url_listuser;
+    authenticator->release_client = auth_remove_url_client;
+
+    state = calloc(1, sizeof(auth_url));
+
+    while(options) {
+        if(!strcmp(options->name, "username"))
+            state->username = strdup(options->value);
+        if(!strcmp(options->name, "password"))
+            state->password = strdup(options->value);
+        if(!strcmp(options->name, "add"))
+            state->addurl = strdup(options->value);
+        if(!strcmp(options->name, "remove"))
+            state->removeurl = strdup(options->value);
+        options = options->next;
+    }
+    authenticator->state = state;
+    INFO0("URL based authentication setup");
+    return authenticator;
+}
+

Added: icecast/branches/icecast-kh/src/auth_url.h
===================================================================
--- icecast/branches/icecast-kh/src/auth_url.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/auth_url.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,28 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_URL_H__
+#define __AUTH_URL_H__
+#ifdef HAVE_AUTH_URL
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_url_auth (config_options_t *options);
+#endif
+#endif
+
+

Added: icecast/branches/icecast-kh/src/cfgfile.c
===================================================================
--- icecast/branches/icecast-kh/src/cfgfile.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/cfgfile.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,958 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+#include "thread/thread.h"
+#include "cfgfile.h"
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h"
+
+#define CATMODULE "CONFIG"
+#define CONFIG_DEFAULT_LOCATION "Earth"
+#define CONFIG_DEFAULT_ADMIN "icemaster at localhost"
+#define CONFIG_DEFAULT_CLIENT_LIMIT 256
+#define CONFIG_DEFAULT_SOURCE_LIMIT 16
+#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (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
+#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
+#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
+#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
+#define CONFIG_DEFAULT_ICE_LOGIN 0
+#define CONFIG_DEFAULT_FILESERVE 1
+#define CONFIG_DEFAULT_TOUCH_FREQ 5
+#define CONFIG_DEFAULT_HOSTNAME "localhost"
+#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
+#define CONFIG_DEFAULT_ERROR_LOG "error.log"
+#define CONFIG_DEFAULT_LOG_LEVEL 4
+#define CONFIG_DEFAULT_CHROOT 0
+#define CONFIG_DEFAULT_CHUID 0
+#define CONFIG_DEFAULT_USER NULL
+#define CONFIG_DEFAULT_GROUP NULL
+#define CONFIG_MASTER_UPDATE_INTERVAL 120
+#define CONFIG_YP_URL_TIMEOUT 10
+
+#ifndef _WIN32
+#define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast"
+#define CONFIG_DEFAULT_LOG_DIR "/usr/local/icecast/logs"
+#define CONFIG_DEFAULT_WEBROOT_DIR "/usr/local/icecast/webroot"
+#define CONFIG_DEFAULT_ADMINROOT_DIR "/usr/local/icecast/admin"
+#else
+#define CONFIG_DEFAULT_BASE_DIR ".\\"
+#define CONFIG_DEFAULT_LOG_DIR ".\\logs"
+#define CONFIG_DEFAULT_WEBROOT_DIR ".\\webroot"
+#define CONFIG_DEFAULT_ADMINROOT_DIR ".\\admin"
+#endif
+
+static ice_config_t _current_configuration;
+static ice_config_locks _locks;
+
+static void _set_defaults(ice_config_t *c);
+static void _parse_root(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_directory(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_logging(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_security(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *c);
+static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *c);
+static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
+
+static void create_locks() {
+    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() {
+    thread_mutex_destroy(&_locks.relay_lock);
+    thread_mutex_destroy(&_locks.mounts_lock);
+    thread_mutex_destroy(&_locks.config_lock);
+}
+
+void config_initialize(void) {
+    create_locks();
+}
+
+void config_shutdown(void) {
+    config_get_config();
+    config_clear(&_current_configuration);
+    config_release_config();
+    release_locks();
+}
+
+void config_init_configuration(ice_config_t *configuration)
+{
+    memset(configuration, 0, sizeof(ice_config_t));
+    _set_defaults(configuration);
+}
+
+void config_clear(ice_config_t *c)
+{
+    ice_config_dir_t *dirnode, *nextdirnode;
+    relay_server *relay, *nextrelay;
+    mount_proxy *mount, *nextmount;
+    aliases *alias, *nextalias;
+    int i;
+    config_options_t *option;
+
+    if (c->config_filename)
+        free(c->config_filename);
+
+    if (c->location && c->location != CONFIG_DEFAULT_LOCATION)
+        xmlFree(c->location);
+    if (c->admin && c->admin != CONFIG_DEFAULT_ADMIN)
+        xmlFree(c->admin);
+    if (c->source_password && c->source_password != CONFIG_DEFAULT_SOURCE_PASSWORD)
+        xmlFree(c->source_password);
+    if (c->admin_username)
+        xmlFree(c->admin_username);
+    if (c->admin_password)
+        xmlFree(c->admin_password);
+    if (c->relay_username)
+        xmlFree(c->relay_username);
+    if (c->relay_password)
+        xmlFree(c->relay_password);
+    if (c->hostname && c->hostname != CONFIG_DEFAULT_HOSTNAME)
+        xmlFree(c->hostname);
+    if (c->base_dir && c->base_dir != CONFIG_DEFAULT_BASE_DIR)
+        xmlFree(c->base_dir);
+    if (c->log_dir && c->log_dir != CONFIG_DEFAULT_LOG_DIR)
+        xmlFree(c->log_dir);
+    if (c->webroot_dir && c->webroot_dir != CONFIG_DEFAULT_WEBROOT_DIR)
+        xmlFree(c->webroot_dir);
+    if (c->adminroot_dir && c->adminroot_dir != CONFIG_DEFAULT_ADMINROOT_DIR)
+        xmlFree(c->adminroot_dir);
+    if (c->pidfile)
+        xmlFree(c->pidfile);
+    if (c->access_log && c->access_log != CONFIG_DEFAULT_ACCESS_LOG)
+        xmlFree(c->access_log);
+    if (c->error_log && c->error_log != CONFIG_DEFAULT_ERROR_LOG)
+        xmlFree(c->error_log);
+    for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
+        if (c->listeners[i].bind_address) xmlFree(c->listeners[i].bind_address);
+    }
+    if (c->master_server) xmlFree(c->master_server);
+    if (c->master_username) xmlFree(c->master_username);
+    if (c->master_password) xmlFree(c->master_password);
+    if (c->user) xmlFree(c->user);
+    if (c->group) xmlFree(c->group);
+
+    thread_mutex_lock(&(_locks.relay_lock));
+    relay = c->relay;
+    while(relay) {
+        nextrelay = relay->next;
+        xmlFree(relay->server);
+        xmlFree(relay->mount);
+        xmlFree(relay->localmount);
+        free(relay);
+        relay = nextrelay;
+    }
+    thread_mutex_unlock(&(_locks.relay_lock));
+
+    thread_mutex_lock(&(_locks.mounts_lock));
+    mount = c->mounts;
+    while(mount) {
+        nextmount = mount->next;
+        xmlFree(mount->mountname);
+        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);
+        option = mount->auth_options;
+        while(option) {
+            config_options_t *nextopt = option->next;
+            xmlFree(option->name);
+            xmlFree(option->value);
+            free(option);
+            option = nextopt;
+        }
+
+        free(mount);
+        mount = nextmount;
+    }
+    thread_mutex_unlock(&(_locks.mounts_lock));
+
+    alias = c->aliases;
+    while(alias) {
+        nextalias = alias->next;
+        xmlFree(alias->source);
+        xmlFree(alias->destination);
+        xmlFree(alias->bind_address);
+        free(alias);
+        alias = nextalias;
+    }
+
+    dirnode = c->dir_list;
+    while(dirnode) {
+        nextdirnode = dirnode->next;
+        xmlFree(dirnode->host);
+        free(dirnode);
+        dirnode = nextdirnode;
+    }
+#ifdef HAVE_YP
+    i = 0;
+    while (i < c->num_yp_directories)
+    {
+        xmlFree (c->yp_url[i]);
+        i++;
+    }
+#endif
+
+    memset(c, 0, sizeof(ice_config_t));
+}
+
+int config_initial_parse_file(const char *filename)
+{
+    /* Since we're already pointing at it, we don't need to copy it in place */
+    return config_parse_file(filename, &_current_configuration);
+}
+
+int config_parse_file(const char *filename, ice_config_t *configuration)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node;
+
+    if (filename == NULL || strcmp(filename, "") == 0) return CONFIG_EINSANE;
+
+    xmlInitParser();
+    doc = xmlParseFile(filename);
+    if (doc == NULL) {
+        return CONFIG_EPARSE;
+    }
+
+    node = xmlDocGetRootElement(doc);
+    if (node == NULL) {
+        xmlFreeDoc(doc);
+        xmlCleanupParser();
+        return CONFIG_ENOROOT;
+    }
+
+    if (strcmp(node->name, "icecast") != 0) {
+        xmlFreeDoc(doc);
+        xmlCleanupParser();
+        return CONFIG_EBADROOT;
+    }
+
+    config_init_configuration(configuration);
+
+    configuration->config_filename = (char *)strdup(filename);
+
+    _parse_root(doc, node->xmlChildrenNode, configuration);
+
+    xmlFreeDoc(doc);
+
+    return 0;
+}
+
+int config_parse_cmdline(int arg, char **argv)
+{
+    return 0;
+}
+
+ice_config_locks *config_locks(void)
+{
+    return &_locks;
+}
+
+void config_release_config(void)
+{
+    thread_mutex_unlock(&(_locks.config_lock));
+}
+
+ice_config_t *config_get_config(void)
+{
+    thread_mutex_lock(&(_locks.config_lock));
+    return &_current_configuration;
+}
+
+/* MUST be called with the lock held! */
+void config_set_config(ice_config_t *config) {
+    memcpy(&_current_configuration, config, sizeof(ice_config_t));
+}
+
+ice_config_t *config_get_config_unlocked(void)
+{
+    return &_current_configuration;
+}
+
+static void _set_defaults(ice_config_t *configuration)
+{
+    configuration->location = CONFIG_DEFAULT_LOCATION;
+    configuration->admin = CONFIG_DEFAULT_ADMIN;
+    configuration->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
+    configuration->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
+    configuration->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
+    configuration->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;
+    configuration->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
+    configuration->source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
+    configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
+    configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
+    configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
+    configuration->dir_list = NULL;
+    configuration->hostname = CONFIG_DEFAULT_HOSTNAME;
+    configuration->port = 0;
+    configuration->listeners[0].port = 0;
+    configuration->listeners[0].bind_address = NULL;
+    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;
+    configuration->webroot_dir = CONFIG_DEFAULT_WEBROOT_DIR;
+    configuration->adminroot_dir = CONFIG_DEFAULT_ADMINROOT_DIR;
+    configuration->access_log = CONFIG_DEFAULT_ACCESS_LOG;
+    configuration->error_log = CONFIG_DEFAULT_ERROR_LOG;
+    configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
+    configuration->chroot = CONFIG_DEFAULT_CHROOT;
+    configuration->chuid = CONFIG_DEFAULT_CHUID;
+    configuration->user = CONFIG_DEFAULT_USER;
+    configuration->group = CONFIG_DEFAULT_GROUP;
+    configuration->num_yp_directories = 0;
+    configuration->relay_username = NULL;
+    configuration->relay_password = NULL;
+}
+
+static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "location") == 0) {
+            if (configuration->location && configuration->location != CONFIG_DEFAULT_LOCATION) xmlFree(configuration->location);
+            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "admin") == 0) {
+            if (configuration->admin && configuration->admin != CONFIG_DEFAULT_ADMIN) xmlFree(configuration->admin);
+            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if(strcmp(node->name, "authentication") == 0) {
+            _parse_authentication(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "source-password") == 0) {
+            /* TODO: This is the backwards-compatibility location */
+            char *mount, *pass;
+            if ((mount = (char *)xmlGetProp(node, "mount")) != NULL) {
+                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+                /* FIXME: This is a placeholder for per-mount passwords */
+            }
+            else {
+                if (configuration->source_password && configuration->source_password != CONFIG_DEFAULT_SOURCE_PASSWORD) xmlFree(configuration->source_password);
+                configuration->source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            }
+        } else if (strcmp(node->name, "icelogin") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->ice_login = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "fileserve") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->fileserve = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "hostname") == 0) {
+            if (configuration->hostname && configuration->hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(configuration->hostname);
+            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "listen-socket") == 0) {
+            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "port") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->port = atoi(tmp);
+            configuration->listeners[0].port = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "bind-address") == 0) {
+            if (configuration->listeners[0].bind_address)
+                xmlFree(configuration->listeners[0].bind_address);
+            configuration->listeners[0].bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "master-server") == 0) {
+            if (configuration->master_server) xmlFree(configuration->master_server);
+            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "master-username") == 0) {
+            if (configuration->master_username) xmlFree(configuration->master_username);
+            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "master-password") == 0) {
+            if (configuration->master_password) xmlFree(configuration->master_password);
+            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "master-server-port") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->master_server_port = atoi(tmp);
+            xmlFree (tmp);
+        } else if (strcmp(node->name, "master-update-interval") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->master_update_interval = atoi(tmp);
+            xmlFree (tmp);
+        } else if (strcmp(node->name, "limits") == 0) {
+            _parse_limits(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "relay") == 0) {
+            _parse_relay(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "mount") == 0) {
+            _parse_mount(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "directory") == 0) {
+            _parse_directory(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "paths") == 0) {
+            _parse_paths(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "logging") == 0) {
+            _parse_logging(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "security") == 0) {
+            _parse_security(doc, node->xmlChildrenNode, configuration);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_limits(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "clients") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->client_limit = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "sources") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->source_limit = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "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);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "threadpool") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->threadpool_size = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "client-timeout") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->client_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "header-timeout") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->header_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "source-timeout") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->source_timeout = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+    mount_proxy *mount = calloc(1, sizeof(mount_proxy));
+    mount_proxy *current = configuration->mounts;
+    mount_proxy *last=NULL;
+    xmlNodePtr option;
+    config_options_t *last_option;
+
+    while(current) {
+        last = current;
+        current = current->next;
+    }
+
+    if(last)
+        last->next = mount;
+    else
+        configuration->mounts = mount;
+
+    mount->max_listeners = -1;
+    mount->next = NULL;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "mount-name") == 0) {
+            mount->mountname = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "username") == 0) {
+            mount->username = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "password") == 0) {
+            mount->password = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "dump-file") == 0) {
+            mount->dumpfile = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "fallback-mount") == 0) {
+            mount->fallback_mount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "fallback-when-full") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->fallback_when_full = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "max-listeners") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->max_listeners = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "fallback-override") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->fallback_override = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "no-mount") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->no_mount = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "no-yp") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->no_yp = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "authentication") == 0) {
+            mount->auth_type = xmlGetProp(node, "type");
+            option = node->xmlChildrenNode;
+            last_option = NULL;
+            while(option != NULL) {
+                if(strcmp(option->name, "option") == 0) {
+                    config_options_t *opt = malloc(sizeof(config_options_t));
+                    opt->name = xmlGetProp(option, "name");
+                    if(!opt->name) {
+                        free(opt);
+                        option = option->next;
+                        continue;
+                    }
+                    opt->value = xmlGetProp(option, "value");
+                    if(!opt->value) {
+                        free(opt->name);
+                        free(opt);
+                        option = option->next;
+                        continue;
+                    }
+                    opt->next = NULL;
+
+                    if(last_option)
+                        last_option->next = opt;
+                    else
+                        mount->auth_options = opt;
+                    last_option = opt;
+                }
+                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)
+            {
+                mount->source_timeout = atoi (tmp);
+                xmlFree(tmp);
+            }
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_relay(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+    relay_server *relay = calloc(1, sizeof(relay_server));
+    relay_server *current = configuration->relay;
+    relay_server *last=NULL;
+
+    while(current) {
+        last = current;
+        current = current->next;
+    }
+
+    if(last)
+        last->next = relay;
+    else
+        configuration->relay = relay;
+
+    relay->next = NULL;
+    relay->mp3metadata = 1;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "server") == 0) {
+            relay->server = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "port") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->port = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "mount") == 0) {
+            relay->mount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "local-mount") == 0) {
+            relay->localmount = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "relay-shoutcast-metadata") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->mp3metadata = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "username") == 0) {
+            relay->username = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "password") == 0) {
+            relay->password = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+        else if (strcmp(node->name, "on-demand") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            relay->on_demand = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+    if (relay->localmount == NULL)
+        relay->localmount = xmlStrdup (relay->mount);
+}
+
+static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    listener_t *listener = NULL;
+    int i;
+    char *tmp;
+
+    for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
+        if(configuration->listeners[i].port <= 0) {
+            listener = &(configuration->listeners[i]);
+            break;
+        }
+    }
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "port") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->port == 0)
+                configuration->port = atoi(tmp);
+            listener->port = atoi(tmp);
+            if(tmp) xmlFree(tmp);
+        }
+        else if (strcmp(node->name, "bind-address") == 0) {
+            listener->bind_address = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "source-password") == 0) {
+            char *mount, *pass;
+            if ((mount = (char *)xmlGetProp(node, "mount")) != NULL) {
+                pass = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+                /* FIXME: This is a placeholder for per-mount passwords */
+            }
+            else {
+                if (configuration->source_password &&
+                        configuration->source_password !=
+                        CONFIG_DEFAULT_SOURCE_PASSWORD)
+                    xmlFree(configuration->source_password);
+                configuration->source_password =
+                    (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            }
+        } else if (strcmp(node->name, "admin-password") == 0) {
+            if(configuration->admin_password)
+                xmlFree(configuration->admin_password);
+            configuration->admin_password =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "admin-user") == 0) {
+            if(configuration->admin_username)
+                xmlFree(configuration->admin_username);
+            configuration->admin_username =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "relay-password") == 0) {
+            if(configuration->relay_password)
+                xmlFree(configuration->relay_password);
+            configuration->relay_password =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "relay-user") == 0) {
+            if(configuration->relay_username)
+                xmlFree(configuration->relay_username);
+            configuration->relay_username =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_directory(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *tmp;
+
+    if (configuration->num_yp_directories >= MAX_YP_DIRECTORIES) {
+        ERROR0("Maximum number of yp directories exceeded!");
+        return;
+    }
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "yp-url") == 0) {
+            if (configuration->yp_url[configuration->num_yp_directories])
+                xmlFree(configuration->yp_url[configuration->num_yp_directories]);
+            configuration->yp_url[configuration->num_yp_directories] =
+                (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "yp-url-timeout") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->yp_url_timeout[configuration->num_yp_directories] =
+                atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "server") == 0) {
+            _add_server(doc, node->xmlChildrenNode, configuration);
+        } else if (strcmp(node->name, "touch-interval") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            configuration->yp_touch_interval[configuration->num_yp_directories]
+                = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+    configuration->num_yp_directories++;
+}
+
+static void _parse_paths(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    char *temp;
+    aliases *alias, *current, *last;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "basedir") == 0) {
+            if (configuration->base_dir && configuration->base_dir != CONFIG_DEFAULT_BASE_DIR) xmlFree(configuration->base_dir);
+            configuration->base_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "logdir") == 0) {
+            if (configuration->log_dir && configuration->log_dir != CONFIG_DEFAULT_LOG_DIR) xmlFree(configuration->log_dir);
+            configuration->log_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "pidfile") == 0) {
+            if (configuration->pidfile) xmlFree(configuration->pidfile);
+            configuration->pidfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "webroot") == 0) {
+            if (configuration->webroot_dir && configuration->webroot_dir != CONFIG_DEFAULT_WEBROOT_DIR) xmlFree(configuration->webroot_dir);
+            configuration->webroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->webroot_dir[strlen(configuration->webroot_dir)-1] == '/')
+                configuration->webroot_dir[strlen(configuration->webroot_dir)-1] = 0;
+        } else if (strcmp(node->name, "adminroot") == 0) {
+            if (configuration->adminroot_dir && configuration->adminroot_dir != CONFIG_DEFAULT_ADMINROOT_DIR)
+                xmlFree(configuration->adminroot_dir);
+            configuration->adminroot_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            if(configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] == '/')
+                configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] = 0;
+        } else if (strcmp(node->name, "alias") == 0) {
+            alias = malloc(sizeof(aliases));
+            alias->next = NULL;
+            alias->source = xmlGetProp(node, "source");
+            if(alias->source == NULL) {
+                free(alias);
+                continue;
+            }
+            alias->destination = xmlGetProp(node, "dest");
+            if(alias->destination == NULL) {
+                xmlFree(alias->source);
+                free(alias);
+                continue;
+            }
+            temp = NULL;
+            temp = xmlGetProp(node, "port");
+            if(temp != NULL) {
+                alias->port = atoi(temp);
+                xmlFree(temp);
+            }
+            else
+                alias->port = -1;
+            alias->bind_address = xmlGetProp(node, "bind-address");
+            current = configuration->aliases;
+            last = NULL;
+            while(current) {
+                last = current;
+                current = current->next;
+            }
+            if(last)
+                last->next = alias;
+            else
+                configuration->aliases = alias;
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_logging(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "accesslog") == 0) {
+            if (configuration->access_log && configuration->access_log != CONFIG_DEFAULT_ACCESS_LOG) xmlFree(configuration->access_log);
+            configuration->access_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "errorlog") == 0) {
+            if (configuration->error_log && configuration->error_log != CONFIG_DEFAULT_ERROR_LOG) xmlFree(configuration->error_log);
+            configuration->error_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+        } else if (strcmp(node->name, "loglevel") == 0) {
+           char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+           configuration->loglevel = atoi(tmp);
+           if (tmp) xmlFree(tmp);
+        }
+    } while ((node = node->next));
+}
+
+static void _parse_security(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+   char *tmp;
+   xmlNodePtr oldnode;
+
+   do {
+       if (node == NULL) break;
+       if (xmlIsBlankNode(node)) continue;
+
+       if (strcmp(node->name, "chroot") == 0) {
+           tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+           configuration->chroot = atoi(tmp);
+           if (tmp) xmlFree(tmp);
+       } else if (strcmp(node->name, "changeowner") == 0) {
+           configuration->chuid = 1;
+           oldnode = node;
+           node = node->xmlChildrenNode;
+           do {
+               if(node == NULL) break;
+               if(xmlIsBlankNode(node)) continue;
+               if(strcmp(node->name, "user") == 0) {
+                   if(configuration->user) xmlFree(configuration->user);
+                   configuration->user = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+               } else if(strcmp(node->name, "group") == 0) {
+                   if(configuration->group) xmlFree(configuration->group);
+                   configuration->group = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+               }
+           } while((node = node->next));
+           node = oldnode;
+       }
+   } while ((node = node->next));
+}
+
+static void _add_server(xmlDocPtr doc, xmlNodePtr node,
+        ice_config_t *configuration)
+{
+    ice_config_dir_t *dirnode, *server;
+    int addnode;
+    char *tmp;
+
+    server = (ice_config_dir_t *)malloc(sizeof(ice_config_dir_t));
+    server->touch_interval = configuration->touch_interval;
+    server->host = NULL;
+    addnode = 0;
+
+    do {
+        if (node == NULL) break;
+        if (xmlIsBlankNode(node)) continue;
+
+        if (strcmp(node->name, "host") == 0) {
+            server->host = (char *)xmlNodeListGetString(doc,
+                    node->xmlChildrenNode, 1);
+            addnode = 1;
+        } else if (strcmp(node->name, "touch-interval") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            server->touch_interval = atoi(tmp);
+            if (tmp) xmlFree(tmp);
+        }
+        server->next = NULL;
+    } while ((node = node->next));
+
+    if (addnode) {
+        dirnode = configuration->dir_list;
+        if (dirnode == NULL) {
+            configuration->dir_list = server;
+        } else {
+            while (dirnode->next) dirnode = dirnode->next;
+
+            dirnode->next = server;
+        }
+
+        server = NULL;
+        addnode = 0;
+    }
+
+}
+
+

Added: icecast/branches/icecast-kh/src/cfgfile.h
===================================================================
--- icecast/branches/icecast-kh/src/cfgfile.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/cfgfile.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,176 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __CFGFILE_H__
+#define __CFGFILE_H__
+
+#define CONFIG_EINSANE -1
+#define CONFIG_ENOROOT -2
+#define CONFIG_EBADROOT -3
+#define CONFIG_EPARSE -4
+
+#define MAX_YP_DIRECTORIES 25
+
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "global.h"
+
+typedef struct ice_config_dir_tag
+{
+    char *host;
+    int touch_interval;
+    struct ice_config_dir_tag *next;
+} ice_config_dir_t;
+
+typedef struct _config_options {
+    char *name;
+    char *value;
+    struct _config_options *next;
+} config_options_t;
+
+typedef struct _mount_proxy {
+    char *mountname; /* The mountpoint this proxy is used for */
+
+    char *username; /* Username and password for this mountpoint. If unset, */
+    char *password; /* falls back to global source password */
+
+    char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
+                       to not dump. */
+    int fallback_when_full; /* switch new listener to fallback source
+                               when max listeners reached */
+    int max_listeners; /* Max listeners for this mountpoint only. -1 to not
+                          limit here (i.e. only use the global limit) */
+    char *fallback_mount; /* Fallback mountname */
+
+    int fallback_override; /* When this source arrives, do we steal back
+                              clients from the fallback? */
+    int no_mount; /* Do we permit direct requests of this mountpoint? (or only
+                     indirect, through fallbacks) */
+    int 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;
+
+typedef struct _aliases {
+    char *source;
+    char *destination;
+    int port;
+    char *bind_address;
+    struct _aliases *next;
+}aliases;
+
+typedef struct {
+    int port;
+    char *bind_address;
+} listener_t;
+
+typedef struct ice_config_tag
+{
+    char *config_filename;
+
+    char *location;
+    char *admin;
+
+    int client_limit;
+    int source_limit;
+    unsigned queue_size_limit;
+    unsigned burst_size_limit;
+    int threadpool_size;
+    int client_timeout;
+    int header_timeout;
+    int source_timeout;
+    int ice_login;
+    int fileserve;
+
+    char *source_password;
+    char *admin_username;
+    char *admin_password;
+    char *relay_username;
+    char *relay_password;
+
+    int touch_interval;
+    ice_config_dir_t *dir_list;
+
+    char *hostname;
+    int port;
+
+    listener_t listeners[MAX_LISTEN_SOCKETS];
+
+    char *master_server;
+    int master_server_port;
+    int master_update_interval;
+    char *master_username;
+    char *master_password;
+
+    relay_server *relay;
+
+    mount_proxy *mounts;
+
+    char *base_dir;
+    char *log_dir;
+    char *pidfile;
+    char *webroot_dir;
+    char *adminroot_dir;
+    aliases *aliases;
+
+    char *access_log;
+    char *error_log;
+    int loglevel;
+
+    int chroot;
+    int chuid;
+    char *user;
+    char *group;
+    char *yp_url[MAX_YP_DIRECTORIES];
+    int    yp_url_timeout[MAX_YP_DIRECTORIES];
+    int    yp_touch_interval[MAX_YP_DIRECTORIES];
+    int num_yp_directories;
+} ice_config_t;
+
+typedef struct {
+    mutex_t config_lock;
+    mutex_t relay_lock;
+    mutex_t mounts_lock;
+} ice_config_locks;
+
+void config_initialize(void);
+void config_shutdown(void);
+
+int config_parse_file(const char *filename, ice_config_t *configuration);
+int config_initial_parse_file(const char *filename);
+int config_parse_cmdline(int arg, char **argv);
+void config_set_config(ice_config_t *config);
+void config_clear(ice_config_t *config);
+
+int config_rehash(void);
+
+ice_config_locks *config_locks(void);
+
+ice_config_t *config_get_config(void);
+void config_release_config(void);
+
+/* To be used ONLY in one-time startup code */
+ice_config_t *config_get_config_unlocked(void);
+
+#endif  /* __CFGFILE_H__ */
+
+
+

Added: icecast/branches/icecast-kh/src/client.c
===================================================================
--- icecast/branches/icecast-kh/src/client.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/client.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,171 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* client.c
+**
+** client interface implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+
+#include "connection.h"
+#include "refbuf.h"
+
+#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->pos = 0;
+
+    return client;
+}
+
+void client_destroy(client_t *client)
+{
+    if (client == NULL)
+        return;
+    /* write log entry if ip is set (some things don't set it, like outgoing
+     * slave requests
+     */
+    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);
+
+    if (client->free_client_data)
+        client->free_client_data (client);
+
+    free(client->username);
+    free(client->password);
+
+    free(client);
+}
+
+void client_send_400(client_t *client, char *message) {
+    int bytes;
+    bytes = sock_write(client->con->sock, "HTTP/1.0 400 Bad Request\r\n"
+            "Content-Type: text/html\r\n\r\n"
+            "<b>%s</b>\r\n", message);
+    if(bytes > 0) client->con->sent_bytes = bytes;
+    client->respcode = 400;
+    client_destroy(client);
+}
+
+void client_send_404(client_t *client, char *message) {
+
+    int bytes;
+    bytes = sock_write(client->con->sock, "HTTP/1.0 404 File Not Found\r\n"
+            "Content-Type: text/html\r\n\r\n"
+            "<b>%s</b>\r\n", message);
+    if(bytes > 0) client->con->sent_bytes = bytes;
+    client->respcode = 404;
+    client_destroy(client);
+}
+
+void client_send_504(client_t *client, char *message) {
+    int bytes;
+    client->respcode = 504;
+    bytes = sock_write(client->con->sock,
+            "HTTP/1.0 504 Server Full\r\n"
+            "Content-Type: text/html\r\n\r\n"
+            "<b>%s</b>\r\n", message);
+       if (bytes > 0) client->con->sent_bytes = bytes;
+    client_destroy(client);
+}
+
+void client_send_401(client_t *client) {
+    int bytes = sock_write(client->con->sock,
+            "HTTP/1.0 401 Authentication Required\r\n"
+            "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
+            "\r\n"
+            "You need to authenticate\r\n");
+    if(bytes > 0) client->con->sent_bytes = bytes;
+    client->respcode = 401;
+    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;
+}
+

Added: icecast/branches/icecast-kh/src/client.h
===================================================================
--- icecast/branches/icecast-kh/src/client.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/client.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,79 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* client.h
+**
+** client data structions and function definitions
+**
+*/
+#ifndef __CLIENT_H__
+#define __CLIENT_H__
+
+#include <aio.h>
+
+#include "connection.h"
+#include "refbuf.h"
+
+typedef struct _client_tag
+{
+    /* the client's connection */
+    connection_t *con;
+    /* the client's http headers */
+    http_parser_t *parser;
+
+    /* http response code for this client */
+    int respcode;
+
+    /* auth completed, 0 not yet, 1 passed, 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);
+
+    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);
+void client_destroy(client_t *client);
+void client_send_504(client_t *client, char *message);
+void client_send_404(client_t *client, char *message);
+void client_send_401(client_t *client);
+void client_send_400(client_t *client, char *message);
+int client_send_bytes (client_t *client, const void *buf, unsigned len);
+
+#endif  /* __CLIENT_H__ */

Added: icecast/branches/icecast-kh/src/compat.h
===================================================================
--- icecast/branches/icecast-kh/src/compat.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/compat.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,31 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* compat.h
+ *
+ * This file contains most of the ugliness for header portability
+ * and common types across various systems like Win32, Linux and
+ * Solaris.
+ */
+
+/* Make sure we define 64 bit types */
+#ifdef _WIN32
+#  define int64_t __int64
+#  define uint64_t unsigned __int64
+#  define uint32_t unsigned int
+#else
+#  if defined(HAVE_STDINT_H)
+#    include <stdint.h>
+#  elif defined(HAVE_INTTYPES_H)
+#    include <inttypes.h>
+#  endif
+#endif

Added: icecast/branches/icecast-kh/src/connection.c
===================================================================
--- icecast/branches/icecast-kh/src/connection.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/connection.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,1020 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#else
+#include <winsock2.h>
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#include "os.h"
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "net/sock.h"
+#include "httpp/httpp.h"
+
+#include "cfgfile.h"
+#include "global.h"
+#include "util.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "xslt.h"
+#include "fserve.h"
+#include "sighandler.h"
+
+#include "yp.h"
+#include "source.h"
+#include "format.h"
+#include "format_mp3.h"
+#include "event.h"
+#include "admin.h"
+
+#define CATMODULE "connection"
+
+typedef struct con_queue_tag {
+    connection_t *con;
+    struct con_queue_tag *next;
+} con_queue_t;
+
+typedef struct _thread_queue_tag {
+    thread_type *thread_id;
+    struct _thread_queue_tag *next;
+} thread_queue_t;
+
+static mutex_t _connection_mutex;
+static unsigned long _current_id = 0;
+static int _initialized = 0;
+
+static con_queue_t *_queue = NULL;
+static mutex_t _queue_mutex;
+
+static thread_queue_t *_conhands = NULL;
+
+rwlock_t _source_shutdown_rwlock;
+
+static void *_handle_connection(void *arg);
+
+void connection_initialize(void)
+{
+    if (_initialized) return;
+
+    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(&global.shutdown_cond);
+
+    _initialized = 1;
+}
+
+void connection_shutdown(void)
+{
+    if (!_initialized) return;
+
+    thread_cond_destroy(&global.shutdown_cond);
+    thread_rwlock_destroy(&_source_shutdown_rwlock);
+    thread_mutex_destroy(&_queue_mutex);
+    thread_mutex_destroy(&_connection_mutex);
+    thread_mutex_destroy(&move_clients_mutex);
+
+    _initialized = 0;
+}
+
+static unsigned long _next_connection_id(void)
+{
+    unsigned long id;
+
+    thread_mutex_lock(&_connection_mutex);
+    id = _current_id++;
+    thread_mutex_unlock(&_connection_mutex);
+
+    return id;
+}
+
+connection_t *create_connection(sock_t sock, sock_t serversock, char *ip) {
+    connection_t *con;
+    con = (connection_t *)malloc(sizeof(connection_t));
+    memset(con, 0, sizeof(connection_t));
+    con->sock = sock;
+    con->serversock = serversock;
+    con->con_time = time(NULL);
+    con->id = _next_connection_id();
+    con->ip = ip;
+
+    con->event_number = EVENT_NO_EVENT;
+    con->event = NULL;
+
+    return con;
+}
+
+static int wait_for_serversock(int timeout)
+{
+#ifdef HAVE_POLL
+    struct pollfd ufds[MAX_LISTEN_SOCKETS];
+    int i, ret;
+
+    for(i=0; i < global.server_sockets; i++) {
+        ufds[i].fd = global.serversock[i];
+        ufds[i].events = POLLIN;
+        ufds[i].revents = 0;
+    }
+
+    ret = poll(ufds, global.server_sockets, timeout);
+    if(ret < 0) {
+        return -2;
+    }
+    else if(ret == 0) {
+        return -1;
+    }
+    else {
+        int dst;
+        for(i=0; i < global.server_sockets; i++) {
+            if(ufds[i].revents & POLLIN)
+                return ufds[i].fd;
+            if(ufds[i].revents & (POLLHUP|POLLERR|POLLNVAL))
+            {
+                if (ufds[i].revents & (POLLHUP|POLLERR))
+                {
+                    close (global.serversock[i]);
+                    WARN0("Had to close a listening socket");
+                }
+                global.serversock[i] = -1;
+            }
+        }
+        /* remove any closed sockets */
+        for(i=0, dst=0; i < global.server_sockets; i++)
+        {
+            if (global.serversock[i] == -1)
+                continue;
+            if (i!=dst)
+                global.serversock[dst] = global.serversock[i];
+            dst++;
+        }
+        global.server_sockets = dst;
+        return -1;
+    }
+#else
+    fd_set rfds;
+    struct timeval tv, *p=NULL;
+    int i, ret;
+    int max = -1;
+
+    FD_ZERO(&rfds);
+
+    for(i=0; i < global.server_sockets; i++) {
+        FD_SET(global.serversock[i], &rfds);
+        if(global.serversock[i] > max)
+            max = global.serversock[i];
+    }
+
+    if(timeout >= 0) {
+        tv.tv_sec = timeout/1000;
+        tv.tv_usec = (timeout % 1000) * 1000;
+        p = &tv;
+    }
+
+    ret = select(max+1, &rfds, NULL, NULL, p);
+    if(ret < 0) {
+        return -2;
+    }
+    else if(ret == 0) {
+        return -1;
+    }
+    else {
+        for(i=0; i < global.server_sockets; i++) {
+            if(FD_ISSET(global.serversock[i], &rfds))
+                return global.serversock[i];
+        }
+        return -1; /* Should be impossible, stop compiler warnings */
+    }
+#endif
+}
+
+static connection_t *_accept_connection(void)
+{
+    int sock;
+    connection_t *con;
+    char *ip;
+    int serversock;
+
+    serversock = wait_for_serversock(100);
+    if(serversock < 0)
+        return NULL;
+
+    /* malloc enough room for a full IP address (including ipv6) */
+    ip = (char *)malloc(MAX_ADDR_LEN);
+
+    sock = sock_accept(serversock, ip, MAX_ADDR_LEN);
+    if (sock >= 0) {
+        con = create_connection(sock, serversock, ip);
+
+        return con;
+    }
+
+    if (!sock_recoverable(sock_error()))
+    {
+        WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
+        abort();
+        /* global.running = ICE_HALTING; */
+    }
+
+    free(ip);
+
+    return NULL;
+}
+
+static void _add_connection(connection_t *con)
+{
+    con_queue_t *node;
+
+    node = (con_queue_t *)malloc(sizeof(con_queue_t));
+
+    thread_mutex_lock(&_queue_mutex);
+    node->con = con;
+    node->next = _queue;
+    _queue = node;
+    thread_mutex_unlock(&_queue_mutex);
+
+}
+
+static void _push_thread(thread_queue_t **queue, thread_type *thread_id)
+{
+    /* create item */
+    thread_queue_t *item = (thread_queue_t *)malloc(sizeof(thread_queue_t));
+    item->thread_id = thread_id;
+    item->next = NULL;
+
+
+    thread_mutex_lock(&_queue_mutex);
+    if (*queue == NULL) {
+        *queue = item;
+    } else {
+        item->next = *queue;
+        *queue = item;
+    }
+    thread_mutex_unlock(&_queue_mutex);
+}
+
+static thread_type *_pop_thread(thread_queue_t **queue)
+{
+    thread_type *id;
+    thread_queue_t *item;
+
+    thread_mutex_lock(&_queue_mutex);
+
+    item = *queue;
+    if (item == NULL) {
+        thread_mutex_unlock(&_queue_mutex);
+        return NULL;
+    }
+
+    *queue = item->next;
+    item->next = NULL;
+    id = item->thread_id;
+    free(item);
+
+    thread_mutex_unlock(&_queue_mutex);
+
+    return id;
+}
+
+static void _build_pool(void)
+{
+    ice_config_t *config;
+    int i;
+    thread_type *tid;
+    char buff[64];
+    int threadpool_size;
+
+    config = config_get_config();
+    threadpool_size = config->threadpool_size;
+    config_release_config();
+
+    for (i = 0; i < threadpool_size; i++) {
+        snprintf(buff, 64, "Connection Thread #%d", i);
+        tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
+        _push_thread(&_conhands, tid);
+    }
+}
+
+static void _destroy_pool(void)
+{
+    thread_type *id;
+    int i;
+
+    i = 0;
+
+    id = _pop_thread(&_conhands);
+    while (id != NULL) {
+        thread_join(id);
+        id = _pop_thread(&_conhands);
+    }
+    INFO0("All connection threads down");
+}
+
+void connection_accept_loop(void)
+{
+    connection_t *con;
+
+    _build_pool();
+
+    while (global.running == ICE_RUNNING)
+    {
+        if (global . schedule_config_reread)
+        {
+            /* reread config file */
+            INFO0("Scheduling config reread ...");
+
+            connection_inject_event(EVENT_CONFIG_READ, NULL);
+            global . schedule_config_reread = 0;
+        }
+
+        con = _accept_connection();
+
+        if (con) {
+            _add_connection(con);
+        }
+    }
+
+    /* Give all the other threads notification to shut down */
+    thread_cond_broadcast(&global.shutdown_cond);
+
+    _destroy_pool();
+
+    /* wait for all the sources to shutdown */
+    thread_rwlock_wlock(&_source_shutdown_rwlock);
+    thread_rwlock_unlock(&_source_shutdown_rwlock);
+}
+
+static connection_t *_get_connection(void)
+{
+    con_queue_t *node = NULL;
+    con_queue_t *oldnode = NULL;
+    connection_t *con = NULL;
+
+    thread_mutex_lock(&_queue_mutex);
+    if (_queue) {
+        node = _queue;
+        while (node->next) {
+            oldnode = node;
+            node = node->next;
+        }
+
+        /* node is now the last node
+        ** and oldnode is the previous one, or NULL
+        */
+        if (oldnode) oldnode->next = NULL;
+        else (_queue) = NULL;
+    }
+    thread_mutex_unlock(&_queue_mutex);
+
+    if (node) {
+        con = node->con;
+        free(node);
+    }
+
+    return con;
+}
+
+void connection_inject_event(int eventnum, void *event_data) {
+    connection_t *con = calloc(1, sizeof(connection_t));
+
+    con->event_number = eventnum;
+    con->event = event_data;
+
+    _add_connection(con);
+}
+
+
+/* Called when activating a source. Verifies that the source count is not
+ * exceeded and applies any initial parameters.
+ */
+int connection_complete_source (source_t *source)
+{
+    ice_config_t *config = config_get_config();
+
+    global_lock ();
+    DEBUG1 ("sources count is %d", global.sources);
+
+    if (global.sources < config->source_limit)
+    {
+        char *contenttype;
+        mount_proxy *mountproxy = config->mounts;
+        format_type_t format_type;
+
+        /* setup format handler */
+        contenttype = httpp_getvar (source->parser, "content-type");
+        if (contenttype != NULL)
+        {
+            format_type = format_get_type (contenttype);
+
+            if (format_type == FORMAT_ERROR)
+            {
+                global_unlock();
+                config_release_config();
+                if (source->client)
+                    client_send_404 (source->client, "Content-type not supported");
+                WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
+                return -1;
+            }
+        }
+        else
+        {
+            WARN0("No content-type header, falling back to backwards compatibility mode "
+                    "for icecast 1.x relays. Assuming content is mp3.");
+            format_type = FORMAT_TYPE_MP3;
+        }
+
+        if (format_get_plugin (format_type, source) < 0)
+        {
+            global_unlock();
+            config_release_config();
+            if (source->client)
+                client_send_404 (source->client, "internal format allocation problem");
+            WARN1 ("plugin format failed for \"%s\"", source->mount);
+            return -1;
+        }
+
+        global.sources++;
+        global_unlock();
+
+        /* 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,
+         * because we can't use this client to return an error code/message,
+         * so we only do this once we know we're going to accept the source.
+         */
+        if (source->client == NULL)
+            source->client = client_create (source->con, source->parser);
+
+        while (mountproxy)
+        {
+            if (strcmp (mountproxy->mountname, source->mount) == 0)
+            {
+                source_apply_mount (source, mountproxy);
+                break;
+            }
+            mountproxy = mountproxy->next;
+        }
+        config_release_config();
+
+        source->shutdown_rwlock = &_source_shutdown_rwlock;
+        DEBUG0 ("source is ready to start");
+
+        return 0;
+    }
+    WARN1("Request to add source when maximum source limit"
+            "reached %d", global.sources);
+
+    global_unlock();
+    config_release_config();
+
+    if (source->client)
+        client_send_404 (source->client, "too many sources connected");
+
+    return -1;
+}
+
+
+static int _check_pass_http(http_parser_t *parser,
+        char *correctuser, char *correctpass)
+{
+    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
+    char *header = httpp_getvar(parser, "authorization");
+    char *userpass, *tmp;
+    char *username, *password;
+
+    if(header == NULL)
+        return 0;
+
+    if(strncmp(header, "Basic ", 6))
+        return 0;
+
+    userpass = util_base64_decode(header+6);
+    if(userpass == NULL) {
+        WARN1("Base64 decode of Authorization header \"%s\" failed",
+                header+6);
+        return 0;
+    }
+
+    tmp = strchr(userpass, ':');
+    if(!tmp) {
+        free(userpass);
+        return 0;
+    }
+    *tmp = 0;
+    username = userpass;
+    password = tmp+1;
+
+    if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
+        free(userpass);
+        return 0;
+    }
+    free(userpass);
+
+    return 1;
+}
+
+static int _check_pass_icy(http_parser_t *parser, char *correctpass)
+{
+    char *password;
+
+    password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
+    if(!password)
+        return 0;
+
+    if (strcmp(password, correctpass))
+        return 0;
+    else
+        return 1;
+}
+
+static int _check_pass_ice(http_parser_t *parser, char *correctpass)
+{
+    char *password;
+
+    password = httpp_getvar(parser, "ice-password");
+    if(!password)
+        password = "";
+
+    if (strcmp(password, correctpass))
+        return 0;
+    else
+        return 1;
+}
+
+int connection_check_admin_pass(http_parser_t *parser)
+{
+    int ret;
+    ice_config_t *config = config_get_config();
+    char *pass = config->admin_password;
+    char *user = config->admin_username;
+
+    if(!pass || !user) {
+        config_release_config();
+        return 0;
+    }
+
+    ret = _check_pass_http(parser, user, pass);
+    config_release_config();
+    return ret;
+}
+
+int connection_check_relay_pass(http_parser_t *parser)
+{
+    int ret;
+    ice_config_t *config = config_get_config();
+    char *pass = config->relay_password;
+    char *user = config->relay_username;
+
+    if(!pass || !user) {
+        config_release_config();
+        return 0;
+    }
+
+    ret = _check_pass_http(parser, user, pass);
+    config_release_config();
+    return ret;
+}
+
+
+int connection_check_source_pass(http_parser_t *parser, char *mount)
+{
+    ice_config_t *config = config_get_config();
+    char *pass = config->source_password;
+    char *user = "source";
+    int ret;
+    int ice_login = config->ice_login;
+    char *protocol;
+
+    mount_proxy *mountinfo = config->mounts;
+    thread_mutex_lock(&(config_locks()->mounts_lock));
+
+    while(mountinfo) {
+        if(!strcmp(mountinfo->mountname, mount)) {
+            if(mountinfo->password)
+                pass = mountinfo->password;
+            if(mountinfo->username)
+                user = mountinfo->username;
+            break;
+        }
+        mountinfo = mountinfo->next;
+    }
+
+    thread_mutex_unlock(&(config_locks()->mounts_lock));
+
+    if(!pass) {
+        WARN0("No source password set, rejecting source");
+        config_release_config();
+        return 0;
+    }
+
+    protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
+    if(protocol != NULL && !strcmp(protocol, "ICY")) {
+        ret = _check_pass_icy(parser, pass);
+    }
+    else {
+        ret = _check_pass_http(parser, user, pass);
+        if(!ret && ice_login)
+        {
+            ret = _check_pass_ice(parser, pass);
+            if(ret)
+                WARN0("Source is using deprecated icecast login");
+        }
+    }
+    config_release_config();
+    return ret;
+}
+
+
+static void _handle_source_request(connection_t *con,
+        http_parser_t *parser, char *uri)
+{
+    client_t *client;
+    source_t *source;
+
+    client = client_create(con, parser);
+
+    INFO1("Source logging in at mountpoint \"%s\"", 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
+         */
+        /* TODO: Do what the above comment says */
+        INFO1("Source (%s) attempted to login with invalid or missing password", uri);
+        client_send_401(client);
+        return;
+    }
+    source = source_reserve (uri);
+    if (source)
+    {
+        source->client = client;
+        source->parser = parser;
+        source->con = con;
+        if (connection_complete_source (source) < 0)
+        {
+            source->client = NULL;
+            source_free_source (source);
+        }
+        else
+            thread_create ("Source Thread", source_client_thread,
+                    source, THREAD_DETACHED);
+    }
+    else
+    {
+        client_send_404 (client, "Mountpoint in use");
+    }
+}
+
+
+static void _handle_stats_request(connection_t *con,
+        http_parser_t *parser, char *uri)
+{
+    stats_connection_t *stats;
+
+    stats_event_inc(NULL, "stats_connections");
+
+    if (!connection_check_admin_pass(parser))
+    {
+        ERROR0("Bad password for stats connection");
+        connection_close(con);
+        httpp_destroy(parser);
+        return;
+    }
+
+    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;
+    stats->con = con;
+
+    thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED);
+}
+
+static void _handle_get_request(connection_t *con,
+        http_parser_t *parser, char *passed_uri)
+{
+    char *fullpath;
+    client_t *client;
+    int bytes;
+    struct stat statbuf;
+    source_t *source;
+    int fileserve;
+    char *host;
+    int port;
+    int i;
+    char *serverhost = NULL;
+    int serverport = 0;
+    aliases *alias;
+    ice_config_t *config;
+    int client_limit;
+    char *uri = passed_uri;
+
+    DEBUG1("start with %s", passed_uri);
+    config = config_get_config();
+    fileserve = config->fileserve;
+    host = config->hostname;
+    port = config->port;
+    for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
+        if(global.serversock[i] == con->serversock) {
+            serverhost = config->listeners[i].bind_address;
+            serverport = config->listeners[i].port;
+            break;
+        }
+    }
+    alias = config->aliases;
+    client_limit = config->client_limit;
+
+
+    stats_event_inc(NULL, "client_connections");
+
+    /* there are several types of HTTP GET clients
+    ** media clients, which are looking for a source (eg, URI = /stream.ogg)
+    ** stats clients, which are looking for /admin/stats.xml
+    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx
+    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
+    ** we need to handle the latter two before the former, as the latter two
+    ** aren't subject to the limits.
+    */
+    /* TODO: add GUID-xxxxxx */
+
+    /* Handle aliases */
+    while(alias) {
+        if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
+            uri = strdup (alias->destination);
+            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
+            break;
+        }
+        alias = alias->next;
+    }
+    config_release_config();
+
+    /* 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;
+    }
+
+    /* Here we are parsing the URI request to see
+    ** if the extension is .xsl, if so, then process
+    ** this request as an XSLT request
+    */
+    fullpath = util_get_path_from_normalised_uri(uri);
+    if (util_check_valid_extension(fullpath) == XSLT_CONTENT) {
+        /* If the file exists, then transform it, otherwise, write a 404 */
+        if (stat(fullpath, &statbuf) == 0) {
+            DEBUG0("Stats request, sending XSL transformed stats");
+            client->respcode = 200;
+            bytes = sock_write(client->con->sock,
+                    "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
+            if(bytes > 0) client->con->sent_bytes = bytes;
+            stats_transform_xslt(client, fullpath);
+            client_destroy(client);
+        }
+        else {
+            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 &&
+#ifdef _WIN32
+            ((statbuf.st_mode) & _S_IFREG))
+#else
+            S_ISREG(statbuf.st_mode))
+#endif
+    {
+        fserve_client_create(client, fullpath);
+        free(fullpath);
+        if (uri != passed_uri) free (uri);
+        return;
+    }
+    free(fullpath);
+
+    if(strcmp(util_get_extension(uri), "m3u") == 0) {
+        char *sourceuri = strdup(uri);
+        char *dot = strrchr(sourceuri, '.');
+        *dot = 0;
+        avl_tree_rlock(global.source_tree);
+        source = source_find_mount(sourceuri);
+           if (source) {
+            client->respcode = 200;
+            bytes = sock_write(client->con->sock,
+                    "HTTP/1.0 200 OK\r\n"
+                    "Content-Type: audio/x-mpegurl\r\n\r\n"
+                    "http://%s:%d%s\r\n",
+                    host,
+                    port,
+                    sourceuri
+                    );
+            if(bytes > 0) client->con->sent_bytes = bytes;
+            client_destroy(client);
+        }
+        else if(fileserve) {
+            fullpath = util_get_path_from_normalised_uri(sourceuri);
+            if(stat(fullpath, &statbuf) == 0) {
+                fserve_client_create(client, fullpath);
+                free(fullpath);
+            }
+            else {
+                free(fullpath);
+                fullpath = util_get_path_from_normalised_uri(uri);
+                if(stat(fullpath, &statbuf) == 0) {
+                    fserve_client_create(client, fullpath);
+                    free(fullpath);
+                }
+                else {
+                    free(fullpath);
+                    client_send_404(client,
+                            "The file you requested could not be found");
+                }
+            }
+        }
+        else {
+            client_send_404(client, "The file you requested could not be found");
+        }
+        avl_tree_unlock(global.source_tree);
+        free(sourceuri);
+        if (uri != passed_uri) free (uri);
+        return;
+    }
+
+    global_lock();
+    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();
+
+    add_client (uri, client);
+
+    if (uri != passed_uri) free (uri);
+}
+
+static void *_handle_connection(void *arg)
+{
+    char header[4096];
+    connection_t *con;
+    http_parser_t *parser;
+    char *rawuri, *uri;
+    client_t *client;
+
+    while (global.running == ICE_RUNNING) {
+        memset(header, 0, 4096);
+
+        thread_sleep (100000);
+        if (global.running != ICE_RUNNING) break;
+
+        /* grab a connection and set the socket to blocking */
+        while ((con = _get_connection())) {
+
+            /* Handle meta-connections */
+            if(con->event_number > 0) {
+                switch(con->event_number) {
+                    case EVENT_CONFIG_READ:
+                        event_config_read(con->event);
+                        break;
+                    default:
+                        ERROR1("Unknown event number: %d", con->event_number);
+                        break;
+                }
+                free(con);
+                continue;
+            }
+
+            stats_event_inc(NULL, "connections");
+
+            sock_set_blocking(con->sock, SOCK_BLOCK);
+
+            /* fill header with the http header */
+            if (util_read_header(con->sock, header, 4096) == 0) {
+                /* either we didn't get a complete header, or we timed out */
+                connection_close(con);
+                continue;
+            }
+
+            parser = httpp_create_parser();
+            httpp_initialize(parser, NULL);
+            if (httpp_parse(parser, header, strlen(header))) {
+                /* handle the connection or something */
+
+                if (strcmp("ICE",  httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) &&
+                    strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) {
+                    ERROR0("Bad HTTP protocol detected");
+                    connection_close(con);
+                    httpp_destroy(parser);
+                    continue;
+                }
+
+                rawuri = httpp_getvar(parser, HTTPP_VAR_URI);
+                uri = util_normalise_uri(rawuri);
+
+                if(!uri) {
+                    client = client_create(con, parser);
+                    client_send_404(client, "The path you requested was invalid");
+                    continue;
+                }
+
+                if (parser->req_type == httpp_req_source) {
+                    _handle_source_request(con, parser, uri);
+                }
+                else if (parser->req_type == httpp_req_stats) {
+                    _handle_stats_request(con, parser, uri);
+                }
+                else if (parser->req_type == httpp_req_get) {
+                    _handle_get_request(con, parser, uri);
+                }
+                else {
+                    ERROR0("Wrong request type from client");
+                    connection_close(con);
+                    httpp_destroy(parser);
+                }
+
+                free(uri);
+            }
+            else if(httpp_parse_icy(parser, header, strlen(header))) {
+                /* TODO: Map incoming icy connections to /icy_0, etc. */
+                char mount[20];
+                unsigned i = 0;
+
+                strcpy(mount, "/");
+
+                avl_tree_rlock(global.source_tree);
+                while (source_find_mount (mount) != NULL) {
+                    snprintf (mount, sizeof (mount), "/icy_%u", i++);
+                }
+                avl_tree_unlock(global.source_tree);
+
+                _handle_source_request(con, parser, mount);
+            }
+            else {
+                ERROR0("HTTP request parsing failed");
+                connection_close(con);
+                httpp_destroy(parser);
+                continue;
+            }
+        }
+    }
+    DEBUG0 ("Connection thread done");
+
+    return NULL;
+}
+
+void connection_close(connection_t *con)
+{
+    sock_close(con->sock);
+    if (con->ip) free(con->ip);
+    if (con->host) free(con->host);
+    free(con);
+}

Added: icecast/branches/icecast-kh/src/connection.h
===================================================================
--- icecast/branches/icecast-kh/src/connection.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/connection.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,61 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __CONNECTION_H__
+#define __CONNECTION_H__
+
+#include <sys/types.h>
+#include <time.h>
+#include "compat.h"
+#include "httpp/httpp.h"
+#include "thread/thread.h"
+#include "net/sock.h"
+
+struct _client_tag;
+struct source_tag;
+
+typedef struct connection_tag
+{
+    unsigned long id;
+
+    time_t con_time;
+    uint64_t sent_bytes;
+
+
+    int sock;
+    int serversock;
+    int error;
+
+    char *ip;
+    char *host;
+
+    /* For 'fake' connections */
+    int event_number;
+    void *event;
+} connection_t;
+
+void connection_initialize(void);
+void connection_shutdown(void);
+void connection_accept_loop(void);
+void connection_close(connection_t *con);
+connection_t *create_connection(sock_t sock, sock_t serversock, char *ip);
+int connection_complete_source (struct source_tag *source);
+
+void connection_inject_event(int eventnum, void *event_data);
+
+int connection_check_source_pass(http_parser_t *parser, char *mount);
+int connection_check_relay_pass(http_parser_t *parser);
+int connection_check_admin_pass(http_parser_t *parser);
+
+extern rwlock_t _source_shutdown_rwlock;
+
+#endif  /* __CONNECTION_H__ */

Added: icecast/branches/icecast-kh/src/event.c
===================================================================
--- icecast/branches/icecast-kh/src/event.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/event.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,67 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "event.h"
+#include "cfgfile.h"
+#include "yp.h"
+
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h"
+#include "slave.h"
+
+#define CATMODULE "event"
+
+void event_config_read(void *arg)
+{
+    int ret;
+    ice_config_t *config;
+    ice_config_t new_config;
+    /* reread config file */
+
+    config = config_get_config(); /* Both to get the lock, and to be able
+                                     to find out the config filename */
+    ret = config_parse_file(config->config_filename, &new_config);
+    if(ret < 0) {
+        ERROR0("Error parsing config, not replacing existing config");
+        switch(ret) {
+            case CONFIG_EINSANE:
+                ERROR0("Config filename null or blank");
+                break;
+            case CONFIG_ENOROOT:
+                ERROR1("Root element not found in %s", config->config_filename);
+                break;
+            case CONFIG_EBADROOT:
+                ERROR1("Not an icecast2 config file: %s",
+                        config->config_filename);
+                break;
+            default:
+                ERROR1("Parse error in reading %s", config->config_filename);
+                break;
+        }
+        config_release_config();
+    }
+    else {
+        config_clear(config);
+        config_set_config(&new_config);
+        restart_logging ();
+        slave_recheck();
+        yp_recheck_config (config);
+
+        config_release_config();
+    }
+}
+

Added: icecast/branches/icecast-kh/src/event.h
===================================================================
--- icecast/branches/icecast-kh/src/event.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/event.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,21 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __EVENT_H__
+#define __EVENT_H__
+
+#define EVENT_NO_EVENT 0
+#define EVENT_CONFIG_READ 1
+
+void event_config_read(void *nothing);
+
+#endif  /* __EVENT_H__ */

Added: icecast/branches/icecast-kh/src/format.c
===================================================================
--- icecast/branches/icecast-kh/src/format.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,211 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; -*- */
+/* format.c
+**
+** format plugin implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#include <time.h>
+
+#include "connection.h"
+#include "refbuf.h"
+
+#include "source.h"
+#include "format.h"
+#include "global.h"
+#include "httpp/httpp.h"
+
+#include "format_ogg.h"
+#include "format_mp3.h"
+
+#include "logging.h"
+#define CATMODULE "format"
+
+#ifdef WIN32
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+format_type_t format_get_type(char *contenttype)
+{
+    if(strcmp(contenttype, "application/x-ogg") == 0)
+        return FORMAT_TYPE_OGG; /* Backwards compatibility */
+    else if(strcmp(contenttype, "application/ogg") == 0)
+        return FORMAT_TYPE_OGG; /* Now blessed by IANA */
+    else if(strcmp(contenttype, "audio/mpeg") == 0)
+        return FORMAT_TYPE_MP3;
+    else if(strcmp(contenttype, "audio/x-mpeg") == 0)
+        return FORMAT_TYPE_MP3;
+    else
+        return FORMAT_ERROR;
+}
+
+char *format_get_mimetype(format_type_t type)
+{
+    switch(type) {
+        case FORMAT_TYPE_OGG:
+            return "application/ogg";
+            break;
+        case FORMAT_TYPE_MP3:
+            return "audio/mpeg";
+            break;
+        default:
+            return NULL;
+    }
+}
+
+int format_get_plugin(format_type_t type, source_t *source)
+{
+    int ret;
+
+    switch (type)
+    {
+    case FORMAT_TYPE_OGG:
+        ret = format_ogg_get_plugin (source);
+        break;
+    case FORMAT_TYPE_MP3:
+        ret = format_mp3_get_plugin (source);
+        break;
+    default:
+        ret = -1;
+        break;
+    }
+
+    return ret;
+}
+
+
+int format_generic_write_buf_to_client(format_plugin_t *format,
+        client_t *client, unsigned char *buf, int len)
+{
+    int ret;
+
+    ret = client_send_bytes (client, buf, len);
+    if (ret < 0 && client->con->error == 0)
+        ret = 0;
+
+    return ret;
+}
+
+
+void format_prepare_headers (source_t *source, client_t *client)
+{
+    unsigned remaining;
+    char *ptr;
+    int bytes;
+    int bitrate_filtered = 0;
+    avl_node *node;
+    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);
+    while (node)
+    {
+        int next = 1;
+        http_var_t *var = (http_var_t *)node->key;
+        bytes = 0;
+        if (!strcasecmp (var->name, "ice-audio-info"))
+        {
+            /* convert ice-audio-info to icy-br */
+            char *brfield = NULL;
+            unsigned int bitrate;
+
+            if (bitrate_filtered == 0)
+                brfield = strstr (var->value, "bitrate=");
+            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
+            {
+                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
+                next = 0;
+                bitrate_filtered = 1;
+            }
+            else
+                /* show ice-audio_info header as well because of relays */
+                bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value);
+        }
+        else
+        {
+            if (strcasecmp (var->name, "ice-password") &&
+                strcasecmp (var->name, "icy-metaint"))
+            {
+                if (!strncasecmp ("ice-", var->name, 4))
+                {
+                    if (!strcasecmp ("ice-public", var->name))
+                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
+                    else
+                        if (!strcasecmp ("ice-bitrate", var->name))
+                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
+                        else
+                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
+                                    var->name + 3, var->value);
+                }
+                else
+                    if (!strncasecmp ("icy-", var->name, 4))
+                    {
+                        bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
+                                var->name + 3, var->value);
+                    }
+            }
+        }
+
+        remaining -= bytes;
+        ptr += bytes;
+        if (next)
+            node = avl_get_next (node);
+    }
+    avl_tree_unlock (source->parser->vars);
+
+    bytes = snprintf (ptr, remaining, "Server: %s\r\n", ICECAST_VERSION_STRING);
+    remaining -= bytes;
+    ptr += bytes;
+
+    bytes = snprintf (ptr, remaining, "\r\n");
+    remaining -= bytes;
+    ptr += bytes;
+
+    client->predata_len = client->predata_size - remaining;
+}
+
+
+void format_initialise ()
+{
+    format_ogg_initialise ();
+}
+

Added: icecast/branches/icecast-kh/src/format.h
===================================================================
--- icecast/branches/icecast-kh/src/format.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,72 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format.h
+**
+** format plugin header
+**
+*/
+#ifndef __FORMAT_H__
+#define __FORMAT_H__
+
+#include "client.h"
+#include "refbuf.h"
+#include "httpp/httpp.h"
+
+struct source_tag;
+
+typedef enum _format_type_tag
+{
+    FORMAT_ERROR, /* No format, source not processable */
+    FORMAT_TYPE_OGG,
+    FORMAT_TYPE_VORBIS,
+    FORMAT_TYPE_MP3
+} format_type_t;
+
+typedef struct _format_plugin_tag
+{
+    format_type_t type;
+
+    /* we need to know the mount to report statistics */
+    char *mount;
+
+    char *format_description;
+
+    /* set this is the data format has a header that
+    ** we must send before regular data
+    */
+
+    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;
+} format_plugin_t;
+
+format_type_t format_get_type(char *contenttype);
+char *format_get_mimetype(format_type_t type);
+int format_get_plugin(format_type_t type, struct source_tag *source);
+
+int format_generic_write_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__ */
+

Added: icecast/branches/icecast-kh/src/format_mp3.c
===================================================================
--- icecast/branches/icecast-kh/src/format_mp3.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_mp3.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,572 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; -*- */
+/* format_mp3.c
+**
+** format plugin for mp3
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+
+#include "format_mp3.h"
+
+#ifdef WIN32
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#define snprintf _snprintf
+#endif
+
+#define CATMODULE "format-mp3"
+
+/* Note that this seems to be 8192 in shoutcast - perhaps we want to be the
+ * same for compability with crappy clients?
+ */
+#define ICY_METADATA_INTERVAL 8192
+
+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);
+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 metadata_offset;
+   unsigned since_meta_block;
+   int in_metadata;
+   refbuf_t *associated;
+} mp3_client_data;
+
+int format_mp3_get_plugin (source_t *source)
+{
+    char *metadata;
+    format_plugin_t *plugin;
+    mp3_state *state = calloc(1, sizeof(mp3_state));
+    refbuf_t *meta;
+
+    plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
+
+    plugin->type = FORMAT_TYPE_MP3;
+    plugin->get_buffer = mp3_get_no_meta;
+    plugin->write_buf_to_client = format_mp3_write_buf_to_client;
+    plugin->write_buf_to_file = write_mp3_to_file;
+    plugin->create_client_data = format_mp3_create_client_data;
+    plugin->free_plugin = format_mp3_free_plugin;
+    plugin->set_tag = mp3_set_tag;
+    plugin->prerelease = NULL;
+    plugin->format_description = "MP3 audio";
+
+    plugin->_state = state;
+
+    meta = refbuf_new (1);
+    memcpy (meta->data, "", 1);
+    meta->len = 1;
+    state->metadata = meta;
+    state->interval = ICY_METADATA_INTERVAL;
+
+    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 0;
+}
+
+
+static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value)
+{
+    mp3_state *source_mp3 = plugin->_state;
+    unsigned len;
+    const char meta[] = "StreamTitle='";
+    int size = sizeof (meta) + 1;
+
+    if (tag==NULL || value == NULL)
+        return;
+
+    len = strlen (value)+1;
+    size += len;
+    if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
+    {
+        char *p = strdup (value);
+        if (p)
+        {
+            free (source_mp3->url_title);
+            free (source_mp3->url_artist);
+            source_mp3->url_artist = NULL;
+            source_mp3->url_title = p;
+            source_mp3->update_metadata = 1;
+        }
+        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;
+}
+
+
+static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned meta_len)
+{
+    if (metadata)
+    {
+        char *end, *p;
+        int len;
+
+        do
+        {
+            metadata++;
+            if (strncmp (metadata, "StreamTitle='", 13))
+                break;
+            if ((end = strstr (metadata, "\';")) == NULL)
+                break;
+            len = (end - metadata) - 13;
+            p = calloc (1, len+1);
+            if (p)
+            {
+                memcpy (p, metadata+13, len);
+                stats_event (source->mount, "title", p);
+                yp_touch (source->mount);
+                free (p);
+            }
+        } while (0);
+    }
+}
+
+
+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 (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
+            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;
+    }
+}
+
+
+static int send_mp3_metadata (client_t *client, refbuf_t *associated)
+{
+    int ret = 0;
+    unsigned char *metadata;
+    int meta_len;
+    mp3_client_data *client_mp3 = client->format_data;
+
+    if (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);
+
+    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;
+
+    return ret;
+}
+
+
+/* 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, 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 (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;
+
+    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 (ret < (int)associated->len)
+                break;
+            written += ret;
+        }
+        /* 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;
+
+            /* sending the metadata block */
+            if (remaining <= len)
+            {
+                /* send any mp3 before the metadata block */
+                if (remaining)
+                {
+                    ret = client_send_bytes (client, buf, remaining);
+
+                    if (ret > 0)
+                    {
+                        client_mp3->since_meta_block += ret;
+                        client->pos += ret;
+                    }
+                    if (ret < (int)remaining)
+                        break;
+                    written += ret;
+                }
+                ret = send_mp3_metadata (client, refbuf->associated);
+                if (client_mp3->in_metadata)
+                    break;
+                written += ret;
+                /* change buf and len */
+                buf += remaining;
+                len -= remaining;
+            }
+        }
+        /* write any mp3, maybe after the metadata block */
+        if (len)
+        {
+            ret = client_send_bytes (client, buf, len);
+
+            if (ret > 0)
+            {
+                client_mp3->since_meta_block += ret;
+                client->pos += ret;
+            }
+            if (ret < (int)len)
+                break;
+            written += ret;
+        }
+        ret = 0;
+        /* 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 *plugin)
+{
+    /* free the plugin instance */
+    mp3_state *state = plugin->_state;
+
+    free(state);
+    free(plugin);
+}
+
+
+static refbuf_t *mp3_get_no_meta (source_t *source)
+{
+    int bytes;
+    refbuf_t *refbuf;
+    mp3_state *source_mp3 = source->format->_state;
+
+    if ((refbuf = refbuf_new (4096)) == NULL)
+        return NULL;
+    bytes = sock_read_bytes (source->con->sock, refbuf->data, 4096);
+
+    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 (!sock_recoverable (sock_error()))
+        source->running = 0;
+
+    return NULL;
+}
+
+
+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 = refbuf_new (2048);
+    src = refbuf->data;
+
+    ret = sock_read_bytes (source->con->sock, refbuf->data, 2048);
+
+    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;
+
+        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;
+
+        /* is there only enough to account for mp3 data */
+        if (bytes <= mp3_block)
+        {
+            refbuf->len += bytes;
+            source_mp3->offset += bytes;
+            break;
+        }
+        /* we have enough data to get to the metadata block, but only transfer upto it */
+        if (mp3_block)
+        {
+            src += mp3_block;
+            bytes -= mp3_block;
+            refbuf->len += mp3_block;
+            source_mp3->offset += mp3_block;
+            continue;
+        }
+
+        /* 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);
+        }
+
+        /* 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);
+
+        /* overwrite metadata in the buffer */
+        bytes -= metadata_remaining;
+        memmove (src, src+metadata_remaining, bytes);
+
+        /* 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;
+
+            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;
+            }
+        }
+        source_mp3->offset = 0;
+        source_mp3->build_metadata_len = 0;
+    }
+    refbuf->associated = source_mp3->metadata;
+    refbuf->sync_point = 1;
+
+    return refbuf;
+}
+
+
+static void mp3_set_predata (source_t *source, client_t *client)
+{
+    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 int format_mp3_create_client_data(source_t *source, client_t *client)
+{
+    mp3_client_data *data = calloc(1,sizeof(mp3_client_data));
+    char *metadata;
+
+    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;
+
+        mp3_set_predata (source, client);
+    }
+
+    return 0;
+}
+
+
+static void free_mp3_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    if (refbuf->len == 0)
+        return;
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+    }
+}
+

Added: icecast/branches/icecast-kh/src/format_mp3.h
===================================================================
--- icecast/branches/icecast-kh/src/format_mp3.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_mp3.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,41 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_mp3.h
+**
+** mp3 format plugin
+**
+*/
+#ifndef __FORMAT_MP3_H__
+#define __FORMAT_MP3_H__
+
+#include "refbuf.h"
+
+typedef struct {
+    /* These are for inline metadata */
+    int inline_metadata_interval;
+    unsigned interval;
+    int 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;
+
+int format_mp3_get_plugin(struct source_tag *src);
+
+#endif  /* __FORMAT_MP3_H__ */

Added: icecast/branches/icecast-kh/src/format_ogg.c
===================================================================
--- icecast/branches/icecast-kh/src/format_ogg.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_ogg.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,798 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_ogg.c
+**
+** format plugin for Ogg
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#ifdef HAVE_THEORA
+#include <theora/theora.h>
+#endif
+#ifdef HAVE_SPEEX
+#include <speex_header.h>
+#endif
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+
+#define CATMODULE "format-ogg"
+#include "logging.h"
+
+struct _ogg_state_tag;
+
+/* per codec/logical structure */
+typedef struct _ogg_codec_tag
+{
+    ogg_stream_state os;
+    unsigned headers;
+    void *specific;
+    struct _ogg_state_tag *feed;
+    struct _ogg_codec_tag *next;
+    refbuf_t *(*process_page)(struct _ogg_codec_tag *codec, ogg_page *page);
+    void (*codec_free)(struct _ogg_codec_tag *codec);
+    refbuf_t        *possible_start;
+} ogg_codec_t;
+
+
+typedef struct _ogg_state_tag
+{
+    char *mount;
+    ogg_sync_state oy;
+
+    ogg_codec_t *codecs;
+    char *artist;
+    char *title;
+    int send_yp_info;
+    refbuf_t *file_headers;
+    refbuf_t *header_pages;
+    refbuf_t *header_pages_tail;
+    int headers_completed;
+    long bitrate;
+    ogg_codec_t *codec_sync;
+} ogg_state_t;
+
+
+struct client_vorbis
+{
+    refbuf_t *headers;
+    refbuf_t *header_page;
+    unsigned pos;
+    int headers_sent;
+};
+
+
+void refbuf_page_prerelease (struct source_tag *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+
+    /* only theora will be marking refbufs as sync that are behind in the
+     * queue, here we just make sure that it isn't the one we are going to
+     * remove. */
+    if (ogg_info->codec_sync)
+    {
+        if (ogg_info->codec_sync->possible_start == refbuf)
+            ogg_info->codec_sync->possible_start = NULL;
+    }
+}
+
+
+static refbuf_t *make_refbuf_with_page (ogg_page *page)
+{
+    refbuf_t *refbuf = refbuf_new (page->header_len + page->body_len);
+
+    refbuf->idx = ogg_page_pageno (page);
+    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;
+    return refbuf;
+}
+
+
+void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page)
+{
+    refbuf_t *refbuf = make_refbuf_with_page (page);
+
+    if (ogg_info->header_pages_tail)
+        ogg_info->header_pages_tail->next = refbuf;
+    ogg_info->header_pages_tail = refbuf;
+
+    if (ogg_info->header_pages == NULL)
+        ogg_info->header_pages = refbuf;
+}
+
+
+
+/**** vorbis ****/
+typedef struct _vorbis_codec_tag
+{
+    vorbis_info vi;
+    vorbis_comment vc;
+} vorbis_codec_t;
+
+
+static void vorbis_codec_free (ogg_codec_t *codec)
+{
+    vorbis_codec_t *vorbis = codec->specific;
+
+    codec->feed->artist = NULL;
+    codec->feed->title = NULL;
+    vorbis_info_clear (&vorbis->vi);
+    vorbis_comment_clear (&vorbis->vc);
+    ogg_stream_clear (&codec->os);
+    free (vorbis);
+    free (codec);
+}
+
+static refbuf_t *process_vorbis_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+
+    if (ogg_page_granulepos (page) == 0)
+    {
+        vorbis_codec_t *vorbis = codec->specific;
+        ogg_packet packet;
+
+        ogg_stream_pagein (&codec->os, page);
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           if (vorbis_synthesis_headerin (&vorbis->vi, &vorbis->vc, &packet) < 0)
+           {
+               /* set some error code */
+               return NULL;
+           }
+           codec->headers++;
+        }
+        /* add header page to associated list */
+        format_ogg_attach_header (codec->feed, page);
+        if (codec->headers == 3)
+        {
+            ogg_state_t *ogg_info = codec->feed;
+
+            ogg_info->send_yp_info = 1;
+            ogg_info->title  = vorbis_comment_query (&vorbis->vc, "TITLE", 0);
+            ogg_info->artist = vorbis_comment_query (&vorbis->vc, "ARTIST", 0);
+            ogg_info->bitrate += vorbis->vi.bitrate_nominal;
+            stats_event_args (codec->feed->mount, "audio-samplerate", "%ld", (long)vorbis->vi.rate);
+            stats_event_args (codec->feed->mount, "audio-channels", "%ld", (long)vorbis->vi.channels);
+            stats_event_args (codec->feed->mount, "audio-bitrate", "%ld", (long)vorbis->vi.bitrate_nominal);
+        }
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    if (codec->feed->codec_sync == NULL)
+        refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_vorbis_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    vorbis_codec_t *vorbis_codec = calloc (1, sizeof (vorbis_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    vorbis_info_init (&vorbis_codec->vi);
+    vorbis_comment_init (&vorbis_codec->vc);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    if (vorbis_synthesis_headerin (&vorbis_codec->vi, &vorbis_codec->vc, &packet) < 0)
+    {
+        ogg_stream_clear (&codec->os);
+        vorbis_info_clear (&vorbis_codec->vi);
+        vorbis_comment_clear (&vorbis_codec->vc);
+        free (vorbis_codec);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial vorbis header");
+    codec->specific = vorbis_codec;
+    codec->process_page = process_vorbis_page;
+    codec->codec_free = vorbis_codec_free;
+    codec->headers = 1;
+    return codec;
+}
+
+#ifdef HAVE_SPEEX
+
+static void speex_codec_free (ogg_codec_t *codec)
+{
+    ogg_stream_clear (&codec->os);
+    free (codec);
+}
+
+
+static refbuf_t *process_speex_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+
+    if (codec->headers < 2)
+    {
+        ogg_packet packet;
+
+        ogg_stream_pagein (&codec->os, page);
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           /* first time around (normal case) yields comments */
+           codec->headers++;
+        }
+        /* add header page to associated list */
+        format_ogg_attach_header (codec->feed, page);
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    if (codec->feed->codec_sync == NULL)
+        refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_speex_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+    SpeexHeader *header;
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    header = speex_packet_to_header (packet.packet, packet.bytes);
+    if (header == NULL)
+    {
+        ogg_stream_clear (&codec->os);
+        free (header);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial speex header");
+    codec->process_page = process_speex_page;
+    codec->codec_free = speex_codec_free;
+    codec->headers = 1;
+    free (header);
+    return codec;
+}
+#endif
+
+#ifdef HAVE_THEORA
+
+typedef struct _theora_codec_tag
+{
+    theora_info     ti;
+    theora_comment  tc;
+    int             granule_shift;
+    ogg_int64_t     last_iframe;
+    ogg_int64_t     prev_granulepos;
+} theora_codec_t;
+
+
+static void theora_codec_free (ogg_codec_t *codec)
+{
+    theora_codec_t *theora = codec->specific;
+
+    theora_info_clear (&theora->ti);
+    theora_comment_clear (&theora->tc);
+    ogg_stream_clear (&codec->os);
+    free (theora);
+    free (codec);
+}
+
+
+static int _ilog (unsigned int v)
+{
+  int ret=0;
+  while(v){
+    ret++;
+    v>>=1;
+  }
+  return ret;
+}
+
+
+static refbuf_t *process_theora_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+    theora_codec_t *theora = codec->specific;
+    ogg_int64_t granulepos;
+
+    granulepos = ogg_page_granulepos (page);
+    // printf ("   granulepos of page %ld is %lld\n", ogg_page_pageno (page), granulepos);
+    if (granulepos == 0)
+    {
+        ogg_packet packet;
+        ogg_state_t *ogg_info = codec->feed;
+
+        ogg_stream_pagein (&codec->os, page);
+        // printf ("page %ld processing\n", ogg_page_pageno (page));
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           if (theora_decode_header (&theora->ti, &theora->tc, &packet) < 0)
+           {
+               /* set some error code */
+               WARN0 ("problem with theora header");
+               return NULL;
+           }
+           codec->headers++;
+           // printf ("header packets: %d\n", codec->headers);
+           if (codec->headers == 3)
+           {
+               theora->granule_shift = _ilog (theora->ti.keyframe_frequency_force - 1);
+               DEBUG1 ("granule shift is %lu", theora->granule_shift);
+               theora->last_iframe = (ogg_int64_t)-1;
+               codec->possible_start = NULL;
+               ogg_info->bitrate += theora->ti.target_bitrate;
+               stats_event_args (codec->feed->mount, "video_bitrate",
+                       "%ld", (long)theora->ti.target_bitrate);
+               stats_event_args (codec->feed->mount, "frame_size",
+                       "%ld x %ld", (long)theora->ti.frame_width, (long)theora->ti.frame_height);
+               stats_event_args (codec->feed->mount, "framerate",
+                       "%.2f", (float)theora->ti.fps_numerator/theora->ti.fps_denominator);
+           }
+        }
+        /* add page to associated list */
+        format_ogg_attach_header (ogg_info, page);
+
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    refbuf->idx = ogg_page_pageno (page);
+
+    // DEBUG2 ("granulepos is %lld,  %p", granulepos, refbuf);
+    if (granulepos == -1 || granulepos == theora->prev_granulepos)
+    {
+        if (codec->possible_start == NULL)
+        {
+            // DEBUG1 ("granulepos is unset, possible beginning %p", refbuf);
+            codec->possible_start = refbuf;
+        }
+        // DEBUG1 ("possible start is %p", codec->possible_start);
+    }
+    else
+    {
+        if ((granulepos >> theora->granule_shift) != theora->last_iframe)
+        {
+            theora->last_iframe = (granulepos >> theora->granule_shift);
+            // DEBUG2 ("iframe changed to %lld (%p)", theora->last_iframe, refbuf);
+            if (codec->possible_start == NULL)
+                codec->possible_start = refbuf;
+            codec->possible_start->sync_point = 1;
+            // DEBUG1 ("possible start is %p", codec->possible_start);
+        }
+        else
+        {
+            if (theora->prev_granulepos != -1)
+                codec->possible_start = refbuf;
+        }
+    }
+    theora->prev_granulepos = granulepos;
+
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_theora_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    theora_codec_t *theora_codec = calloc (1, sizeof (theora_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    theora_info_init (&theora_codec->ti);
+    theora_comment_init (&theora_codec->tc);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    if (theora_decode_header (&theora_codec->ti, &theora_codec->tc, &packet) < 0)
+    {
+        theora_info_clear (&theora_codec->ti);
+        theora_comment_clear (&theora_codec->tc);
+        ogg_stream_clear (&codec->os);
+        free (theora_codec);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial theora header");
+    // printf ("initial page %ld processing\n", ogg_page_pageno (page));
+    codec->specific = theora_codec;
+    codec->process_page = process_theora_page;
+    codec->codec_free = theora_codec_free;
+    codec->headers = 1;
+    return codec;
+}
+#endif /* THEORA */
+
+static void format_ogg_free_plugin (format_plugin_t *plugin);
+static int  create_ogg_client_data(source_t *source, client_t *client);
+static void free_ogg_client_data (client_t *client);
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
+static refbuf_t *ogg_get_buffer (source_t *source);
+static int write_buf_to_client (format_plugin_t *self, client_t *client);
+
+
+static void free_ogg_codecs (ogg_state_t *ogg_info)
+{
+    ogg_codec_t *codec;
+
+    if (ogg_info == NULL)
+        return;
+    codec = ogg_info->codecs;
+    while (codec)
+    {
+        ogg_codec_t *next = codec->next;
+        codec->codec_free (codec);
+        codec = next;
+    }
+    ogg_info->codecs = NULL;
+    ogg_info->headers_completed = 0;
+}
+
+
+int format_ogg_get_plugin (source_t *source)
+{
+    format_plugin_t *plugin;
+    ogg_state_t *state = calloc (1, sizeof (ogg_state_t));
+
+    plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
+
+    plugin->type = FORMAT_TYPE_OGG;
+    plugin->format_description = "Ogg Vorbis";
+    plugin->get_buffer = ogg_get_buffer;
+    plugin->write_buf_to_client = write_buf_to_client;
+    plugin->write_buf_to_file = write_ogg_to_file;
+    plugin->create_client_data = create_ogg_client_data;
+    plugin->free_plugin = format_ogg_free_plugin;
+    plugin->set_tag = NULL;
+    plugin->prerelease = refbuf_page_prerelease;
+
+    ogg_sync_init (&state->oy);
+
+    plugin->_state = state;
+    source->format = plugin;
+    state->mount = source->mount;
+
+    return 0;
+}
+
+
+void format_ogg_free_plugin (format_plugin_t *plugin)
+{
+    ogg_state_t *state = plugin->_state;
+
+    /* free memory associated with this plugin instance */
+    free_ogg_codecs (state);
+
+    /* free state memory */
+    ogg_sync_clear (&state->oy);
+    free (state);
+
+    /* free the plugin instance */
+    free (plugin);
+}
+
+
+
+static int process_initial_page (ogg_state_t *ogg_info, ogg_page *page)
+{
+    ogg_codec_t *codec;
+
+    if (ogg_info->headers_completed)
+    {
+        ogg_info->bitrate = 0;
+        ogg_info->codec_sync = NULL;
+        /* need to zap old list of codecs when next group of BOS pages appear */
+        free_ogg_codecs (ogg_info);
+        ogg_info->header_pages = NULL;
+        ogg_info->header_pages_tail = NULL;
+    }
+    do
+    {
+        codec = initial_vorbis_page (page);
+        if (codec)
+            break;
+#ifdef HAVE_THEORA
+        codec = initial_theora_page (page);
+        if (codec)
+        {
+            ogg_info->codec_sync = codec;
+            break;
+        }
+#endif
+#ifdef HAVE_SPEEX
+        codec = initial_speex_page (page);
+        if (codec)
+            break;
+#endif
+
+        /* any others */
+        INFO0 ("Seen BOS page with unknown type");
+        return -1;
+    } while (0);
+
+    if (codec)
+    {
+        /* add codec to list */
+        codec->next = ogg_info->codecs;
+        ogg_info->codecs = codec;
+
+        codec->feed = ogg_info;
+        format_ogg_attach_header (ogg_info, page);
+        // printf ("initial header page stored at %p\n", ogg_info->header_pages);
+    }
+
+    return 0;
+}
+
+
+static refbuf_t *process_ogg_page (ogg_state_t *ogg_info, ogg_page *page)
+{
+    ogg_codec_t *codec = ogg_info->codecs;
+
+    while (codec)
+    {
+        if (ogg_page_serialno (page) == codec->os.serialno)
+        {
+            refbuf_t *refbuf = codec->process_page (codec, page);
+            if (refbuf)
+                refbuf->associated = ogg_info->header_pages;
+            return refbuf;
+        }
+        codec = codec->next;
+    }
+    return NULL;
+}
+
+
+static refbuf_t *ogg_get_buffer (source_t *source)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+    char *data = NULL;
+    int bytes;
+    ogg_page page;
+    refbuf_t *refbuf = NULL;
+
+    while (1)
+    {
+        while (1)
+        {
+            if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
+            {
+                if (ogg_page_bos (&page))
+                {
+                    process_initial_page (ogg_info, &page);
+                    continue;
+                }
+                // printf ("finished with BOS pages\n");
+                ogg_info->headers_completed = 1;
+                /* process the extracted page */
+                refbuf = process_ogg_page (ogg_info, &page);
+
+                if (ogg_info->send_yp_info)
+                {
+                    char *tag;
+                    tag = ogg_info->title;
+                    if (tag == NULL)
+                        tag = "unknown";
+                    stats_event (source->mount, "title", tag);
+                    INFO1("Updating title \"%s\"", tag);
+
+                    tag = ogg_info->artist;
+                    if (tag == NULL)
+                        tag = "unknown";
+                    stats_event (source->mount, "artist", tag);
+                    if (ogg_info->bitrate)
+                        stats_event_args (source->mount, "ice-bitrate", "%u", ogg_info->bitrate/1000);
+
+                    INFO1("Updating artist \"%s\"", tag);
+                    ogg_info->send_yp_info = 0;
+                    yp_touch (source->mount);
+                }
+                if (refbuf)
+                    return refbuf;
+                continue;
+            }
+            break;
+        }
+        /* we need more data to continue getting pages */
+        data = ogg_sync_buffer (&ogg_info->oy, 4096);
+
+        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 (&ogg_info->oy, 0);
+            source->running = 0;
+            return NULL;
+        }
+        if (bytes == 0)
+        {
+            INFO1 ("End of Stream %s", source->mount);
+            ogg_sync_wrote (&ogg_info->oy, 0);
+            source->running = 0;
+            return NULL;
+        }
+        ogg_sync_wrote (&ogg_info->oy, bytes);
+    }
+}
+
+
+static int create_ogg_client_data (source_t *source, client_t *client)
+{
+    struct client_vorbis *client_data = calloc (1, sizeof (struct client_vorbis));
+    int ret = -1;
+
+    if (client_data)
+    {
+        client_data->headers_sent = 1;
+        client->format_data = client_data;
+        client->free_client_data = free_ogg_client_data;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void free_ogg_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+
+static int send_ogg_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)
+    {
+        client_data->header_page = headers;
+        client_data->pos = 0;
+        client_data->headers_sent = 0;
+    }
+    refbuf = client_data->header_page;
+    while (refbuf)
+    {
+        char *data = refbuf->data + client_data->pos;
+        unsigned len = refbuf->len - client_data->pos;
+        int ret;
+
+        ret = client_send_bytes (client, data, len);
+        if (ret > 0)
+           written += ret;
+        if (ret < (int)len)
+            return written ? written : -1;
+        client_data->pos += ret;
+        if (client_data->pos == refbuf->len)
+        {
+            refbuf = refbuf->next;
+            client_data->header_page = refbuf;
+            client_data->pos = 0;
+        }
+    }
+    client_data->headers_sent = 1;
+    client_data->headers = headers;
+    return written;
+}
+
+
+static int 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;
+    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)
+        {
+            ret = send_ogg_headers (client, refbuf->associated);
+            if (client_data->headers_sent == 0)
+                break;
+            written += ret;
+        }
+        ret = client_send_bytes (client, buf, len);
+
+        if (ret > 0)
+            client->pos += ret;
+
+        if (ret < (int)len)
+            break;
+        written += ret;
+        /* we have now written the page(s) */
+        ret = 0;
+    } while (0);
+
+    if (ret > 0)
+       written += ret;
+    return written ? written : -1;
+}
+
+
+static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
+{
+    int ret = 1;
+
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+
+    if (ogg_info->file_headers != refbuf->associated)
+    {
+        refbuf_t *header = refbuf->associated;
+        while (header)
+        {
+            if (write_ogg_data (source, header) == 0)
+                return;
+            header = header->next;
+        }
+        ogg_info->file_headers = refbuf->associated;
+    }
+    write_ogg_data (source, refbuf);
+}
+
+

Added: icecast/branches/icecast-kh/src/format_ogg.h
===================================================================
--- icecast/branches/icecast-kh/src/format_ogg.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_ogg.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,24 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_ogg.h
+**
+** vorbis format plugin header
+**
+*/
+#ifndef __FORMAT_OGG_H__
+#define __FORMAT_OGG_H__
+
+int format_ogg_get_plugin (source_t *source);
+#define format_ogg_initialise() do{}while(0)
+
+#endif  /* __FORMAT_OGG_H__ */

Added: icecast/branches/icecast-kh/src/format_vorbis.c
===================================================================
--- icecast/branches/icecast-kh/src/format_vorbis.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_vorbis.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,797 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_vorbis.c
+**
+** format plugin for vorbis
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+
+#define CATMODULE "format-vorbis"
+#include "logging.h"
+
+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, out_os;
+    vorbis_info vi;
+    vorbis_comment vc;
+
+    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;
+
+struct client_vorbis
+{
+    refbuf_t *headers;
+    refbuf_t *header_page;
+    unsigned pos;
+    int headers_sent;
+};
+
+
+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_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->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 0;
+}
+
+void format_vorbis_free_plugin (format_plugin_t *plugin)
+{
+    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);
+    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;
+    }
+}
+
+/* handle the headers we want going to the clients */
+static int process_vorbis_headers (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;
+
+    /* 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;
+}
+
+
+/* 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 *tag;
+    ogg_packet header;
+    vstate_t *source_vorbis = source->format->_state;
+
+    /* 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--;
+    }
+
+    /* we have all headers */
+
+    /* 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);
+
+    tag = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
+    if (tag == NULL)
+        tag = "unknown";
+    stats_event (source->mount, "artist", tag);
+
+    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;
+
+    /* printf ("finished with incoming header packets\n"); */
+    source_vorbis->process_packet = process_vorbis_headers;
+
+    return 1;
+}
+
+
+
+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);
+
+    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;
+}
+
+
+static void update_comments (source_t *source)
+{
+    vstate_t *source_vorbis = source->format->_state;
+    vorbis_comment vc;
+    ogg_packet header;
+
+    initiate_flush (source_vorbis);
+
+    /* 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;
+}
+
+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;
+    }
+}
+
+
+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;
+}
+
+static void free_vorbis_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+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;
+}
+
+
+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 int write_vorbis_data (struct source_tag *source, refbuf_t *refbuf)
+{
+    int ret = 1;
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void write_vorbis_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    vstate_t *source_vorbis = source->format->_state;
+
+    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);
+}
+
+
+void format_ogg_initialise (void)
+{
+    next_rebuild_serialno = 1;
+    thread_mutex_create (&serial_lock);
+}
+

Added: icecast/branches/icecast-kh/src/format_vorbis.h
===================================================================
--- icecast/branches/icecast-kh/src/format_vorbis.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/format_vorbis.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,24 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_vorbis.h
+**
+** vorbis format plugin header
+**
+*/
+#ifndef __FORMAT_VORBIS_H__
+#define __FORMAT_VORBIS_H__
+
+int format_ogg_get_plugin (source_t *source);
+void format_ogg_initialise (void);
+
+#endif  /* __FORMAT_VORBIS_H__ */

Added: icecast/branches/icecast-kh/src/fserve.c
===================================================================
--- icecast/branches/icecast-kh/src/fserve.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/fserve.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,522 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "format.h"
+#include "logging.h"
+#include "cfgfile.h"
+#include "util.h"
+
+#include "fserve.h"
+
+#undef CATMODULE
+#define CATMODULE "fserve"
+
+#define BUFSIZE 4096
+
+#ifdef _WIN32
+#define MIMETYPESFILE ".\\mime.types"
+#else
+#define MIMETYPESFILE "/etc/mime.types"
+#endif
+
+static avl_tree *client_tree;
+static avl_tree *pending_tree;
+static avl_tree *mimetypes = NULL;
+
+static cond_t fserv_cond;
+static thread_type *fserv_thread;
+static int run_fserv;
+static int fserve_clients;
+static int client_tree_changed=0;
+
+#ifdef HAVE_POLL
+static struct pollfd *ufds = NULL;
+static int ufdssize = 0;
+#else
+static fd_set fds;
+static int fd_max = 0;
+#endif
+
+typedef struct {
+    char *ext;
+    char *type;
+} mime_type;
+
+/* avl tree helper */
+static int _compare_clients(void *compare_arg, void *a, void *b);
+static int _remove_client(void *key);
+static int _free_client(void *key);
+static int _delete_mapping(void *mapping);
+static void *fserv_thread_function(void *arg);
+static void create_mime_mappings(char *fn);
+
+void fserve_initialize(void)
+{
+    ice_config_t *config = config_get_config();
+    int serve = config->fileserve;
+
+    config_release_config();
+
+    if(!serve)
+        return;
+
+    create_mime_mappings(MIMETYPESFILE);
+
+    client_tree = avl_tree_new(_compare_clients, NULL);
+    pending_tree = avl_tree_new(_compare_clients, NULL);
+    thread_cond_create(&fserv_cond);
+
+    run_fserv = 1;
+
+    fserv_thread = thread_create("File Serving Thread",
+            fserv_thread_function, NULL, THREAD_ATTACHED);
+}
+
+void fserve_shutdown(void)
+{
+    ice_config_t *config = config_get_config();
+    int serve = config->fileserve;
+
+    config_release_config();
+
+    if(!serve)
+        return;
+
+    if(!run_fserv)
+        return;
+
+    avl_tree_free(mimetypes, _delete_mapping);
+
+    run_fserv = 0;
+    thread_cond_signal(&fserv_cond);
+    thread_join(fserv_thread);
+
+    thread_cond_destroy(&fserv_cond);
+    avl_tree_free(client_tree, _free_client);
+    avl_tree_free(pending_tree, _free_client);
+}
+
+static void wait_for_fds() {
+    avl_node *client_node;
+    fserve_t *client;
+    int i;
+
+    while(run_fserv) {
+#ifdef HAVE_POLL
+        if(client_tree_changed) {
+            client_tree_changed = 0;
+            i = 0;
+            ufdssize = fserve_clients;
+            ufds = realloc(ufds, ufdssize * sizeof(struct pollfd));
+            avl_tree_rlock(client_tree);
+            client_node = avl_get_first(client_tree);
+            while(client_node) {
+                client = client_node->key;
+                ufds[i].fd = client->client->con->sock;
+                ufds[i].events = POLLOUT;
+                client_node = avl_get_next(client_node);
+            }
+            avl_tree_unlock(client_tree);
+        }
+
+        if(poll(ufds, ufdssize, 200) > 0)
+            return;
+#else
+        struct timeval tv;
+        fd_set realfds;
+        tv.tv_sec = 0;
+        tv.tv_usec = 200000;
+        if(client_tree_changed) {
+            client_tree_changed = 0;
+            i=0;
+            FD_ZERO(&fds);
+            fd_max = 0;
+            avl_tree_rlock(client_tree);
+            client_node = avl_get_first(client_tree);
+            while(client_node) {
+                client = client_node->key;
+                FD_SET(client->client->con->sock, &fds);
+                if(client->client->con->sock > fd_max)
+                    fd_max = client->client->con->sock;
+                client_node = avl_get_next(client_node);
+            }
+            avl_tree_unlock(client_tree);
+        }
+
+        memcpy(&realfds, &fds, sizeof(fd_set));
+        if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
+            return;
+#endif
+        else {
+            avl_tree_rlock(pending_tree);
+            client_node = avl_get_first(pending_tree);
+            avl_tree_unlock(pending_tree);
+            if(client_node)
+                return;
+        }
+    }
+}
+
+static void *fserv_thread_function(void *arg)
+{
+    avl_node *client_node, *pending_node;
+    fserve_t *client;
+    int sbytes, bytes;
+
+    while (run_fserv) {
+        avl_tree_rlock(client_tree);
+
+        client_node = avl_get_first(client_tree);
+        if(!client_node) {
+            avl_tree_rlock(pending_tree);
+            pending_node = avl_get_first(pending_tree);
+            if(!pending_node) {
+                /* There are no current clients. Wait until there are... */
+                avl_tree_unlock(pending_tree);
+                avl_tree_unlock(client_tree);
+                thread_cond_wait(&fserv_cond);
+                continue;
+            }
+            avl_tree_unlock(pending_tree);
+        }
+
+        /* This isn't hugely efficient, but it'll do for now */
+        avl_tree_unlock(client_tree);
+        wait_for_fds();
+
+        avl_tree_rlock(client_tree);
+        client_node = avl_get_first(client_tree);
+
+        while(client_node) {
+            avl_node_wlock(client_node);
+
+            client = (fserve_t *)client_node->key;
+
+            if(client->offset >= client->datasize) {
+                /* Grab a new chunk */
+                bytes = fread(client->buf, 1, BUFSIZE, client->file);
+                if(bytes <= 0) {
+                    client->client->con->error = 1;
+                    avl_node_unlock(client_node);
+                    client_node = avl_get_next(client_node);
+                    continue;
+                }
+                client->offset = 0;
+                client->datasize = bytes;
+            }
+
+            /* Now try and send current chunk. */
+            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) {
+                client->offset += sbytes;
+            }
+
+            avl_node_unlock(client_node);
+            client_node = avl_get_next(client_node);
+        }
+
+        avl_tree_unlock(client_tree);
+
+        /* Now we need a write lock instead, to delete done clients. */
+        avl_tree_wlock(client_tree);
+
+        client_node = avl_get_first(client_tree);
+        while(client_node) {
+            client = (fserve_t *)client_node->key;
+            if(client->client->con->error) {
+                fserve_clients--;
+                client_node = avl_get_next(client_node);
+                avl_delete(client_tree, (void *)client, _free_client);
+                client_tree_changed = 1;
+                continue;
+            }
+            client_node = avl_get_next(client_node);
+        }
+
+        avl_tree_wlock(pending_tree);
+
+        /* And now insert new clients. */
+        client_node = avl_get_first(pending_tree);
+        while(client_node) {
+            client = (fserve_t *)client_node->key;
+            avl_insert(client_tree, client);
+            client_tree_changed = 1;
+            fserve_clients++;
+            stats_event_inc(NULL, "clients");
+            client_node = avl_get_next(client_node);
+
+        }
+
+        /* clear pending */
+        while(avl_get_first(pending_tree)) {
+            avl_delete(pending_tree, avl_get_first(pending_tree)->key,
+                    _remove_client);
+        }
+
+        avl_tree_unlock(pending_tree);
+        avl_tree_unlock(client_tree);
+    }
+
+    /* Shutdown path */
+
+    avl_tree_wlock(pending_tree);
+    while(avl_get_first(pending_tree))
+        avl_delete(pending_tree, avl_get_first(pending_tree)->key,
+                _free_client);
+    avl_tree_unlock(pending_tree);
+
+    avl_tree_wlock(client_tree);
+    while(avl_get_first(client_tree))
+        avl_delete(client_tree, avl_get_first(client_tree)->key,
+                _free_client);
+    avl_tree_unlock(client_tree);
+
+    thread_exit(0);
+    return NULL;
+}
+
+static char *fserve_content_type(char *path)
+{
+    char *ext = util_get_extension(path);
+    mime_type exttype = {ext, NULL};
+    void *result;
+
+    if (!avl_get_by_key (mimetypes, &exttype, &result))
+    {
+        mime_type *mime = result;
+        return mime->type;
+    }
+    else {
+        /* Fallbacks for a few basic ones */
+        if(!strcmp(ext, "ogg"))
+            return "application/ogg";
+        else if(!strcmp(ext, "mp3"))
+            return "audio/mpeg";
+        else if(!strcmp(ext, "html"))
+            return "text/html";
+        else if(!strcmp(ext, "txt"))
+            return "text/plain";
+        else
+            return "application/octet-stream";
+    }
+}
+
+static void fserve_client_destroy(fserve_t *client)
+{
+    if(client) {
+        if(client->buf)
+            free(client->buf);
+        if(client->file)
+            fclose(client->file);
+
+        if(client->client)
+            client_destroy(client->client);
+        free(client);
+    }
+}
+
+int fserve_client_create(client_t *httpclient, char *path)
+{
+    fserve_t *client = calloc(1, sizeof(fserve_t));
+    int bytes;
+    int client_limit;
+    ice_config_t *config = config_get_config();
+
+    client_limit = config->client_limit;
+    config_release_config();
+
+    client->file = fopen(path, "rb");
+    if(!client->file) {
+        client_send_404(httpclient, "File not readable");
+        return -1;
+    }
+
+    client->client = httpclient;
+    client->offset = 0;
+    client->datasize = 0;
+    client->buf = malloc(BUFSIZE);
+
+    global_lock();
+    if(global.clients >= client_limit) {
+        httpclient->respcode = 504;
+        bytes = sock_write(httpclient->con->sock,
+                "HTTP/1.0 504 Server Full\r\n"
+                "Content-Type: text/html\r\n\r\n"
+                "<b>Server is full, try again later.</b>\r\n");
+        if(bytes > 0) httpclient->con->sent_bytes = bytes;
+        fserve_client_destroy(client);
+        global_unlock();
+        return -1;
+    }
+    global.clients++;
+    global_unlock();
+
+    httpclient->respcode = 200;
+    bytes = sock_write(httpclient->con->sock,
+            "HTTP/1.0 200 OK\r\n"
+            "Content-Type: %s\r\n\r\n",
+            fserve_content_type(path));
+    if(bytes > 0) httpclient->con->sent_bytes = bytes;
+
+    sock_set_blocking(client->client->con->sock, SOCK_NONBLOCK);
+    sock_set_nodelay(client->client->con->sock);
+
+    avl_tree_wlock(pending_tree);
+    avl_insert(pending_tree, client);
+    avl_tree_unlock(pending_tree);
+
+    thread_cond_signal(&fserv_cond);
+
+    return 0;
+}
+
+static int _compare_clients(void *compare_arg, void *a, void *b)
+{
+    fserve_t *clienta = (fserve_t *)a;
+    fserve_t *clientb = (fserve_t *)b;
+
+    connection_t *cona = clienta->client->con;
+    connection_t *conb = clientb->client->con;
+
+    if (cona->id < conb->id) return -1;
+    if (cona->id > conb->id) return 1;
+
+    return 0;
+}
+
+static int _remove_client(void *key)
+{
+    return 1;
+}
+
+static int _free_client(void *key)
+{
+    fserve_t *client = (fserve_t *)key;
+
+    fserve_client_destroy(client);
+    global_lock();
+    global.clients--;
+    global_unlock();
+    stats_event_dec(NULL, "clients");
+
+
+    return 1;
+}
+
+static int _delete_mapping(void *mapping) {
+    mime_type *map = mapping;
+    free(map->ext);
+    free(map->type);
+    free(map);
+
+    return 1;
+}
+
+static int _compare_mappings(void *arg, void *a, void *b)
+{
+    return strcmp(
+            ((mime_type *)a)->ext,
+            ((mime_type *)b)->ext);
+}
+
+static void create_mime_mappings(char *fn) {
+    FILE *mimefile = fopen(fn, "r");
+    char line[4096];
+    char *type, *ext, *cur;
+    mime_type *mapping;
+
+    mimetypes = avl_tree_new(_compare_mappings, NULL);
+
+    if(!mimefile)
+        return;
+
+    while(fgets(line, 4096, mimefile))
+    {
+        line[4095] = 0;
+
+        if(*line == 0 || *line == '#')
+            continue;
+
+        type = line;
+
+        cur = line;
+
+        while(*cur != ' ' && *cur != '\t' && *cur)
+            cur++;
+        if(*cur == 0)
+            continue;
+
+        *cur++ = 0;
+
+        while(1) {
+            while(*cur == ' ' || *cur == '\t')
+                cur++;
+            if(*cur == 0)
+                break;
+
+            ext = cur;
+            while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
+                cur++;
+            *cur++ = 0;
+            if(*ext)
+            {
+                void *tmp;
+                /* Add a new extension->type mapping */
+                mapping = malloc(sizeof(mime_type));
+                mapping->ext = strdup(ext);
+                mapping->type = strdup(type);
+                if(!avl_get_by_key(mimetypes, mapping, &tmp))
+                    avl_delete(mimetypes, mapping, _delete_mapping);
+                avl_insert(mimetypes, mapping);
+            }
+        }
+    }
+
+    fclose(mimefile);
+}
+

Added: icecast/branches/icecast-kh/src/fserve.h
===================================================================
--- icecast/branches/icecast-kh/src/fserve.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/fserve.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,35 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __FSERVE_H__
+#define __FSERVE_H__
+
+#include <stdio.h>
+
+typedef struct
+{
+    client_t *client;
+
+    FILE *file;
+    int offset;
+    int datasize;
+    unsigned char *buf;
+} fserve_t;
+
+void fserve_initialize(void);
+void fserve_shutdown(void);
+int fserve_client_create(client_t *httpclient, char *path);
+
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/global.c
===================================================================
--- icecast/branches/icecast-kh/src/global.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/global.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,63 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "source.h"
+#include "format.h"
+
+#include "global.h"
+
+ice_global_t global;
+
+static mutex_t _global_mutex;
+
+void global_initialize(void)
+{
+    memset(global.serversock, 0, sizeof(int)*MAX_LISTEN_SOCKETS);
+    global.server_sockets = 0;
+    global.relays = NULL;
+    global.master_relays = NULL;
+    global.running = 0;
+    global.clients = 0;
+    global.sources = 0;
+    global.source_tree = avl_tree_new(source_compare_sources, NULL);
+    thread_mutex_create("global", &_global_mutex);
+}
+
+void global_shutdown(void)
+{
+    thread_mutex_destroy(&_global_mutex);
+    avl_tree_free(global.source_tree, NULL);
+}
+
+void global_lock(void)
+{
+    thread_mutex_lock(&_global_mutex);
+}
+
+void global_unlock(void)
+{
+    thread_mutex_unlock(&_global_mutex);
+}

Added: icecast/branches/icecast-kh/src/global.h
===================================================================
--- icecast/branches/icecast-kh/src/global.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/global.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,55 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __GLOBAL_H__
+#define __GLOBAL_H__
+
+#define ICE_LISTEN_QUEUE 5
+
+#define ICE_RUNNING 1
+#define ICE_HALTING 2
+
+#define ICECAST_VERSION_STRING "Icecast " PACKAGE_VERSION
+
+#define MAX_LISTEN_SOCKETS 10
+
+#include "thread/thread.h"
+#include "slave.h"
+
+typedef struct ice_global_tag
+{
+    int serversock[MAX_LISTEN_SOCKETS];
+    int server_sockets;
+
+    int running;
+
+    int sources;
+    int clients;
+    int schedule_config_reread;
+
+    avl_tree *source_tree;
+    /* for locally defined relays */
+    struct _relay_server *relays;
+    /* relays retrieved from master */
+    struct _relay_server *master_relays;
+
+    cond_t shutdown_cond;
+} ice_global_t;
+
+extern ice_global_t global;
+
+void global_initialize(void);
+void global_shutdown(void);
+void global_lock(void);
+void global_unlock(void);
+
+#endif  /* __GLOBAL_H__ */

Added: icecast/branches/icecast-kh/src/logging.c
===================================================================
--- icecast/branches/icecast-kh/src/logging.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/logging.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,122 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include "thread/thread.h"
+#include "httpp/httpp.h"
+
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+
+#include "os.h"
+#include "cfgfile.h"
+#include "logging.h"
+#include "util.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+/* the global log descriptors */
+int errorlog = 0;
+int accesslog = 0;
+
+/*
+** ADDR USER AUTH DATE REQUEST CODE BYTES REFERER AGENT [TIME]
+**
+** ADDR = client->con->ip
+** USER = -
+**      we should do this for real once we support authentication
+** AUTH = -
+** DATE = _make_date(client->con->con_time)
+** REQUEST = build from client->parser
+** CODE = client->respcode
+** BYTES = client->con->sent_bytes
+** REFERER = get from client->parser
+** AGENT = get from client->parser
+** TIME = timing_get_time() - client->con->con_time
+*/
+void logging_access(client_t *client)
+{
+    char datebuf[128];
+    char reqbuf[1024];
+    struct tm thetime;
+    time_t now;
+    time_t stayed;
+    char *referrer, *user_agent;
+
+    now = time(NULL);
+
+    /* build the data */
+    localtime_r (&now, &thetime);
+    strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
+
+    /* build the request */
+    snprintf (reqbuf, sizeof(reqbuf), "%s %s %s/%s",
+            httpp_getvar (client->parser, HTTPP_VAR_REQ_TYPE),
+            httpp_getvar (client->parser, HTTPP_VAR_URI),
+            httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL),
+            httpp_getvar (client->parser, HTTPP_VAR_VERSION));
+
+    stayed = now - client->con->con_time;
+
+    referrer = httpp_getvar (client->parser, "referer");
+    if (referrer == NULL)
+        referrer = "-";
+
+    user_agent = httpp_getvar (client->parser, "user-agent");
+    if (user_agent == NULL)
+        user_agent = "-";
+
+#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, (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
+}
+
+
+
+void restart_logging ()
+{
+    ice_config_t *config = config_get_config_unlocked();
+
+    if (strcmp (config->error_log, "-"))
+    {
+        char fn_error[FILENAME_MAX];
+        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
+        log_set_filename (errorlog, fn_error);
+        log_set_level (errorlog, config->loglevel);
+        log_reopen (errorlog);
+    }
+
+    if (strcmp (config->access_log, "-"))
+    {
+        char fn_error[FILENAME_MAX];
+        snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->access_log);
+        log_set_filename (accesslog, fn_error);
+        log_reopen (accesslog);
+    }
+}

Added: icecast/branches/icecast-kh/src/logging.h
===================================================================
--- icecast/branches/icecast-kh/src/logging.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/logging.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,100 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+
+#include "log/log.h"
+
+/* declare the global log descriptors */
+
+extern int errorlog;
+extern int accesslog;
+
+/* these are all ERRORx and WARNx where _x_ is the number of parameters
+** it takes.  it turns out most other copmilers don't have support for
+** varargs macros.  that totally sucks, but this is still pretty easy.
+**
+** feel free to add more here if needed.
+*/
+
+#ifdef _WIN32
+#define __FUNCTION__ strrchr (__FILE__, '\\') ? strrchr (__FILE__, '\\') + 1 : __FILE__
+#endif
+
+#define ERROR0(y) log_write(errorlog, 1, CATMODULE "/", __FUNCTION__, y)
+#define ERROR1(y, a) log_write(errorlog, 1, CATMODULE "/", __FUNCTION__, y, a)
+#define ERROR2(y, a, b) log_write(errorlog, 1, CATMODULE "/", __FUNCTION__, y, a, b)
+#define ERROR3(y, a, b, c) log_write(errorlog, 1, CATMODULE "/", __FUNCTION__, y, a, b, c)
+
+#define WARN0(y) log_write(errorlog, 2, CATMODULE "/", __FUNCTION__, y)
+#define WARN1(y, a) log_write(errorlog, 2, CATMODULE "/", __FUNCTION__, y, a)
+#define WARN2(y, a, b) log_write(errorlog, 2, CATMODULE "/", __FUNCTION__, y, a, b)
+#define WARN3(y, a, b, c) log_write(errorlog, 2, CATMODULE "/", __FUNCTION__, y, a, b, c)
+
+#define INFO0(y) log_write(errorlog, 3, CATMODULE "/", __FUNCTION__, y)
+#define INFO1(y, a) log_write(errorlog, 3, CATMODULE "/", __FUNCTION__, y, a)
+#define INFO2(y, a, b) log_write(errorlog, 3, CATMODULE "/", __FUNCTION__, y, a, b)
+#define INFO3(y, a, b, c) log_write(errorlog, 3, CATMODULE "/", __FUNCTION__, y, a, b, c)
+
+#define DEBUG0(y) log_write(errorlog, 4, CATMODULE "/", __FUNCTION__, y)
+#define DEBUG1(y, a) log_write(errorlog, 4, CATMODULE "/", __FUNCTION__, y, a)
+#define DEBUG2(y, a, b) log_write(errorlog, 4, CATMODULE "/", __FUNCTION__, y, a, b)
+#define DEBUG3(y, a, b, c) log_write(errorlog, 4, CATMODULE "/", __FUNCTION__, y, a, b, c)
+#define DEBUG4(y, a, b, c, d) log_write(errorlog, 4, CATMODULE "/", __FUNCTION__, y, a, b, c, d)
+
+/* CATMODULE is the category or module that logging messages come from.
+** we set one here in cause someone forgets in the .c file.
+*/
+/*#define CATMODULE "unknown"
+ */
+
+/* this is the logging call to write entries to the access_log
+** the combined log format is:
+** ADDR USER AUTH DATE REQUEST CODE BYTES REFERER AGENT [TIME]
+** ADDR = ip address of client
+** USER = username if authenticated
+** AUTH = auth type, not used, and set to "-"
+** DATE = date in "[30/Apr/2001:01:25:34 -0700]" format
+** REQUEST = request, ie "GET /live.ogg HTTP/1.0"
+** CODE = response code, ie, 200 or 404
+** BYTES = total bytes of data sent (other than headers)
+** REFERER = the refering URL
+** AGENT = the user agent
+**
+** for icecast, we add on extra field at the end, which will be
+** ignored by normal log parsers
+**
+** TIME = seconds that the connection lasted
+**
+** this allows you to get bitrates (BYTES / TIME)
+** and figure out exact times of connections
+**
+** it should be noted also that events are sent on client disconnect,
+** so the DATE is the timestamp of disconnection.  DATE - TIME is the
+** time of connection.
+*/
+
+#define LOGGING_FORMAT_CLF "%d/%b/%Y:%H:%M:%S %z"
+
+void logging_access(client_t *client);
+void restart_logging (void);
+
+#endif  /* __LOGGING_H__ */
+
+
+
+
+
+
+
+

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

Added: icecast/branches/icecast-kh/src/md5.c
===================================================================
--- icecast/branches/icecast-kh/src/md5.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/md5.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,277 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/*
+ * md5.c
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* Modified for icecast by Mike Smith <msmith at xiph.org>, mostly changing header
+ * and type definitions
+ */
+
+#include "config.h"
+#include "compat.h"
+#include "md5.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static void MD5Transform(uint32_t buf[4], uint32_t const in[HASH_LEN]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do
+	{
+		t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+			((unsigned) buf[1] << 8 | buf[0]);
+		*(uint32_t *) buf = t;
+		buf += 4;
+    }
+	while (--longs);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf,
+        unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+		ctx->bits[1]++;
+
+	/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;
+	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+    if (t)
+	{
+		unsigned char *p = (unsigned char *) ctx->in + t;
+		t = 64 - t;
+		if (len < t)
+		{
+			memcpy(p, buf, len);
+			return;
+		}
+
+		memcpy(p, buf, t);
+		byteReverse(ctx->in, HASH_LEN);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+		buf += t;
+		len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64)
+	{
+		memcpy(ctx->in, buf, 64);
+		byteReverse(ctx->in, HASH_LEN);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+		buf += 64;
+		len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[HASH_LEN], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+	 always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8)
+	{
+		/* Two lots of padding:  Pad the first block to 64 bytes */
+		memset(p, 0, count);
+		byteReverse(ctx->in, HASH_LEN);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+		/* Now fill the next block with 56 bytes */
+		memset(ctx->in, 0, 56);
+    }
+	else
+	{
+		/* Pad block to 56 bytes */
+		memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+    ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, HASH_LEN);
+    memset(ctx, 0, sizeof(ctx));
+	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+# define F1(x, y, z) (z ^ (x & (y ^ z)))
+# define F2(x, y, z) F1(z, x, y)
+# define F3(x, y, z) (x ^ y ^ z)
+# 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) 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
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32_t buf[4], uint32_t const in[HASH_LEN])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+

Added: icecast/branches/icecast-kh/src/md5.h
===================================================================
--- icecast/branches/icecast-kh/src/md5.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/md5.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,36 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __MD5_H__
+#define __MD5_H__
+
+#include "config.h"
+#include "compat.h"
+
+#define HASH_LEN     16
+
+struct MD5Context
+{
+    uint32_t     buf[4];
+    uint32_t     bits[2];
+    unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+        unsigned len);
+void MD5Final(unsigned char digest[HASH_LEN], struct MD5Context *context);
+
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/os.h
===================================================================
--- icecast/branches/icecast-kh/src/os.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/os.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,30 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __OS_H__
+#define __OS_H__
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#define PATH_SEPARATOR "\\"
+#define size_t int
+#define ssize_t int
+#else
+#define PATH_SEPARATOR "/"
+#endif
+
+#endif  /* __OS_H__ */

Added: icecast/branches/icecast-kh/src/refbuf.c
===================================================================
--- icecast/branches/icecast-kh/src/refbuf.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/refbuf.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,80 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* refbuf.c
+**
+** reference counting buffer implementation
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "refbuf.h"
+
+void refbuf_initialize(void)
+{
+}
+
+void refbuf_shutdown(void)
+{
+}
+
+
+void refbuf_free (refbuf_t *refbuf)
+{
+    free (refbuf->data);
+    free (refbuf);
+}
+
+
+refbuf_t *refbuf_new(unsigned long size)
+{
+    refbuf_t *refbuf;
+
+    refbuf = malloc (sizeof(refbuf_t));
+    if (refbuf)
+    {
+        refbuf->data = NULL;
+        if (size && (refbuf->data = malloc (size)) == NULL)
+        {
+            free (refbuf);
+            return NULL;
+        }
+        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 refbuf;
+}
+
+
+void refbuf_release(refbuf_t *refbuf)
+{
+    while (refbuf->associated)
+    {
+        refbuf_t *ref = refbuf->associated;
+        refbuf->associated = ref->next;
+        refbuf->refbuf_associated_release (ref);
+    }
+    refbuf->refbuf_release (refbuf);
+}
+

Added: icecast/branches/icecast-kh/src/refbuf.h
===================================================================
--- icecast/branches/icecast-kh/src/refbuf.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/refbuf.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,51 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* refbuf.h
+**
+** reference counting data buffer
+**
+*/
+#ifndef __REFBUF_H__
+#define __REFBUF_H__
+
+typedef struct _refbuf_tag
+{
+    char *data;
+    unsigned 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 *);
+
+    struct _refbuf_tag *next;
+} refbuf_t;
+
+void refbuf_initialize(void);
+void refbuf_shutdown(void);
+
+void refbuf_free (refbuf_t *refbuf);
+refbuf_t *refbuf_new(unsigned long size);
+void refbuf_release(refbuf_t *self);
+
+
+#endif  /* __REFBUF_H__ */
+
+
+
+
+
+
+
+

Added: icecast/branches/icecast-kh/src/sighandler.c
===================================================================
--- icecast/branches/icecast-kh/src/sighandler.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/sighandler.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,70 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+
+#include "global.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "logging.h"
+#include "event.h"
+
+#define CATMODULE "sighandler"
+
+#ifndef _WIN32
+void _sig_hup(int signo);
+void _sig_die(int signo);
+void _sig_ignore(int signo);
+#endif
+
+void sighandler_initialize(void)
+{
+#ifndef _WIN32
+    signal(SIGHUP, _sig_hup);
+    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)
+{
+    global . schedule_config_reread = 1;
+    /* some OSes require us to reattach the signal handler */
+    signal(SIGHUP, _sig_hup);
+}
+
+void _sig_die(int signo)
+{
+    INFO1("Caught signal %d, shutting down...", signo);
+
+    /* inform the server to start shutting down */
+    global.running = ICE_HALTING;
+}
+
+#endif

Added: icecast/branches/icecast-kh/src/sighandler.h
===================================================================
--- icecast/branches/icecast-kh/src/sighandler.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/sighandler.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,21 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __SIGHANDLER_H__
+#define __SIGHANDLER_H__
+
+extern int schedule_config_reread;
+
+void sighandler_initialize(void);
+
+
+#endif  /* __SIGHANDLER_H__ */

Added: icecast/branches/icecast-kh/src/slave.c
===================================================================
--- icecast/branches/icecast-kh/src/slave.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/slave.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,488 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* slave.c
+ * by Ciaran Anscomb <ciaran.anscomb at 6809.org.uk>
+ *
+ * Periodically requests a list of streams from a master server
+ * and creates source threads for any it doesn't already have.
+ * */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#else
+#include <winsock2.h>
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#include "os.h"
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "net/sock.h"
+#include "httpp/httpp.h"
+
+#include "cfgfile.h"
+#include "global.h"
+#include "util.h"
+#include "connection.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "source.h"
+#include "format.h"
+
+#define CATMODULE "slave"
+
+static void *_slave_thread(void *arg);
+thread_type *_slave_thread_id;
+static int slave_running = 0;
+static unsigned max_interval = 0;
+
+relay_server *relay_free (relay_server *relay)
+{
+    relay_server *next = relay->next;
+    DEBUG1("freeing relay %s", relay->localmount);
+    if (relay->source)
+       source_free_source (relay->source);
+    xmlFree (relay->server);
+    xmlFree (relay->mount);
+    xmlFree (relay->localmount);
+    xmlFree (relay->username);
+    xmlFree (relay->password);
+    xmlFree (relay);
+    return next;
+}
+
+
+relay_server *relay_copy (relay_server *r)
+{
+    relay_server *copy = calloc (1, sizeof (relay_server));
+
+    if (copy)
+    {
+        copy->server = xmlStrdup (r->server);
+        copy->mount = xmlStrdup (r->mount);
+        copy->localmount = xmlStrdup (r->localmount);
+        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;
+}
+
+
+static void *_relay_thread (void *arg)
+{
+    relay_server *relay = arg;
+
+    relay->running = 1;
+    stats_event_inc(NULL, "source_relay_connections");
+
+    source_main (relay->source);
+
+    relay->running = 0;
+    if (relay->cleanup)
+        relay_free (relay);
+
+    return NULL;
+}
+
+
+void slave_recheck (void)
+{
+    max_interval = 0;
+}
+
+
+void slave_initialize(void)
+{
+    if (slave_running)
+        return;
+
+    slave_running = 1;
+    _slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
+}
+
+
+void slave_shutdown(void)
+{
+    relay_server *relay;
+
+    if (!slave_running)
+        return;
+    slave_running = 0;
+    thread_join (_slave_thread_id);
+
+    relay = global.relays;
+    while (relay)
+        relay = relay_free (relay);
+    global.relays = NULL;
+
+    relay = global.master_relays;
+    while (relay)
+        relay = relay_free (relay);
+    global.master_relays = NULL;
+}
+
+
+/* This does the actual connection for a relay. A thread is
+ * started off if a connection can be acquired
+ */
+static void start_relay_stream (relay_server *relay)
+{
+    sock_t streamsock = SOCK_ERROR;
+    source_t *src = relay->source;
+    http_parser_t *parser = NULL;
+    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)
+        {
+            WARN3("Failed to relay stream from master server, couldn't connect to http://%s:%d%s",
+                    relay->server, relay->port, relay->mount);
+            break;
+        }
+        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
+         * we don't send in this header then relay will not have mp3 metadata.
+         */
+        sock_write(streamsock, "GET %s HTTP/1.0\r\n"
+                "User-Agent: " ICECAST_VERSION_STRING "\r\n"
+                "%s"
+                "%s"
+                "\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)
+        {
+            WARN0("Header read failed");
+            break;
+        }
+        parser = httpp_create_parser();
+        httpp_initialize (parser, NULL);
+        if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
+        {
+            ERROR0("Error parsing relay request");
+            break;
+        }
+        if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
+        {
+            ERROR1("Error from relay request: %s", httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
+            break;
+        }
+        src->parser = parser;
+        src->con = con;
+        if (connection_complete_source (src) < 0)
+        {
+            DEBUG0("Failed to complete source initialisation");
+            break;
+        }
+        thread_create ("Relay Thread", _relay_thread, relay, THREAD_DETACHED);
+
+        return;
+    } while (0);
+
+    if (con == NULL && streamsock != SOCK_ERROR)
+        sock_close (streamsock);
+    if (con)
+        connection_close (con);
+    src->con = NULL;
+    if (parser)
+        httpp_destroy (parser);
+    src->parser = NULL;
+    source_clear_source (relay->source);
+}
+
+
+/* wrapper for starting the provided relay stream */
+static void check_relay_stream (relay_server *relay)
+{
+    if (relay->source == NULL)
+    {
+        /* 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)
+    {
+        start_relay_stream (relay);
+    }
+}
+
+
+/* go through updated looking for relays that are different configured. The
+ * returned list contains relays that should be kept running, current contains
+ * the list of relays to shutdown
+ */
+static relay_server *
+update_relay_set (relay_server **current, relay_server *updated)
+{
+    relay_server *relay = updated;
+    relay_server *existing_relay, **existing_p;
+    relay_server *new_list = NULL;
+
+    while (relay)
+    {
+         existing_relay = *current;
+         existing_p = current;
+
+         while (existing_relay)
+         {
+             if (strcmp (relay->localmount, existing_relay->localmount) == 0)
+                 break;
+             existing_p = &existing_relay->next;
+             existing_relay = existing_relay->next;
+         }
+         if (existing_relay == NULL)
+         {
+             /* new one, copy and insert */
+             existing_relay = relay_copy (relay);
+         }
+         else
+         {
+             *existing_p = existing_relay->next;
+         }
+         existing_relay->next = new_list;
+         new_list = existing_relay;
+         relay = relay->next;
+    }
+    return new_list;
+}
+
+
+/* update the relay_list with entries from new_relay_list. Any new relays
+ * are added to the list, and any not listed in the provided new_relay_list
+ * get marked for shutting down, just in case they are not shutting down by
+ * themselves
+ */
+static void
+update_relays (relay_server **relay_list, relay_server *new_relay_list)
+{
+    relay_server *relay, *current;
+
+    current = update_relay_set (relay_list, new_relay_list);
+
+    /* ok whats left, lets make sure they shut down */
+    relay = *relay_list;
+    while (relay)
+    {
+        relay->cleanup = 1;
+        if (relay->source)
+        {
+            if (relay->source->running)
+                DEBUG1 ("requested %s to shut down", relay->source->mount);
+            relay->source->running = 0;
+            relay = relay->next;
+        }
+        else
+            relay = relay_free (relay);
+    }
+    /* re-assign new set */
+    *relay_list = current;
+}
+
+
+static int update_from_master(ice_config_t *config)
+{
+    char *master = NULL, *password = NULL, *username= NULL;
+    int port;
+    sock_t mastersock;
+    int ret = 0;
+    char buf[256];
+    do
+    {
+        char *authheader, *data;
+        relay_server *relays = NULL, *relay;
+        int len, count = 1;
+
+        if (config->master_username)
+            username = strdup (config->master_password);
+        else
+            username = strdup ("relay");
+        if (config->master_password)
+            password = strdup (config->master_password);
+
+        if (config->master_server)
+            master = strdup (config->master_server);
+
+        port = config->master_server_port;
+
+        if (password == NULL || master == NULL || port == 0)
+            break;
+        ret = 1;
+        config_release_config();
+        mastersock = sock_connect_wto (master, port, 0);
+
+        if (mastersock == SOCK_ERROR)
+        {
+            WARN0("Relay slave failed to contact master server to fetch stream list");
+            break;
+        }
+
+        len = strlen(username) + strlen(password) + 1;
+        authheader = malloc(len+1);
+        strcpy(authheader, username);
+        strcat(authheader, ":");
+        strcat(authheader, password);
+        data = util_base64_encode(authheader);
+        sock_write (mastersock,
+                "GET /admin/streamlist.txt HTTP/1.0\r\n"
+                "Authorization: Basic %s\r\n"
+                "\r\n", data);
+        free(authheader);
+        free(data);
+
+        while (sock_read_line(mastersock, buf, sizeof(buf)))
+        {
+            if (!strlen(buf))
+                break;
+        }
+        while (sock_read_line(mastersock, buf, sizeof(buf)))
+        {
+            relay_server *r;
+            if (!strlen(buf))
+                continue;
+            DEBUG2 ("read %d from master \"%s\"", count++, buf);
+            r = calloc (1, sizeof (relay_server));
+            if (r)
+            {
+                r->server = xmlStrdup (master);
+                r->port = port;
+                r->mount = xmlStrdup (buf);
+                r->localmount = xmlStrdup (buf);
+                r->mp3metadata = 1;
+                r->next = relays;
+                relays = r;
+            }
+        }
+        sock_close (mastersock);
+
+        update_relays (&global.master_relays, relays);
+        /* start any inactive relays */
+        relay = global.master_relays;
+        while (relay)
+        {
+            check_relay_stream (relay);
+            relay = relay->next;
+        }
+        relay = relays;
+        while (relay)
+            relay = relay_free (relay);
+    } while(0);
+
+    if (master)
+        free (master);
+    if (username)
+        free (username);
+    if (password)
+        free (password);
+
+    return ret;
+}
+
+
+static void *_slave_thread(void *arg)
+{
+    ice_config_t *config;
+    relay_server *relay;
+    unsigned interval = 0;
+
+    while (slave_running)
+    {
+        thread_sleep (1000000);
+        if (max_interval > ++interval)
+            continue;
+
+        interval = 0;
+        config = config_get_config();
+
+        max_interval = config->master_update_interval;
+
+        /* the connection could time some time, so the lock can drop */
+        if (update_from_master (config))
+            config = config_get_config();
+
+        thread_mutex_lock (&(config_locks()->relay_lock));
+
+        update_relays (&global.relays, config->relay);
+
+        config_release_config();
+
+        /* start any inactive relays */
+        relay = global.relays;
+        while (relay)
+        {
+            check_relay_stream (relay);
+            relay = relay->next;
+        }
+        thread_mutex_unlock (&(config_locks()->relay_lock));
+    }
+    INFO0 ("Slave thread shutdown complete");
+
+    return NULL;
+}
+

Added: icecast/branches/icecast-kh/src/slave.h
===================================================================
--- icecast/branches/icecast-kh/src/slave.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/slave.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,37 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __SLAVE_H__
+#define __SLAVE_H__
+
+typedef struct _relay_server {
+    char *server;
+    int port;
+    char *mount;
+    char *username;
+    char *password;
+    char *localmount;
+    struct source_tag *source;
+    int mp3metadata;
+    int on_demand;
+    int running;
+    int cleanup;
+    struct _relay_server *next;
+} relay_server;
+
+
+void slave_initialize(void);
+void slave_shutdown(void);
+void slave_recheck (void);
+relay_server *relay_free (relay_server *relay);
+
+#endif  /* __SLAVE_H__ */

Added: icecast/branches/icecast-kh/src/source.c
===================================================================
--- icecast/branches/icecast-kh/src/source.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/source.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,1238 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ogg/ogg.h>
+#include <errno.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#else
+#include <winsock2.h>
+#include <windows.h>
+#define snprintf _snprintf
+#endif
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "logging.h"
+#include "cfgfile.h"
+#include "util.h"
+#include "source.h"
+#include "format.h"
+#include "auth.h"
+
+#undef CATMODULE
+#define CATMODULE "source"
+
+#define MAX_FALLBACK_DEPTH 10
+
+mutex_t move_clients_mutex;
+
+/* avl tree helper */
+static int _compare_clients(void *compare_arg, void *a, void *b);
+static 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
+ * NULL.
+ */
+source_t *source_reserve (const char *mount)
+{
+    source_t *src = NULL;
+
+    do
+    {
+        avl_tree_wlock (global.source_tree);
+        src = source_find_mount_raw (mount);
+        if (src)
+        {
+            src = NULL;
+            break;
+        }
+
+        src = calloc (1, sizeof(source_t));
+        if (src == NULL)
+            break;
+
+        /* 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);
+
+    avl_tree_unlock (global.source_tree);
+    return src;
+}
+
+
+/* Find a mount with this raw name - ignoring fallbacks. You should have the
+ * global source tree locked to call this.
+ */
+source_t *source_find_mount_raw(const char *mount)
+{
+    source_t *source;
+    avl_node *node;
+    int cmp;
+
+    if (!mount) {
+        return NULL;
+    }
+    /* get the root node */
+    node = global.source_tree->root->right;
+
+    while (node) {
+        source = (source_t *)node->key;
+        cmp = strcmp(mount, source->mount);
+        if (cmp < 0)
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return source;
+    }
+
+    /* didn't find it */
+    return NULL;
+}
+
+
+/* Search for mount, if the mount is there but not currently running then
+ * check the fallback, and so on.  Must have a global source lock to call
+ * this function.
+ */
+source_t *source_find_mount (const char *mount)
+{
+    source_t *source = NULL;
+    ice_config_t *config;
+    mount_proxy *mountinfo;
+    int depth = 0;
+
+    config = config_get_config();
+    while (mount != NULL)
+    {
+        /* limit the number of times through, maybe infinite */
+        if (depth > MAX_FALLBACK_DEPTH)
+        {
+            source = NULL;
+            break;
+        }
+
+        source = source_find_mount_raw(mount);
+        if (source)
+        {
+            if (source->running)
+                break;
+            if (source->on_demand)
+                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;
+        source = NULL;
+        while (mountinfo)
+        {
+            if (strcmp (mountinfo->mountname, mount) == 0)
+                break;
+            mountinfo = mountinfo->next;
+        }
+        if (mountinfo)
+            mount = mountinfo->fallback_mount;
+        else
+            mount = NULL;
+        depth++;
+    }
+
+    config_release_config();
+    return source;
+}
+
+
+int source_compare_sources(void *arg, void *a, void *b)
+{
+    source_t *srca = (source_t *)a;
+    source_t *srcb = (source_t *)b;
+
+    return strcmp(srca->mount, srcb->mount);
+}
+
+
+void source_clear_source (source_t *source)
+{
+    DEBUG1 ("clearing source \"%s\"", source->mount);
+    client_destroy(source->client);
+    source->client = NULL;
+    source->parser = NULL;
+    source->con = NULL;
+
+    if (source->dumpfile)
+    {
+        INFO1 ("Closing dumpfile for %s", source->mount);
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+    }
+
+    /* lets drop any clients still connected */
+    while (source->active_clients)
+    {
+        client_t *client = source->active_clients;
+        source->active_clients = client->next;
+        source_free_client (source, client);
+    }
+    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;
+
+    /* flush out the stream data, we don't want any left over */
+    while (source->stream_data)
+    {
+        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);
+    }
+    source->stream_data_tail = NULL;
+
+    if (source->format && source->format->free_plugin)
+    {
+        source->format->free_plugin (source->format);
+    }
+    source->format = NULL;
+
+    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;
+
+    free (source->on_connect);
+    source->on_connect = NULL;
+
+    free (source->on_disconnect);
+    source->on_disconnect = NULL;
+
+    source->on_demand_req = 0;
+}
+
+
+/* Remove the provided source from the global tree and free it */
+void source_free_source (source_t *source)
+{
+    DEBUG1 ("freeing source \"%s\"", source->mount);
+    avl_tree_wlock (global.source_tree);
+    avl_delete (global.source_tree, source, NULL);
+    avl_tree_unlock (global.source_tree);
+
+    /* 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);
+
+    return;
+}
+
+
+client_t *source_find_client(source_t *source, int id)
+{
+    client_t fakeclient, *client = NULL;
+    connection_t fakecon;
+
+    fakeclient.con = &fakecon;
+    fakeclient.con->id = id;
+
+    client = source->active_clients;
+    while (client)
+    {
+        if (_compare_clients (NULL, client, &fakeclient) == 0)
+            break;
+        client = client->next;
+    }
+
+    return client;
+}
+
+
+/* Move clients from source to dest provided dest is running
+ * and that the stream format is the same.
+ * The only lock that should be held when this is called is the
+ * source tree lock
+ */
+void source_move_clients (source_t *source, source_t *dest)
+{
+    /* 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 && 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;
+    }
+
+    do
+    {
+        long count = 0;
+        thread_mutex_lock (&source->lock);
+
+        if (source->format == NULL)
+        {
+            INFO1 ("source mount %s is not available", source->mount);
+            break;
+        }
+        if (dest->format)
+        {
+            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;
+}
+
+
+/* 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)
+        {
+            client->predata_offset += bytes;
+            return 0;
+        }
+        free (client->predata);
+        client->predata_size = client->predata_len = client->predata_offset = 0;
+        client->predata = NULL;
+    }
+
+    /* 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 */
+        }
+
+        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)
+        {
+            client_t *to_go = client;
+
+            *client_p = client->next;
+            if (client->next == NULL)
+                source->active_clients_tail = client_p;
+            client = client->next;
+
+            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;
+            }
+        }
+        else
+        {
+            client_p = &client->next;
+            client = client->next;
+        }
+    }
+}
+
+
+/* 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 = NULL;
+    int listen_url_size;
+
+    /* 6 for max size of port */
+    listen_url_size = strlen("http://") + strlen(config->hostname) +
+        strlen(":") + 6 + strlen(source->mount) + 1;
+
+    listenurl = malloc (listen_url_size);
+    if (listenurl)
+    {
+        snprintf (listenurl, listen_url_size, "http://%s:%d%s",
+                config->hostname, config->port, source->mount);
+        config_release_config();
+
+        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 = 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");
+    if (str)
+    {
+        parse_audio_info(source, str);
+        stats_event (source->mount, "audio_info", str);
+    }
+
+    if (source->dumpfilename != NULL)
+    {
+        source->dumpfile = fopen (source->dumpfilename, "ab");
+        if (source->dumpfile == NULL)
+        {
+            WARN2("Cannot open dump file \"%s\" for appending: %s, disabling.",
+                    source->dumpfilename, strerror(errno));
+        }
+    }
+
+    /* grab a read lock, to make sure we get a chance to cleanup */
+    thread_rwlock_rlock (source->shutdown_rwlock);
+
+    /* start off the statistics */
+    source->listeners = 0;
+    stats_event_inc (NULL, "sources");
+    stats_event_inc (NULL, "source_total_connections");
+    stats_event (source->mount, "listeners", "0");
+    stats_event (source->mount, "type", source->format->format_description);
+
+    sock_set_blocking (source->con->sock, SOCK_NONBLOCK);
+
+    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 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
+    */
+
+    if (source->fallback_override && source->fallback_mount)
+    {
+        source_t *fallback_source;
+
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount(source->fallback_mount);
+
+        if (fallback_source)
+            source_move_clients (fallback_source, source);
+
+        avl_tree_unlock(global.source_tree);
+    }
+    thread_mutex_lock (&source->lock);
+    if (source->yp_public)
+        yp_add (source);
+}
+
+
+static void source_shutdown (source_t *source)
+{
+    INFO1("Source \"%s\" exiting", source->mount);
+    source->running = 0;
+
+    yp_remove (source->mount);
+
+    if (source->on_disconnect)
+        source_run_script (source->on_disconnect, source->mount);
+
+    if (source->fallback_mount)
+    {
+        source_t *fallback_source;
+
+        avl_tree_rlock(global.source_tree);
+        fallback_source = source_find_mount (source->fallback_mount);
+
+        if (fallback_source != NULL)
+        {
+            /* be careful wrt to deadlocking */
+            thread_mutex_unlock (&source->lock);
+            source_move_clients (source, fallback_source);
+            thread_mutex_lock (&source->lock);
+        }
+
+        avl_tree_unlock (global.source_tree);
+    }
+
+    /* delete this sources stats */
+    stats_event_dec (NULL, "sources");
+    stats_event (source->mount, "listeners", NULL);
+
+    /* we don't remove the source from the tree here, it may be a relay and
+       therefore reserved */
+    source_clear_source (source);
+
+    thread_mutex_unlock (&source->lock);
+
+    global_lock();
+    global.sources--;
+    global_unlock();
+
+    /* release our hold on the lock so the main thread can continue cleaning up */
+    thread_rwlock_unlock(source->shutdown_rwlock);
+}
+
+
+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;
+
+    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");
+}
+
+
+/* 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;
+
+    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);
+
+        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;
+
+            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;
+            }
+            if (source->fallback_mount)
+                mount = strdup (source->fallback_mount);
+            else
+                mount = NULL;
+        }
+
+        thread_mutex_unlock (&source->lock);
+    }
+
+    if (auth_check_client (source, client) != AUTH_OK)
+    {
+        thread_mutex_unlock (&source->lock);
+        INFO0 ("listener failed to authenticate");
+        return -2;
+    }
+    source->new_listeners++;
+
+    thread_mutex_unlock (&source->lock);
+    return ret;
+}
+
+
+void add_client (char *mount, client_t *client)
+{
+    int added = -3;
+
+    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();
+}
+
+
+static void process_pending_clients (source_t *source)
+{
+    unsigned count = 0;
+    client_t *client = source->pending_clients;
+
+    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;
+
+    if (count)
+    {
+        DEBUG1("Adding %d client(s)", count);
+        source->listeners += count;
+    }
+}
+
+
+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)
+    {
+        int remove_from_q;
+
+        get_next_buffer (source);
+
+        remove_from_q = 0;
+
+        /* 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;
+
+        /* add pending clients */
+        if (source->check_pending)
+            process_pending_clients (source);
+
+        process_listeners (source, 0, remove_from_q);
+
+        /* 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;
+        }
+
+        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;
+
+    source_shutdown (source);
+}
+
+
+static int _compare_clients(void *compare_arg, void *a, void *b)
+{
+    client_t *clienta = (client_t *)a;
+    client_t *clientb = (client_t *)b;
+
+    connection_t *cona = clienta->con;
+    connection_t *conb = clientb->con;
+
+    if (cona->id < conb->id) return -1;
+    if (cona->id > conb->id) return 1;
+
+    return 0;
+}
+
+
+int source_free_client (source_t *source, client_t *client)
+{
+    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);
+
+    return 1;
+}
+
+
+static void parse_audio_info (source_t *source, const char *s)
+{
+    const char *start = s;
+    unsigned len;
+
+    while (start != NULL && *start != '\0')
+    {
+        if ((s = strchr (start, ';')) == NULL)
+            len = strlen (start);
+        else
+        {
+            len = (int)(s - start);
+            s++; /* skip passed the ';' */
+        }
+        if (len)
+        {
+            char name[200], value[200];
+            char *esc;
+
+            sscanf (start, "%199[^=]=%199[^;\r\n]", name, value);
+            esc = util_url_unescape (value);
+            if (esc)
+            {
+                util_dict_set (source->audio_info, name, esc);
+                stats_event (source->mount, name, value);
+                free (esc);
+            }
+        }
+        start = s;
+    }
+}
+
+
+void source_apply_mount (source_t *source, mount_proxy *mountinfo)
+{
+    DEBUG1("Applying mount information for \"%s\"", source->mount);
+    source->max_listeners = mountinfo->max_listeners;
+    source->fallback_override = mountinfo->fallback_override;
+    source->no_mount = mountinfo->no_mount;
+    if (mountinfo->fallback_mount)
+    {
+        source->fallback_mount = strdup (mountinfo->fallback_mount);
+        DEBUG1 ("fallback %s", mountinfo->fallback_mount);
+    }
+    if (mountinfo->auth_type != NULL)
+    {
+        source->authenticator = auth_get_authenticator(
+                mountinfo->auth_type, mountinfo->auth_options);
+        stats_event(source->mount, "authenticator", mountinfo->auth_type);
+    }
+    if (mountinfo->dumpfile)
+    {
+        DEBUG1("Dumping stream to %s", mountinfo->dumpfile);
+        source->dumpfilename = strdup (mountinfo->dumpfile);
+    }
+    if (mountinfo->queue_size_limit)
+    {
+        source->queue_size_limit = mountinfo->queue_size_limit;
+        DEBUG1 ("queue size to %u", source->queue_size_limit);
+    }
+    if (mountinfo->source_timeout)
+    {
+        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);
+    }
+}
+
+
+void *source_client_thread (void *arg)
+{
+    source_t *source = arg;
+    const char ok_msg[] = "HTTP/1.0 200 OK\r\n\r\n";
+    int bytes;
+
+    source->client->respcode = 200;
+    bytes = sock_write_bytes (source->client->con->sock, ok_msg, sizeof (ok_msg)-1);
+    if (bytes < (int)sizeof (ok_msg)-1)
+    {
+        global_lock();
+        global.sources--;
+        global_unlock();
+        WARN0 ("Error writing 200 OK message to source client");
+    }
+    else
+    {
+        source->client->con->sent_bytes += bytes;
+
+        stats_event_inc(NULL, "source_client_connections");
+        source_main (source);
+    }
+    source_free_source (source);
+    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

Added: icecast/branches/icecast-kh/src/source.h
===================================================================
--- icecast/branches/icecast-kh/src/source.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/source.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,105 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __SOURCE_H__
+#define __SOURCE_H__
+
+#include "cfgfile.h"
+#include "yp.h"
+#include "util.h"
+#include "format.h"
+
+#include <stdio.h>
+
+struct auth_tag;
+
+typedef struct source_tag
+{
+    client_t *client;
+    connection_t *con;
+    http_parser_t *parser;
+
+    char *mount;
+
+    /* If this source drops, try to move all clients to this fallback */
+    char *fallback_mount;
+
+    /* set to zero to request the source to shutdown without causing a global
+     * shutdown */
+    int running;
+
+    struct _format_plugin_tag *format;
+
+    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;
+
+    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 */
+
+    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);
+void *source_client_thread (void *arg);
+void source_apply_mount (source_t *source, mount_proxy *mountinfo);
+void source_clear_source (source_t *source);
+source_t *source_find_mount(const char *mount);
+source_t *source_find_mount_raw(const char *mount);
+client_t *source_find_client(source_t *source, int id);
+int source_compare_sources(void *arg, void *a, void *b);
+void source_free_source(source_t *source);
+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;
+
+#endif
+
+

Added: icecast/branches/icecast-kh/src/stats.c
===================================================================
--- icecast/branches/icecast-kh/src/stats.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/stats.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,858 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <thread/thread.h>
+#include <avl/avl.h>
+#include <httpp/httpp.h>
+#include <net/sock.h>
+
+#include "connection.h"
+
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "xslt.h"
+
+#ifdef _WIN32
+#define vsnprintf _vsnprintf
+#endif
+
+typedef struct _event_listener_tag
+{
+    stats_event_t **queue;
+    mutex_t *mutex;
+
+    struct _event_listener_tag *next;
+} event_listener_t;
+
+int _stats_running = 0;
+thread_type *_stats_thread_id;
+int _stats_threads = 0;
+
+stats_t _stats;
+mutex_t _stats_mutex;
+
+stats_event_t *_global_event_queue;
+mutex_t _global_event_mutex;
+
+cond_t _event_signal_cond;
+
+event_listener_t *_event_listeners;
+
+
+
+static void *_stats_thread(void *arg);
+static int _compare_stats(void *a, void *b, void *arg);
+static int _compare_source_stats(void *a, void *b, void *arg);
+static int _free_stats(void *key);
+static int _free_source_stats(void *key);
+static void _add_event_to_queue(stats_event_t *event, stats_event_t **queue);
+static stats_node_t *_find_node(avl_tree *tree, char *name);
+static stats_source_t *_find_source(avl_tree *tree, char *source);
+static void _free_event(stats_event_t *event);
+
+void stats_initialize()
+{
+    _event_listeners = NULL;
+
+    /* set up global struct */
+    _stats.global_tree = avl_tree_new(_compare_stats, NULL);
+    _stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
+
+    /* set up global mutex */
+    thread_mutex_create("stats", &_stats_mutex);
+
+    /* set up event signaler */
+    thread_cond_create(&_event_signal_cond);
+
+    /* set up stats queues */
+    _global_event_queue = NULL;
+    thread_mutex_create("stats_global_event", &_global_event_mutex);
+
+    /* fire off the stats thread */
+    _stats_running = 1;
+    _stats_thread_id = thread_create("Stats Thread", _stats_thread, NULL, THREAD_ATTACHED);
+}
+
+void stats_shutdown()
+{
+    int n;
+    stats_event_t *event, *next;
+
+    if(!_stats_running) /* We can't shutdown if we're not running. */
+        return;
+
+    /* wait for thread to exit */
+    _stats_running = 0;
+    thread_join(_stats_thread_id);
+
+    /* wait for other threads to shut down */
+    do {
+        thread_sleep(300000);
+        thread_mutex_lock(&_stats_mutex);
+        n = _stats_threads;
+        thread_mutex_unlock(&_stats_mutex);
+    } while (n > 0);
+
+    /* free the queues */
+
+    /* destroy the queue mutexes */
+    thread_mutex_destroy(&_global_event_mutex);
+
+    /* tear it all down */
+    thread_cond_destroy(&_event_signal_cond);
+    thread_mutex_destroy(&_stats_mutex);
+    avl_tree_free(_stats.source_tree, _free_source_stats);
+    avl_tree_free(_stats.global_tree, _free_stats);
+
+    event = _global_event_queue;
+    while(event) {
+        if(event->source)
+            free(event->source);
+        if(event->value)
+            free(event->value);
+        if(event->name)
+            free(event->name);
+        next = event->next;
+        free(event);
+        event = next;
+    }
+}
+
+stats_t *stats_get_stats()
+{
+    /* lock global stats
+
+     copy stats
+
+     unlock global stats
+
+     return copied stats */
+
+    return NULL;
+}
+
+void stats_event(char *source, char *name, char *value)
+{
+    stats_event_t *node;
+    stats_event_t *event;
+
+    if (name == NULL || strcmp(name, "") == 0) return;
+
+    /* build event */
+    event = (stats_event_t *)malloc(sizeof(stats_event_t));
+    event->source = NULL;
+    if (source != NULL) event->source = (char *)strdup(source);
+    event->name = (char *)strdup(name);
+    event->value = NULL;
+    event->next = NULL;
+    if (value != NULL) event->value = (char *)strdup(value);
+
+    /* queue event */
+    thread_mutex_lock(&_global_event_mutex);
+    if (_global_event_queue == NULL) {
+        _global_event_queue = event;
+    } else {
+        node = _global_event_queue;
+        while (node->next) node = node->next;
+        node->next = event;
+    }
+    thread_mutex_unlock(&_global_event_mutex);
+}
+
+void stats_event_args(char *source, char *name, char *format, ...)
+{
+    char buf[1024];
+    va_list val;
+
+    va_start(val, format);
+    vsnprintf(buf, 1024, format, val);
+    va_end(val);
+
+    stats_event(source, name, buf);
+}
+
+static char *_get_stats(char *source, char *name)
+{
+    stats_node_t *stats = NULL;
+    stats_source_t *src = NULL;
+    char *value = NULL;
+
+    thread_mutex_lock(&_stats_mutex);
+
+    if (source == NULL) {
+        stats = _find_node(_stats.global_tree, name);
+    } else {
+        src = _find_source(_stats.source_tree, source);
+        if (src) {
+            stats = _find_node(src->stats_tree, name);
+        }
+    }
+
+    if (stats) value = (char *)strdup(stats->value);
+
+    thread_mutex_unlock(&_stats_mutex);
+
+    return value;
+}
+
+char *stats_get_value(char *source, char *name)
+{
+    return(_get_stats(source, name));
+}
+void stats_event_inc(char *source, char *name)
+{
+    char *old_value;
+    int new_value;
+
+    old_value = _get_stats(source, name);
+    if (old_value != NULL) {
+        new_value = atoi(old_value);
+        free(old_value);
+        new_value++;
+    } else {
+        new_value = 1;
+    }
+
+    stats_event_args(source, name, "%d", new_value);
+}
+
+void stats_event_add(char *source, char *name, unsigned long value)
+{
+    char *old_value;
+    unsigned long new_value;
+
+    old_value = _get_stats(source, name);
+    if (old_value != NULL) {
+        new_value = atol(old_value);
+        free(old_value);
+        new_value += value;
+    } else {
+        new_value = value;
+    }
+
+    stats_event_args(source, name, "%ld", new_value);
+}
+
+void stats_event_dec(char *source, char *name)
+{
+    char *old_value;
+    int new_value;
+
+    old_value = _get_stats(source, name);
+    if (old_value != NULL) {
+        new_value = atoi(old_value);
+        free(old_value);
+        new_value--;
+        if (new_value < 0) new_value = 0;
+    } else {
+        new_value = 0;
+    }
+
+    stats_event_args(source, name, "%d", new_value);
+}
+
+/* note: you must call this function only when you have exclusive access
+** to the avl_tree
+*/
+static stats_node_t *_find_node(avl_tree *stats_tree, char *name)
+{
+    stats_node_t *stats;
+    avl_node *node;
+    int cmp;
+
+    /* get the root node */
+    node = stats_tree->root->right;
+
+    while (node) {
+        stats = (stats_node_t *)node->key;
+        cmp = strcmp(name, stats->name);
+        if (cmp < 0)
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return stats;
+    }
+
+    /* didn't find it */
+    return NULL;
+}
+
+/* note: you must call this function only when you have exclusive access
+** to the avl_tree
+*/
+static stats_source_t *_find_source(avl_tree *source_tree, char *source)
+{
+    stats_source_t *stats;
+    avl_node *node;
+    int cmp;
+
+    /* get the root node */
+    node = source_tree->root->right;
+    while (node) {
+        stats = (stats_source_t *)node->key;
+        cmp = strcmp(source, stats->source);
+        if (cmp < 0)
+            node = node->left;
+        else if (cmp > 0)
+            node = node->right;
+        else
+            return stats;
+    }
+
+    /* didn't find it */
+    return NULL;
+}
+
+static stats_event_t *_copy_event(stats_event_t *event)
+{
+    stats_event_t *copy = (stats_event_t *)malloc(sizeof(stats_event_t));
+    if (event->source)
+        copy->source = (char *)strdup(event->source);
+    else
+        copy->source = NULL;
+    copy->name = (char *)strdup(event->name);
+    if (event->value)
+        copy->value = (char *)strdup(event->value);
+    else
+        copy->value = NULL;
+    copy->next = NULL;
+
+    return copy;
+}
+
+static void *_stats_thread(void *arg)
+{
+    stats_event_t *event;
+    stats_event_t *copy;
+    stats_node_t *node;
+    stats_node_t *anode;
+    stats_source_t *snode;
+    stats_source_t *asnode;
+    event_listener_t *listener;
+    avl_node *avlnode;
+
+    while (_stats_running) {
+        thread_mutex_lock(&_global_event_mutex);
+        if (_global_event_queue != NULL) {
+            /* grab the next event from the queue */
+            event = _global_event_queue;
+            _global_event_queue = event->next;
+            event->next = NULL;
+            thread_mutex_unlock(&_global_event_mutex);
+
+            thread_mutex_lock(&_stats_mutex);
+            if (event->source == NULL) {
+                /* we have a global event */
+                if (event->value != NULL) {
+                    /* adding/updating */
+                    node = _find_node(_stats.global_tree, event->name);
+                    if (node == NULL) {
+                        /* add node */
+                        anode = (stats_node_t *)malloc(sizeof(stats_node_t));
+                        anode->name = (char *)strdup(event->name);
+                        anode->value = (char *)strdup(event->value);
+
+                        avl_insert(_stats.global_tree, (void *)anode);
+                    } else {
+                        /* update node */
+                        free(node->value);
+                        node->value = (char *)strdup(event->value);
+                    }
+
+                } else {
+                    /* we're deleting */
+                    node = _find_node(_stats.global_tree, event->name);
+                    if (node != NULL)
+                        avl_delete(_stats.global_tree, (void *)node, _free_stats);
+                }
+            } else {
+                /* we have a source event */
+
+                snode = _find_source(_stats.source_tree, event->source);
+                if (snode != NULL) {
+                    /* this is a source we already have a tree for */
+                    if (event->value != NULL) {
+                        /* we're add/updating */
+                        node = _find_node(snode->stats_tree, event->name);
+                        if (node == NULL) {
+                            /* adding node */
+                            anode = (stats_node_t *)malloc(sizeof(stats_node_t));
+                            anode->name = (char *)strdup(event->name);
+                            anode->value = (char *)strdup(event->value);
+
+                            avl_insert(snode->stats_tree, (void *)anode);
+                        } else {
+                            /* updating node */
+                            free(node->value);
+                            node->value = (char *)strdup(event->value);
+                        }
+                    } else {
+                        /* we're deleting */
+                        node = _find_node(snode->stats_tree, event->name);
+                        if (node != NULL) {
+                            avl_delete(snode->stats_tree, (void *)node, _free_stats);
+
+                                avlnode = avl_get_first(snode->stats_tree);
+                            if (avlnode == NULL) {
+                                avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
+                            }
+                        }
+                    }
+                } else {
+                    /* this is a new source */
+                    asnode = (stats_source_t *)malloc(sizeof(stats_source_t));
+                    asnode->source = (char *)strdup(event->source);
+                    asnode->stats_tree = avl_tree_new(_compare_stats, NULL);
+
+                    anode = (stats_node_t *)malloc(sizeof(stats_node_t));
+                    anode->name = (char *)strdup(event->name);
+                    anode->value = (char *)strdup(event->value);
+
+                    avl_insert(asnode->stats_tree, (void *)anode);
+
+                    avl_insert(_stats.source_tree, (void *)asnode);
+                }
+            }
+
+            /* now we have an event that's been processed into the running stats */
+            /* this event should get copied to event listeners' queues */
+            listener = _event_listeners;
+            while (listener) {
+                copy = _copy_event(event);
+                thread_mutex_lock(listener->mutex);
+                _add_event_to_queue(copy, listener->queue);
+                thread_mutex_unlock(listener->mutex);
+
+                listener = listener->next;
+            }
+            thread_cond_broadcast(&_event_signal_cond);
+
+            /* now we need to destroy the event */
+            _free_event(event);
+
+            thread_mutex_unlock(&_stats_mutex);
+            continue;
+        } else {
+            thread_mutex_unlock(&_global_event_mutex);
+        }
+
+        thread_sleep(300000);
+    }
+
+    /* wake the other threads so they can shut down cleanly */
+    thread_cond_broadcast(&_event_signal_cond);
+
+    return NULL;
+}
+
+/* you must have the _stats_mutex locked here */
+static void _register_listener(stats_event_t **queue, mutex_t *mutex)
+{
+    event_listener_t *node;
+    event_listener_t *evli = (event_listener_t *)malloc(sizeof(event_listener_t));
+
+    evli->queue = queue;
+    evli->mutex = mutex;
+    evli->next = NULL;
+
+    if (_event_listeners == NULL) {
+        _event_listeners = evli;
+    } else {
+        node = _event_listeners;
+        while (node->next) node = node->next;
+        node->next = evli;
+    }
+}
+
+static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
+{
+    stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
+
+    if (source != NULL)
+        event->source = (char *)strdup(source);
+    else
+        event->source = NULL;
+    event->name = (char *)strdup(node->name);
+    event->value = (char *)strdup(node->value);
+    event->next = NULL;
+
+    return event;
+}
+
+static void _add_event_to_queue(stats_event_t *event, stats_event_t **queue)
+{
+    stats_event_t *node;
+
+    if (*queue == NULL) {
+        *queue = event;
+    } else {
+        node = *queue;
+        while (node->next) node = node->next;
+        node->next = event;
+    }
+}
+
+static stats_event_t *_get_event_from_queue(stats_event_t **queue)
+{
+    stats_event_t *event;
+
+    if (*queue == NULL) return NULL;
+
+    event = *queue;
+    *queue = (*queue)->next;
+    event->next = NULL;
+
+    return event;
+}
+
+static int _send_event_to_client(stats_event_t *event, connection_t *con)
+{
+    int ret;
+
+    /* send data to the client!!!! */
+    ret = sock_write(con->sock, "EVENT %s %s %s\n", (event->source != NULL) ? event->source : "global", event->name, event->value ? event->value : "null");
+
+    return (ret == -1) ? 0 : 1;
+}
+
+void _dump_stats_to_queue(stats_event_t **queue)
+{
+    avl_node *node;
+    avl_node *node2;
+    stats_event_t *event;
+    stats_source_t *source;
+
+    thread_mutex_lock(&_stats_mutex);
+    /* first we fill our queue with the current stats */
+    /* start with the global stats */
+    node = avl_get_first(_stats.global_tree);
+    while (node) {
+        event = _make_event_from_node((stats_node_t *)node->key, NULL);
+        _add_event_to_queue(event, queue);
+
+        node = avl_get_next(node);
+    }
+
+    /* now the stats for each source */
+    node = avl_get_first(_stats.source_tree);
+    while (node) {
+        source = (stats_source_t *)node->key;
+        node2 = avl_get_first(source->stats_tree);
+        while (node2) {
+            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
+            _add_event_to_queue(event, queue);
+
+            node2 = avl_get_next(node2);
+        }
+
+        node = avl_get_next(node);
+    }
+    thread_mutex_unlock(&_stats_mutex);
+}
+
+/* factoring out code for stats loops
+** this function copies all stats to queue, and registers
+** the queue for all new events atomically.
+** note: mutex must already be created!
+*/
+static void _atomic_get_and_register(stats_event_t **queue, mutex_t *mutex)
+{
+    avl_node *node;
+    avl_node *node2;
+    stats_event_t *event;
+    stats_source_t *source;
+
+    thread_mutex_lock(&_stats_mutex);
+
+    /* first we fill our queue with the current stats */
+
+    /* start with the global stats */
+    node = avl_get_first(_stats.global_tree);
+    while (node) {
+        event = _make_event_from_node((stats_node_t *)node->key, NULL);
+        _add_event_to_queue(event, queue);
+
+        node = avl_get_next(node);
+    }
+
+    /* now the stats for each source */
+    node = avl_get_first(_stats.source_tree);
+    while (node) {
+        source = (stats_source_t *)node->key;
+        node2 = avl_get_first(source->stats_tree);
+        while (node2) {
+            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
+            _add_event_to_queue(event, queue);
+
+            node2 = avl_get_next(node2);
+        }
+
+        node = avl_get_next(node);
+    }
+
+    /* now we register to receive future event notices */
+    _register_listener(queue, mutex);
+
+    thread_mutex_unlock(&_stats_mutex);
+}
+
+void *stats_connection(void *arg)
+{
+    stats_connection_t *statcon = (stats_connection_t *)arg;
+    stats_event_t *local_event_queue = NULL;
+    mutex_t local_event_mutex;
+    stats_event_t *event;
+
+    /* increment the thread count */
+    thread_mutex_lock(&_stats_mutex);
+    _stats_threads++;
+    thread_mutex_unlock(&_stats_mutex);
+
+    thread_mutex_create("stats local event", &local_event_mutex);
+
+    _atomic_get_and_register(&local_event_queue, &local_event_mutex);
+
+    while (_stats_running) {
+        thread_mutex_lock(&local_event_mutex);
+        event = _get_event_from_queue(&local_event_queue);
+        if (event != NULL) {
+            if (!_send_event_to_client(event, statcon->con)) {
+                _free_event(event);
+                thread_mutex_unlock(&local_event_mutex);
+                break;
+            }
+            _free_event(event);
+        } else {
+            thread_mutex_unlock(&local_event_mutex);
+            thread_cond_wait(&_event_signal_cond);
+            continue;
+        }
+
+        thread_mutex_unlock(&local_event_mutex);
+    }
+
+    thread_mutex_destroy(&local_event_mutex);
+
+    thread_mutex_lock(&_stats_mutex);
+    _stats_threads--;
+    thread_mutex_unlock(&_stats_mutex);
+
+    return NULL;
+}
+
+typedef struct _source_xml_tag {
+    char *mount;
+    xmlNodePtr node;
+
+    struct _source_xml_tag *next;
+} source_xml_t;
+
+static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
+{
+    source_xml_t *node, *node2;
+    int found = 0;
+
+    /* search for existing node */
+    node = *list;
+    while (node) {
+        if (strcmp(node->mount, mount) == 0) {
+            found = 1;
+            break;
+        }
+        node = node->next;
+    }
+
+    if (found) return node->node;
+
+    /* if we didn't find it, we must build it and add it to the list */
+
+    /* build node */
+    node = (source_xml_t *)malloc(sizeof(source_xml_t));
+    node->mount = strdup(mount);
+    node->node = xmlNewChild(root, NULL, "source", NULL);
+    xmlSetProp(node->node, "mount", mount);
+    node->next = NULL;
+
+    /* add node */
+    if (*list == NULL) {
+        *list = node;
+    } else {
+        node2 = *list;
+        while (node2->next) node2 = node2->next;
+        node2->next = node;
+    }
+
+    return node->node;
+}
+
+void stats_transform_xslt(client_t *client, char *xslpath)
+{
+    xmlDocPtr doc;
+
+    stats_get_xml(&doc);
+
+    xslt_transform(doc, xslpath, client);
+
+    xmlFreeDoc(doc);
+}
+
+void stats_get_xml(xmlDocPtr *doc)
+{
+    stats_event_t *event;
+    stats_event_t *queue;
+    xmlNodePtr node, srcnode;
+    source_xml_t *src_nodes = NULL;
+    source_xml_t *next;
+
+    queue = NULL;
+    _dump_stats_to_queue(&queue);
+
+    *doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
+    xmlDocSetRootElement(*doc, node);
+
+
+    event = _get_event_from_queue(&queue);
+    while (event) {
+        if (event->source == NULL) {
+            xmlNewChild(node, NULL, event->name, event->value);
+        } else {
+            srcnode = _find_xml_node(event->source, &src_nodes, node);
+            xmlNewChild(srcnode, NULL, event->name, event->value);
+        }
+
+        _free_event(event);
+        event = _get_event_from_queue(&queue);
+    }
+
+    while (src_nodes) {
+        next = src_nodes->next;
+        free(src_nodes->mount);
+        free(src_nodes);
+        src_nodes = next;
+    }
+}
+void stats_sendxml(client_t *client)
+{
+    int bytes;
+    stats_event_t *event;
+    stats_event_t *queue;
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode;
+    int len;
+    xmlChar *buff = NULL;
+    source_xml_t *snd;
+    source_xml_t *src_nodes = NULL;
+
+    queue = NULL;
+    _dump_stats_to_queue(&queue);
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
+    xmlDocSetRootElement(doc, node);
+
+
+    event = _get_event_from_queue(&queue);
+    while (event) {
+        if (event->source == NULL) {
+            xmlNewChild(node, NULL, event->name, event->value);
+        } else {
+            srcnode = _find_xml_node(event->source, &src_nodes, node);
+            xmlNewChild(srcnode, NULL, event->name, event->value);
+        }
+
+        _free_event(event);
+        event = _get_event_from_queue(&queue);
+    }
+
+    xmlDocDumpMemory(doc, &buff, &len);
+    xmlFreeDoc(doc);
+
+    client->respcode = 200;
+    bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n"
+               "Content-Length: %d\r\n"
+               "Content-Type: text/xml\r\n"
+               "\r\n", len);
+    if (bytes > 0) client->con->sent_bytes += bytes;
+    else goto send_error;
+
+    bytes = client_send_bytes (client, buff, (unsigned)len);
+
+ send_error:
+    while (src_nodes) {
+        snd = src_nodes->next;
+        free(src_nodes->mount);
+        free(src_nodes);
+        src_nodes = snd;
+    }
+    if (buff) xmlFree(buff);
+}
+
+static int _compare_stats(void *arg, void *a, void *b)
+{
+    stats_node_t *nodea = (stats_node_t *)a;
+    stats_node_t *nodeb = (stats_node_t *)b;
+
+    return strcmp(nodea->name, nodeb->name);
+}
+
+static int _compare_source_stats(void *arg, void *a, void *b)
+{
+    stats_source_t *nodea = (stats_source_t *)a;
+    stats_source_t *nodeb = (stats_source_t *)b;
+
+    return strcmp(nodea->source, nodeb->source);
+}
+
+static int _free_stats(void *key)
+{
+    stats_node_t *node = (stats_node_t *)key;
+    free(node->value);
+    free(node->name);
+    free(node);
+
+    return 1;
+}
+
+static int _free_source_stats(void *key)
+{
+    stats_source_t *node = (stats_source_t *)key;
+    avl_tree_free(node->stats_tree, _free_stats);
+    free(node->source);
+    free(node);
+
+    return 1;
+}
+
+static void _free_event(stats_event_t *event)
+{
+    if (event->source) free(event->source);
+    if (event->name) free(event->name);
+    if (event->value) free(event->value);
+    free(event);
+}

Added: icecast/branches/icecast-kh/src/stats.h
===================================================================
--- icecast/branches/icecast-kh/src/stats.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/stats.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,99 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __STATS_H__
+#define __STATS_H__
+
+#include "connection.h"
+#include "httpp/httpp.h"
+#include "client.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+
+typedef struct _stats_connection_tag
+{
+    connection_t *con;
+    http_parser_t *parser;
+} stats_connection_t;
+
+typedef struct _stats_node_tag
+{
+    char *name;
+    char *value;
+} stats_node_t;
+
+typedef struct _stats_event_tag
+{
+    char *source;
+    char *name;
+    char *value;
+
+    struct _stats_event_tag *next;
+} stats_event_t;
+
+typedef struct _stats_source_tag
+{
+    char *source;
+    avl_tree *stats_tree;
+} stats_source_t;
+
+typedef struct _stats_tag
+{
+    avl_tree *global_tree;
+
+    /* global stats
+    start_time
+    total_users
+    max_users
+    total_sources
+    max_sources
+    total_user_connections
+    total_source_connections
+    */
+
+    avl_tree *source_tree;
+
+    /* stats by source, and for stats
+    start_time
+    total_users
+    max_users
+    */
+
+} stats_t;
+
+void stats_initialize();
+void stats_shutdown();
+
+stats_t *stats_get_stats();
+
+void stats_event(char *source, char *name, char *value);
+void stats_event_args(char *source, char *name, char *format, ...);
+void stats_event_inc(char *source, char *name);
+void stats_event_add(char *source, char *name, unsigned long value);
+void stats_event_dec(char *source, char *name);
+
+void *stats_connection(void *arg);
+void *stats_callback(void *arg);
+
+void stats_transform_xslt(client_t *client, char *xslpath);
+void stats_sendxml(client_t *client);
+void stats_get_xml(xmlDocPtr *doc);
+char *stats_get_value(char *source, char *name);
+
+#endif  /* __STATS_H__ */
+
+
+
+
+

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

Added: icecast/branches/icecast-kh/src/util.h
===================================================================
--- icecast/branches/icecast-kh/src/util.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/util.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,52 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#define XSLT_CONTENT 1
+#define HTML_CONTENT 2
+
+int util_timed_wait_for_fd(int fd, int timeout);
+int util_read_header(int sock, char *buff, unsigned long len);
+int util_check_valid_extension(char *uri);
+char *util_get_extension(char *path);
+char *util_get_path_from_uri(char *uri);
+char *util_get_path_from_normalised_uri(char *uri);
+char *util_normalise_uri(char *uri);
+char *util_base64_encode(char *data);
+char *util_base64_decode(unsigned char *input);
+char *util_bin_to_hex(unsigned char *data, int len);
+
+char *util_url_unescape(char *src);
+char *util_url_escape(char *src);
+
+/* String dictionary type, without support for NULL keys, or multiple
+ * instances of the same key */
+typedef struct _util_dict {
+  char *key;
+  char *val;
+  struct _util_dict *next;
+} util_dict;
+
+util_dict *util_dict_new(void);
+void util_dict_free(util_dict *dict);
+/* dict, key must not be NULL. */
+int util_dict_set(util_dict *dict, const char *key, const char *val);
+const char *util_dict_get(util_dict *dict, const char *key);
+char *util_dict_urlencode(util_dict *dict, char delim);
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r (const time_t *timep, struct tm *result);
+#endif
+
+#endif  /* __UTIL_H__ */

Added: icecast/branches/icecast-kh/src/xslt.c
===================================================================
--- icecast/branches/icecast-kh/src/xslt.c	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/xslt.c	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,191 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/debugXML.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlIO.h>
+#include <libxml/DOCBparser.h>
+#include <libxml/xinclude.h>
+#include <libxml/catalog.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+#include "connection.h"
+
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+
+#define CATMODULE "xslt"
+
+#include "logging.h"
+
+typedef struct {
+    char              *filename;
+    time_t             last_modified;
+    time_t             cache_age;
+    xsltStylesheetPtr  stylesheet;
+} stylesheet_cache_t;
+
+/* Keep it small... */
+#define CACHESIZE 3
+
+stylesheet_cache_t cache[CACHESIZE];
+mutex_t xsltlock;
+
+void xslt_initialize()
+{
+    memset(cache, 0, sizeof(stylesheet_cache_t)*CACHESIZE);
+    thread_mutex_create("xslt", &xsltlock);
+}
+
+void xslt_shutdown() {
+    int i;
+
+    for(i=0; i < CACHESIZE; i++) {
+        if(cache[i].filename)
+            free(cache[i].filename);
+        if(cache[i].stylesheet)
+            xsltFreeStylesheet(cache[i].stylesheet);
+    }
+
+    xsltCleanupGlobals();
+}
+
+static int evict_cache_entry() {
+    int i, age=0, oldest=0;
+
+    for(i=0; i < CACHESIZE; i++) {
+        if(cache[i].cache_age > age) {
+            age = cache[i].cache_age;
+            oldest = i;
+        }
+    }
+
+    xsltFreeStylesheet(cache[oldest].stylesheet);
+    free(cache[oldest].filename);
+
+    return oldest;
+}
+
+static xsltStylesheetPtr xslt_get_stylesheet(char *fn) {
+    int i;
+    int empty = -1;
+    struct stat file;
+
+    if(stat(fn, &file)) {
+        DEBUG1("Error checking for stylesheet file: %s", strerror(errno));
+        return NULL;
+    }
+
+    for(i=0; i < CACHESIZE; i++) {
+        if(cache[i].filename)
+        {
+#ifdef _WIN32
+            if(!stricmp(fn, cache[i].filename))
+#else
+            if(!strcmp(fn, cache[i].filename))
+#endif
+            {
+                if(file.st_mtime > cache[i].last_modified)
+                {
+                    xsltFreeStylesheet(cache[i].stylesheet);
+
+                    cache[i].last_modified = file.st_mtime;
+                    cache[i].stylesheet = xsltParseStylesheetFile(fn);
+                    cache[i].cache_age = time(NULL);
+                }
+                DEBUG1("Using cached sheet %i", i);
+                return cache[i].stylesheet;
+            }
+        }
+        else
+            empty = i;
+    }
+
+    if(empty>=0)
+        i = empty;
+    else
+        i = evict_cache_entry();
+
+    cache[i].last_modified = file.st_mtime;
+    cache[i].filename = strdup(fn);
+    cache[i].stylesheet = xsltParseStylesheetFile(fn);
+    cache[i].cache_age = time(NULL);
+    return cache[i].stylesheet;
+}
+
+void xslt_transform(xmlDocPtr doc, char *xslfilename, client_t *client)
+{
+    xmlOutputBufferPtr outputBuffer;
+    xmlDocPtr    res;
+    xsltStylesheetPtr cur;
+    const char *params[16 + 1];
+    size_t count,bytes;
+
+    params[0] = NULL;
+
+    xmlSubstituteEntitiesDefault(1);
+    xmlLoadExtDtdDefaultValue = 1;
+
+    thread_mutex_lock(&xsltlock);
+    cur = xslt_get_stylesheet(xslfilename);
+    thread_mutex_unlock(&xsltlock);
+
+    if (cur == NULL) {
+        bytes = sock_write_string(client->con->sock,
+                (char *)"Could not parse XSLT file");
+        if(bytes > 0) client->con->sent_bytes += bytes;
+
+        return;
+    }
+
+    res = xsltApplyStylesheet(cur, doc, params);
+
+    outputBuffer = xmlAllocOutputBuffer(NULL);
+
+    count = xsltSaveResultTo(outputBuffer, res, cur);
+
+    /*  Add null byte to end. */
+    bytes = xmlOutputBufferWrite(outputBuffer, 1, "");
+
+    if(sock_write_string(client->con->sock,
+                (char *)outputBuffer->buffer->content))
+        client->con->sent_bytes += bytes;
+
+    xmlOutputBufferClose(outputBuffer);
+    xmlFreeDoc(res);
+}
+

Added: icecast/branches/icecast-kh/src/xslt.h
===================================================================
--- icecast/branches/icecast-kh/src/xslt.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/xslt.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,40 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#include <libxml/xmlmemory.h>
+#include <libxml/debugXML.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlIO.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+
+#include "thread/thread.h"
+#include "avl/avl.h"
+#include "httpp/httpp.h"
+#include "net/sock.h"
+
+
+#include "connection.h"
+
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+
+
+void xslt_transform(xmlDocPtr doc, char *xslfilename, client_t *client);
+void xslt_initialize();
+void xslt_shutdown();
+

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

Added: icecast/branches/icecast-kh/src/yp.h
===================================================================
--- icecast/branches/icecast-kh/src/yp.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/src/yp.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,53 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org,
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __YP_H__
+#define __YP_H__
+
+#include <stdio.h>
+
+#define  YP_SERVER_NAME 1
+#define  YP_SERVER_DESC 2
+#define  YP_SERVER_GENRE 3
+#define  YP_SERVER_URL 4
+#define  YP_BITRATE 5
+#define  YP_AUDIO_INFO 6
+#define  YP_SERVER_TYPE 7
+#define  YP_CURRENT_SONG 8
+
+struct source_tag;
+
+#define YP_ADD_ALL -1
+
+#ifdef USE_YP
+void yp_add (struct source_tag *source);
+void yp_remove (const char *mount);
+void yp_touch (const char *mount);
+void yp_recheck_config (ice_config_t *config);
+void yp_initialize();
+void yp_shutdown();
+
+#else
+
+#define yp_add(x)               do{}while(0)
+#define yp_remove(x)            do{}while(0)
+#define yp_touch(x)             do{}while(0)
+#define yp_recheck_config(x)    do{}while(0)
+#define yp_initialize()         do{}while(0)
+#define yp_shutdown()           do{}while(0)
+
+#endif /* USE_YP */
+
+#endif
+
+

Added: icecast/branches/icecast-kh/web/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/web/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/web/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+webdir = $(pkgdatadir)/web
+dist_web_DATA = status.xsl \
+                status2.xsl \
+                corner_bottomleft.jpg \
+                corner_bottomright.jpg \
+                corner_topleft.jpg \
+                corner_topright.jpg \
+                icecast.png \
+                key.gif \
+                style.css

Added: icecast/branches/icecast-kh/web/corner_bottomleft.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/corner_bottomleft.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/corner_bottomright.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/corner_bottomright.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/corner_topleft.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/corner_topleft.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/corner_topright.jpg
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/corner_topright.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/icecast.png
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/icecast.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/key.gif
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/web/key.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/web/status.xsl
===================================================================
--- icecast/branches/icecast-kh/web/status.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/web/status.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,84 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+</head>
+<body bgcolor="black">
+<table border="0" width="100%%">
+<tr>
+<td width="50"></td>
+<td>
+<h2>Icecast Status Page</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<xsl:choose>
+<xsl:when test="listeners">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)
+<xsl:if test="authenticator"> <a href="/auth.xsl"><img border="0" src="/key.gif"/></a> </xsl:if>
+</h3>
+<table border="0" cellpadding="4">
+<xsl:if test="server_name">
+<tr><td>Stream Title:</td><td class="streamdata"> <xsl:value-of select="server_name" /></td></tr>
+</xsl:if>
+<xsl:if test="server_description">
+<tr><td>Stream Description:</td><td class="streamdata"> <xsl:value-of select="server_description" /></td></tr>
+</xsl:if>
+<xsl:if test="type">
+<tr><td width="130"> Stream Type:</td><td class="streamdata"><xsl:value-of select="type" /></td></tr>
+</xsl:if>
+<xsl:if test="bitrate">
+<tr><td>Bitrate:</td><td class="streamdata"> <xsl:value-of select="bitrate" /></td></tr>
+</xsl:if>
+<xsl:if test="listeners">
+<tr><td>Stream Listeners:</td><td class="streamdata"> <xsl:value-of select="listeners" /></td></tr>
+</xsl:if>
+<xsl:if test="genre">
+<tr><td>Stream Genre:</td><td class="streamdata"> <xsl:value-of select="genre" /></td></tr>
+</xsl:if>
+<xsl:if test="server_url">
+<tr><td>Stream URL:</td><td class="streamdata"> <a href="{server_url}"><xsl:value-of select="server_url" /></a></td></tr>
+</xsl:if>
+<tr><td>Current Song:</td><td class="streamdata">
+<xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if><xsl:value-of select="title" /></td></tr>
+<tr><td>Listen:</td><td class="streamdata">
+<xsl:choose>
+<xsl:when test="authenticator">
+<a href="auth.xsl">Click to Listen</a>
+</xsl:when>
+<xsl:otherwise>
+<a href="{@mount}.m3u">Click to Listen</a>
+</xsl:otherwise>
+</xsl:choose>
+</td></tr>
+</table>
+</xsl:when>
+<xsl:otherwise>
+<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
+</xsl:otherwise>
+</xsl:choose>
+<br></br>
+<br></br>
+</xsl:for-each>
+</div>
+<div class="roundbottom">
+<img src="corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<br></br><br></br>
+</td>
+<td width="25"></td></tr>
+</table>
+<div class="poster"><img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/web/status.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/web/status2.xsl
===================================================================
--- icecast/branches/icecast-kh/web/status2.xsl	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/web/status2.xsl	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,12 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output method="html" indent="yes" />
+<xsl:template match = "/icestats" >
+<pre>
+MountPoint,Connections,Stream Name,Current Listeners,Description,Currently Playing,Stream URL
+Global,Client:<xsl:value-of select="connections" /> Source: <xsl:value-of select="source_connections" />,,<xsl:value-of select="listeners" />,,
+<xsl:for-each select="source">
+<xsl:value-of select="@mount" />,,<xsl:value-of select="name" />,<xsl:value-of select="listeners" />,<xsl:value-of select="description" />,<xsl:value-of select="artist" /> - <xsl:value-of select="title" />,<xsl:value-of select="url" />
+</xsl:for-each>
+</pre>
+</xsl:template>
+</xsl:stylesheet>


Property changes on: icecast/branches/icecast-kh/web/status2.xsl
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/web/style.css
===================================================================
--- icecast/branches/icecast-kh/web/style.css	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/web/style.css	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,198 @@
+/******************************************************************************
+
+	This file styles the bar that goes across the top of all Xiph.Org
+	pages.
+
+	The style that comes from this was first (to my knowledge) at
+	http://alistapart.com/stories/practicalcss/ in the
+	"Splitting the Difference" section.
+
+******************************************************************************/
+
+/* This effect doesn't work at all if all content is pinched in a bit. */
+html, body {
+	margin: 0;
+	padding: 0;
+}
+
+.xiphnav {
+    font-family: Verdana, sans-serif;
+	font-weight: normal;
+    padding:    .25em;
+    margin-bottom:  .5em;
+    border-bottom:  1px solid #000;
+    color: #000;
+    background: #aaa;
+}
+h2 {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    font-size: 250%;
+    color: #FFFFFF;
+}
+.nav {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    font-size: 110%;
+    color: #FFFFFF;
+}
+.nav:hover {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    color: #F8EF64;
+}
+.xiphnav_a {
+    text-decoration: none;
+    font-weight: normal;
+    color: #000;
+}
+.news {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    color: #FFFFFF;
+}
+.newsheader {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 110%;
+    color: #F8EF64;
+    background: #444444;
+}
+.streamtd {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 85%;
+    color: #FFFFFF;
+    padding:15px;
+}
+.streamtd_alt {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    font-size: 85%;
+    color: #FFFFFF;
+}
+
+.streamtd_alt_2 {
+    font-family: Verdana, sans-serif;
+    text-decoration: underline;
+    font-weight: normal;
+    font-size: 85%;
+    color: #FFFFFF;
+}
+
+td {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: normal;
+    color: #FFFFFF;
+}
+.roundcont {
+	width: 90%;
+	background-color: #656565;
+	color: #fff;
+}
+
+.newscontent {
+	margin: 0 20px;
+}
+.newscontent h3 {
+	margin: 10px 0px;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 110%;
+	color: #F8EF64;
+        border-bottom:  3px dashed #000000;
+}
+.newscontent h4 {
+	margin: 10px 0px;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: bold;
+	font-size: 110%;
+	color: #F8EF64;
+}
+.newscontent p {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+}
+.newscontent td {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+}
+.newscontent td.streamdata {
+	margin: 0 0;
+	font-family: Verdana, sans-serif;
+	text-decoration: none;
+	font-weight: none;
+	font-size: 90%;
+        color: #F8EF64;
+}
+.newscontent a {
+    font-family: Verdana, sans-serif;
+    text-decoration: underline;
+    font-weight: bold;
+    color: #F8EF64;
+}
+.newscontent a:hover {
+    font-family: Verdana, sans-serif;
+    text-decoration: underline;
+    font-weight: bold;
+    color: #FFFFFF;
+}
+.newscontent a.nav2 {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    font-weight: bold;
+    background: #444444;
+    color: #F8EF64;
+}
+.newscontent a.nav2:hover {
+    font-family: Verdana, sans-serif;
+    text-decoration: none;
+    background: #777777;
+    font-weight: bold;
+    color: #FFFFFF;
+}
+.poster {
+    font-family: Verdana, sans-serif;
+	margin: 0px 0px;
+	display: block;
+	text-decoration: none;
+	font-size: 100%;
+	color: #F8EF64;
+    border-top:  3px dashed #000000;
+}
+.roundcont p {
+	margin: 10px 50px;
+}
+
+.roundtop {
+	background: url(corner_topright.jpg) no-repeat top right;
+}
+
+.roundbottom {
+	background: url(corner_bottomright.jpg) no-repeat top right;
+}
+
+img.corner {
+	width: 15px;
+	height: 15px;
+	border: none;
+	display: block !important;
+}
+
+

Added: icecast/branches/icecast-kh/win32/ConfigTab.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/ConfigTab.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/ConfigTab.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,79 @@
+// ConfigTab.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Icecast2win.h"
+#include "ConfigTab.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CConfigTab dialog
+
+
+CConfigTab::CConfigTab(CWnd* pParent /*=NULL*/)
+	: CTabPageSSL(CConfigTab::IDD, pParent)
+{
+	//{{AFX_DATA_INIT(CConfigTab)
+	m_Config = _T("");
+	//}}AFX_DATA_INIT
+}
+
+
+void CConfigTab::DoDataExchange(CDataExchange* pDX)
+{
+	CTabPageSSL::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CConfigTab)
+	DDX_Control(pDX, IDC_CONFIG, m_ConfigCtrl);
+	DDX_Text(pDX, IDC_CONFIG, m_Config);
+	//}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CConfigTab, CTabPageSSL)
+	//{{AFX_MSG_MAP(CConfigTab)
+	ON_EN_KILLFOCUS(IDC_CONFIG, OnKillfocusConfig)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CConfigTab message handlers
+
+void CConfigTab::SaveConfiguration()
+{
+	// TODO: Add your command handler code here
+	// TODO: Add your control notification handler code here
+	FILE	*filep;
+	char	buffer[2046] = "";
+	CIcecast2winApp	*myApp = (CIcecast2winApp *)AfxGetApp();
+
+	UpdateData(TRUE);
+	if (m_Config != "") {
+		filep = fopen(myApp->m_configFile, "w");
+		if (filep) {
+			fputs(LPCSTR(m_Config), filep);
+			fclose(filep);
+		}
+	}
+}
+
+BOOL CConfigTab::OnInitDialog()
+{
+	CTabPageSSL::OnInitDialog();
+
+	// TODO: Add extra initialization here
+	AddAnchor(IDC_CONFIG, TOP_LEFT, BOTTOM_RIGHT);
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	              // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CConfigTab::OnKillfocusConfig()
+{
+	// TODO: Add your control notification handler code here
+	SaveConfiguration();
+}

Added: icecast/branches/icecast-kh/win32/ConfigTab.h
===================================================================
--- icecast/branches/icecast-kh/win32/ConfigTab.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/ConfigTab.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,50 @@
+#if !defined(AFX_CONFIGTAB_H__D8B0CC28_59FA_44E8_91A4_377C64F67DCF__INCLUDED_)
+#define AFX_CONFIGTAB_H__D8B0CC28_59FA_44E8_91A4_377C64F67DCF__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// ConfigTab.h : header file
+//
+#include "TabPageSSL.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CConfigTab dialog
+
+class CConfigTab : public CTabPageSSL
+{
+// Construction
+public:
+	void SaveConfiguration();
+	CConfigTab(CWnd* pParent = NULL);   // standard constructor
+
+// Dialog Data
+	//{{AFX_DATA(CConfigTab)
+	enum { IDD = IDD_CONFIGDIALOG };
+	CEdit	m_ConfigCtrl;
+	CString	m_Config;
+	//}}AFX_DATA
+
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CConfigTab)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+	// Generated message map functions
+	//{{AFX_MSG(CConfigTab)
+	virtual BOOL OnInitDialog();
+	afx_msg void OnKillfocusConfig();
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_CONFIGTAB_H__D8B0CC28_59FA_44E8_91A4_377C64F67DCF__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/Icecast2win.clw
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2win.clw	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2win.clw	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,152 @@
+; CLW file contains information for the MFC ClassWizard
+
+[General Info]
+Version=1
+LastClass=CStatus
+LastTemplate=CDialog
+NewFileInclude1=#include "stdafx.h"
+NewFileInclude2=#include "Icecast2win.h"
+
+ClassCount=6
+Class1=CIcecast2winApp
+Class2=CIcecast2winDlg
+Class3=CAboutDlg
+
+ResourceCount=10
+Resource1=IDD_SSTATUS
+Resource2=IDR_MAINFRAME
+Resource3=IDD_ABOUTBOX
+Resource4=IDR_MENU2
+Class4=CStatus
+Resource5=IDR_TRAY
+Class5=CConfigTab
+Class6=CStatsTab
+Resource6=IDR_MENU4
+Resource7=IDD_ICECAST2WIN_DIALOG
+Resource8=IDD_CONFIGDIALOG
+Resource9=IDR_MENU3
+Resource10=IDD_STATSDIALOG
+
+[CLS:CIcecast2winApp]
+Type=0
+HeaderFile=Icecast2win.h
+ImplementationFile=Icecast2win.cpp
+Filter=N
+
+[CLS:CIcecast2winDlg]
+Type=0
+HeaderFile=Icecast2winDlg.h
+ImplementationFile=Icecast2winDlg.cpp
+Filter=C
+LastObject=ID_ABOUT_HELP
+BaseClass=CResizableDialog
+VirtualFilter=dWC
+
+[CLS:CAboutDlg]
+Type=0
+HeaderFile=Icecast2winDlg.h
+ImplementationFile=Icecast2winDlg.cpp
+Filter=D
+
+[DLG:IDD_ABOUTBOX]
+Type=1
+Class=CAboutDlg
+ControlCount=3
+Control1=IDC_STATIC,static,1342177283
+Control2=IDC_STATIC,static,1342308480
+Control3=IDOK,button,1342373889
+
+[DLG:IDD_ICECAST2WIN_DIALOG]
+Type=1
+Class=CIcecast2winDlg
+ControlCount=8
+Control1=IDC_MAINTAB,SysTabControl32,1342177280
+Control2=IDC_START,button,1342242816
+Control3=IDC_AUTOSTART,button,1342251011
+Control4=IDC_STATIC,static,1342177294
+Control5=IDC_SERVERSTATUS,static,1342177294
+Control6=IDC_STATIC_SS,static,1342308865
+Control7=IDC_STATICBLACK,static,1342177294
+Control8=IDC_HIDESYSTRAY,button,1342242816
+
+[DLG:IDD_SSTATUS]
+Type=1
+Class=CStatus
+ControlCount=5
+Control1=IDC_FILLER2,static,1342308352
+Control2=IDC_GLOBALSTAT_LIST,SysListView32,1350631425
+Control3=IDC_STATIC_GS,static,1342308353
+Control4=IDC_STATIC_RUN,static,1342308352
+Control5=IDC_RUNNINGFOR,static,1342308352
+
+[CLS:CStatus]
+Type=0
+HeaderFile=Status.h
+ImplementationFile=Status.cpp
+BaseClass=CTabPageSSL
+Filter=D
+LastObject=ID_POPUP_ADDTOGLOBALSTATLIST
+VirtualFilter=dWC
+
+[DLG:IDD_CONFIGDIALOG]
+Type=1
+Class=CConfigTab
+ControlCount=1
+Control1=IDC_CONFIG,edit,1352732868
+
+[CLS:CConfigTab]
+Type=0
+HeaderFile=ConfigTab.h
+ImplementationFile=ConfigTab.cpp
+BaseClass=CTabPageSSL
+Filter=D
+VirtualFilter=dWC
+LastObject=IDC_CONFIG
+
+[DLG:IDD_STATSDIALOG]
+Type=1
+Class=CStatsTab
+ControlCount=5
+Control1=IDC_STATSLIST,SysListView32,1350631425
+Control2=IDC_SOURCELIST,SysListView32,1350631425
+Control3=IDC_FILLER1,static,1342308352
+Control4=IDC_STATIC_SLS,static,1342308353
+Control5=IDC_STATIC,static,1342308352
+
+[CLS:CStatsTab]
+Type=0
+HeaderFile=StatsTab.h
+ImplementationFile=StatsTab.cpp
+BaseClass=CTabPageSSL
+Filter=D
+VirtualFilter=dWC
+LastObject=IDC_SOURCELIST
+
+[MNU:IDR_MENU2]
+Type=1
+Class=?
+Command1=ID_POPUP_ADDTOGLOBALSTATLIST
+CommandCount=1
+
+[MNU:IDR_MENU3]
+Type=1
+Class=?
+Command1=ID__DELETEFROMGLOBALSTATS
+Command2=ID__MAKETHISSTATTHEWINDOWTITLE
+CommandCount=2
+
+[MNU:IDR_TRAY]
+Type=1
+Class=CIcecast2winDlg
+Command1=ID_BLANK_RESTORE
+CommandCount=1
+
+[MNU:IDR_MENU4]
+Type=1
+Class=CIcecast2winDlg
+Command1=ID_FILE_EXIT
+Command2=ID_FILE_EDITCONFIGURATION
+Command3=ID_ABOUT_HELP
+Command4=ID_ABOUT_CREDITS
+CommandCount=4
+


Property changes on: icecast/branches/icecast-kh/win32/Icecast2win.clw
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/icecast-kh/win32/Icecast2win.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2win.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2win.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,89 @@
+// Icecast2win.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafx.h"
+#include "Icecast2win.h"
+#include "Icecast2winDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winApp
+
+BEGIN_MESSAGE_MAP(CIcecast2winApp, CWinApp)
+	//{{AFX_MSG_MAP(CIcecast2winApp)
+		// NOTE - the ClassWizard will add and remove mapping macros here.
+		//    DO NOT EDIT what you see in these blocks of generated code!
+	//}}AFX_MSG
+	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+#include "colors.h"
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winApp construction
+
+CIcecast2winApp::CIcecast2winApp()
+{
+	// TODO: add construction code here,
+	// Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CIcecast2winApp object
+
+CIcecast2winApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winApp initialization
+
+BOOL CIcecast2winApp::InitInstance()
+{
+	AfxEnableControlContainer();
+
+	// Standard initialization
+	// If you are not using these features and wish to reduce the size
+	//  of your final executable, you should remove from the following
+	//  the specific initialization routines you do not need.
+
+	if (strlen(m_lpCmdLine) > 0) {
+		strcpy(m_configFile, m_lpCmdLine);
+	}
+	else {
+		strcpy(m_configFile, ".\\icecast.xml");
+	}
+
+
+
+#ifdef _AFXDLL
+	Enable3dControls();			// Call this when using MFC in a shared DLL
+#else
+	Enable3dControlsStatic();	// Call this when linking to MFC statically
+#endif
+
+	CIcecast2winDlg dlg;
+	m_pMainWnd = &dlg;
+
+//	SetDialogBkColor(BGCOLOR,TEXTCOLOR);
+
+	m_pIconList[0] = LoadIcon (MAKEINTRESOURCE(IDR_MAINFRAME));
+
+	int nResponse = dlg.DoModal();
+	if (nResponse == IDOK)
+	{
+		// TODO: Place code here to handle when the dialog is
+		//  dismissed with OK
+	}
+	else if (nResponse == IDCANCEL)
+	{
+		// TODO: Place code here to handle when the dialog is
+		//  dismissed with Cancel
+	}
+
+	// Since the dialog has been closed, return FALSE so that we exit the
+	//  application, rather than start the application's message pump.
+	return FALSE;
+}

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

Added: icecast/branches/icecast-kh/win32/Icecast2win.dsw
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2win.dsw	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2win.dsw	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,47 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Icecast2win"=.\Icecast2win.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name libxml
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name icecast
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "icecast"=.\icecast.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

Added: icecast/branches/icecast-kh/win32/Icecast2win.h
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2win.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2win.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,52 @@
+// Icecast2win.h : main header file for the ICECAST2WIN application
+//
+
+#if !defined(AFX_ICECAST2WIN_H__76A528C9_A424_4417_BFDF_0E556A9EE4F1__INCLUDED_)
+#define AFX_ICECAST2WIN_H__76A528C9_A424_4417_BFDF_0E556A9EE4F1__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+#error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h"		// main symbols
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winApp:
+// See Icecast2win.cpp for the implementation of this class
+//
+
+class CIcecast2winApp : public CWinApp
+{
+public:
+	char m_configFile[1024];
+	HICON m_pIconList[2];
+	CIcecast2winApp();
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CIcecast2winApp)
+	public:
+	virtual BOOL InitInstance();
+	//}}AFX_VIRTUAL
+
+// Implementation
+
+	//{{AFX_MSG(CIcecast2winApp)
+		// NOTE - the ClassWizard will add and remove member functions here.
+		//    DO NOT EDIT what you see in these blocks of generated code !
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+extern CIcecast2winApp theApp;
+#endif // !defined(AFX_ICECAST2WIN_H__76A528C9_A424_4417_BFDF_0E556A9EE4F1__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/Icecast2win.rc
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2win.rc	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2win.rc	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,301 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+    "#define _AFX_NO_OLE_RESOURCES\r\n"
+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+    "\r\n"
+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+    "#ifdef _WIN32\r\n"
+    "LANGUAGE 9, 1\r\n"
+    "#pragma code_page(1252)\r\n"
+    "#endif //_WIN32\r\n"
+    "#include ""res\\Icecast2win.rc2""  // non-Microsoft Visual C++ edited resources\r\n"
+    "#include ""afxres.rc""         // Standard components\r\n"
+    "#endif\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 235, 55
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About Icecast2win"
+FONT 8, "MS Sans Serif"
+BEGIN
+    ICON            IDR_MAINFRAME,IDC_STATIC,11,17,20,20
+    LTEXT           "Icecast 2 Win32",IDC_STATIC,40,10,119,8,SS_NOPREFIX
+    DEFPUSHBUTTON   "OK",IDOK,178,7,50,14,WS_GROUP
+END
+
+IDD_ICECAST2WIN_DIALOG DIALOGEX 0, 0, 318, 249
+STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+    WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "icecast2"
+MENU IDR_MENU4
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Tab1",IDC_MAINTAB,"SysTabControl32",0x0,0,48,318,201
+    PUSHBUTTON      "Start Server",IDC_START,17,31,56,14
+    CONTROL         "Start Server on Application Startup",IDC_AUTOSTART,
+                    "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,76,
+                    29,81,17
+    CONTROL         159,IDC_STATIC,"Static",SS_BITMAP,0,0,117,20
+    CONTROL         151,IDC_SERVERSTATUS,"Static",SS_BITMAP,173,32,65,18,
+                    WS_EX_CLIENTEDGE
+    CTEXT           "Server Status",IDC_STATIC_SS,172,23,65,10,
+                    SS_CENTERIMAGE
+    CONTROL         150,IDC_STATICBLACK,"Static",SS_BITMAP,116,0,200,20
+    PUSHBUTTON      "Hide To Systray",IDC_HIDESYSTRAY,257,22,58,12
+END
+
+IDD_SSTATUS DIALOG DISCARDABLE  0, 0, 320, 210
+STYLE WS_CHILD
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT           "",IDC_FILLER2,0,199,319,11
+    CONTROL         "List1",IDC_GLOBALSTAT_LIST,"SysListView32",LVS_REPORT |
+                    WS_BORDER | WS_TABSTOP,9,34,300,147
+    CTEXT           "Global Statistics",IDC_STATIC_GS,9,6,300,10
+    LTEXT           "Server Has Been Running For ",IDC_STATIC_RUN,46,184,100,
+                    8
+    LTEXT           "",IDC_RUNNINGFOR,149,184,156,8
+END
+
+IDD_CONFIGDIALOG DIALOG DISCARDABLE  0, 0, 320, 210
+STYLE WS_CHILD
+FONT 8, "MS Sans Serif"
+BEGIN
+    EDITTEXT        IDC_CONFIG,0,0,320,210,ES_MULTILINE | ES_AUTOVSCROLL |
+                    ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+END
+
+IDD_STATSDIALOG DIALOG DISCARDABLE  0, 0, 320, 210
+STYLE WS_CHILD
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "List1",IDC_STATSLIST,"SysListView32",LVS_REPORT |
+                    WS_BORDER | WS_TABSTOP,142,34,149,160
+    CONTROL         "List2",IDC_SOURCELIST,"SysListView32",LVS_REPORT |
+                    WS_BORDER | WS_TABSTOP,9,34,111,159
+    LTEXT           "",IDC_FILLER1,0,199,320,11
+    CTEXT           "Source Level Statistics",IDC_STATIC_SLS,9,7,300,10
+    LTEXT           "Click source to view statistics",IDC_STATIC,18,25,111,8
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0"
+        BEGIN
+            VALUE "CompanyName", "\0"
+            VALUE "FileDescription", "Icecast2win MFC Application\0"
+            VALUE "FileVersion", "1, 0, 0, 1\0"
+            VALUE "InternalName", "Icecast2win\0"
+            VALUE "LegalCopyright", "Copyright (C) 2001\0"
+            VALUE "LegalTrademarks", "\0"
+            VALUE "OriginalFilename", "Icecast2win.EXE\0"
+            VALUE "ProductName", "Icecast2win Application\0"
+            VALUE "ProductVersion", "1, 0, 0, 1\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+    IDD_ABOUTBOX, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 228
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 48
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_BITMAP4             BITMAP  DISCARDABLE     "black.bmp"
+IDB_BITMAP5             BITMAP  DISCARDABLE     "stopped.bmp"
+IDB_BITMAP6             BITMAP  DISCARDABLE     "running.bmp"
+IDB_BITMAP7             BITMAP  DISCARDABLE     "icecast2logo2.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME           ICON    DISCARDABLE     "icecast.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU2 MENU DISCARDABLE
+BEGIN
+    POPUP "Popup"
+    BEGIN
+        MENUITEM "Add To Global Stat List",     ID_POPUP_ADDTOGLOBALSTATLIST
+    END
+END
+
+IDR_MENU3 MENU DISCARDABLE
+BEGIN
+    POPUP " "
+    BEGIN
+        MENUITEM "Delete From Global Stats",    ID__DELETEFROMGLOBALSTATS
+        MENUITEM "Make this stat the window title",
+                                                ID__MAKETHISSTATTHEWINDOWTITLE
+
+    END
+END
+
+IDR_TRAY MENU DISCARDABLE
+BEGIN
+    POPUP "Blank"
+    BEGIN
+        MENUITEM "Restore",                     ID_BLANK_RESTORE
+    END
+END
+
+IDR_MENU4 MENU DISCARDABLE
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "E&xit",                       ID_FILE_EXIT
+    END
+    POPUP "C&onfiguration"
+    BEGIN
+        MENUITEM "&Edit Configuration",         ID_FILE_EDITCONFIGURATION
+    END
+    POPUP "&About"
+    BEGIN
+        MENUITEM "H&elp",                       ID_ABOUT_HELP
+        MENUITEM "Cr&edits",                    ID_ABOUT_CREDITS
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+    IDS_ABOUTBOX            "&About Icecast2win..."
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "res\Icecast2win.rc2"  // non-Microsoft Visual C++ edited resources
+#include "afxres.rc"         // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+

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

Added: icecast/branches/icecast-kh/win32/Icecast2winDlg.h
===================================================================
--- icecast/branches/icecast-kh/win32/Icecast2winDlg.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Icecast2winDlg.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,124 @@
+// Icecast2winDlg.h : header file
+//
+
+#if !defined(AFX_ICECAST2WINDLG_H__23B4DA8B_C9BC_49C8_A62C_37FC6BC5E54A__INCLUDED_)
+#define AFX_ICECAST2WINDLG_H__23B4DA8B_C9BC_49C8_A62C_37FC6BC5E54A__INCLUDED_
+
+#include "ResizableDialog.h"
+#include "TabCtrlSSL.h"
+#include "TabPageSSL.h"
+
+#include "ConfigTab.h"
+#include "StatsTab.h"
+#include "Status.h"
+#include "TrayNot.h"
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+/////////////////////////////////////////////////////////////////////////////
+// CIcecast2winDlg dialog
+
+class CIcecast2winDlg : public CResizableDialog
+{
+// Construction
+public:
+	time_t serverStart;
+	void config_read();
+	void config_write();
+	void UpdateStatsLists();
+	CConfigTab	configTab;
+	CStatsTab	statsTab;
+	CStatus		statusTab;
+	int	m_colSource0Width;
+	int	m_colStats0Width;
+	int	m_colStats1Width;
+	int	m_colGStats0Width;
+	int	m_colGStats1Width;
+	int	m_colGStats2Width;
+	CFont labelFont;
+	CBitmap runningBitmap;
+	CBitmap stoppedBitmap;
+	CTrayNot* m_pTray;
+	BOOL m_bHidden;
+	int  m_iconSwap;
+
+
+
+
+
+	void StopServer();
+	bool m_isRunning;
+	void DisableControl(UINT control);
+	void EnableControl(UINT control);
+	void getTag(char *pbuf, char *ptag, char *dest);
+	CString m_ErrorLog;
+	CString m_AccessLog;
+	void ParseConfig();
+	void LoadConfig();
+	CIcecast2winDlg(CWnd* pParent = NULL);	// standard constructor
+
+// Dialog Data
+	//{{AFX_DATA(CIcecast2winDlg)
+	enum { IDD = IDD_ICECAST2WIN_DIALOG };
+	CStatic	m_SS;
+	CStatic	m_ServerStatusBitmap;
+	CStatic	m_iceLogo;
+	CButton	m_StartButton;
+	CEdit	m_StatsEditCtrl;
+	CEdit	m_ConfigEditCtrl;
+	CEdit	m_ErrorEditCtrl;
+	CEdit	m_AccessEditCtrl;
+	CTabCtrlSSL	m_MainTab;
+	CString	m_AccessEdit;
+	CString	m_ErrorEdit;
+	CString	m_ConfigEdit;
+	CString	m_ServerStatus;
+	CString	m_SourcesConnected;
+	CString	m_NumClients;
+	FILE	*filep_accesslog;
+	FILE	*filep_errorlog;
+	CString	m_StatsEdit;
+	BOOL	m_Autostart;
+	//}}AFX_DATA
+
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CIcecast2winDlg)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+	HICON m_hIcon;
+
+	// Generated message map functions
+	//{{AFX_MSG(CIcecast2winDlg)
+	virtual BOOL OnInitDialog();
+	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
+	afx_msg void OnPaint();
+	afx_msg HCURSOR OnQueryDragIcon();
+	afx_msg void OnSelchangeMaintab(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnFileExit();
+	afx_msg void OnTimer(UINT nIDEvent);
+	afx_msg void OnFileStartserver();
+	afx_msg void OnFileStopserver();
+	afx_msg void OnStart();
+	afx_msg void OnClose();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnHidesystray();
+	afx_msg void OnHide();
+	afx_msg void OnBlankRestore();
+	afx_msg LONG OnTrayNotify ( WPARAM wParam, LPARAM lParam );
+	afx_msg void OnDestroy();
+	afx_msg void OnFileEditconfiguration();
+	afx_msg void OnAboutHelp();
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_ICECAST2WINDLG_H__23B4DA8B_C9BC_49C8_A62C_37FC6BC5E54A__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/win32/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,15 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = res
+
+EXTRA_DIST = ConfigTab.cpp ConfigTab.h Icecast2win.clw Icecast2win.cpp \
+    Icecast2win.dsp Icecast2win.dsw Icecast2win.h Icecast2win.rc \
+    Icecast2winDlg.cpp Icecast2winDlg.h Makefile.am ResizableDialog.cpp \
+    ResizableDialog.h StatsTab.cpp StatsTab.h Status.cpp Status.h StdAfx.cpp \
+    StdAfx.h TabCtrlSSL.cpp TabCtrlSSL.h TabPageSSL.cpp TabPageSSL.h black.bmp \
+    colors.h icecast.dsp icecast.ico icecast2.iss icecast2logo2.bmp\
+    resource.h running.bmp stopped.bmp TRAYNOT.h Traynot.cpp \
+    icecast2_console.dsw icecast2_console.dsp
+

Added: icecast/branches/icecast-kh/win32/ResizableDialog.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/ResizableDialog.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/ResizableDialog.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,442 @@
+// ResizableDialog.cpp : implementation file
+//
+/////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2000 by Paolo Messina
+// (ppescher at yahoo.com)
+//
+// Free for non-commercial use.
+// You may change the code to your needs,
+// provided that credits to the original
+// author is given in the modified files.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "ResizableDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CResizableDialog
+
+inline void CResizableDialog::Construct()
+{
+	m_bInitDone = FALSE;
+
+	m_bUseMinTrack = TRUE;
+	m_bUseMaxTrack = FALSE;
+	m_bUseMaxRect = FALSE;
+
+	m_bShowGrip = TRUE;
+
+	m_bEnableSaveRestore = FALSE;
+
+	m_szGripSize.cx = GetSystemMetrics(SM_CXVSCROLL);
+	m_szGripSize.cy = GetSystemMetrics(SM_CYHSCROLL);
+}
+
+CResizableDialog::CResizableDialog()
+{
+	Construct();
+}
+
+CResizableDialog::CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd)
+	: CDialog(nIDTemplate, pParentWnd)
+{
+	Construct();
+}
+
+CResizableDialog::CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd)
+	: CDialog(lpszTemplateName, pParentWnd)
+{
+	Construct();
+}
+
+CResizableDialog::~CResizableDialog()
+{
+	// for safety
+	m_arrLayout.RemoveAll();
+}
+
+
+BEGIN_MESSAGE_MAP(CResizableDialog, CDialog)
+	//{{AFX_MSG_MAP(CResizableDialog)
+	ON_WM_NCHITTEST()
+	ON_WM_GETMINMAXINFO()
+	ON_WM_SIZE()
+	ON_WM_DESTROY()
+	ON_WM_PAINT()
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CResizableDialog message handlers
+
+
+BOOL CResizableDialog::OnInitDialog()
+{
+	CDialog::OnInitDialog();
+
+	UpdateGripPos();
+
+	// gets the template size as the min track size
+	CRect rc;
+	GetWindowRect(&rc);
+	m_ptMinTrackSize.x = rc.Width();
+	m_ptMinTrackSize.y = rc.Height();
+
+	m_bInitDone = TRUE;
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	              // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CResizableDialog::OnDestroy()
+{
+	CDialog::OnDestroy();
+
+	if (m_bEnableSaveRestore)
+		SaveWindowRect();
+
+	// remove old windows
+	m_arrLayout.RemoveAll();
+}
+
+void CResizableDialog::OnPaint()
+{
+	CPaintDC dc(this); // device context for painting
+
+	if (m_bShowGrip && !IsZoomed())
+	{
+		// draw size-grip
+		dc.DrawFrameControl(&m_rcGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+	}
+}
+
+void CResizableDialog::OnSize(UINT nType, int cx, int cy)
+{
+	CWnd::OnSize(nType, cx, cy);
+
+	if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
+		return;		// arrangement not needed
+
+	if (m_bInitDone)
+	{
+		ArrangeLayout();
+	}
+}
+
+UINT CResizableDialog::OnNcHitTest(CPoint point)
+{
+	CPoint pt = point;
+	ScreenToClient(&pt);
+
+	// if in size grip and in client area
+	if (m_bShowGrip && m_rcGripRect.PtInRect(pt) &&
+		pt.x >= 0 && pt.y >= 0)
+		return HTBOTTOMRIGHT;
+
+	return CDialog::OnNcHitTest(point);
+}
+
+void CResizableDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
+{
+	if (!m_bInitDone)
+		return;
+
+	if (m_bUseMinTrack)
+		lpMMI->ptMinTrackSize = m_ptMinTrackSize;
+
+	if (m_bUseMaxTrack)
+		lpMMI->ptMaxTrackSize = m_ptMaxTrackSize;
+
+	if (m_bUseMaxRect)
+	{
+		lpMMI->ptMaxPosition = m_ptMaxPos;
+		lpMMI->ptMaxSize = m_ptMaxSize;
+	}
+}
+
+// layout functions
+
+void CResizableDialog::AddAnchor(HWND wnd, CSize tl_type, CSize br_type)
+{
+	ASSERT(wnd != NULL && ::IsWindow(wnd));
+	ASSERT(::IsChild(*this, wnd));
+	ASSERT(tl_type != NOANCHOR);
+
+	// get control's window class
+
+	CString st;
+	GetClassName(wnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH);
+	st.ReleaseBuffer();
+	st.MakeUpper();
+
+	// add the style 'clipsiblings' to a GroupBox
+	// to avoid unnecessary repainting of controls inside
+	if (st == "BUTTON")
+	{
+		DWORD style = GetWindowLong(wnd, GWL_STYLE);
+		if (style & BS_GROUPBOX)
+			SetWindowLong(wnd, GWL_STYLE, style | WS_CLIPSIBLINGS);
+	}
+
+	// wnd classes that don't redraw client area correctly
+	// when the hor scroll pos changes due to a resizing
+	BOOL hscroll = FALSE;
+	if (st == "LISTBOX")
+		hscroll = TRUE;
+
+	// wnd classes that need refresh when resized
+	BOOL refresh = FALSE;
+	if (st == "STATIC")
+	{
+		DWORD style = GetWindowLong(wnd, GWL_STYLE);
+
+		switch (style & SS_TYPEMASK)
+		{
+		case SS_LEFT:
+		case SS_CENTER:
+		case SS_RIGHT:
+			// word-wrapped text needs refresh
+			refresh = TRUE;
+		}
+
+		// centered images or text need refresh
+		if (style & SS_CENTERIMAGE)
+			refresh = TRUE;
+
+		// simple text never needs refresh
+		if (style & SS_TYPEMASK == SS_SIMPLE)
+			refresh = FALSE;
+	}
+
+	// get dialog's and control's rect
+	CRect wndrc, objrc;
+
+	GetClientRect(&wndrc);
+	::GetWindowRect(wnd, &objrc);
+	ScreenToClient(&objrc);
+
+	CSize tl_margin, br_margin;
+
+	if (br_type == NOANCHOR)
+		br_type = tl_type;
+
+	// calculate margin for the top-left corner
+
+	tl_margin.cx = objrc.left - wndrc.Width() * tl_type.cx / 100;
+	tl_margin.cy = objrc.top - wndrc.Height() * tl_type.cy / 100;
+
+	// calculate margin for the bottom-right corner
+
+	br_margin.cx = objrc.right - wndrc.Width() * br_type.cx / 100;
+	br_margin.cy = objrc.bottom - wndrc.Height() * br_type.cy / 100;
+
+	// add to the list
+	Layout obj(wnd, tl_type, tl_margin,	br_type, br_margin, hscroll, refresh);
+	m_arrLayout.Add(obj);
+}
+
+void CResizableDialog::ArrangeLayout()
+{
+	// update size-grip
+	InvalidateRect(&m_rcGripRect);
+	UpdateGripPos();
+	InvalidateRect(&m_rcGripRect);
+
+	// init some vars
+	CRect wndrc;
+	GetClientRect(&wndrc);
+
+	int i, count = m_arrLayout.GetSize();
+	HDWP hdwp = BeginDeferWindowPos(count);
+
+	for (i=0; i<count; ++i)
+	{
+		Layout& obj = m_arrLayout[i];
+
+		CRect objrc, newrc;
+		CWnd* wnd = CWnd::FromHandle(obj.hwnd); // temporary solution
+
+		wnd->GetWindowRect(&objrc);
+		ScreenToClient(&objrc);
+
+		// calculate new top-left corner
+
+		newrc.left = obj.tl_margin.cx + wndrc.Width() * obj.tl_type.cx / 100;
+		newrc.top = obj.tl_margin.cy + wndrc.Height() * obj.tl_type.cy / 100;
+
+		// calculate new bottom-right corner
+
+		newrc.right = obj.br_margin.cx + wndrc.Width() * obj.br_type.cx / 100;
+		newrc.bottom = obj.br_margin.cy + wndrc.Height() * obj.br_type.cy / 100;
+
+		if (!newrc.EqualRect(&objrc))
+		{
+			if (obj.adj_hscroll)
+			{
+				// needs repainting, due to horiz scrolling
+				int diff = newrc.Width() - objrc.Width();
+				int max = wnd->GetScrollLimit(SB_HORZ);
+
+				obj.need_refresh = FALSE;
+				if (max > 0 && wnd->GetScrollPos(SB_HORZ) > max - diff)
+				{
+					obj.need_refresh = TRUE;
+				}
+			}
+
+			// set flags
+			DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION;
+			if (newrc.TopLeft() == objrc.TopLeft())
+				flags |= SWP_NOMOVE;
+			if (newrc.Size() == objrc.Size())
+				flags |= SWP_NOSIZE;
+
+			DeferWindowPos(hdwp, obj.hwnd, NULL, newrc.left, newrc.top,
+				newrc.Width(), newrc.Height(), flags);
+		}
+	}
+	// go re-arrange child windows
+	EndDeferWindowPos(hdwp);
+
+	// refresh those that need
+	for (i=0; i<count; ++i)
+	{
+		Layout& obj = m_arrLayout[i];
+		CWnd* wnd = CWnd::FromHandle(obj.hwnd); // temporary solution
+
+		if (obj.need_refresh)
+		{
+			wnd->Invalidate();
+			wnd->UpdateWindow();
+		}
+	}
+}
+
+void CResizableDialog::UpdateGripPos()
+{
+	// size-grip goes bottom right in the client area
+
+	GetClientRect(&m_rcGripRect);
+
+	m_rcGripRect.left = m_rcGripRect.right - m_szGripSize.cx;
+	m_rcGripRect.top = m_rcGripRect.bottom - m_szGripSize.cy;
+}
+
+// protected members
+
+void CResizableDialog::ShowSizeGrip(BOOL bShow)
+{
+	if (m_bShowGrip != bShow)
+	{
+		m_bShowGrip = bShow;
+		InvalidateRect(&m_rcGripRect);
+	}
+}
+
+void CResizableDialog::SetMaximizedRect(const CRect& rc)
+{
+	m_bUseMaxRect = TRUE;
+
+	m_ptMaxPos = rc.TopLeft();
+	m_ptMaxSize.x = rc.Width();
+	m_ptMaxSize.y = rc.Height();
+}
+
+void CResizableDialog::ResetMaximizedRect()
+{
+	m_bUseMaxRect = FALSE;
+}
+
+void CResizableDialog::SetMinTrackSize(const CSize& size)
+{
+	m_bUseMinTrack = TRUE;
+
+	m_ptMinTrackSize.x = size.cx;
+	m_ptMinTrackSize.y = size.cy;
+}
+
+void CResizableDialog::ResetMinTrackSize()
+{
+	m_bUseMinTrack = FALSE;
+}
+
+void CResizableDialog::SetMaxTrackSize(const CSize& size)
+{
+	m_bUseMaxTrack = TRUE;
+
+	m_ptMaxTrackSize.x = size.cx;
+	m_ptMaxTrackSize.y = size.cy;
+}
+
+void CResizableDialog::ResetMaxTrackSize()
+{
+	m_bUseMaxTrack = FALSE;
+}
+
+// NOTE: this must be called after all the other settings
+//       to have the dialog and its controls displayed properly
+void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry)
+{
+	m_sSection = pszSection;
+	m_sEntry = pszEntry;
+
+	m_bEnableSaveRestore = TRUE;
+
+	LoadWindowRect();
+}
+
+
+// used to save/restore window's size and position
+// either in the registry or a private .INI file
+// depending on your application settings
+
+#define PROFILE_FMT 	_T("%d,%d,%d,%d,%d,%d")
+
+void CResizableDialog::SaveWindowRect()
+{
+	CString data;
+	WINDOWPLACEMENT wp;
+
+	ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
+	wp.length = sizeof(WINDOWPLACEMENT);
+	GetWindowPlacement(&wp);
+
+	RECT& rc = wp.rcNormalPosition;	// alias
+
+	data.Format(PROFILE_FMT, rc.left, rc.top,
+		rc.right, rc.bottom, wp.showCmd, wp.flags);
+
+	AfxGetApp()->WriteProfileString(m_sSection, m_sEntry, data);
+}
+
+void CResizableDialog::LoadWindowRect()
+{
+	CString data;
+	WINDOWPLACEMENT wp;
+
+	data = AfxGetApp()->GetProfileString(m_sSection, m_sEntry);
+
+	if (data.IsEmpty())	// never saved before
+		return;
+
+	ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
+	wp.length = sizeof(WINDOWPLACEMENT);
+	GetWindowPlacement(&wp);
+
+	RECT& rc = wp.rcNormalPosition;	// alias
+
+	if (_stscanf(data, PROFILE_FMT, &rc.left, &rc.top,
+		&rc.right, &rc.bottom, &wp.showCmd, &wp.flags) == 6)
+	{
+		SetWindowPlacement(&wp);
+	}
+}

Added: icecast/branches/icecast-kh/win32/ResizableDialog.h
===================================================================
--- icecast/branches/icecast-kh/win32/ResizableDialog.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/ResizableDialog.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,173 @@
+#if !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_)
+#define AFX_RESIZABLEDIALOG_H__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// ResizableDialog.h : header file
+//
+/////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2000 by Paolo Messina
+// (ppescher at yahoo.com)
+//
+// Free for non-commercial use.
+// You may change the code to your needs,
+// provided that credits to the original
+// author is given in the modified files.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#include <afxtempl.h>
+#include <afxwin.h>
+
+// useful compatibility constants (the only one required is NOANCHOR)
+
+#if !defined(__SIZE_ANCHORS_)
+#define __SIZE_ANCHORS_
+
+const CSize
+	NOANCHOR(-1,-1),
+	TOP_LEFT(0,0), TOP_CENTER(50,0), TOP_RIGHT(100,0),
+	MIDDLE_LEFT(0,50), MIDDLE_CENTER(50,50), MIDDLE_RIGHT(100,50),
+	BOTTOM_LEFT(0,100), BOTTOM_CENTER(50,100), BOTTOM_RIGHT(100,100);
+
+#endif // !defined(__SIZE_ANCHORS_)
+
+/////////////////////////////////////////////////////////////////////////////
+// CResizableDialog window
+
+class CResizableDialog : public CDialog
+{
+
+// Construction
+public:
+	CResizableDialog();
+	CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL);
+	CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);
+
+// Attributes
+private:
+	// flags
+	BOOL m_bShowGrip;
+	BOOL m_bUseMaxTrack;
+	BOOL m_bUseMinTrack;
+	BOOL m_bUseMaxRect;
+	BOOL m_bEnableSaveRestore;
+
+	// internal status
+	CString m_sSection;			// section name and
+	CString m_sEntry;			// entry for save/restore
+
+	BOOL m_bInitDone;			// if all internal vars initialized
+
+	SIZE m_szGripSize;			// set at construction time
+
+	CRect m_rcGripRect;			// current pos of grip
+
+	POINT m_ptMinTrackSize;		// min tracking size
+	POINT m_ptMaxTrackSize;		// max tracking size
+	POINT m_ptMaxPos;			// maximized position
+	POINT m_ptMaxSize;			// maximized size
+
+	class Layout
+	{
+	public:
+		HWND hwnd;
+
+		BOOL adj_hscroll;
+		BOOL need_refresh;
+
+		// upper-left corner
+		CSize tl_type;
+		CSize tl_margin;
+
+		// bottom-right corner
+		CSize br_type;
+		CSize br_margin;
+
+	public:
+		Layout()
+			: hwnd(NULL), adj_hscroll(FALSE), need_refresh(FALSE),
+			tl_type(0,0), tl_margin(0,0),
+			br_type(0,0), br_margin(0,0)
+		{
+		};
+
+		Layout(HWND hw, SIZE tl_t, SIZE tl_m,
+			SIZE br_t, SIZE br_m, BOOL hscroll, BOOL refresh)
+		{
+			hwnd = hw;
+
+			adj_hscroll = hscroll;
+			need_refresh = refresh;
+
+			tl_type = tl_t;
+			tl_margin = tl_m;
+
+			br_type = br_t;
+			br_margin = br_m;
+		};
+	};
+
+	CArray<Layout, Layout&> m_arrLayout;	// list of repositionable controls
+
+// Operations
+public:
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CResizableDialog)
+	//}}AFX_VIRTUAL
+
+// Implementation
+public:
+	virtual ~CResizableDialog();
+
+// used internally
+private:
+	void Construct();
+	void LoadWindowRect();
+	void SaveWindowRect();
+	void ArrangeLayout();
+	void UpdateGripPos();
+
+// callable from derived classes
+//protected:
+public:
+	void AddAnchor(HWND wnd, CSize tl_type,
+		CSize br_type = NOANCHOR);	// add anchors to a control
+	void AddAnchor(UINT ctrl_ID, CSize tl_type,
+		CSize br_type = NOANCHOR)	// add anchors to a control
+	{
+		AddAnchor(::GetDlgItem(*this, ctrl_ID), tl_type, br_type);
+	};
+	void ShowSizeGrip(BOOL bShow);				// show or hide the size grip
+	void SetMaximizedRect(const CRect& rc);		// set window rect when maximized
+	void ResetMaximizedRect();					// reset to default maximized rect
+	void SetMinTrackSize(const CSize& size);	// set minimum tracking size
+	void ResetMinTrackSize();					// reset to default minimum tracking size
+	void SetMaxTrackSize(const CSize& size);	// set maximum tracking size
+	void ResetMaxTrackSize();					// reset to default maximum tracking size
+	void EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry);	// section and entry in app's profile
+
+// Generated message map functions
+protected:
+	//{{AFX_MSG(CResizableDialog)
+	virtual BOOL OnInitDialog();
+	afx_msg UINT OnNcHitTest(CPoint point);
+	afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnDestroy();
+	afx_msg void OnPaint();
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_)
\ No newline at end of file

Added: icecast/branches/icecast-kh/win32/StatsTab.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/StatsTab.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/StatsTab.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,149 @@
+// StatsTab.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Icecast2win.h"
+#include "StatsTab.h"
+
+#include "Icecast2winDlg.h"
+
+extern CIcecast2winDlg	*g_mainDialog;
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern void AddToAdditionalGlobalStats(CString source, CString name);
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatsTab dialog
+
+
+CStatsTab::CStatsTab(CWnd* pParent /*=NULL*/)
+	: CTabPageSSL(CStatsTab::IDD, pParent)
+{
+	//{{AFX_DATA_INIT(CStatsTab)
+		// NOTE: the ClassWizard will add member initialization here
+	//}}AFX_DATA_INIT
+}
+
+
+void CStatsTab::DoDataExchange(CDataExchange* pDX)
+{
+	CTabPageSSL::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CStatsTab)
+	DDX_Control(pDX, IDC_STATIC_SLS, m_SLS);
+	DDX_Control(pDX, IDC_STATSLIST, m_StatsListCtrl);
+	DDX_Control(pDX, IDC_SOURCELIST, m_SourceListCtrl);
+	//}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CStatsTab, CTabPageSSL)
+	//{{AFX_MSG_MAP(CStatsTab)
+	ON_NOTIFY(NM_DBLCLK, IDC_SOURCELIST, OnDblclkSourcelist)
+	ON_NOTIFY(NM_RCLICK, IDC_STATSLIST, OnRclickStatslist)
+	ON_NOTIFY(NM_CLICK, IDC_SOURCELIST, OnClickSourcelist)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatsTab message handlers
+
+BOOL CStatsTab::OnInitDialog()
+{
+	CTabPageSSL::OnInitDialog();
+
+	// TODO: Add extra initialization here
+	m_SourceListCtrl.InsertColumn(0, _T("Source"), LVCFMT_LEFT, m_colSource0Width);
+	m_StatsListCtrl.InsertColumn(0, _T("Statistic"), LVCFMT_LEFT, m_colStats0Width);
+	m_StatsListCtrl.InsertColumn(1, _T("Value"), LVCFMT_LEFT, m_colStats1Width);
+
+	AddAnchor(IDC_STATSLIST, TOP_LEFT, BOTTOM_RIGHT);
+	AddAnchor(IDC_SOURCELIST, TOP_LEFT, BOTTOM_LEFT);
+	AddAnchor(IDC_FILLER1, BOTTOM_LEFT, BOTTOM_RIGHT);
+
+	m_SourceListCtrl.SetSelectionMark(0);
+	m_SLS.SetFont(&(g_mainDialog->labelFont), TRUE);
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	              // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CStatsTab::OnDblclkSourcelist(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// TODO: Add your control notification handler code here
+	g_mainDialog->statsTab.m_StatsListCtrl.DeleteAllItems();
+	g_mainDialog->UpdateStatsLists();
+	*pResult = 0;
+}
+
+void CStatsTab::OnRclickStatslist(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// TODO: Add your control notification handler code here
+	CMenu	menu;
+
+
+    CPoint point;
+    ::GetCursorPos(&point); //where is the mouse?
+
+    DWORD dwSelectionMade;
+    menu.LoadMenu(IDR_MENU2);
+    CMenu *pmenuPopup = menu.GetSubMenu(0);
+    dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|TPM_LEFTBUTTON|
+                                                       TPM_NONOTIFY|TPM_RETURNCMD),
+                                                       point.x, point.y, this);
+
+    pmenuPopup->DestroyMenu();
+	char	msg[255] ="";
+	char	buffer[1024] = "";
+	char	buffer2[1024] = "";
+
+	CString name;
+	CString source;
+	POSITION pos;
+	switch (dwSelectionMade) {
+	case ID_POPUP_ADDTOGLOBALSTATLIST :
+		pos = m_StatsListCtrl.GetFirstSelectedItemPosition();
+		if (pos != NULL) {
+			int nItem = m_StatsListCtrl.GetNextSelectedItem(pos);
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_TEXT;
+			lvi.iItem = nItem;
+			lvi.iSubItem = 0;
+			lvi.pszText = buffer;
+			lvi.cchTextMax = sizeof(buffer);
+			m_StatsListCtrl.GetItem(&lvi);
+			name = buffer;
+	   }
+		pos = m_SourceListCtrl.GetFirstSelectedItemPosition();
+		if (pos != NULL) {
+			int nItem = m_SourceListCtrl.GetNextSelectedItem(pos);
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_TEXT;
+			lvi.iItem = nItem;
+			lvi.iSubItem = 0;
+			lvi.pszText = buffer2;
+			lvi.cchTextMax = sizeof(buffer2);
+			m_SourceListCtrl.GetItem(&lvi);
+			source = buffer2;
+	   }
+		AddToAdditionalGlobalStats(source, name);
+		break;
+	default :
+		break;
+	}
+
+	*pResult = 0;
+}
+
+void CStatsTab::OnClickSourcelist(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// TODO: Add your control notification handler code here
+	OnDblclkSourcelist(pNMHDR, pResult);
+	*pResult = 0;
+}

Added: icecast/branches/icecast-kh/win32/StatsTab.h
===================================================================
--- icecast/branches/icecast-kh/win32/StatsTab.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/StatsTab.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,55 @@
+#if !defined(AFX_STATSTAB_H__64B82CAB_8D6D_45A6_84FD_666F6317E5F2__INCLUDED_)
+#define AFX_STATSTAB_H__64B82CAB_8D6D_45A6_84FD_666F6317E5F2__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// StatsTab.h : header file
+//
+#include "TabPageSSL.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatsTab dialog
+
+class CStatsTab : public CTabPageSSL
+{
+// Construction
+public:
+	int m_colStats1Width;
+	int m_colStats0Width;
+	int m_colSource0Width;
+	CStatsTab(CWnd* pParent = NULL);   // standard constructor
+
+// Dialog Data
+	//{{AFX_DATA(CStatsTab)
+	enum { IDD = IDD_STATSDIALOG };
+	CStatic	m_SLS;
+	CListCtrl	m_StatsListCtrl;
+	CListCtrl	m_SourceListCtrl;
+	//}}AFX_DATA
+
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CStatsTab)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+	// Generated message map functions
+	//{{AFX_MSG(CStatsTab)
+	virtual BOOL OnInitDialog();
+	afx_msg void OnDblclkSourcelist(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnRclickStatslist(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnClickSourcelist(NMHDR* pNMHDR, LRESULT* pResult);
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STATSTAB_H__64B82CAB_8D6D_45A6_84FD_666F6317E5F2__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/Status.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/Status.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Status.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,160 @@
+// Status.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Icecast2win.h"
+#include "Status.h"
+
+#include "Icecast2winDlg.h"
+
+extern CIcecast2winDlg	*g_mainDialog;
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern void RemoveFromAdditionalGlobalStats(CString source, CString name);
+extern void AddToTitleAdditionalGlobalStats(CString source, CString name);
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatus dialog
+
+
+CStatus::CStatus(CWnd* pParent /*=NULL*/)
+	: CTabPageSSL(CStatus::IDD, pParent)
+{
+	//{{AFX_DATA_INIT(CStatus)
+	m_Clients = _T("");
+	m_Sources = _T("");
+	m_RunningFor = _T("");
+	//}}AFX_DATA_INIT
+}
+
+
+void CStatus::DoDataExchange(CDataExchange* pDX)
+{
+	CTabPageSSL::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CStatus)
+	DDX_Control(pDX, IDC_STATIC_GS, m_GS);
+	DDX_Control(pDX, IDC_GLOBALSTAT_LIST, m_GlobalStatList);
+	DDX_Text(pDX, IDC_RUNNINGFOR, m_RunningFor);
+	//}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CStatus, CTabPageSSL)
+	//{{AFX_MSG_MAP(CStatus)
+	ON_NOTIFY(NM_RCLICK, IDC_GLOBALSTAT_LIST, OnRclickGlobalstatList)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatus message handlers
+
+BOOL CStatus::OnInitDialog()
+{
+	CTabPageSSL::OnInitDialog();
+
+
+	m_GlobalStatList.InsertColumn(0, _T("Stat Type"), LVCFMT_LEFT, m_colStats0Width);
+	m_GlobalStatList.InsertColumn(1, _T("Name"), LVCFMT_LEFT, m_colStats1Width);
+	m_GlobalStatList.InsertColumn(2, _T("Value"), LVCFMT_LEFT, m_colStats2Width);
+
+	m_GlobalStatList.SetExtendedStyle(LVS_EX_FULLROWSELECT);
+	// TODO: Add extra initialization here
+	AddAnchor(IDC_FILLER2, BOTTOM_LEFT, BOTTOM_RIGHT);
+	AddAnchor(IDC_GLOBALSTAT_LIST, TOP_LEFT, BOTTOM_RIGHT);
+	AddAnchor(IDC_STATIC_RUN, BOTTOM_LEFT, BOTTOM_RIGHT);
+	AddAnchor(IDC_RUNNINGFOR, BOTTOM_LEFT, BOTTOM_RIGHT);
+
+	m_GS.SetFont(&(g_mainDialog->labelFont), TRUE);
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	              // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CStatus::OnRclickGlobalstatList(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// TODO: Add your control notification handler code here
+	CMenu	menu;
+
+
+    CPoint point;
+    ::GetCursorPos(&point); //where is the mouse?
+
+    DWORD dwSelectionMade;
+    menu.LoadMenu(IDR_MENU3);
+    CMenu *pmenuPopup = menu.GetSubMenu(0);
+    dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|TPM_LEFTBUTTON|
+                                                       TPM_NONOTIFY|TPM_RETURNCMD),
+                                                       point.x, point.y, this);
+
+    pmenuPopup->DestroyMenu();
+	char	msg[255] ="";
+	char	buffer[1024] = "";
+	char	buffer2[1024] = "";
+
+	CString name;
+	CString source;
+	POSITION pos;
+	int nItem;
+	switch (dwSelectionMade) {
+	case ID__DELETEFROMGLOBALSTATS :
+		pos = m_GlobalStatList.GetFirstSelectedItemPosition();
+		if (pos != NULL) {
+			nItem = m_GlobalStatList.GetNextSelectedItem(pos);
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_TEXT;
+			lvi.iItem = nItem;
+			lvi.iSubItem = 0;
+			lvi.pszText = buffer;
+			lvi.cchTextMax = sizeof(buffer);
+			m_GlobalStatList.GetItem(&lvi);
+			source = buffer;
+			lvi.iSubItem = 1;
+			lvi.pszText = buffer2;
+			lvi.cchTextMax = sizeof(buffer2);
+			m_GlobalStatList.GetItem(&lvi);
+			name = buffer2;
+
+			if (source == "Global Stat") {
+				MessageBox("Sorry, but you can't delete this type of stat", NULL, MB_OK);
+			}
+			else {
+				RemoveFromAdditionalGlobalStats(source, name);
+				m_GlobalStatList.DeleteItem(nItem);
+			}
+	   }
+		break;
+	case ID__MAKETHISSTATTHEWINDOWTITLE :
+		pos = m_GlobalStatList.GetFirstSelectedItemPosition();
+		if (pos != NULL) {
+			nItem = m_GlobalStatList.GetNextSelectedItem(pos);
+			LVITEM	lvi;
+
+			lvi.mask =  LVIF_TEXT;
+			lvi.iItem = nItem;
+			lvi.iSubItem = 0;
+			lvi.pszText = buffer;
+			lvi.cchTextMax = sizeof(buffer);
+			m_GlobalStatList.GetItem(&lvi);
+			source = buffer;
+			lvi.iSubItem = 1;
+			lvi.pszText = buffer2;
+			lvi.cchTextMax = sizeof(buffer2);
+			m_GlobalStatList.GetItem(&lvi);
+			name = buffer2;
+
+			AddToTitleAdditionalGlobalStats(source, name);
+		}
+		break;
+	default :
+		break;
+	}
+
+	*pResult = 0;
+}

Added: icecast/branches/icecast-kh/win32/Status.h
===================================================================
--- icecast/branches/icecast-kh/win32/Status.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Status.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,59 @@
+#if !defined(AFX_STATUS_H__DE59E22B_FD4F_4131_B347_48BD9FAC9348__INCLUDED_)
+#define AFX_STATUS_H__DE59E22B_FD4F_4131_B347_48BD9FAC9348__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// Status.h : header file
+//
+#include "TabPageSSL.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CStatus dialog
+
+class CStatus : public CTabPageSSL
+{
+// Construction
+public:
+	int m_colStats2Width;
+	int m_colStats1Width;
+	int m_colStats0Width;
+	CFont labelFont;
+	CStatus(CWnd* pParent = NULL);   // standard constructor
+
+// Dialog Data
+	CBitmap runningBitmap;
+	CBitmap stoppedBitmap;
+
+	//{{AFX_DATA(CStatus)
+	enum { IDD = IDD_SSTATUS };
+	CStatic	m_GS;
+	CListCtrl	m_GlobalStatList;
+	CString	m_Clients;
+	CString	m_Sources;
+	CString	m_RunningFor;
+	//}}AFX_DATA
+
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CStatus)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+	// Generated message map functions
+	//{{AFX_MSG(CStatus)
+	virtual BOOL OnInitDialog();
+	afx_msg void OnRclickGlobalstatList(NMHDR* pNMHDR, LRESULT* pResult);
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STATUS_H__DE59E22B_FD4F_4131_B347_48BD9FAC9348__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/StdAfx.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/StdAfx.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/StdAfx.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+//	Icecast2win.pch will be the pre-compiled header
+//	stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+
+

Added: icecast/branches/icecast-kh/win32/StdAfx.h
===================================================================
--- icecast/branches/icecast-kh/win32/StdAfx.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/StdAfx.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,27 @@
+// stdafx.h : include file for standard system include files,
+//  or project specific include files that are used frequently, but
+//      are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__2C2EF8C3_43CD_47D2_A979_EC36873E602D__INCLUDED_)
+#define AFX_STDAFX_H__2C2EF8C3_43CD_47D2_A979_EC36873E602D__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+#include <afxdisp.h>        // MFC Automation classes
+#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>			// MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#define WM_TRAY_NOTIFY WM_APP+1000
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__2C2EF8C3_43CD_47D2_A979_EC36873E602D__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/TRAYNOT.h
===================================================================
--- icecast/branches/icecast-kh/win32/TRAYNOT.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/TRAYNOT.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,35 @@
+// CTrayNot window
+#ifndef _CTRAYNOT
+#define _CTRAYNOT
+
+class CTrayNot : public CObject
+{
+// Construction
+public:
+	CTrayNot ( CWnd* pWnd, UINT uCallbackMessage,
+				  LPCTSTR szTip, HICON* pList ) ;
+
+// Attributes
+public:
+	BOOL			m_bEnabled ;
+	NOTIFYICONDATA	m_tnd ;
+	HICON*			m_pIconList ;
+
+
+public:
+	void SetState ( int id = 0 ) ;
+
+// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CTrayNot)
+	//}}AFX_VIRTUAL
+
+// Implementation
+public:
+	virtual ~CTrayNot();
+	void SetTIP(char *pTip);
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif

Added: icecast/branches/icecast-kh/win32/TabCtrlSSL.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/TabCtrlSSL.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/TabCtrlSSL.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,499 @@
+// TabCtrlSSL.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "TabCtrlSSL.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Construction
+
+CTabCtrlSSL::CTabCtrlSSL () {
+#ifndef _AFX_NO_OCC_SUPPORT
+	AfxEnableControlContainer ();
+#endif // !_AFX_NO_OCC_SUPPORT
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Destruction
+
+CTabCtrlSSL::~CTabCtrlSSL (void) {
+}
+
+BEGIN_MESSAGE_MAP(CTabCtrlSSL, CTabCtrl)
+	//{{AFX_MSG_MAP(CTabCtrlSSL)
+	ON_WM_DESTROY ()
+	ON_WM_SETFOCUS ()
+	ON_WM_KILLFOCUS ()
+	ON_NOTIFY_REFLECT (TCN_SELCHANGING, OnSelChanging)
+	ON_NOTIFY_REFLECT (TCN_SELCHANGE, OnSelChange)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// Page Functions
+
+int CTabCtrlSSL::AddSSLPage (LPCTSTR pszTitle, int nPageID, CTabPageSSL* pTabPage) {
+	// Add a page to the tab control.
+    TabDelete tabDelete;
+    tabDelete.pTabPage = pTabPage;
+    tabDelete.bDelete = FALSE;
+
+    return AddPage (pszTitle, nPageID, tabDelete);
+}
+
+int CTabCtrlSSL::AddSSLPage (LPCTSTR pszTitle, int nPageID, LPCTSTR pszTemplateName) {
+	// Verify that the dialog template is compatible with CTabCtrlSSL
+	// (debug builds only). If your app asserts here, make sure the dialog
+	// resource you're adding to the view is a borderless child window and
+	// is not marked visible.
+#ifdef _DEBUG
+	if (pszTemplateName != NULL) {
+		BOOL bResult = CheckDialogTemplate (pszTemplateName);
+		ASSERT (bResult);
+	}
+#endif // _DEBUG
+
+	// Add a page to the tab control.
+	// Create a modeless dialog box.
+	CTabPageSSL* pDialog = new CTabPageSSL;
+
+	if (pDialog == NULL) {
+		return -1;
+	}
+
+	if (!pDialog->Create (pszTemplateName, this)) {
+		pDialog->DestroyWindow ();
+		delete pDialog;
+		return -1;
+	}
+
+    TabDelete tabDelete;
+    tabDelete.pTabPage = pDialog;
+    tabDelete.bDelete = TRUE;
+
+    return AddPage (pszTitle, nPageID, tabDelete);
+}
+
+int CTabCtrlSSL::AddSSLPage (LPCTSTR pszTitle, int nPageID, int nTemplateID) {
+	return AddSSLPage (pszTitle, nPageID, MAKEINTRESOURCE (nTemplateID));
+}
+
+BOOL CTabCtrlSSL::RemoveSSLPage (int nIndex) {
+	if (nIndex >= GetItemCount ())
+		return FALSE;
+
+	// Notify derived classes that the page is being destroyed.
+	OnDestroyPage (nIndex, m_nPageIDs[nIndex]);
+
+	// Switch pages if the page being deleted is the current page and it's
+	// not the only remaining page.
+	int nCount = GetItemCount ();
+	if (nCount > 1 && nIndex == GetCurSel ()) {
+		int nPage = nIndex + 1;
+		if (nPage >= nCount)
+			nPage = nCount - 2;
+		ActivateSSLPage (nPage);
+	}
+
+	// Remove the page from the tab control.
+	DeleteItem (nIndex);
+
+	// Destroy the dialog (if any) that represents the page.
+    TabDelete tabDelete = m_tabs[nIndex];
+    CTabPageSSL* pDialog = tabDelete.pTabPage;
+	if (pDialog != NULL) {
+		pDialog->DestroyWindow ();
+		delete pDialog;
+	}
+
+	// Clean up, repaint, and return.
+	m_tabs.RemoveAt (nIndex);
+	m_hFocusWnd.RemoveAt (nIndex);
+	m_nPageIDs.RemoveAt (nIndex);
+	Invalidate ();
+	return TRUE;
+}
+
+int CTabCtrlSSL::GetSSLPageCount (void) {
+	return GetItemCount ();
+}
+
+BOOL CTabCtrlSSL::GetSSLPageTitle (int nIndex, CString &strTitle) {
+	if (nIndex >= GetItemCount ())
+		return FALSE;
+
+	TCHAR szTitle[1024];
+
+	TC_ITEM item;
+	item.mask = TCIF_TEXT;
+	item.pszText = szTitle;
+	item.cchTextMax = sizeof szTitle / sizeof (TCHAR);
+
+	if (!GetItem (nIndex, &item))
+		return FALSE;
+
+	strTitle = item.pszText;
+	return TRUE;
+}
+
+BOOL CTabCtrlSSL::SetSSLPageTitle (int nIndex, LPCTSTR pszTitle) {
+	if (nIndex >= GetItemCount ())
+		return FALSE;
+
+	TC_ITEM item;
+	item.mask = TCIF_TEXT;
+	item.pszText = (LPTSTR) pszTitle;
+
+	BOOL bResult = SetItem (nIndex, &item);
+	if (bResult)
+		Invalidate ();
+	return bResult;
+}
+
+int CTabCtrlSSL::GetSSLPageID (int nIndex) {
+	if (nIndex >= GetItemCount ())
+		return -1;
+
+	return m_nPageIDs[nIndex];
+}
+
+int CTabCtrlSSL::SetSSLPageID (int nIndex, int nPageID) {
+	if (nIndex >= GetItemCount ())
+		return -1;
+
+	int nOldPageID = m_nPageIDs[nIndex];
+	m_nPageIDs[nIndex] = nPageID;
+	return nOldPageID;
+}
+
+BOOL CTabCtrlSSL::ActivateSSLPage (int nIndex) {
+	if (nIndex >= GetItemCount ())
+		return FALSE;
+
+	// Do nothing if the specified page is already active.
+	if (nIndex == GetCurSel ())
+		return TRUE;
+
+	// Deactivate the current page.
+	int nOldIndex = GetCurSel ();
+
+	if (nIndex != -1) {
+        TabDelete tabDelete = m_tabs[nOldIndex];
+        CTabPageSSL* pDialog = tabDelete.pTabPage;
+		if (pDialog != NULL) {
+			m_hFocusWnd[nOldIndex] = ::GetFocus ();
+			pDialog->ShowWindow (SW_HIDE);
+		}
+	}
+
+	// Activate the new one.
+	SetCurSel (nIndex);
+    TabDelete tabDelete = m_tabs[nIndex];
+    CTabPageSSL* pDialog = tabDelete.pTabPage;
+
+	if (pDialog != NULL) {
+		::SetFocus (m_hFocusWnd[nIndex]);
+		CRect rect;
+		GetClientRect (&rect);
+		ResizeDialog (nIndex, rect.Width (), rect.Height ());
+		pDialog->ShowWindow (SW_SHOW);
+	}
+	return TRUE;
+}
+
+int CTabCtrlSSL::GetSSLActivePage (void) {
+	return GetCurSel ();
+}
+
+CWnd* CTabCtrlSSL::GetSSLPage (int nIndex) {
+	if (nIndex >= GetItemCount ())
+		return NULL;
+
+    TabDelete tabDelete = m_tabs[nIndex];
+    return (CWnd*) tabDelete.pTabPage;
+}
+
+int CTabCtrlSSL::GetSSLPageIndex (int nPageID) {
+	int nCount = GetItemCount ();
+	if (nCount == 0)
+		return -1;
+
+	for (int i=0; i<nCount; i++) {
+		if (m_nPageIDs[i] == nPageID)
+			return i;
+	}
+
+	return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Private helper functions
+
+#ifdef _DEBUG
+BOOL CTabCtrlSSL::CheckDialogTemplate (LPCTSTR pszTemplateName) {
+	// Verify that the dialog resource exists.
+	ASSERT (pszTemplateName != NULL);
+	HINSTANCE hInstance = AfxFindResourceHandle (pszTemplateName, RT_DIALOG);
+	HRSRC hResource = ::FindResource (hInstance, pszTemplateName, RT_DIALOG);
+
+	if (hResource == NULL)
+		return FALSE; // Resource doesn't exist
+
+	HGLOBAL hTemplate = LoadResource (hInstance, hResource);
+	ASSERT (hTemplate != NULL);
+
+	// Get the dialog's style bits.
+	DLGTEMPLATEEX* pTemplate = (DLGTEMPLATEEX*) LockResource (hTemplate);
+
+	DWORD dwStyle;
+	if (pTemplate->signature == 0xFFFF)
+		dwStyle = pTemplate->style;
+	else
+		dwStyle = ((DLGTEMPLATE*) pTemplate)->style;
+
+	UnlockResource (hTemplate);
+	FreeResource (hTemplate);
+
+	// Verify that the dialog is an invisible child window.
+	if (dwStyle & WS_VISIBLE)
+		return FALSE; // WS_VISIBLE flag is set
+
+	if (!(dwStyle & WS_CHILD))
+		return FALSE; // WS_CHILD flag isn't set
+
+	// Verify that the dialog has no border and no title bar.
+	if (dwStyle & (WS_BORDER | WS_THICKFRAME | DS_MODALFRAME))
+		return FALSE; // One or more border flags are set
+
+	if (dwStyle & WS_CAPTION)
+		return FALSE; // WS_CAPTION flag is set
+
+	return TRUE;
+}
+#endif // _DEBUG
+
+void CTabCtrlSSL::ResizeDialog (int nIndex, int cx, int cy) {
+	if (nIndex != -1) {
+        TabDelete tabDelete = m_tabs[nIndex];
+        CTabPageSSL* pDialog = tabDelete.pTabPage;
+
+		if (pDialog != NULL) {
+			CRect rect;
+			GetItemRect (nIndex, &rect);
+
+			int x, y, nWidth, nHeight;
+			DWORD dwStyle = GetStyle ();
+
+			if (dwStyle & TCS_VERTICAL) { // Vertical tabs
+				int nTabWidth =
+					rect.Width () * GetRowCount ();
+				x = (dwStyle & TCS_RIGHT) ? 4 : nTabWidth + 4;
+				y = 4;
+				nWidth = cx - nTabWidth - 8;
+				nHeight = cy - 8;
+			}
+			else { // Horizontal tabs
+				int nTabHeight =
+					rect.Height () * GetRowCount ();
+				x = 4;
+				y = (dwStyle & TCS_BOTTOM) ? 4 : nTabHeight + 4;
+				nWidth = cx - 8;
+				nHeight = cy - nTabHeight - 8;
+
+
+
+			}
+			pDialog->SetWindowPos (NULL, x, y, nWidth, nHeight, SWP_NOZORDER);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Overridables
+
+BOOL CTabCtrlSSL::OnInitPage (int nIndex, int nPageID) {
+	// TODO: Override in derived class to initialise pages.
+	return TRUE;
+}
+
+void CTabCtrlSSL::OnActivatePage (int nIndex, int nPageID) {
+	// TODO: Override in derived class to respond to page activations.
+}
+
+void CTabCtrlSSL::OnDeactivatePage (int nIndex, int nPageID) {
+	// TODO: Override in derived class to respond to page deactivations.
+}
+
+void CTabCtrlSSL::OnDestroyPage (int nIndex, int nPageID) {
+	// TODO: Override in derived class to free resources.
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Message handlers
+
+void CTabCtrlSSL::OnSelChanging (NMHDR* pNMHDR, LRESULT* pResult) {
+	// Notify derived classes that the selection is changing.
+	int nIndex = GetCurSel ();
+	if (nIndex == -1)
+		return;
+
+	OnDeactivatePage (nIndex, m_nPageIDs[nIndex]);
+
+	// Save the input focus and hide the old page.
+    TabDelete tabDelete = m_tabs[nIndex];
+    CTabPageSSL* pDialog = tabDelete.pTabPage;
+
+	if (pDialog != NULL) {
+		m_hFocusWnd[nIndex] = ::GetFocus ();
+		pDialog->ShowWindow (SW_HIDE);
+	}
+	*pResult = 0;
+}
+
+void CTabCtrlSSL::OnSelChange (NMHDR* pNMHDR, LRESULT* pResult) {
+	int nIndex = GetCurSel ();
+	if (nIndex == -1)
+		return;
+
+	// Show the new page.
+    TabDelete tabDelete = m_tabs[nIndex];
+    CTabPageSSL* pDialog = tabDelete.pTabPage;
+
+	if (pDialog != NULL) {
+		::SetFocus (m_hFocusWnd[nIndex]);
+		CRect rect;
+		GetClientRect (&rect);
+		ResizeDialog (nIndex, rect.Width (), rect.Height ());
+		pDialog->ShowWindow (SW_SHOW);
+	}
+
+	// Notify derived classes that the selection has changed.
+	OnActivatePage (nIndex, m_nPageIDs[nIndex]);
+	*pResult = 0;
+}
+
+void CTabCtrlSSL::OnSetFocus (CWnd* pOldWnd) {
+	CTabCtrl::OnSetFocus (pOldWnd);
+
+	// Set the focus to a control on the current page.
+	int nIndex = GetCurSel ();
+	if (nIndex != -1)
+		::SetFocus (m_hFocusWnd[nIndex]);
+}
+
+void CTabCtrlSSL::OnKillFocus (CWnd* pNewWnd) {
+	CTabCtrl::OnKillFocus (pNewWnd);
+
+	// Save the HWND of the control that holds the input focus.
+	int nIndex = GetCurSel ();
+	if (nIndex != -1)
+		m_hFocusWnd[nIndex] = ::GetFocus ();
+}
+
+// My thanks to Tomasz Sowinski for all his help coming up with a workable
+// solution to the stack versus heap object destruction
+void CTabCtrlSSL::OnDestroy (void) {
+	int nCount = m_tabs.GetSize ();
+
+	// Destroy dialogs and delete CTabCtrlSSL objects.
+	if (nCount > 0) {
+		for (int i=nCount - 1; i>=0; i--) {
+			OnDestroyPage (i, m_nPageIDs[i]);
+            TabDelete tabDelete = m_tabs[i];
+            CTabPageSSL* pDialog = tabDelete.pTabPage;
+			if (pDialog != NULL) {
+				pDialog->DestroyWindow ();
+                if (TRUE == tabDelete.bDelete) {
+                    delete pDialog;
+                }
+			}
+		}
+	}
+
+	// Clean up the internal arrays.
+	m_tabs.RemoveAll ();
+	m_hFocusWnd.RemoveAll ();
+	m_nPageIDs.RemoveAll ();
+
+	CTabCtrl::OnDestroy ();
+}
+
+BOOL CTabCtrlSSL::OnCommand (WPARAM wParam, LPARAM lParam) {
+	// Forward WM_COMMAND messages to the dialog's parent.
+	return GetParent ()->SendMessage (WM_COMMAND, wParam, lParam);
+}
+
+BOOL CTabCtrlSSL::OnNotify (WPARAM wParam, LPARAM lParam, LRESULT* pResult) {
+	// Forward WM_NOTIFY messages to the dialog's parent.
+	return GetParent ()->SendMessage (WM_NOTIFY, wParam, lParam);
+}
+
+BOOL CTabCtrlSSL::OnCmdMsg (UINT nID, int nCode, void* pExtra,
+	AFX_CMDHANDLERINFO* pHandlerInfo) {
+	// Forward ActiveX control events to the dialog's parent.
+#ifndef _AFX_NO_OCC_SUPPORT
+	if (nCode == CN_EVENT)
+		return GetParent ()->OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+#endif // !_AFX_NO_OCC_SUPPORT
+
+	return CTabCtrl::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+}
+
+int CTabCtrlSSL::AddPage (LPCTSTR pszTitle, int nPageID, TabDelete tabDelete) {
+	// Add a page to the tab control.
+	TC_ITEM item;
+	item.mask = TCIF_TEXT;
+	item.pszText = (LPTSTR) pszTitle;
+	int nIndex = GetItemCount ();
+
+	if (InsertItem (nIndex, &item) == -1)
+		return -1;
+
+    if (NULL == tabDelete.pTabPage) {
+		// Fail - no point calling the function with a NULL pointer!
+		DeleteItem (nIndex);
+		return -1;
+	}
+	else {
+		// Record the address of the dialog object and the page ID.
+		int nArrayIndex = m_tabs.Add (tabDelete);
+		ASSERT (nIndex == nArrayIndex);
+
+		nArrayIndex = m_nPageIDs.Add (nPageID);
+		ASSERT (nIndex == nArrayIndex);
+
+		// Size and position the dialog box within the view.
+        tabDelete.pTabPage->SetParent (this); // Just to be sure
+
+		CRect rect;
+		GetClientRect (&rect);
+
+		if (rect.Width () > 0 && rect.Height () > 0)
+			ResizeDialog (nIndex, rect.Width (), rect.Height ());
+
+		// Initialize the page.
+		if (OnInitPage (nIndex, nPageID)) {
+			// Make sure the first control in the dialog is the one that
+			// receives the input focus when the page is displayed.
+			HWND hwndFocus = tabDelete.pTabPage->GetTopWindow ()->m_hWnd;
+			nArrayIndex = m_hFocusWnd.Add (hwndFocus);
+			ASSERT (nIndex == nArrayIndex);
+		}
+		else {
+			// Make the control that currently has the input focus is the one
+			// that receives the input focus when the page is displayed.
+			m_hFocusWnd.Add (::GetFocus ());
+		}
+
+		// If this is the first page added to the view, make it visible.
+		if (nIndex == 0)
+			tabDelete.pTabPage->ShowWindow (SW_SHOW);
+	}
+	return nIndex;
+}

Added: icecast/branches/icecast-kh/win32/TabCtrlSSL.h
===================================================================
--- icecast/branches/icecast-kh/win32/TabCtrlSSL.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/TabCtrlSSL.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,98 @@
+#if !defined(AFX_TABCTRLSSL_H__75BE48A7_864C_11D5_9F04_000102FB9990__INCLUDED_)
+#define AFX_TABCTRLSSL_H__75BE48A7_864C_11D5_9F04_000102FB9990__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// TabCtrlEx.h : header file
+//
+
+#include <afxtempl.h>
+#include "TabPageSSL.h"
+
+#ifdef _DEBUG
+#pragma pack (push, 1)
+
+typedef struct {
+	WORD dlgVer;
+	WORD signature;
+	DWORD helpID;
+	DWORD exStyle;
+	DWORD style;
+	WORD cDlgItems;
+	short x;
+	short y;
+	short cx;
+	short cy;
+} DLGTEMPLATEEX;
+
+#pragma pack (pop)
+#endif // _DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CTabCtrlSSL window
+
+class CTabCtrlSSL : public CTabCtrl {
+public:
+// Construction
+	CTabCtrlSSL ();
+// Destruction
+	virtual ~CTabCtrlSSL (void);
+// Page Functions
+	int		AddSSLPage (LPCTSTR pszTitle, int nPageID, CTabPageSSL* pTabPage);
+	int		AddSSLPage (LPCTSTR pszTitle, int nPageID,	LPCTSTR pszTemplateName);
+	int		AddSSLPage (LPCTSTR pszTitle, int nPageID, int nTemplateID);
+	BOOL	RemoveSSLPage (int nIndex);
+	int		GetSSLPageCount (void);
+	BOOL	GetSSLPageTitle (int nIndex, CString& strTitle);
+	BOOL	SetSSLPageTitle (int nIndex, LPCTSTR pszTitle);
+	int		GetSSLPageID (int nIndex);
+	int		SetSSLPageID (int nIndex, int nPageID);
+	BOOL	ActivateSSLPage (int nIndex);
+	int		GetSSLActivePage (void);
+	CWnd*	GetSSLPage (int nIndex);
+	int		GetSSLPageIndex (int nPageID);
+	void ResizeDialog (int nIndex, int cx, int cy);
+
+protected:
+    struct TabDelete {
+        CTabPageSSL*   pTabPage;
+        BOOL        bDelete;
+    };
+    CArray<TabDelete, TabDelete> m_tabs;
+	CArray<HWND, HWND> m_hFocusWnd;
+	CArray<int, int> m_nPageIDs;
+
+	int AddPage (LPCTSTR pszTitle, int nPageID, TabDelete tabDelete);
+
+    virtual BOOL OnInitPage (int nIndex, int nPageID);
+	virtual void OnActivatePage (int nIndex, int nPageID);
+	virtual void OnDeactivatePage (int nIndex, int nPageID);
+	virtual void OnDestroyPage (int nIndex, int nPageID);
+	virtual BOOL OnCommand (WPARAM wParam, LPARAM lParam);
+	virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+	virtual BOOL OnCmdMsg (UINT nID, int nCode, void* pExtra,
+		AFX_CMDHANDLERINFO* pHandlerInfo);
+
+#ifdef _DEBUG
+	BOOL CheckDialogTemplate (LPCTSTR pszTemplateName);
+#endif // _DEBUG
+	// Generated message map functions
+protected:
+	//{{AFX_MSG(CTabCtrlSSL)
+	afx_msg void OnDestroy (void);
+	afx_msg void OnSetFocus (CWnd* pOldWnd);
+	afx_msg void OnKillFocus (CWnd* pNewWnd);
+	afx_msg void OnSelChanging (NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnSelChange (NMHDR* pNMHDR, LRESULT* pResult);
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_TABCTRLSSL_H__75BE48A7_864C_11D5_9F04_000102FB9990__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/TabPageSSL.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/TabPageSSL.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/TabPageSSL.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,76 @@
+#include "stdafx.h"
+#include "TabPageSSL.h"
+#include "ResizableDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Construction
+
+CTabPageSSL::CTabPageSSL () {
+#ifndef _AFX_NO_OCC_SUPPORT
+	AfxEnableControlContainer ();
+#endif // !_AFX_NO_OCC_SUPPORT
+}
+
+CTabPageSSL::CTabPageSSL (UINT nIDTemplate, CWnd* pParent /*=NULL*/)
+	: CResizableDialog(nIDTemplate, pParent) {
+#ifndef _AFX_NO_OCC_SUPPORT
+	AfxEnableControlContainer ();
+#endif // !_AFX_NO_OCC_SUPPORT
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Destruction
+
+CTabPageSSL::~CTabPageSSL () {
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Message Handlers
+
+void CTabPageSSL::OnOK (void) {
+	//
+	// Prevent CDialog::OnOK from calling EndDialog.
+	//
+}
+
+void CTabPageSSL::OnCancel (void) {
+	//
+	// Prevent CDialog::OnCancel from calling EndDialog.
+	//
+}
+
+BOOL CTabPageSSL::OnCommand (WPARAM wParam, LPARAM lParam) {
+	// Call base class OnCommand to allow message map processing
+	CResizableDialog::OnCommand (wParam, lParam);
+	//
+	// Forward WM_COMMAND messages to the dialog's parent.
+	//
+	return GetParent ()->SendMessage (WM_COMMAND, wParam, lParam);
+}
+
+BOOL CTabPageSSL::OnNotify (WPARAM wParam, LPARAM lParam, LRESULT* pResult) {
+	//
+	// Forward WM_NOTIFY messages to the dialog's parent.
+	//
+	CResizableDialog::OnNotify (wParam, lParam, pResult);
+	return GetParent ()->SendMessage (WM_NOTIFY, wParam, lParam);
+}
+
+BOOL CTabPageSSL::OnCmdMsg (UINT nID, int nCode, void* pExtra,
+	AFX_CMDHANDLERINFO* pHandlerInfo) {
+	//
+	// Forward ActiveX control events to the dialog's parent.
+	//
+#ifndef _AFX_NO_OCC_SUPPORT
+	if (nCode == CN_EVENT)
+		return GetParent ()->OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+#endif // !_AFX_NO_OCC_SUPPORT
+
+	return CResizableDialog::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+}

Added: icecast/branches/icecast-kh/win32/TabPageSSL.h
===================================================================
--- icecast/branches/icecast-kh/win32/TabPageSSL.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/TabPageSSL.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,27 @@
+#if !defined(AFX_TABPAGESSL_H__619331B3_7DE7_4DB1_A039_2103E87E8E71__INCLUDED_)
+#define AFX_TABPAGESSL_H__619331B3_7DE7_4DB1_A039_2103E87E8E71__INCLUDED_
+
+/////////////////////////////////////////////////////////////////////////////
+// CTabPageSSL declaration
+#include "ResizableDialog.h"
+
+class CTabPageSSL : public CResizableDialog
+{
+public:
+// Construction
+	CTabPageSSL ();	// Default Constructor
+	CTabPageSSL (UINT nIDTemplate, CWnd* pParent = NULL);	// Standard Constructor
+// Destruction
+	~CTabPageSSL ();
+
+protected:
+// Message Handlers
+	virtual BOOL OnCommand (WPARAM wParam, LPARAM lParam);
+	virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+	virtual void OnOK (void);
+	virtual void OnCancel (void);
+	virtual BOOL OnCmdMsg (UINT nID, int nCode, void* pExtra,
+		AFX_CMDHANDLERINFO* pHandlerInfo);
+};
+
+#endif // !defined(AFX_TABPAGE_H__619331B3_7DE7_4DB1_A039_2103E87E8E71__INCLUDED_)

Added: icecast/branches/icecast-kh/win32/Traynot.cpp
===================================================================
--- icecast/branches/icecast-kh/win32/Traynot.cpp	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/Traynot.cpp	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,65 @@
+#include "stdafx.h"
+#include "TrayNot.h"
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CTrayNot
+
+CTrayNot::CTrayNot ( CWnd* pWnd, UINT uCallbackMessage,
+							LPCTSTR szTip, HICON* pList )
+{
+	// this is only for Windows 95 (or higher)
+	m_bEnabled = ( GetVersion() & 0xff ) >= 4 ;
+	if (!m_bEnabled)
+		return ;
+
+	// load up the NOTIFYICONDATA structure
+	m_tnd.cbSize = sizeof(NOTIFYICONDATA) ;
+	m_tnd.hWnd = pWnd->GetSafeHwnd() ;
+	m_tnd.uID = 0 ;
+	m_tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP ;
+	m_tnd.uCallbackMessage = uCallbackMessage;
+	strcpy ( m_tnd.szTip, "Icecast2");
+	//or you could use:
+	//strcpy ( m_tnd.szTip, AfxGetApp()->m_pszAppName);
+	//this will display the app name instead of the string you specify
+
+
+
+	// save the pointer to the icon list and set the initial
+	// default icon.
+	m_pIconList = pList ;
+	m_tnd.hIcon = m_pIconList[0] ;
+	Shell_NotifyIcon (NIM_ADD,&m_tnd);
+
+
+
+}
+
+CTrayNot::~CTrayNot()
+{
+	if (m_bEnabled)
+		Shell_NotifyIcon (NIM_DELETE, &m_tnd);
+}
+
+void CTrayNot::SetState(int id)
+{
+	if (!m_bEnabled)
+		return;
+	m_tnd.hIcon = m_pIconList[id];
+	m_tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP ;
+
+	//Ive found in windows XP that this command makes the icon not visable in the system tray....we dont want that now
+	//do we?
+	Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
+}
+void CTrayNot::SetTIP(char *pTip) {
+	memset(m_tnd.szTip, '\000', sizeof(m_tnd.szTip));
+	strncpy(m_tnd.szTip, pTip, sizeof(m_tnd.szTip)-1);
+
+	Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
+}
\ No newline at end of file

Added: icecast/branches/icecast-kh/win32/black.bmp
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/win32/black.bmp
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/win32/colors.h
===================================================================
--- icecast/branches/icecast-kh/win32/colors.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/colors.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,5 @@
+#define BGCOLOR RGB(0,0,0)
+#define TEXTCOLOR RGB(175,175,175)
+#define TEXTBGCOLOR RGB(0,0,0)
+#define LOGOCOLOR RGB(175,175,175)
+

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

Added: icecast/branches/icecast-kh/win32/icecast.ico
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/win32/icecast.ico
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/win32/icecast2.iss
===================================================================
--- icecast/branches/icecast-kh/win32/icecast2.iss	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/icecast2.iss	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,64 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+
+[Setup]
+AppName=Icecast2 Win32
+AppVerName=Icecast v2.0.0
+AppPublisherURL=http://www.icecast.org
+AppSupportURL=http://www.icecast.org
+AppUpdatesURL=http://www.icecast.org
+DefaultDirName={pf}\Icecast2 Win32
+DefaultGroupName=Icecast2 Win32
+AllowNoIcons=yes
+LicenseFile=..\COPYING
+InfoAfterFile=..\README
+OutputDir=.
+OutputBaseFilename=icecast2_win32_2.0.0_setup
+WizardImageFile=icecast2logo2.bmp
+; uncomment the following line if you want your installation to run on NT 3.51 too.
+; MinVersion=4,3.51
+
+[Tasks]
+Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"; MinVersion: 4,4
+
+[Dirs]
+Name: "{app}\web"
+Name: "{app}\admin"
+Name: "{app}\doc"
+Name: "{app}\logs"
+
+
+[Files]
+Source: "Release\Icecast2.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
+Source: "..\web\corner_bottomleft.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\corner_bottomright.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\corner_topleft.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\corner_topright.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\icecast.png"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\key.gif"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\status2.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\status.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
+Source: "..\web\style.css"; DestDir: "{app}\web"; Flags: ignoreversion
+
+Source: "..\admin\listclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\admin\listmounts.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\admin\moveclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\admin\response.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\admin\stats.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\admin\manageauth.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
+Source: "..\..\pthreads\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\conf\icecast.xml"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\..\iconv\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\..\libxslt\lib\libxslt.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\..\libxml2\lib\libxml2.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\..\curl\lib\Release\libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion
+
+[Icons]
+
+Name: "{group}\Icecast2 Win32"; Filename: "{app}\Icecast2.exe";WorkingDir: "{app}";
+Name: "{userdesktop}\Icecast2 Win32"; Filename: "{app}\Icecast2.exe"; MinVersion: 4,4; Tasks: desktopicon;WorkingDir: "{app}";
+
+[Run]
+

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

Added: icecast/branches/icecast-kh/win32/icecast2_console.dsw
===================================================================
--- icecast/branches/icecast-kh/win32/icecast2_console.dsw	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/icecast2_console.dsw	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,44 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "icecast"=.\icecast.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "icecast2 console"=.\icecast2_console.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name icecast
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

Added: icecast/branches/icecast-kh/win32/icecast2logo2.bmp
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/win32/icecast2logo2.bmp
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/win32/res/Icecast2win.rc2
===================================================================
--- icecast/branches/icecast-kh/win32/res/Icecast2win.rc2	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/res/Icecast2win.rc2	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,13 @@
+//
+// ICECAST2WIN.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+	#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////

Added: icecast/branches/icecast-kh/win32/res/Makefile.am
===================================================================
--- icecast/branches/icecast-kh/win32/res/Makefile.am	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/res/Makefile.am	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,6 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = Icecast2win.rc2
+

Added: icecast/branches/icecast-kh/win32/resource.h
===================================================================
--- icecast/branches/icecast-kh/win32/resource.h	2004-06-24 18:24:57 UTC (rev 6847)
+++ icecast/branches/icecast-kh/win32/resource.h	2004-06-24 18:26:43 UTC (rev 6848)
@@ -0,0 +1,76 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by Icecast2win.rc
+//
+#define IDM_ABOUTBOX                    0x0010
+#define IDD_ABOUTBOX                    100
+#define IDS_ABOUTBOX                    101
+#define IDD_ICECAST2WIN_DIALOG          102
+#define IDR_MAINFRAME                   128
+#define IDR_MENU1                       132
+#define IDB_BITMAP1                     134
+#define IDB_BITMAP2                     135
+#define IDI_R                           141
+#define IDI_G                           142
+#define IDI_ICON1                       142
+#define IDD_SERVERSTATUS                144
+#define IDD_SSTATUS                     145
+#define IDD_CONFIGDIALOG                146
+#define IDD_STATSDIALOG                 147
+#define IDB_BITMAP3                     149
+#define IDB_BITMAP4                     150
+#define IDB_BITMAP5                     151
+#define IDB_BITMAP6                     152
+#define IDR_MENU2                       153
+#define IDR_MENU3                       154
+#define IDC_CURSOR1                     155
+#define IDC_CURSOR2                     156
+#define IDB_BITMAP7                     159
+#define IDR_TRAY                        160
+#define IDR_MENU4                       161
+#define IDC_MAINTAB                     1000
+#define IDC_ERROR_EDIT                  1003
+#define IDC_ACCESS_EDIT                 1004
+#define IDC_CONFIG_EDIT                 1006
+#define IDC_SERVERSTATUS                1008
+#define IDC_SOURCES_CONNECTED           1009
+#define IDC_NUMBER_CLIENTS              1010
+#define IDC_GROUP1                      1011
+#define IDC_STATS_EDIT                  1012
+#define IDC_CONFIG                      1020
+#define IDC_STATSLIST                   1021
+#define IDC_SOURCELIST                  1022
+#define IDC_START                       1023
+#define IDC_AUTOSTART                   1024
+#define IDC_FILLER1                     1025
+#define IDC_FILLER2                     1026
+#define IDC_STATIC_SS                   1029
+#define IDC_GLOBALSTAT_LIST             1030
+#define IDC_STATIC_GS                   1031
+#define IDC_STATIC_SLS                  1032
+#define IDC_RUNNINGFOR                  1033
+#define IDC_STATIC_RUN                  1034
+#define IDC_STATICBLACK                 1035
+#define IDC_HIDESYSTRAY                 1036
+#define ID_FILE_STARTSERVER             32771
+#define ID_FILE_EXIT                    32772
+#define ID_FILE_STOPSERVER              32774
+#define ID_FILE                         32775
+#define ID_POPUP_ADDTOGLOBALSTATLIST    32776
+#define ID__DELETEFROMGLOBALSTATS       32777
+#define ID__MAKETHISSTATTHEWINDOWTITLE  32779
+#define ID_BLANK_RESTORE                32780
+#define ID_ABOUT_HELP                   32781
+#define ID_FILE_EDITCONFIGURATION       32782
+#define ID_ABOUT_CREDITS                32784
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        162
+#define _APS_NEXT_COMMAND_VALUE         32785
+#define _APS_NEXT_CONTROL_VALUE         1037
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif

Added: icecast/branches/icecast-kh/win32/running.bmp
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/win32/running.bmp
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream

Added: icecast/branches/icecast-kh/win32/stopped.bmp
===================================================================
(Binary files differ)


Property changes on: icecast/branches/icecast-kh/win32/stopped.bmp
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream



More information about the commits mailing list