1
0
mirror of http://mpg123.de/trunk/.git synced 2025-08-06 10:02:38 +03:00

merge in multinet to trunk

git-svn-id: svn://scm.orgis.org/mpg123/trunk@5161 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
thor
2022-09-26 11:10:42 +00:00
parent f33c6196e7
commit 4f26cf4b51
21 changed files with 563 additions and 846 deletions

14
NEWS
View File

@@ -1,18 +1,22 @@
1.31.0 1.31.0
------ ------
TODO: Let curl handle redirects again, or make this an option somehow.
Right now this eases testing of redirect code in streamdump.c.
- mpg123: - mpg123:
-- TODO: Revert to internal network code for plain HTTP to ensure continued -- Revert to internal network code for plain HTTP to ensure continued
support for original shoutcast servers that do not talk proper HTTP. support for original shoutcast servers that do not talk proper HTTP.
External backends are built at the same time and can be enforced using
--network <backend>.
-- Try-witout-port for internal network code is gone. We do not need to
keep each ancient hack for specific hosts.
-- Handle redirections independently of the backend behind net123. -- Handle redirections independently of the backend behind net123.
-- Set proxy environment variables when --proxy is specified, for net123
backends to use.
-- Continue reading for long commands in generic control, avoiding -- Continue reading for long commands in generic control, avoiding
unnecessary unfinished command errors. unnecessary unfinished command errors.
-- Change error message from 'unknown command' to -- Change error message from 'unknown command' to
'unknown command with arguments' to avoid confusion why 'help foo' 'unknown command with arguments' to avoid confusion why 'help foo'
is unknown, as opposed to 'help'. is unknown, as opposed to 'help'.
- some build fixes for compiler pickyness
1.30.3 1.30.3
------ ------

View File

@@ -9,7 +9,7 @@ dnl 2.69 at least.
AC_PREREQ([2.69]) AC_PREREQ([2.69])
dnl ############# Initialisation dnl ############# Initialisation
AC_INIT([mpg123], [1.30.3], [maintainer@mpg123.org]) AC_INIT([mpg123], [1.31.0], [maintainer@mpg123.org])
dnl Increment API_VERSION when the API gets changes (new functions). dnl Increment API_VERSION when the API gets changes (new functions).
dnl libmpg123 dnl libmpg123
@@ -1218,7 +1218,7 @@ if test "x$ac_cv_sys_posix_termios" = "xyes"; then
term_type=posix term_type=posix
fi fi
AC_CHECK_FUNCS( random ) AC_CHECK_FUNCS( random setenv unsetenv )
# Check for sched_setscheduler # Check for sched_setscheduler
AC_CHECK_FUNCS( sched_setscheduler setuid getuid) AC_CHECK_FUNCS( sched_setscheduler setuid getuid)
@@ -2577,7 +2577,10 @@ AC_ARG_ENABLE(network,
AC_MSG_CHECKING([for preferred network support]) AC_MSG_CHECKING([for preferred network support])
AC_ARG_WITH([network], AC_ARG_WITH([network],
[AS_HELP_STRING([--with-network=<type>], [Available options, depending on platform, are auto, none, internal, winhttp, wininet, and exec (wget or curl binaries).])], [AS_HELP_STRING([--with-network=<type>],
[Available options, depending on platform, are auto, none, internal, winhttp, wininet (or wininethttp for both), and exec (wget or curl binaries).
The internal code is always built in addition to external options for plain HTTP (esp. Shoutcast v1) support.
The external option is for HTTPS by default, but can be used for HTTP, too.])],
[ [
case "$withval" in case "$withval" in
exec) exec)
@@ -2592,6 +2595,10 @@ AC_ARG_WITH([network],
network_type="wininet" network_type="wininet"
AC_MSG_RESULT([wininet]) AC_MSG_RESULT([wininet])
;; ;;
wininethttp)
network_type="wininethttp"
AC_MSG_RESULT([wininethttp])
;;
internal) internal)
network_type="internal" network_type="internal"
AC_MSG_RESULT([internal]) AC_MSG_RESULT([internal])
@@ -2619,7 +2626,7 @@ if test x$network_type = xauto; then
if test "x$have_fork" = "xyes"; then if test "x$have_fork" = "xyes"; then
network_type=exec network_type=exec
elif test "x$win32_specific_codes" = "xenabled"; then elif test "x$win32_specific_codes" = "xenabled"; then
network_type=wininet network_type=wininethttp
elif test "x$have_network" = "xyes"; then elif test "x$have_network" = "xyes"; then
network_type="internal" network_type="internal"
else else
@@ -2635,7 +2642,7 @@ fi
if test x$network_type = xexec -a x$have_fork != xyes; then if test x$network_type = xexec -a x$have_fork != xyes; then
AC_MSG_ERROR([exec network support selected but fork not available]) AC_MSG_ERROR([exec network support selected but fork not available])
fi fi
if test x$network_type = xwininet -o x$network_type = xwinhttp && test x$win32_specific_codes != xenabled; then if test x$network_type = xwininet -o x$network_type = xwinhttp -o x$network_type = xwininethttp && test x$win32_specific_codes != xenabled; then
AC_MSG_ERROR([wininet or winhttp is currently only for Windows]) AC_MSG_ERROR([wininet or winhttp is currently only for Windows])
fi fi
@@ -2644,7 +2651,7 @@ AM_CONDITIONAL([WIN32_CODES], [ test "x$win32_specific_codes" = xenabled ])
if test x"$ipv6" = xauto; then if test x"$ipv6" = xauto; then
AC_MSG_CHECKING([IPv6 use]) AC_MSG_CHECKING([IPv6 use])
if test x"$have_ipv6" = xyes -a x"$network_type" = xinternal; then if test x"$have_ipv6" = xyes -a x"$network_type" != xdisabled; then
ipv6=enabled ipv6=enabled
else else
ipv6=disabled ipv6=disabled
@@ -2652,14 +2659,31 @@ if test x"$ipv6" = xauto; then
AC_MSG_RESULT([$ipv6]) AC_MSG_RESULT([$ipv6])
fi fi
if test x$network_type = xinternal -a x$network_internal = xwinsock2; then if test x$network_type != xdisabled -a x$network_internal = xwinsock2; then
AC_DEFINE([WANT_WIN32_SOCKETS], [1], [ Define to use Win32 sockets ]) AC_DEFINE([WANT_WIN32_SOCKETS], [1], [ Define to use Win32 sockets ])
fi fi
AM_CONDITIONAL([NETWORK_WINSOCK], [ test x$network_type = xinternal -a x$network_internal = xwinsock2 ]) AM_CONDITIONAL([NETWORK_WINSOCK], [ test x$network_type != xdisabled -a x$network_internal = xwinsock2 ])
AM_CONDITIONAL([NET123], [ test x$network_type != xdisabled ])
AM_CONDITIONAL([NET123_EXEC], [ test x$network_type = xexec ]) AM_CONDITIONAL([NET123_EXEC], [ test x$network_type = xexec ])
AM_CONDITIONAL([NET123_WINHTTP], [ test x$network_type = xwinhttp ]) AM_CONDITIONAL([NET123_WINHTTP], [ test x$network_type = xwinhttp || test x$network_type = xwininethttp ])
AM_CONDITIONAL([NET123_WININET], [ test x$network_type = xwininet ]) AM_CONDITIONAL([NET123_WININET], [ test x$network_type = xwininet || test x$network_type = xwininethttp ])
case "$network_type" in
exec)
AC_DEFINE(NET123_EXEC, 1, [ Define for executable-based networking (for HTTPS). ])
;;
winhttp)
AC_DEFINE(NET123_WINHTTP, 1, [ Define for winhttp networking (for HTTPS). ])
;;
wininet)
AC_DEFINE(NET123_WININET, 1, [ Define for wininet networking (for HTTPS). ])
;;
wininethttp)
AC_DEFINE(NET123_WININET, 1, [ Define for wininet networking (for HTTPS). ])
AC_DEFINE(NET123_WINHTTP, 1, [ Define for winhttp networking (for HTTPS). ])
;;
esac
dnl ############## Terminal choice dnl ############## Terminal choice
@@ -2685,7 +2709,7 @@ if test x"$fifo" = xenabled; then
fi fi
dnl ############## Network enable dnl ############## Network enable
if test x"$network_type" = xinternal; then if test x"$network_type" != xdisabled; then
AC_DEFINE(NETWORK, 1, [ Define if network support is enabled. ]) AC_DEFINE(NETWORK, 1, [ Define if network support is enabled. ])
if test x"$have_network" = xno; then if test x"$have_network" = xno; then
AC_MSG_WARN( [ You forced network code while I think there is support missing! ] ) AC_MSG_WARN( [ You forced network code while I think there is support missing! ] )
@@ -2742,9 +2766,11 @@ echo "
Extreme debugging ....... $xdebugging Extreme debugging ....... $xdebugging
Seek table size ......... $seektable Seek table size ......... $seektable
FIFO support ............ $fifo FIFO support ............ $fifo
Buffer .................. $buffer Buffer .................. $buffer"
Network (http streams) .. $network_type" if test x$network_type != xdisabled; then
if test x$network_type = xinternal; then if test x$network_type != xinternal; then
echo " External network ........ $network_type"
fi
echo " Internal network type ... $network_internal echo " Internal network type ... $network_internal
IPv6 (getaddrinfo) ...... $ipv6" IPv6 (getaddrinfo) ...... $ipv6"
fi fi

View File

@@ -46,6 +46,8 @@ check_function_exists(mkfifo HAVE_MKFIFO)
check_function_exists(mmap HAVE_MMAP) check_function_exists(mmap HAVE_MMAP)
check_function_exists(nl_langinfo HAVE_NL_LANGINFO) check_function_exists(nl_langinfo HAVE_NL_LANGINFO)
check_function_exists(random HAVE_RANDOM) check_function_exists(random HAVE_RANDOM)
check_function_exists(setenv HAVE_SETENV)
check_function_exists(unsetenv HAVE_UNSETENV)
check_function_exists(setlocale HAVE_SETLOCALE) check_function_exists(setlocale HAVE_SETLOCALE)
check_function_exists(setpriority HAVE_SETPRIORITY) check_function_exists(setpriority HAVE_SETPRIORITY)
check_function_exists(shmget HAVE_SHMGET) check_function_exists(shmget HAVE_SHMGET)

View File

