1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-23 06:25:24 +00:00

reimport icecast-kh to branches/kh/icecast; this time as a branch of trunk/icecast

svn path=/icecast/branches/kh/icecast/; revision=7095
This commit is contained in:
j 2004-07-11 18:09:05 +00:00
parent 081a4f6194
commit 09f7f18d5b
177 changed files with 21242 additions and 3348 deletions

4
AUTHORS Normal file
View File

@ -0,0 +1,4 @@
Jack Moffitt <jack@icecast.org>
Michael Smith <msmith@icecast.org>
oddsock <oddsock@oddsock.org>
Karl Heyes <karl@xiph.org>

340
COPYING Normal file
View File

@ -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.

48
HACKING Normal file
View File

@ -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.

22
Makefile.am Normal file
View File

@ -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"

416
NEWS Normal file
View File

@ -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)

21
README Normal file
View File

@ -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@xiph.org or icecast-dev@xiph.org, or come and see
us at irc.freenode.net, channel #icecast, if you have any troubles.

89
TODO Normal file
View File

@ -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?

8
admin/Makefile.am Normal file
View File

@ -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

79
admin/listclients.xsl Executable file
View File

@ -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>

65
admin/listmounts.xsl Executable file
View File

@ -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>

91
admin/manageauth.xsl Executable file
View File

@ -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>

61
admin/moveclients.xsl Executable file
View File

@ -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>

54
admin/response.xsl Executable file
View File

@ -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>

101
admin/stats.xsl Executable file
View File

@ -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>

113
autogen.sh Executable file
View File

@ -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

28
conf/Makefile.am Normal file
View File

@ -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,@pkgdatadir\@,$(pkgdatadir),g' \
-e 's,@localstatedir\@,$(localstatedir),g' \
-e 's,@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@"

165
conf/icecast.xml.in Normal file
View File

@ -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>

158
configure.in Normal file
View File

@ -0,0 +1,158 @@
AC_INIT([Icecast], [2.0-kh47], [karl@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])

8
debian/Makefile.am vendored Normal file
View File

@ -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

12
debian/README.Debian vendored Normal file
View File

@ -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@thebasement.org>

198
debian/changelog vendored Normal file
View File

@ -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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@thebasement.org> Thu, 20 Mar 2003 12:58:49 -0800
icecast2 (0.00.cvs030315-0.1) unstable; urgency=low
* Initial Release.
-- Keegan Quinn <ice@thebasement.org> Sun, 16 Mar 2003 13:45:23 -0800

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
4

22
debian/control vendored Normal file
View File

@ -0,0 +1,22 @@
Source: icecast2
Section: sound
Priority: optional
Maintainer: Keegan Quinn <ice@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@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.

26
debian/copyright vendored Normal file
View File

@ -0,0 +1,26 @@
This package was debianized by Keegan Quinn <ice@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@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'.

24
debian/icecast2.1 vendored Normal file
View File

@ -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@icecast.org>.
This manual page was written by Keegan Quinn <ice@thebasement.org>.

19
debian/icecast2.default vendored Normal file
View File

@ -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

67
debian/icecast2.init vendored Normal file
View File

@ -0,0 +1,67 @@
#! /bin/sh
#
# icecast2
#
# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
# Modified for Debian
# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
#
# Further modified by Keegan Quinn <ice@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

1
debian/icecast2.manpages vendored Normal file
View File

@ -0,0 +1 @@
debian/icecast2.1

43
debian/icecast2.postinst vendored Normal file
View File

@ -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

29
debian/icecast2.postrm vendored Normal file
View File

@ -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

27
debian/icecast2.preinst vendored Normal file
View File

@ -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

34
debian/rules vendored Executable file
View File

@ -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

3
debian/watch vendored Normal file
View File

@ -0,0 +1,3 @@
version=2
http://www.icecast.org/download.html files/icecast/icecast-(.*)\.tar\.gz debian uupdate

