1 /*
2  *  A neon HTTP input plugin for Audacious
3  *  Copyright (C) 2007 Ralf Ertzinger
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #define __STDC_FORMAT_MACROS
21 #include <inttypes.h>
22 #include <pthread.h>
23 #include <stdint.h>
24 #include <string.h>
25 
26 #include <glib.h>
27 
28 #include <libaudcore/audstrings.h>
29 #include <libaudcore/i18n.h>
30 #include <libaudcore/plugin.h>
31 #include <libaudcore/ringbuf.h>
32 #include <libaudcore/runtime.h>
33 
34 #include <ne_auth.h>
35 #include <ne_session.h>
36 #include <ne_socket.h>
37 #include <ne_redirect.h>
38 #include <ne_request.h>
39 #include <ne_uri.h>
40 #include <ne_utils.h>
41 
42 #include "cert_verification.h"
43 
44 #define NEON_NETBLKSIZE     (4096)
45 #define NEON_ICY_BUFSIZE    (4096)
46 #define NEON_RETRY_COUNT 6
47 #undef feof
48 
49 enum FillBufferResult {
50     FILL_BUFFER_SUCCESS,
51     FILL_BUFFER_ERROR,
52     FILL_BUFFER_EOF
53 };
54 
55 enum neon_reader_t {
56     NEON_READER_INIT = 0,
57     NEON_READER_RUN = 1,
58     NEON_READER_ERROR,
59     NEON_READER_EOF,
60     NEON_READER_TERM
61 };
62 
63 struct reader_status
64 {
65     bool reading = false;
66     neon_reader_t status = NEON_READER_INIT;
67 
68     pthread_mutex_t mutex;
69     pthread_cond_t cond;
70 
reader_statusreader_status71     reader_status ()
72     {
73         pthread_mutex_init (& mutex, nullptr);
74         pthread_cond_init (& cond, nullptr);
75     }
76 
~reader_statusreader_status77     ~reader_status ()
78     {
79         pthread_mutex_destroy (& mutex);
80         pthread_cond_destroy (& cond);
81     }
82 };
83 
84 struct icy_metadata
85 {
86     String stream_name;
87     String stream_title;
88     String stream_url;
89     String stream_contenttype;
90     int stream_bitrate = 0;
91 };
92 
93 static const char * const neon_schemes[] = {"http", "https"};
94 
95 class NeonTransport : public TransportPlugin
96 {
97 public:
98     static constexpr PluginInfo info = {N_("Neon HTTP/HTTPS Plugin"), PACKAGE};
99 
NeonTransport()100     constexpr NeonTransport () : TransportPlugin (info, neon_schemes) {}
101 
102     bool init ();
103     void cleanup ();
104 
105     VFSImpl * fopen (const char * path, const char * mode, String & error);
106 };
107 
108 EXPORT NeonTransport aud_plugin_instance;
109 
init()110 bool NeonTransport::init ()
111 {
112     int ret = ne_sock_init ();
113 
114     if (ret != 0)
115     {
116         AUDERR ("Could not initialize neon library: %d\n", ret);
117         return false;
118     }
119 
120     return true;
121 }
122 
cleanup()123 void NeonTransport::cleanup ()
124 {
125     ne_sock_exit ();
126 }
127 
128 class NeonFile : public VFSImpl
129 {
130 public:
131     NeonFile (const char * url);
132     ~NeonFile ();
133 
134     int open_handle (int64_t startbyte, String * error = nullptr);
135 
136 protected:
137     int64_t fread (void * ptr, int64_t size, int64_t nmemb);
138     int fseek (int64_t offset, VFSSeekType whence);
139 
140     int64_t ftell ();
141     int64_t fsize ();
142     bool feof ();
143 
144     int64_t fwrite (const void * ptr, int64_t size, int64_t nmemb);
145     int ftruncate (int64_t length);
146     int fflush ();
147 
148     String get_metadata (const char * field);
149 
150 private:
151     String m_url;               /* The URL, as passed to us */
152     ne_uri m_purl = ne_uri ();  /* The URL, parsed into a structure */
153 
154     unsigned char m_redircount = 0;     /* Redirect count for the opened URL */
155     int64_t m_pos = 0;                  /* Current position in the stream
156                                            (number of last byte delivered to the player) */
157     int64_t m_content_start = 0;        /* Start position in the stream */
158     int64_t m_content_length = -1;      /* Total content length, counting from
159                                            content_start, if known. -1 if unknown */
160     bool m_can_ranges = false;          /* true if the webserver advertised accept-range: bytes */
161     int64_t m_icy_metaint = 0;          /* Interval in which the server will
162                                            send metadata announcements. 0 if no announcments */
163     int64_t m_icy_metaleft = 0;         /* Bytes left until the next metadata block */
164     int m_icy_len = 0;                  /* Bytes in current metadata block */
165 
166     bool m_eof = false;
167 
168     RingBuf<char> m_rb;           /* Ringbuffer for our data */
169     Index<char> m_icy_buf;        /* Buffer for ICY metadata */
170     icy_metadata m_icy_metadata;  /* Current ICY metadata */
171 
172     ne_session * m_session = nullptr;
173     ne_request * m_request = nullptr;
174 
175     pthread_t m_reader;
176     reader_status m_reader_status;
177 
178     void kill_reader ();
179     int server_auth (const char * realm, int attempt, char * username, char * password);
180     void handle_headers ();
181     int open_request (int64_t startbyte, String * error);
182     FillBufferResult fill_buffer ();
183     void reader ();
184     int64_t try_fread (void * ptr, int64_t size, int64_t nmemb, bool & data_read);
185 
server_auth_callback(void * data,const char * realm,int attempt,char * username,char * password)186     static int server_auth_callback (void * data, const char * realm, int attempt,
187      char * username, char * password)
188         { return ((NeonFile *) data)->server_auth (realm, attempt, username, password); }
189 
reader_thread(void * data)190     static void * reader_thread (void * data)
191         { ((NeonFile *) data)->reader (); return nullptr; }
192 };
193 
NeonFile(const char * url)194 NeonFile::NeonFile (const char * url) :
195     m_url (url)
196 {
197     int buffer_kb = aud_get_int ("net_buffer_kb");
198     m_rb.alloc (1024 * aud::clamp (buffer_kb, 16, 1024));
199 }
200 
~NeonFile()201 NeonFile::~NeonFile ()
202 {
203     if (m_reader_status.reading)
204         kill_reader ();
205 
206     if (m_request)
207         ne_request_destroy (m_request);
208     if (m_session)
209         ne_session_destroy (m_session);
210 
211     ne_uri_free (& m_purl);
212 }
213 
neon_strcmp(const char * str,const char * cmp)214 static bool neon_strcmp (const char * str, const char * cmp)
215 {
216     return ! g_ascii_strncasecmp (str, cmp, strlen (cmp));
217 }
218 
add_icy(struct icy_metadata * m,const char * name,const char * value)219 static void add_icy (struct icy_metadata * m, const char * name, const char * value)
220 {
221     if (neon_strcmp (name, "StreamTitle"))
222     {
223         AUDDBG ("Found StreamTitle: %s\n", value);
224         m->stream_title = String (str_to_utf8 (value, -1));
225     }
226 
227     if (neon_strcmp (name, "StreamUrl"))
228     {
229         AUDDBG ("Found StreamUrl: %s\n", value);
230         m->stream_url = String (str_to_utf8 (value, -1));
231     }
232 }
233 
parse_icy(struct icy_metadata * m,char * metadata,int len)234 static void parse_icy (struct icy_metadata * m, char * metadata, int len)
235 {
236     enum TagReadState
237     {
238         STATE_READ_NAME,
239         STATE_WAIT_VALUE,
240         STATE_READ_VALUE,
241         STATE_WAIT_NAME,
242     };
243 
244     TagReadState state = STATE_READ_NAME;
245 
246     char * p = metadata;
247     char * tstart = metadata;
248     int pos = 1;
249 
250     char name[NEON_ICY_BUFSIZE];
251     char value[NEON_ICY_BUFSIZE];
252 
253     name[0] = 0;
254     value[0] = 0;
255 
256     while (pos < len && p[0])
257     {
258         switch (state)
259         {
260         case STATE_READ_NAME:
261 
262             /* Reading tag name */
263             if (p[0] == '=')
264             {
265                 /* End of tag name. */
266                 p[0] = 0;
267                 g_strlcpy (name, tstart, NEON_ICY_BUFSIZE);
268                 AUDDBG ("Found tag name: %s\n", name);
269                 state = STATE_WAIT_VALUE;
270             }
271 
272             break;
273 
274         case STATE_WAIT_VALUE:
275 
276             /* Waiting for start of value */
277             if (p[0] == '\'')
278             {
279                 /* Leading ' of value */
280                 tstart = p + 1;
281                 state = STATE_READ_VALUE;
282                 value[0] = 0;
283             }
284 
285             break;
286 
287         case STATE_READ_VALUE:
288 
289             /* Reading value */
290             if (p[0] == '\'' && p[1] == ';')
291             {
292                 /* End of value */
293                 p[0] = 0;
294                 g_strlcpy (value, tstart, NEON_ICY_BUFSIZE);
295                 AUDDBG ("Found tag value: %s\n", value);
296                 add_icy (m, name, value);
297                 state = STATE_WAIT_NAME;
298             }
299 
300             break;
301 
302         case STATE_WAIT_NAME:
303 
304             /* Waiting for next tag start */
305             if (p[0] == ';')
306             {
307                 /* Next tag name starts after this char */
308                 tstart = p + 1;
309                 state = STATE_READ_NAME;
310                 name[0] = 0;
311                 value[0] = 0;
312             }
313 
314             break;
315         }
316 
317         p ++;
318         pos ++;
319     }
320 }
321 
kill_reader()322 void NeonFile::kill_reader ()
323 {
324     AUDDBG ("Signaling reader thread to terminate\n");
325     pthread_mutex_lock (& m_reader_status.mutex);
326     m_reader_status.reading = false;
327     pthread_cond_broadcast (& m_reader_status.cond);
328     pthread_mutex_unlock (& m_reader_status.mutex);
329 
330     AUDDBG ("Waiting for reader thread to die...\n");
331     pthread_join (m_reader, nullptr);
332     AUDDBG ("Reader thread has died\n");
333 }
334 
server_auth(const char * realm,int attempt,char * username,char * password)335 int NeonFile::server_auth (const char * realm, int attempt, char * username, char * password)
336 {
337     if (! m_purl.userinfo || ! m_purl.userinfo[0])
338     {
339         AUDERR ("Authentication required, but no credentials set\n");
340         return 1;
341     }
342 
343     char * * authtok = g_strsplit (m_purl.userinfo, ":", 2);
344 
345     if (strlen (authtok[1]) > NE_ABUFSIZ - 1 || strlen (authtok[0]) > NE_ABUFSIZ - 1)
346     {
347         AUDERR ("Username/Password too long\n");
348         g_strfreev (authtok);
349         return 1;
350     }
351 
352     g_strlcpy (username, authtok[0], NE_ABUFSIZ);
353     g_strlcpy (password, authtok[1], NE_ABUFSIZ);
354 
355     AUDDBG ("Authenticating: Username: %s, Password: %s\n", username, password);
356 
357     g_strfreev (authtok);
358 
359     return attempt;
360 }
361 
handle_headers()362 void NeonFile::handle_headers ()
363 {
364     const char * name;
365     const char * value;
366     void * cursor = nullptr;
367 
368     AUDDBG ("Header responses:\n");
369 
370     while ((cursor = ne_response_header_iterate (m_request, cursor, & name, & value)))
371     {
372         AUDDBG ("HEADER: %s: %s\n", name, value);
373 
374         if (neon_strcmp (name, "accept-ranges"))
375         {
376             /* The server advertises range capability. we need "bytes" */
377             if (strstr (value, "bytes"))
378             {
379                 AUDDBG ("server can_ranges\n");
380                 m_can_ranges = true;
381             }
382         }
383         else if (neon_strcmp (name, "content-length"))
384         {
385             /* The server sent us the content length. Parse and store. */
386             char * endptr;
387             int64_t len = strtoll (value, & endptr, 10);
388 
389             if (value[0] && ! endptr[0] && len >= 0)
390             {
391                 /* Valid data. */
392                 AUDDBG ("Content length as advertised by server: %" PRId64 "\n", len);
393                 m_content_length = len;
394             }
395             else
396                 AUDERR ("Invalid content length header: %s\n", value);
397         }
398         else if (neon_strcmp (name, "content-type"))
399         {
400             /* The server sent us a content type. Save it for later */
401             AUDDBG ("Content-Type: %s\n", value);
402             m_icy_metadata.stream_contenttype = String (str_to_utf8 (value, -1));
403         }
404         else if (neon_strcmp (name, "icy-metaint"))
405         {
406             /* The server sent us a ICY metaint header. Parse and store. */
407             char * endptr;
408             int64_t len = strtoll (value, & endptr, 10);
409 
410             if (value[0] && ! endptr[0] && len > 0)
411             {
412                 /* Valid data */
413                 AUDDBG ("ICY MetaInt as advertised by server: %" PRId64 "\n", len);
414                 m_icy_metaint = len;
415                 m_icy_metaleft = len;
416             }
417             else
418                 AUDERR ("Invalid ICY MetaInt header: %s\n", value);
419         }
420         else if (neon_strcmp (name, "icy-name"))
421         {
422             /* The server sent us a ICY name. Save it for later */
423             AUDDBG ("ICY stream name: %s\n", value);
424             m_icy_metadata.stream_name = String (value);
425         }
426         else if (neon_strcmp (name, "icy-br"))
427         {
428             /* The server sent us a bitrate. We might want to use it. */
429             AUDDBG ("ICY bitrate: %d\n", atoi (value));
430             m_icy_metadata.stream_bitrate = atoi (value);
431         }
432     }
433 }
434 
neon_proxy_auth_cb(void * userdata,const char * realm,int attempt,char * username,char * password)435 static int neon_proxy_auth_cb (void * userdata, const char * realm, int attempt,
436  char * username, char * password)
437 {
438     String value = aud_get_str ("proxy_user");
439     g_strlcpy (username, value, NE_ABUFSIZ);
440 
441     value = aud_get_str ("proxy_pass");
442     g_strlcpy (password, value, NE_ABUFSIZ);
443 
444     return attempt;
445 }
446 
open_request(int64_t startbyte,String * error)447 int NeonFile::open_request (int64_t startbyte, String * error)
448 {
449     int ret;
450     const ne_status * status;
451     ne_uri * rediruri;
452 
453     if (m_purl.query && * (m_purl.query))
454     {
455         StringBuf tmp = str_concat ({m_purl.path, "?", m_purl.query});
456         m_request = ne_request_create (m_session, "GET", tmp);
457     }
458     else
459         m_request = ne_request_create (m_session, "GET", m_purl.path);
460 
461     if (startbyte > 0)
462         ne_add_request_header (m_request, "Range", str_printf ("bytes=%" PRIu64 "-", startbyte));
463 
464     ne_add_request_header (m_request, "Icy-MetaData", "1");
465 
466     /* Try to connect to the server. */
467     AUDDBG ("<%p> Connecting...\n", this);
468     ret = ne_begin_request (m_request);
469     status = ne_get_status (m_request);
470     AUDDBG ("<%p> Return: %d, Status: %d\n", this, ret, status->code);
471 
472     if (ret == NE_OK)
473     {
474         switch (status->code)
475         {
476         case 401:
477             /* Authorization required. Reconnect to authenticate */
478             AUDDBG ("Reconnecting due to 401\n");
479             ne_end_request (m_request);
480             ret = ne_begin_request (m_request);
481             break;
482 
483         case 301:
484         case 302:
485         case 303:
486         case 307:
487             /* Redirect encountered. Reconnect. */
488             ne_end_request (m_request);
489             ret = NE_REDIRECT;
490             break;
491 
492         case 407:
493             /* Proxy auth required. Reconnect to authenticate */
494             AUDDBG ("Reconnecting due to 407\n");
495             ne_end_request (m_request);
496             ret = ne_begin_request (m_request);
497             break;
498         }
499     }
500 
501     switch (ret)
502     {
503     case NE_OK:
504         if (status->code > 199 && status->code < 300)
505         {
506             /* URL opened OK */
507             AUDDBG ("<%p> URL opened OK\n", this);
508             m_content_start = startbyte;
509             m_pos = startbyte;
510             handle_headers ();
511             return 0;
512         }
513 
514         break;
515 
516     case NE_REDIRECT:
517         /* We hit a redirect. Handle it. */
518         AUDDBG ("<%p> Redirect encountered\n", this);
519         m_redircount += 1;
520         rediruri = (ne_uri *) ne_redirect_location (m_session);
521         ne_request_destroy (m_request);
522         m_request = nullptr;
523 
524         if (! rediruri)
525         {
526             if (error)
527                 * error = String (_("Error parsing redirect"));
528 
529             AUDERR ("<%p> Could not parse redirect response\n", this);
530             return -1;
531         }
532 
533         ne_uri_free (& m_purl);
534         ne_uri_copy (& m_purl, rediruri);
535         return 1;
536     }
537 
538     /* Something went wrong. */
539     const char * ne_error = ne_get_error (m_session);
540     if (error)
541         * error = String (ne_error ? ne_error : _("Unknown HTTP error"));
542 
543     AUDERR ("<%p> Could not open URL: %d (%d)\n", this, ret, status->code);
544 
545     if (ne_error)
546         AUDERR ("<%p> neon error string: %s\n", this, ne_error);
547 
548     ne_request_destroy (m_request);
549     m_request = nullptr;
550     return -1;
551 }
552 
open_handle(int64_t startbyte,String * error)553 int NeonFile::open_handle (int64_t startbyte, String * error)
554 {
555     int ret;
556     String proxy_host;
557     int proxy_port = 0;
558     String proxy_user (""); // ne_session_socks_proxy requires non NULL user and password
559     String proxy_pass ("");
560     bool socks_proxy = false;
561     ne_sock_sversion socks_type = NE_SOCK_SOCKSV4A;
562 
563     bool use_proxy = aud_get_bool ("use_proxy");
564     bool use_proxy_auth = aud_get_bool ("use_proxy_auth");
565 
566     if (use_proxy)
567     {
568         proxy_host = aud_get_str ("proxy_host");
569         proxy_port = aud_get_int ("proxy_port");
570         socks_proxy = aud_get_bool ("socks_proxy");
571 
572         if (use_proxy_auth)
573         {
574             proxy_user = aud_get_str ("proxy_user");
575             proxy_pass = aud_get_str ("proxy_pass");
576         }
577 
578         if (socks_proxy)
579         {
580             socks_type = aud_get_int ("socks_type") == 0 ? NE_SOCK_SOCKSV4A : NE_SOCK_SOCKSV5;
581         }
582     }
583 
584     m_redircount = 0;
585 
586     AUDDBG ("<%p> Parsing URL\n", this);
587 
588     if (ne_uri_parse (m_url, & m_purl) != 0)
589     {
590         if (error)
591             * error = String (_("Error parsing URL"));
592 
593         AUDERR ("<%p> Could not parse URL '%s'\n", this, (const char *) m_url);
594         return -1;
595     }
596 
597     while (m_redircount < 10)
598     {
599         if (! m_purl.port)
600             m_purl.port = ne_uri_defaultport (m_purl.scheme);
601 
602         AUDDBG ("<%p> Creating session to %s://%s:%d\n", this,
603          m_purl.scheme, m_purl.host, m_purl.port);
604         m_session = ne_session_create (m_purl.scheme,
605          m_purl.host, m_purl.port);
606         ne_redirect_register (m_session);
607         ne_add_server_auth (m_session, NE_AUTH_BASIC, server_auth_callback, this);
608         ne_set_session_flag (m_session, NE_SESSFLAG_ICYPROTO, 1);
609         ne_set_session_flag (m_session, NE_SESSFLAG_PERSIST, 0);
610         ne_set_connect_timeout (m_session, 10);
611         ne_set_read_timeout (m_session, 10);
612         ne_set_useragent (m_session, "Audacious/" PACKAGE_VERSION);
613 
614         if (use_proxy)
615         {
616             AUDDBG ("<%p> Using proxy: %s:%d\n", this, (const char *) proxy_host, proxy_port);
617             if (socks_proxy)
618             {
619                 ne_session_socks_proxy (m_session, socks_type, proxy_host, proxy_port, proxy_user, proxy_pass);
620             }
621             else
622             {
623                 ne_session_proxy (m_session, proxy_host, proxy_port);
624             }
625 
626             if (use_proxy_auth)
627             {
628                 AUDDBG ("<%p> Using proxy authentication\n", this);
629                 ne_add_proxy_auth (m_session, NE_AUTH_BASIC,
630                  neon_proxy_auth_cb, (void *) this);
631             }
632         }
633 
634         if (! strcmp ("https", m_purl.scheme))
635         {
636             ne_ssl_trust_default_ca (m_session);
637             ne_ssl_set_verify (m_session,
638              neon_vfs_verify_environment_ssl_certs, m_session);
639         }
640 
641         AUDDBG ("<%p> Creating request\n", this);
642         ret = open_request (startbyte, error);
643 
644         if (! ret)
645             return 0;
646 
647         if (ret == -1)
648         {
649             ne_session_destroy (m_session);
650             m_session = nullptr;
651             return -1;
652         }
653 
654         AUDDBG ("<%p> Following redirect...\n", this);
655         ne_session_destroy (m_session);
656         m_session = nullptr;
657     }
658 
659     /* If we get here, our redirect count exceeded */
660     if (error)
661         * error = String (_("Too many redirects"));
662 
663     AUDERR ("<%p> Redirect count exceeded for URL %s\n", this, (const char *) m_url);
664     return 1;
665 }
666 
fill_buffer()667 FillBufferResult NeonFile::fill_buffer ()
668 {
669     char buffer[NEON_NETBLKSIZE];
670     int to_read;
671 
672     pthread_mutex_lock (& m_reader_status.mutex);
673     to_read = aud::min (m_rb.space (), NEON_NETBLKSIZE);
674     pthread_mutex_unlock (& m_reader_status.mutex);
675 
676     int bsize = ne_read_response_block (m_request, buffer, to_read);
677 
678     if (! bsize)
679     {
680         AUDDBG ("<%p> End of file encountered\n", this);
681         return FILL_BUFFER_EOF;
682     }
683 
684     if (bsize < 0)
685     {
686         AUDERR ("<%p> Error while reading from the network\n", this);
687         ne_request_destroy (m_request);
688         m_request = nullptr;
689         return FILL_BUFFER_ERROR;
690     }
691 
692     AUDDBG ("<%p> Read %d bytes of %d\n", this, bsize, to_read);
693 
694     pthread_mutex_lock (& m_reader_status.mutex);
695     m_rb.copy_in (buffer, bsize);
696     pthread_mutex_unlock (& m_reader_status.mutex);
697 
698     return FILL_BUFFER_SUCCESS;
699 }
700 
reader()701 void NeonFile::reader ()
702 {
703     pthread_mutex_lock (& m_reader_status.mutex);
704 
705     while (m_reader_status.reading)
706     {
707         /* Hit the network only if we have more than NEON_NETBLKSIZE of free buffer */
708         if (m_rb.space () > NEON_NETBLKSIZE)
709         {
710             pthread_mutex_unlock (& m_reader_status.mutex);
711 
712             FillBufferResult ret = fill_buffer ();
713 
714             pthread_mutex_lock (& m_reader_status.mutex);
715 
716             /* Wake up main thread if it is waiting. */
717             pthread_cond_broadcast (& m_reader_status.cond);
718 
719             if (ret == FILL_BUFFER_ERROR)
720             {
721                 AUDERR ("<%p> Error while reading from the network. "
722                         "Terminating reader thread\n", this);
723                 m_reader_status.status = NEON_READER_ERROR;
724                 pthread_mutex_unlock (& m_reader_status.mutex);
725                 return;
726             }
727             else if (ret == FILL_BUFFER_EOF)
728             {
729                 AUDDBG ("<%p> EOF encountered while reading from the network. "
730                         "Terminating reader thread\n", this);
731                 m_reader_status.status = NEON_READER_EOF;
732                 pthread_mutex_unlock (& m_reader_status.mutex);
733                 return;
734             }
735         }
736         else
737         {
738             /* Not enough free space in the buffer.
739              * Sleep until the main thread wakes us up. */
740             pthread_cond_wait (& m_reader_status.cond, & m_reader_status.mutex);
741         }
742     }
743 
744     AUDDBG ("<%p> Reader thread terminating gracefully\n", this);
745     m_reader_status.status = NEON_READER_TERM;
746     pthread_mutex_unlock (& m_reader_status.mutex);
747 }
748 
fopen(const char * path,const char * mode,String & error)749 VFSImpl * NeonTransport::fopen (const char * path, const char * mode, String & error)
750 {
751     NeonFile * file = new NeonFile (path);
752 
753     AUDDBG ("<%p> Trying to open '%s' with neon\n", file, path);
754 
755     if (file->open_handle (0, & error) != 0)
756     {
757         AUDERR ("<%p> Could not open URL\n", file);
758         delete file;
759         return nullptr;
760     }
761 
762     return file;
763 }
764 
try_fread(void * ptr,int64_t size,int64_t nmemb,bool & data_read)765 int64_t NeonFile::try_fread (void * ptr, int64_t size, int64_t nmemb, bool & data_read)
766 {
767     if (! m_request)
768     {
769         AUDERR ("<%p> No request to read from, seek gone wrong?\n", this);
770         return 0;
771     }
772 
773     if (! size || ! nmemb || m_eof)
774         return 0;
775 
776     /* If the buffer is empty, wait for the reader thread to fill it. */
777     pthread_mutex_lock (& m_reader_status.mutex);
778 
779     for (int retries = 0; retries < NEON_RETRY_COUNT; retries ++)
780     {
781         if (m_rb.len () / size > 0 || ! m_reader_status.reading ||
782          m_reader_status.status != NEON_READER_RUN)
783             break;
784 
785         pthread_cond_broadcast (& m_reader_status.cond);
786         pthread_cond_wait (& m_reader_status.cond, & m_reader_status.mutex);
787     }
788 
789     pthread_mutex_unlock (& m_reader_status.mutex);
790 
791     if (! m_reader_status.reading)
792     {
793         if (m_reader_status.status != NEON_READER_EOF || m_content_length != -1)
794         {
795             /* There is no reader thread yet. Read the first bytes from
796              * the network ourselves, and then fire up the reader thread
797              * to keep the buffer filled up. */
798             AUDDBG ("<%p> Doing initial buffer fill\n", this);
799             FillBufferResult ret = fill_buffer ();
800 
801             if (ret == FILL_BUFFER_ERROR)
802             {
803                 AUDERR ("<%p> Error while reading from the network\n", this);
804                 return 0;
805             }
806 
807             /* We have some data in the buffer now.
808              * Start the reader thread if we did not reach EOF during
809              * the initial fill */
810             pthread_mutex_lock (& m_reader_status.mutex);
811 
812             if (ret == FILL_BUFFER_SUCCESS)
813             {
814                 m_reader_status.reading = true;
815                 AUDDBG ("<%p> Starting reader thread\n", this);
816                 pthread_create (& m_reader, nullptr, reader_thread, this);
817                 m_reader_status.status = NEON_READER_RUN;
818             }
819             else if (ret == FILL_BUFFER_EOF)
820             {
821                 AUDDBG ("<%p> No reader thread needed (stream has reached EOF during fill)\n", this);
822                 m_reader_status.reading = false;
823                 m_reader_status.status = NEON_READER_EOF;
824             }
825 
826             pthread_mutex_unlock (& m_reader_status.mutex);
827         }
828     }
829     else
830     {
831         /* There already is a reader thread. Look if it is in good shape. */
832         pthread_mutex_lock (& m_reader_status.mutex);
833 
834         switch (m_reader_status.status)
835         {
836         case NEON_READER_INIT:
837         case NEON_READER_RUN:
838             /* All is well, nothing to be done. */
839             break;
840 
841         case NEON_READER_ERROR:
842             /* A reader error happened. Log it, and treat it like an EOF
843              * condition, by falling through to the NEON_READER_EOF codepath. */
844             AUDDBG ("<%p> NEON_READER_ERROR happened. Terminating reader thread and marking EOF.\n", this);
845             m_reader_status.status = NEON_READER_EOF;
846             pthread_mutex_unlock (& m_reader_status.mutex);
847 
848             if (m_reader_status.reading)
849                 kill_reader ();
850 
851             pthread_mutex_lock (& m_reader_status.mutex);
852 
853         case NEON_READER_EOF:
854             /* If there still is data in the buffer, carry on.
855              * If not, terminate the reader thread and return 0. */
856             if (! m_rb.len ())
857             {
858                 AUDDBG ("<%p> Reached end of stream\n", this);
859                 pthread_mutex_unlock (& m_reader_status.mutex);
860 
861                 if (m_reader_status.reading)
862                     kill_reader ();
863 
864                 m_eof = true;
865                 return 0;
866             }
867 
868             break;
869 
870         case NEON_READER_TERM:
871             /* The reader thread terminated gracefully, most likely on our own request.
872              * We should not get here. */
873             g_warn_if_reached ();
874             pthread_mutex_unlock (& m_reader_status.mutex);
875             return 0;
876         }
877 
878         pthread_mutex_unlock (& m_reader_status.mutex);
879     }
880 
881     /* Deliver data from the buffer */
882     pthread_mutex_lock (& m_reader_status.mutex);
883 
884     if (m_rb.len ())
885         data_read = true;
886     else
887     {
888         /* The buffer is still empty, we can deliver no data! */
889         AUDERR ("<%p> Buffer still underrun, fatal.\n", this);
890         pthread_mutex_unlock (& m_reader_status.mutex);
891         return 0;
892     }
893 
894     int64_t belem = m_rb.len () / size;
895 
896     if (m_icy_metaint)
897     {
898         if (! m_icy_metaleft)
899         {
900             if (! m_icy_len)
901             {
902                 /* The next data in the buffer is a ICY metadata announcement.
903                  * Get the length byte */
904                 m_icy_len = 16 * (unsigned char) m_rb.head ();
905                 m_rb.pop ();
906 
907                 AUDDBG ("<%p> Expecting %d bytes of ICY metadata\n", this, m_icy_len);
908             }
909 
910             if (m_icy_buf.len () < m_icy_len)
911                 m_rb.move_out (m_icy_buf, -1, aud::min (m_icy_len - m_icy_buf.len (), m_rb.len ()));
912 
913             if (m_icy_buf.len () >= m_icy_len)
914             {
915                 /* Grab the metadata from the buffer and send it to the parser */
916                 parse_icy (& m_icy_metadata, m_icy_buf.begin (), m_icy_buf.len ());
917 
918                 /* Reset countdown to next announcement */
919                 m_icy_buf.clear ();
920                 m_icy_len = 0;
921                 m_icy_metaleft = m_icy_metaint;
922             }
923         }
924 
925         /* The maximum number of bytes we can deliver is determined
926          * by the number of bytes left until the next metadata announcement */
927         belem = aud::min ((int64_t) m_rb.len (), m_icy_metaleft) / size;
928     }
929 
930     nmemb = aud::min (belem, nmemb);
931     m_rb.move_out ((char *) ptr, nmemb * size);
932 
933     /* Signal the network thread to continue reading */
934     if (m_reader_status.status == NEON_READER_EOF)
935     {
936         if (! m_rb.len ())
937         {
938             AUDDBG ("<%p> stream EOF reached and buffer empty\n", this);
939             m_eof = true;
940         }
941     }
942     else
943         pthread_cond_broadcast (& m_reader_status.cond);
944 
945     pthread_mutex_unlock (& m_reader_status.mutex);
946 
947     m_pos += nmemb * size;
948     m_icy_metaleft -= nmemb * size;
949 
950     return nmemb;
951 }
952 
953 /* try_fread will do only a partial read if the buffer underruns, so we
954  * must call it repeatedly until we have read the full request. */
fread(void * buffer,int64_t size,int64_t count)955 int64_t NeonFile::fread (void * buffer, int64_t size, int64_t count)
956 {
957     int64_t total = 0;
958 
959     AUDDBG ("<%p> fread %d x %d\n", this, (int) size, (int) count);
960 
961     while (count > 0)
962     {
963         bool data_read = false;
964         int64_t part = try_fread (buffer, size, count, data_read);
965         if (! data_read)
966             break;
967 
968         buffer = (char *) buffer + size * part;
969         total += part;
970         count -= part;
971     }
972 
973     AUDDBG ("<%p> fread = %d\n", this, (int) total);
974 
975     return total;
976 }
977 
fwrite(const void * ptr,int64_t size,int64_t nmemb)978 int64_t NeonFile::fwrite (const void * ptr, int64_t size, int64_t nmemb)
979 {
980     AUDERR ("<%p> NOT IMPLEMENTED\n", this);
981 
982     return 0;
983 }
984 
ftell()985 int64_t NeonFile::ftell ()
986 {
987     AUDDBG ("<%p> Current file position: %" PRId64 "\n", this, m_pos);
988 
989     return m_pos;
990 }
991 
feof()992 bool NeonFile::feof ()
993 {
994     AUDDBG ("<%p> EOF status: %s\n", this, m_eof ? "true" : "false");
995 
996     return m_eof;
997 }
998 
ftruncate(int64_t size)999 int NeonFile::ftruncate (int64_t size)
1000 {
1001     AUDERR ("<%p> NOT IMPLEMENTED\n", this);
1002 
1003     return 0;
1004 }
1005 
fflush()1006 int NeonFile::fflush ()
1007 {
1008     return 0; /* no-op */
1009 }
1010 
fseek(int64_t offset,VFSSeekType whence)1011 int NeonFile::fseek (int64_t offset, VFSSeekType whence)
1012 {
1013     AUDDBG ("<%p> Seek requested: offset %" PRId64 ", whence %d\n", this, offset, whence);
1014 
1015     /* To seek to a non-zero offset, two things must be satisfied:
1016      * - the server must advertise a content-length
1017      * - the server must advertise accept-ranges: bytes */
1018     if ((whence != VFS_SEEK_SET || offset) && (m_content_length < 0 || ! m_can_ranges))
1019     {
1020         AUDDBG ("<%p> Can not seek due to server restrictions\n", this);
1021         return -1;
1022     }
1023 
1024     int64_t content_length = m_content_length + m_content_start;
1025     int64_t newpos;
1026 
1027     switch (whence)
1028     {
1029     case VFS_SEEK_SET:
1030         newpos = offset;
1031         break;
1032 
1033     case VFS_SEEK_CUR:
1034         newpos = m_pos + offset;
1035         break;
1036 
1037     case VFS_SEEK_END:
1038         if (offset == 0)
1039         {
1040             m_pos = content_length;
1041             m_eof = true;
1042             return 0;
1043         }
1044 
1045         newpos = content_length + offset;
1046         break;
1047 
1048     default:
1049         AUDERR ("<%p> Invalid whence specified\n", this);
1050         return -1;
1051     }
1052 
1053     AUDDBG ("<%p> Position to seek to: %" PRId64 ", current: %" PRId64 "\n", this, newpos, m_pos);
1054 
1055     if (newpos < 0)
1056     {
1057         AUDERR ("<%p> Can not seek before start of stream\n", this);
1058         return -1;
1059     }
1060 
1061     if (newpos && newpos >= content_length)
1062     {
1063         AUDERR ("<%p> Can not seek beyond end of stream (%" PRId64 " >= %"
1064          PRId64 "\n", this, newpos, content_length);
1065         return -1;
1066     }
1067 
1068     if (newpos == m_pos)
1069         return 0;
1070 
1071     /* To seek to the new position we have to
1072      * - stop the current reader thread, if there is one
1073      * - destroy the current request
1074      * - dump all data currently in the ringbuffer
1075      * - create a new request starting at newpos */
1076     if (m_reader_status.reading)
1077         kill_reader ();
1078 
1079     if (m_request)
1080     {
1081         ne_request_destroy (m_request);
1082         m_request = nullptr;
1083     }
1084 
1085     if (m_session)
1086     {
1087         ne_session_destroy (m_session);
1088         m_session = nullptr;
1089     }
1090 
1091     m_rb.discard ();
1092     m_icy_buf.clear ();
1093     m_icy_len = 0;
1094 
1095     if (open_handle (newpos) != 0)
1096     {
1097         AUDERR ("<%p> Error while creating new request!\n", this);
1098         return -1;
1099     }
1100 
1101     /* Things seem to have worked. The next read request will start
1102      * the reader thread again. */
1103     m_eof = false;
1104 
1105     return 0;
1106 }
1107 
get_metadata(const char * field)1108 String NeonFile::get_metadata (const char * field)
1109 {
1110     AUDDBG ("<%p> Field name: %s\n", this, field);
1111 
1112     if (! strcmp (field, "track-name") && m_icy_metadata.stream_title)
1113         return m_icy_metadata.stream_title;
1114 
1115     if (! strcmp (field, "stream-name") && m_icy_metadata.stream_name)
1116         return m_icy_metadata.stream_name;
1117 
1118     if (! strcmp (field, "content-type") && m_icy_metadata.stream_contenttype)
1119         return m_icy_metadata.stream_contenttype;
1120 
1121     if (! strcmp (field, "content-bitrate"))
1122         return String (int_to_str (m_icy_metadata.stream_bitrate * 1000));
1123 
1124     return String ();
1125 }
1126 
fsize()1127 int64_t NeonFile::fsize ()
1128 {
1129     if (m_content_length < 0)
1130     {
1131         AUDDBG ("<%p> Unknown content length\n", this);
1132         return -1;
1133     }
1134 
1135     return m_content_start + m_content_length;
1136 }
1137