@@ -118,17 +118,21 @@ if TERM_NONE
src_mpg123_SOURCES += src/term_none.c src_mpg123_SOURCES += src/term_none.c
endif endif
if NET123
src_mpg123_SOURCES += src/net123.h
endif
if NET123_EXEC if NET123_EXEC
src_mpg123_SOURCES += src/net123.h src/net123_exec.c src_mpg123_SOURCES += src/net123_exec.c
endif endif
if NET123_WINHTTP if NET123_WINHTTP
src_mpg123_SOURCES += src/net123.h src/net123_winhttp.c src_mpg123_SOURCES += src/net123_winhttp.c
src_mpg123_LDADD += -lwinhttp src_mpg123_LDADD += -lwinhttp
endif endif
if NET123_WININET if NET123_WININET
src_mpg123_SOURCES += src/net123.h src/net123_wininet.c src_mpg123_SOURCES += src/net123_wininet.c
src_mpg123_LDADD += -lwininet src_mpg123_LDADD += -lwininet
endif endif

View File

@@ -179,7 +179,6 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
off_t rframes; off_t rframes;
int spf; int spf;
double basevol, realvol; double basevol, realvol;
char *icy;
long rate; long rate;
int framesize; int framesize;
struct mpg123_frameinfo mi; struct mpg123_frameinfo mi;

View File

@@ -751,7 +751,6 @@ int control_generic (mpg123_handle *fr)
/* Simple EQ: SEQ <BASS> <MID> <TREBLE> */ /* Simple EQ: SEQ <BASS> <MID> <TREBLE> */
if (!strcasecmp(cmd, "SEQ")) { if (!strcasecmp(cmd, "SEQ")) {
double b,m,t; double b,m,t;
int cn;
if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3) if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3)
{ {
mpg123_eq_bands(fr, MPG123_LR, 0, 0, b); mpg123_eq_bands(fr, MPG123_LR, 0, 0, b);

View File

@@ -153,74 +153,6 @@ debunk_result:
#ifdef NETWORK #ifdef NETWORK
#if !defined (WANT_WIN32_SOCKETS)
static int writestring (int fd, mpg123_string *string)
{
size_t result, bytes;
char *ptr = string->p;
bytes = string->fill ? string->fill-1 : 0;
while(bytes)
{
result = write(fd, ptr, bytes);
if(result < 0 && errno != EINTR)
{
merror("writing http string: %s", strerror(errno));
return FALSE;
}
else if(result == 0)
{
error("write: socket closed unexpectedly");
return FALSE;
}
ptr += result;
bytes -= result;
}
return TRUE;
}
static size_t readstring (mpg123_string *string, size_t maxlen, int fd)
{
int err;
debug2("Attempting readstring on %d for %"SIZE_P" bytes", fd, (size_p)maxlen);
string->fill = 0;
while(maxlen == 0 || string->fill < maxlen)
{
if(string->size-string->fill < 1)
if(!mpg123_grow_string(string, string->fill+4096))
{
error("Cannot allocate memory for reading.");
string->fill = 0;
return 0;
}
err = read(fd,string->p+string->fill,1);
/* Whoa... reading one byte at a time... one could ensure the line break in another way, but more work. */
if( err == 1)
{
string->fill++;
if(string->p[string->fill-1] == '\n') break;
}
else if(errno != EINTR)
{
error("Error reading from socket or unexpected EOF.");
string->fill = 0;
/* bail out to prevent endless loop */
return 0;
}
}
if(!mpg123_grow_string(string, string->fill+1))
{
string->fill=0;
}
else
{
string->p[string->fill] = 0;
string->fill++;
}
return string->fill;
}
#endif /* WANT_WIN32_SOCKETS */
void encode64 (char *source,char *destination) void encode64 (char *source,char *destination)
{ {
@@ -383,11 +315,10 @@ int translate_url(const char *url, mpg123_string *purl)
return TRUE; return TRUE;
} }
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, int *try_without_port) int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, const char * const *client_head)
{ {
char* ttemp; char* ttemp;
int ret = TRUE; int ret = TRUE;
const char *icy = param.talk_icy ? icy_yes : icy_no;
/* hm, my test redirection had troubles with line break before HTTP/1.0 */ /* hm, my test redirection had troubles with line break before HTTP/1.0 */
if((ttemp = strchr(request->p,'\r')) != NULL){ *ttemp = 0; request->fill = ttemp-request->p+1; } if((ttemp = strchr(request->p,'\r')) != NULL){ *ttemp = 0; request->fill = ttemp-request->p+1; }
@@ -405,26 +336,14 @@ int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *por
if(host->fill) if(host->fill)
{ /* Give virtual hosting a chance... adding the "Host: ... " line. */ { /* Give virtual hosting a chance... adding the "Host: ... " line. */
debug2("Host: %s:%s", host->p, port->p); debug2("Host: %s:%s", host->p, port->p);
if( mpg123_add_string(request, "Host: ") if(!( mpg123_add_string(request, "Host: ")
&& mpg123_add_string(request, host->p) && mpg123_add_string(request, host->p)
&& ( *try_without_port || ( && mpg123_add_string(request, ":")
mpg123_add_string(request, ":") && mpg123_add_string(request, port->p)
&& mpg123_add_string(request, port->p) )) && mpg123_add_string(request, "\r\n") ))
&& mpg123_add_string(request, "\r\n") ) return FALSE;
{
if(*try_without_port) *try_without_port = 0;
}
else return FALSE;
} }
/* Acceptance, stream setup. */
if( !append_accept(request)
|| !mpg123_add_string(request, "\r\n")
|| !mpg123_add_string(request, CONN_HEAD)
|| !mpg123_add_string(request, icy)
|| !mpg123_add_string(request, "\r\n") )
return FALSE;
/* Authorization. */ /* Authorization. */
if (httpauth1->fill || param.httpauth) { if (httpauth1->fill || param.httpauth) {
char *buf; char *buf;
@@ -456,72 +375,30 @@ int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *por
free(buf); /* Watch out for leaking if you introduce returns before this line. */ free(buf); /* Watch out for leaking if you introduce returns before this line. */
} }
while(ret && *client_head)
{
ret = mpg123_add_string(request, *client_head);
if(ret)
ret = mpg123_add_string(request, "\r\n");
++client_head;
}
if(ret) ret = mpg123_add_string(request, "\r\n"); if(ret) ret = mpg123_add_string(request, "\r\n");
return ret; return ret;
} }
#if !defined (WANT_WIN32_SOCKETS)
static int resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
{
debug1("request_url:%s", request_url->p);
/* initialized with full old url */
if(!mpg123_copy_string(request_url, purl)) return FALSE;
/* We may strip it down to a prefix ot totally. */ int http_open(const char* url, struct httpdata *hd, const char * const *client_head)
if(strncasecmp(response->p, "Location: http://", 17))
{ /* OK, only partial strip, need prefix for relative path. */
char* ptmp = NULL;
/* though it's not RFC (?), accept relative URIs as wget does */
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
/* not absolute uri, could still be server-absolute */
/* I prepend a part of the request... out of the request */
if(response->p[10] == '/')
{
/* only prepend http://server/ */
/* I null the first / after http:// */
ptmp = strchr(purl->p+7,'/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
}
else
{
/* prepend http://server/path/ */
/* now we want the last / */
ptmp = strrchr(purl->p+7, '/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
}
}
else purl->fill = 0;
debug1("prefix=%s", purl->fill ? purl->p : "");
if(!mpg123_add_string(purl, response->p+10)) return FALSE;
debug1(" purl: %s", purl->p);
debug1("old request_url: %s", request_url->p);
return TRUE;
}
int http_open(const char* url, struct httpdata *hd)
{ {
mpg123_string purl, host, port, path; mpg123_string purl, host, port, path;
mpg123_string request, response, request_url; mpg123_string request, request_url;
mpg123_string httpauth1; mpg123_string httpauth1;
int sock = -1; int sock = -1;
int oom = 0; int oom = 0;
int relocate, numrelocs = 0;
int got_location = FALSE;
/*
workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
this site's apache gives me a relocation to the same place when I give the port in Host request field
for the record: Apache/2.0.51 (Fedora)
*/
int try_without_port = 0;
mpg123_init_string(&purl); mpg123_init_string(&purl);
mpg123_init_string(&host); mpg123_init_string(&host);
mpg123_init_string(&port); mpg123_init_string(&port);
mpg123_init_string(&path); mpg123_init_string(&path);
mpg123_init_string(&request); mpg123_init_string(&request);
mpg123_init_string(&response);
mpg123_init_string(&request_url); mpg123_init_string(&request_url);
mpg123_init_string(&httpauth1); mpg123_init_string(&httpauth1);
@@ -533,26 +410,6 @@ int http_open(const char* url, struct httpdata *hd)
/* Don't confuse the different auth strings... */ /* Don't confuse the different auth strings... */
if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; } if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }
/* "GET http://" 11
* " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
* 26 + PACKAGE_NAME + PACKAGE_VERSION
* accept header + accept_length()
* "Authorization: Basic \r\n" 23
* "\r\n" 2
* ... plus the other predefined header lines
*/
/* Just use this estimate as first guess to reduce malloc calls in string library. */
{
size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION)
+ accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
if( !mpg123_grow_string(&request, length_estimate)
|| !mpg123_grow_string(&response,4096) )
{
oom=1; goto exit;
}
}
do
{ {
/* Storing the request url, with http:// prepended if needed. */ /* Storing the request url, with http:// prepended if needed. */
/* used to be url here... seemed wrong to me (when loop advanced...) */ /* used to be url here... seemed wrong to me (when loop advanced...) */
@@ -585,7 +442,7 @@ int http_open(const char* url, struct httpdata *hd)
} }
} }
if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; } if(!fill_request(&request, &host, &port, &httpauth1, client_head)){ oom=1; goto exit; }
httpauth1.fill = 0; /* We use the auth data from the URL only once. */ httpauth1.fill = 0; /* We use the auth data from the URL only once. */
if (hd->proxystate >= PROXY_HOST) if (hd->proxystate >= PROXY_HOST)
@@ -599,105 +456,27 @@ int http_open(const char* url, struct httpdata *hd)
} }
} }
debug2("attempting to open_connection to %s:%s", host.p, port.p); debug2("attempting to open_connection to %s:%s", host.p, port.p);
#ifdef WANT_WIN32_SOCKETS
sock = win32_net_open_connection(&host, &port);
#else
sock = open_connection(&host, &port); sock = open_connection(&host, &port);
#endif
if(sock < 0) if(sock < 0)
{ {
error1("Unable to establish connection to %s", host.fill ? host.p : ""); error1("Unable to establish connection to %s", host.fill ? host.p : "");
goto exit; goto exit;
} }
#define http_failure close(sock); sock=-1; goto exit; #define http_failure close(sock); sock=-1; goto exit;
if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p); if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
if(!writestring(sock, &request)){ http_failure; } #ifdef WANT_WIN32_SOCKETS
relocate = FALSE; if(!win32_net_writestring (sock, &request))
/* Arbitrary length limit here... */ #else
#define safe_readstring \ if(unintr_write(sock, request.p, request.fill-1) != request.fill-1)
readstring(&response, SIZE_MAX/16, sock); \ #endif
if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
{ \
error("HTTP response line exceeds max. length"); \
http_failure; \
} \
else if(response.fill == 0) \
{ \
error("readstring failed"); \
http_failure; \
} \
if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
safe_readstring;
{ {
char *sptr; http_failure;
if((sptr = strchr(response.p, ' ')))
{
if(response.fill > sptr-response.p+2)
switch (sptr[1])
{
case '3':
relocate = TRUE;
case '2':
break;
default:
fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
http_failure;
}
else{ error("Too short response,"); http_failure; }
}
} }
/* If we are relocated, we need to look out for a Location header. */
got_location = FALSE;
do
{
safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
if (!strncasecmp(response.p, "Location: ", 10))
{ /* It is a redirection! */
if(!resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }
if(!strcmp(purl.p, request_url.p))
{
warning("relocated to very same place! trying request again without host port");
try_without_port = 1;
}
got_location = TRUE;
}
else
{ /* We got a header line (or the closing empty line). */
char *tmp;
debug1("searching for header values... %s", response.p);
/* Not sure if I want to bail out on error here. */
/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
get_header_string(&response, "content-type", &hd->content_type);
get_header_string(&response, "icy-name", &hd->icy_name);
get_header_string(&response, "icy-url", &hd->icy_url);
/* watch out for icy-metaint */
if((tmp = get_header_val("icy-metaint", &response)))
{
hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
debug1("got icy-metaint %li", (long int)hd->icy_interval);
}
}
} while(response.p[0] != '\r' && response.p[0] != '\n');
if(relocate)
{
close(sock);
sock = -1;
/* Forget content type, might just relate to a displayed error page,
not the resource being redirected to. */
mpg123_free_string(&hd->content_type);
mpg123_init_string(&hd->content_type);
}
} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
if(relocate)
{
if(!got_location)
error("Server meant to redirect but failed to provide a location!");
else
error1("Too many HTTP relocations (%i).", numrelocs);
http_failure;
} }
exit: /* The end as well as the exception handling point... */ exit: /* The end as well as the exception handling point... */
@@ -708,22 +487,10 @@ exit: /* The end as well as the exception handling point... */
mpg123_free_string(&port); mpg123_free_string(&port);
mpg123_free_string(&path); mpg123_free_string(&path);
mpg123_free_string(&request); mpg123_free_string(&request);
mpg123_free_string(&response);
mpg123_free_string(&request_url); mpg123_free_string(&request_url);
mpg123_free_string(&httpauth1); mpg123_free_string(&httpauth1);
return sock; return sock;
} }
#endif /*WANT_WIN32_SOCKETS*/
#else /* NETWORK */
/* stub */
int http_open (const char* url, struct httpdata *hd)
{
if(!param.quiet)
error("HTTP support not built in.");
return -1;
}
#endif #endif
/* EOF */ /* EOF */

