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

resync with recent trunk changes, contenttype merge and final bits of

handling shoutcast DSP client.

svn path=/icecast/branches/kh/icecast/; revision=8233
This commit is contained in:
Karl Heyes 2004-11-19 19:06:51 +00:00
parent 4b94e72ca3
commit 7afbc8a5f8
21 changed files with 151 additions and 823 deletions

View File

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

45
README
View File

@ -1,8 +1,41 @@
Icecast2 Beta 1.
icecast 2.x - README
---------------------------------------------------------------------
This is an beta release. Not all functionality is fully implemented, though
we believe it to be very stable for all the currently available features.
Icecast is a streaming media server which currently supports Ogg
Vorbis and MP3 audio streams. It can be used to create an Internet
radio station or a privately running jukebox and many things in
between. It is very versatile in that new formats can be added
relatively easily and supports open standards for commuincation and
interaction.
Icecast is distributed under the GNU GPL, version 2. A copy of this
license is included with this software in the COPYING file.
Prerequisites
---------------------------------------------------------------------
icecast requires the following packages :
* libxml2 - http://xmlsoft.org/downloads.html
* libxslt - http://xmlsoft.org/XSLT/downloads.html
* curl - http://curl.haxx.se/download.html (>= version 7.10 required)
NOTE: icecast may be compiled without curl, however this will
disable all Directory server interaction (YP).
* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required)
A Note About RPMS
---------------------------------------------------------------------
This section only applies to you if your operating system uses RPMS.
In order to build icecast, you will need to install the "devel" RPM
packages for each of the prerequisite packages in addition to the
normal RPMS for each package.
please check the websites for each of the prerequisite packages for
appropriate download links for RPMS.
Build/Install
---------------------------------------------------------------------
To build icecast on a Unix platform, perform the following :
Run
@ -12,9 +45,11 @@ Run
To build and install this release.
A sample config file will be placed in /usr/local/etc (on UNIX) or in the current working directory (on Win32) and is called icecast.xml
A sample config file will be placed in /usr/local/etc (on UNIX) or in
the current working directory (on Win32) and is called icecast.xml
Documentation for icecast is available in the doc directory, by viewing doc/icecast2_TOC.html in a browser.
Documentation for icecast is available in the doc directory, by
viewing doc/index.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.

46
TODO
View File

@ -1,33 +1,13 @@
2.0 CRITICAL - These are the things without which 2.0 cannot be released
____________
- Should icecast automatically (i.e. without needing -c) look for the config
file in /etc/icecast.xml or something?
- libshout 2.0 and ices 2.0 releases, also an ices 0.x release that works with
this. Without source clients, icecast isn't much use...
- integrate/include all the documentation done by external groups.
- generally we don't do proper checking for the correct versions of various
libraries (this is probably more of an issue with ices2, but it also affects
icecast)
BUGS
----
- stats get off? this needs testing more testing.
- some stuff (like 'genre') isn't making it into the stats dump
- logging - bytes send and time listening may both be broken?
- slave servers don't work. relay user is not respected by the source (only
admin can read /admin/streamlist), and the slave can't parse the xml result
of streamlist anyway (it expects a simple mountpoint per line)
FEATURES
--------
- Should icecast automatically (i.e. without needing -c) look for the config
file in /etc/icecast.xml or something?
- pull out vorbis comments. and send to stats. This seems to be being
done, but it isn't working right.
@ -45,41 +25,21 @@ FEATURES
- 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.

View File

@ -138,6 +138,12 @@
<fileserve>1</fileserve>
<!-- set the mountpoint for a shoutcast source to use, the default if not
specified is /stream but you can change it here if an alternative is
wanted or an extension is required
<shoutcast-mount>/live.nsv</shoutcast-mount>
-->
<paths>
<!-- basedir is only used if chroot is enabled -->
<basedir>@pkgdatadir@</basedir>

View File

@ -74,7 +74,7 @@ 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_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed!]))
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS])

Binary file not shown.

View File

@ -1,5 +1,5 @@
Name: icecast
Version: 2.0.0
Version: 2.1.0-kh
Release: 1
Summary: Xiph Streaming media server that supports multiple audio formats.

View File

