mpg123: mpg123-1.30.1

From 586c237857498ad32713ba6e5cfcea54372cc13f Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Wed, 13 Jul 2022 03:25:24 +0300
Subject: [PATCH] mpg123-1.30.1

---
 INSTALL           |   5 ++-
 NEWS              |  12 ++++++
 configure         |  22 +++++-----
 configure.ac      |   4 +-
 man1/mpg123.1     |  70 ++++++++++++++-----------------
 mpg123.spec       |   2 +-
 src/net123_exec.c | 104 +++++++++++++++++++++++++++++++++++++---------
 src/streamdump.c  |  16 ++++---
 8 files changed, 156 insertions(+), 79 deletions(-)

diff --git a/INSTALL b/INSTALL
index bede127..d6edfa1 100644
--- a/INSTALL
+++ b/INSTALL
@@ -18,8 +18,9 @@ You really need:
 
 - For other exotic platforms, also see ports/
 
-- If building from direct SCM checkout, you need GNU autotools installed
-  (see developer build below).
+- If building from direct source code repository checkout, as opposed to
+  a release or snapshot tarball, you need GNU autotools installed
+  (see Developer Build below).
 
 You want:
 
diff --git a/NEWS b/NEWS
index dd0e302..a249368 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,15 @@
+1.30.1
+------
+- mpg123:
+-- Show stderr of network helpers in -vvv mode.
+-- Use curl --http0.9, if available, to support shoutcast v1 streams
+   without wget (wget not needing such switch, yet).
+-- Support file:// URLs for local access as was intended with the last
+   release.
+-- Give more helpful error message if neither wget nor curl are usable, also
+   allow error messages from curl to appear when not --quiet.
+-- Update the man page.
+
 1.30.0
 ------
 - build:
diff --git a/configure b/configure
index db3d7f4..f34ae4a 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for mpg123 1.30.0.
+# Generated by GNU Autoconf 2.69 for mpg123 1.30.1.
 #
 # Report bugs to <maintainer@mpg123.org>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='mpg123'
 PACKAGE_TARNAME='mpg123'
-PACKAGE_VERSION='1.30.0'
-PACKAGE_STRING='mpg123 1.30.0'
+PACKAGE_VERSION='1.30.1'
+PACKAGE_STRING='mpg123 1.30.1'
 PACKAGE_BUGREPORT='maintainer@mpg123.org'
 PACKAGE_URL=''
 