9
doc/Index.hhk Normal file
View File

@ -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>

13
doc/Makefile.am Normal file
View File

@ -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)

BIN
doc/icecast2.chm Normal file

Binary file not shown.

62
doc/icecast2.hhc Normal file
View File

@ -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>

23
doc/icecast2.hhp Normal file
View File

@ -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]

119
doc/icecast2_admin.html Executable file
View File

@ -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>

59
doc/icecast2_basicsetup.html Executable file
View File

@ -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>

403
doc/icecast2_config_file.html Executable file
View File

@ -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>

85
doc/icecast2_faq.html Executable file
View File

@ -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 Nullsofts 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 cant 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>

37
doc/icecast2_glossary.html Executable file
View File

@ -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>

43
doc/icecast2_introduction.html Executable file
View File

@ -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@icecast.org
<br>
<br>
<br>
</div>
</body>
</html>

54
doc/icecast2_listenerauth.html Executable file
View File

@ -0,0 +1,54 @@
<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 Listener Authentication</h1>
<table width=100%><tr><td bgcolor="#007B79" height="10" align="center"></td></tr></table>
<br>
<br>
<br>
<h2>Listener Authentication</h2>
<p>Listener authentication is a feature of icecast which allows you to secure certain mountpoint so that in order to listen, a listener must provide a username and password. With this feature a simple pay-for-play operation can be setup. This section will show you the basics of setting up and maintaining this component.</p>
<br>
<br>
<br>
<h2>HTPASSWD Listener Authentication</h2>
<h3>Config File Entries</h3>
<p>In order to use listener authentication, you MUST configure a mount specific option. This means that you have to provide a &lt;mount&gt; section in the main icecast config file. The following is an example :</p>
<pre>
&lt;mount&gt;
&lt;mount-name&gt;/example-complex.ogg&lt;/mount-name&gt;
&lt;authentication type="htpasswd"&gt;
&lt;option name="filename" value="myauth"/&gt;
&lt;option name="allow_duplicate_users" value="0"/&gt;
&lt;/authentication&gt;
&lt;/mount&gt;
</pre>
<p>To support listener authentication you MUST provide at a minimum &lt;mount-name&gt; and &lt;authentication&gt;. The mount-name is the name of the mountpoint that you will use to connect your source client with and authentication configures what type of icecast2 authenticator to use. Currently, only a single type "htpasswd" is implemented. New authenticators will be added later. Each authenticator has a variable number of options that are required and these are specified as shown in the example. The htpasswd authenticator requires a few parameters. The first, filename, specifies the name of the file to use to store users and passwords. Note that this file need not exist (and probably will not exist when you first set it up). Icecast has built-in support for managing users and passwords via the web admin interface. More on this later in this section. The second option, allow_duplicate_users, if set to 0, will prevent multiple connections using the same username. Setting this value to 1 will enable mutltiple connections from the same username on a given mountpoint. Note there is no way to specify a "max connections" for a particular user.
<p>Icecast supports a mixture of streams that require listener authentication and those that do not. Only mounts that are named in the config file can be configured for listener authentication.</p>
<br>
<br>
<br>
<h3>Configuring Users and Passwords</h3>
<p>Once the appropriate entries are made to the config file, connect your source client (using the mountpoint you named in the config file). To configure users and passwords for this stream you must use the web-based admin interface. Navigate to http://server:ip/admin/stats.xsl to begin. If you have configured everything properly, you should see a screen like the following :</p>
<img border=1 src="listener_auth1.jpg">
<p>You will see a red key in front of all mountpoint configured for listener authentication. Also note that this page will only show CONNECTED mountpoints.</p>
<p>To manage users and passwords for this mountpoint, click on the red key or follow the "Manage Authentication" link. The following screen will be shown :</p>
<img border=1 src="listener_auth2.jpg">
<p>This screen will show all the users configured for this mountpoint. Adding users is as simple as entering a username and password in the fields and clicking "Add New User". Note that usernames MUST be unique and there are NO restrictions on passwords. You can delete users by clicking the appropriate delete link next to each user.</p>
<br>
<br>
<br>
<h3>Finishing it all off</h3>
<p>Ok, so you've created your users, and you have everything setup properly, how do your users login ? Well, we've provided a simple login form that you can use for this purpose. This page (http://server:port/auth.xsl) will bring up a form that users can use to enter their username and password.</p>
<img border=1 src="listener_auth3.jpg">
<p>This page will serve a m3u with the username and password and in most cases should open the correct media player and begin playing your stream</p>
<br>
<br>
<br>
<h2>A note about players and authentication</h2>
<p>We do not have an exaustive list of players that support listener authentication. We use standard HTTP basic authentication, and in general, many media players support this if they support anything at all. Winamp and Foobar2000 support HTTP basic authentication on windows, and XMMS supports it on unix platforms.</p>
</div>
</body>
</html>

47
doc/icecast2_relay.html Executable file
View File

@ -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>

83
doc/icecast2_stats.html Executable file
View File

@ -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>

54
doc/icecast2_win32.html Executable file
View File

@ -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>

36
doc/icecast2_yp.html Executable file
View File

@ -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>

76
doc/index.html Executable file
View File

@ -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@xiph.org or icecast-dev@xiph.org, or come and see
us at irc.freenode.net, channel #icecast, if you have any troubles.
</pre>

19
doc/index_win32.html Normal file
View File

@ -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>

BIN
doc/listener_auth1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
doc/listener_auth2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
doc/listener_auth3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
doc/stats1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

79
doc/style.css Normal file
View File

@ -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 }