View File

@@ -49,20 +49,17 @@ int debunk_mime(const char* mime);
int proxy_init(struct httpdata *hd); int proxy_init(struct httpdata *hd);
int translate_url(const char *url, mpg123_string *purl); int translate_url(const char *url, mpg123_string *purl);
size_t accept_length(void); size_t accept_length(void);
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, int *try_without_port); int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, const char * const *client_head);
void get_header_string(mpg123_string *response, const char *fieldname, mpg123_string *store); void get_header_string(mpg123_string *response, const char *fieldname, mpg123_string *store);
char *get_header_val(const char *hname, mpg123_string *response); char *get_header_val(const char *hname, mpg123_string *response);
/* needed for HTTP/1.1 non-pipelining mode */
/* #define CONN_HEAD "Connection: close\r\n" */
#define CONN_HEAD ""
#define icy_yes "Icy-MetaData: 1" #define icy_yes "Icy-MetaData: 1"
#define icy_no "Icy-MetaData: 0" #define icy_no "Icy-MetaData: 0"
// Append an accept header line to the string, without line end. // Append an accept header line to the string, without line end.
int append_accept(mpg123_string *s); int append_accept(mpg123_string *s);
/* takes url and content type string address, opens resource, returns fd for data, allocates and sets content type */ // Open HTTP URL with internal network code.
extern int http_open (const char* url, struct httpdata *hd); int http_open(const char* url, struct httpdata *hd, const char * const *client_head);
#endif #endif

View File

@@ -45,7 +45,7 @@ int utf8force = 0; // enforce UTF-8 workings
int utf8env = 0; // produce UTF-8 text output int utf8env = 0; // produce UTF-8 text output
int utf8loc = 0; // have actual UTF-8 locale (so that mbstowcs() works) int utf8loc = 0; // have actual UTF-8 locale (so that mbstowcs() works)
static int term_is_fun = -1; //static int term_is_fun = -1;
static const char joker_symbol = '?'; static const char joker_symbol = '?';
static const char *uni_repl = "\xef\xbf\xbd"; static const char *uni_repl = "\xef\xbf\xbd";

View File

@@ -483,7 +483,7 @@ static void set_appflag(char *arg, topt *opts)
#if defined(NETWORK) || defined(NET123) #if defined(NETWORK) || defined(NET123)
static void set_httpauth(char *arg, topt *opts) static void set_httpauth(char *arg, topt *opts)
{ {
param.httpauth = strdup(arg); param.httpauth = compat_strdup(arg);
// Do not advertise the password for all system users. // Do not advertise the password for all system users.
memset(arg, 'x', strlen(arg)); memset(arg, 'x', strlen(arg));
} }
@@ -1599,14 +1599,13 @@ static void long_usage(int err)
fprintf(o," -y --no-resync DISABLES resync on error (--resync is deprecated)\n"); fprintf(o," -y --no-resync DISABLES resync on error (--resync is deprecated)\n");
fprintf(o," -F --no-frankenstein disable support for Frankenstein streams\n"); fprintf(o," -F --no-frankenstein disable support for Frankenstein streams\n");
#if defined(NETWORK) || defined(NET123) #if defined(NETWORK) || defined(NET123)
#ifdef NETWORK fprintf(o," -p <f> --proxy <f> override proxy environemnt variable for plain HTTP\n");
fprintf(o," -p <f> --proxy <f> set WWW proxy\n");
#else
fprintf(o," --network <b> select network backend, available: auto"); fprintf(o," --network <b> select network backend, available: auto");
const char **nb = net123_backends; const char **nb = net123_backends;
while(*nb){ fprintf(o, " %s", *nb++); } while(*nb){ fprintf(o, " %s", *nb++); }
fprintf(o, "\n"); fprintf(o, "\n");
#endif fprintf(o," (auto meaning internal code for plain HTTP and the\n");
fprintf(o," first external option for HTTPS)\n");
fprintf(o," -u --auth set auth values for HTTP access\n"); fprintf(o," -u --auth set auth values for HTTP access\n");
fprintf(o," --auth-file set auth values for HTTP access from given file\n"); fprintf(o," --auth-file set auth values for HTTP access from given file\n");
fprintf(o," --ignore-mime ignore HTTP MIME types (content-type)\n"); fprintf(o," --ignore-mime ignore HTTP MIME types (content-type)\n");
@@ -1635,6 +1634,7 @@ static void long_usage(int err)
fprintf(o," --list-devices list the available output devices for given output module\n"); fprintf(o," --list-devices list the available output devices for given output module\n");
fprintf(o," -a <d> --audiodevice <d> select audio device (depending on chosen module)\n"); fprintf(o," -a <d> --audiodevice <d> select audio device (depending on chosen module)\n");
fprintf(o," -s --stdout write raw audio to stdout (host native format)\n"); fprintf(o," -s --stdout write raw audio to stdout (host native format)\n");
fprintf(o," -O <f> --outfile <f> write raw audio to given file (- is stdout)\n");
fprintf(o," -w <f> --wav <f> write samples as WAV file in <f> (- is stdout)\n"); fprintf(o," -w <f> --wav <f> write samples as WAV file in <f> (- is stdout)\n");
fprintf(o," --au <f> write samples as Sun AU file in <f> (- is stdout)\n"); fprintf(o," --au <f> write samples as Sun AU file in <f> (- is stdout)\n");
fprintf(o," --cdr <f> write samples as raw CD audio file in <f> (- is stdout)\n"); fprintf(o," --cdr <f> write samples as raw CD audio file in <f> (- is stdout)\n");

View File

@@ -39,12 +39,17 @@
#ifndef _MPG123_NET123_H_ #ifndef _MPG123_NET123_H_
#define _MPG123_NET123_H_ #define _MPG123_NET123_H_
#include "config.h"
#include <sys/types.h> #include <sys/types.h>
// The network implementation defines the struct for private use. // stream handle for differing implementations, build-time plugins
// The purpose is just to keep enough context to be able to struct net123_handle_struct
// call net123_read() and net123_close() afterwards. {
struct net123_handle_struct; void *parts; // custom internal data
// callbacks
size_t (*read)(struct net123_handle_struct *nh, void *buf, size_t bufsize);
void (*close)(struct net123_handle_struct *nh);
};
typedef struct net123_handle_struct net123_handle; typedef struct net123_handle_struct net123_handle;
extern const char *net123_backends[]; extern const char *net123_backends[];
@@ -54,16 +59,16 @@ extern const char *net123_backends[];
// and then the raw data. // and then the raw data.
// client_head contains header lines to send with the request, without // client_head contains header lines to send with the request, without
// line ending // line ending
net123_handle *net123_open(const char *url, const char * const *client_head);
// Read data into buffer, return bytes read. // Variant for the external binding.
// This handles interrupts (EAGAIN, EINTR, ..) internally and only returns #ifdef NET123_EXEC
// a short byte count on EOF or error. End of file or error is not distinguished: net123_handle *net123_open_exec(const char *url, const char * const *client_head);
// For the user, it only matters if there will be more bytes or not. #endif
// Feel free to communicate errors via error() / merror() functions inside. #ifdef NET123_WININET
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize); net123_handle *net123_open_wininet(const char *url, const char * const *client_head);
#endif
// Call that to free up resources, end processes. #ifdef NET123_WINHTTP
void net123_close(net123_handle *nh); net123_handle *net123_open_winhttp(const char *url, const char * const *client_head);
#endif
#endif #endif

View File