@@ -1683,7 +1683,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures mpg123 1.30.0 to adapt to many kinds of systems.
+\`configure' configures mpg123 1.30.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1754,7 +1754,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of mpg123 1.30.0:";;
+     short | recursive ) echo "Configuration of mpg123 1.30.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1988,7 +1988,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-mpg123 configure 1.30.0
+mpg123 configure 1.30.1
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2594,7 +2594,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by mpg123 $as_me 1.30.0, which was
+It was created by mpg123 $as_me 1.30.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3553,7 +3553,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='mpg123'
- VERSION='1.30.0'
+ VERSION='1.30.1'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -19559,7 +19559,7 @@ $as_echo_n "checking for automatic network support... " >&6; }
   if test "x$have_fork" = "xyes"; then
     network_type=exec
   elif test "x$win32_specific_codes" = "xenabled"; then
-    network_type=winhttp
+    network_type=wininet
   elif test "x$have_network" = "xyes"; then
     network_type="internal"
   else
@@ -20656,7 +20656,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by mpg123 $as_me 1.30.0, which was
+This file was extended by mpg123 $as_me 1.30.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -20722,7 +20722,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-mpg123 config.status 1.30.0
+mpg123 config.status 1.30.1
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 9eb685d..95f56e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ dnl 2.69 at least.
 AC_PREREQ([2.69])
 
 dnl ############# Initialisation
-AC_INIT([mpg123], [1.30.0], [maintainer@mpg123.org])
+AC_INIT([mpg123], [1.30.1], [maintainer@mpg123.org])
 dnl Increment API_VERSION when the API gets changes (new functions).
 
 dnl libmpg123
@@ -2619,7 +2619,7 @@ if test x$network_type = xauto; then
   if test "x$have_fork" = "xyes"; then
     network_type=exec
   elif test "x$win32_specific_codes" = "xenabled"; then
-    network_type=winhttp
+    network_type=wininet
   elif test "x$have_network" = "xyes"; then
     network_type="internal"
   else
diff --git a/man1/mpg123.1 b/man1/mpg123.1
index c4a3a38..36666a8 100644
--- a/man1/mpg123.1
+++ b/man1/mpg123.1
@@ -1,4 +1,4 @@
-.TH mpg123 1 "26 Apr 2020"
+.TH mpg123 1 "11 Jul 2022"
 .SH NAME
 mpg123 \- play audio MPEG 1.0/2.0/2.5 stream (layers 1, 2 and 3)
 .SH SYNOPSIS
@@ -25,9 +25,10 @@ The path name(s) of one or more input files.  They must be
 valid MPEG-1.0/2.0/2.5 audio layer 1, 2 or 3 bit streams.
 If a dash ``\-'' is specified, MPEG data will
 be read from the standard input.  Furthermore, any name
-starting with ``http://'' is recognized as
+starting with ``http://'' or ``https://'' is recognized as
 .I URL
-(see next section).
+(see next section), while a leading ``file://'' is being stripped for
+normal local file access, for consistency (since mpg123 1.30.1).
 .SH OPTIONS
 .B mpg123
 options may be either the traditional POSIX one letter options,
@@ -72,27 +73,20 @@ This comes a bit closer to the notion of a MP3 file as a defined collection
 of MPEG frames that belong together, but gets rid of the flexibility that can be fun at times but
 mostly is hell for the programmer of the parser and decoder ...
 .TP
+\fB\-\^\-network \fI backend
+Select network  backend (helper program), choices are usually auto, wget, and curl.
+Auto means to try the first available backend.
+.TP
 \fB\-\^-resync\-limit \fIbytes\fR
 Set number of bytes to search for valid MPEG data once lost in stream; <0 means search whole stream.
 If you know there are huge chunks of invalid data in your files... here is your hammer.
 Note: Only since version 1.14 this also increases the amount of junk skipped on beginning.
 .TP
-\fB\-p \fIURL \fR| \fBnone\fR, \fB\-\^\-proxy \fIURL \fR| \fBnone
-The specified
-.I proxy
-will be used for HTTP requests.  It
-should be specified as full URL (``http://host.domain:port/''),
-but the ``http://'' prefix, the port number and the trailing
-slash are optional (the default port is 80).  Specifying
-.B none
-means not to use any proxy, and to retrieve files directly
-from the respective servers.  See also the
-``HTTP SUPPORT'' section.
-.TP
 \fB\-u \fIauth\fR, \fB\-\^\-auth \fIauth
 HTTP authentication to use when receiving files via HTTP.
 The format used is user:password. Mpg123 will clear this quickly, but it may still appear
-in sight of other users or even just in your shell history.
+in sight of other users or even just in your shell history. You may seek alternative ways
+to specify that to your network backend.
 .TP
 \fB\-\^\-auth-file \fIauthfile
 Provide the authentication info via given file instead of command line directly.
@@ -469,28 +463,25 @@ Print the version string.
 In addition to reading MPEG audio streams from ordinary
 files and from the standard input,
 .B mpg123
-supports retrieval of MPEG audio files or playlists via the HTTP protocol, 
+supports retrieval of MPEG audio streams or playlists via the HTTP  protocol,
 which is used in the World Wide Web (WWW).  Such files are
-specified using a so-called URL, which starts with ``http://''.  When a file with
-that prefix is encountered,
+specified using a so-called URL, which starts with http:// or https://.
+When a file with that prefix is encountered,
 .B mpg123
-attempts to open an HTTP connection to the server in order to
-retrieve that file to decode and play it.
-.P
-It is often useful to retrieve files through a WWW cache or
-so-called proxy.  To accomplish this,
+since 1.30.0 will by default call an external helper program (either
+.BR wget (1)
+or
+.BR curl (1),
+see the
+.B \-\^\-network
+option)
+to retrieve the resource. You can configure access via a proxy
+server using the standard environment variables those programs support. The
+.BR \-\^\-proxy
+option that
 .B mpg123
-examines the environment for variables named
-.BR MP3_HTTP_PROXY ", " http_proxy " and " HTTP_PROXY ,
-in this order.  The value of the first one that is set will
-be used as proxy specification.  To override this, you can
-use the
-.B \-p
-command line option (see the ``OPTIONS'' section).  Specifying
-.B "\-p none"
-will enforce contacting the server directly without using
-any proxy, even if one of the above environment variables
-is set.
+before 1.30.0 used for its internal network code is gone
+in the default build now and will probably disappear for good with 1.31.1.
 .P
 Note that, in order to play MPEG audio files from a WWW
 server, it is necessary that the connection to that server
@@ -506,9 +497,8 @@ to your local harddisk (e.g. using
 .BR wget (1))
 and then play them from there.
 .P
-If authentication is needed to access the file it can be
-specified with the 
-.BR "\-u user:pass".
+Streams with embedded ICY metadata are supported, the interval being communicated via HTTP
+headers or \fB\-\^\-icy-interval\fR.
 .SH INTERRUPT
 When in terminal control mode, you can quit via pressing the q key, 
 while any time you can abort
@@ -622,7 +612,9 @@ input side.
 Mostly MPEG-1 layer 2 and 3 are tested in real life.
 Please report any issues and provide test files to help fixing them.
 .P
-No CRC error checking is performed.
+No CRC error checking is performed. But the decoder is built and tested
+to behave nicely with damaged streams. Mostly, damaged frames will just be
+silent.
 .P
 Some platforms lack audio hardware support; you may be able to use the
 .B -s
diff --git a/mpg123.spec b/mpg123.spec
index e3b9105..90188f8 100644
--- a/mpg123.spec
+++ b/mpg123.spec
@@ -3,7 +3,7 @@
 # - devel packages for alsa, sdl, etc... to build the respective output modules.
 Summary:	The fast console mpeg audio decoder/player.
 Name:		mpg123
-Version:	1.30.0
+Version:	1.30.1
 Release:	1
 URL:		http://www.mpg123.org/
 License:	GPL
diff --git a/src/net123_exec.c b/src/net123_exec.c
index 61601e3..0bb6403 100644
--- a/src/net123_exec.c
+++ b/src/net123_exec.c
@@ -65,12 +65,28 @@ static char *catstr(const char *par, const char *value)
 static int got_curl = -1;
 static int got_wget = -1;
 
-static int check_program(char **argv)
+// Check if program executes, also test if given token occurs in its output.
+// Returns 0 if not, 1 if exec works, 2 if also token found.
+// Token has to be < 1024 characters in length.
+static int check_program(char * const *argv, const char *token)
 {
+	int fd[2];
+	int gottoken = 0;
+	if(token)
+	{
+		if(pipe(fd))
+			return 0;
+		compat_binmode(fd[0], TRUE);
+		compat_binmode(fd[1], TRUE);
+	}
 	pid_t pid = fork();
 	if(pid == 0)
 	{
-		int outfd = open("/dev/null", O_WRONLY);
+		int outfd = fd[1];
+		if(token)
+			close(fd[0]);
+		else
+			outfd = open("/dev/null", O_WRONLY);
 		dup2(outfd, STDOUT_FILENO);
 		int infd  = open("/dev/null", O_RDONLY);
 		dup2(infd,  STDIN_FILENO);
@@ -81,10 +97,41 @@ static int check_program(char **argv)
 	}
 	else if(pid > 0)
 	{
+		if(token)
+		{
+			char buf[1024];
+			close(fd[1]);
+			size_t toklen = strlen(token);
+			if(toklen > 0 && toklen < sizeof(buf))
+			{
+				size_t bufoff = 0;
+				size_t got;
+				while( (got = unintr_read(fd[0], buf+bufoff, sizeof(buf)-1-bufoff)) )
+				{
+					bufoff += got;
+					buf[bufoff] = 0; // Now it's a terminated string.
+					if(!gottoken && strstr(buf, token))
+						gottoken = 1;
+					if(gottoken)
+						bufoff = 0; // just forget everything
+					else if(bufoff > toklen)
+					{
+						// Remember the last toklen-1 bytes to compare later.
+						memmove(buf, buf+bufoff-toklen+1, toklen-1);
+						bufoff = toklen-1;
+					}
+				}
+			}
+			close(fd[0]);
+		}
 		int stat;
 		if( (waitpid(pid, &stat, 0) == pid)
 			&& WIFEXITED(stat) && WEXITSTATUS(stat)==0 )
-			return 1;
+			return 1+gottoken;
+	} else if(token)
+	{
+		close(fd[0]);
+		close(fd[1]);
 	}
 	return 0; // false, not there
 }
@@ -147,6 +194,9 @@ static char **curl_argv(const char *url, const char * const * client_head)
 		"curl" // begins with program name
 #ifdef DEBUG
 	,	"--verbose"
+#else
+	,	"--silent"
+	,	"--show-error"
 #endif
 	,	"--dump-header"
 	,	"-"
@@ -156,6 +206,8 @@ static char **curl_argv(const char *url, const char * const * client_head)
 	// Get the count of argument strings right!
 	// Fixed args + agent + client headers [+ auth] + URL + NULL
 	int argc = sizeof(base_args)/sizeof(char*)+2+2*cheads+1+1;
+	if(got_curl > 1)
+		argc++; // add --http0.9
 	char *httpauth = NULL;
 	if(param.httpauth && (httpauth = compat_strdup(param.httpauth)))
 		argc += 2;
@@ -168,6 +220,8 @@ static char **curl_argv(const char *url, const char * const * client_head)
 	int an = 0;
 	for(;an<sizeof(base_args)/sizeof(char*); ++an)
 		argv[an] = compat_strdup(base_args[an]);
+	if(got_curl > 1)
+		argv[an++] = compat_strdup("--http0.9");
 	argv[an++] = compat_strdup("--user-agent");
 	argv[an++] = compat_strdup(PACKAGE_NAME "/" PACKAGE_VERSION);
 	for(size_t ch=0; ch < cheads; ++ch)
@@ -188,29 +242,38 @@ static char **curl_argv(const char *url, const char * const * client_head)
 net123_handle *net123_open(const char *url, const char * const * client_head)
 {
 	int use_curl = 0;
+	char * const curl_check_argv[] = { "curl", "--help", "all", NULL };
+	char * const wget_check_argv[] = { "wget", "--version", NULL };
 	// Semi-threadsafe: The check might take place multiple times, but writing the integer
 	// should be safe enough.
 	if(!strcmp("auto",param.network_backend))
 	{
-		char *curl_argv[] = { "curl", "--version", NULL };
-		char *wget_argv[] = { "wget", "--version", NULL };
-		if(got_curl < 0)
-			got_curl = check_program(curl_argv);
 		if(got_wget < 0)
-			got_wget = check_program(wget_argv);
-		if(got_wget < 1 && got_curl == 1)
+			got_wget = check_program(wget_check_argv, NULL);
+		if(!got_wget && got_curl < 0)
+			got_curl = check_program(curl_check_argv, "--http0.9");
+		if(got_wget < 1 && got_curl)
 			use_curl = 1;
 	} else if(!strcmp("curl", param.network_backend))
 	{
+		if(got_curl < 0) // Still need to know if HTTP/0.9 option is there.
+			got_curl = check_program(curl_check_argv, "--http0.9");
 		use_curl = 1;
 	} else if(!strcmp("wget", param.network_backend))
 	{
+		if(got_wget < 0)
+			got_wget = check_program(wget_check_argv, NULL);
 		use_curl = 0;
 	} else
 	{
 		merror("invalid network backend specified: %s", param.network_backend);
 		return NULL;
 	}
+	if((!use_curl && !got_wget) || (use_curl && !got_curl))
+	{
+		error("missing working network helper program (wget or curl)");
+		return NULL;
+	}
 
 	int fd[2];
 	int hi = -1; // index of header value that might get a continuation line
@@ -252,20 +315,23 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
 		if(!argv)
 			exit(1);
 		errno = 0;
-		if(param.verbose > 2)
+		if(!param.quiet)
 		{
-			char **a = argv;
-			fprintf(stderr, "HTTP helper command:\n");
-			while(*a)
+			if(param.verbose > 2)
 			{
-				fprintf(stderr, " %s\n", *a);
-				++a;
+				char **a = argv;
+				fprintf(stderr, "HTTP helper command:\n");
+				while(*a)
+				{
+					fprintf(stderr, " %s\n", *a);
+					++a;
+				}
 			}
+		} else
+		{
+			int errfd = open("/dev/null", O_WRONLY);
+			dup2(errfd, STDERR_FILENO);
 		}
-#ifndef DEBUG
-		int errfd = open("/dev/null", O_WRONLY);
-		dup2(errfd, STDERR_FILENO);
-#endif
 		execvp(argv[0], argv);
 		merror("cannot execute %s: %s", argv[0], strerror(errno));
 		exit(1);
diff --git a/src/streamdump.c b/src/streamdump.c
index 242c88a..92a7985 100644
--- a/src/streamdump.c
+++ b/src/streamdump.c
@@ -159,9 +159,11 @@ int stream_parse_headers(struct stream *sd)
 	int hn = sizeof(head)/sizeof(char*);
 	int hi = -1;
 	int got_ok = 0;
+	int got_line = 0;
 	debug("parsing headers");
 	while(stream_getline(sd, &line) > 0)
 	{
+		got_line = 1;
 		mdebug("HTTP in: %s", line.p);
 		if(line.p[0] == 0)
 		{
@@ -239,7 +241,11 @@ int stream_parse_headers(struct stream *sd)
 		if(param.verbose > 1)
 			fprintf(stderr, "Info: ICY interval %li\n", (long)sd->htd.icy_interval);
 	}
-	if(!got_ok)
+	if(!got_line)
+	{
+		error("no data at all from network resource");
+		ret = -1;
+	} else if(!got_ok)
 	{
 		error("missing positive server response");
 		ret = -1;
@@ -273,8 +279,6 @@ struct stream *stream_open(const char *url)
 		sd->fd = STDIN_FILENO;
 		compat_binmode(STDIN_FILENO, TRUE);
 	}
-	else if(!strncasecmp("file://", url, 7))
-		url+= 7; // use local file access for files, the scheme may be useful
 #ifdef NET123
 	else if(!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8))
 	{
@@ -287,7 +291,7 @@ struct stream *stream_open(const char *url)
 		append_accept(&accept);
 		client_head[1] = accept.p;
 		sd->nh = net123_open(url, client_head);
-		if(stream_parse_headers(sd))
+		if(!sd->nh || stream_parse_headers(sd))
 		{
 			stream_close(sd);
 			return NULL;
@@ -311,11 +315,13 @@ struct stream *stream_open(const char *url)
 	else
 	{
 		// plain file access
+		if(!strncasecmp("file://", url, 7))
+			url+= 7; // Might be useful to prepend file scheme prefix for local stuff.
 		errno = 0;
 		sd->fd = compat_open(url, O_RDONLY|O_BINARY);
 		if(sd->fd < 0)
 		{
-			merror("failed to open file: %s", strerror(errno));
+			merror("failed to open file: %s: %s", url, strerror(errno));
 			stream_close(sd);
 			return NULL;
 		}