34
doc/win32_section1.html Normal file
View File

@ -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>

9
doc/win32_section2.html Normal file
View File

@ -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>

9
doc/win32_section3.html Normal file
View File

@ -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>

BIN
doc/windowtitle.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

58
icecast.spec Normal file
View File

@ -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@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

31
src/Makefile.am Normal file
View File

@ -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@"

1
src/TODO Normal file
View File

@ -0,0 +1 @@
need a shutdown function in case anything else in the code needs to have icecast gracefully shutdown.

896
src/admin.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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;
}

21
src/admin.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

175
src/auth.c Normal file
View File

@ -0,0 +1,175 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
/**
* 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")) {
#ifdef WIN32
ERROR1("Authenticator type: \"%s\" not supported on win32 platform", type);
return NULL;
#else
auth = auth_get_cmd_auth(options);
auth->type = strdup(type);
#endif
}
else if(!strcmp(type, "htpasswd")) {
auth = auth_get_htpasswd_auth(options);
auth->type = strdup(type);
}
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);
}

56
src/auth.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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

958
src/cfgfile.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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@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;
}
}

176
src/cfgfile.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

171
src/client.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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;
}

81
src/client.h Normal file
View File

@ -0,0 +1,81 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
/* client.h
**
** client data structions and function definitions
**
*/
#ifndef __CLIENT_H__
#define __CLIENT_H__
#ifndef WIN32
#include <aio.h>
#endif
#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__ */

31
src/compat.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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

75
src/configtest.c Normal file
View File

@ -0,0 +1,75 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include "cfgfile.h"
void _dump_config(ice_config_t *config);
int main(void)
{
ice_config_t *config;
config_initialize();
config_parse_file("icecast.xml");
config = config_get_config_unlocked();
_dump_config(config);
config_shutdown();
return 0;
}
void _dump_config(ice_config_t *config)
{
ice_config_dir_t *node;
printf("-----\n");
printf("location = %s\n", config->location);
printf("admin = %s\n", config->admin);
printf("client_limit = %d\n", config->client_limit);
printf("source_limit = %d\n", config->source_limit);
printf("threadpool_size = %d\n", config->threadpool_size);
printf("client_timeout = %d\n", config->client_timeout);
printf("source_password = %s\n", config->source_password);
printf("touch_interval = %d\n", config->touch_interval);
node = config->dir_list;
while (node) {
printf("directory.touch_interval = %d\n", node->touch_interval);
printf("directory.host = %s\n", node->host);
node = node->next;
}
printf("hostname = %s\n", config->hostname);
printf("port = %d\n", config->port);
printf("bind_address = %s\n", config->bind_address);
printf("base_dir = %s\n", config->base_dir);
printf("log_dir = %s\n", config->log_dir);
printf("access_log = %s\n", config->access_log);
printf("error_log = %s\n", config->error_log);
printf("loglevel = %d\n", config->loglevel);
printf("-----\n");
}