@@ -8,6 +8,9 @@
specifically if param.network_backend is set accordingly. specifically if param.network_backend is set accordingly.
*/ */
// kill
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "config.h" #include "config.h"
#include "net123.h" #include "net123.h"
@@ -34,18 +37,11 @@
// wget --user=... --password=... // wget --user=... --password=...
// Alternatively: Have them in .netrc. // Alternatively: Have them in .netrc.
const char *net123_backends[] = typedef struct
{
"wget"
, "curl"
, NULL
};
struct net123_handle_struct
{ {
int fd; int fd;
pid_t worker; pid_t worker;
}; } exec_handle;
// Combine two given strings into one newly allocated one. // Combine two given strings into one newly allocated one.
// Use: (--parameter=, value) -> --parameter=value // Use: (--parameter=, value) -> --parameter=value
@@ -198,8 +194,6 @@ static char **curl_argv(const char *url, const char * const * client_head)
, "--silent" , "--silent"
, "--show-error" , "--show-error"
#endif #endif
, "--max-redirs"
, "0"
, "--dump-header" , "--dump-header"
, "-" , "-"
}; };
@@ -241,7 +235,36 @@ static char **curl_argv(const char *url, const char * const * client_head)
return argv; return argv;
} }
net123_handle *net123_open(const char *url, const char * const * client_head)
static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize)
{
if(!nh || (bufsize && !buf))
return 0;
return unintr_read(((exec_handle*)nh->parts)->fd, buf, bufsize);
}
static void net123_close(net123_handle *nh)
{
if(!nh)
return;
exec_handle *eh = nh->parts;
if(eh->worker)
{
kill(eh->worker, SIGKILL);
errno = 0;
if(waitpid(eh->worker, NULL, 0) < 0)
merror("failed to wait for worker process: %s", strerror(errno));
else if(param.verbose > 1)
fprintf(stderr, "Note: network helper %"PRIiMAX" finished\n", (intmax_t)eh->worker);
}
if(eh->fd > -1)
close(eh->fd);
free(nh->parts);
free(nh);
}
net123_handle *net123_open_exec(const char *url, const char * const * client_head)
{ {
int use_curl = 0; int use_curl = 0;
char * const curl_check_argv[] = { "curl", "--help", "all", NULL }; char * const curl_check_argv[] = { "curl", "--help", "all", NULL };
@@ -278,12 +301,21 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
} }
int fd[2]; int fd[2];
int hi = -1; // index of header value that might get a continuation line
net123_handle *nh = malloc(sizeof(net123_handle)); net123_handle *nh = malloc(sizeof(net123_handle));
if(!nh) exec_handle *eh = malloc(sizeof(exec_handle));
if(!nh || !eh)
{
if(nh)
free(nh);
if(eh)
free(eh);
return NULL; return NULL;
nh->fd = -1; }
nh->worker = 0; nh->parts = eh;
nh->read = net123_read;
nh->close = net123_close;
eh->fd = -1;
eh->worker = 0;
errno = 0; errno = 0;
if(pipe(fd)) if(pipe(fd))
{ {
@@ -295,15 +327,15 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
compat_binmode(fd[0], TRUE); compat_binmode(fd[0], TRUE);
compat_binmode(fd[1], TRUE); compat_binmode(fd[1], TRUE);
nh->worker = fork(); eh->worker = fork();
if(nh->worker == -1) if(eh->worker == -1)
{ {
merror("fork failed: %s", strerror(errno)); merror("fork failed: %s", strerror(errno));
free(nh); free(nh);
return NULL; return NULL;
} }
if(nh->worker == 0) if(eh->worker == 0)
{ {
close(fd[0]); close(fd[0]);
dup2(fd[1], STDOUT_FILENO); dup2(fd[1], STDOUT_FILENO);
@@ -311,7 +343,6 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
dup2(infd, STDIN_FILENO); dup2(infd, STDIN_FILENO);
// child // child
// Proxy environment variables can just be set in the user and inherited here, right? // Proxy environment variables can just be set in the user and inherited here, right?
int argc;
char **argv = use_curl ? curl_argv(url, client_head) : wget_argv(url, client_head); char **argv = use_curl ? curl_argv(url, client_head) : wget_argv(url, client_head);
if(!argv) if(!argv)
@@ -340,35 +371,9 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
} }
// parent // parent
if(param.verbose > 1) if(param.verbose > 1)
fprintf(stderr, "Note: started network helper with PID %"PRIiMAX"\n", (intmax_t)nh->worker); fprintf(stderr, "Note: started network helper with PID %"PRIiMAX"\n", (intmax_t)eh->worker);
errno = 0; errno = 0;
close(fd[1]); close(fd[1]);
nh->fd = fd[0]; eh->fd = fd[0];
return nh; return nh;
} }
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize)
{
if(!nh || (bufsize && !buf))
return 0;
return unintr_read(nh->fd, buf, bufsize);
}
void net123_close(net123_handle *nh)
{
if(!nh)
return;
if(nh->worker)
{
kill(nh->worker, SIGKILL);
errno = 0;
if(waitpid(nh->worker, NULL, 0) < 0)
merror("failed to wait for worker process: %s", strerror(errno));
else if(param.verbose > 1)
fprintf(stderr, "Note: network helper %"PRIiMAX" finished\n", (intmax_t)nh->worker);
}
if(nh->fd > -1)
close(nh->fd);
free(nh);
}

View File