@ -148,14 +148,14 @@ int admin_get_command(char *command)
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, MANAGEAUTH_RAW_REQUEST))
return COMMAND_RAW_MANAGEAUTH;
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_MANAGEAUTH;
else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
return COMMAND_BUILDM3U;
else if(!strcmp(command, ADM_FUNCTION_RAW_REQUEST))
return COMMAND_RAW_ADMIN_FUNCTION;
else if(!strcmp(command, ADM_FUNCTION_TRANSFORMED_REQUEST))
@ -236,8 +236,8 @@ xmlDocPtr admin_build_sourcelist(char *current_source)
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);
xmlNewChild (srcnode, NULL, "content-type",
source->format->contenttype);
if (source->authenticator)
{
xmlNewChild(srcnode, NULL, "authenticator",
@ -321,9 +321,18 @@ void admin_handle_request(client_t *client, char *uri)
}
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
ice_config_t *config = config_get_config ();
ice_config_t *config;
char *pass = httpp_get_query_param (client->parser, "pass");
if (pass == NULL)
{
client_send_400 (client, "missing pass parameter");
return;
}
config = config_get_config ();
httpp_set_query_param (client->parser, "mount", config->shoutcast_mount);
httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY");
httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass);
config_release_config ();
}
@ -336,9 +345,18 @@ void admin_handle_request(client_t *client, char *uri)
client->authenticated = 1;
}
/* This is a mount request, but admin user is allowed */
if (client->authenticated != 1) {
if (connection_check_admin_pass(client->parser))
client->authenticated = 1;
if (client->authenticated != 1)
{
if (connection_check_admin_pass(client->parser) == 0)
{
if (connection_check_source_pass(client->parser, mount) == 0)
{
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);
@ -353,30 +371,25 @@ void admin_handle_request(client_t *client, char *uri)
}
else
{
if (source->running == 0 && source->on_demand == 0)
{
avl_tree_unlock (global.source_tree);
INFO2("Received admin command %s on unavailable mount \"%s\"",
command_string, mount);
client_send_400 (client, "Source is not available");
return;
}
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
source->shoutcast_compat == 0)
{
avl_tree_unlock (global.source_tree);
ERROR0 ("illegal change of metadata on non-shoutcast "
"compatible stream");
client_send_400 (client, "illegal metadata call");
return;
}
INFO2("Received admin command %s on mount \"%s\"",
command_string, mount);
if (source->shoutcast_compat == 0)
{
if (source->running == 0 && source->on_demand == 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;
}
if (client->authenticated != 1)
{
if (connection_check_source_pass(client->parser, mount) == 0)
{
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
avl_tree_unlock(global.source_tree);
client_send_401(client);
return;
}
}
}
admin_handle_mount_request (client, source, command);
avl_tree_unlock(global.source_tree);
}
@ -940,27 +953,11 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
{
char *action;
char *value;
char *source_pass;
char *config_source_pass;
ice_config_t *config;
DEBUG0("Got shoutcast metadata update request");
COMMAND_REQUIRE(client, "mode", action);
COMMAND_REQUIRE(client, "song", value);
COMMAND_REQUIRE(client, "pass", source_pass);
config = config_get_config();
config_source_pass = strdup(config->source_password);
config_release_config();
if ((source->format->type != FORMAT_TYPE_MP3) &&
(source->format->type != FORMAT_TYPE_NSV))
{
thread_mutex_unlock (&source->lock);
client_send_400 (client, "Not mp3 or NSV, cannot update metadata");
return;
}
if (strcmp (action, "updinfo") != 0)
{
@ -969,18 +966,6 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
return;
}
if (strcmp(source_pass, config_source_pass) != 0)
{
thread_mutex_unlock (&source->lock);
ERROR0("Invalid source password specified, metadata not updated");
client_send_400 (client, "Invalid source password");
return;
}
if (config_source_pass) {
free(config_source_pass);
}
if (source->format && source->format->set_tag)
{
source->format->set_tag (source->format, "title", value);

View File

@ -464,7 +464,7 @@ int connection_complete_source (source_t *source)
{
WARN0("No content-type header, falling back to backwards compatibility mode "
"for icecast 1.x relays. Assuming content is mp3.");
format_type = FORMAT_TYPE_MP3;
format_type = FORMAT_TYPE_GENERIC;
}
if (format_get_plugin (format_type, source) < 0)
@ -582,13 +582,18 @@ int connection_check_admin_pass(http_parser_t *parser)
ice_config_t *config = config_get_config();
char *pass = config->admin_password;
char *user = config->admin_username;
char *protocol;
if(!pass || !user) {
config_release_config();
return 0;
}
ret = _check_pass_http(parser, user, pass);
protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
if (protocol && strcmp (protocol, "ICY") == 0)
ret = _check_pass_icy (parser, pass);
else
ret = _check_pass_http (parser, user, pass);
config_release_config();
return ret;
}
@ -881,9 +886,8 @@ static void _handle_get_request(connection_t *con,
if (uri != passed_uri) free (uri);
}
void _handle_shoutcast_compatible(connection_t *con, char *source_password) {
void _handle_shoutcast_compatible(connection_t *con, char *mount, char *source_password) {
char shoutcast_password[256];
char shoutcast_source[256];
char *http_compliant;
int http_compliant_len = 0;
char header[4096];
@ -924,18 +928,14 @@ void _handle_shoutcast_compatible(connection_t *con, char *source_password) {
/* Here we create a valid HTTP request based of the information
that was passed in via the non-HTTP style protocol above. This
means we can use some of our existing code to handle this case */
memset(shoutcast_source, 0, sizeof (shoutcast_source));
strcpy(shoutcast_source, "SOURCE / HTTP/1.0\r\n");
http_compliant_len = strlen(shoutcast_source) +
strlen(header) + 1;
http_compliant_len = strlen(header) + strlen(mount) + 20;
http_compliant = (char *)calloc(1, http_compliant_len);
sprintf(http_compliant, "%s%s", shoutcast_source,
header);
snprintf (http_compliant, http_compliant_len,
"SOURCE %s HTTP/1.0\r\n%s", mount, header);
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, http_compliant,
strlen(http_compliant))) {
_handle_source_request(con, parser, "/", SHOUTCAST_SOURCE_AUTH);
if (httpp_parse(parser, http_compliant, strlen(http_compliant))) {
_handle_source_request(con, parser, mount, SHOUTCAST_SOURCE_AUTH);
free(http_compliant);
return;
}
@ -990,10 +990,12 @@ static void *_handle_connection(void *arg)
if(global.serversock[i] == con->serversock) {
config = config_get_config();
if (config->listeners[i].shoutcast_compat) {
char *shoutcast_mount = strdup (config->shoutcast_mount);
source_password = strdup(config->source_password);
config_release_config();
_handle_shoutcast_compatible(con, source_password);
_handle_shoutcast_compatible(con, shoutcast_mount, source_password);
free(source_password);
free (shoutcast_mount);
continue_flag = 1;
break;
}

View File

@ -58,75 +58,29 @@ format_type_t format_get_type(char *contenttype)
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 if(strcmp(contenttype, "video/nsv") == 0)
return FORMAT_TYPE_NSV;
else if(strcmp(contenttype, "audio/aac") == 0)
return FORMAT_TYPE_AAC;
else if(strcmp(contenttype, "audio/aacp") == 0)
return FORMAT_TYPE_AACPLUS;
else
return FORMAT_ERROR;
}
const 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;
case FORMAT_TYPE_NSV:
return "video/nsv";
break;
case FORMAT_TYPE_AAC:
return "audio/aac";
break;
case FORMAT_TYPE_AACPLUS:
return "audio/aacp";
break;
default:
return NULL;
}
/* We default to the Generic format handler, which
can handle many more formats than just mp3 */
return FORMAT_TYPE_GENERIC;
}
int format_get_plugin(format_type_t type, source_t *source)
{
int ret = -1;
switch (type)
{
case FORMAT_TYPE_OGG:
ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_MP3:
ret = format_mp3_get_plugin (source);
break;
case FORMAT_TYPE_NSV:
ret = format_mp3_get_plugin (source);
source->format->format_description = "NSV Video";
source->format->type = FORMAT_TYPE_NSV;
break;
case FORMAT_TYPE_AAC:
ret = format_mp3_get_plugin (source);
source->format->format_description = "AAC Audio";
source->format->type = FORMAT_TYPE_AAC;
break;
case FORMAT_TYPE_AACPLUS:
ret = format_mp3_get_plugin (source);
source->format->format_description = "AACPlus Audio";
source->format->type = FORMAT_TYPE_AACPLUS;
break;
default:
break;
switch (type) {
case FORMAT_TYPE_OGG:
ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_GENERIC:
ret = format_mp3_get_plugin (source);
break;
default:
break;
}
stats_event (source->mount, "content-type",
format_get_mimetype(source->format->type));
if (ret < 0)
stats_event (source->mount, "content-type",
source->format->contenttype);
return ret;
}
@ -274,7 +228,7 @@ static int format_prepare_headers (source_t *source, client_t *client)
client->respcode = 200;
bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
"Content-Type: %s\r\n", format_get_mimetype (source->format->type));
"Content-Type: %s\r\n", source->format->contenttype);
remaining -= bytes;
ptr += bytes;

View File

@ -29,11 +29,7 @@ typedef enum _format_type_tag
{
FORMAT_ERROR, /* No format, source not processable */
FORMAT_TYPE_OGG,
FORMAT_TYPE_VORBIS,
FORMAT_TYPE_MP3,
FORMAT_TYPE_NSV,
FORMAT_TYPE_AAC,
FORMAT_TYPE_AACPLUS
FORMAT_TYPE_GENERIC
} format_type_t;
typedef struct _format_plugin_tag
@ -43,7 +39,7 @@ typedef struct _format_plugin_tag
/* we need to know the mount to report statistics */
char *mount;
char *format_description;
char *contenttype;
refbuf_t *(*get_buffer)(struct source_tag *);
int (*write_buf_to_client)(struct source_tag *source, client_t *client);
@ -59,7 +55,6 @@ typedef struct _format_plugin_tag
} format_plugin_t;
format_type_t format_get_type(char *contenttype);
const char *format_get_mimetype(format_type_t type);
int format_get_plugin(format_type_t type, struct source_tag *source);
int format_generic_write_to_client (struct source_tag *source, client_t *client);