1020
src/connection.c Normal file

File diff suppressed because it is too large Load Diff

61
src/connection.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

67
src/event.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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();
}
}

21
src/event.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

212
src/format.c Normal file
View File

@ -0,0 +1,212 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
/* -*- 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
#define snprintf _snprintf
#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 ();
}

72
src/format.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

572
src/format_mp3.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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;
}
}

41
src/format_mp3.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

797
src/format_vorbis.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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", &serial_lock);
}

24
src/format_vorbis.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

522
src/fserve.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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);
}

35
src/fserve.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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

63
src/global.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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);
}

55
src/global.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

122
src/logging.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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);
}
}

100
src/logging.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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__ */

494
src/main.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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;
}

277
src/md5.c Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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@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;
}

36
src/md5.h Normal file
View File

@ -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@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@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

View File

@ -1,6 +0,0 @@
Makefile
Makefile.in
.deps
.libs
*.la
*.lo

View File

@ -1,3 +0,0 @@
defines that affect compilation
none currently

View File

@ -1,481 +0,0 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 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.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, 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 library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, 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 companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Library 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -1,21 +0,0 @@
## Process this with automake to create Makefile.in
AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = BUILDING COPYING README TODO test_resolver.c
noinst_LTLIBRARIES = libicenet.la
noinst_HEADERS = resolver.h sock.h
libicenet_la_SOURCES = sock.c resolver.c
libicenet_la_CFLAGS = @XIPH_CFLAGS@
AM_CPPFLAGS = @XIPH_CPPFLAGS@
INCLUDES = -I$(srcdir)/..
debug:
$(MAKE) all CFLAGS="@DEBUG@"
profile:
$(MAKE) all CFLAGS="@PROFILE@

View File

@ -1,10 +0,0 @@
This is a name resolving library that's threadsafe.
Right now it only implements this with mutexes, but we should extend it to use gethostbyXXXX_r()
if it's available.
It shoudl work on win32, but i'm probably forgetting a headerfile.
API is basically not going to change. Please consult with the rest of the team before changing the interface.
jack.

View File

@ -1 +0,0 @@
- add getXbyY_r function support

View File