@@ -5,13 +5,11 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <winhttp.h> #include <winhttp.h>
const char *net123_backends[] = { "(always winhttp)", NULL };
// The network implementation defines the struct for private use. // The network implementation defines the struct for private use.
// The purpose is just to keep enough context to be able to // The purpose is just to keep enough context to be able to
// call net123_read() and net123_close() afterwards. // call net123_read() and net123_close() afterwards.
#define URL_COMPONENTS_LENGTH 255 #define URL_COMPONENTS_LENGTH 255
struct net123_handle_struct { typedef struct {
HINTERNET session; HINTERNET session;
HINTERNET connect; HINTERNET connect;
HINTERNET request; HINTERNET request;
@@ -26,7 +24,7 @@ struct net123_handle_struct {
size_t headers_pos, headers_len; size_t headers_pos, headers_len;
DWORD internetStatus, internetStatusLength; DWORD internetStatus, internetStatusLength;
LPVOID additionalInfo; LPVOID additionalInfo;
}; } winhttp_handle;
#define MPG123CONCAT_(x,y) x ## y #define MPG123CONCAT_(x,y) x ## y
#define MPG123CONCAT(x,y) MPG123CONCAT_(x,y) #define MPG123CONCAT(x,y) MPG123CONCAT_(x,y)
@@ -34,7 +32,7 @@ struct net123_handle_struct {
#define MPG123STRINGIFY(x) MPG123STRINGIFY_(x) #define MPG123STRINGIFY(x) MPG123STRINGIFY_(x)
#define MPG123WSTR(x) MPG123CONCAT(L,MPG123STRINGIFY(x)) #define MPG123WSTR(x) MPG123CONCAT(L,MPG123STRINGIFY(x))
static DWORD wrap_auth(net123_handle *nh){ static DWORD wrap_auth(winhttp_handle *nh){
DWORD mode; DWORD mode;
DWORD ret; DWORD ret;
@@ -81,13 +79,16 @@ static void debug_crack(URL_COMPONENTS *comps){}
static static
void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){ void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){
net123_handle *nh = (net123_handle *)dwContext; winhttp_handle *nh = (winhttp_handle *)dwContext;
nh->internetStatus = dwInternetStatus; nh->internetStatus = dwInternetStatus;
nh->additionalInfo = lpvStatusInformation; nh->additionalInfo = lpvStatusInformation;
nh->internetStatusLength = dwStatusInformationLength; nh->internetStatusLength = dwStatusInformationLength;
} }
net123_handle *net123_open(const char *url, const char * const *client_head){ static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize);
static void net123_close(net123_handle *nh);
net123_handle *net123_open_winhttp(const char *url, const char * const *client_head){
LPWSTR urlW = NULL, headers = NULL; LPWSTR urlW = NULL, headers = NULL;
size_t ii; size_t ii;
WINBOOL res; WINBOOL res;
@@ -101,8 +102,18 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
win32_utf8_wide(url, &urlW, NULL); win32_utf8_wide(url, &urlW, NULL);
if(urlW == NULL) goto cleanup; if(urlW == NULL) goto cleanup;
net123_handle *ret = calloc(1, sizeof(net123_handle)); winhttp_handle *ret = calloc(1, sizeof(winhttp_handle));
if (!ret) return ret; if (!ret) goto cleanup;
net123_handle *handle = calloc(1, sizeof(net123_handle));
if (!handle) {
free(ret);
goto cleanup;
}
handle->parts = ret;
handle->read = net123_read;
handle->close = net123_close;
ret->comps.dwStructSize = sizeof(ret->comps); ret->comps.dwStructSize = sizeof(ret->comps);
ret->comps.dwSchemeLength = 0; ret->comps.dwSchemeLength = 0;
@@ -206,13 +217,13 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
} }
debug("net123_open OK"); debug("net123_open OK");
return ret; return handle;
cleanup: cleanup:
debug("net123_open error"); debug("net123_open error");
if (urlW) free(urlW); if (urlW) free(urlW);
net123_close(ret); net123_close(handle);
ret = NULL; handle = NULL;
return ret; return handle;
} }
// Read data into buffer, return bytes read. // Read data into buffer, return bytes read.
@@ -221,20 +232,21 @@ cleanup:
// For the user, it only matters if there will be more bytes or not. // For the user, it only matters if there will be more bytes or not.
// Feel free to communicate errors via error() / merror() functions inside. // Feel free to communicate errors via error() / merror() functions inside.
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){ size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
winhttp_handle *h = nh->parts;
size_t ret; size_t ret;
size_t to_copy = nh->headers_len - nh->headers_pos; size_t to_copy = h->headers_len - h->headers_pos;
DWORD bytesread = 0; DWORD bytesread = 0;
if(to_copy){ if(to_copy){
ret = to_copy <= bufsize ? to_copy : bufsize; ret = to_copy <= bufsize ? to_copy : bufsize;
memcpy(buf, nh->headers + nh->headers_pos, ret); memcpy(buf, h->headers + h->headers_pos, ret);
nh->headers_pos += ret; h->headers_pos += ret;
return ret; return ret;
} }
/* is this needed? */ /* is this needed? */
to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize; to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize;
if(!WinHttpReadData(nh->request, buf, to_copy, &bytesread)){ if(!WinHttpReadData(h->request, buf, to_copy, &bytesread)){
return EOF; return EOF;
} }
return bytesread; return bytesread;
@@ -242,21 +254,26 @@ size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
// Call that to free up resources, end processes. // Call that to free up resources, end processes.
void net123_close(net123_handle *nh){ void net123_close(net123_handle *nh){
if(nh->headers) { if (!nh) return;
free(nh->headers); winhttp_handle *h = nh->parts;
nh->headers = NULL; if(h) {
} if(h->headers) {
if(nh->request) { free(h->headers);
WinHttpCloseHandle(nh->request); h->headers = NULL;
nh->request = NULL; }
} if(h->request) {
if(nh->connect) { WinHttpCloseHandle(h->request);
WinHttpCloseHandle(nh->connect); h->request = NULL;
nh->connect = NULL; }
} if(h->connect) {
if(nh->session) { WinHttpCloseHandle(h->connect);
WinHttpCloseHandle(nh->session); h->connect = NULL;
nh->session = NULL; }
if(h->session) {
WinHttpCloseHandle(h->session);
h->session = NULL;
}
free(h);
} }
free(nh); free(nh);
} }

View File

@@ -5,13 +5,11 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <wininet.h> #include <wininet.h>
const char *net123_backends[] = { "(always wininet)", NULL };
// The network implementation defines the struct for private use. // The network implementation defines the struct for private use.
// The purpose is just to keep enough context to be able to // The purpose is just to keep enough context to be able to
// call net123_read() and net123_close() afterwards. // call net123_read() and net123_close() afterwards.
#define URL_COMPONENTS_LENGTH 255 #define URL_COMPONENTS_LENGTH 255
struct net123_handle_struct { typedef struct {
HINTERNET session; HINTERNET session;
HINTERNET connect; HINTERNET connect;
HINTERNET request; HINTERNET request;
@@ -28,7 +26,7 @@ struct net123_handle_struct {
DWORD HttpQueryInfoIndex; DWORD HttpQueryInfoIndex;
DWORD internetStatus, internetStatusLength; DWORD internetStatus, internetStatusLength;
LPVOID additionalInfo; LPVOID additionalInfo;
}; } wininet_handle;
#define MPG123CONCAT_(x,y) x ## y #define MPG123CONCAT_(x,y) x ## y
#define MPG123CONCAT(x,y) MPG123CONCAT_(x,y) #define MPG123CONCAT(x,y) MPG123CONCAT_(x,y)
@@ -60,13 +58,17 @@ static void debug_crack(URL_COMPONENTSW *comps){}
static static
void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){ void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){
net123_handle *nh = (net123_handle *)dwContext; debug("In net123_ssl_errors");
wininet_handle *nh = (wininet_handle *)(dwContext);
nh->internetStatus = dwInternetStatus; nh->internetStatus = dwInternetStatus;
nh->additionalInfo = lpvStatusInformation; nh->additionalInfo = lpvStatusInformation;
nh->internetStatusLength = dwStatusInformationLength; nh->internetStatusLength = dwStatusInformationLength;
} }
net123_handle *net123_open(const char *url, const char * const *client_head){ static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize);
static void net123_close(net123_handle *nh);
net123_handle *net123_open_wininet(const char *url, const char * const *client_head){
LPWSTR urlW = NULL, headers = NULL; LPWSTR urlW = NULL, headers = NULL;
size_t ii; size_t ii;
WINBOOL res; WINBOOL res;
@@ -78,51 +80,63 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
if(urlW == NULL) goto cleanup; if(urlW == NULL) goto cleanup;
net123_handle *ret = calloc(1, sizeof(net123_handle)); net123_handle *ret = calloc(1, sizeof(net123_handle));
if (!ret) return ret; wininet_handle *wh = calloc(1, sizeof(wininet_handle));
if(!ret || !wh)
{
if(ret)
free(ret);
if(wh)
free(wh);
return NULL;
}
ret->comps.dwStructSize = sizeof(ret->comps); ret->parts = wh;
ret->comps.dwSchemeLength = URL_COMPONENTS_LENGTH - 1; ret->read = net123_read;
ret->comps.dwUserNameLength = URL_COMPONENTS_LENGTH - 1; ret->close = net123_close;
ret->comps.dwPasswordLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwHostNameLength = URL_COMPONENTS_LENGTH - 1; wh->comps.dwStructSize = sizeof(wh->comps);
ret->comps.dwUrlPathLength = URL_COMPONENTS_LENGTH - 1; wh->comps.dwSchemeLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwExtraInfoLength = URL_COMPONENTS_LENGTH - 1; wh->comps.dwUserNameLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszHostName = ret->lpszHostName; wh->comps.dwPasswordLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszUserName = ret->lpszUserName; wh->comps.dwHostNameLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszPassword = ret->lpszPassword; wh->comps.dwUrlPathLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszUrlPath = ret->lpszUrlPath; wh->comps.dwExtraInfoLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszExtraInfo = ret->lpszExtraInfo; wh->comps.lpszHostName = wh->lpszHostName;
ret->comps.lpszScheme = ret->lpszScheme; wh->comps.lpszUserName = wh->lpszUserName;
wh->comps.lpszPassword = wh->lpszPassword;
wh->comps.lpszUrlPath = wh->lpszUrlPath;
wh->comps.lpszExtraInfo = wh->lpszExtraInfo;
wh->comps.lpszScheme = wh->lpszScheme;
debug1("net123_open start crack %S", urlW); debug1("net123_open start crack %S", urlW);
if(!(res = InternetCrackUrlW(urlW, 0, 0, &ret->comps))) { if(!(res = InternetCrackUrlW(urlW, 0, 0, &wh->comps))) {
debug1("net123_open crack fail %lu", GetLastError()); debug1("net123_open crack fail %lu", GetLastError());
goto cleanup; goto cleanup;
} }
debug("net123_open crack OK"); debug("net123_open crack OK");
debug_crack(&ret->comps); debug_crack(&wh->comps);
ret->session = InternetOpenW(useragent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); wh->session = InternetOpenW(useragent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
free(urlW); free(urlW);
urlW = NULL; urlW = NULL;
debug("net123_open InternetOpenW OK"); debug("net123_open InternetOpenW OK");
if(!ret->session) goto cleanup; if(!wh->session) goto cleanup;
debug2("net123_open InternetConnectW %S %u", ret->comps.lpszHostName, ret->comps.nPort); debug2("net123_open InternetConnectW %S %u", wh->comps.lpszHostName, wh->comps.nPort);
ret->connect = InternetConnectW(ret->session, ret->comps.lpszHostName, ret->comps.nPort, wh->connect = InternetConnectW(wh->session, wh->comps.lpszHostName, wh->comps.nPort,
ret->comps.dwUserNameLength ? ret->comps.lpszUserName : NULL, ret->comps.dwPasswordLength ? ret->comps.lpszPassword : NULL, wh->comps.dwUserNameLength ? wh->comps.lpszUserName : NULL, wh->comps.dwPasswordLength ? wh->comps.lpszPassword : NULL,
INTERNET_SERVICE_HTTP, 0, 0); INTERNET_SERVICE_HTTP, 0, 0);
if(!ret->connect) goto cleanup; if(!wh->connect) goto cleanup;
debug("net123_open InternetConnectW OK"); debug("net123_open InternetConnectW OK");
debug1("HttpOpenRequestW GET %S", ret->comps.lpszUrlPath); debug1("HttpOpenRequestW GET %S", wh->comps.lpszUrlPath);
ret->request = HttpOpenRequestW(ret->connect, L"GET", ret->comps.lpszUrlPath, NULL, NULL, NULL, ret->comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)ret); wh->request = HttpOpenRequestW(wh->connect, L"GET", wh->comps.lpszUrlPath, NULL, NULL, NULL, wh->comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)wh);
if(!ret->request) goto cleanup; if(!wh->request) goto cleanup;
debug("HttpOpenRequestW GET OK"); debug("HttpOpenRequestW GET OK");
cb = InternetSetStatusCallback(ret->request, (INTERNET_STATUS_CALLBACK)net123_ssl_errors); cb = InternetSetStatusCallback(wh->request, (INTERNET_STATUS_CALLBACK)net123_ssl_errors);
if(cb != NULL){ if(cb != NULL){
error1("InternetSetStatusCallback failed to install callback, errors might not be reported properly! (%lu)", GetLastError()); error1("InternetSetStatusCallback failed to install callback, errors might not be reported properly! (%lu)", GetLastError());
} }
@@ -132,7 +146,7 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
if(!headers) if(!headers)
goto cleanup; goto cleanup;
debug1("HttpAddRequestHeadersW add %S", headers); debug1("HttpAddRequestHeadersW add %S", headers);
res = HttpAddRequestHeadersW(ret->request, headers, (DWORD) -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); res = HttpAddRequestHeadersW(wh->request, headers, (DWORD) -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
debug2("HttpAddRequestHeadersW returns %u %lu", res, res ? 0 : GetLastError()); debug2("HttpAddRequestHeadersW returns %u %lu", res, res ? 0 : GetLastError());
free(headers); free(headers);
headers = NULL; headers = NULL;
@@ -140,13 +154,14 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
debug("net123_open ADD HEADERS OK"); debug("net123_open ADD HEADERS OK");
res = HttpSendRequestW(ret->request, NULL, 0, NULL, 0); res = HttpSendRequestW(wh->request, NULL, 0, NULL, 0);
if (!res) { if (!res) {
res = GetLastError(); res = GetLastError();
error1("HttpSendRequestW failed with %lu", res); error1("HttpSendRequestW failed with %lu", res);
goto cleanup; goto cleanup;
} }
debug("HttpSendRequestW OK");
// dummy, cannot be null // dummy, cannot be null
headers = calloc(1,1); headers = calloc(1,1);
@@ -155,18 +170,20 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
error("Cannot allocate dummy buffer for HttpQueryInfoW"); error("Cannot allocate dummy buffer for HttpQueryInfoW");
goto cleanup; goto cleanup;
} }
res = HttpQueryInfoW(ret->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &ret->HttpQueryInfoIndex); debug("Try HttpQueryInfoW pass 1");
res = HttpQueryInfoW(wh->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &wh->HttpQueryInfoIndex);
free(headers); free(headers);
debug("HttpQueryInfoW pass 1 OK");
if(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER && headerlen > 0) { if(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER && headerlen > 0) {
/* buffer size is in bytes, not including terminator */ /* buffer size is in bytes, not including terminator */
headers = calloc(1, headerlen + sizeof(*headers)); headers = calloc(1, headerlen + sizeof(*headers));
if (!headers) goto cleanup; if (!headers) goto cleanup;
res = HttpQueryInfoW(ret->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &ret->HttpQueryInfoIndex); res = HttpQueryInfoW(wh->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &wh->HttpQueryInfoIndex);
debug3("HttpQueryInfoW returned %u, err %u : %S", res, GetLastError(), headers ? headers : L"null"); debug3("HttpQueryInfoW returned %u, err %u : %S", res, GetLastError(), headers ? headers : L"null");
win32_wide_utf7(headers, &ret->headers, &ret->headers_len); win32_wide_utf7(headers, &wh->headers, &wh->headers_len);
/* bytes written, skip the terminating null, we want to stop at the \r\n\r\n */ /* bytes written, skip the terminating null, we want to stop at the \r\n\r\n */
ret->headers_len --; wh->headers_len --;
free(headers); free(headers);
headers = NULL; headers = NULL;
} else { } else {
@@ -184,21 +201,24 @@ cleanup:
return ret; return ret;
} }
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){ static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
if(!nh || !nh->parts)
return 0;
wininet_handle *wh = nh->parts;
size_t ret; size_t ret;
size_t to_copy = nh->headers_len - nh->headers_pos; size_t to_copy = wh->headers_len - wh->headers_pos;
DWORD bytesread = 0; DWORD bytesread = 0;
if(to_copy){ if(to_copy){
ret = to_copy <= bufsize ? to_copy : bufsize; ret = to_copy <= bufsize ? to_copy : bufsize;
memcpy(buf, nh->headers + nh->headers_pos, ret); memcpy(buf, wh->headers + wh->headers_pos, ret);
nh->headers_pos += ret; wh->headers_pos += ret;
return ret; return ret;
} }
/* is this needed? */ /* is this needed? */
to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize; to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize;
if(!InternetReadFile(nh->request, buf, to_copy, &bytesread)){ if(!InternetReadFile(wh->request, buf, to_copy, &bytesread)){
error1("InternetReadFile exited with %d", GetLastError()); error1("InternetReadFile exited with %d", GetLastError());
return EOF; return EOF;
} }
@@ -206,22 +226,28 @@ size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
} }
// Call that to free up resources, end processes. // Call that to free up resources, end processes.
void net123_close(net123_handle *nh){ static void net123_close(net123_handle *nh){
if(nh->headers) { if(!nh)
free(nh->headers); return;
nh->headers = NULL; wininet_handle *wh = nh->parts;
if(!wh) /*???*/
return;
if(wh->headers) {
free(wh->headers);
wh->headers = NULL;
} }
if(nh->request) { if(wh->request) {
InternetCloseHandle(nh->request); InternetCloseHandle(wh->request);
nh->request = NULL; wh->request = NULL;
} }
if(nh->connect) { if(wh->connect) {
InternetCloseHandle(nh->connect); InternetCloseHandle(wh->connect);
nh->connect = NULL; wh->connect = NULL;
} }
if(nh->session) { if(wh->session) {
InternetCloseHandle(nh->session); InternetCloseHandle(wh->session);
nh->session = NULL; wh->session = NULL;
} }
free(nh); free(nh);
free(wh);
} }