View File

@ -82,7 +82,7 @@ int format_mp3_get_plugin (source_t *source)
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
plugin->type = FORMAT_TYPE_MP3;
plugin->type = FORMAT_TYPE_GENERIC;
plugin->get_buffer = mp3_get_no_meta;
plugin->write_buf_to_client = format_mp3_write_buf_to_client;
plugin->write_buf_to_file = write_mp3_to_file;
@ -90,9 +90,14 @@ int format_mp3_get_plugin (source_t *source)
plugin->free_plugin = format_mp3_free_plugin;
plugin->set_tag = mp3_set_tag;
plugin->prerelease = NULL;
plugin->format_description = "MP3 audio";
plugin->apply_settings = format_mp3_apply_settings;
plugin->contenttype = httpp_getvar (source->parser, "content-type");
if (plugin->contenttype == NULL) {
/* We default to MP3 audio for old clients without content types */
plugin->contenttype = "audio/mpeg";
}
plugin->_state = state;
/* initial metadata needs to be blank for sending to clients and for

View File

@ -551,7 +551,6 @@ int format_ogg_get_plugin (source_t *source)
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
plugin->type = FORMAT_TYPE_OGG;
plugin->format_description = "Ogg Vorbis";
plugin->get_buffer = ogg_get_buffer;
plugin->write_buf_to_client = write_buf_to_client;
plugin->write_buf_to_file = write_ogg_to_file;
@ -559,6 +558,7 @@ int format_ogg_get_plugin (source_t *source)
plugin->free_plugin = format_ogg_free_plugin;
plugin->set_tag = NULL;
plugin->prerelease = refbuf_page_prerelease;
plugin->contenttype = "application/ogg";
ogg_sync_init (&state->oy);

View File

@ -121,13 +121,13 @@ int format_ogg_get_plugin (source_t *source)
plugin = (format_plugin_t *)calloc(1, 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;
plugin->contenttype = "application/ogg";
state = (vstate_t *)calloc(1, sizeof(vstate_t));
ogg_sync_init(&state->oy);

View File

@ -427,14 +427,14 @@ int fserve_client_create(client_t *httpclient, char *path)
bytes = sock_write(httpclient->con->sock,
"HTTP/1.1 206 Partial Content\r\n"
"Date: %s\r\n"
"Content-Length: %ld\r\n"
"Content-Length: " FORMAT_INT64 "\r\n"
"Content-Range: bytes " FORMAT_INT64 \
"-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n"
"Content-Type: %s\r\n\r\n",
currenttime,
new_content_len,
rangenumber,
rangenumber+new_content_len,
endpos,
client->content_length,
fserve_content_type(path));
}
@ -445,8 +445,10 @@ int fserve_client_create(client_t *httpclient, char *path)
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));
"Content-Length: " FORMAT_INT64 "\r\n"
"Content-Type: %s\r\n\r\n",
client->content_length,
fserve_content_type(path));
}
if(bytes > 0) httpclient->con->sent_bytes = bytes;

View File

@ -20,8 +20,8 @@ typedef struct _fserve_t
client_t *client;
FILE *file;
int ready;
int64_t content_length;
int ready;
struct _fserve_t *next;
} fserve_t;

View File

@ -88,6 +88,7 @@ static void _stop_logging(void)
{
log_close(errorlog);
log_close(accesslog);
log_close(playlistlog);
}
static void _initialize_subsystems(void)
@ -203,7 +204,6 @@ static int _start_logging(void)
strerror(errno));
_fatal_error(buf);
}
log_set_level(errorlog, config->loglevel);
if(strcmp(config->access_log, "-")) {

View File

@ -738,7 +738,6 @@ static void source_init (source_t *source)
source->listeners = 0;
stats_event_inc (NULL, "sources");
stats_event_inc (NULL, "source_total_connections");
stats_event (source->mount, "type", source->format->format_description);
if (source->con)
sock_set_blocking (source->con->sock, SOCK_NONBLOCK);

View File

@ -512,7 +512,7 @@ static ypdata_t *create_yp_entry (source_t *source)
break;
/* ice-* is icecast, icy-* is shoutcast */
add_yp_info (yp, "server_type", source->format->format_description, YP_SERVER_TYPE);
add_yp_info (yp, "server_type", source->format->contenttype, YP_SERVER_TYPE);
if ((s = httpp_getvar(source->parser, "ice-name"))) {
add_yp_info (yp, "server_name", s, YP_SERVER_NAME);
}

View File

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

View File

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