@ -1,226 +0,0 @@
/*
* resolver.c - name resolver library
*
* Copyright (C) 1999 the icecast team <team@icecast.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <winsock2.h>
#endif
#ifndef NO_THREAD
#include <thread/thread.h>
#else
#define thread_mutex_create(x) do{}while(0)
#define thread_mutex_destroy(x) do{}while(0)
#define thread_mutex_lock(x) do{}while(0)
#define thread_mutex_unlock(x) do{}while(0)
#endif
#include "resolver.h"
#include "sock.h"
/* internal function */
static int _isip(const char *what);
/* internal data */
#ifndef NO_THREAD
static mutex_t _resolver_mutex;
#endif
static int _initialized = 0;
#ifdef HAVE_INET_PTON
static int _isip(const char *what)
{
union {
struct in_addr v4addr;
struct in6_addr v6addr;
} addr_u;
if (inet_pton(AF_INET, what, &addr_u.v4addr) <= 0)
return inet_pton(AF_INET6, what, &addr_u.v6addr) > 0 ? 1 : 0;
return 1;
}
#else
static int _isip(const char *what)
{
struct in_addr inp;
return inet_aton(what, &inp);
}
#endif
#if defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO)
char *resolver_getname(const char *ip, char *buff, int len)
{
struct addrinfo *head = NULL, hints;
char *ret = NULL;
if (!_isip(ip)) {
strncpy(buff, ip, len);
buff [len-1] = '\0';
return buff;
}
memset (&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
if (getaddrinfo (ip, NULL, &hints, &head))
return NULL;
if (head)
{
if (getnameinfo(head->ai_addr, head->ai_addrlen, buff, len, NULL,
0, NI_NAMEREQD) == 0)
ret = buff;
freeaddrinfo (head);
}
return ret;
}
char *resolver_getip(const char *name, char *buff, int len)
{
struct addrinfo *head, hints;
char *ret = NULL;
if (_isip(name)) {
strncpy(buff, name, len);
buff [len-1] = '\0';
return buff;
}
memset (&hints, 0, sizeof (hints));
hints . ai_family = AF_UNSPEC;
hints . ai_socktype = SOCK_STREAM;
if (getaddrinfo (name, NULL, &hints, &head))
return NULL;
if (head)
{
if (getnameinfo(head->ai_addr, head->ai_addrlen, buff, len, NULL,
0, NI_NUMERICHOST) == 0)
ret = buff;
freeaddrinfo (head);
}
return ret;
}
#else
char *resolver_getname(const char *ip, char *buff, int len)
{
struct hostent *host;
char *ret = NULL;
struct in_addr addr;
if (! _isip(ip))
{
strncpy(buff, ip, len);
buff [len-1] = '\0';
return buff;
}
thread_mutex_lock(&_resolver_mutex);
if (inet_aton (ip, &addr)) {
if ((host=gethostbyaddr (&addr, sizeof (struct in_addr), AF_INET)))
{
ret = strncpy (buff, host->h_name, len);
buff [len-1] = '\0';
}
}
thread_mutex_unlock(&_resolver_mutex);
return ret;
}
char *resolver_getip(const char *name, char *buff, int len)
{
struct hostent *host;
char *ret = NULL;
if (_isip(name))
{
strncpy(buff, name, len);
buff [len-1] = '\0';
return buff;
}
thread_mutex_lock(&_resolver_mutex);
host = gethostbyname(name);
if (host)
{
char * temp = inet_ntoa(*(struct in_addr *)host->h_addr);
ret = strncpy(buff, temp, len);
buff [len-1] = '\0';
}
thread_mutex_unlock(&_resolver_mutex);
return ret;
}
#endif
void resolver_initialize()
{
/* initialize the lib if we havne't done so already */
if (!_initialized)
{
_initialized = 1;
thread_mutex_create ("resolver", &_resolver_mutex);
/* keep dns connects (TCP) open */
#ifdef HAVE_SETHOSTENT
sethostent(1);
#endif
}
}
void resolver_shutdown(void)
{
if (_initialized)
{
thread_mutex_destroy(&_resolver_mutex);
_initialized = 0;
#ifdef HAVE_ENDHOSTENT
endhostent();
#endif
}
}

View File

@ -1,41 +0,0 @@
/*
** resolver.h
**
** name resolver library header
**
*/
#ifndef __RESOLVER_H
#define __RESOLVER_H
/*
** resolver_lookup
**
** resolves a hosts name from it's ip address
** or
** resolves an ip address from it's host name
**
** returns a pointer to buff, or NULL if an error occured
**
*/
#ifdef _mangle
# define resolver_initialize _mangle(resolver_initialize)
# define resolver_shutdown _mangle(resolver_shutdown)
# define resolver_getname _mangle(resolver_getname)
# define resolver_getip _mangle(resolver_getip)
#endif
void resolver_initialize(void);
void resolver_shutdown(void);
char *resolver_getname(const char *ip, char *buff, int len);
char *resolver_getip(const char *name, char *buff, int len);
#endif

Some files were not shown because too many files have changed in this diff Show More