View File

@@ -357,7 +357,8 @@ static int add_next_file (int argc, char *argv[], int args_utf8)
} }
} }
pl.entry = 0; pl.entry = 0;
if(pl.file && pl.file->network) #ifdef NET123
if(pl.file && pl.file->nh)
{ {
debug1("htd.content_type.p: %p", (void*) pl.file->htd.content_type.p); debug1("htd.content_type.p: %p", (void*) pl.file->htd.content_type.p);
if(!APPFLAG(MPG123APP_IGNORE_MIME) && pl.file->htd.content_type.p != NULL) if(!APPFLAG(MPG123APP_IGNORE_MIME) && pl.file->htd.content_type.p != NULL)
@@ -402,6 +403,7 @@ static int add_next_file (int argc, char *argv[], int args_utf8)
} }
} }
} }
#endif
if(!pl.file) if(!pl.file)
{ {
param.listname = NULL; // why? param.listname = NULL; // why?

View File

@@ -4,11 +4,15 @@
This evolved into the generic I/O interposer for direct file or http stream This evolved into the generic I/O interposer for direct file or http stream
access, with explicit buffering for getline. access, with explicit buffering for getline.
copyright 2010-2019 by the mpg123 project - free software under the terms of the LGPL 2.1 copyright 2010-2022 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
*/ */
// setenv
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "streamdump.h" #include "streamdump.h"
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
@@ -22,6 +26,139 @@
#define HTTP_MAX_RELOCATIONS 20 #define HTTP_MAX_RELOCATIONS 20
#endif #endif
#if defined(NETWORK) && !defined(NET123)
#error "NETWORK only with NET123 from now on!"
#endif
#if !defined(NETWORK) && defined(NET123)
#error "NET123 only with NETWORK from now on!"
#endif
#ifdef NETWORK
const char *net123_backends[] =
{
"internal"
#ifdef NET123_EXEC
, "wget"
, "curl"
#endif
#ifdef NET123_WINHTTP
, "winhttp"
#endif
#ifdef NET123_WININET
, "wininet"
#endif
, NULL
};
// Net123 variant for our internal code that has safer legacy support for HTTP shoutcast.
static size_t net123_read_internal( struct net123_handle_struct *nh,
void *buf, size_t bufsize )
{
if(!nh)
return 0;
int *fdp = nh->parts;
#ifdef WANT_WIN32_SOCKETS
return win32_net_read(*fdp, buf, bufsize);
#else
return unintr_read(*fdp, buf, bufsize);
#endif
}
static void net123_close_internal(struct net123_handle_struct *nh)
{
if(nh)
return;
int *fdp = nh->parts;
#ifdef WANT_WIN32_SOCKETS
if(*fdp != SOCKET_ERROR)
win32_net_close(*fdp);
#else
close(*fdp);
#endif
free(fdp);
free(nh);
}
static net123_handle *net123_open_internal( const char *url
, const char * const *client_head, struct httpdata *hd )
{
net123_handle *nh = malloc(sizeof(net123_handle));
if(!nh)
return NULL;
int *fdp = malloc(sizeof(int));
if(!fdp)
{
free(nh);
return NULL;
}
nh->parts = fdp;
nh->read = net123_read_internal;
nh->close = net123_close_internal;
// Handles win32_net internally.
*fdp = http_open(url, hd, client_head);
if(*fdp >= 0)
return nh;
free(fdp);
free(nh);
return NULL;
}
// Decide which backend to load.
static net123_handle *net123_open( const char *url
, const char * const *client_head, struct httpdata *hd )
{
int autochoose = !strcmp("auto", param.network_backend);
int https = !strncasecmp("https://", url, 8);
if(param.proxyurl)
{
if(strcmp(param.proxyurl, "none"))
{
#ifdef HAVE_SETENV
setenv("http_proxy", param.proxyurl, 1);
setenv("HTTP_PROXY", param.proxyurl, 1);
setenv("https_proxy", param.proxyurl, 1);
setenv("HTTPS_PROXY", param.proxyurl, 1);
#endif
} else
{
#ifdef HAVE_UNSETENV
unsetenv("http_proxy");
unsetenv("HTTP_PROXY");
unsetenv("https_proxy");
unsetenv("HTTPS_PROXY");
#endif
}
}
if( (autochoose && !https)
|| !strcmp("internal", param.network_backend) )
{
if(https && !param.quiet)
fprintf(stderr, "Note: HTTPS will fail with internal network code.\n");
return net123_open_internal(url, client_head, hd);
}
#ifdef NET123_EXEC
if( autochoose
|| !strcmp("wget", param.network_backend)
|| !strcmp("curl", param.network_backend) )
return net123_open_exec(url, client_head);
#endif
#ifdef NET123_WINHTTP
if(autochoose || !strcmp("winhttp", param.network_backend))
return net123_open_winhttp(url, client_head);
#endif
#ifdef NET123_WININET
if(autochoose || !strcmp("wininet", param.network_backend))
return net123_open_wininet(url, client_head);
#endif
merror("no network backend for %s", https ? "HTTPS" : "HTTP");
return NULL;
}
#endif
/* Stream dump descriptor. */ /* Stream dump descriptor. */
static int dump_fd = -1; static int dump_fd = -1;
@@ -31,13 +168,9 @@ static int dump_fd = -1;
static ssize_t stream_read_raw(struct stream *sd, void *buf, size_t count) static ssize_t stream_read_raw(struct stream *sd, void *buf, size_t count)
{ {
ssize_t ret = -1; ssize_t ret = -1;
#ifdef NET123 #ifdef NETWORK
if(sd->nh) if(sd->nh)
ret = net123_read(sd->nh, buf, count); ret = (ssize_t) sd->nh->read(sd->nh, buf, count);
#endif
#ifdef WANT_WIN32_SOCKETS
if(sd->fd >= 0 && sd->network)
ret = win32_net_read(sd->fd, buf, count);
#endif #endif
if(sd->fd >= 0) // plain file or network socket if(sd->fd >= 0) // plain file or network socket
ret = (ssize_t) unintr_read(sd->fd, buf, count); ret = (ssize_t) unintr_read(sd->fd, buf, count);
@@ -79,8 +212,12 @@ static ssize_t stream_read(struct stream *sd, void *buf, size_t count)
static off_t stream_seek(struct stream *sd, off_t pos, int whence) static off_t stream_seek(struct stream *sd, off_t pos, int whence)
{ {
if(!sd || sd->network) if(!sd)
return -1; return -1;
#ifdef NET123
if(sd->nh)
return -1;
#endif
return lseek(sd->fd, pos, whence); return lseek(sd->fd, pos, whence);
} }
@@ -151,6 +288,7 @@ ssize_t stream_getline(struct stream *sd, mpg123_string *line)
} }
} }
#ifdef NETWORK
// Return 0 on success, non-zero when there is an error or more work to do. // Return 0 on success, non-zero when there is an error or more work to do.
// -1: error, 1: redirection to given location // -1: error, 1: redirection to given location
static int stream_parse_headers(struct stream *sd, mpg123_string *location) static int stream_parse_headers(struct stream *sd, mpg123_string *location)
@@ -278,11 +416,84 @@ static int stream_parse_headers(struct stream *sd, mpg123_string *location)
return ret; return ret;
} }
// resolve relative locations given the initial full URL
// full URL ensured to either start with http:// or https://
// (case-insensitive), location non-empty
static void relocate_url(mpg123_string *location, const char *url)
{
if(!strncasecmp(location->p, "http://", 7) || !strncasecmp(location->p, "https://", 8))
return;
if(!url || (strncasecmp(url, "http://", 7) && strncasecmp(url, "https://", 8)))
{
mpg123_resize_string(location, 0);
return;
}
if(!param.quiet)
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
mpg123_string purl;
mpg123_string workbuf;
mpg123_init_string(&purl);
mpg123_init_string(&workbuf);
if(mpg123_set_string(&purl, url) && mpg123_move_string(location, &workbuf))
{
debug1("relocate request_url: %s", purl.p);
// location somewhat relative, either /some/path or even just some/path
char* ptmp = NULL;
// Though it's not RFC (?), accept relative URIs as wget does.
if(workbuf.p[0] == '/')
{
// server-absolute only prepend http://server/
// I null the first / after http:// or https://
size_t off = (purl.p[4] == 's') ? 8 : 7;
ptmp = strchr(purl.p+off,'/');
if(ptmp != NULL)
{
purl.fill = ptmp-purl.p+1;
purl.p[purl.fill-1] = 0;
}
}
else
{
// relative to current directory
// prepend http://server/path/
// first cutting off parameter stuff from URL
for(size_t i=0; i<purl.fill; ++i)
{
if(purl.p[i] == '?' || purl.p[i] == '#')
{
purl.p[i] = 0;
purl.fill = i+1;
break;
}
}
// now the last slash, keeping it
ptmp = strrchr(purl.p, '/');
if(ptmp != NULL)
{
purl.fill = ptmp-purl.p+2;
purl.p[purl.fill-1] = 0;
}
}
// only the prefix left
debug1("prefix=%s", purl.p);
mpg123_add_string(location, purl.p);
}
mpg123_add_string(location, workbuf.p);
mpg123_free_string(&workbuf);
mpg123_free_string(&purl);
}
#endif
static void stream_init(struct stream *sd) static void stream_init(struct stream *sd)
{ {
sd->bufp = sd->buf; sd->bufp = sd->buf;
sd->fill = 0; sd->fill = 0;
sd->network = 0;
sd->fd = -1; sd->fd = -1;
#ifdef NET123 #ifdef NET123
sd->nh = NULL; sd->nh = NULL;
@@ -296,15 +507,8 @@ static void stream_reset(struct stream *sd)
{ {
#ifdef NET123 #ifdef NET123
if(sd->nh) if(sd->nh)
net123_close(sd->nh); sd->nh->close(sd->nh);
sd->nh = NULL; sd->nh = NULL;
#endif
#ifdef WANT_WIN32_SOCKETS
if(sd->fd >= 0 && sd->network)
{
if(sd->fd != SOCKET_ERROR)
win32_net_close(sd->fd);
}
#endif #endif
if(sd->fd >= 0) // plain file or network socket if(sd->fd >= 0) // plain file or network socket
close(sd->fd); close(sd->fd);
@@ -327,10 +531,9 @@ struct stream *stream_open(const char *url)
sd->fd = STDIN_FILENO; sd->fd = STDIN_FILENO;
compat_binmode(STDIN_FILENO, TRUE); compat_binmode(STDIN_FILENO, TRUE);
} }
#ifdef NET123 #ifdef NETWORK
else if(!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8)) else if(!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8))
{ {
sd->network = 1;
// Network stream with header parsing. // Network stream with header parsing.
const char *client_head[] = { NULL, NULL, NULL }; const char *client_head[] = { NULL, NULL, NULL };
client_head[0] = param.talk_icy ? icy_yes : icy_no; client_head[0] = param.talk_icy ? icy_yes : icy_no;
@@ -339,15 +542,18 @@ struct stream *stream_open(const char *url)
append_accept(&accept); append_accept(&accept);
client_head[1] = accept.p; client_head[1] = accept.p;
mpg123_string location; mpg123_string location;
mpg123_string urlcopy;
mpg123_init_string(&location); mpg123_init_string(&location);
mpg123_init_string(&urlcopy);
int numrelocs = 0; int numrelocs = 0;
while(sd) while(sd)
{ {
sd->nh = net123_open(url, client_head); sd->nh = net123_open(url, client_head, &sd->htd);
if(!sd->nh) if(!sd->nh)
{ {
stream_close(sd); stream_close(sd);
sd = NULL; sd = NULL;
break;
} }
location.fill = 0; location.fill = 0;
if(stream_parse_headers(sd, &location)) if(stream_parse_headers(sd, &location))
@@ -358,35 +564,29 @@ struct stream *stream_open(const char *url)
if(++numrelocs > HTTP_MAX_RELOCATIONS) if(++numrelocs > HTTP_MAX_RELOCATIONS)
{ {
merror("too many HTTP redirections: %i", numrelocs); merror("too many HTTP redirections: %i", numrelocs);
stream_close(sd); url = NULL;
sd = NULL;
} else } else
url = location.p; // Used one time, then possibly overwritten. {
relocate_url(&location, url); // resolve relative locations
mpg123_copy_string(&location, &urlcopy);
url = urlcopy.p;
}
} else } else
{
url = NULL;
}
if(!url)
{ {
stream_close(sd); stream_close(sd);
sd = NULL; sd = NULL;
} }
} else break; // Successful end. } else break; // Successful end.
} }
mpg123_free_string(&urlcopy);
mpg123_free_string(&location); mpg123_free_string(&location);
mpg123_free_string(&accept); mpg123_free_string(&accept);
// Either sd is NULL or we got a stream ready. // Either sd is NULL or we got a stream ready.
} }
#elif defined(NETWORK)
else if(!strncasecmp("http://", url, 7))
{
#ifdef WANT_WIN32_SOCKETS
sd->fd = win32_net_http_open(url, &sd->htd);
#else
sd->fd = http_open(url, &sd->htd);
#endif
if(sd->fd < 0)
{
stream_close(sd);
return NULL;
}
}
#endif #endif
else else
{ {
@@ -482,12 +682,7 @@ int dump_setup(struct stream *sd, mpg123_handle *mh)
ret = mpg123_open_handle(mh, sd); ret = mpg123_open_handle(mh, sd);
} else } else
{ {
#ifdef WANT_WIN32_SOCKETS mpg123_replace_reader(mh, NULL, NULL);
if(sd->network)
win32_net_replace(mh);
else // ensure libmpg123 is using its own reader otherwise
#endif
mpg123_replace_reader(mh, NULL, NULL);
ret = mpg123_open_fd(mh, sd->fd); ret = mpg123_open_fd(mh, sd->fd);
} }
if(ret != MPG123_OK) if(ret != MPG123_OK)

View File

@@ -18,6 +18,9 @@
#ifdef NET123 #ifdef NET123
#include "net123.h" #include "net123.h"
#endif #endif
#ifdef NETWORK
#include "httpget.h"
#endif
// The stream is either addressed via file descriptor or net123 handle. // The stream is either addressed via file descriptor or net123 handle.
struct stream struct stream
@@ -25,7 +28,6 @@ struct stream
char buf[256]; // buffer for getline char buf[256]; // buffer for getline
char *bufp; // read pointer in buffer char *bufp; // read pointer in buffer
int fill; // bytes in buffer int fill; // bytes in buffer
int network; // flag to mark network streams (with httpdata)
int fd; // if > 0: plain file descriptor or win32 net socket int fd; // if > 0: plain file descriptor or win32 net socket
struct httpdata htd; struct httpdata htd;
#ifdef NET123 #ifdef NET123

View File

@@ -232,8 +232,9 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
case MPG123_QUIT_KEY: case MPG123_QUIT_KEY:
debug("QUIT"); debug("QUIT");
if(stopped) if(stopped)
{ if(param.verbose) {
print_stat(fr,0,ao,0,&param); if(param.verbose)
print_stat(fr,0,ao,0,&param);
stopped = 0; stopped = 0;
out123_pause(ao); /* no chance for annoying underrun warnings */ out123_pause(ao); /* no chance for annoying underrun warnings */

View File

@@ -8,6 +8,10 @@
initially written by Thomas Orgis initially written by Thomas Orgis
*/ */
// ctermid
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "config.h" #include "config.h"
#ifdef __OS2__ #ifdef __OS2__

View File

@@ -1,14 +1,7 @@
#include "config.h" #include "win32_support.h"
#include "mpg123.h"
#include "mpg123app.h" #include "mpg123app.h"
#include "httpget.h"
#include "resolver.h"
#include "compat.h"
#include <errno.h>
#include "debug.h" #include "debug.h"
#if defined (WANT_WIN32_SOCKETS)
#ifdef DEBUG #ifdef DEBUG
#define msgme(x) win32_net_msg(x,__FILE__,__LINE__) #define msgme(x) win32_net_msg(x,__FILE__,__LINE__)
#define msgme1 win32_net_msg(1,__FILE__,__LINE__) #define msgme1 win32_net_msg(1,__FILE__,__LINE__)
@@ -115,28 +108,6 @@ static int get_sock_ch (int sock)
return (((int) c)&0xff); return (((int) c)&0xff);
return -1; return -1;
} }
/* Addapted from from newlib*/
char *win32_net_fgets(char *s, int n, int stream)
{
char c = 0;
char *buf;
buf = s;
debug1("Pseudo net fgets attempts to read %d bytes from network.", n - 1);
while (--n > 0 && (c = get_sock_ch (stream)) != -1)
{
*s++ = c;
if (c == '\n' || c == '\r')
break;
}
debug1("Pseudo net fgets got %"SIZE_P" bytes.", (size_p)(s - buf));
if (c == -1 && s == buf)
{
debug("Pseudo net fgets met a premature end.");
return NULL;
}
*s = 0;
return buf;
}
ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte) ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte)
{ {
@@ -148,18 +119,6 @@ ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte)
return ret; return ret;
} }
off_t win32_net_lseek (int a, off_t b, int c)
{
debug("lseek on a socket called!");
return -1;
}
void win32_net_replace (mpg123_handle *fr)
{
debug("win32_net_replace ran");
mpg123_replace_reader(fr, win32_net_read, win32_net_lseek);
}
static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{ {
debug("win32_net_timeout_connect ran"); debug("win32_net_timeout_connect ran");
@@ -239,7 +198,7 @@ static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_add
} }
} }
static int win32_net_open_connection(mpg123_string *host, mpg123_string *port) int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
{ {
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *addr, *addrlist; struct addrinfo *addr, *addrlist;
@@ -286,52 +245,10 @@ static int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
} }
freeaddrinfo(addrlist); freeaddrinfo(addrlist);
return 1; return ws.local_socket == SOCKET_ERROR ? -1 : 1;
} }
static size_t win32_net_readstring (mpg123_string *string, size_t maxlen, int fd) int win32_net_writestring (int fd, mpg123_string *string)
{
debug2("Attempting readstring on %d for %"SIZE_P" bytes", fd, (size_p)maxlen);
int err;
string->fill = 0;
while(maxlen == 0 || string->fill < maxlen)
{
if(string->size-string->fill < 1)
if(!mpg123_grow_string(string, string->fill+4096))
{
error("Cannot allocate memory for reading.");
string->fill = 0;
return 0;
}
err = win32_net_read(0,string->p+string->fill,1); /*fd is ignored */
/* Whoa... reading one byte at a time... one could ensure the line break in another way, but more work. */
if( err == 1)
{
string->fill++;
if(string->p[string->fill-1] == '\n') break;
}
else if(errno != EINTR)
{
error("Error reading from socket or unexpected EOF.");
string->fill = 0;
/* bail out to prevent endless loop */
return 0;
}
}
if(!mpg123_grow_string(string, string->fill+1))
{
string->fill=0;
}
else
{
string->p[string->fill] = 0;
string->fill++;
}
return string->fill;
}
static int win32_net_writestring (int fd, mpg123_string *string)
{ {
size_t result, bytes; size_t result, bytes;
char *ptr = string->p; char *ptr = string->p;
@@ -355,253 +272,3 @@ static int win32_net_writestring (int fd, mpg123_string *string)
} }
return TRUE; return TRUE;
} }
static int win32_net_resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
{
debug1("request_url:%s", request_url->p);
/* initialized with full old url */
if(!mpg123_copy_string(request_url, purl)) return FALSE;
/* We may strip it down to a prefix ot totally. */
if(strncasecmp(response->p, "Location: http://", 17))
{ /* OK, only partial strip, need prefix for relative path. */
char* ptmp = NULL;
/* though it's not RFC (?), accept relative URIs as wget does */
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
/* not absolute uri, could still be server-absolute */
/* I prepend a part of the request... out of the request */
if(response->p[10] == '/')
{
/* only prepend http://server/ */
/* I null the first / after http:// */
ptmp = strchr(purl->p+7,'/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
}
else
{
/* prepend http://server/path/ */
/* now we want the last / */
ptmp = strrchr(purl->p+7, '/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
}
}
else purl->fill = 0;
debug1("prefix=%s", purl->fill ? purl->p : "");
if(!mpg123_add_string(purl, response->p+10)) return FALSE;
debug1(" purl: %s", purl->p);
debug1("old request_url: %s", request_url->p);
return TRUE;
}
int win32_net_http_open(const char* url, struct httpdata *hd)
{
mpg123_string purl, host, port, path;
mpg123_string request, response, request_url;
mpg123_string httpauth1;
ws.local_socket = SOCKET_ERROR;
int oom = 0;
int relocate, numrelocs = 0;
int got_location = FALSE;
/*
workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
this site's apache gives me a relocation to the same place when I give the port in Host request field
for the record: Apache/2.0.51 (Fedora)
*/
int try_without_port = 0;
mpg123_init_string(&purl);
mpg123_init_string(&host);
mpg123_init_string(&port);
mpg123_init_string(&path);
mpg123_init_string(&request);
mpg123_init_string(&response);
mpg123_init_string(&request_url);
mpg123_init_string(&httpauth1);
/* Get initial info for proxy server. Once. */
if(hd->proxystate == PROXY_UNKNOWN && !proxy_init(hd)) goto exit;
if(!translate_url(url, &purl)){ oom=1; goto exit; }
/* Don't confuse the different auth strings... */
if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }
/* "GET http://" 11
* " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
* 26 + PACKAGE_NAME + PACKAGE_VERSION
* accept header + accept_length()
* "Authorization: Basic \r\n" 23
* "\r\n" 2
* ... plus the other predefined header lines
*/
/* Just use this estimate as first guess to reduce malloc calls in string library. */
{
size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION)
+ accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
if( !mpg123_grow_string(&request, length_estimate)
|| !mpg123_grow_string(&response,4096) )
{
oom=1; goto exit;
}
}
do
{
/* Storing the request url, with http:// prepended if needed. */
/* used to be url here... seemed wrong to me (when loop advanced...) */
if(strncasecmp(purl.p, "http://", 7) != 0) mpg123_set_string(&request_url, "http://");
else mpg123_set_string(&request_url, "");
mpg123_add_string(&request_url, purl.p);
if(!split_url(&purl, NULL, &host, &port, &path)){ oom=1; goto exit; }
if (hd->proxystate >= PROXY_HOST)
{
/* We will connect to proxy, full URL goes into the request. */
if( !mpg123_set_string(&request, "GET ")
|| !mpg123_add_string(&request, request_url.p) )
{
oom=1; goto exit;
}
}
else
{
/* We will connect to the host from the URL and only the path goes into the request. */
if( !mpg123_set_string(&request, "GET ")
|| !mpg123_add_string(&request, path.p) )
{
oom=1; goto exit;
}
}
if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; }
httpauth1.fill = 0; /* We use the auth data from the URL only once. */
if (hd->proxystate >= PROXY_HOST)
{
if( !mpg123_copy_string(&hd->proxyhost, &host)
|| !mpg123_copy_string(&hd->proxyport, &port) )
{
oom=1; goto exit;
}
}
debug2("attempting to open_connection to %s:%s", host.p, port.p);
win32_net_open_connection(&host, &port);
if(ws.local_socket == SOCKET_ERROR)
{
error1("Unable to establish connection to %s", host.fill ? host.p : "");
goto exit;
}
debug("win32_net_open_connection succeed");
#define http_failure win32_net_close(ws.local_socket); ws.local_socket=SOCKET_ERROR; goto exit;
if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
if(!win32_net_writestring(ws.local_socket, &request)){ http_failure; }
debug("Skipping fdopen for WSA sockets");
relocate = FALSE;
/* Arbitrary length limit here... */
#define safe_readstring \
win32_net_readstring(&response, SIZE_MAX/16, -1); \
if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
{ \
error("HTTP response line exceeds max. length"); \
http_failure; \
} \
else if(response.fill == 0) \
{ \
error("readstring failed"); \
http_failure; \
} \
if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
safe_readstring;
{
char *sptr;
if((sptr = strchr(response.p, ' ')))
{
if(response.fill > sptr-response.p+2)
switch (sptr[1])
{
case '3':
relocate = TRUE;
case '2':
break;
default:
fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
http_failure;
}
else{ error("Too short response,"); http_failure; }
}
}
/* If we are relocated, we need to look out for a Location header. */
got_location = FALSE;
do
{
safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
if (!strncasecmp(response.p, "Location: ", 10))
{ /* It is a redirection! */
if(!win32_net_resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }
if(!strcmp(purl.p, request_url.p))
{
warning("relocated to very same place! trying request again without host port");
try_without_port = 1;
}
got_location = TRUE;
}
else
{ /* We got a header line (or the closing empty line). */
char *tmp;
debug1("searching for header values... %s", response.p);
/* Not sure if I want to bail out on error here. */
/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
get_header_string(&response, "content-type", &hd->content_type);
get_header_string(&response, "icy-name", &hd->icy_name);
get_header_string(&response, "icy-url", &hd->icy_url);
/* watch out for icy-metaint */
if((tmp = get_header_val("icy-metaint", &response)))
{
hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
debug1("got icy-metaint %li", (long int)hd->icy_interval);
}
}
} while(response.p[0] != '\r' && response.p[0] != '\n');
if (relocate) { win32_net_close(ws.local_socket); ws.local_socket=SOCKET_ERROR; }
} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
if(relocate)
{
if(!got_location)
error("Server meant to redirect but failed to provide a location!");
else
error1("Too many HTTP relocations (%i).", numrelocs);
http_failure;
}
exit: /* The end as well as the exception handling point... */
if(oom) error("Apparently, I ran out of memory or had some bad input data...");
mpg123_free_string(&purl);
mpg123_free_string(&host);
mpg123_free_string(&port);
mpg123_free_string(&path);
mpg123_free_string(&request);
mpg123_free_string(&response);
mpg123_free_string(&request_url);
mpg123_free_string(&httpauth1);
if (ws.local_socket == SOCKET_ERROR || oom)
return -1;
else
return 1;
}
#else
int win32_net_http_open(const char* url, struct httpdata *hd)
{
return -1;
}
#endif /*WANT_WIN32_SOCKETS */

View File

@@ -9,7 +9,6 @@
#include "config.h" #include "config.h"
#include "mpg123.h" #include "mpg123.h"
#include "httpget.h"
#ifdef HAVE_WINDOWS_H #ifdef HAVE_WINDOWS_H
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
@@ -32,15 +31,18 @@
Note: Do not treat return values as valid file/socket handles, they only indicate success/failure. Note: Do not treat return values as valid file/socket handles, they only indicate success/failure.
file descriptors are ignored, only the local ws.local_socket is used for storing socket handle, file descriptors are ignored, only the local ws.local_socket is used for storing socket handle,
so the socket handle is always associated with the last call to win32_net_http_open so the socket handle is always associated with the last call to win32_net_http_open
TODO: Move the socket descriptor/state struct into streamdump.c, which wraps all network
connections. Stored in in nh->parts, it enables multiple sockets being opened.
*/ */
/** /**
* Opens an http URL * Opens an http URL
* @param[in] url URL to open * @param[in] host to connect to
* @param[out] hd http data info * @param[in] port to use
* @return -1 for failure, 1 for success * @return -1 for failure, 1 for success
*/ */
int win32_net_http_open(const char* url, struct httpdata *hd); int win32_net_open_connection(mpg123_string *host, mpg123_string *port);
/** /**
* Reads from network socket * Reads from network socket
@@ -61,13 +63,12 @@ ssize_t win32_net_read (int fildes, void *buf, size_t nbyte);
ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte); ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte);
/** /**
* Similar to fgets - get a string from a stream * Writes a whole mpg123_string to the network socket
* @param[out] s buffer to Write to * @param[in] filedes Value is ignored, last open connection is used.
* @param[in] n bytes of data to read. * @param[in] string the string to write
* @param[in] stream ignored for compatiblity, last open connection is used. * @return TRUE if successful, FALS on error
* @return pointer to s if successful, NULL if failture
*/ */
char *win32_net_fgets(char *s, int n, int stream); int win32_net_writestring (int filedes, mpg123_string *string);
/** /**
* Initialize Winsock 2.2. * Initialize Winsock 2.2.
@@ -84,12 +85,6 @@ void win32_net_deinit (void);
* @param[in] sock value is ignored. * @param[in] sock value is ignored.
*/ */
void win32_net_close (int sock); void win32_net_close (int sock);
/**
* Set reader callback for mpg123_open_fd
* @param[in] fr pointer to a mpg123_handle struct.
*/
void win32_net_replace (mpg123_handle *fr);
#endif #endif
#ifdef WANT_WIN32_UNICODE #ifdef WANT_WIN32_UNICODE