diff --git a/INSTALL.md b/INSTALL.md
index 72425584..74942905 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -35,13 +35,14 @@ simultaneous streaming to multiple DAAP clients.
Optional packages:
- Feature | Configure argument | Packages
- --------------------|--------------------------|------------------------------------------------
- Chromecast | `--enable-chromecast` | libgnutls*-dev libprotobuf-c-dev
- Spotify | `--enable-spotify` | libspotify-dev
- Player web UI | `--disable-webinterface` | libwebsockets-dev
- Live web UI | `--with-libwebsockets` | libwebsockets-dev
- Pulseaudio | `--with-pulseaudio` | libpulse-dev
+ Feature | Configure argument | Packages
+ ---------------------|--------------------------|------------------------------------------------
+ Chromecast | `--enable-chromecast` | libgnutls*-dev libprotobuf-c-dev
+ Spotify (built-in) | `--disable-spotify` | libprotobuf-c-dev
+ Spotify (libspotify) | `--enable-libspotify` | libspotify-dev
+ Player web UI | `--disable-webinterface` | libwebsockets-dev
+ Live web UI | `--with-libwebsockets` | libwebsockets-dev
+ Pulseaudio | `--with-pulseaudio` | libpulse-dev
Then run the following (adding configure arguments for optional features):
@@ -266,7 +267,7 @@ Libraries:
- libpulse (optional - Pulseaudio local audio)
from
- libspotify (optional - Spotify support)
- from
+ (deprecated by Spotify)
- libgnutls (optional - Chromecast support)
from
- libprotobuf-c (optional - Chromecast support)
@@ -310,11 +311,16 @@ needed.
To display the configure options `run ./configure --help`.
-Support for Spotify is optional. Use `--enable-spotify` to enable this feature.
-If you enable this feature `libspotify/api.h` is required at compile time.
-Forked-daapd uses runtime dynamic linking to the libspotify library, so even
-though you compiled with `--enable-spotify`, the executable will still be able
-to run on systems without libspotify (the Spotify features will then be disabled).
+Support for Spotify is optional. Use `--disable-spotify` to disable this feature.
+OwnTone supports two ways of integrating with Spotify: Using its own, built-in
+integration layer (which is the default), or to use Spotify's deprecated
+libspotify. To enable the latter, you must configure with `--enable-libspotify`
+and also make sure libspotify's `libspotify/api.h` is installed at compile time.
+At runtime, libspotify must be installed, and `use_libspotify` must be enabled
+in owntone.conf. OwnTone uses runtime dynamic linking to the libspotify library,
+so even though you compiled with `--enable-libspotify`, the executable will
+still be able to run on systems without libspotify. If you only want libspotify
+integration, you can use `--disable-spotify` and `--enable-libspotify`.
Support for LastFM scrobbling is optional. Use `--enable-lastfm` to enable this
feature.
diff --git a/Makefile.am b/Makefile.am
index 6a219d6f..b9c3b3bc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,7 @@
+if COND_LIBRESPOTC
+LIBRESPOTC_SUBDIR=src/inputs/librespot-c
+endif
+
ACLOCAL_AMFLAGS = -I m4
RPM_SPEC_FILE = owntone.spec
@@ -8,7 +12,7 @@ sysconf_DATA = $(CONF_FILE)
BUILT_SOURCES = $(CONF_FILE) $(SYSTEMD_SERVICE_FILE)
-SUBDIRS = sqlext src htdocs
+SUBDIRS = $(LIBRESPOTC_SUBDIR) sqlext src htdocs
dist_man_MANS = owntone.8
diff --git a/README.md b/README.md
index 808569c2..a1f83039 100644
--- a/README.md
+++ b/README.md
@@ -473,44 +473,30 @@ curl "http://localhost:3689/logout?session-id=50"
## Spotify
-### Via libspotify
+OwnTone has built-in support for playback of the tracks in your Spotify library.
-OwnTone has support for playback of the tracks in your Spotify library.
+You must have a Spotify premium account. If you normally log into Spotify with
+your Facebook account you must first go to Spotify's web site where you can get
+the Spotify username and password that matches your account.
-1. Go to the [web interface](http://owntone.local:3689) and check that your
- version of OwnTone was built with Spotify support.
-2. You must have a Spotify premium account. If you normally log into Spotify
- with your Facebook account you must first go to Spotify's web site where you
- can get the Spotify username and password that matches your account.
-3. Make sure you have `libspotify` installed. Unfortunately, it is no longer
- available from Spotify, and at the time of writing this they have not
- provided an alternative. However, on most Debian-based platforms, you can
- still get it like this:
- - Add the mopidy repository, see [instructions](https://apt.mopidy.com)
- - Install with `apt install libspotify-dev`
+You must also make sure that your browser can reach OwnTone's web interface via
+the address [http://owntone.local:3689](http://owntone.local:3689). Try it right
+now! That is where Spotify's OAuth page will redirect your browser with the
+token that OwnTone needs, so it must work. The address is announced by the
+server via mDNS, but if that for some reason doesn't work then configure it via
+router or .hosts file. You can remove it again after completing the login.
-Once the above is in order you can login to Spotify via the web interface. The
-procedure for logging in to Spotify is a two-step procedure due to the current
-state of libspotify, but the web interface makes both steps available to you.
+To authorize OwnTone, open the web interface, locate Settings > Online Services
+and then click the Authorize button. You will then be sent to Spotify's
+authorization service, which will send you back to the web interface after
+you have given the authorization.
-Note that the address [http://owntone.local:3689](http://owntone.local:3689)
-must be working on your local network to complete the Spotify OAuth web login.
-The address is announced automatically via mDNS, but if that for some reason
-doesn't work then configure it via router or .hosts file. You can remove it
-again after completing the login. This is needed because the redirect_uri
-parameter of the Spotify token request is to this address.
-
-Spotify no longer automatically notifies clients about playlist updates, so you
+Spotify no longer automatically notifies clients about library updates, so you
have to trigger updates manually. You can for instance set up a cron job that
runs `/usr/bin/curl http://localhost:3689/api/update`
-OwnTone will not store your password, but will still be able to log you in
-automatically afterwards, because libspotify saves a login token. You can
-configure the location of your Spotify user data in the configuration file.
-
-To permanently logout and remove Spotify tracks + credentials make a request to
-[http://[your_server_address_here]:3689/api/spotify-logout](http://[your_server_address_here]:3689/api/spotify-logout)
-and also delete the contents of `/var/cache/owntone/libspotify`.
+To logout and remove Spotify tracks + credentials make a request to
+[http://[your_server_address_here]:3689/api/spotify-logout](http://[your_server_address_here]:3689/api/spotify-logout).
Limitations:
You will not be able to do any playlist management through OwnTone - use
@@ -540,6 +526,13 @@ pipe = "/srv/music/spotify"
metadataPipe = "/srv/music/spotify.metadata"
```
+### Via libspotify
+
+This method is being deprecated, but is still available if the server was built
+with it, libspotify is installed and `use_libspotify` is enabled in the config
+file. Please consult [previous README versions](#references) for details on
+using libspotify.
+
## LastFM
diff --git a/configure.ac b/configure.ac
index f7947cd7..8732fd92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -286,21 +286,34 @@ AS_IF([[test "x$with_avahi" = "xno"]],
[AC_MSG_ERROR([[Avahi client or Bonjour DNS_SD required, please install one.]])])])
AM_CONDITIONAL([COND_AVAHI], [[test "x$with_avahi" = "xyes"]])
-dnl Spotify with dynamic linking to libspotify
-OWNTONE_ARG_ENABLE([Spotify support], [spotify], [SPOTIFY],
+dnl Spotify support
+OWNTONE_ARG_DISABLE([Spotify support], [spotify], [SPOTIFY_LIBRESPOTC],
+ [OWNTONE_MODULES_CHECK([OWNTONE_OPTS], [LIBPROTOBUF_C],
+ [libprotobuf-c >= 1.0.0], [protobuf_c_message_pack],
+ [protobuf-c/protobuf-c.h])
+ OWNTONE_VAR_PREPEND([OWNTONE_OPTS_LIBS], [inputs/librespot-c/librespot-c.a])
+ AC_DEFINE([SPOTIFY], 1,
+ [Define to 1 to enable Spotify])
+ ])
+AM_CONDITIONAL([COND_LIBRESPOTC], [[test "x$enable_spotify" = "xyes"]])
+
+dnl Spotify with dynamic linking to libspotify (legacy)
+OWNTONE_ARG_ENABLE([legacy libspotify support], [libspotify], [SPOTIFY_LIBSPOTIFY],
[AS_IF([[test "x$with_libevent_pthreads" = "xno"]],
- [AC_MSG_ERROR([[Spotify support requires libevent_pthreads]])])
- OWNTONE_MODULES_CHECK([SPOTIFY], [LIBSPOTIFY], [libspotify],
+ [AC_MSG_ERROR([[libspotify support requires libevent_pthreads]])])
+ OWNTONE_MODULES_CHECK([SPOTIFY_LIBSPOTIFY], [LIBSPOTIFY], [libspotify],
[], [libspotify/api.h])
- AC_DEFINE([HAVE_SPOTIFY_H], 1,
- [Define to 1 if you have the header file.])
dnl Don't link with libspotify, use dynamic linking
AC_SEARCH_LIBS([dlopen], [dl], [],
- [AC_MSG_ERROR([[Spotify support requires dlopen]])])
- OWNTONE_VAR_PREPEND([OWNTONE_OPTS_CPPFLAGS], [$SPOTIFY_CPPFLAGS])
+ [AC_MSG_ERROR([[libspotify support requires dlopen]])])
+ OWNTONE_VAR_PREPEND([OWNTONE_OPTS_CPPFLAGS], [$SPOTIFY_LIBSPOTIFY_CPPFLAGS])
OWNTONE_VAR_PREPEND([OWNTONE_OPTS_LIBS], [-rdynamic])
+ AC_DEFINE([SPOTIFY], 1,
+ [Define to 1 to enable Spotify])
])
-AM_CONDITIONAL([COND_SPOTIFY], [[test "x$enable_spotify" = "xyes"]])
+AM_CONDITIONAL([COND_LIBSPOTIFY], [[test "x$enable_libspotify" = "xyes"]])
+
+AM_CONDITIONAL([COND_SPOTIFY], [[test "x$enable_spotify" = "xyes" -o "x$enable_libspotify" = "xyes"]])
dnl LastFM support
OWNTONE_ARG_DISABLE([LastFM support], [lastfm], [LASTFM])
@@ -359,6 +372,11 @@ OWNTONE_GROUP=${withval:-$OWNTONE_USER}
AC_SUBST([OWNTONE_GROUP])
dnl --- End options ---
+dnl Unconditional since we always want to produce Makefiles for dist targets
+AC_CONFIG_SUBDIRS([
+ src/inputs/librespot-c
+])
+
AC_CONFIG_FILES([
src/Makefile
sqlext/Makefile
diff --git a/owntone.conf.in b/owntone.conf.in
index 16c29fd2..9ac4de20 100644
--- a/owntone.conf.in
+++ b/owntone.conf.in
@@ -346,10 +346,16 @@ audio {
# Spotify settings (only have effect if Spotify enabled - see README/INSTALL)
spotify {
+ # The server can stream from Spotify using either its own implementation
+ # or using Spotify's libspotify (which was deprecated many years ago)
+# use_libspotify = false
+
# Directory where user settings should be stored (credentials)
+ # (only has effect with libspotify)
# settings_dir = "@localstatedir@/cache/@PACKAGE@/libspotify"
# Cache directory
+ # (only has effect with libspotify)
# cache_dir = "/tmp"
# Set preferred bitrate for music streaming
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ea89e21..05ace749 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,7 +2,17 @@
sbin_PROGRAMS = owntone
if COND_SPOTIFY
-SPOTIFY_SRC=spotify.c spotify.h spotify_webapi.c spotify_webapi.h inputs/spotify.c
+SPOTIFY_SRC = \
+ library/spotify_webapi.c library/spotify_webapi.h inputs/spotify.c inputs/spotify.h
+endif
+if COND_LIBRESPOTC
+LIBRESPOTC_SRC = \
+ inputs/spotify_librespotc.c
+endif
+if COND_LIBSPOTIFY
+LIBSPOTIFY_SRC = \
+ inputs/spotify_libspotify.c \
+ inputs/libspotify/libspotify.c inputs/libspotify/libspotify.h
endif
if COND_LASTFM
@@ -104,7 +114,6 @@ owntone_SOURCES = main.c \
library.c library.h \
$(MDNS_SRC) mdns.h \
remote_pairing.c remote_pairing.h \
- avio_evbuffer.c avio_evbuffer.h \
httpd.c httpd.h \
httpd_rsp.c httpd_rsp.h \
httpd_daap.c httpd_daap.h \
@@ -134,7 +143,7 @@ owntone_SOURCES = main.c \
outputs/streaming.c outputs/dummy.c outputs/fifo.c \
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
- $(SPOTIFY_SRC) \
+ $(SPOTIFY_SRC) $(LIBRESPOTC_SRC) $(LIBSPOTIFY_SRC) \
$(LASTFM_SRC) \
$(MPD_SRC) \
listener.c listener.h \
diff --git a/src/artwork.c b/src/artwork.c
index 627e727e..9bbb236b 100644
--- a/src/artwork.c
+++ b/src/artwork.c
@@ -45,8 +45,8 @@
#include "artwork.h"
-#ifdef HAVE_SPOTIFY_H
-# include "spotify_webapi.h"
+#ifdef SPOTIFY
+# include "library/spotify_webapi.h"
#endif
/* This artwork module will look for artwork by consulting a set of sources one
@@ -605,6 +605,7 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
{
struct decode_ctx *xcode_decode = NULL;
struct encode_ctx *xcode_encode = NULL;
+ struct transcode_evbuf_io xcode_evbuf_io = { 0 };
struct evbuffer *xcode_buf = NULL;
void *frame;
int src_width;
@@ -633,9 +634,15 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
ret = ART_E_ERROR;
goto out;
}
+
+ xcode_evbuf_io.evbuf = xcode_buf;
+ xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, NULL, &xcode_evbuf_io, 0); // Covers XCODE_PNG too
+ }
+ else
+ {
+ xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, path, NULL, 0); // Covers XCODE_PNG too
}
- xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, path, xcode_buf, 0); // Covers XCODE_PNG too
if (!xcode_decode)
{
if (path)
@@ -1608,7 +1615,7 @@ source_item_coverartarchive_get(struct artwork_ctx *ctx)
return ret;
}
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
static int
source_item_spotifywebapi_track_get(struct artwork_ctx *ctx)
{
diff --git a/src/avio_evbuffer.c b/src/avio_evbuffer.c
deleted file mode 100644
index 67ce21f2..00000000
--- a/src/avio_evbuffer.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 Julien BLACHE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include
-#endif
-
-#include
-
-#include
-
-#include "logger.h"
-#include "avio_evbuffer.h"
-
-/*
- * libav AVIO interface for evbuffers
- */
-
-#define BUFFER_SIZE 4096
-
-struct avio_evbuffer {
- struct evbuffer *evbuf;
- uint8_t *buffer;
-};
-
-static int
-avio_evbuffer_read(void *opaque, uint8_t *buf, int size)
-{
- struct avio_evbuffer *ae;
- int ret;
-
- ae = (struct avio_evbuffer *)opaque;
-
- ret = evbuffer_remove(ae->evbuf, buf, size);
-
- // Must return AVERROR, see avio.h: avio_alloc_context()
- return (ret > 0) ? ret : AVERROR_EOF;
-}
-
-static int
-avio_evbuffer_write(void *opaque, uint8_t *buf, int size)
-{
- struct avio_evbuffer *ae;
- int ret;
-
- ae = (struct avio_evbuffer *)opaque;
-
- ret = evbuffer_add(ae->evbuf, buf, size);
-
- return (ret == 0) ? size : -1;
-}
-
-static AVIOContext *
-avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
-{
- struct avio_evbuffer *ae;
- AVIOContext *s;
-
- ae = (struct avio_evbuffer *)malloc(sizeof(struct avio_evbuffer));
- if (!ae)
- {
- DPRINTF(E_LOG, L_FFMPEG, "Out of memory for avio_evbuffer\n");
-
- return NULL;
- }
-
- ae->buffer = av_mallocz(BUFFER_SIZE);
- if (!ae->buffer)
- {
- DPRINTF(E_LOG, L_FFMPEG, "Out of memory for avio buffer\n");
-
- free(ae);
- return NULL;
- }
-
- ae->evbuf = evbuf;
-
- if (is_output)
- s = avio_alloc_context(ae->buffer, BUFFER_SIZE, 1, ae, NULL, avio_evbuffer_write, NULL);
- else
- s = avio_alloc_context(ae->buffer, BUFFER_SIZE, 0, ae, avio_evbuffer_read, NULL, NULL);
-
- if (!s)
- {
- DPRINTF(E_LOG, L_FFMPEG, "Could not allocate AVIOContext\n");
-
- av_free(ae->buffer);
- free(ae);
- return NULL;
- }
-
- s->seekable = 0;
-
- return s;
-}
-
-AVIOContext *
-avio_input_evbuffer_open(struct evbuffer *evbuf)
-{
- return avio_evbuffer_open(evbuf, 0);
-}
-
-AVIOContext *
-avio_output_evbuffer_open(struct evbuffer *evbuf)
-{
- return avio_evbuffer_open(evbuf, 1);
-}
-
-void
-avio_evbuffer_close(AVIOContext *s)
-{
- struct avio_evbuffer *ae;
-
- if (!s)
- return;
-
- ae = (struct avio_evbuffer *)s->opaque;
-
- avio_flush(s);
-
- av_free(s->buffer);
- free(ae);
-
- av_free(s);
-}
diff --git a/src/avio_evbuffer.h b/src/avio_evbuffer.h
deleted file mode 100644
index 93dc1234..00000000
--- a/src/avio_evbuffer.h
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#ifndef __AVIO_EVBUFFER_H__
-#define __AVIO_EVBUFFER_H__
-
-#include
-
-AVIOContext *
-avio_input_evbuffer_open(struct evbuffer *evbuf);
-
-AVIOContext *
-avio_output_evbuffer_open(struct evbuffer *evbuf);
-
-void
-avio_evbuffer_close(AVIOContext *s);
-
-#endif /* !__AVIO_EVBUFFER_H__ */
diff --git a/src/conffile.c b/src/conffile.c
index 41a13fcd..d41710e0 100644
--- a/src/conffile.c
+++ b/src/conffile.c
@@ -188,6 +188,7 @@ static cfg_opt_t sec_fifo[] =
/* Spotify section structure */
static cfg_opt_t sec_spotify[] =
{
+ CFG_BOOL("use_libspotify", cfg_false, CFGF_NONE),
CFG_STR("settings_dir", STATEDIR "/cache/" PACKAGE "/libspotify", CFGF_NONE),
CFG_STR("cache_dir", "/tmp", CFGF_NONE),
CFG_INT("bitrate", 0, CFGF_NONE),
diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c
index c94802a3..5eb5e58a 100644
--- a/src/httpd_jsonapi.c
+++ b/src/httpd_jsonapi.c
@@ -55,9 +55,9 @@
#include "remote_pairing.h"
#include "settings.h"
#include "smartpl_query.h"
-#ifdef HAVE_SPOTIFY_H
-# include "spotify_webapi.h"
-# include "spotify.h"
+#ifdef SPOTIFY
+# include "library/spotify_webapi.h"
+# include "inputs/spotify.h"
#endif
@@ -1201,11 +1201,11 @@ jsonapi_reply_spotify(struct httpd_request *hreq)
CHECK_NULL(L_WEB, jreply = json_object_new_object());
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
int httpd_port;
char redirect_uri[256];
char *oauth_uri;
- struct spotify_status_info info;
+ struct spotify_status sp_status;
struct spotifywebapi_status_info webapi_info;
struct spotifywebapi_access_token webapi_token;
@@ -1225,10 +1225,10 @@ jsonapi_reply_spotify(struct httpd_request *hreq)
json_object_object_add(jreply, "oauth_uri", json_object_new_string(oauth_uri));
free(oauth_uri);
- spotify_status_info_get(&info);
- json_object_object_add(jreply, "libspotify_installed", json_object_new_boolean(info.libspotify_installed));
- json_object_object_add(jreply, "libspotify_logged_in", json_object_new_boolean(info.libspotify_logged_in));
- safe_json_add_string(jreply, "libspotify_user", info.libspotify_user);
+ spotify_status_get(&sp_status);
+ json_object_object_add(jreply, "libspotify_installed", json_object_new_boolean(sp_status.installed));
+ json_object_object_add(jreply, "libspotify_logged_in", json_object_new_boolean(sp_status.logged_in));
+ safe_json_add_string(jreply, "libspotify_user", sp_status.username);
spotifywebapi_status_info_get(&webapi_info);
json_object_object_add(jreply, "webapi_token_valid", json_object_new_boolean(webapi_info.token_valid));
@@ -1255,12 +1255,12 @@ jsonapi_reply_spotify(struct httpd_request *hreq)
static int
jsonapi_reply_spotify_login(struct httpd_request *hreq)
{
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
struct evbuffer *in_evbuf;
json_object* request;
const char *user;
const char *password;
- char *errmsg = NULL;
+ const char *errmsg;
json_object* jreply;
json_object* errors;
int ret;
@@ -1282,7 +1282,7 @@ jsonapi_reply_spotify_login(struct httpd_request *hreq)
password = jparse_str_from_obj(request, "password");
if (user && strlen(user) > 0 && password && strlen(password) > 0)
{
- ret = spotify_login_user(user, password, &errmsg);
+ ret = spotify_login(user, password, &errmsg);
if (ret < 0)
{
json_object_object_add(jreply, "success", json_object_new_boolean(false));
@@ -1294,7 +1294,6 @@ jsonapi_reply_spotify_login(struct httpd_request *hreq)
{
json_object_object_add(jreply, "success", json_object_new_boolean(true));
}
- free(errmsg);
}
else
{
@@ -1323,7 +1322,8 @@ jsonapi_reply_spotify_login(struct httpd_request *hreq)
static int
jsonapi_reply_spotify_logout(struct httpd_request *hreq)
{
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
+ spotifywebapi_purge();
spotify_logout();
#endif
return HTTP_NOCONTENT;
diff --git a/src/httpd_oauth.c b/src/httpd_oauth.c
index e5a04f8a..46d8b27a 100644
--- a/src/httpd_oauth.c
+++ b/src/httpd_oauth.c
@@ -32,19 +32,19 @@
#include "logger.h"
#include "misc.h"
#include "conffile.h"
-#ifdef HAVE_SPOTIFY_H
-# include "spotify_webapi.h"
+#ifdef SPOTIFY
+# include "library/spotify_webapi.h"
#endif
/* --------------------------- REPLY HANDLERS ------------------------------- */
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
static int
oauth_reply_spotify(struct httpd_request *hreq)
{
char redirect_uri[256];
- char *errmsg;
+ const char *errmsg;
int httpd_port;
int ret;
@@ -56,7 +56,6 @@ oauth_reply_spotify(struct httpd_request *hreq)
{
DPRINTF(E_LOG, L_WEB, "Could not parse Spotify OAuth callback: '%s'\n", hreq->uri_parsed->uri);
httpd_send_error(hreq->req, HTTP_INTERNAL, errmsg);
- free(errmsg);
return -1;
}
diff --git a/src/input.c b/src/input.c
index d6425f1c..34974917 100644
--- a/src/input.c
+++ b/src/input.c
@@ -58,9 +58,12 @@ extern struct input_definition input_file;
extern struct input_definition input_http;
extern struct input_definition input_pipe;
extern struct input_definition input_timer;
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY_LIBRESPOTC
extern struct input_definition input_spotify;
#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+extern struct input_definition input_libspotify;
+#endif
// Must be in sync with enum input_types
static struct input_definition *inputs[] = {
@@ -68,8 +71,11 @@ static struct input_definition *inputs[] = {
&input_http,
&input_pipe,
&input_timer,
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY_LIBRESPOTC
&input_spotify,
+#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+ &input_libspotify,
#endif
NULL
};
@@ -171,10 +177,16 @@ map_data_kind(int data_kind)
case DATA_KIND_PIPE:
return INPUT_TYPE_PIPE;
-#ifdef HAVE_SPOTIFY_H
case DATA_KIND_SPOTIFY:
- return INPUT_TYPE_SPOTIFY;
+#ifdef SPOTIFY_LIBRESPOTC
+ if (!inputs[INPUT_TYPE_SPOTIFY]->disabled)
+ return INPUT_TYPE_SPOTIFY;
#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+ if (!inputs[INPUT_TYPE_LIBSPOTIFY]->disabled)
+ return INPUT_TYPE_LIBSPOTIFY;
+#endif
+ return -1;
default:
return -1;
@@ -420,6 +432,7 @@ setup(struct input_source *source, struct db_queue_item *queue_item, int seek_ms
source->id = queue_item->file_id;
source->len_ms = queue_item->song_length;
source->path = safe_strdup(queue_item->path);
+ source->evbase = evbase_input;
DPRINTF(E_DBG, L_PLAYER, "Setting up input item '%s' (item id %" PRIu32 ")\n", source->path, source->item_id);
@@ -457,6 +470,8 @@ start(void *arg, int *retval)
struct db_queue_item *queue_item;
int ret;
+ DPRINTF(E_WARN, L_PLAYER, "now %d, item_id %d, now item_id %d\n", input_now_reading.open, cmdarg->item_id, input_now_reading.item_id);
+
// If we are asked to start the item that is currently open we can just seek
if (input_now_reading.open && cmdarg->item_id == input_now_reading.item_id)
{
@@ -768,7 +783,7 @@ input_read(void *data, size_t size, short *flag, void **flagdata)
if (*flag || (debug_elapsed > 10 * one_sec_size))
{
debug_elapsed = 0;
- DPRINTF(E_DBG, L_PLAYER, "READ %zu bytes (%d/%d/%d), WROTE %zu bytes (%d/%d/%d), SIZE %zu (=%zu), FLAGS %04x\n",
+ DPRINTF(E_DBG, L_PLAYER, "READ %zu bytes (%d/%d/%d), WROTE %zu bytes (%d/%d/%d), DIFF %zu, SIZE %zu/%d, FLAGS %04x\n",
input_buffer.bytes_read,
input_buffer.cur_read_quality.sample_rate,
input_buffer.cur_read_quality.bits_per_sample,
@@ -777,8 +792,9 @@ input_read(void *data, size_t size, short *flag, void **flagdata)
input_buffer.cur_write_quality.sample_rate,
input_buffer.cur_write_quality.bits_per_sample,
input_buffer.cur_write_quality.channels,
- evbuffer_get_length(input_buffer.evbuf),
input_buffer.bytes_written - input_buffer.bytes_read,
+ evbuffer_get_length(input_buffer.evbuf),
+ INPUT_BUFFER_THRESHOLD,
*flag);
}
#endif
diff --git a/src/input.h b/src/input.h
index c50425f7..1e0c2821 100644
--- a/src/input.h
+++ b/src/input.h
@@ -16,9 +16,12 @@ enum input_types
INPUT_TYPE_HTTP,
INPUT_TYPE_PIPE,
INPUT_TYPE_TIMER,
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY_LIBRESPOTC
INPUT_TYPE_SPOTIFY,
#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+ INPUT_TYPE_LIBSPOTIFY,
+#endif
};
enum input_flags
@@ -62,6 +65,10 @@ struct input_source
// Opaque pointer to data that the input backend sets up when start() is
// called, and that is cleaned up by the backend when stop() is called
void *input_ctx;
+
+ // The input's event base that the input backend can use for own events
+ struct event_base *evbase;
+
// Private evbuf. Alloc'ed by backend at start() and free'd at stop()
struct evbuffer *evbuf;
// Private source quality storage
@@ -128,7 +135,7 @@ struct input_definition
/* ---------------------- Interface towards input backends ------------------ */
-/* Thread: input and spotify */
+/* Thread: input */
/*
* Transfer stream data to the player's input buffer. Data must be PCM-LE
diff --git a/src/inputs/librespot-c/.gitignore b/src/inputs/librespot-c/.gitignore
new file mode 100644
index 00000000..c777bee4
--- /dev/null
+++ b/src/inputs/librespot-c/.gitignore
@@ -0,0 +1,38 @@
+*~
+*.swp
+Makefile.in
+Makefile
+*.o
+*.lo
+*.a
+*.la
+.dirstamp
+.deps/
+.libs/
+
+# autofoo stuff
+autom4te.cache
+aclocal.m4
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+autotools-stamp
+build-stamp
+ar-lib
+
+/.settings
+/.cproject
+/.project
+/.autotools
+/.vscode
diff --git a/src/inputs/librespot-c/LICENSE b/src/inputs/librespot-c/LICENSE
new file mode 100644
index 00000000..9cf10627
--- /dev/null
+++ b/src/inputs/librespot-c/LICENSE
@@ -0,0 +1,19 @@
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/inputs/librespot-c/Makefile.am b/src/inputs/librespot-c/Makefile.am
new file mode 100644
index 00000000..08106ab9
--- /dev/null
+++ b/src/inputs/librespot-c/Makefile.am
@@ -0,0 +1,26 @@
+SUBDIRS = tests
+
+noinst_LIBRARIES = librespot-c.a
+
+SHANNON_SRC = \
+ src/shannon/ShannonFast.c src/shannon/Shannon.h src/shannon/ShannonInternal.h
+
+PROTO_SRC = \
+ src/proto/keyexchange.pb-c.c src/proto/keyexchange.pb-c.h \
+ src/proto/authentication.pb-c.c src/proto/authentication.pb-c.h \
+ src/proto/mercury.pb-c.c src/proto/mercury.pb-c.h \
+ src/proto/metadata.pb-c.c src/proto/metadata.pb-c.h
+
+CORE_SRC = \
+ src/librespot-c.c src/connection.c src/channel.c src/crypto.c src/commands.c
+
+librespot_c_a_SOURCES = \
+ $(CORE_SRC) \
+ $(SHANNON_SRC) \
+ $(PROTO_SRC)
+
+noinst_HEADERS = \
+ librespot-c.h src/librespot-c-internal.h src/connection.h \
+ src/channel.h src/crypto.h src/commands.h
+
+EXTRA_DIST = README.md LICENSE
diff --git a/src/inputs/librespot-c/README.md b/src/inputs/librespot-c/README.md
new file mode 100644
index 00000000..330d55ef
--- /dev/null
+++ b/src/inputs/librespot-c/README.md
@@ -0,0 +1,13 @@
+Build:
+- autoreconf -i && ./configure && make
+
+Test:
+- make check
+- ./tests/test1
+
+Dependencies:
+- libevent-dev libgcrypt20-dev libcurl4-gnutls-dev libjson-c-dev libprotobuf-c-dev
+
+Credits:
+- librespot (https://github.com/librespot-org/librespot)
+- timniederhausen for Shannon cipher (https://github.com/timniederhausen/shannon)
diff --git a/src/inputs/librespot-c/configure.ac b/src/inputs/librespot-c/configure.ac
new file mode 100644
index 00000000..96d82f03
--- /dev/null
+++ b/src/inputs/librespot-c/configure.ac
@@ -0,0 +1,22 @@
+AC_INIT([librespot-c], [0.1])
+AC_CONFIG_AUX_DIR([.])
+AM_INIT_AUTOMAKE([foreign subdir-objects])
+AC_PROG_CC
+AM_PROG_AR
+AC_PROG_RANLIB
+
+AC_CHECK_HEADERS_ONCE([endian.h sys/endian.h])
+AC_CHECK_DECL([htobe16], [],
+ [AC_CHECK_HEADERS([libkern/OSByteOrder.h], [], [AC_MSG_ERROR([[Missing functions to swap byte order]])])],
+)
+
+AC_SEARCH_LIBS([pthread_exit], [pthread], [], [AC_MSG_ERROR([[pthreads library is required]])])
+
+PKG_CHECK_MODULES([LIBEVENT], [libevent])
+PKG_CHECK_MODULES([JSON_C], [json-c])
+PKG_CHECK_MODULES([LIBGCRYPT], [libgcrypt])
+PKG_CHECK_MODULES([LIBCURL], [libcurl])
+PKG_CHECK_MODULES([LIBPROTOBUF_C], [libprotobuf-c])
+
+AC_CONFIG_FILES([Makefile tests/Makefile])
+AC_OUTPUT
diff --git a/src/inputs/librespot-c/librespot-c.h b/src/inputs/librespot-c/librespot-c.h
new file mode 100644
index 00000000..eb3d91c8
--- /dev/null
+++ b/src/inputs/librespot-c/librespot-c.h
@@ -0,0 +1,116 @@
+#ifndef __LIBRESPOT_C_H__
+#define __LIBRESPOT_C_H__
+
+#include
+#include
+#include
+
+#define LIBRESPOT_C_VERSION_MAJOR 0
+#define LIBRESPOT_C_VERSION_MINOR 1
+
+
+struct sp_session;
+
+enum sp_bitrates
+{
+ SP_BITRATE_ANY,
+ SP_BITRATE_96,
+ SP_BITRATE_160,
+ SP_BITRATE_320,
+};
+
+typedef void (*sp_progress_cb)(int fd, void *arg, size_t received, size_t len);
+
+struct sp_credentials
+{
+ char username[64];
+ char password[32];
+
+ uint8_t stored_cred[256]; // Actual size is 146, but leave room for some more
+ size_t stored_cred_len;
+ uint8_t token[256]; // Actual size is ?
+ size_t token_len;
+};
+
+struct sp_metadata
+{
+ size_t file_len;
+};
+
+struct sp_sysinfo
+{
+ char client_name[16];
+ char client_version[16];
+ char client_build_id[16];
+ char device_id[41]; // librespot gives a 20 byte id (so 40 char hex + 1 zero term)
+};
+
+struct sp_callbacks
+{
+ // Bring your own https client and tcp connector
+ int (*https_get)(char **body, const char *url);
+ int (*tcp_connect)(const char *address, unsigned short port);
+ void (*tcp_disconnect)(int fd);
+
+ // Optional - set name of thread
+ void (*thread_name_set)(pthread_t thread);
+
+ // Debugging
+ void (*hexdump)(const char *msg, uint8_t *data, size_t data_len);
+ void (*logmsg)(const char *fmt, ...);
+};
+
+
+
+struct sp_session *
+librespotc_login_password(const char *username, const char *password);
+
+struct sp_session *
+librespotc_login_stored_cred(const char *username, uint8_t *stored_cred, size_t stored_cred_len);
+
+struct sp_session *
+librespotc_login_token(const char *username, const char *token);
+
+int
+librespotc_logout(struct sp_session *session);
+
+int
+librespotc_bitrate_set(struct sp_session *session, enum sp_bitrates bitrate);
+
+int
+librespotc_credentials_get(struct sp_credentials *credentials, struct sp_session *session);
+
+// Returns a file descriptor (in non-blocking mode) from which caller can read
+// one chunk of data. To get more data written/start playback loop, call
+// librespotc_play().
+int
+librespotc_open(const char *path, struct sp_session *session);
+
+// Continues writing data to the file descriptor until error or end of track.
+// A read of the fd that returns 0 means end of track, and a negative read
+// return value means error. progress_cb and cb_arg optional.
+void
+librespotc_write(int fd, sp_progress_cb progress_cb, void *cb_arg);
+
+// Seeks to pos (measured in bytes, so must not exceed file_len), flushes old
+// data from the fd and prepares one chunk of data for reading.
+int
+librespotc_seek(int fd, size_t pos);
+
+// Closes a track download, incl. the fd.
+int
+librespotc_close(int fd);
+
+int
+librespotc_metadata_get(struct sp_metadata *metadata, int fd);
+
+const char *
+librespotc_last_errmsg(void);
+
+int
+librespotc_init(struct sp_sysinfo *sysinfo, struct sp_callbacks *callbacks);
+
+void
+librespotc_deinit(void);
+
+#endif /* !__LIBRESPOT_C_H__ */
diff --git a/src/inputs/librespot-c/src/channel.c b/src/inputs/librespot-c/src/channel.c
new file mode 100644
index 00000000..adf048be
--- /dev/null
+++ b/src/inputs/librespot-c/src/channel.c
@@ -0,0 +1,446 @@
+#include
+
+#include "librespot-c-internal.h"
+
+/* -------------------------------- Channels -------------------------------- */
+
+/*
+Here is my current understanding of the channel concept:
+
+1. A channel is established for retrieving chunks of audio. A channel is not a
+ separate connection, all the traffic goes via the same Shannon-encrypted tcp
+ connection as the rest.
+2. It depends on the cmd whether a channel is used. CmdStreamChunk,
+ CmdStreamChunkRes, CmdChannelError, CmdChannelAbort use channels. A channel
+ is identified with a uint16_t, which is the first 2 bytes of these packets.
+3. A channel is established with CmdStreamChunk where receiver picks channel id.
+ Spotify responds with CmdStreamChunkRes that initially has some headers after
+ the channel id. The headers are "reverse tlv": uint16_t header length,
+ uint8_t header id, uint8_t header_data[]. The length includes the id length.
+4. After the headers are sent the channel switches to data mode. This is
+ signalled by a header length of 0. In data mode Spotify sends the requested
+ chunks of audio (CmdStreamChunkRes) which have the audio right after the
+ channel id prefix. The audio is AES encrypted with a per-file key. An empty
+ CmdStreamChunkRes indicates the end. The caller can then make a new
+ CmdStreamChunk requesting the next data.
+5. For Ogg, the first 167 bytes of audio is a special Spotify header.
+6. The channel can presumably be reset with CmdChannelAbort (?)
+*/
+
+static int
+path_to_media_id_and_type(struct sp_file *file)
+{
+ char *ptr;
+
+ file->media_type = SP_MEDIA_UNKNOWN;
+ if (strstr(file->path, ":track:"))
+ file->media_type = SP_MEDIA_TRACK;
+ else if (strstr(file->path, ":episode:"))
+ file->media_type = SP_MEDIA_EPISODE;
+ else
+ return -1;
+
+ ptr = strrchr(file->path, ':');
+ if (!ptr || strlen(ptr + 1) != 22)
+ return -1;
+
+ return crypto_base62_to_bin(file->media_id, sizeof(file->media_id), ptr + 1);
+}
+
+struct sp_channel *
+channel_get(uint32_t channel_id, struct sp_session *session)
+{
+ if (channel_id > sizeof(session->channels)/sizeof(session->channels)[0])
+ return NULL;
+
+ if (!session->channels[channel_id].is_allocated)
+ return NULL;
+
+ return &session->channels[channel_id];
+}
+
+void
+channel_free(struct sp_channel *channel)
+{
+ if (!channel || !channel->is_allocated)
+ return;
+
+ if (channel->audio_buf)
+ evbuffer_free(channel->audio_buf);
+
+ if (channel->audio_write_ev)
+ event_free(channel->audio_write_ev);
+
+ if (channel->audio_fd[0] >= 0)
+ close(channel->audio_fd[0]);
+
+ if (channel->audio_fd[1] >= 0)
+ close(channel->audio_fd[1]);
+
+ crypto_aes_free(&channel->file.decrypt);
+
+ free(channel->file.path);
+
+ memset(channel, 0, sizeof(struct sp_channel));
+
+ channel->audio_fd[0] = -1;
+ channel->audio_fd[1] = -1;
+}
+
+void
+channel_free_all(struct sp_session *session)
+{
+ int i;
+
+ for (i = 0; i < sizeof(session->channels)/sizeof(session->channels)[0]; i++)
+ channel_free(&session->channels[i]);
+}
+
+int
+channel_new(struct sp_channel **new_channel, struct sp_session *session, const char *path, struct event_base *evbase, event_callback_fn write_cb)
+{
+ struct sp_channel *channel;
+ uint16_t i = SP_DEFAULT_CHANNEL;
+ int ret;
+
+ channel = &session->channels[i];
+
+ channel_free(channel);
+ channel->id = i;
+ channel->is_allocated = true;
+
+ channel->file.path = strdup(path);
+ path_to_media_id_and_type(&channel->file);
+
+ // Set up the audio I/O
+ ret = pipe(channel->audio_fd);
+ if (ret < 0)
+ goto error;
+
+ if (fcntl(channel->audio_fd[0], F_SETFL, O_CLOEXEC | O_NONBLOCK) < 0)
+ goto error;
+
+ if (fcntl(channel->audio_fd[1], F_SETFL, O_CLOEXEC | O_NONBLOCK) < 0)
+ goto error;
+
+ channel->audio_write_ev = event_new(evbase, channel->audio_fd[1], EV_WRITE, write_cb, session);
+ if (!channel->audio_write_ev)
+ goto error;
+
+ channel->audio_buf = evbuffer_new();
+ if (!channel->audio_buf)
+ goto error;
+
+ *new_channel = channel;
+
+ return 0;
+
+ error:
+ channel_free(channel);
+ return -1;
+}
+
+// Set the fd to non-blocking in case the caller changed that, and then read
+// until empty
+static int
+channel_flush(int fd)
+{
+ uint8_t buf[4096];
+ int flags;
+ int got;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1)
+ return -1;
+
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ do
+ got = read(fd, buf, sizeof(buf));
+ while (got > 0);
+
+ fcntl(fd, F_SETFL, flags);
+ return 0;
+}
+
+void
+channel_play(struct sp_channel *channel)
+{
+ channel->is_writing = true;
+}
+
+void
+channel_stop(struct sp_channel *channel)
+{
+ channel->is_writing = false;
+
+ // This will tell the reader that there is no more to read. He should then
+ // call librespotc_close(), which will clean up the rest of the channel via
+ // channel_free().
+ close(channel->audio_fd[1]);
+ channel->audio_fd[1] = -1;
+}
+
+int
+channel_seek(struct sp_channel *channel, size_t pos)
+{
+ uint32_t seek_words;
+ int ret;
+
+ ret = channel_flush(channel->audio_fd[0]);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Could not flush read fd before seeking");
+
+ channel->seek_pos = pos;
+
+ // If seek + header isn't word aligned we will get up to 3 bytes before the
+ // actual seek position. We will remove those when they are received.
+ channel->seek_align = (pos + SP_OGG_HEADER_LEN) % 4;
+
+ seek_words = (pos + SP_OGG_HEADER_LEN) / 4;
+
+ ret = crypto_aes_seek(&channel->file.decrypt, 4 * seek_words, &sp_errmsg);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_DECRYPTION, sp_errmsg);
+
+ // Set the offset and received counter to match the seek
+ channel->file.offset_words = seek_words;
+ channel->file.received_words = seek_words;
+
+ return 0;
+
+ error:
+ return ret;
+}
+
+void
+channel_pause(struct sp_channel *channel)
+{
+ channel_flush(channel->audio_fd[0]);
+
+ channel->is_writing = false;
+}
+
+// Always returns number of byte read so caller can advance read pointer. If
+// header->len == 0 is returned it means that there are no more headers, and
+// caller should switch the channel to data mode.
+static ssize_t
+channel_header_parse(struct sp_channel_header *header, uint8_t *data, size_t data_len)
+{
+ uint8_t *ptr;
+ uint16_t be;
+
+ if (data_len < sizeof(be))
+ return -1;
+
+ ptr = data;
+ memset(header, 0, sizeof(struct sp_channel_header));
+
+ memcpy(&be, ptr, sizeof(be));
+ header->len = be16toh(be);
+ ptr += sizeof(be);
+
+ if (header->len == 0)
+ goto done; // No more headers
+ else if (data_len < header->len + sizeof(be))
+ return -1;
+
+ header->id = ptr[0];
+ ptr += 1;
+
+ header->data = ptr;
+ header->data_len = header->len - 1;
+ ptr += header->data_len;
+
+ assert(ptr - data == header->len + sizeof(be));
+
+ done:
+ return header->len + sizeof(be);
+}
+
+static void
+channel_header_handle(struct sp_channel *channel, struct sp_channel_header *header)
+{
+ uint32_t be32;
+
+ sp_cb.hexdump("Received header\n", header->data, header->data_len);
+
+ // The only header that librespot seems to use is 0x3, which is the audio file
+ // size in words (incl. headers?)
+ if (header->id == 0x3)
+ {
+ if (header->data_len != sizeof(be32))
+ {
+ sp_cb.logmsg("Unexpected header length for header id 0x3\n");
+ return;
+ }
+
+ memcpy(&be32, header->data, sizeof(be32));
+ channel->file.len_words = be32toh(be32);
+ }
+}
+
+static ssize_t
+channel_header_trailer_read(struct sp_channel *channel, uint8_t *msg, size_t msg_len, struct sp_session *session)
+{
+ ssize_t parsed_len;
+ ssize_t consumed_len;
+ int ret;
+
+ channel->file.end_of_chunk = false;
+ channel->file.end_of_file = false;
+
+ if (msg_len == 0)
+ {
+ channel->file.end_of_chunk = true;
+ channel->file.end_of_file = (channel->file.received_words >= channel->file.len_words);
+
+ // In preparation for next chunk
+ channel->file.offset_words += SP_CHUNK_LEN_WORDS;
+ channel->is_data_mode = false;
+
+ return 0;
+ }
+ else if (channel->is_data_mode)
+ {
+ return 0;
+ }
+
+ for (consumed_len = 0; msg_len > 0; msg += parsed_len, msg_len -= parsed_len)
+ {
+ parsed_len = channel_header_parse(&channel->header, msg, msg_len);
+ if (parsed_len < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Invalid channel header");
+
+ consumed_len += parsed_len;
+
+ if (channel->header.len == 0)
+ {
+ channel->is_data_mode = true;
+ break; // All headers read
+ }
+
+ channel_header_handle(channel, &channel->header);
+ }
+
+ return consumed_len;
+
+ error:
+ return ret;
+}
+
+static ssize_t
+channel_data_read(struct sp_channel *channel, uint8_t *msg, size_t msg_len, struct sp_session *session)
+{
+ const char *errmsg;
+ int ret;
+
+ assert (msg_len % 4 == 0);
+
+ channel->file.received_words += msg_len / 4;
+
+ ret = crypto_aes_decrypt(msg, msg_len, &channel->file.decrypt, &errmsg);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_DECRYPTION, errmsg);
+
+ // Skip Spotify header
+ // TODO What to do here when seeking
+ if (!channel->is_spotify_header_received)
+ {
+ if (msg_len < SP_OGG_HEADER_LEN)
+ RETURN_ERROR(SP_ERR_INVALID, "Invalid data received");
+
+ channel->is_spotify_header_received = true;
+
+ msg += SP_OGG_HEADER_LEN;
+ msg_len -= SP_OGG_HEADER_LEN;
+ }
+
+ // See explanation of this in channel_seek()
+ if (channel->seek_align)
+ {
+ msg += channel->seek_align;
+ msg_len -= channel->seek_align;
+ channel->seek_align = 0;
+ }
+
+ channel->body.data = msg;
+ channel->body.data_len = msg_len;
+
+ return 0;
+
+ error:
+ return ret;
+}
+
+int
+channel_data_write(struct sp_channel *channel)
+{
+ ssize_t wrote;
+ int ret;
+
+ wrote = evbuffer_write(channel->audio_buf, channel->audio_fd[1]);
+ if (wrote < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ return SP_OK_WAIT;
+ else if (wrote < 0)
+ RETURN_ERROR(SP_ERR_WRITE, "Error writing to audio pipe");
+
+ channel->audio_written_len += wrote;
+
+ if (evbuffer_get_length(channel->audio_buf) > 0)
+ return SP_OK_WAIT;
+
+ return SP_OK_DONE;
+
+ error:
+ return ret;
+}
+
+int
+channel_msg_read(uint16_t *channel_id, uint8_t *msg, size_t msg_len, struct sp_session *session)
+{
+ struct sp_channel *channel;
+ uint16_t be;
+ ssize_t consumed_len;
+ int ret;
+
+ if (msg_len < sizeof(be))
+ RETURN_ERROR(SP_ERR_INVALID, "Chunk response is too small");
+
+ memcpy(&be, msg, sizeof(be));
+ *channel_id = be16toh(be);
+
+ channel = channel_get(*channel_id, session);
+ if (!channel)
+ {
+ sp_cb.hexdump("Message with unknown channel\n", msg, msg_len);
+ RETURN_ERROR(SP_ERR_INVALID, "Could not recognize channel in chunk response");
+ }
+
+ msg += sizeof(be);
+ msg_len -= sizeof(be);
+
+ // Will set data_mode, end_of_file and end_of_chunk as appropriate
+ consumed_len = channel_header_trailer_read(channel, msg, msg_len, session);
+ if (consumed_len < 0)
+ RETURN_ERROR((int)consumed_len, sp_errmsg);
+
+ msg += consumed_len;
+ msg_len -= consumed_len;
+
+ channel->body.data = NULL;
+ channel->body.data_len = 0;
+
+ if (!channel->is_data_mode || !(msg_len > 0))
+ return 0; // Not in data mode or no data to read
+
+ consumed_len = channel_data_read(channel, msg, msg_len, session);
+ if (consumed_len < 0)
+ RETURN_ERROR((int)consumed_len, sp_errmsg);
+
+ return 0;
+
+ error:
+ return ret;
+}
+
diff --git a/src/inputs/librespot-c/src/channel.h b/src/inputs/librespot-c/src/channel.h
new file mode 100644
index 00000000..d010adc0
--- /dev/null
+++ b/src/inputs/librespot-c/src/channel.h
@@ -0,0 +1,29 @@
+struct sp_channel *
+channel_get(uint32_t channel_id, struct sp_session *session);
+
+void
+channel_free(struct sp_channel *channel);
+
+void
+channel_free_all(struct sp_session *session);
+
+int
+channel_new(struct sp_channel **channel, struct sp_session *session, const char *path, struct event_base *evbase, event_callback_fn write_cb);
+
+int
+channel_data_write(struct sp_channel *channel);
+
+void
+channel_play(struct sp_channel *channel);
+
+void
+channel_stop(struct sp_channel *channel);
+
+int
+channel_seek(struct sp_channel *channel, size_t pos);
+
+void
+channel_pause(struct sp_channel *channel);
+
+int
+channel_msg_read(uint16_t *channel_id, uint8_t *msg, size_t msg_len, struct sp_session *session);
diff --git a/src/inputs/librespot-c/src/commands.c b/src/inputs/librespot-c/src/commands.c
new file mode 100644
index 00000000..09d66205
--- /dev/null
+++ b/src/inputs/librespot-c/src/commands.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2016 Christian Meffert
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "commands.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+struct command
+{
+ pthread_mutex_t lck;
+ pthread_cond_t cond;
+
+ command_function func;
+ command_function func_bh;
+ void *arg;
+ int nonblock;
+ int ret;
+ int pending;
+};
+
+struct commands_base
+{
+ struct event_base *evbase;
+ command_exit_cb exit_cb;
+ int command_pipe[2];
+ struct event *command_event;
+ struct command *current_cmd;
+};
+
+static int
+mutex_init(pthread_mutex_t *mutex)
+{
+ pthread_mutexattr_t mattr;
+ int err;
+
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK);
+ err = pthread_mutex_init(mutex, &mattr);
+ pthread_mutexattr_destroy(&mattr);
+
+ return err;
+}
+
+/*
+ * Asynchronous execution of the command function
+ */
+static void
+command_cb_async(struct commands_base *cmdbase, struct command *cmd)
+{
+ enum command_state cmdstate;
+
+ // Command is executed asynchronously
+ cmdstate = cmd->func(cmd->arg, &cmd->ret);
+
+ // Only free arg if there are no pending events (used in worker.c)
+ if (cmdstate != COMMAND_PENDING && cmd->arg)
+ free(cmd->arg);
+
+ free(cmd);
+
+ event_add(cmdbase->command_event, NULL);
+}
+
+/*
+ * Synchronous execution of the command function
+ */
+static void
+command_cb_sync(struct commands_base *cmdbase, struct command *cmd)
+{
+ enum command_state cmdstate;
+
+ pthread_mutex_lock(&cmd->lck);
+
+ cmdstate = cmd->func(cmd->arg, &cmd->ret);
+ if (cmdstate == COMMAND_PENDING)
+ {
+ // Command execution is waiting for pending events before returning to the caller
+ cmdbase->current_cmd = cmd;
+ cmd->pending = cmd->ret;
+ }
+ else
+ {
+ // Command execution finished, execute the bottom half function
+ if (cmd->ret == 0 && cmd->func_bh)
+ cmd->func_bh(cmd->arg, &cmd->ret);
+
+ event_add(cmdbase->command_event, NULL);
+
+ // Signal the calling thread that the command execution finished
+ pthread_cond_signal(&cmd->cond);
+ pthread_mutex_unlock(&cmd->lck);
+
+ // Note if cmd->func was cmdloop_exit then cmdbase may be invalid now,
+ // because commands_base_destroy() may have freed it
+ }
+}
+
+/*
+ * Event callback function
+ *
+ * Function is triggered by libevent if there is data to read on the command pipe (writing to the command pipe happens through
+ * the send_command function).
+ */
+static void
+command_cb(int fd, short what, void *arg)
+{
+ struct commands_base *cmdbase;
+ struct command *cmd;
+ int ret;
+
+ cmdbase = arg;
+
+ // Get the command to execute from the pipe
+ ret = read(cmdbase->command_pipe[0], &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd))
+ {
+ // Incorrect length, ignore
+ event_add(cmdbase->command_event, NULL);
+ return;
+ }
+
+ // Execute the command function
+ if (cmd->nonblock)
+ {
+ // Command is executed asynchronously
+ command_cb_async(cmdbase, cmd);
+ }
+ else
+ {
+ // Command is executed synchronously, caller is waiting until signaled that the execution finished
+ command_cb_sync(cmdbase, cmd);
+ }
+}
+
+/*
+ * Writes the given command to the command pipe
+ */
+static int
+send_command(struct commands_base *cmdbase, struct command *cmd)
+{
+ int ret;
+
+ if (!cmd->func)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = write(cmdbase->command_pipe[1], &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd))
+ {
+ // errno set by write()
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Frees the command base and closes the (internally used) pipes
+ */
+int
+commands_base_free(struct commands_base *cmdbase)
+{
+ if (cmdbase->command_event)
+ event_free(cmdbase->command_event);
+
+ close(cmdbase->command_pipe[0]);
+ close(cmdbase->command_pipe[1]);
+ free(cmdbase);
+
+ return 0;
+}
+
+/*
+ * Creates a new command base, needs to be freed by commands_base_destroy or commands_base_free.
+ *
+ * @param evbase The libevent base to use for command handling
+ * @param exit_cb Optional callback function to be called during commands_base_destroy
+ */
+struct commands_base *
+commands_base_new(struct event_base *evbase, command_exit_cb exit_cb)
+{
+ struct commands_base *cmdbase;
+ int ret;
+
+ cmdbase = calloc(1, sizeof(struct commands_base));
+
+#ifdef HAVE_PIPE2
+ ret = pipe2(cmdbase->command_pipe, O_CLOEXEC);
+#else
+ ret = pipe(cmdbase->command_pipe);
+#endif
+ if (ret < 0)
+ {
+ // errno set by pipe
+ free(cmdbase);
+ return NULL;
+ }
+
+ cmdbase->command_event = event_new(evbase, cmdbase->command_pipe[0], EV_READ, command_cb, cmdbase);
+ if (!cmdbase->command_event)
+ {
+ commands_base_free(cmdbase);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret = event_add(cmdbase->command_event, NULL);
+ if (ret != 0)
+ {
+ commands_base_free(cmdbase);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ cmdbase->evbase = evbase;
+ cmdbase->exit_cb = exit_cb;
+
+ return cmdbase;
+}
+
+/*
+ * Gets the current return value for the current pending command.
+ *
+ * If a command has more than one pending event, each event can access the previous set return value
+ * if it depends on it.
+ *
+ * @param cmdbase The command base
+ * @return The current return value
+ */
+int
+commands_exec_returnvalue(struct commands_base *cmdbase)
+{
+ if (cmdbase->current_cmd == NULL)
+ return 0;
+
+ return cmdbase->current_cmd->ret;
+}
+
+/*
+ * If a command function returned COMMAND_PENDING, each event triggered by this command needs to
+ * call command_exec_end, passing it the return value of the event execution.
+ *
+ * If a command function is waiting for multiple events, each event needs to call command_exec_end.
+ * The command base keeps track of the number of still pending events and only returns to the caller
+ * if there are no pending events left.
+ *
+ * @param cmdbase The command base (holds the current pending command)
+ * @param retvalue The return value for the calling thread
+ */
+void
+commands_exec_end(struct commands_base *cmdbase, int retvalue)
+{
+ struct command *current_cmd = cmdbase->current_cmd;
+
+ if (!current_cmd)
+ return;
+
+ // A pending event finished, decrease the number of pending events and update the return value
+ current_cmd->pending--;
+ current_cmd->ret = retvalue;
+
+ // If there are still pending events return
+ if (current_cmd->pending > 0)
+ return;
+
+ // All pending events have finished, execute the bottom half and signal the caller that the command execution finished
+ if (current_cmd->func_bh)
+ current_cmd->func_bh(current_cmd->arg, ¤t_cmd->ret);
+
+ cmdbase->current_cmd = NULL;
+
+ /* Process commands again */
+ event_add(cmdbase->command_event, NULL);
+
+ pthread_cond_signal(¤t_cmd->cond);
+ pthread_mutex_unlock(¤t_cmd->lck);
+}
+
+/*
+ * Execute the function 'func' with the given argument 'arg' in the event loop thread.
+ * Blocks the caller (thread) until the function returned.
+ *
+ * If a function 'func_bh' ("bottom half") is given, it is executed after 'func' has successfully
+ * finished.
+ *
+ * @param cmdbase The command base
+ * @param func The function to be executed
+ * @param func_bh The bottom half function to be executed after all pending events from func are processed
+ * @param arg Argument passed to func (and func_bh)
+ * @return Return value of func (or func_bh if func_bh is not NULL)
+ */
+int
+commands_exec_sync(struct commands_base *cmdbase, command_function func, command_function func_bh, void *arg)
+{
+ struct command cmd;
+ int errsv = 0;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct command));
+ cmd.func = func;
+ cmd.func_bh = func_bh;
+ cmd.arg = arg;
+ cmd.nonblock = 0;
+
+ mutex_init(&cmd.lck);
+ pthread_cond_init(&cmd.cond, NULL);
+
+ pthread_mutex_lock(&cmd.lck);
+
+ ret = send_command(cmdbase, &cmd);
+ if (ret < 0)
+ {
+ errsv = errno;
+ cmd.ret = -1;
+ }
+ else
+ {
+ pthread_cond_wait(&cmd.cond, &cmd.lck);
+ }
+
+ // May change errno, but we don't care about that
+ pthread_mutex_unlock(&cmd.lck);
+
+ pthread_cond_destroy(&cmd.cond);
+ pthread_mutex_destroy(&cmd.lck);
+
+ errno = errsv;
+ return cmd.ret;
+}
+
+/*
+ * Execute the function 'func' with the given argument 'arg' in the event loop thread.
+ * Triggers the function execution and immediately returns (does not wait for func to finish).
+ *
+ * The pointer passed as argument is freed in the event loop thread after func returned.
+ *
+ * @param cmdbase The command base
+ * @param func The function to be executed
+ * @param arg Argument passed to func
+ * @return 0 if triggering the function execution succeeded, -1 on failure.
+ */
+int
+commands_exec_async(struct commands_base *cmdbase, command_function func, void *arg)
+{
+ struct command *cmd;
+ int ret;
+
+ cmd = calloc(1, sizeof(struct command));
+ cmd->func = func;
+ cmd->func_bh = NULL;
+ cmd->arg = arg;
+ cmd->nonblock = 1;
+
+ ret = send_command(cmdbase, cmd);
+ if (ret < 0)
+ {
+ free(cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Command to break the libevent loop
+ *
+ * If the command base was created with an exit_cb function, exit_cb is called before breaking the
+ * libevent loop.
+ *
+ * @param arg The command base
+ * @param retval Always set to COMMAND_END
+ */
+static enum command_state
+cmdloop_exit(void *arg, int *retval)
+{
+ struct commands_base *cmdbase = arg;
+ *retval = 0;
+
+ if (cmdbase->exit_cb)
+ cmdbase->exit_cb();
+
+ event_base_loopbreak(cmdbase->evbase);
+
+ return COMMAND_END;
+}
+
+/*
+ * Break the libevent loop for the given command base, closes the internally used pipes
+ * and frees the command base.
+ *
+ * @param cmdbase The command base
+ */
+void
+commands_base_destroy(struct commands_base *cmdbase)
+{
+ commands_exec_sync(cmdbase, cmdloop_exit, NULL, cmdbase);
+ commands_base_free(cmdbase);
+}
+
diff --git a/src/inputs/librespot-c/src/commands.h b/src/inputs/librespot-c/src/commands.h
new file mode 100644
index 00000000..9076d0f4
--- /dev/null
+++ b/src/inputs/librespot-c/src/commands.h
@@ -0,0 +1,56 @@
+
+#ifndef SRC_COMMANDS_H_
+#define SRC_COMMANDS_H_
+
+#include
+
+enum command_state {
+ COMMAND_END = 0,
+ COMMAND_PENDING = 1,
+};
+
+/*
+ * Function that will be executed in the event loop thread.
+ *
+ * If the function has pending events to complete, it needs to return
+ * COMMAND_PENDING with 'ret' set to the number of pending events to wait for.
+ *
+ * If the function returns with COMMAND_END, command execution will proceed
+ * with the "bottem half" function (if passed to the command_exec function) only
+ * if 'ret' is 0.
+ *
+ * @param arg Opaque pointer passed by command_exec_sync or command_exec_async
+ * @param ret Pointer to the return value for the caller of the command
+ * @return COMMAND_END if there are no pending events (function execution is
+ * complete) or COMMAND_PENDING if there are pending events
+ */
+typedef enum command_state (*command_function)(void *arg, int *ret);
+
+typedef void (*command_exit_cb)(void);
+
+
+struct commands_base;
+
+
+struct commands_base *
+commands_base_new(struct event_base *evbase, command_exit_cb exit_cb);
+
+int
+commands_base_free(struct commands_base *cmdbase);
+
+int
+commands_exec_returnvalue(struct commands_base *cmdbase);
+
+void
+commands_exec_end(struct commands_base *cmdbase, int retvalue);
+
+int
+commands_exec_sync(struct commands_base *cmdbase, command_function func, command_function func_bh, void *arg);
+
+int
+commands_exec_async(struct commands_base *cmdbase, command_function func, void *arg);
+
+void
+commands_base_destroy(struct commands_base *cmdbase);
+
+#endif /* SRC_COMMANDS_H_ */
diff --git a/src/inputs/librespot-c/src/connection.c b/src/inputs/librespot-c/src/connection.c
new file mode 100644
index 00000000..666bc7f0
--- /dev/null
+++ b/src/inputs/librespot-c/src/connection.c
@@ -0,0 +1,1342 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "librespot-c-internal.h"
+#include "connection.h"
+#include "channel.h"
+
+static struct timeval sp_idle_tv = { SP_AP_DISCONNECT_SECS, 0 };
+
+static uint8_t sp_aes_iv[] = { 0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93 };
+
+static struct sp_err_map sp_login_errors[] = {
+ { ERROR_CODE__ProtocolError, "Protocol error" },
+ { ERROR_CODE__TryAnotherAP, "Try another access point" },
+ { ERROR_CODE__BadConnectionId, "Bad connection ID" },
+ { ERROR_CODE__TravelRestriction, "Travel restriction" },
+ { ERROR_CODE__PremiumAccountRequired, "Premium account required" },
+ { ERROR_CODE__BadCredentials, "Bad credentials" },
+ { ERROR_CODE__CouldNotValidateCredentials, "Could not validate credentials" },
+ { ERROR_CODE__AccountExists, "Account exists" },
+ { ERROR_CODE__ExtraVerificationRequired, "Extra verification required" },
+ { ERROR_CODE__InvalidAppKey, "Invalid app key" },
+ { ERROR_CODE__ApplicationBanned, "Application banned" },
+};
+
+/* ---------------------------------- MOCKING ------------------------------- */
+
+#ifdef DEBUG_MOCK
+#include "mock.h"
+
+static int debug_mock_pipe[2];
+static int debug_mock_chunks_written = 0;
+
+static int
+debug_mock_response(struct sp_message *msg, struct sp_connection *conn)
+{
+ if (debug_mock_chunks_written == 1)
+ return 0; // Only write the chunk once
+
+ switch(msg->type)
+ {
+ case MSG_TYPE_CLIENT_HELLO:
+ write(debug_mock_pipe[1], mock_resp_apresponse, sizeof(mock_resp_apresponse));
+ break;
+ case MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED:
+ memcpy(conn->decrypt.key, mock_recv_key, sizeof(conn->decrypt.key));
+ // Spotify will send the replies split in these 6 packets
+ write(debug_mock_pipe[1], mock_resp_client_encrypted1, sizeof(mock_resp_client_encrypted1));
+ write(debug_mock_pipe[1], mock_resp_client_encrypted2, sizeof(mock_resp_client_encrypted2));
+ write(debug_mock_pipe[1], mock_resp_client_encrypted3, sizeof(mock_resp_client_encrypted3));
+ write(debug_mock_pipe[1], mock_resp_client_encrypted4, sizeof(mock_resp_client_encrypted4));
+ write(debug_mock_pipe[1], mock_resp_client_encrypted5, sizeof(mock_resp_client_encrypted5));
+ write(debug_mock_pipe[1], mock_resp_client_encrypted6, sizeof(mock_resp_client_encrypted6));
+ break;
+ case MSG_TYPE_MERCURY_TRACK_GET:
+ write(debug_mock_pipe[1], mock_resp_mercury_req1, sizeof(mock_resp_mercury_req1));
+ write(debug_mock_pipe[1], mock_resp_mercury_req2, sizeof(mock_resp_mercury_req2));
+ break;
+ case MSG_TYPE_AUDIO_KEY_GET:
+ memset(conn->decrypt.key, 0, sizeof(conn->decrypt.key)); // Tells msg_read_one() to skip decryption
+ write(debug_mock_pipe[1], mock_resp_aeskey, sizeof(mock_resp_aeskey));
+ break;
+ case MSG_TYPE_CHUNK_REQUEST:
+ write(debug_mock_pipe[1], mock_resp_chunk1, sizeof(mock_resp_chunk1));
+ write(debug_mock_pipe[1], mock_resp_chunk2, sizeof(mock_resp_chunk2));
+ debug_mock_chunks_written++;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+
+/* --------------------------------- Helpers -------------------------------- */
+
+// Returns true if format of a is preferred over b (and is valid). According to
+// librespot comment most podcasts are 96 kbit.
+static bool
+format_is_preferred(AudioFile *a, AudioFile *b, enum sp_bitrates bitrate_preferred)
+{
+ if (a->format != AUDIO_FILE__FORMAT__OGG_VORBIS_96 &&
+ a->format != AUDIO_FILE__FORMAT__OGG_VORBIS_160 &&
+ a->format != AUDIO_FILE__FORMAT__OGG_VORBIS_320)
+ return false;
+
+ if (!b)
+ return true; // Any format is better than no format
+
+ switch (bitrate_preferred)
+ {
+ case SP_BITRATE_96:
+ return (a->format < b->format); // Prefer lowest
+ case SP_BITRATE_160:
+ if (b->format == AUDIO_FILE__FORMAT__OGG_VORBIS_160)
+ return false;
+ else if (a->format == AUDIO_FILE__FORMAT__OGG_VORBIS_160)
+ return true;
+ else
+ return (a->format < b->format); // Prefer lowest
+ case SP_BITRATE_320:
+ return (a->format > b->format); // Prefer highest
+ case SP_BITRATE_ANY:
+ return (a->format > b->format); // This case shouldn't happen, so this is mostly to avoid compiler warnings
+ }
+
+ return false;
+}
+
+int
+file_select(uint8_t *out, size_t out_len, Track *track, enum sp_bitrates bitrate_preferred)
+{
+ AudioFile *selected = NULL;
+ AudioFile *file;
+ int i;
+
+ for (i = 0; i < track->n_file; i++)
+ {
+ file = track->file[i];
+
+ if (!file->has_file_id || !file->has_format || file->file_id.len != out_len)
+ continue;
+
+ if (format_is_preferred(file, selected, bitrate_preferred))
+ selected = file;
+ }
+
+ if (!selected)
+ return -1;
+
+ memcpy(out, selected->file_id.data, selected->file_id.len);
+
+ return 0;
+}
+
+
+/* --------------------------- Connection handling -------------------------- */
+
+static int
+ap_resolve(char **address, unsigned short *port)
+{
+ char *body;
+ json_object *jresponse = NULL;
+ json_object *ap_list;
+ json_object *ap;
+ char *ap_address = NULL;
+ char *ap_port;
+ int ap_num;
+ int ret;
+
+ free(*address);
+ *address = NULL;
+
+ ret = sp_cb.https_get(&body, SP_AP_RESOLVE_URL);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not connect to access point resolver");
+
+ jresponse = json_tokener_parse(body);
+ if (!jresponse)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not parse reply from access point resolver");
+
+ if (! (json_object_object_get_ex(jresponse, SP_AP_RESOLVE_KEY, &ap_list) || json_object_get_type(ap_list) == json_type_array))
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver");
+
+ ap_num = json_object_array_length(ap_list);
+ ap = json_object_array_get_idx(ap_list, rand() % ap_num);
+ if (! (ap && json_object_get_type(ap) == json_type_string))
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver");
+
+ ap_address = strdup(json_object_get_string(ap));
+
+ if (! (ap_port = strchr(ap_address, ':')))
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver, missing port");
+ *ap_port = '\0';
+ ap_port += 1;
+
+ *address = ap_address;
+ *port = (unsigned short)atoi(ap_port);
+
+ json_object_put(jresponse);
+ free(body);
+ return 0;
+
+ error:
+ free(ap_address);
+ json_object_put(jresponse);
+ free(body);
+ return ret;
+}
+
+static bool
+is_handshake(enum sp_msg_type type)
+{
+ return ( type == MSG_TYPE_CLIENT_HELLO ||
+ type == MSG_TYPE_CLIENT_RESPONSE_PLAINTEXT ||
+ type == MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED );
+}
+
+static void
+connection_clear(struct sp_connection *conn)
+{
+ if (!conn)
+ return;
+
+ if (conn->response_ev)
+ event_free(conn->response_ev);
+ if (conn->idle_ev)
+ event_free(conn->idle_ev);
+ if (conn->timeout_ev)
+ event_free(conn->timeout_ev);
+
+ if (conn->handshake_packets)
+ evbuffer_free(conn->handshake_packets);
+ if (conn->incoming)
+ evbuffer_free(conn->incoming);
+
+ free(conn->ap_address);
+ free(conn->keys.shared_secret);
+
+ memset(conn, 0, sizeof(struct sp_connection));
+ conn->response_fd = -1;
+}
+
+void
+ap_disconnect(struct sp_connection *conn)
+{
+ if (conn->is_connected)
+ sp_cb.tcp_disconnect(conn->response_fd);
+
+ connection_clear(conn);
+}
+
+static void
+connection_idle_cb(int fd, short what, void *arg)
+{
+ struct sp_connection *conn = arg;
+
+ ap_disconnect(conn);
+
+ sp_cb.logmsg("Connection is idle, auto-disconnected\n");
+}
+
+static int
+connection_make(struct sp_connection *conn, struct sp_conn_callbacks *cb, struct sp_session *session)
+{
+ int response_fd;
+ int ret;
+
+ if (!conn->ap_address || !conn->ap_port)
+ {
+ ret = ap_resolve(&conn->ap_address, &conn->ap_port);
+ if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+ }
+
+#ifndef DEBUG_MOCK
+ response_fd = sp_cb.tcp_connect(conn->ap_address, conn->ap_port);
+ if (response_fd < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not connect to access point");
+#else
+ pipe(debug_mock_pipe);
+ response_fd = debug_mock_pipe[0];
+#endif
+
+ conn->response_fd = response_fd;
+ conn->response_ev = event_new(cb->evbase, response_fd, EV_READ | EV_PERSIST, cb->response_cb, session);
+ conn->timeout_ev = evtimer_new(cb->evbase, cb->timeout_cb, conn);
+
+ conn->idle_ev = evtimer_new(cb->evbase, connection_idle_cb, conn);
+
+ conn->handshake_packets = evbuffer_new();
+ conn->incoming = evbuffer_new();
+
+ crypto_keys_set(&conn->keys);
+ conn->encrypt.logmsg = sp_cb.logmsg;
+ conn->decrypt.logmsg = sp_cb.logmsg;
+
+ event_add(conn->response_ev, NULL);
+
+ conn->is_connected = true;
+
+ return 0;
+
+ error:
+ return ret;
+}
+
+enum sp_error
+ap_connect(enum sp_msg_type type, struct sp_conn_callbacks *cb, struct sp_session *session)
+{
+ int ret;
+
+ if (!session->conn.is_connected)
+ {
+ ret = connection_make(&session->conn, cb, session);
+ if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+ }
+
+ if (is_handshake(type) || session->conn.handshake_completed)
+ return SP_OK_DONE; // Proceed right away
+
+ return SP_OK_WAIT; // Caller must login again
+
+ error:
+ ap_disconnect(&session->conn);
+ return ret;
+}
+
+/* ------------------------------ Raw packets ------------------------------- */
+
+static ssize_t
+packet_make_encrypted(uint8_t *out, size_t out_len, uint8_t cmd, const uint8_t *payload, size_t payload_len, struct crypto_cipher *cipher)
+{
+ uint16_t be;
+ size_t plain_len;
+ ssize_t pkt_len;
+ uint8_t *ptr;
+
+ be = htobe16(payload_len);
+
+ plain_len = sizeof(cmd) + sizeof(be) + payload_len;
+ if (plain_len > out_len)
+ goto error;
+
+ ptr = out;
+ memcpy(ptr, &cmd, sizeof(cmd));
+ ptr += sizeof(cmd);
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be);
+ memcpy(ptr, payload, payload_len);
+
+// sp_cb.hexdump("Encrypting packet\n", out, plain_len);
+
+ pkt_len = crypto_encrypt(out, out_len, plain_len, cipher);
+ if (pkt_len < 9)
+ goto error;
+
+ return pkt_len;
+
+ error:
+ return -1;
+}
+
+static ssize_t
+packet_make_plain(uint8_t *out, size_t out_len, uint8_t *protobuf, size_t protobuf_len, bool add_version_header)
+{
+ const uint8_t version_header[] = { 0x00, 0x04 };
+ size_t header_len;
+ ssize_t len;
+ uint32_t be;
+
+ header_len = add_version_header ? sizeof(be) + sizeof(version_header) : sizeof(be);
+
+ len = header_len + protobuf_len;
+ if (len > out_len)
+ return -1;
+
+ if (add_version_header)
+ memcpy(out, version_header, sizeof(version_header));
+
+ be = htobe32(len);
+ memcpy(out + header_len - sizeof(be), &be, sizeof(be)); // Last bytes of the header is the length
+ memcpy(out + header_len, protobuf, protobuf_len);
+
+ return len;
+}
+
+
+/* ---------------------------- Mercury messages ---------------------------- */
+
+static void
+mercury_free(struct sp_mercury *mercury, int content_only)
+{
+ int i;
+
+ if (!mercury)
+ return;
+
+ free(mercury->uri);
+ free(mercury->method);
+ free(mercury->content_type);
+
+ for (i = 0; i < mercury->parts_num; i++)
+ {
+ free(mercury->parts[i].data);
+
+ if (mercury->parts[i].track)
+ track__free_unpacked(mercury->parts[i].track, NULL);
+ }
+
+ if (content_only)
+ memset(mercury, 0, sizeof(struct sp_mercury));
+ else
+ free(mercury);
+}
+
+static int
+mercury_parse(struct sp_mercury *mercury, uint8_t *payload, size_t payload_len)
+{
+ Header *header;
+ uint8_t *ptr;
+ uint16_t be;
+ uint64_t be64;
+ uint16_t seq_len;
+ uint16_t header_len;
+ size_t required_len; // For size checking
+ uint8_t flags;
+ int i;
+
+ ptr = payload;
+
+ required_len = sizeof(be);
+ if (required_len > payload_len)
+ goto error_length; // Length check 1
+
+ memcpy(&be, ptr, sizeof(be));
+ seq_len = be16toh(be);
+ ptr += sizeof(be); // 1: length += sizeof(be)
+
+ required_len += seq_len + sizeof(flags) + sizeof(be) + sizeof(be);
+ if (required_len > payload_len || seq_len != sizeof(be64))
+ goto error_length; // Length check 2
+
+ memcpy(&be64, ptr, sizeof(be64));
+ mercury->seq = be64toh(be64);
+ ptr += seq_len; // 2: length += seq_len
+
+ memcpy(&flags, ptr, sizeof(flags));
+ ptr += sizeof(flags); // 2: length += sizeof(flags)
+
+ memcpy(&be, ptr, sizeof(be));
+ mercury->parts_num = be16toh(be) - 1; // What's the deal with the 1...?
+ ptr += sizeof(be); // 2: length += sizeof(be)
+
+ if (mercury->parts_num > SP_MERCURY_MAX_PARTS)
+ return -1;
+
+ memcpy(&be, ptr, sizeof(be));
+ header_len = be16toh(be);
+ ptr += sizeof(be); // 2: length += sizeof(be)
+
+ required_len += header_len;
+ if (required_len > payload_len)
+ goto error_length; // Length check 3
+
+ header = header__unpack(NULL, header_len, ptr);
+ if (!header)
+ goto error_length;
+ ptr += header_len; // 3: length += header_len
+
+ mercury->uri = header->uri ? strdup(header->uri) : NULL;
+ mercury->method = header->method ? strdup(header->method) : NULL;
+ mercury->content_type = header->content_type ? strdup(header->content_type) : NULL;
+
+ for (i = 0; i < mercury->parts_num; i++)
+ {
+ required_len += sizeof(be);
+ if (required_len > payload_len)
+ goto error_length; // Length check 4
+
+ memcpy(&be, ptr, sizeof(be));
+ mercury->parts[i].len = be16toh(be);
+ ptr += sizeof(be); // 4: length += sizeof(be)
+
+ required_len += mercury->parts[i].len;
+ if (required_len > payload_len)
+ goto error_length; // Length check 5
+
+ mercury->parts[i].data = malloc(mercury->parts[i].len);
+ memcpy(mercury->parts[i].data, ptr, mercury->parts[i].len);
+ ptr += mercury->parts[i].len; // 5: length += mercury->parts[i].len
+
+ mercury->parts[i].track = track__unpack(NULL, mercury->parts[i].len, mercury->parts[i].data);
+ }
+
+ header__free_unpacked(header, NULL);
+
+ assert(ptr == payload + required_len);
+
+ return 0;
+
+ error_length:
+ mercury_free(mercury, 1);
+ return -1;
+}
+
+
+/* --------------------------- Incoming messages ---------------------------- */
+
+static enum sp_error
+response_client_hello(uint8_t *msg, size_t msg_len, struct sp_session *session)
+{
+ struct sp_connection *conn = &session->conn;
+ APResponseMessage *apresponse;
+ size_t header_len = 4; // TODO make a define
+ int ret;
+
+ apresponse = apresponse_message__unpack(NULL, msg_len - header_len, msg + header_len);
+ if (!apresponse)
+ RETURN_ERROR(SP_ERR_INVALID, "Could not unpack apresponse from access point");
+
+ // TODO check APLoginFailed
+
+ // Not sure if necessary
+ if (!apresponse->challenge || !apresponse->challenge->login_crypto_challenge)
+ RETURN_ERROR(SP_ERR_INVALID, "Missing challenge in response from access point");
+
+ crypto_shared_secret(
+ &conn->keys.shared_secret, &conn->keys.shared_secret_len,
+ conn->keys.private_key, sizeof(conn->keys.private_key),
+ apresponse->challenge->login_crypto_challenge->diffie_hellman->gs.data, apresponse->challenge->login_crypto_challenge->diffie_hellman->gs.len);
+
+ apresponse_message__free_unpacked(apresponse, NULL);
+
+ conn->handshake_completed = true;
+
+ return SP_OK_DONE;
+
+ error:
+ return ret;
+}
+
+static enum sp_error
+response_apwelcome(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ APWelcome *apwelcome;
+ int ret;
+
+ apwelcome = apwelcome__unpack(NULL, payload_len, payload);
+ if (!apwelcome)
+ RETURN_ERROR(SP_ERR_INVALID, "Could not unpack apwelcome response from access point");
+
+ if (apwelcome->reusable_auth_credentials_type == AUTHENTICATION_TYPE__AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS)
+ {
+ if (apwelcome->reusable_auth_credentials.len > sizeof(session->credentials.stored_cred))
+ RETURN_ERROR(SP_ERR_INVALID, "Credentials from Spotify longer than expected");
+
+ session->credentials.stored_cred_len = apwelcome->reusable_auth_credentials.len;
+ memcpy(session->credentials.stored_cred, apwelcome->reusable_auth_credentials.data, session->credentials.stored_cred_len);
+ }
+
+ apwelcome__free_unpacked(apwelcome, NULL);
+
+ return SP_OK_DONE;
+
+ error:
+ return ret;
+}
+
+static enum sp_error
+response_aplogin_failed(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ APLoginFailed *aplogin_failed;
+
+ aplogin_failed = aplogin_failed__unpack(NULL, payload_len, payload);
+ if (!aplogin_failed)
+ {
+ sp_errmsg = "Could not unpack login failure from access point";
+ return SP_ERR_LOGINFAILED;
+ }
+
+ sp_errmsg = "(unknown login error)";
+ for (int i = 0; i < sizeof(sp_login_errors); i++)
+ {
+ if (sp_login_errors[i].errorcode != aplogin_failed->error_code)
+ continue;
+
+ sp_errmsg = sp_login_errors[i].errmsg;
+ break;
+ }
+
+ aplogin_failed__free_unpacked(aplogin_failed, NULL);
+
+ return SP_ERR_LOGINFAILED;
+}
+
+static enum sp_error
+response_chunk_res(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ struct sp_channel *channel;
+ uint16_t channel_id;
+ int ret;
+
+ ret = channel_msg_read(&channel_id, payload, payload_len, session);
+ if (ret < 0)
+ goto error;
+
+ channel = &session->channels[channel_id];
+
+ // Save any audio data to a buffer that will be written to audio_fd[1] when
+ // it is writable. Note that request for next chunk will also happen then.
+ evbuffer_add(channel->audio_buf, channel->body.data, channel->body.data_len);
+
+// sp_cb.logmsg("EOC is %d, data is %zu, buflen is %zu\n", channel->file.end_of_chunk, channel->body.data_len, evbuffer_get_length(channel->audio_buf));
+
+ return channel->file.end_of_chunk ? SP_OK_DATA : SP_OK_OTHER;
+
+ error:
+ return ret;
+}
+
+static enum sp_error
+response_aes_key(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ struct sp_channel *channel;
+ const char *errmsg;
+ uint32_t be32;
+ uint32_t channel_id;
+ int ret;
+
+ // Payload is expected to consist of seq (uint32 BE), and key (16 bytes)
+ if (payload_len != sizeof(be32) + 16)
+ RETURN_ERROR(SP_ERR_DECRYPTION, "Unexpected key received");
+
+ memcpy(&be32, payload, sizeof(be32));
+ channel_id = be32toh(be32);
+
+ channel = channel_get(channel_id, session);
+ if (!channel)
+ RETURN_ERROR(SP_ERR_INVALID, "Unexpected channel received");
+
+ memcpy(channel->file.key, payload + 4, 16);
+
+ ret = crypto_aes_new(&channel->file.decrypt, channel->file.key, sizeof(channel->file.key), sp_aes_iv, sizeof(sp_aes_iv), &errmsg);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_DECRYPTION, errmsg);
+
+ return SP_OK_DONE;
+
+ error:
+ return ret;
+}
+
+static enum sp_error
+response_aes_key_error(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ sp_errmsg = "Did not get key for decrypting track";
+
+ return SP_ERR_DECRYPTION;
+}
+
+static enum sp_error
+response_mercury_req(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ struct sp_mercury mercury = { 0 };
+ struct sp_channel *channel;
+ uint32_t channel_id;
+ int ret;
+
+ ret = mercury_parse(&mercury, payload, payload_len);
+ if (ret < 0)
+ {
+ sp_errmsg = "Could not parse message from Spotify";
+ return SP_ERR_INVALID;
+ }
+
+ if (mercury.parts_num != 1 || !mercury.parts[0].track)
+ RETURN_ERROR(SP_ERR_INVALID, "Unexpected track response from Spotify");
+
+ channel_id = (uint32_t)mercury.seq;
+
+ channel = channel_get(channel_id, session);
+ if (!channel)
+ RETURN_ERROR(SP_ERR_INVALID, "Unexpected channel received");
+
+ ret = file_select(channel->file.id, sizeof(channel->file.id), mercury.parts[0].track, session->bitrate_preferred);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Could not find track data");
+
+ mercury_free(&mercury, 1);
+
+ return SP_OK_DONE; // Continue to get AES key
+
+ error:
+ mercury_free(&mercury, 1);
+ return ret;
+}
+
+static enum sp_error
+response_ping(uint8_t *payload, size_t payload_len, struct sp_session *session)
+{
+ msg_pong(session);
+
+ return SP_OK_OTHER;
+}
+
+static enum sp_error
+response_generic(uint8_t *msg, size_t msg_len, struct sp_session *session)
+{
+ enum sp_cmd_type cmd;
+ uint8_t *payload;
+ size_t payload_len;
+ int ret;
+
+ cmd = msg[0];
+ payload = msg + 3;
+ payload_len = msg_len - 3 - 4;
+
+ switch (cmd)
+ {
+ case CmdAPWelcome:
+ ret = response_apwelcome(payload, payload_len, session);
+ break;
+ case CmdAuthFailure:
+ ret = response_aplogin_failed(payload, payload_len, session);
+ break;
+ case CmdPing:
+ ret = response_ping(payload, payload_len, session);
+ break;
+ case CmdStreamChunkRes:
+ ret = response_chunk_res(payload, payload_len, session);
+ break;
+ case CmdCountryCode:
+ memcpy(session->country, payload, sizeof(session->country) - 1);
+ ret = SP_OK_OTHER;
+ break;
+ case CmdAesKey:
+ ret = response_aes_key(payload, payload_len, session);
+ break;
+ case CmdAesKeyError:
+ ret = response_aes_key_error(payload, payload_len, session);
+ break;
+ case CmdMercuryReq:
+ ret = response_mercury_req(payload, payload_len, session);
+ break;
+ case CmdLegacyWelcome: // 0 bytes, ignored by librespot
+ case CmdSecretBlock: // ignored by librespot
+ case 0x50: // XML received after login, ignored by librespot
+ case CmdLicenseVersion: // ignored by librespot
+ default:
+ ret = SP_OK_OTHER;
+ }
+
+ return ret;
+}
+
+static enum sp_error
+msg_read_one(uint8_t **out, size_t *out_len, uint8_t *in, size_t in_len, struct sp_connection *conn)
+{
+ uint32_t be32;
+ ssize_t msg_len;
+ int ret;
+
+#ifdef DEBUG_MOCK
+ if (conn->is_encrypted && !conn->decrypt.key[0] && !conn->decrypt.key[1])
+ {
+ uint16_t be;
+ memcpy(&be, in + 1, sizeof(be));
+ msg_len = be16toh(be) + 7;
+ if (msg_len > in_len)
+ return SP_OK_WAIT;
+
+ *out = malloc(msg_len);
+ *out_len = msg_len;
+ evbuffer_remove(conn->incoming, *out, msg_len);
+
+ return SP_OK_DONE;
+ }
+#endif
+
+ if (conn->is_encrypted)
+ {
+ msg_len = crypto_decrypt(in, in_len, &conn->decrypt);
+ if (msg_len < 0)
+ RETURN_ERROR(SP_ERR_DECRYPTION, "Decryption error");
+ if (msg_len == 0)
+ return SP_OK_WAIT;
+ }
+ else
+ {
+ if (in_len < sizeof(be32))
+ return SP_OK_WAIT; // Wait for more data, size header is incomplete
+
+ memcpy(&be32, in, sizeof(be32));
+ msg_len = be32toh(be32);
+ if (msg_len < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Invalid message length");
+ if (msg_len > in_len)
+ return SP_OK_WAIT;
+
+ if (!conn->handshake_completed)
+ evbuffer_add(conn->handshake_packets, in, msg_len);
+ }
+
+ // At this point we have a complete, decrypted message.
+ *out = malloc(msg_len);
+ *out_len = msg_len;
+ evbuffer_remove(conn->incoming, *out, msg_len);
+
+ return SP_OK_DONE;
+
+ error:
+ return ret;
+}
+
+enum sp_error
+response_read(struct sp_session *session)
+{
+ struct sp_connection *conn = &session->conn;
+ uint8_t *in;
+ size_t in_len;
+ uint8_t *msg;
+ size_t msg_len;
+ int ret;
+
+ in_len = evbuffer_get_length(conn->incoming);
+ in = evbuffer_pullup(conn->incoming, -1);
+
+ ret = msg_read_one(&msg, &msg_len, in, in_len, conn);
+ if (ret != SP_OK_DONE)
+ goto error;
+
+ if (msg_len < 128)
+ sp_cb.hexdump("Received message\n", msg, msg_len);
+ else
+ sp_cb.hexdump("Received message (truncated)\n", msg, 128);
+
+ if (!session->response_handler)
+ RETURN_ERROR(SP_ERR_INVALID, "Unexpected response from Spotify, aborting");
+
+ // Handler must return SP_OK_DONE if the message is a response to a request.
+ // It must return SP_OK_OTHER if the message is something else (e.g. a ping),
+ // SP_ERR_xxx if the response indicates an error. Finally, SP_OK_DATA is like
+ // DONE except it also means that there is new audio data to write.
+ ret = session->response_handler(msg, msg_len, session);
+ free(msg);
+
+ return ret;
+
+ error:
+ return ret;
+}
+
+
+/* --------------------------- Outgoing messages ---------------------------- */
+
+// This message is constructed like librespot does it, see handshake.rs
+static ssize_t
+msg_make_client_hello(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ ClientHello client_hello = CLIENT_HELLO__INIT;
+ BuildInfo build_info = BUILD_INFO__INIT;
+ LoginCryptoHelloUnion login_crypto = LOGIN_CRYPTO_HELLO_UNION__INIT;
+ LoginCryptoDiffieHellmanHello diffie_hellman = LOGIN_CRYPTO_DIFFIE_HELLMAN_HELLO__INIT;
+ Cryptosuite crypto_suite = CRYPTOSUITE__CRYPTO_SUITE_SHANNON;
+ uint8_t padding[1] = { 0x1e };
+ uint8_t nonce[16] = { 0 };
+ size_t len;
+
+ build_info.product = PRODUCT__PRODUCT_PARTNER;
+ build_info.platform = PLATFORM__PLATFORM_LINUX_X86;
+ build_info.version = 109800078;
+
+ diffie_hellman.gc.len = sizeof(session->conn.keys.public_key);
+ diffie_hellman.gc.data = session->conn.keys.public_key;
+ diffie_hellman.server_keys_known = 1;
+
+ login_crypto.diffie_hellman = &diffie_hellman;
+
+ client_hello.build_info = &build_info;
+ client_hello.n_cryptosuites_supported = 1;
+ client_hello.cryptosuites_supported = &crypto_suite;
+ client_hello.login_crypto_hello = &login_crypto;
+ client_hello.client_nonce.len = sizeof(nonce);
+ client_hello.client_nonce.data = nonce;
+ client_hello.has_padding = 1;
+ client_hello.padding.len = sizeof(padding);
+ client_hello.padding.data = padding;
+
+ len = client_hello__get_packed_size(&client_hello);
+ if (len > out_len)
+ return -1;
+
+ client_hello__pack(&client_hello, out);
+
+ return len;
+}
+
+static int
+client_response_crypto(uint8_t **challenge, size_t *challenge_len, struct sp_connection *conn)
+{
+ uint8_t *packets;
+ size_t packets_len;
+ int ret;
+
+ packets_len = evbuffer_get_length(conn->handshake_packets);
+ packets = malloc(packets_len);
+ evbuffer_remove(conn->handshake_packets, packets, packets_len);
+
+ ret = crypto_challenge(challenge, challenge_len,
+ conn->encrypt.key, sizeof(conn->encrypt.key),
+ conn->decrypt.key, sizeof(conn->decrypt.key),
+ packets, packets_len,
+ conn->keys.shared_secret, conn->keys.shared_secret_len);
+
+ free(packets);
+
+ return ret;
+}
+
+static ssize_t
+msg_make_client_response_plaintext(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ ClientResponsePlaintext client_response = CLIENT_RESPONSE_PLAINTEXT__INIT;
+ LoginCryptoResponseUnion login_crypto_response = LOGIN_CRYPTO_RESPONSE_UNION__INIT;
+ LoginCryptoDiffieHellmanResponse diffie_hellman = LOGIN_CRYPTO_DIFFIE_HELLMAN_RESPONSE__INIT;
+ uint8_t *challenge;
+ size_t challenge_len;
+ ssize_t len;
+ int ret;
+
+ ret = client_response_crypto(&challenge, &challenge_len, &session->conn);
+ if (ret < 0)
+ return -1;
+
+ diffie_hellman.hmac.len = challenge_len;
+ diffie_hellman.hmac.data = challenge;
+
+ login_crypto_response.diffie_hellman = &diffie_hellman;
+
+ client_response.login_crypto_response = &login_crypto_response;
+
+ len = client_response_plaintext__get_packed_size(&client_response);
+ if (len > out_len)
+ {
+ free(challenge);
+ return -1;
+ }
+
+ client_response_plaintext__pack(&client_response, out);
+
+ free(challenge);
+ return len;
+}
+
+static ssize_t
+msg_make_client_response_encrypted(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ ClientResponseEncrypted client_response = CLIENT_RESPONSE_ENCRYPTED__INIT;
+ LoginCredentials login_credentials = LOGIN_CREDENTIALS__INIT;
+ SystemInfo system_info = SYSTEM_INFO__INIT;
+ char system_information_string[64];
+ char version_string[64];
+ ssize_t len;
+
+ login_credentials.has_auth_data = 1;
+ login_credentials.username = session->credentials.username;
+
+ if (session->credentials.stored_cred_len > 0)
+ {
+ login_credentials.typ = AUTHENTICATION_TYPE__AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS;
+ login_credentials.auth_data.len = session->credentials.stored_cred_len;
+ login_credentials.auth_data.data = session->credentials.stored_cred;
+ }
+ else if (session->credentials.token_len > 0)
+ {
+ login_credentials.typ = AUTHENTICATION_TYPE__AUTHENTICATION_SPOTIFY_TOKEN;
+ login_credentials.auth_data.len = session->credentials.token_len;
+ login_credentials.auth_data.data = session->credentials.token;
+ }
+ else if (strlen(session->credentials.password))
+ {
+ login_credentials.typ = AUTHENTICATION_TYPE__AUTHENTICATION_USER_PASS;
+ login_credentials.auth_data.len = strlen(session->credentials.password);
+ login_credentials.auth_data.data = (unsigned char *)session->credentials.password;
+ }
+ else
+ return -1;
+
+ system_info.cpu_family = CPU_FAMILY__CPU_UNKNOWN;
+ system_info.os = OS__OS_UNKNOWN;
+ snprintf(system_information_string, sizeof(system_information_string), "%s_%s_%s",
+ sp_sysinfo.client_name, sp_sysinfo.client_version, sp_sysinfo.client_build_id);
+ system_info.system_information_string = system_information_string;
+ system_info.device_id = sp_sysinfo.device_id;
+
+ client_response.login_credentials = &login_credentials;
+ client_response.system_info = &system_info;
+ snprintf(version_string, sizeof(version_string), "%s-%s", sp_sysinfo.client_name, sp_sysinfo.client_version);
+ client_response.version_string = version_string;
+
+ len = client_response_encrypted__get_packed_size(&client_response);
+ if (len > out_len)
+ return -1;
+
+ client_response_encrypted__pack(&client_response, out);
+
+ return len;
+}
+
+// From librespot-golang:
+// Mercury is the protocol implementation for Spotify Connect playback control and metadata fetching. It works as a
+// PUB/SUB system, where you, as an audio sink, subscribes to the events of a specified user (playlist changes) but
+// also access various metadata normally fetched by external players (tracks metadata, playlists, artists, etc).
+static ssize_t
+msg_make_mercury_req(uint8_t *out, size_t out_len, struct sp_mercury *mercury)
+{
+ Header header = HEADER__INIT;
+ uint8_t *ptr;
+ uint16_t be;
+ uint64_t be64;
+ uint8_t flags = 1; // Flags "final" according to librespot
+ size_t prefix_len;
+ size_t header_len;
+ size_t body_len;
+ int i;
+
+ prefix_len = sizeof(be) + sizeof(be64) + sizeof(flags) + sizeof(be) + sizeof(be);
+
+ if (prefix_len > out_len)
+ return -1; // Buffer too small
+
+ ptr = out;
+
+ be = htobe16(sizeof(be64));
+ memcpy(ptr, &be, sizeof(be)); // prefix_len += sizeof(be)
+ ptr += sizeof(be);
+
+ be64 = htobe64(mercury->seq);
+ memcpy(ptr, &be64, sizeof(be64)); // prefix_len += sizeof(be64)
+ ptr += sizeof(be64);
+
+ memcpy(ptr, &flags, sizeof(flags)); // prefix_len += sizeof(flags)
+ ptr += sizeof(flags);
+
+ be = htobe16(1 + mercury->parts_num); // = payload_len + 1 = "parts count"?
+ memcpy(ptr, &be, sizeof(be)); // prefix_len += sizeof(be)
+ ptr += sizeof(be);
+
+ header.uri = mercury->uri;
+ header.method = mercury->method; // "GET", "SUB" etc
+ header.content_type = mercury->content_type;
+
+ header_len = header__get_packed_size(&header);
+ if (header_len + prefix_len > out_len)
+ return -1; // Buffer too small
+
+ be = htobe16(header_len);
+ memcpy(ptr, &be, sizeof(be)); // prefix_len += sizeof(be)
+ ptr += sizeof(be);
+
+ assert(ptr - out == prefix_len);
+
+ header__pack(&header, ptr);
+ ptr += header_len;
+
+ body_len = 0;
+ for (i = 0; i < mercury->parts_num; i++)
+ {
+ body_len += sizeof(be) + mercury->parts[i].len;
+ if (body_len + header_len + prefix_len > out_len)
+ return -1; // Buffer too small
+
+ be = htobe16(mercury->parts[i].len);
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be);
+
+ memcpy(ptr, mercury->parts[i].data, mercury->parts[i].len);
+ ptr += mercury->parts[i].len;
+ }
+
+ assert(ptr - out == header_len + prefix_len + body_len);
+
+ return header_len + prefix_len + body_len;
+}
+
+static ssize_t
+msg_make_mercury_track_get(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ struct sp_mercury mercury = { 0 };
+ struct sp_channel *channel = session->now_streaming_channel;
+ char uri[256];
+ char *ptr;
+ int i;
+
+ assert(sizeof(uri) > sizeof(SP_MERCURY_URI_TRACK) + 2 * sizeof(channel->file.media_id));
+
+ ptr = uri;
+ ptr += sprintf(ptr, "%s", SP_MERCURY_URI_TRACK);
+
+ for (i = 0; i < sizeof(channel->file.media_id); i++)
+ ptr += sprintf(ptr, "%02x", channel->file.media_id[i]);
+
+ mercury.method = "GET";
+ mercury.seq = channel->id;
+ mercury.uri = uri;
+
+ return msg_make_mercury_req(out, out_len, &mercury);
+}
+
+static ssize_t
+msg_make_mercury_episode_get(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ struct sp_mercury mercury = { 0 };
+ struct sp_channel *channel = session->now_streaming_channel;
+ char uri[256];
+ char *ptr;
+ int i;
+
+ assert(sizeof(uri) > sizeof(SP_MERCURY_URI_EPISODE) + 2 * sizeof(channel->file.media_id));
+
+ ptr = uri;
+ ptr += sprintf(ptr, "%s", SP_MERCURY_URI_EPISODE);
+
+ for (i = 0; i < sizeof(channel->file.media_id); i++)
+ ptr += sprintf(ptr, "%02x", channel->file.media_id[i]);
+
+ mercury.method = "GET";
+ mercury.seq = channel->id;
+ mercury.uri = uri;
+
+ return msg_make_mercury_req(out, out_len, &mercury);
+}
+
+static ssize_t
+msg_make_audio_key_get(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ struct sp_channel *channel = session->now_streaming_channel;
+ size_t required_len;
+ uint32_t be32;
+ uint16_t be;
+ uint8_t *ptr;
+
+ required_len = sizeof(channel->file.id) + sizeof(channel->file.media_id) + sizeof(be32) + sizeof(be);
+
+ if (required_len > out_len)
+ return -1;
+
+ ptr = out;
+
+ memcpy(ptr, channel->file.id, sizeof(channel->file.id));
+ ptr += sizeof(channel->file.id);
+
+ memcpy(ptr, channel->file.media_id, sizeof(channel->file.media_id));
+ ptr += sizeof(channel->file.media_id);
+
+ be32 = htobe32(channel->id);
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32);
+
+ be = htobe16(0); // Unknown
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be);
+
+ return required_len;
+}
+
+static ssize_t
+msg_make_chunk_request(uint8_t *out, size_t out_len, struct sp_session *session)
+{
+ struct sp_channel *channel = session->now_streaming_channel;
+ uint8_t *ptr;
+ uint16_t be;
+ uint32_t be32;
+ size_t required_len;
+
+ if (!channel)
+ return -1;
+
+ ptr = out;
+
+ required_len = 3 * sizeof(be) + sizeof(channel->file.id) + 5 * sizeof(be32);
+ if (required_len > out_len)
+ return -1;
+
+ be = htobe16(channel->id);
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be); // x1
+
+ be = htobe16(1); // Unknown purpose
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be); // x2
+
+ be = htobe16(0); // Unknown purpose
+ memcpy(ptr, &be, sizeof(be));
+ ptr += sizeof(be); // x3
+
+ be32 = htobe32(0); // Unknown purpose
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32); // x1
+
+ be32 = htobe32(0x00009C40); // Unknown purpose
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32); // x2
+
+ be32 = htobe32(0x00020000); // Unknown purpose
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32); // x3
+
+ memcpy(ptr, channel->file.id, sizeof(channel->file.id));
+ ptr += sizeof(channel->file.id);
+
+ be32 = htobe32(channel->file.offset_words);
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32); // x4
+
+ be32 = htobe32(channel->file.offset_words + SP_CHUNK_LEN_WORDS);
+ memcpy(ptr, &be32, sizeof(be32));
+ ptr += sizeof(be32); // x5
+
+ assert(required_len == ptr - out);
+ assert(required_len == 46);
+
+ return required_len;
+}
+
+int
+msg_make(struct sp_message *msg, enum sp_msg_type type, struct sp_session *session)
+{
+ memset(msg, 0, sizeof(struct sp_message));
+ msg->type = type;
+
+ switch (type)
+ {
+ case MSG_TYPE_CLIENT_HELLO:
+ msg->len = msg_make_client_hello(msg->data, sizeof(msg->data), session);
+ msg->type_next = MSG_TYPE_CLIENT_RESPONSE_PLAINTEXT;
+ msg->add_version_header = true;
+ msg->response_handler = response_client_hello;
+ break;
+ case MSG_TYPE_CLIENT_RESPONSE_PLAINTEXT:
+ msg->len = msg_make_client_response_plaintext(msg->data, sizeof(msg->data), session);
+ msg->type_next = MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED;
+ msg->response_handler = NULL; // No response expected
+ break;
+ case MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED:
+ msg->len = msg_make_client_response_encrypted(msg->data, sizeof(msg->data), session);
+ msg->cmd = CmdLogin;
+ msg->encrypt = true;
+ msg->response_handler = response_generic;
+ break;
+ case MSG_TYPE_MERCURY_TRACK_GET:
+ msg->len = msg_make_mercury_track_get(msg->data, sizeof(msg->data), session);
+ msg->cmd = CmdMercuryReq;
+ msg->encrypt = true;
+ msg->type_next = MSG_TYPE_AUDIO_KEY_GET;
+ msg->response_handler = response_generic;
+ break;
+ case MSG_TYPE_MERCURY_EPISODE_GET:
+ msg->len = msg_make_mercury_episode_get(msg->data, sizeof(msg->data), session);
+ msg->cmd = CmdMercuryReq;
+ msg->encrypt = true;
+ msg->type_next = MSG_TYPE_AUDIO_KEY_GET;
+ msg->response_handler = response_generic;
+ break;
+ case MSG_TYPE_AUDIO_KEY_GET:
+ msg->len = msg_make_audio_key_get(msg->data, sizeof(msg->data), session);
+ msg->cmd = CmdRequestKey;
+ msg->encrypt = true;
+ msg->type_next = MSG_TYPE_CHUNK_REQUEST;
+ msg->response_handler = response_generic;
+ break;
+ case MSG_TYPE_CHUNK_REQUEST:
+ msg->len = msg_make_chunk_request(msg->data, sizeof(msg->data), session);
+ msg->cmd = CmdStreamChunk;
+ msg->encrypt = true;
+ msg->response_handler = response_generic;
+ break;
+ case MSG_TYPE_PONG:
+ msg->len = 4;
+ msg->cmd = CmdPong;
+ msg->encrypt = true;
+ memset(msg->data, 0, msg->len); // librespot just replies with zeroes
+ break;
+ default:
+ msg->len = -1;
+ }
+
+ return (msg->len < 0) ? -1 : 0;
+}
+
+int
+msg_send(struct sp_message *msg, struct sp_connection *conn)
+{
+ uint8_t pkt[4096];
+ ssize_t pkt_len;
+ int ret;
+
+ if (conn->is_encrypted)
+ pkt_len = packet_make_encrypted(pkt, sizeof(pkt), msg->cmd, msg->data, msg->len, &conn->encrypt);
+ else
+ pkt_len = packet_make_plain(pkt, sizeof(pkt), msg->data, msg->len, msg->add_version_header);
+
+ if (pkt_len < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Error constructing packet to Spotify");
+
+#ifndef DEBUG_MOCK
+ ret = send(conn->response_fd, pkt, pkt_len, 0);
+ if (ret != pkt_len)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Error sending packet to Spotify");
+
+// sp_cb.logmsg("Sent pkt type %d (cmd=0x%02x) with size %zu (fd=%d)\n", msg->type, msg->cmd, pkt_len, conn->response_fd);
+#else
+ ret = debug_mock_response(msg, conn);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Error mocking send packet to Spotify");
+
+ sp_cb.logmsg("Mocked send/response pkt type %d (cmd=0x%02x) with size %zu\n", msg->type, msg->cmd, pkt_len);
+#endif
+
+ // Save sent packet for MAC calculation later
+ if (!conn->handshake_completed)
+ evbuffer_add(conn->handshake_packets, pkt, pkt_len);
+
+ // Reset the disconnect timer
+ event_add(conn->idle_ev, &sp_idle_tv);
+
+ return 0;
+
+ error:
+ return ret;
+}
+
+int
+msg_pong(struct sp_session *session)
+{
+ struct sp_message msg;
+ int ret;
+
+ ret = msg_make(&msg, MSG_TYPE_PONG, session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Error constructing pong message to Spotify");
+
+ ret = msg_send(&msg, &session->conn);
+ if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+
+ return 0;
+
+ error:
+ return ret;
+}
diff --git a/src/inputs/librespot-c/src/connection.h b/src/inputs/librespot-c/src/connection.h
new file mode 100644
index 00000000..5bc3c005
--- /dev/null
+++ b/src/inputs/librespot-c/src/connection.h
@@ -0,0 +1,17 @@
+void
+ap_disconnect(struct sp_connection *conn);
+
+enum sp_error
+ap_connect(enum sp_msg_type type, struct sp_conn_callbacks *cb, struct sp_session *session);
+
+enum sp_error
+response_read(struct sp_session *session);
+
+int
+msg_make(struct sp_message *msg, enum sp_msg_type type, struct sp_session *session);
+
+int
+msg_send(struct sp_message *msg, struct sp_connection *conn);
+
+int
+msg_pong(struct sp_session *session);
diff --git a/src/inputs/librespot-c/src/crypto.c b/src/inputs/librespot-c/src/crypto.c
new file mode 100644
index 00000000..b056cf45
--- /dev/null
+++ b/src/inputs/librespot-c/src/crypto.c
@@ -0,0 +1,464 @@
+#include
+#include
+#include
+#include
+
+#include
+#include // for isdigit(), isupper(), islower()
+
+#include "librespot-c-internal.h" // For endian compat functions
+#include "crypto.h"
+
+
+/* ----------------------------------- Crypto ------------------------------- */
+
+#define SHA512_DIGEST_LENGTH 64
+#define bnum_new(bn) \
+ do { \
+ if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \
+ if (!gcry_check_version("1.5.4")) \
+ abort(); \
+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
+ } \
+ bn = gcry_mpi_new(1); \
+ } while (0)
+#define bnum_free(bn) gcry_mpi_release(bn)
+#define bnum_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
+#define bnum_is_zero(bn) (gcry_mpi_cmp_ui(bn, (unsigned long)0) == 0)
+#define bnum_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
+#define bnum_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
+#define bnum_hex2bn(bn, buf) gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0)
+#define bnum_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
+#define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b)
+#define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b)
+#define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b)
+#define bnum_mod(bn, a, b) gcry_mpi_mod(bn, a, b)
+typedef gcry_mpi_t bnum;
+__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
+{
+ gcry_mpi_powm(bn, y, q, p);
+}
+__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m)
+{
+ gcry_mpi_addm(bn, a, b, m);
+}
+
+static const uint8_t generator_bytes[] = { 0x2 };
+static const uint8_t prime_bytes[] =
+{
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
+ 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
+ 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
+ 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
+ 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
+ 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static void
+crypto_log(const char *fmt, ...)
+{
+ return;
+}
+
+/*
+static void
+crypto_hexdump(const char *msg, uint8_t *mem, size_t len)
+{
+ return;
+}
+*/
+
+int
+crypto_keys_set(struct crypto_keys *keys)
+{
+ bnum generator;
+ bnum prime;
+ bnum private_key;
+ bnum public_key;
+
+ bnum_bin2bn(generator, generator_bytes, sizeof(generator_bytes));
+ bnum_bin2bn(prime, prime_bytes, sizeof(prime_bytes));
+ bnum_new(private_key);
+ bnum_new(public_key);
+
+// bnum_random(private_key, 8 * (sizeof(keys->private_key) - 1)); // Not sure why it is 95 bytes?
+ bnum_random(private_key, 8 * sizeof(keys->private_key));
+
+ bnum_modexp(public_key, generator, private_key, prime);
+
+ memset(keys, 0, sizeof(struct crypto_keys));
+ bnum_bn2bin(private_key, keys->private_key, sizeof(keys->private_key));
+ bnum_bn2bin(public_key, keys->public_key, sizeof(keys->public_key));
+
+ bnum_free(generator);
+ bnum_free(prime);
+ bnum_free(private_key);
+ bnum_free(public_key);
+
+ return 0;
+}
+
+void
+crypto_shared_secret(uint8_t **shared_secret_bytes, size_t *shared_secret_bytes_len,
+ uint8_t *private_key_bytes, size_t private_key_bytes_len,
+ uint8_t *server_key_bytes, size_t server_key_bytes_len)
+{
+ bnum private_key;
+ bnum server_key;
+ bnum prime;
+ bnum shared_secret;
+
+ bnum_bin2bn(private_key, private_key_bytes, private_key_bytes_len);
+ bnum_bin2bn(server_key, server_key_bytes, server_key_bytes_len);
+ bnum_bin2bn(prime, prime_bytes, sizeof(prime_bytes));
+ bnum_new(shared_secret);
+
+ bnum_modexp(shared_secret, server_key, private_key, prime);
+
+ *shared_secret_bytes_len = bnum_num_bytes(shared_secret);
+ *shared_secret_bytes = malloc(*shared_secret_bytes_len);
+ bnum_bn2bin(shared_secret, *shared_secret_bytes, *shared_secret_bytes_len);
+
+ bnum_free(private_key);
+ bnum_free(server_key);
+ bnum_free(prime);
+ bnum_free(shared_secret);
+}
+
+// Calculates challenge and send/receive keys. The challenge is allocated,
+// caller must free
+int
+crypto_challenge(uint8_t **challenge, size_t *challenge_len,
+ uint8_t *send_key, size_t send_key_len,
+ uint8_t *recv_key, size_t recv_key_len,
+ uint8_t *packets, size_t packets_len,
+ uint8_t *shared_secret, size_t shared_secret_len)
+{
+ gcry_mac_hd_t hd = NULL;
+ uint8_t data[0x64];
+ uint8_t i;
+ size_t offset;
+ size_t len;
+
+ if (gcry_mac_open(&hd, GCRY_MAC_HMAC_SHA1, 0, NULL) != GPG_ERR_NO_ERROR)
+ goto error;
+
+ if (gcry_mac_setkey(hd, shared_secret, shared_secret_len) != GPG_ERR_NO_ERROR)
+ goto error;
+
+ offset = 0;
+ for (i = 1; i <= 6; i++)
+ {
+ gcry_mac_write(hd, packets, packets_len);
+ gcry_mac_write(hd, &i, sizeof(i));
+ len = sizeof(data) - offset;
+ gcry_mac_read(hd, data + offset, &len);
+ offset += len;
+ gcry_mac_reset(hd);
+ }
+
+ gcry_mac_close(hd);
+ hd = NULL;
+
+ assert(send_key_len == 32);
+ assert(recv_key_len == 32);
+
+ memcpy(send_key, data + 20, send_key_len);
+ memcpy(recv_key, data + 52, recv_key_len);
+
+ // Calculate challenge
+ if (gcry_mac_open(&hd, GCRY_MAC_HMAC_SHA1, 0, NULL) != GPG_ERR_NO_ERROR)
+ goto error;
+
+ if (gcry_mac_setkey(hd, data, 20) != GPG_ERR_NO_ERROR)
+ goto error;
+
+ gcry_mac_write(hd, packets, packets_len);
+
+ *challenge_len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA1);
+ *challenge = malloc(*challenge_len);
+ gcry_mac_read(hd, *challenge, challenge_len);
+ gcry_mac_close(hd);
+
+ return 0;
+
+ error:
+ if (hd)
+ gcry_mac_close(hd);
+ return -1;
+}
+
+// Inplace encryption, buf_len must be larger than plain_len so that the mac
+// can be added
+ssize_t
+crypto_encrypt(uint8_t *buf, size_t buf_len, size_t plain_len, struct crypto_cipher *cipher)
+{
+ uint32_t nonce;
+ uint8_t mac[4];
+ size_t encrypted_len;
+
+ encrypted_len = plain_len + sizeof(mac);
+ if (encrypted_len > buf_len)
+ return -1;
+
+ shn_key(&cipher->shannon, cipher->key, sizeof(cipher->key));
+
+ nonce = htobe32(cipher->nonce);
+ shn_nonce(&cipher->shannon, (uint8_t *)&nonce, sizeof(nonce));
+
+ shn_encrypt(&cipher->shannon, buf, plain_len);
+ shn_finish(&cipher->shannon, mac, sizeof(mac));
+
+ memcpy(buf + plain_len, mac, sizeof(mac));
+
+ cipher->nonce++;
+
+ return encrypted_len;
+}
+
+static size_t
+payload_len_get(uint8_t *header)
+{
+ uint16_t be;
+ memcpy(&be, header + 1, sizeof(be));
+ return (size_t)be16toh(be);
+}
+
+// *encrypted will consist of a header (3 bytes, encrypted), payload length (2
+// bytes, encrypted, BE), the encrypted payload and then the mac (4 bytes, not
+// encrypted). The return will be the number of bytes decrypted (incl mac if a
+// whole packet was decrypted). Zero means not enough data for a packet.
+ssize_t
+crypto_decrypt(uint8_t *encrypted, size_t encrypted_len, struct crypto_cipher *cipher)
+{
+ uint32_t nonce;
+ uint8_t mac[4];
+ size_t header_len = sizeof(cipher->last_header);
+ size_t payload_len;
+
+ crypto_log("Decrypting %zu bytes with nonce %u\n", encrypted_len, cipher->nonce);
+// crypto_hexdump("Key\n", cipher->key, sizeof(cipher->key));
+// crypto_hexdump("Encrypted\n", encrypted, encrypted_len);
+
+ // In case we didn't even receive the basics, header and mac, then return.
+ if (encrypted_len < header_len + sizeof(mac))
+ {
+ crypto_log("Waiting for %zu header bytes, have %zu\n", header_len + sizeof(mac), encrypted_len);
+ return 0;
+ }
+
+ // Will be zero if this is the first pass
+ payload_len = payload_len_get(cipher->last_header);
+ if (!payload_len)
+ {
+ shn_key(&cipher->shannon, cipher->key, sizeof(cipher->key));
+
+ nonce = htobe32(cipher->nonce);
+ shn_nonce(&cipher->shannon, (uint8_t *)&nonce, sizeof(nonce));
+
+ // Decrypt header to get the size, save it in case another pass will be
+ // required
+ shn_decrypt(&cipher->shannon, encrypted, header_len);
+ memcpy(cipher->last_header, encrypted, header_len);
+
+ payload_len = payload_len_get(cipher->last_header);
+
+// crypto_log("Payload len is %zu\n", payload_len);
+// crypto_hexdump("Decrypted header\n", encrypted, header_len);
+ }
+
+ // At this point the header is already decrypted, so now decrypt the payload
+ encrypted += header_len;
+ encrypted_len -= header_len + sizeof(mac);
+
+ // Not enough data for decrypting the entire packet
+ if (payload_len > encrypted_len)
+ {
+ crypto_log("Waiting for %zu payload bytes, have %zu\n", payload_len, encrypted_len);
+ return 0;
+ }
+
+ shn_decrypt(&cipher->shannon, encrypted, payload_len);
+
+// crypto_hexdump("Decrypted payload\n", encrypted, payload_len);
+
+ shn_finish(&cipher->shannon, mac, sizeof(mac));
+// crypto_hexdump("mac in\n", encrypted + payload_len, sizeof(mac));
+// crypto_hexdump("mac our\n", mac, sizeof(mac));
+ if (memcmp(mac, encrypted + payload_len, sizeof(mac)) != 0)
+ {
+ crypto_log("MAC VALIDATION FAILED\n"); // TODO
+ memset(cipher->last_header, 0, header_len);
+ return -1;
+ }
+
+ cipher->nonce++;
+ memset(cipher->last_header, 0, header_len);
+
+ return header_len + payload_len + sizeof(mac);
+}
+
+void
+crypto_aes_free(struct crypto_aes_cipher *cipher)
+{
+ if (!cipher || !cipher->aes)
+ return;
+
+ gcry_cipher_close(cipher->aes);
+}
+
+int
+crypto_aes_new(struct crypto_aes_cipher *cipher, uint8_t *key, size_t key_len, uint8_t *iv, size_t iv_len, const char **errmsg)
+{
+ gcry_error_t err;
+
+ err = gcry_cipher_open(&cipher->aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0);
+ if (err)
+ {
+ *errmsg = "Error initialising AES 128 CTR decryption";
+ goto error;
+ }
+
+ err = gcry_cipher_setkey(cipher->aes, key, key_len);
+ if (err)
+ {
+ *errmsg = "Could not set key for AES 128 CTR";
+ goto error;
+ }
+
+ err = gcry_cipher_setctr(cipher->aes, iv, iv_len);
+ if (err)
+ {
+ *errmsg = "Could not set iv for AES 128 CTR";
+ goto error;
+ }
+
+ memcpy(cipher->aes_iv, iv, iv_len);
+
+ return 0;
+
+ error:
+ crypto_aes_free(cipher);
+ return -1;
+}
+
+int
+crypto_aes_seek(struct crypto_aes_cipher *cipher, size_t seek, const char **errmsg)
+{
+ gcry_error_t err;
+ uint64_t be64;
+ uint64_t ctr;
+ uint8_t iv[16];
+ size_t iv_len;
+ size_t num_blocks;
+ size_t offset;
+
+ iv_len = gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES128);
+
+ assert(iv_len == sizeof(iv));
+
+ memcpy(iv, cipher->aes_iv, iv_len);
+ num_blocks = seek / iv_len;
+ offset = seek % iv_len;
+
+ // Advance the block counter
+ memcpy(&be64, iv + iv_len / 2, iv_len / 2);
+ ctr = be64toh(be64);
+ ctr += num_blocks;
+ be64 = htobe64(ctr);
+ memcpy(iv + iv_len / 2, &be64, iv_len / 2);
+
+ err = gcry_cipher_setctr(cipher->aes, iv, iv_len);
+ if (err)
+ {
+ *errmsg = "Could not set iv for AES 128 CTR";
+ return -1;
+ }
+
+ // Advance if the seek is into a block. iv is used because we have it already,
+ // it could be any buffer as long as it big enough
+ err = gcry_cipher_decrypt(cipher->aes, iv, offset, NULL, 0);
+ if (err)
+ {
+ *errmsg = "Error CTR offset while seeking";
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+crypto_aes_decrypt(uint8_t *encrypted, size_t encrypted_len, struct crypto_aes_cipher *cipher, const char **errmsg)
+{
+ gcry_error_t err;
+
+ err = gcry_cipher_decrypt(cipher->aes, encrypted, encrypted_len, NULL, 0);
+ if (err)
+ {
+ *errmsg = "Error CTR decrypting";
+ return -1;
+ }
+
+ return 0;
+}
+
+static unsigned char
+crypto_base62_digit(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ else if (islower(c))
+ return c - 'a' + 10;
+ else if (isupper(c))
+ return c - 'A' + 10 + 26;
+ else
+ return 0xff;
+}
+
+// base 62 to bin: 4gtj0ZuMWRw8WioT9SXsC2 -> 8c283882b29346829b8d021f52f5c2ce
+// 00AdHZ94Jb7oVdHVJmJsIU -> 004f421c7e934635aaf778180a8fd068
+// (note that the function prefixes with zeroes)
+int
+crypto_base62_to_bin(uint8_t *out, size_t out_len, const char *in)
+{
+ uint8_t u8;
+ bnum n;
+ bnum base;
+ bnum digit;
+ const char *ptr;
+ size_t len;
+
+ u8 = 62;
+ bnum_bin2bn(base, &u8, sizeof(u8));
+ bnum_new(n);
+
+ for (ptr = in; *ptr; ptr++)
+ {
+ // n = 62 * n + base62_digit(*p);
+ bnum_mul(n, n, base);
+ u8 = crypto_base62_digit(*ptr);
+
+ // Heavy on alloc's, but means we can use bnum compability wrapper
+ bnum_bin2bn(digit, &u8, sizeof(u8));
+ bnum_add(n, n, digit);
+ bnum_free(digit);
+ }
+
+ len = bnum_num_bytes(n);
+ if (len > out_len)
+ goto error;
+
+ memset(out, 0, out_len - len);
+ bnum_bn2bin(n, out + out_len - len, len);
+
+ bnum_free(n);
+ bnum_free(base);
+
+ return (int)out_len;
+
+ error:
+ bnum_free(n);
+ bnum_free(base);
+ return -1;
+}
diff --git a/src/inputs/librespot-c/src/crypto.h b/src/inputs/librespot-c/src/crypto.h
new file mode 100644
index 00000000..2cba11b6
--- /dev/null
+++ b/src/inputs/librespot-c/src/crypto.h
@@ -0,0 +1,75 @@
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include
+#include
+#include
+
+#include "shannon/Shannon.h"
+
+struct crypto_cipher
+{
+ shn_ctx shannon;
+ uint8_t key[32];
+ uint32_t nonce;
+ uint8_t last_header[3]; // uint8 cmd and uint16 BE size
+
+ void (*logmsg)(const char *fmt, ...);
+};
+
+struct crypto_aes_cipher
+{
+ gcry_cipher_hd_t aes;
+ uint8_t key[16];
+ uint8_t aes_iv[16];
+};
+
+struct crypto_keys
+{
+ uint8_t private_key[96];
+ uint8_t public_key[96];
+
+ uint8_t *shared_secret;
+ size_t shared_secret_len;
+};
+
+
+void
+crypto_shared_secret(uint8_t **shared_secret_bytes, size_t *shared_secret_bytes_len,
+ uint8_t *private_key_bytes, size_t private_key_bytes_len,
+ uint8_t *server_key_bytes, size_t server_key_bytes_len);
+
+int
+crypto_challenge(uint8_t **challenge, size_t *challenge_len,
+ uint8_t *send_key, size_t send_key_len,
+ uint8_t *recv_key, size_t recv_key_len,
+ uint8_t *packets, size_t packets_len,
+ uint8_t *shared_secret, size_t shared_secret_len);
+
+int
+crypto_keys_set(struct crypto_keys *keys);
+
+ssize_t
+crypto_encrypt(uint8_t *buf, size_t buf_len, size_t plain_len, struct crypto_cipher *cipher);
+
+ssize_t
+crypto_decrypt(uint8_t *encrypted, size_t encrypted_len, struct crypto_cipher *cipher);
+
+
+void
+crypto_aes_free(struct crypto_aes_cipher *cipher);
+
+int
+crypto_aes_new(struct crypto_aes_cipher *cipher, uint8_t *key, size_t key_len, uint8_t *iv, size_t iv_len, const char **errmsg);
+
+int
+crypto_aes_seek(struct crypto_aes_cipher *cipher, size_t seek, const char **errmsg);
+
+int
+crypto_aes_decrypt(uint8_t *encrypted, size_t encrypted_len, struct crypto_aes_cipher *cipher, const char **errmsg);
+
+
+int
+crypto_base62_to_bin(uint8_t *out, size_t out_len, const char *in);
+
+#endif /* __CRYPTO_H__ */
diff --git a/src/inputs/librespot-c/src/librespot-c-internal.h b/src/inputs/librespot-c/src/librespot-c-internal.h
new file mode 100644
index 00000000..a9ed6f30
--- /dev/null
+++ b/src/inputs/librespot-c/src/librespot-c-internal.h
@@ -0,0 +1,343 @@
+#ifndef __LIBRESPOT_C_INTERNAL_H__
+#define __LIBRESPOT_C_INTERNAL_H__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#ifdef HAVE_ENDIAN_H
+# include
+#elif defined(HAVE_SYS_ENDIAN_H)
+# include
+#elif defined(HAVE_LIBKERN_OSBYTEORDER_H)
+#include
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#endif
+
+#include "librespot-c.h"
+#include "crypto.h"
+
+#include "proto/keyexchange.pb-c.h"
+#include "proto/authentication.pb-c.h"
+#include "proto/mercury.pb-c.h"
+#include "proto/metadata.pb-c.h"
+
+#define SP_AP_RESOLVE_URL "https://APResolve.spotify.com/"
+#define SP_AP_RESOLVE_KEY "ap_list"
+
+// Disconnect from AP after this number of secs idle
+#define SP_AP_DISCONNECT_SECS 60
+
+// Max wait for AP to respond
+#define SP_AP_TIMEOUT_SECS 10
+
+// If client hasn't requested anything in particular
+#define SP_BITRATE_DEFAULT SP_BITRATE_320
+
+// A "mercury" response may contain multiple parts (e.g. multiple tracks), even
+// though this implenentation currently expects just one.
+#define SP_MERCURY_MAX_PARTS 32
+
+// librespot uses /3, but -golang and -java use /4
+#define SP_MERCURY_URI_TRACK "hm://metadata/4/track/"
+#define SP_MERCURY_URI_EPISODE "hm://metadata/4/episode/"
+
+// Special Spotify header that comes before the actual Ogg data
+#define SP_OGG_HEADER_LEN 167
+
+// For now we just always use channel 0, expand with more if needed
+#define SP_DEFAULT_CHANNEL 0
+
+// Download in chunks of 32768 bytes. The chunks shouldn't be too large because
+// it makes seeking slow (seeking involves jumping around in the file), but
+// large enough that the file can be probed from the first chunk.
+#define SP_CHUNK_LEN_WORDS 1024 * 8
+
+// Shorthand for error handling
+#define RETURN_ERROR(r, m) \
+ do { ret = (r); sp_errmsg = (m); goto error; } while(0)
+
+enum sp_error
+{
+ SP_OK_OTHER = 3,
+ SP_OK_WAIT = 2,
+ SP_OK_DATA = 1,
+ SP_OK_DONE = 0,
+ SP_ERR_OOM = -1,
+ SP_ERR_INVALID = -2,
+ SP_ERR_DECRYPTION = -3,
+ SP_ERR_WRITE = -4,
+ SP_ERR_NOCONNECTION = -5,
+ SP_ERR_OCCUPIED = -6,
+ SP_ERR_NOSESSION = -7,
+ SP_ERR_LOGINFAILED = -8,
+ SP_ERR_TIMEOUT = -9,
+};
+
+enum sp_msg_type
+{
+ MSG_TYPE_NONE,
+ MSG_TYPE_CLIENT_HELLO,
+ MSG_TYPE_CLIENT_RESPONSE_PLAINTEXT,
+ MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED,
+ MSG_TYPE_PONG,
+ MSG_TYPE_MERCURY_TRACK_GET,
+ MSG_TYPE_MERCURY_EPISODE_GET,
+ MSG_TYPE_AUDIO_KEY_GET,
+ MSG_TYPE_CHUNK_REQUEST,
+};
+
+enum sp_media_type
+{
+ SP_MEDIA_UNKNOWN,
+ SP_MEDIA_TRACK,
+ SP_MEDIA_EPISODE,
+};
+
+// From librespot-golang
+enum sp_cmd_type
+{
+ CmdNone = 0x00,
+ CmdSecretBlock = 0x02,
+ CmdPing = 0x04,
+ CmdStreamChunk = 0x08,
+
+ CmdStreamChunkRes = 0x09,
+ CmdChannelError = 0x0a,
+ CmdChannelAbort = 0x0b,
+ CmdRequestKey = 0x0c,
+ CmdAesKey = 0x0d,
+ CmdAesKeyError = 0x0e,
+
+ CmdImage = 0x19,
+ CmdCountryCode = 0x1b,
+
+ CmdPong = 0x49,
+ CmdPongAck = 0x4a,
+ CmdPause = 0x4b,
+
+ CmdProductInfo = 0x50,
+ CmdLegacyWelcome = 0x69,
+
+ CmdLicenseVersion = 0x76,
+ CmdLogin = 0xab,
+ CmdAPWelcome = 0xac,
+ CmdAuthFailure = 0xad,
+
+ CmdMercuryReq = 0xb2,
+ CmdMercurySub = 0xb3,
+ CmdMercuryUnsub = 0xb4,
+};
+
+struct sp_cmdargs
+{
+ struct sp_session *session;
+ struct sp_credentials *credentials;
+ struct sp_metadata *metadata;
+ const char *username;
+ const char *password;
+ uint8_t *stored_cred;
+ size_t stored_cred_len;
+ const char *token;
+ const char *path;
+ int fd_read;
+ int fd_write;
+ size_t seek_pos;
+ enum sp_bitrates bitrate;
+
+ sp_progress_cb progress_cb;
+ void *cb_arg;
+};
+
+struct sp_conn_callbacks
+{
+ struct event_base *evbase;
+
+ event_callback_fn response_cb;
+ event_callback_fn timeout_cb;
+};
+
+struct sp_message
+{
+ enum sp_msg_type type;
+ enum sp_cmd_type cmd;
+
+ bool encrypt;
+ bool add_version_header;
+
+ enum sp_msg_type type_next;
+ enum sp_msg_type type_queued;
+
+ int (*response_handler)(uint8_t *msg, size_t msg_len, struct sp_session *session);
+
+ ssize_t len;
+ uint8_t data[4096];
+};
+
+struct sp_connection
+{
+ bool is_connected;
+ bool is_encrypted;
+
+ // Resolved access point
+ char *ap_address;
+ unsigned short ap_port;
+
+ // Where we receive data from Spotify
+ int response_fd;
+ struct event *response_ev;
+
+ // Connection timers
+ struct event *idle_ev;
+ struct event *timeout_ev;
+
+ // Holds incoming data
+ struct evbuffer *incoming;
+
+ // Buffer holding client hello and ap response, since they are needed for
+ // MAC calculation
+ bool handshake_completed;
+ struct evbuffer *handshake_packets;
+
+ struct crypto_keys keys;
+ struct crypto_cipher encrypt;
+ struct crypto_cipher decrypt;
+};
+
+struct sp_mercury
+{
+ char *uri;
+ char *method;
+ char *content_type;
+
+ uint64_t seq;
+
+ uint16_t parts_num;
+ struct sp_mercury_parts
+ {
+ uint8_t *data;
+ size_t len;
+
+ Track *track;
+ } parts[SP_MERCURY_MAX_PARTS];
+};
+
+struct sp_file
+{
+ uint8_t id[20];
+
+ char *path; // The Spotify URI, e.g. spotify:episode:3KRjRyqv5ou5SilNMYBR4E
+ uint8_t media_id[16]; // Decoded value of the URIs base62
+ enum sp_media_type media_type; // track or episode from URI
+
+ uint8_t key[16];
+
+ uint16_t channel_id;
+
+ // Length and download progress
+ size_t len_words; // Length of file in words (32 bit)
+ size_t offset_words;
+ size_t received_words;
+ bool end_of_file;
+ bool end_of_chunk;
+ bool open;
+
+ struct crypto_aes_cipher decrypt;
+};
+
+struct sp_channel_header
+{
+ uint16_t len;
+ uint8_t id;
+ uint8_t *data;
+ size_t data_len;
+};
+
+struct sp_channel_body
+{
+ uint8_t *data;
+ size_t data_len;
+};
+
+struct sp_channel
+{
+ int id;
+
+ bool is_allocated;
+ bool is_writing;
+ bool is_data_mode;
+ bool is_spotify_header_received;
+ size_t seek_pos;
+ size_t seek_align;
+
+ // pipe where we write audio data
+ int audio_fd[2];
+ // Triggers when fd is writable
+ struct event *audio_write_ev;
+ // Storage of audio until it can be written to the pipe
+ struct evbuffer *audio_buf;
+ // How much we have written to the fd (only used for debug)
+ size_t audio_written_len;
+
+ struct sp_file file;
+
+ // Latest header and body received
+ struct sp_channel_header header;
+ struct sp_channel_body body;
+
+ // Callbacks made during playback
+ sp_progress_cb progress_cb;
+ void *cb_arg;
+};
+
+// Linked list of sessions
+struct sp_session
+{
+ struct sp_connection conn;
+
+ bool is_logged_in;
+ struct sp_credentials credentials;
+ char country[3]; // Incl null term
+
+ enum sp_bitrates bitrate_preferred;
+
+ struct sp_channel channels[8];
+
+ // Points to the channel that is streaming, and via this information about
+ // the current track is also available
+ struct sp_channel *now_streaming_channel;
+
+ // Go to next step in a request sequence
+ struct event *continue_ev;
+
+ // Current (or last) message being processed
+ enum sp_msg_type msg_type_queued;
+ enum sp_msg_type msg_type_next;
+ int (*response_handler)(uint8_t *, size_t, struct sp_session *);
+
+ struct sp_session *next;
+};
+
+struct sp_err_map
+{
+ ErrorCode errorcode;
+ const char *errmsg;
+};
+
+extern struct sp_callbacks sp_cb;
+extern struct sp_sysinfo sp_sysinfo;
+extern const char *sp_errmsg;
+
+#endif // __LIBRESPOT_C_INTERNAL_H__
diff --git a/src/inputs/librespot-c/src/librespot-c.c b/src/inputs/librespot-c/src/librespot-c.c
new file mode 100644
index 00000000..1a794d68
--- /dev/null
+++ b/src/inputs/librespot-c/src/librespot-c.c
@@ -0,0 +1,977 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+
+/*
+Illustration of the general flow, where receive and writing the result are async
+operations. For some commands, e.g. open and seek, the entire sequence is
+encapsulated in a sync command, which doesn't return until final "done, error or
+timeout". The command play is async, so all "done/error/timeout" is returned via
+callbacks. Also, play will loop the flow, i.e. after writing a chunk of data it
+will go back and ask for the next chunk of data from Spotify.
+
+In some cases there is no result to write, or no reponse expected, but then the
+events for proceeding are activated directly.
+
+ |---next----*------------next-------------*----------next----------*
+ v | | |
+----------> start/send ------------------> recv ----------------> write result
+^ | ^ | ^ |
+|---reconnect---* |------wait------* |------wait------*
+ | | |
+ v v v
+ done/error done/error/timeout done/error
+
+"next": on success, continue with next command
+"wait": waiting for more data or for write to become possible
+"timeout": receive or write took too long to complete
+*/
+
+#include
+
+#include "librespot-c-internal.h"
+#include "commands.h"
+#include "connection.h"
+#include "channel.h"
+
+/* TODO list
+
+ - protect against DOS
+*/
+
+
+/* -------------------------------- Globals --------------------------------- */
+
+// Shared
+struct sp_callbacks sp_cb;
+struct sp_sysinfo sp_sysinfo;
+const char *sp_errmsg;
+
+static struct sp_session *sp_sessions;
+
+static bool sp_initialized;
+
+static pthread_t sp_tid;
+static struct event_base *sp_evbase;
+static struct commands_base *sp_cmdbase;
+
+static struct timeval sp_response_timeout_tv = { SP_AP_TIMEOUT_SECS, 0 };
+
+
+// Forwards
+static int
+request_make(enum sp_msg_type type, struct sp_session *session);
+
+
+/* -------------------------------- Session --------------------------------- */
+
+static void
+session_free(struct sp_session *session)
+{
+ if (!session)
+ return;
+
+ channel_free_all(session);
+
+ ap_disconnect(&session->conn);
+
+ event_free(session->continue_ev);
+ free(session);
+}
+
+static void
+session_cleanup(struct sp_session *session)
+{
+ struct sp_session *s;
+
+ if (!session)
+ return;
+
+ if (session == sp_sessions)
+ sp_sessions = session->next;
+ else
+ {
+ for (s = sp_sessions; s && (s->next != session); s = s->next)
+ ; /* EMPTY */
+
+ if (s)
+ s->next = session->next;
+ }
+
+ session_free(session);
+}
+
+static int
+session_new(struct sp_session **out, struct sp_cmdargs *cmdargs, event_callback_fn cb)
+{
+ struct sp_session *session;
+ int ret;
+
+ session = calloc(1, sizeof(struct sp_session));
+ if (!session)
+ RETURN_ERROR(SP_ERR_OOM, "Out of memory creating session");
+
+ session->continue_ev = evtimer_new(sp_evbase, cb, session);
+ if (!session->continue_ev)
+ RETURN_ERROR(SP_ERR_OOM, "Out of memory creating session event");
+
+ snprintf(session->credentials.username, sizeof(session->credentials.username), "%s", cmdargs->username);
+
+ if (cmdargs->stored_cred)
+ {
+ if (cmdargs->stored_cred_len > sizeof(session->credentials.stored_cred))
+ RETURN_ERROR(SP_ERR_INVALID, "Invalid stored credential");
+
+ session->credentials.stored_cred_len = cmdargs->stored_cred_len;
+ memcpy(session->credentials.stored_cred, cmdargs->stored_cred, session->credentials.stored_cred_len);
+ }
+ else if (cmdargs->token)
+ {
+ if (strlen(cmdargs->token) > sizeof(session->credentials.token))
+ RETURN_ERROR(SP_ERR_INVALID, "Invalid token");
+
+ session->credentials.token_len = strlen(cmdargs->token);
+ memcpy(session->credentials.token, cmdargs->token, session->credentials.token_len);
+ }
+ else
+ {
+ snprintf(session->credentials.password, sizeof(session->credentials.password), "%s", cmdargs->password);
+ }
+
+ session->bitrate_preferred = SP_BITRATE_DEFAULT;
+
+ // Add to linked list
+ session->next = sp_sessions;
+ sp_sessions = session;
+
+ *out = session;
+
+ return 0;
+
+ error:
+ session_free(session);
+ return ret;
+}
+
+static int
+session_check(struct sp_session *session)
+{
+ struct sp_session *s;
+
+ for (s = sp_sessions; s; s = s->next)
+ {
+ if (s == session)
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct sp_session *
+session_find_by_fd(int fd)
+{
+ struct sp_session *s;
+
+ for (s = sp_sessions; s; s = s->next)
+ {
+ if (s->now_streaming_channel && s->now_streaming_channel->audio_fd[0] == fd)
+ return s;
+ }
+
+ return NULL;
+}
+
+static void
+session_return(struct sp_session *session, enum sp_error err)
+{
+ struct sp_channel *channel = session->now_streaming_channel;
+ int ret;
+
+ ret = commands_exec_returnvalue(sp_cmdbase);
+ if (ret == 0) // Here we are async, i.e. no pending command
+ {
+ // track_write() completed, close the write end which means reader will
+ // get an EOF
+ if (channel && channel->is_writing && err == SP_OK_DONE)
+ channel_stop(channel);
+ return;
+ }
+
+ commands_exec_end(sp_cmdbase, err);
+}
+
+// Rolls back from an error situation. If it is a failed login then the session
+// will be closed, but if it just a connection timeout we keep the session, but
+// drop the ongoing download.
+static void
+session_error(struct sp_session *session, enum sp_error err)
+{
+ struct sp_channel *channel = session->now_streaming_channel;
+
+ sp_cb.logmsg("Session error: %d\n", err);
+
+ session_return(session, err);
+
+ if (!session->is_logged_in)
+ {
+ session_cleanup(session);
+ return;
+ }
+
+ channel_free(channel);
+ session->now_streaming_channel = NULL;
+}
+
+
+/* ------------------------ Main sequence control --------------------------- */
+
+// This callback must determine if a new request should be made, or if we are
+// done and should return to caller
+static void
+continue_cb(int fd, short what, void *arg)
+{
+ struct sp_session *session = arg;
+ enum sp_msg_type type = MSG_TYPE_NONE;
+ int ret;
+
+ // type_next has priority, since this is what we use to chain a sequence, e.g.
+ // the handshake sequence. type_queued is what comes after, e.g. first a
+ // handshake (type_next) and then a chunk request (type_queued)
+ if (session->msg_type_next != MSG_TYPE_NONE)
+ {
+// sp_cb.logmsg(">>> msg_next >>>\n");
+
+ type = session->msg_type_next;
+ session->msg_type_next = MSG_TYPE_NONE;
+ }
+ else if (session->msg_type_queued != MSG_TYPE_NONE)
+ {
+// sp_cb.logmsg(">>> msg_queued >>>\n");
+
+ type = session->msg_type_queued;
+ session->msg_type_queued = MSG_TYPE_NONE;
+ }
+
+ if (type != MSG_TYPE_NONE)
+ {
+ ret = request_make(type, session);
+ if (ret < 0)
+ session_error(session, ret);
+ }
+ else
+ session_return(session, SP_OK_DONE); // All done, yay!
+}
+
+// This callback is triggered by response_cb when the message response handler
+// said that there was data to write. If not all data can be written in one pass
+// it will re-add the event.
+static void
+audio_write_cb(int fd, short what, void *arg)
+{
+ struct sp_session *session = arg;
+ struct sp_channel *channel = session->now_streaming_channel;
+ int ret;
+
+ if (!channel)
+ RETURN_ERROR(SP_ERR_INVALID, "Write result request, but not streaming right now");
+
+ ret = channel_data_write(channel);
+ switch (ret)
+ {
+ case SP_OK_WAIT:
+ event_add(channel->audio_write_ev, NULL);
+ break;
+ case SP_OK_DONE:
+ event_active(session->continue_ev, 0, 0);
+ break;
+ default:
+ goto error;
+ }
+
+ return;
+
+ error:
+ session_error(session, ret);
+}
+
+static void
+timeout_cb(int fd, short what, void *arg)
+{
+ struct sp_session *session = arg;
+
+ sp_errmsg = "Timeout waiting for Spotify response";
+
+ session_error(session, SP_ERR_TIMEOUT);
+}
+
+static void
+response_cb(int fd, short what, void *arg)
+{
+ struct sp_session *session = arg;
+ struct sp_connection *conn = &session->conn;
+ struct sp_channel *channel = session->now_streaming_channel;
+ int ret;
+
+ if (what == EV_READ)
+ {
+ ret = evbuffer_read(conn->incoming, fd, -1);
+ if (ret == 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "The access point disconnected");
+ else if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Connection to Spotify returned an error");
+
+// sp_cb.logmsg("Received data len %d\n", ret);
+ }
+
+ ret = response_read(session);
+ switch (ret)
+ {
+ case SP_OK_WAIT: // Incomplete, wait for more data
+ break;
+ case SP_OK_DATA:
+ if (channel->is_writing && !channel->file.end_of_file)
+ session->msg_type_next = MSG_TYPE_CHUNK_REQUEST;
+ if (channel->progress_cb)
+ channel->progress_cb(channel->audio_fd[0], channel->cb_arg, 4 * channel->file.received_words - SP_OGG_HEADER_LEN, 4 * channel->file.len_words - SP_OGG_HEADER_LEN);
+
+ event_del(conn->timeout_ev);
+ event_add(channel->audio_write_ev, NULL);
+ break;
+ case SP_OK_DONE: // Got the response we expected, but possibly more to process
+ if (evbuffer_get_length(conn->incoming) > 0)
+ event_active(conn->response_ev, 0, 0);
+
+ event_del(conn->timeout_ev);
+ event_active(session->continue_ev, 0, 0);
+ break;
+ case SP_OK_OTHER: // Not the response we were waiting for, check for other
+ if (evbuffer_get_length(conn->incoming) > 0)
+ event_active(conn->response_ev, 0, 0);
+ break;
+ default:
+ event_del(conn->timeout_ev);
+ goto error;
+ }
+
+ return;
+
+ error:
+ session_error(session, ret);
+}
+
+static int
+relogin(enum sp_msg_type type, struct sp_session *session)
+{
+ int ret;
+
+ if (session->msg_type_queued != MSG_TYPE_NONE)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Cannot send message, another request is waiting for handshake");
+
+ ret = request_make(MSG_TYPE_CLIENT_HELLO, session);
+ if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+
+ // In case we lost connection to the AP we have to make a new handshake for
+ // the non-handshake message types. So queue the message until the handshake
+ // is complete.
+ session->msg_type_queued = type;
+ return 0;
+
+ error:
+ return ret;
+}
+
+static int
+request_make(enum sp_msg_type type, struct sp_session *session)
+{
+ struct sp_message msg;
+ struct sp_connection *conn = &session->conn;
+ struct sp_conn_callbacks cb = { sp_evbase, response_cb, timeout_cb };
+ int ret;
+
+ // Make sure the connection is in a state suitable for sending this message
+ ret = ap_connect(type, &cb, session);
+ if (ret == SP_OK_WAIT)
+ return relogin(type, session); // Can't proceed right now, the handshake needs to complete first
+ else if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+
+ ret = msg_make(&msg, type, session);
+ if (type == MSG_TYPE_CLIENT_RESPONSE_ENCRYPTED)
+ memset(session->credentials.password, 0, sizeof(session->credentials.password));
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_INVALID, "Error constructing message to Spotify");
+
+ if (msg.encrypt)
+ conn->is_encrypted = true;
+
+ ret = msg_send(&msg, conn);
+ if (ret < 0)
+ RETURN_ERROR(ret, sp_errmsg);
+
+ // Only start timeout timer if a response is expected, otherwise go straight
+ // to next message
+ if (msg.response_handler)
+ event_add(conn->timeout_ev, &sp_response_timeout_tv);
+ else
+ event_active(session->continue_ev, 0, 0);
+
+ session->msg_type_next = msg.type_next;
+ session->response_handler = msg.response_handler;
+
+ return 0;
+
+ error:
+ return ret;
+}
+
+
+/* ----------------------------- Implementation ----------------------------- */
+
+// This command is async
+static enum command_state
+track_write(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session;
+ struct sp_channel *channel;
+ int ret;
+
+ *retval = 0;
+
+ session = session_find_by_fd(cmdargs->fd_read);
+ if (!session)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Cannot play track, no valid session found");
+
+ channel = session->now_streaming_channel;
+ if (!channel || !channel->is_allocated)
+ RETURN_ERROR(SP_ERR_INVALID, "No active channel to play, has track been opened?");
+
+ channel_play(channel);
+
+ ret = request_make(MSG_TYPE_CHUNK_REQUEST, session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not send request for audio chunk");
+
+ channel->progress_cb = cmdargs->progress_cb;
+ channel->cb_arg = cmdargs->cb_arg;
+
+ return COMMAND_END;
+
+ error:
+ sp_cb.logmsg("Error %d: %s", ret, sp_errmsg);
+
+ return COMMAND_END;
+}
+
+static enum command_state
+track_pause(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session;
+ struct sp_channel *channel;
+ int ret;
+
+ session = session_find_by_fd(cmdargs->fd_read);
+ if (!session)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Cannot pause track, no valid session found");
+
+ channel = session->now_streaming_channel;
+ if (!channel || !channel->is_allocated)
+ RETURN_ERROR(SP_ERR_INVALID, "No active channel to pause, has track been opened?");
+
+ // If we are playing we are in the process of downloading a chunk, and in that
+ // case we need that to complete before doing anything else with the channel,
+ // e.g. reset it as track_close() does.
+ if (!channel->is_writing)
+ {
+ *retval = 0;
+ return COMMAND_END;
+ }
+
+ channel_pause(channel);
+
+ *retval = 1;
+ return COMMAND_PENDING;
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+track_seek(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session;
+ struct sp_channel *channel;
+ int ret;
+
+ session = session_find_by_fd(cmdargs->fd_read);
+ if (!session)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Cannot seek, no valid session found");
+
+ channel = session->now_streaming_channel;
+ if (!channel || !channel->is_allocated)
+ RETURN_ERROR(SP_ERR_INVALID, "No active channel to seek, has track been opened?");
+ else if (channel->is_writing)
+ RETURN_ERROR(SP_ERR_INVALID, "Seeking during playback not currently supported");
+
+ // This operation is not safe during chunk downloading because it changes the
+ // AES decryptor to match the new position. It also flushes the pipe.
+ channel_seek(channel, cmdargs->seek_pos);
+
+ ret = request_make(MSG_TYPE_CHUNK_REQUEST, session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not send track seek request");
+
+ *retval = 1;
+ return COMMAND_PENDING;
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+track_close(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session;
+ int ret;
+
+ session = session_find_by_fd(cmdargs->fd_read);
+ if (!session)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Cannot close track, no valid session found");
+
+ channel_free(session->now_streaming_channel);
+ session->now_streaming_channel = NULL;
+
+ *retval = 0;
+ return COMMAND_END;
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+media_open(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session = cmdargs->session;
+ struct sp_channel *channel = NULL;
+ enum sp_msg_type type;
+ int ret;
+
+ ret = session_check(session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Cannot open media, session is invalid");
+
+ if (session->now_streaming_channel)
+ RETURN_ERROR(SP_ERR_OCCUPIED, "Already getting media");
+
+ ret = channel_new(&channel, session, cmdargs->path, sp_evbase, audio_write_cb);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_OOM, "Could not setup a channel");
+
+ cmdargs->fd_read = channel->audio_fd[0];
+
+ // Must be set before calling request_make() because this info is needed for
+ // making the request
+ session->now_streaming_channel = channel;
+
+ if (channel->file.media_type == SP_MEDIA_TRACK)
+ type = MSG_TYPE_MERCURY_TRACK_GET;
+ else if (channel->file.media_type == SP_MEDIA_EPISODE)
+ type = MSG_TYPE_MERCURY_EPISODE_GET;
+ else
+ RETURN_ERROR(SP_ERR_INVALID, "Unknown media type in Spotify path");
+
+ // Kicks of a sequence where we first get file info, then get the AES key and
+ // then the first chunk (incl. headers)
+ ret = request_make(type, session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOCONNECTION, "Could not send media request");
+
+ *retval = 1;
+ return COMMAND_PENDING;
+
+ error:
+ if (channel)
+ {
+ session->now_streaming_channel = NULL;
+ channel_free(channel);
+ }
+
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+media_open_bh(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+
+ if (*retval == SP_OK_DONE)
+ *retval = cmdargs->fd_read;
+
+ return COMMAND_END;
+}
+
+static enum command_state
+login(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session = NULL;
+ int ret;
+
+ ret = session_new(&session, cmdargs, continue_cb);
+ if (ret < 0)
+ goto error;
+
+ ret = request_make(MSG_TYPE_CLIENT_HELLO, session);
+ if (ret < 0)
+ goto error;
+
+ cmdargs->session = session;
+
+ *retval = 1; // Pending command_exec_sync, i.e. response from Spotify
+ return COMMAND_PENDING;
+
+ error:
+ session_cleanup(session);
+
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+login_bh(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+
+ if (*retval == SP_OK_DONE)
+ cmdargs->session->is_logged_in = true;
+ else
+ cmdargs->session = NULL;
+
+ return COMMAND_END;
+}
+
+static enum command_state
+logout(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session = cmdargs->session;
+ int ret;
+
+ ret = session_check(session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Session has disappeared, cannot logout");
+
+ session_cleanup(session);
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+metadata_get(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session;
+ struct sp_metadata *metadata = cmdargs->metadata;
+ int ret = 0;
+
+ session = session_find_by_fd(cmdargs->fd_read);
+ if (!session || !session->now_streaming_channel)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Session has disappeared, cannot get metadata");
+
+ memset(metadata, 0, sizeof(struct sp_metadata));
+ metadata->file_len = 4 * session->now_streaming_channel->file.len_words - SP_OGG_HEADER_LEN;;
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+bitrate_set(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session = cmdargs->session;
+ int ret;
+
+ if (cmdargs->bitrate == SP_BITRATE_ANY)
+ cmdargs->bitrate = SP_BITRATE_DEFAULT;
+
+ ret = session_check(session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Session has disappeared, cannot set bitrate");
+
+ session->bitrate_preferred = cmdargs->bitrate;
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+static enum command_state
+credentials_get(void *arg, int *retval)
+{
+ struct sp_cmdargs *cmdargs = arg;
+ struct sp_session *session = cmdargs->session;
+ struct sp_credentials *credentials = cmdargs->credentials;
+ int ret;
+
+ ret = session_check(session);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_NOSESSION, "Session has disappeared, cannot get credentials");
+
+ memcpy(credentials, &session->credentials, sizeof(struct sp_credentials));
+
+ error:
+ *retval = ret;
+ return COMMAND_END;
+}
+
+
+/* ------------------------------ Event loop -------------------------------- */
+
+static void *
+librespotc(void *arg)
+{
+ event_base_dispatch(sp_evbase);
+
+ pthread_exit(NULL);
+}
+
+
+/* ---------------------------------- API ----------------------------------- */
+
+int
+librespotc_open(const char *path, struct sp_session *session)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.session = session;
+ cmdargs.path = path;
+
+ return commands_exec_sync(sp_cmdbase, media_open, media_open_bh, &cmdargs);
+}
+
+int
+librespotc_seek(int fd, size_t pos)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.fd_read = fd;
+ cmdargs.seek_pos = pos;
+
+ return commands_exec_sync(sp_cmdbase, track_seek, NULL, &cmdargs);
+}
+
+// Starts writing audio for the caller to read from the file descriptor
+void
+librespotc_write(int fd, sp_progress_cb progress_cb, void *cb_arg)
+{
+ struct sp_cmdargs *cmdargs;
+
+ cmdargs = calloc(1, sizeof(struct sp_cmdargs));
+
+ cmdargs->fd_read = fd;
+ cmdargs->progress_cb = progress_cb;
+ cmdargs->cb_arg = cb_arg;
+
+ commands_exec_async(sp_cmdbase, track_write, cmdargs);
+}
+
+int
+librespotc_close(int fd)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.fd_read = fd;
+
+ return commands_exec_sync(sp_cmdbase, track_pause, track_close, &cmdargs);
+}
+
+struct sp_session *
+librespotc_login_password(const char *username, const char *password)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.username = username;
+ cmdargs.password = password;
+
+ commands_exec_sync(sp_cmdbase, login, login_bh, &cmdargs);
+
+ return cmdargs.session;
+}
+
+struct sp_session *
+librespotc_login_stored_cred(const char *username, uint8_t *stored_cred, size_t stored_cred_len)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.username = username;
+ cmdargs.stored_cred = stored_cred;
+ cmdargs.stored_cred_len = stored_cred_len;
+
+ commands_exec_sync(sp_cmdbase, login, login_bh, &cmdargs);
+
+ return cmdargs.session;
+}
+
+struct sp_session *
+librespotc_login_token(const char *username, const char *token)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.username = username;
+ cmdargs.token = token;
+
+ commands_exec_sync(sp_cmdbase, login, login_bh, &cmdargs);
+
+ return cmdargs.session;
+}
+
+int
+librespotc_logout(struct sp_session *session)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.session = session;
+
+ return commands_exec_sync(sp_cmdbase, logout, NULL, &cmdargs);
+}
+
+int
+librespotc_metadata_get(struct sp_metadata *metadata, int fd)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.metadata = metadata;
+ cmdargs.fd_read = fd;
+
+ return commands_exec_sync(sp_cmdbase, metadata_get, NULL, &cmdargs);
+}
+
+int
+librespotc_bitrate_set(struct sp_session *session, enum sp_bitrates bitrate)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.session = session;
+ cmdargs.bitrate = bitrate;
+
+ return commands_exec_sync(sp_cmdbase, bitrate_set, NULL, &cmdargs);
+}
+
+int
+librespotc_credentials_get(struct sp_credentials *credentials, struct sp_session *session)
+{
+ struct sp_cmdargs cmdargs = { 0 };
+
+ cmdargs.credentials = credentials;
+ cmdargs.session = session;
+
+ return commands_exec_sync(sp_cmdbase, credentials_get, NULL, &cmdargs);
+}
+
+const char *
+librespotc_last_errmsg(void)
+{
+ return sp_errmsg ? sp_errmsg : "(no error)";
+}
+
+int
+librespotc_init(struct sp_sysinfo *sysinfo, struct sp_callbacks *callbacks)
+{
+ int ret;
+
+ if (sp_initialized)
+ RETURN_ERROR(SP_ERR_INVALID, "librespot-c already initialized");
+
+ sp_cb = *callbacks;
+ sp_initialized = true;
+
+ memcpy(&sp_sysinfo, sysinfo, sizeof(struct sp_sysinfo));
+
+ sp_evbase = event_base_new();
+ if (!sp_evbase)
+ RETURN_ERROR(SP_ERR_OOM, "event_base_new() failed");
+
+ sp_cmdbase = commands_base_new(sp_evbase, NULL);
+ if (!sp_cmdbase)
+ RETURN_ERROR(SP_ERR_OOM, "commands_base_new() failed");
+
+ ret = pthread_create(&sp_tid, NULL, librespotc, NULL);
+ if (ret < 0)
+ RETURN_ERROR(SP_ERR_OOM, "Could not start thread");
+
+ if (sp_cb.thread_name_set)
+ sp_cb.thread_name_set(sp_tid);
+
+ return 0;
+
+ error:
+ librespotc_deinit();
+ return ret;
+}
+
+void
+librespotc_deinit()
+{
+ struct sp_session *session;
+
+ if (sp_cmdbase)
+ {
+ commands_base_destroy(sp_cmdbase);
+ sp_cmdbase = NULL;
+ }
+
+ for (session = sp_sessions; sp_sessions; session = sp_sessions)
+ {
+ sp_sessions = session->next;
+ session_free(session);
+ }
+
+ if (sp_tid)
+ {
+ pthread_join(sp_tid, NULL);
+ }
+
+ if (sp_evbase)
+ {
+ event_base_free(sp_evbase);
+ sp_evbase = NULL;
+ }
+
+ sp_initialized = false;
+ memset(&sp_cb, 0, sizeof(struct sp_callbacks));
+
+ return;
+}
diff --git a/src/inputs/librespot-c/src/proto/ad-hermes-proxy.proto b/src/inputs/librespot-c/src/proto/ad-hermes-proxy.proto
new file mode 100644
index 00000000..219bbcbf
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/ad-hermes-proxy.proto
@@ -0,0 +1,51 @@
+syntax = "proto2";
+
+message Rule {
+ optional string type = 0x1;
+ optional uint32 times = 0x2;
+ optional uint64 interval = 0x3;
+}
+
+message AdRequest {
+ optional string client_language = 0x1;
+ optional string product = 0x2;
+ optional uint32 version = 0x3;
+ optional string type = 0x4;
+ repeated string avoidAds = 0x5;
+}
+
+message AdQueueResponse {
+ repeated AdQueueEntry adQueueEntry = 0x1;
+}
+
+message AdFile {
+ optional string id = 0x1;
+ optional string format = 0x2;
+}
+
+message AdQueueEntry {
+ optional uint64 start_time = 0x1;
+ optional uint64 end_time = 0x2;
+ optional double priority = 0x3;
+ optional string token = 0x4;
+ optional uint32 ad_version = 0x5;
+ optional string id = 0x6;
+ optional string type = 0x7;
+ optional string campaign = 0x8;
+ optional string advertiser = 0x9;
+ optional string url = 0xa;
+ optional uint64 duration = 0xb;
+ optional uint64 expiry = 0xc;
+ optional string tracking_url = 0xd;
+ optional string banner_type = 0xe;
+ optional string html = 0xf;
+ optional string image = 0x10;
+ optional string background_image = 0x11;
+ optional string background_url = 0x12;
+ optional string background_color = 0x13;
+ optional string title = 0x14;
+ optional string caption = 0x15;
+ repeated AdFile file = 0x16;
+ repeated Rule rule = 0x17;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/appstore.proto b/src/inputs/librespot-c/src/proto/appstore.proto
new file mode 100644
index 00000000..bddaaf30
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/appstore.proto
@@ -0,0 +1,95 @@
+syntax = "proto2";
+
+message AppInfo {
+ optional string identifier = 0x1;
+ optional int32 version_int = 0x2;
+}
+
+message AppInfoList {
+ repeated AppInfo items = 0x1;
+}
+
+message SemanticVersion {
+ optional int32 major = 0x1;
+ optional int32 minor = 0x2;
+ optional int32 patch = 0x3;
+}
+
+message RequestHeader {
+ optional string market = 0x1;
+ optional Platform platform = 0x2;
+ enum Platform {
+ WIN32_X86 = 0x0;
+ OSX_X86 = 0x1;
+ LINUX_X86 = 0x2;
+ IPHONE_ARM = 0x3;
+ SYMBIANS60_ARM = 0x4;
+ OSX_POWERPC = 0x5;
+ ANDROID_ARM = 0x6;
+ WINCE_ARM = 0x7;
+ LINUX_X86_64 = 0x8;
+ OSX_X86_64 = 0x9;
+ PALM_ARM = 0xa;
+ LINUX_SH = 0xb;
+ FREEBSD_X86 = 0xc;
+ FREEBSD_X86_64 = 0xd;
+ BLACKBERRY_ARM = 0xe;
+ SONOS_UNKNOWN = 0xf;
+ LINUX_MIPS = 0x10;
+ LINUX_ARM = 0x11;
+ LOGITECH_ARM = 0x12;
+ LINUX_BLACKFIN = 0x13;
+ ONKYO_ARM = 0x15;
+ QNXNTO_ARM = 0x16;
+ BADPLATFORM = 0xff;
+ }
+ optional AppInfoList app_infos = 0x6;
+ optional string bridge_identifier = 0x7;
+ optional SemanticVersion bridge_version = 0x8;
+ optional DeviceClass device_class = 0x9;
+ enum DeviceClass {
+ DESKTOP = 0x1;
+ TABLET = 0x2;
+ MOBILE = 0x3;
+ WEB = 0x4;
+ TV = 0x5;
+ }
+}
+
+message AppItem {
+ optional string identifier = 0x1;
+ optional Requirement requirement = 0x2;
+ enum Requirement {
+ REQUIRED_INSTALL = 0x1;
+ LAZYLOAD = 0x2;
+ OPTIONAL_INSTALL = 0x3;
+ }
+ optional string manifest = 0x4;
+ optional string checksum = 0x5;
+ optional string bundle_uri = 0x6;
+ optional string small_icon_uri = 0x7;
+ optional string large_icon_uri = 0x8;
+ optional string medium_icon_uri = 0x9;
+ optional Type bundle_type = 0xa;
+ enum Type {
+ APPLICATION = 0x0;
+ FRAMEWORK = 0x1;
+ BRIDGE = 0x2;
+ }
+ optional SemanticVersion version = 0xb;
+ optional uint32 ttl_in_seconds = 0xc;
+ optional IdentifierList categories = 0xd;
+}
+
+message AppList {
+ repeated AppItem items = 0x1;
+}
+
+message IdentifierList {
+ repeated string identifiers = 0x1;
+}
+
+message BannerConfig {
+ optional string json = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/authentication.pb-c.c b/src/inputs/librespot-c/src/proto/authentication.pb-c.c
new file mode 100644
index 00000000..080a1d0d
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/authentication.pb-c.c
@@ -0,0 +1,2063 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/authentication.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include "authentication.pb-c.h"
+void client_response_encrypted__init
+ (ClientResponseEncrypted *message)
+{
+ static const ClientResponseEncrypted init_value = CLIENT_RESPONSE_ENCRYPTED__INIT;
+ *message = init_value;
+}
+size_t client_response_encrypted__get_packed_size
+ (const ClientResponseEncrypted *message)
+{
+ assert(message->base.descriptor == &client_response_encrypted__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t client_response_encrypted__pack
+ (const ClientResponseEncrypted *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &client_response_encrypted__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t client_response_encrypted__pack_to_buffer
+ (const ClientResponseEncrypted *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &client_response_encrypted__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ClientResponseEncrypted *
+ client_response_encrypted__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ClientResponseEncrypted *)
+ protobuf_c_message_unpack (&client_response_encrypted__descriptor,
+ allocator, len, data);
+}
+void client_response_encrypted__free_unpacked
+ (ClientResponseEncrypted *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &client_response_encrypted__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_credentials__init
+ (LoginCredentials *message)
+{
+ static const LoginCredentials init_value = LOGIN_CREDENTIALS__INIT;
+ *message = init_value;
+}
+size_t login_credentials__get_packed_size
+ (const LoginCredentials *message)
+{
+ assert(message->base.descriptor == &login_credentials__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_credentials__pack
+ (const LoginCredentials *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_credentials__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_credentials__pack_to_buffer
+ (const LoginCredentials *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_credentials__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCredentials *
+ login_credentials__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCredentials *)
+ protobuf_c_message_unpack (&login_credentials__descriptor,
+ allocator, len, data);
+}
+void login_credentials__free_unpacked
+ (LoginCredentials *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_credentials__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_response_union__init
+ (FingerprintResponseUnion *message)
+{
+ static const FingerprintResponseUnion init_value = FINGERPRINT_RESPONSE_UNION__INIT;
+ *message = init_value;
+}
+size_t fingerprint_response_union__get_packed_size
+ (const FingerprintResponseUnion *message)
+{
+ assert(message->base.descriptor == &fingerprint_response_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_response_union__pack
+ (const FingerprintResponseUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_response_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_response_union__pack_to_buffer
+ (const FingerprintResponseUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_response_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintResponseUnion *
+ fingerprint_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintResponseUnion *)
+ protobuf_c_message_unpack (&fingerprint_response_union__descriptor,
+ allocator, len, data);
+}
+void fingerprint_response_union__free_unpacked
+ (FingerprintResponseUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_response_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_grain_response__init
+ (FingerprintGrainResponse *message)
+{
+ static const FingerprintGrainResponse init_value = FINGERPRINT_GRAIN_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t fingerprint_grain_response__get_packed_size
+ (const FingerprintGrainResponse *message)
+{
+ assert(message->base.descriptor == &fingerprint_grain_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_grain_response__pack
+ (const FingerprintGrainResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_grain_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_grain_response__pack_to_buffer
+ (const FingerprintGrainResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_grain_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintGrainResponse *
+ fingerprint_grain_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintGrainResponse *)
+ protobuf_c_message_unpack (&fingerprint_grain_response__descriptor,
+ allocator, len, data);
+}
+void fingerprint_grain_response__free_unpacked
+ (FingerprintGrainResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_grain_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_hmac_ripemd_response__init
+ (FingerprintHmacRipemdResponse *message)
+{
+ static const FingerprintHmacRipemdResponse init_value = FINGERPRINT_HMAC_RIPEMD_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t fingerprint_hmac_ripemd_response__get_packed_size
+ (const FingerprintHmacRipemdResponse *message)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_hmac_ripemd_response__pack
+ (const FingerprintHmacRipemdResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_hmac_ripemd_response__pack_to_buffer
+ (const FingerprintHmacRipemdResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintHmacRipemdResponse *
+ fingerprint_hmac_ripemd_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintHmacRipemdResponse *)
+ protobuf_c_message_unpack (&fingerprint_hmac_ripemd_response__descriptor,
+ allocator, len, data);
+}
+void fingerprint_hmac_ripemd_response__free_unpacked
+ (FingerprintHmacRipemdResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void peer_ticket_union__init
+ (PeerTicketUnion *message)
+{
+ static const PeerTicketUnion init_value = PEER_TICKET_UNION__INIT;
+ *message = init_value;
+}
+size_t peer_ticket_union__get_packed_size
+ (const PeerTicketUnion *message)
+{
+ assert(message->base.descriptor == &peer_ticket_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t peer_ticket_union__pack
+ (const PeerTicketUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &peer_ticket_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t peer_ticket_union__pack_to_buffer
+ (const PeerTicketUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &peer_ticket_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PeerTicketUnion *
+ peer_ticket_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PeerTicketUnion *)
+ protobuf_c_message_unpack (&peer_ticket_union__descriptor,
+ allocator, len, data);
+}
+void peer_ticket_union__free_unpacked
+ (PeerTicketUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &peer_ticket_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void peer_ticket_public_key__init
+ (PeerTicketPublicKey *message)
+{
+ static const PeerTicketPublicKey init_value = PEER_TICKET_PUBLIC_KEY__INIT;
+ *message = init_value;
+}
+size_t peer_ticket_public_key__get_packed_size
+ (const PeerTicketPublicKey *message)
+{
+ assert(message->base.descriptor == &peer_ticket_public_key__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t peer_ticket_public_key__pack
+ (const PeerTicketPublicKey *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &peer_ticket_public_key__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t peer_ticket_public_key__pack_to_buffer
+ (const PeerTicketPublicKey *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &peer_ticket_public_key__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PeerTicketPublicKey *
+ peer_ticket_public_key__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PeerTicketPublicKey *)
+ protobuf_c_message_unpack (&peer_ticket_public_key__descriptor,
+ allocator, len, data);
+}
+void peer_ticket_public_key__free_unpacked
+ (PeerTicketPublicKey *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &peer_ticket_public_key__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void peer_ticket_old__init
+ (PeerTicketOld *message)
+{
+ static const PeerTicketOld init_value = PEER_TICKET_OLD__INIT;
+ *message = init_value;
+}
+size_t peer_ticket_old__get_packed_size
+ (const PeerTicketOld *message)
+{
+ assert(message->base.descriptor == &peer_ticket_old__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t peer_ticket_old__pack
+ (const PeerTicketOld *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &peer_ticket_old__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t peer_ticket_old__pack_to_buffer
+ (const PeerTicketOld *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &peer_ticket_old__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PeerTicketOld *
+ peer_ticket_old__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PeerTicketOld *)
+ protobuf_c_message_unpack (&peer_ticket_old__descriptor,
+ allocator, len, data);
+}
+void peer_ticket_old__free_unpacked
+ (PeerTicketOld *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &peer_ticket_old__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void system_info__init
+ (SystemInfo *message)
+{
+ static const SystemInfo init_value = SYSTEM_INFO__INIT;
+ *message = init_value;
+}
+size_t system_info__get_packed_size
+ (const SystemInfo *message)
+{
+ assert(message->base.descriptor == &system_info__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t system_info__pack
+ (const SystemInfo *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &system_info__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t system_info__pack_to_buffer
+ (const SystemInfo *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &system_info__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+SystemInfo *
+ system_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (SystemInfo *)
+ protobuf_c_message_unpack (&system_info__descriptor,
+ allocator, len, data);
+}
+void system_info__free_unpacked
+ (SystemInfo *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &system_info__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void libspotify_app_key__init
+ (LibspotifyAppKey *message)
+{
+ static const LibspotifyAppKey init_value = LIBSPOTIFY_APP_KEY__INIT;
+ *message = init_value;
+}
+size_t libspotify_app_key__get_packed_size
+ (const LibspotifyAppKey *message)
+{
+ assert(message->base.descriptor == &libspotify_app_key__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t libspotify_app_key__pack
+ (const LibspotifyAppKey *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &libspotify_app_key__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t libspotify_app_key__pack_to_buffer
+ (const LibspotifyAppKey *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &libspotify_app_key__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LibspotifyAppKey *
+ libspotify_app_key__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LibspotifyAppKey *)
+ protobuf_c_message_unpack (&libspotify_app_key__descriptor,
+ allocator, len, data);
+}
+void libspotify_app_key__free_unpacked
+ (LibspotifyAppKey *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &libspotify_app_key__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void client_info__init
+ (ClientInfo *message)
+{
+ static const ClientInfo init_value = CLIENT_INFO__INIT;
+ *message = init_value;
+}
+size_t client_info__get_packed_size
+ (const ClientInfo *message)
+{
+ assert(message->base.descriptor == &client_info__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t client_info__pack
+ (const ClientInfo *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &client_info__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t client_info__pack_to_buffer
+ (const ClientInfo *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &client_info__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ClientInfo *
+ client_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ClientInfo *)
+ protobuf_c_message_unpack (&client_info__descriptor,
+ allocator, len, data);
+}
+void client_info__free_unpacked
+ (ClientInfo *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &client_info__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void client_info_facebook__init
+ (ClientInfoFacebook *message)
+{
+ static const ClientInfoFacebook init_value = CLIENT_INFO_FACEBOOK__INIT;
+ *message = init_value;
+}
+size_t client_info_facebook__get_packed_size
+ (const ClientInfoFacebook *message)
+{
+ assert(message->base.descriptor == &client_info_facebook__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t client_info_facebook__pack
+ (const ClientInfoFacebook *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &client_info_facebook__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t client_info_facebook__pack_to_buffer
+ (const ClientInfoFacebook *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &client_info_facebook__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ClientInfoFacebook *
+ client_info_facebook__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ClientInfoFacebook *)
+ protobuf_c_message_unpack (&client_info_facebook__descriptor,
+ allocator, len, data);
+}
+void client_info_facebook__free_unpacked
+ (ClientInfoFacebook *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &client_info_facebook__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void apwelcome__init
+ (APWelcome *message)
+{
+ static const APWelcome init_value = APWELCOME__INIT;
+ *message = init_value;
+}
+size_t apwelcome__get_packed_size
+ (const APWelcome *message)
+{
+ assert(message->base.descriptor == &apwelcome__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t apwelcome__pack
+ (const APWelcome *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &apwelcome__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t apwelcome__pack_to_buffer
+ (const APWelcome *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &apwelcome__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+APWelcome *
+ apwelcome__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (APWelcome *)
+ protobuf_c_message_unpack (&apwelcome__descriptor,
+ allocator, len, data);
+}
+void apwelcome__free_unpacked
+ (APWelcome *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &apwelcome__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void account_info__init
+ (AccountInfo *message)
+{
+ static const AccountInfo init_value = ACCOUNT_INFO__INIT;
+ *message = init_value;
+}
+size_t account_info__get_packed_size
+ (const AccountInfo *message)
+{
+ assert(message->base.descriptor == &account_info__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t account_info__pack
+ (const AccountInfo *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &account_info__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t account_info__pack_to_buffer
+ (const AccountInfo *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &account_info__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+AccountInfo *
+ account_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (AccountInfo *)
+ protobuf_c_message_unpack (&account_info__descriptor,
+ allocator, len, data);
+}
+void account_info__free_unpacked
+ (AccountInfo *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &account_info__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void account_info_spotify__init
+ (AccountInfoSpotify *message)
+{
+ static const AccountInfoSpotify init_value = ACCOUNT_INFO_SPOTIFY__INIT;
+ *message = init_value;
+}
+size_t account_info_spotify__get_packed_size
+ (const AccountInfoSpotify *message)
+{
+ assert(message->base.descriptor == &account_info_spotify__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t account_info_spotify__pack
+ (const AccountInfoSpotify *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &account_info_spotify__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t account_info_spotify__pack_to_buffer
+ (const AccountInfoSpotify *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &account_info_spotify__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+AccountInfoSpotify *
+ account_info_spotify__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (AccountInfoSpotify *)
+ protobuf_c_message_unpack (&account_info_spotify__descriptor,
+ allocator, len, data);
+}
+void account_info_spotify__free_unpacked
+ (AccountInfoSpotify *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &account_info_spotify__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void account_info_facebook__init
+ (AccountInfoFacebook *message)
+{
+ static const AccountInfoFacebook init_value = ACCOUNT_INFO_FACEBOOK__INIT;
+ *message = init_value;
+}
+size_t account_info_facebook__get_packed_size
+ (const AccountInfoFacebook *message)
+{
+ assert(message->base.descriptor == &account_info_facebook__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t account_info_facebook__pack
+ (const AccountInfoFacebook *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &account_info_facebook__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t account_info_facebook__pack_to_buffer
+ (const AccountInfoFacebook *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &account_info_facebook__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+AccountInfoFacebook *
+ account_info_facebook__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (AccountInfoFacebook *)
+ protobuf_c_message_unpack (&account_info_facebook__descriptor,
+ allocator, len, data);
+}
+void account_info_facebook__free_unpacked
+ (AccountInfoFacebook *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &account_info_facebook__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor client_response_encrypted__field_descriptors[9] =
+{
+ {
+ "login_credentials",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, login_credentials),
+ &login_credentials__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "account_creation",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(ClientResponseEncrypted, has_account_creation),
+ offsetof(ClientResponseEncrypted, account_creation),
+ &account_creation__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "fingerprint_response",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, fingerprint_response),
+ &fingerprint_response_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "peer_ticket",
+ 40,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, peer_ticket),
+ &peer_ticket_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "system_info",
+ 50,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, system_info),
+ &system_info__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "platform_model",
+ 60,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, platform_model),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "version_string",
+ 70,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, version_string),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "appkey",
+ 80,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, appkey),
+ &libspotify_app_key__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "client_info",
+ 90,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponseEncrypted, client_info),
+ &client_info__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned client_response_encrypted__field_indices_by_name[] = {
+ 1, /* field[1] = account_creation */
+ 7, /* field[7] = appkey */
+ 8, /* field[8] = client_info */
+ 2, /* field[2] = fingerprint_response */
+ 0, /* field[0] = login_credentials */
+ 3, /* field[3] = peer_ticket */
+ 5, /* field[5] = platform_model */
+ 4, /* field[4] = system_info */
+ 6, /* field[6] = version_string */
+};
+static const ProtobufCIntRange client_response_encrypted__number_ranges[9 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 50, 4 },
+ { 60, 5 },
+ { 70, 6 },
+ { 80, 7 },
+ { 90, 8 },
+ { 0, 9 }
+};
+const ProtobufCMessageDescriptor client_response_encrypted__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ClientResponseEncrypted",
+ "ClientResponseEncrypted",
+ "ClientResponseEncrypted",
+ "",
+ sizeof(ClientResponseEncrypted),
+ 9,
+ client_response_encrypted__field_descriptors,
+ client_response_encrypted__field_indices_by_name,
+ 9, client_response_encrypted__number_ranges,
+ (ProtobufCMessageInit) client_response_encrypted__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_credentials__field_descriptors[3] =
+{
+ {
+ "username",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(LoginCredentials, username),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "typ",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(LoginCredentials, typ),
+ &authentication_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "auth_data",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(LoginCredentials, has_auth_data),
+ offsetof(LoginCredentials, auth_data),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_credentials__field_indices_by_name[] = {
+ 2, /* field[2] = auth_data */
+ 1, /* field[1] = typ */
+ 0, /* field[0] = username */
+};
+static const ProtobufCIntRange login_credentials__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor login_credentials__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCredentials",
+ "LoginCredentials",
+ "LoginCredentials",
+ "",
+ sizeof(LoginCredentials),
+ 3,
+ login_credentials__field_descriptors,
+ login_credentials__field_indices_by_name,
+ 3, login_credentials__number_ranges,
+ (ProtobufCMessageInit) login_credentials__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_response_union__field_descriptors[2] =
+{
+ {
+ "grain",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintResponseUnion, grain),
+ &fingerprint_grain_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "hmac_ripemd",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintResponseUnion, hmac_ripemd),
+ &fingerprint_hmac_ripemd_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_response_union__field_indices_by_name[] = {
+ 0, /* field[0] = grain */
+ 1, /* field[1] = hmac_ripemd */
+};
+static const ProtobufCIntRange fingerprint_response_union__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor fingerprint_response_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintResponseUnion",
+ "FingerprintResponseUnion",
+ "FingerprintResponseUnion",
+ "",
+ sizeof(FingerprintResponseUnion),
+ 2,
+ fingerprint_response_union__field_descriptors,
+ fingerprint_response_union__field_indices_by_name,
+ 2, fingerprint_response_union__number_ranges,
+ (ProtobufCMessageInit) fingerprint_response_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_grain_response__field_descriptors[1] =
+{
+ {
+ "encrypted_key",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintGrainResponse, encrypted_key),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_grain_response__field_indices_by_name[] = {
+ 0, /* field[0] = encrypted_key */
+};
+static const ProtobufCIntRange fingerprint_grain_response__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor fingerprint_grain_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintGrainResponse",
+ "FingerprintGrainResponse",
+ "FingerprintGrainResponse",
+ "",
+ sizeof(FingerprintGrainResponse),
+ 1,
+ fingerprint_grain_response__field_descriptors,
+ fingerprint_grain_response__field_indices_by_name,
+ 1, fingerprint_grain_response__number_ranges,
+ (ProtobufCMessageInit) fingerprint_grain_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_hmac_ripemd_response__field_descriptors[1] =
+{
+ {
+ "hmac",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintHmacRipemdResponse, hmac),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_hmac_ripemd_response__field_indices_by_name[] = {
+ 0, /* field[0] = hmac */
+};
+static const ProtobufCIntRange fingerprint_hmac_ripemd_response__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor fingerprint_hmac_ripemd_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintHmacRipemdResponse",
+ "FingerprintHmacRipemdResponse",
+ "FingerprintHmacRipemdResponse",
+ "",
+ sizeof(FingerprintHmacRipemdResponse),
+ 1,
+ fingerprint_hmac_ripemd_response__field_descriptors,
+ fingerprint_hmac_ripemd_response__field_indices_by_name,
+ 1, fingerprint_hmac_ripemd_response__number_ranges,
+ (ProtobufCMessageInit) fingerprint_hmac_ripemd_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor peer_ticket_union__field_descriptors[2] =
+{
+ {
+ "public_key",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(PeerTicketUnion, public_key),
+ &peer_ticket_public_key__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "old_ticket",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(PeerTicketUnion, old_ticket),
+ &peer_ticket_old__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned peer_ticket_union__field_indices_by_name[] = {
+ 1, /* field[1] = old_ticket */
+ 0, /* field[0] = public_key */
+};
+static const ProtobufCIntRange peer_ticket_union__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor peer_ticket_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PeerTicketUnion",
+ "PeerTicketUnion",
+ "PeerTicketUnion",
+ "",
+ sizeof(PeerTicketUnion),
+ 2,
+ peer_ticket_union__field_descriptors,
+ peer_ticket_union__field_indices_by_name,
+ 2, peer_ticket_union__number_ranges,
+ (ProtobufCMessageInit) peer_ticket_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor peer_ticket_public_key__field_descriptors[1] =
+{
+ {
+ "public_key",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(PeerTicketPublicKey, public_key),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned peer_ticket_public_key__field_indices_by_name[] = {
+ 0, /* field[0] = public_key */
+};
+static const ProtobufCIntRange peer_ticket_public_key__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor peer_ticket_public_key__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PeerTicketPublicKey",
+ "PeerTicketPublicKey",
+ "PeerTicketPublicKey",
+ "",
+ sizeof(PeerTicketPublicKey),
+ 1,
+ peer_ticket_public_key__field_descriptors,
+ peer_ticket_public_key__field_indices_by_name,
+ 1, peer_ticket_public_key__number_ranges,
+ (ProtobufCMessageInit) peer_ticket_public_key__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor peer_ticket_old__field_descriptors[2] =
+{
+ {
+ "peer_ticket",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(PeerTicketOld, peer_ticket),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "peer_ticket_signature",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(PeerTicketOld, peer_ticket_signature),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned peer_ticket_old__field_indices_by_name[] = {
+ 0, /* field[0] = peer_ticket */
+ 1, /* field[1] = peer_ticket_signature */
+};
+static const ProtobufCIntRange peer_ticket_old__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor peer_ticket_old__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PeerTicketOld",
+ "PeerTicketOld",
+ "PeerTicketOld",
+ "",
+ sizeof(PeerTicketOld),
+ 2,
+ peer_ticket_old__field_descriptors,
+ peer_ticket_old__field_indices_by_name,
+ 2, peer_ticket_old__number_ranges,
+ (ProtobufCMessageInit) peer_ticket_old__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor system_info__field_descriptors[10] =
+{
+ {
+ "cpu_family",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(SystemInfo, cpu_family),
+ &cpu_family__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cpu_subtype",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_UINT32,
+ offsetof(SystemInfo, has_cpu_subtype),
+ offsetof(SystemInfo, cpu_subtype),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cpu_ext",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_UINT32,
+ offsetof(SystemInfo, has_cpu_ext),
+ offsetof(SystemInfo, cpu_ext),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "brand",
+ 40,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(SystemInfo, has_brand),
+ offsetof(SystemInfo, brand),
+ &brand__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "brand_flags",
+ 50,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_UINT32,
+ offsetof(SystemInfo, has_brand_flags),
+ offsetof(SystemInfo, brand_flags),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "os",
+ 60,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(SystemInfo, os),
+ &os__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "os_version",
+ 70,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_UINT32,
+ offsetof(SystemInfo, has_os_version),
+ offsetof(SystemInfo, os_version),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "os_ext",
+ 80,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_UINT32,
+ offsetof(SystemInfo, has_os_ext),
+ offsetof(SystemInfo, os_ext),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "system_information_string",
+ 90,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(SystemInfo, system_information_string),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "device_id",
+ 100,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(SystemInfo, device_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned system_info__field_indices_by_name[] = {
+ 3, /* field[3] = brand */
+ 4, /* field[4] = brand_flags */
+ 2, /* field[2] = cpu_ext */
+ 0, /* field[0] = cpu_family */
+ 1, /* field[1] = cpu_subtype */
+ 9, /* field[9] = device_id */
+ 5, /* field[5] = os */
+ 7, /* field[7] = os_ext */
+ 6, /* field[6] = os_version */
+ 8, /* field[8] = system_information_string */
+};
+static const ProtobufCIntRange system_info__number_ranges[10 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 50, 4 },
+ { 60, 5 },
+ { 70, 6 },
+ { 80, 7 },
+ { 90, 8 },
+ { 100, 9 },
+ { 0, 10 }
+};
+const ProtobufCMessageDescriptor system_info__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "SystemInfo",
+ "SystemInfo",
+ "SystemInfo",
+ "",
+ sizeof(SystemInfo),
+ 10,
+ system_info__field_descriptors,
+ system_info__field_indices_by_name,
+ 10, system_info__number_ranges,
+ (ProtobufCMessageInit) system_info__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor libspotify_app_key__field_descriptors[5] =
+{
+ {
+ "version",
+ 1,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(LibspotifyAppKey, version),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "devkey",
+ 2,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LibspotifyAppKey, devkey),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "signature",
+ 3,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LibspotifyAppKey, signature),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "useragent",
+ 4,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(LibspotifyAppKey, useragent),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "callback_hash",
+ 5,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LibspotifyAppKey, callback_hash),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned libspotify_app_key__field_indices_by_name[] = {
+ 4, /* field[4] = callback_hash */
+ 1, /* field[1] = devkey */
+ 2, /* field[2] = signature */
+ 3, /* field[3] = useragent */
+ 0, /* field[0] = version */
+};
+static const ProtobufCIntRange libspotify_app_key__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 5 }
+};
+const ProtobufCMessageDescriptor libspotify_app_key__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LibspotifyAppKey",
+ "LibspotifyAppKey",
+ "LibspotifyAppKey",
+ "",
+ sizeof(LibspotifyAppKey),
+ 5,
+ libspotify_app_key__field_descriptors,
+ libspotify_app_key__field_indices_by_name,
+ 1, libspotify_app_key__number_ranges,
+ (ProtobufCMessageInit) libspotify_app_key__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor client_info__field_descriptors[3] =
+{
+ {
+ "limited",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(ClientInfo, has_limited),
+ offsetof(ClientInfo, limited),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "fb",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientInfo, fb),
+ &client_info_facebook__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "language",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ClientInfo, language),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned client_info__field_indices_by_name[] = {
+ 1, /* field[1] = fb */
+ 2, /* field[2] = language */
+ 0, /* field[0] = limited */
+};
+static const ProtobufCIntRange client_info__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor client_info__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ClientInfo",
+ "ClientInfo",
+ "ClientInfo",
+ "",
+ sizeof(ClientInfo),
+ 3,
+ client_info__field_descriptors,
+ client_info__field_indices_by_name,
+ 1, client_info__number_ranges,
+ (ProtobufCMessageInit) client_info__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor client_info_facebook__field_descriptors[1] =
+{
+ {
+ "machine_id",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ClientInfoFacebook, machine_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned client_info_facebook__field_indices_by_name[] = {
+ 0, /* field[0] = machine_id */
+};
+static const ProtobufCIntRange client_info_facebook__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor client_info_facebook__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ClientInfoFacebook",
+ "ClientInfoFacebook",
+ "ClientInfoFacebook",
+ "",
+ sizeof(ClientInfoFacebook),
+ 1,
+ client_info_facebook__field_descriptors,
+ client_info_facebook__field_indices_by_name,
+ 1, client_info_facebook__number_ranges,
+ (ProtobufCMessageInit) client_info_facebook__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor apwelcome__field_descriptors[8] =
+{
+ {
+ "canonical_username",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, canonical_username),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "account_type_logged_in",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, account_type_logged_in),
+ &account_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "credentials_type_logged_in",
+ 25,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, credentials_type_logged_in),
+ &account_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "reusable_auth_credentials_type",
+ 30,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, reusable_auth_credentials_type),
+ &authentication_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "reusable_auth_credentials",
+ 40,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, reusable_auth_credentials),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "lfs_secret",
+ 50,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(APWelcome, has_lfs_secret),
+ offsetof(APWelcome, lfs_secret),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "account_info",
+ 60,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, account_info),
+ &account_info__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "fb",
+ 70,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APWelcome, fb),
+ &account_info_facebook__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned apwelcome__field_indices_by_name[] = {
+ 6, /* field[6] = account_info */
+ 1, /* field[1] = account_type_logged_in */
+ 0, /* field[0] = canonical_username */
+ 2, /* field[2] = credentials_type_logged_in */
+ 7, /* field[7] = fb */
+ 5, /* field[5] = lfs_secret */
+ 4, /* field[4] = reusable_auth_credentials */
+ 3, /* field[3] = reusable_auth_credentials_type */
+};
+static const ProtobufCIntRange apwelcome__number_ranges[8 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 25, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 50, 5 },
+ { 60, 6 },
+ { 70, 7 },
+ { 0, 8 }
+};
+const ProtobufCMessageDescriptor apwelcome__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "APWelcome",
+ "APWelcome",
+ "APWelcome",
+ "",
+ sizeof(APWelcome),
+ 8,
+ apwelcome__field_descriptors,
+ apwelcome__field_indices_by_name,
+ 8, apwelcome__number_ranges,
+ (ProtobufCMessageInit) apwelcome__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor account_info__field_descriptors[2] =
+{
+ {
+ "spotify",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(AccountInfo, spotify),
+ &account_info_spotify__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "facebook",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(AccountInfo, facebook),
+ &account_info_facebook__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned account_info__field_indices_by_name[] = {
+ 1, /* field[1] = facebook */
+ 0, /* field[0] = spotify */
+};
+static const ProtobufCIntRange account_info__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor account_info__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "AccountInfo",
+ "AccountInfo",
+ "AccountInfo",
+ "",
+ sizeof(AccountInfo),
+ 2,
+ account_info__field_descriptors,
+ account_info__field_indices_by_name,
+ 1, account_info__number_ranges,
+ (ProtobufCMessageInit) account_info__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+#define account_info_spotify__field_descriptors NULL
+#define account_info_spotify__field_indices_by_name NULL
+#define account_info_spotify__number_ranges NULL
+const ProtobufCMessageDescriptor account_info_spotify__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "AccountInfoSpotify",
+ "AccountInfoSpotify",
+ "AccountInfoSpotify",
+ "",
+ sizeof(AccountInfoSpotify),
+ 0,
+ account_info_spotify__field_descriptors,
+ account_info_spotify__field_indices_by_name,
+ 0, account_info_spotify__number_ranges,
+ (ProtobufCMessageInit) account_info_spotify__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor account_info_facebook__field_descriptors[2] =
+{
+ {
+ "access_token",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(AccountInfoFacebook, access_token),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "machine_id",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(AccountInfoFacebook, machine_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned account_info_facebook__field_indices_by_name[] = {
+ 0, /* field[0] = access_token */
+ 1, /* field[1] = machine_id */
+};
+static const ProtobufCIntRange account_info_facebook__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor account_info_facebook__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "AccountInfoFacebook",
+ "AccountInfoFacebook",
+ "AccountInfoFacebook",
+ "",
+ sizeof(AccountInfoFacebook),
+ 2,
+ account_info_facebook__field_descriptors,
+ account_info_facebook__field_indices_by_name,
+ 1, account_info_facebook__number_ranges,
+ (ProtobufCMessageInit) account_info_facebook__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue authentication_type__enum_values_by_number[5] =
+{
+ { "AUTHENTICATION_USER_PASS", "AUTHENTICATION_TYPE__AUTHENTICATION_USER_PASS", 0 },
+ { "AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS", "AUTHENTICATION_TYPE__AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS", 1 },
+ { "AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS", "AUTHENTICATION_TYPE__AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS", 2 },
+ { "AUTHENTICATION_SPOTIFY_TOKEN", "AUTHENTICATION_TYPE__AUTHENTICATION_SPOTIFY_TOKEN", 3 },
+ { "AUTHENTICATION_FACEBOOK_TOKEN", "AUTHENTICATION_TYPE__AUTHENTICATION_FACEBOOK_TOKEN", 4 },
+};
+static const ProtobufCIntRange authentication_type__value_ranges[] = {
+{0, 0},{0, 5}
+};
+static const ProtobufCEnumValueIndex authentication_type__enum_values_by_name[5] =
+{
+ { "AUTHENTICATION_FACEBOOK_TOKEN", 4 },
+ { "AUTHENTICATION_SPOTIFY_TOKEN", 3 },
+ { "AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS", 2 },
+ { "AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS", 1 },
+ { "AUTHENTICATION_USER_PASS", 0 },
+};
+const ProtobufCEnumDescriptor authentication_type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "AuthenticationType",
+ "AuthenticationType",
+ "AuthenticationType",
+ "",
+ 5,
+ authentication_type__enum_values_by_number,
+ 5,
+ authentication_type__enum_values_by_name,
+ 1,
+ authentication_type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue account_creation__enum_values_by_number[2] =
+{
+ { "ACCOUNT_CREATION_ALWAYS_PROMPT", "ACCOUNT_CREATION__ACCOUNT_CREATION_ALWAYS_PROMPT", 1 },
+ { "ACCOUNT_CREATION_ALWAYS_CREATE", "ACCOUNT_CREATION__ACCOUNT_CREATION_ALWAYS_CREATE", 3 },
+};
+static const ProtobufCIntRange account_creation__value_ranges[] = {
+{1, 0},{3, 1},{0, 2}
+};
+static const ProtobufCEnumValueIndex account_creation__enum_values_by_name[2] =
+{
+ { "ACCOUNT_CREATION_ALWAYS_CREATE", 1 },
+ { "ACCOUNT_CREATION_ALWAYS_PROMPT", 0 },
+};
+const ProtobufCEnumDescriptor account_creation__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "AccountCreation",
+ "AccountCreation",
+ "AccountCreation",
+ "",
+ 2,
+ account_creation__enum_values_by_number,
+ 2,
+ account_creation__enum_values_by_name,
+ 2,
+ account_creation__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue cpu_family__enum_values_by_number[10] =
+{
+ { "CPU_UNKNOWN", "CPU_FAMILY__CPU_UNKNOWN", 0 },
+ { "CPU_X86", "CPU_FAMILY__CPU_X86", 1 },
+ { "CPU_X86_64", "CPU_FAMILY__CPU_X86_64", 2 },
+ { "CPU_PPC", "CPU_FAMILY__CPU_PPC", 3 },
+ { "CPU_PPC_64", "CPU_FAMILY__CPU_PPC_64", 4 },
+ { "CPU_ARM", "CPU_FAMILY__CPU_ARM", 5 },
+ { "CPU_IA64", "CPU_FAMILY__CPU_IA64", 6 },
+ { "CPU_SH", "CPU_FAMILY__CPU_SH", 7 },
+ { "CPU_MIPS", "CPU_FAMILY__CPU_MIPS", 8 },
+ { "CPU_BLACKFIN", "CPU_FAMILY__CPU_BLACKFIN", 9 },
+};
+static const ProtobufCIntRange cpu_family__value_ranges[] = {
+{0, 0},{0, 10}
+};
+static const ProtobufCEnumValueIndex cpu_family__enum_values_by_name[10] =
+{
+ { "CPU_ARM", 5 },
+ { "CPU_BLACKFIN", 9 },
+ { "CPU_IA64", 6 },
+ { "CPU_MIPS", 8 },
+ { "CPU_PPC", 3 },
+ { "CPU_PPC_64", 4 },
+ { "CPU_SH", 7 },
+ { "CPU_UNKNOWN", 0 },
+ { "CPU_X86", 1 },
+ { "CPU_X86_64", 2 },
+};
+const ProtobufCEnumDescriptor cpu_family__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "CpuFamily",
+ "CpuFamily",
+ "CpuFamily",
+ "",
+ 10,
+ cpu_family__enum_values_by_number,
+ 10,
+ cpu_family__enum_values_by_name,
+ 1,
+ cpu_family__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue brand__enum_values_by_number[4] =
+{
+ { "BRAND_UNBRANDED", "BRAND__BRAND_UNBRANDED", 0 },
+ { "BRAND_INQ", "BRAND__BRAND_INQ", 1 },
+ { "BRAND_HTC", "BRAND__BRAND_HTC", 2 },
+ { "BRAND_NOKIA", "BRAND__BRAND_NOKIA", 3 },
+};
+static const ProtobufCIntRange brand__value_ranges[] = {
+{0, 0},{0, 4}
+};
+static const ProtobufCEnumValueIndex brand__enum_values_by_name[4] =
+{
+ { "BRAND_HTC", 2 },
+ { "BRAND_INQ", 1 },
+ { "BRAND_NOKIA", 3 },
+ { "BRAND_UNBRANDED", 0 },
+};
+const ProtobufCEnumDescriptor brand__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Brand",
+ "Brand",
+ "Brand",
+ "",
+ 4,
+ brand__enum_values_by_number,
+ 4,
+ brand__enum_values_by_name,
+ 1,
+ brand__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue os__enum_values_by_number[23] =
+{
+ { "OS_UNKNOWN", "OS__OS_UNKNOWN", 0 },
+ { "OS_WINDOWS", "OS__OS_WINDOWS", 1 },
+ { "OS_OSX", "OS__OS_OSX", 2 },
+ { "OS_IPHONE", "OS__OS_IPHONE", 3 },
+ { "OS_S60", "OS__OS_S60", 4 },
+ { "OS_LINUX", "OS__OS_LINUX", 5 },
+ { "OS_WINDOWS_CE", "OS__OS_WINDOWS_CE", 6 },
+ { "OS_ANDROID", "OS__OS_ANDROID", 7 },
+ { "OS_PALM", "OS__OS_PALM", 8 },
+ { "OS_FREEBSD", "OS__OS_FREEBSD", 9 },
+ { "OS_BLACKBERRY", "OS__OS_BLACKBERRY", 10 },
+ { "OS_SONOS", "OS__OS_SONOS", 11 },
+ { "OS_LOGITECH", "OS__OS_LOGITECH", 12 },
+ { "OS_WP7", "OS__OS_WP7", 13 },
+ { "OS_ONKYO", "OS__OS_ONKYO", 14 },
+ { "OS_PHILIPS", "OS__OS_PHILIPS", 15 },
+ { "OS_WD", "OS__OS_WD", 16 },
+ { "OS_VOLVO", "OS__OS_VOLVO", 17 },
+ { "OS_TIVO", "OS__OS_TIVO", 18 },
+ { "OS_AWOX", "OS__OS_AWOX", 19 },
+ { "OS_MEEGO", "OS__OS_MEEGO", 20 },
+ { "OS_QNXNTO", "OS__OS_QNXNTO", 21 },
+ { "OS_BCO", "OS__OS_BCO", 22 },
+};
+static const ProtobufCIntRange os__value_ranges[] = {
+{0, 0},{0, 23}
+};
+static const ProtobufCEnumValueIndex os__enum_values_by_name[23] =
+{
+ { "OS_ANDROID", 7 },
+ { "OS_AWOX", 19 },
+ { "OS_BCO", 22 },
+ { "OS_BLACKBERRY", 10 },
+ { "OS_FREEBSD", 9 },
+ { "OS_IPHONE", 3 },
+ { "OS_LINUX", 5 },
+ { "OS_LOGITECH", 12 },
+ { "OS_MEEGO", 20 },
+ { "OS_ONKYO", 14 },
+ { "OS_OSX", 2 },
+ { "OS_PALM", 8 },
+ { "OS_PHILIPS", 15 },
+ { "OS_QNXNTO", 21 },
+ { "OS_S60", 4 },
+ { "OS_SONOS", 11 },
+ { "OS_TIVO", 18 },
+ { "OS_UNKNOWN", 0 },
+ { "OS_VOLVO", 17 },
+ { "OS_WD", 16 },
+ { "OS_WINDOWS", 1 },
+ { "OS_WINDOWS_CE", 6 },
+ { "OS_WP7", 13 },
+};
+const ProtobufCEnumDescriptor os__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Os",
+ "Os",
+ "Os",
+ "",
+ 23,
+ os__enum_values_by_number,
+ 23,
+ os__enum_values_by_name,
+ 1,
+ os__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue account_type__enum_values_by_number[2] =
+{
+ { "Spotify", "ACCOUNT_TYPE__Spotify", 0 },
+ { "Facebook", "ACCOUNT_TYPE__Facebook", 1 },
+};
+static const ProtobufCIntRange account_type__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex account_type__enum_values_by_name[2] =
+{
+ { "Facebook", 1 },
+ { "Spotify", 0 },
+};
+const ProtobufCEnumDescriptor account_type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "AccountType",
+ "AccountType",
+ "AccountType",
+ "",
+ 2,
+ account_type__enum_values_by_number,
+ 2,
+ account_type__enum_values_by_name,
+ 1,
+ account_type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
diff --git a/src/inputs/librespot-c/src/proto/authentication.pb-c.h b/src/inputs/librespot-c/src/proto/authentication.pb-c.h
new file mode 100644
index 00000000..463ad5cb
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/authentication.pb-c.h
@@ -0,0 +1,697 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/authentication.proto */
+
+#ifndef PROTOBUF_C_proto_2fauthentication_2eproto__INCLUDED
+#define PROTOBUF_C_proto_2fauthentication_2eproto__INCLUDED
+
+#include
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1000000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct _ClientResponseEncrypted ClientResponseEncrypted;
+typedef struct _LoginCredentials LoginCredentials;
+typedef struct _FingerprintResponseUnion FingerprintResponseUnion;
+typedef struct _FingerprintGrainResponse FingerprintGrainResponse;
+typedef struct _FingerprintHmacRipemdResponse FingerprintHmacRipemdResponse;
+typedef struct _PeerTicketUnion PeerTicketUnion;
+typedef struct _PeerTicketPublicKey PeerTicketPublicKey;
+typedef struct _PeerTicketOld PeerTicketOld;
+typedef struct _SystemInfo SystemInfo;
+typedef struct _LibspotifyAppKey LibspotifyAppKey;
+typedef struct _ClientInfo ClientInfo;
+typedef struct _ClientInfoFacebook ClientInfoFacebook;
+typedef struct _APWelcome APWelcome;
+typedef struct _AccountInfo AccountInfo;
+typedef struct _AccountInfoSpotify AccountInfoSpotify;
+typedef struct _AccountInfoFacebook AccountInfoFacebook;
+
+
+/* --- enums --- */
+
+typedef enum _AuthenticationType {
+ AUTHENTICATION_TYPE__AUTHENTICATION_USER_PASS = 0,
+ AUTHENTICATION_TYPE__AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS = 1,
+ AUTHENTICATION_TYPE__AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS = 2,
+ AUTHENTICATION_TYPE__AUTHENTICATION_SPOTIFY_TOKEN = 3,
+ AUTHENTICATION_TYPE__AUTHENTICATION_FACEBOOK_TOKEN = 4
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(AUTHENTICATION_TYPE)
+} AuthenticationType;
+typedef enum _AccountCreation {
+ ACCOUNT_CREATION__ACCOUNT_CREATION_ALWAYS_PROMPT = 1,
+ ACCOUNT_CREATION__ACCOUNT_CREATION_ALWAYS_CREATE = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ACCOUNT_CREATION)
+} AccountCreation;
+typedef enum _CpuFamily {
+ CPU_FAMILY__CPU_UNKNOWN = 0,
+ CPU_FAMILY__CPU_X86 = 1,
+ CPU_FAMILY__CPU_X86_64 = 2,
+ CPU_FAMILY__CPU_PPC = 3,
+ CPU_FAMILY__CPU_PPC_64 = 4,
+ CPU_FAMILY__CPU_ARM = 5,
+ CPU_FAMILY__CPU_IA64 = 6,
+ CPU_FAMILY__CPU_SH = 7,
+ CPU_FAMILY__CPU_MIPS = 8,
+ CPU_FAMILY__CPU_BLACKFIN = 9
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(CPU_FAMILY)
+} CpuFamily;
+typedef enum _Brand {
+ BRAND__BRAND_UNBRANDED = 0,
+ BRAND__BRAND_INQ = 1,
+ BRAND__BRAND_HTC = 2,
+ BRAND__BRAND_NOKIA = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(BRAND)
+} Brand;
+typedef enum _Os {
+ OS__OS_UNKNOWN = 0,
+ OS__OS_WINDOWS = 1,
+ OS__OS_OSX = 2,
+ OS__OS_IPHONE = 3,
+ OS__OS_S60 = 4,
+ OS__OS_LINUX = 5,
+ OS__OS_WINDOWS_CE = 6,
+ OS__OS_ANDROID = 7,
+ OS__OS_PALM = 8,
+ OS__OS_FREEBSD = 9,
+ OS__OS_BLACKBERRY = 10,
+ OS__OS_SONOS = 11,
+ OS__OS_LOGITECH = 12,
+ OS__OS_WP7 = 13,
+ OS__OS_ONKYO = 14,
+ OS__OS_PHILIPS = 15,
+ OS__OS_WD = 16,
+ OS__OS_VOLVO = 17,
+ OS__OS_TIVO = 18,
+ OS__OS_AWOX = 19,
+ OS__OS_MEEGO = 20,
+ OS__OS_QNXNTO = 21,
+ OS__OS_BCO = 22
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(OS)
+} Os;
+typedef enum _AccountType {
+ ACCOUNT_TYPE__Spotify = 0,
+ ACCOUNT_TYPE__Facebook = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ACCOUNT_TYPE)
+} AccountType;
+
+/* --- messages --- */
+
+struct _ClientResponseEncrypted
+{
+ ProtobufCMessage base;
+ LoginCredentials *login_credentials;
+ protobuf_c_boolean has_account_creation;
+ AccountCreation account_creation;
+ FingerprintResponseUnion *fingerprint_response;
+ PeerTicketUnion *peer_ticket;
+ SystemInfo *system_info;
+ char *platform_model;
+ char *version_string;
+ LibspotifyAppKey *appkey;
+ ClientInfo *client_info;
+};
+#define CLIENT_RESPONSE_ENCRYPTED__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_response_encrypted__descriptor) \
+ , NULL, 0, ACCOUNT_CREATION__ACCOUNT_CREATION_ALWAYS_PROMPT, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+
+struct _LoginCredentials
+{
+ ProtobufCMessage base;
+ char *username;
+ AuthenticationType typ;
+ protobuf_c_boolean has_auth_data;
+ ProtobufCBinaryData auth_data;
+};
+#define LOGIN_CREDENTIALS__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_credentials__descriptor) \
+ , NULL, AUTHENTICATION_TYPE__AUTHENTICATION_USER_PASS, 0, {0,NULL} }
+
+
+struct _FingerprintResponseUnion
+{
+ ProtobufCMessage base;
+ FingerprintGrainResponse *grain;
+ FingerprintHmacRipemdResponse *hmac_ripemd;
+};
+#define FINGERPRINT_RESPONSE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_response_union__descriptor) \
+ , NULL, NULL }
+
+
+struct _FingerprintGrainResponse
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData encrypted_key;
+};
+#define FINGERPRINT_GRAIN_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_grain_response__descriptor) \
+ , {0,NULL} }
+
+
+struct _FingerprintHmacRipemdResponse
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData hmac;
+};
+#define FINGERPRINT_HMAC_RIPEMD_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_hmac_ripemd_response__descriptor) \
+ , {0,NULL} }
+
+
+struct _PeerTicketUnion
+{
+ ProtobufCMessage base;
+ PeerTicketPublicKey *public_key;
+ PeerTicketOld *old_ticket;
+};
+#define PEER_TICKET_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&peer_ticket_union__descriptor) \
+ , NULL, NULL }
+
+
+struct _PeerTicketPublicKey
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData public_key;
+};
+#define PEER_TICKET_PUBLIC_KEY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&peer_ticket_public_key__descriptor) \
+ , {0,NULL} }
+
+
+struct _PeerTicketOld
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData peer_ticket;
+ ProtobufCBinaryData peer_ticket_signature;
+};
+#define PEER_TICKET_OLD__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&peer_ticket_old__descriptor) \
+ , {0,NULL}, {0,NULL} }
+
+
+struct _SystemInfo
+{
+ ProtobufCMessage base;
+ CpuFamily cpu_family;
+ protobuf_c_boolean has_cpu_subtype;
+ uint32_t cpu_subtype;
+ protobuf_c_boolean has_cpu_ext;
+ uint32_t cpu_ext;
+ protobuf_c_boolean has_brand;
+ Brand brand;
+ protobuf_c_boolean has_brand_flags;
+ uint32_t brand_flags;
+ Os os;
+ protobuf_c_boolean has_os_version;
+ uint32_t os_version;
+ protobuf_c_boolean has_os_ext;
+ uint32_t os_ext;
+ char *system_information_string;
+ char *device_id;
+};
+#define SYSTEM_INFO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&system_info__descriptor) \
+ , CPU_FAMILY__CPU_UNKNOWN, 0, 0, 0, 0, 0, BRAND__BRAND_UNBRANDED, 0, 0, OS__OS_UNKNOWN, 0, 0, 0, 0, NULL, NULL }
+
+
+struct _LibspotifyAppKey
+{
+ ProtobufCMessage base;
+ uint32_t version;
+ ProtobufCBinaryData devkey;
+ ProtobufCBinaryData signature;
+ char *useragent;
+ ProtobufCBinaryData callback_hash;
+};
+#define LIBSPOTIFY_APP_KEY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&libspotify_app_key__descriptor) \
+ , 0, {0,NULL}, {0,NULL}, NULL, {0,NULL} }
+
+
+struct _ClientInfo
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_limited;
+ protobuf_c_boolean limited;
+ ClientInfoFacebook *fb;
+ char *language;
+};
+#define CLIENT_INFO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_info__descriptor) \
+ , 0, 0, NULL, NULL }
+
+
+struct _ClientInfoFacebook
+{
+ ProtobufCMessage base;
+ char *machine_id;
+};
+#define CLIENT_INFO_FACEBOOK__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_info_facebook__descriptor) \
+ , NULL }
+
+
+struct _APWelcome
+{
+ ProtobufCMessage base;
+ char *canonical_username;
+ AccountType account_type_logged_in;
+ AccountType credentials_type_logged_in;
+ AuthenticationType reusable_auth_credentials_type;
+ ProtobufCBinaryData reusable_auth_credentials;
+ protobuf_c_boolean has_lfs_secret;
+ ProtobufCBinaryData lfs_secret;
+ AccountInfo *account_info;
+ AccountInfoFacebook *fb;
+};
+#define APWELCOME__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&apwelcome__descriptor) \
+ , NULL, ACCOUNT_TYPE__Spotify, ACCOUNT_TYPE__Spotify, AUTHENTICATION_TYPE__AUTHENTICATION_USER_PASS, {0,NULL}, 0, {0,NULL}, NULL, NULL }
+
+
+struct _AccountInfo
+{
+ ProtobufCMessage base;
+ AccountInfoSpotify *spotify;
+ AccountInfoFacebook *facebook;
+};
+#define ACCOUNT_INFO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&account_info__descriptor) \
+ , NULL, NULL }
+
+
+struct _AccountInfoSpotify
+{
+ ProtobufCMessage base;
+};
+#define ACCOUNT_INFO_SPOTIFY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&account_info_spotify__descriptor) \
+ }
+
+
+struct _AccountInfoFacebook
+{
+ ProtobufCMessage base;
+ char *access_token;
+ char *machine_id;
+};
+#define ACCOUNT_INFO_FACEBOOK__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&account_info_facebook__descriptor) \
+ , NULL, NULL }
+
+
+/* ClientResponseEncrypted methods */
+void client_response_encrypted__init
+ (ClientResponseEncrypted *message);
+size_t client_response_encrypted__get_packed_size
+ (const ClientResponseEncrypted *message);
+size_t client_response_encrypted__pack
+ (const ClientResponseEncrypted *message,
+ uint8_t *out);
+size_t client_response_encrypted__pack_to_buffer
+ (const ClientResponseEncrypted *message,
+ ProtobufCBuffer *buffer);
+ClientResponseEncrypted *
+ client_response_encrypted__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_response_encrypted__free_unpacked
+ (ClientResponseEncrypted *message,
+ ProtobufCAllocator *allocator);
+/* LoginCredentials methods */
+void login_credentials__init
+ (LoginCredentials *message);
+size_t login_credentials__get_packed_size
+ (const LoginCredentials *message);
+size_t login_credentials__pack
+ (const LoginCredentials *message,
+ uint8_t *out);
+size_t login_credentials__pack_to_buffer
+ (const LoginCredentials *message,
+ ProtobufCBuffer *buffer);
+LoginCredentials *
+ login_credentials__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_credentials__free_unpacked
+ (LoginCredentials *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintResponseUnion methods */
+void fingerprint_response_union__init
+ (FingerprintResponseUnion *message);
+size_t fingerprint_response_union__get_packed_size
+ (const FingerprintResponseUnion *message);
+size_t fingerprint_response_union__pack
+ (const FingerprintResponseUnion *message,
+ uint8_t *out);
+size_t fingerprint_response_union__pack_to_buffer
+ (const FingerprintResponseUnion *message,
+ ProtobufCBuffer *buffer);
+FingerprintResponseUnion *
+ fingerprint_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_response_union__free_unpacked
+ (FingerprintResponseUnion *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintGrainResponse methods */
+void fingerprint_grain_response__init
+ (FingerprintGrainResponse *message);
+size_t fingerprint_grain_response__get_packed_size
+ (const FingerprintGrainResponse *message);
+size_t fingerprint_grain_response__pack
+ (const FingerprintGrainResponse *message,
+ uint8_t *out);
+size_t fingerprint_grain_response__pack_to_buffer
+ (const FingerprintGrainResponse *message,
+ ProtobufCBuffer *buffer);
+FingerprintGrainResponse *
+ fingerprint_grain_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_grain_response__free_unpacked
+ (FingerprintGrainResponse *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintHmacRipemdResponse methods */
+void fingerprint_hmac_ripemd_response__init
+ (FingerprintHmacRipemdResponse *message);
+size_t fingerprint_hmac_ripemd_response__get_packed_size
+ (const FingerprintHmacRipemdResponse *message);
+size_t fingerprint_hmac_ripemd_response__pack
+ (const FingerprintHmacRipemdResponse *message,
+ uint8_t *out);
+size_t fingerprint_hmac_ripemd_response__pack_to_buffer
+ (const FingerprintHmacRipemdResponse *message,
+ ProtobufCBuffer *buffer);
+FingerprintHmacRipemdResponse *
+ fingerprint_hmac_ripemd_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_hmac_ripemd_response__free_unpacked
+ (FingerprintHmacRipemdResponse *message,
+ ProtobufCAllocator *allocator);
+/* PeerTicketUnion methods */
+void peer_ticket_union__init
+ (PeerTicketUnion *message);
+size_t peer_ticket_union__get_packed_size
+ (const PeerTicketUnion *message);
+size_t peer_ticket_union__pack
+ (const PeerTicketUnion *message,
+ uint8_t *out);
+size_t peer_ticket_union__pack_to_buffer
+ (const PeerTicketUnion *message,
+ ProtobufCBuffer *buffer);
+PeerTicketUnion *
+ peer_ticket_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void peer_ticket_union__free_unpacked
+ (PeerTicketUnion *message,
+ ProtobufCAllocator *allocator);
+/* PeerTicketPublicKey methods */
+void peer_ticket_public_key__init
+ (PeerTicketPublicKey *message);
+size_t peer_ticket_public_key__get_packed_size
+ (const PeerTicketPublicKey *message);
+size_t peer_ticket_public_key__pack
+ (const PeerTicketPublicKey *message,
+ uint8_t *out);
+size_t peer_ticket_public_key__pack_to_buffer
+ (const PeerTicketPublicKey *message,
+ ProtobufCBuffer *buffer);
+PeerTicketPublicKey *
+ peer_ticket_public_key__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void peer_ticket_public_key__free_unpacked
+ (PeerTicketPublicKey *message,
+ ProtobufCAllocator *allocator);
+/* PeerTicketOld methods */
+void peer_ticket_old__init
+ (PeerTicketOld *message);
+size_t peer_ticket_old__get_packed_size
+ (const PeerTicketOld *message);
+size_t peer_ticket_old__pack
+ (const PeerTicketOld *message,
+ uint8_t *out);
+size_t peer_ticket_old__pack_to_buffer
+ (const PeerTicketOld *message,
+ ProtobufCBuffer *buffer);
+PeerTicketOld *
+ peer_ticket_old__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void peer_ticket_old__free_unpacked
+ (PeerTicketOld *message,
+ ProtobufCAllocator *allocator);
+/* SystemInfo methods */
+void system_info__init
+ (SystemInfo *message);
+size_t system_info__get_packed_size
+ (const SystemInfo *message);
+size_t system_info__pack
+ (const SystemInfo *message,
+ uint8_t *out);
+size_t system_info__pack_to_buffer
+ (const SystemInfo *message,
+ ProtobufCBuffer *buffer);
+SystemInfo *
+ system_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void system_info__free_unpacked
+ (SystemInfo *message,
+ ProtobufCAllocator *allocator);
+/* LibspotifyAppKey methods */
+void libspotify_app_key__init
+ (LibspotifyAppKey *message);
+size_t libspotify_app_key__get_packed_size
+ (const LibspotifyAppKey *message);
+size_t libspotify_app_key__pack
+ (const LibspotifyAppKey *message,
+ uint8_t *out);
+size_t libspotify_app_key__pack_to_buffer
+ (const LibspotifyAppKey *message,
+ ProtobufCBuffer *buffer);
+LibspotifyAppKey *
+ libspotify_app_key__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void libspotify_app_key__free_unpacked
+ (LibspotifyAppKey *message,
+ ProtobufCAllocator *allocator);
+/* ClientInfo methods */
+void client_info__init
+ (ClientInfo *message);
+size_t client_info__get_packed_size
+ (const ClientInfo *message);
+size_t client_info__pack
+ (const ClientInfo *message,
+ uint8_t *out);
+size_t client_info__pack_to_buffer
+ (const ClientInfo *message,
+ ProtobufCBuffer *buffer);
+ClientInfo *
+ client_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_info__free_unpacked
+ (ClientInfo *message,
+ ProtobufCAllocator *allocator);
+/* ClientInfoFacebook methods */
+void client_info_facebook__init
+ (ClientInfoFacebook *message);
+size_t client_info_facebook__get_packed_size
+ (const ClientInfoFacebook *message);
+size_t client_info_facebook__pack
+ (const ClientInfoFacebook *message,
+ uint8_t *out);
+size_t client_info_facebook__pack_to_buffer
+ (const ClientInfoFacebook *message,
+ ProtobufCBuffer *buffer);
+ClientInfoFacebook *
+ client_info_facebook__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_info_facebook__free_unpacked
+ (ClientInfoFacebook *message,
+ ProtobufCAllocator *allocator);
+/* APWelcome methods */
+void apwelcome__init
+ (APWelcome *message);
+size_t apwelcome__get_packed_size
+ (const APWelcome *message);
+size_t apwelcome__pack
+ (const APWelcome *message,
+ uint8_t *out);
+size_t apwelcome__pack_to_buffer
+ (const APWelcome *message,
+ ProtobufCBuffer *buffer);
+APWelcome *
+ apwelcome__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void apwelcome__free_unpacked
+ (APWelcome *message,
+ ProtobufCAllocator *allocator);
+/* AccountInfo methods */
+void account_info__init
+ (AccountInfo *message);
+size_t account_info__get_packed_size
+ (const AccountInfo *message);
+size_t account_info__pack
+ (const AccountInfo *message,
+ uint8_t *out);
+size_t account_info__pack_to_buffer
+ (const AccountInfo *message,
+ ProtobufCBuffer *buffer);
+AccountInfo *
+ account_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void account_info__free_unpacked
+ (AccountInfo *message,
+ ProtobufCAllocator *allocator);
+/* AccountInfoSpotify methods */
+void account_info_spotify__init
+ (AccountInfoSpotify *message);
+size_t account_info_spotify__get_packed_size
+ (const AccountInfoSpotify *message);
+size_t account_info_spotify__pack
+ (const AccountInfoSpotify *message,
+ uint8_t *out);
+size_t account_info_spotify__pack_to_buffer
+ (const AccountInfoSpotify *message,
+ ProtobufCBuffer *buffer);
+AccountInfoSpotify *
+ account_info_spotify__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void account_info_spotify__free_unpacked
+ (AccountInfoSpotify *message,
+ ProtobufCAllocator *allocator);
+/* AccountInfoFacebook methods */
+void account_info_facebook__init
+ (AccountInfoFacebook *message);
+size_t account_info_facebook__get_packed_size
+ (const AccountInfoFacebook *message);
+size_t account_info_facebook__pack
+ (const AccountInfoFacebook *message,
+ uint8_t *out);
+size_t account_info_facebook__pack_to_buffer
+ (const AccountInfoFacebook *message,
+ ProtobufCBuffer *buffer);
+AccountInfoFacebook *
+ account_info_facebook__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void account_info_facebook__free_unpacked
+ (AccountInfoFacebook *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*ClientResponseEncrypted_Closure)
+ (const ClientResponseEncrypted *message,
+ void *closure_data);
+typedef void (*LoginCredentials_Closure)
+ (const LoginCredentials *message,
+ void *closure_data);
+typedef void (*FingerprintResponseUnion_Closure)
+ (const FingerprintResponseUnion *message,
+ void *closure_data);
+typedef void (*FingerprintGrainResponse_Closure)
+ (const FingerprintGrainResponse *message,
+ void *closure_data);
+typedef void (*FingerprintHmacRipemdResponse_Closure)
+ (const FingerprintHmacRipemdResponse *message,
+ void *closure_data);
+typedef void (*PeerTicketUnion_Closure)
+ (const PeerTicketUnion *message,
+ void *closure_data);
+typedef void (*PeerTicketPublicKey_Closure)
+ (const PeerTicketPublicKey *message,
+ void *closure_data);
+typedef void (*PeerTicketOld_Closure)
+ (const PeerTicketOld *message,
+ void *closure_data);
+typedef void (*SystemInfo_Closure)
+ (const SystemInfo *message,
+ void *closure_data);
+typedef void (*LibspotifyAppKey_Closure)
+ (const LibspotifyAppKey *message,
+ void *closure_data);
+typedef void (*ClientInfo_Closure)
+ (const ClientInfo *message,
+ void *closure_data);
+typedef void (*ClientInfoFacebook_Closure)
+ (const ClientInfoFacebook *message,
+ void *closure_data);
+typedef void (*APWelcome_Closure)
+ (const APWelcome *message,
+ void *closure_data);
+typedef void (*AccountInfo_Closure)
+ (const AccountInfo *message,
+ void *closure_data);
+typedef void (*AccountInfoSpotify_Closure)
+ (const AccountInfoSpotify *message,
+ void *closure_data);
+typedef void (*AccountInfoFacebook_Closure)
+ (const AccountInfoFacebook *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCEnumDescriptor authentication_type__descriptor;
+extern const ProtobufCEnumDescriptor account_creation__descriptor;
+extern const ProtobufCEnumDescriptor cpu_family__descriptor;
+extern const ProtobufCEnumDescriptor brand__descriptor;
+extern const ProtobufCEnumDescriptor os__descriptor;
+extern const ProtobufCEnumDescriptor account_type__descriptor;
+extern const ProtobufCMessageDescriptor client_response_encrypted__descriptor;
+extern const ProtobufCMessageDescriptor login_credentials__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_response_union__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_grain_response__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_hmac_ripemd_response__descriptor;
+extern const ProtobufCMessageDescriptor peer_ticket_union__descriptor;
+extern const ProtobufCMessageDescriptor peer_ticket_public_key__descriptor;
+extern const ProtobufCMessageDescriptor peer_ticket_old__descriptor;
+extern const ProtobufCMessageDescriptor system_info__descriptor;
+extern const ProtobufCMessageDescriptor libspotify_app_key__descriptor;
+extern const ProtobufCMessageDescriptor client_info__descriptor;
+extern const ProtobufCMessageDescriptor client_info_facebook__descriptor;
+extern const ProtobufCMessageDescriptor apwelcome__descriptor;
+extern const ProtobufCMessageDescriptor account_info__descriptor;
+extern const ProtobufCMessageDescriptor account_info_spotify__descriptor;
+extern const ProtobufCMessageDescriptor account_info_facebook__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_proto_2fauthentication_2eproto__INCLUDED */
diff --git a/src/inputs/librespot-c/src/proto/authentication.proto b/src/inputs/librespot-c/src/proto/authentication.proto
new file mode 100644
index 00000000..3c0aff3e
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/authentication.proto
@@ -0,0 +1,165 @@
+syntax = "proto2";
+
+message ClientResponseEncrypted {
+ required LoginCredentials login_credentials = 0xa;
+ optional AccountCreation account_creation = 0x14;
+ optional FingerprintResponseUnion fingerprint_response = 0x1e;
+ optional PeerTicketUnion peer_ticket = 0x28;
+ required SystemInfo system_info = 0x32;
+ optional string platform_model = 0x3c;
+ optional string version_string = 0x46;
+ optional LibspotifyAppKey appkey = 0x50;
+ optional ClientInfo client_info = 0x5a;
+}
+
+message LoginCredentials {
+ optional string username = 0xa;
+ required AuthenticationType typ = 0x14;
+ optional bytes auth_data = 0x1e;
+}
+
+enum AuthenticationType {
+ AUTHENTICATION_USER_PASS = 0x0;
+ AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS = 0x1;
+ AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS = 0x2;
+ AUTHENTICATION_SPOTIFY_TOKEN = 0x3;
+ AUTHENTICATION_FACEBOOK_TOKEN = 0x4;
+}
+
+enum AccountCreation {
+ ACCOUNT_CREATION_ALWAYS_PROMPT = 0x1;
+ ACCOUNT_CREATION_ALWAYS_CREATE = 0x3;
+}
+
+message FingerprintResponseUnion {
+ optional FingerprintGrainResponse grain = 0xa;
+ optional FingerprintHmacRipemdResponse hmac_ripemd = 0x14;
+}
+
+message FingerprintGrainResponse {
+ required bytes encrypted_key = 0xa;
+}
+
+message FingerprintHmacRipemdResponse {
+ required bytes hmac = 0xa;
+}
+
+message PeerTicketUnion {
+ optional PeerTicketPublicKey public_key = 0xa;
+ optional PeerTicketOld old_ticket = 0x14;
+}
+
+message PeerTicketPublicKey {
+ required bytes public_key = 0xa;
+}
+
+message PeerTicketOld {
+ required bytes peer_ticket = 0xa;
+ required bytes peer_ticket_signature = 0x14;
+}
+
+message SystemInfo {
+ required CpuFamily cpu_family = 0xa;
+ optional uint32 cpu_subtype = 0x14;
+ optional uint32 cpu_ext = 0x1e;
+ optional Brand brand = 0x28;
+ optional uint32 brand_flags = 0x32;
+ required Os os = 0x3c;
+ optional uint32 os_version = 0x46;
+ optional uint32 os_ext = 0x50;
+ optional string system_information_string = 0x5a;
+ optional string device_id = 0x64;
+}
+
+enum CpuFamily {
+ CPU_UNKNOWN = 0x0;
+ CPU_X86 = 0x1;
+ CPU_X86_64 = 0x2;
+ CPU_PPC = 0x3;
+ CPU_PPC_64 = 0x4;
+ CPU_ARM = 0x5;
+ CPU_IA64 = 0x6;
+ CPU_SH = 0x7;
+ CPU_MIPS = 0x8;
+ CPU_BLACKFIN = 0x9;
+}
+
+enum Brand {
+ BRAND_UNBRANDED = 0x0;
+ BRAND_INQ = 0x1;
+ BRAND_HTC = 0x2;
+ BRAND_NOKIA = 0x3;
+}
+
+enum Os {
+ OS_UNKNOWN = 0x0;
+ OS_WINDOWS = 0x1;
+ OS_OSX = 0x2;
+ OS_IPHONE = 0x3;
+ OS_S60 = 0x4;
+ OS_LINUX = 0x5;
+ OS_WINDOWS_CE = 0x6;
+ OS_ANDROID = 0x7;
+ OS_PALM = 0x8;
+ OS_FREEBSD = 0x9;
+ OS_BLACKBERRY = 0xa;
+ OS_SONOS = 0xb;
+ OS_LOGITECH = 0xc;
+ OS_WP7 = 0xd;
+ OS_ONKYO = 0xe;
+ OS_PHILIPS = 0xf;
+ OS_WD = 0x10;
+ OS_VOLVO = 0x11;
+ OS_TIVO = 0x12;
+ OS_AWOX = 0x13;
+ OS_MEEGO = 0x14;
+ OS_QNXNTO = 0x15;
+ OS_BCO = 0x16;
+}
+
+message LibspotifyAppKey {
+ required uint32 version = 0x1;
+ required bytes devkey = 0x2;
+ required bytes signature = 0x3;
+ required string useragent = 0x4;
+ required bytes callback_hash = 0x5;
+}
+
+message ClientInfo {
+ optional bool limited = 0x1;
+ optional ClientInfoFacebook fb = 0x2;
+ optional string language = 0x3;
+}
+
+message ClientInfoFacebook {
+ optional string machine_id = 0x1;
+}
+
+message APWelcome {
+ required string canonical_username = 0xa;
+ required AccountType account_type_logged_in = 0x14;
+ required AccountType credentials_type_logged_in = 0x19;
+ required AuthenticationType reusable_auth_credentials_type = 0x1e;
+ required bytes reusable_auth_credentials = 0x28;
+ optional bytes lfs_secret = 0x32;
+ optional AccountInfo account_info = 0x3c;
+ optional AccountInfoFacebook fb = 0x46;
+}
+
+enum AccountType {
+ Spotify = 0x0;
+ Facebook = 0x1;
+}
+
+message AccountInfo {
+ optional AccountInfoSpotify spotify = 0x1;
+ optional AccountInfoFacebook facebook = 0x2;
+}
+
+message AccountInfoSpotify {
+}
+
+message AccountInfoFacebook {
+ optional string access_token = 0x1;
+ optional string machine_id = 0x2;
+}
diff --git a/src/inputs/librespot-c/src/proto/facebook-publish.proto b/src/inputs/librespot-c/src/proto/facebook-publish.proto
new file mode 100644
index 00000000..4edef249
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/facebook-publish.proto
@@ -0,0 +1,51 @@
+syntax = "proto2";
+
+message EventReply {
+ optional int32 queued = 0x1;
+ optional RetryInfo retry = 0x2;
+}
+
+message RetryInfo {
+ optional int32 retry_delay = 0x1;
+ optional int32 max_retry = 0x2;
+}
+
+message Id {
+ optional string uri = 0x1;
+ optional int64 start_time = 0x2;
+}
+
+message Start {
+ optional int32 length = 0x1;
+ optional string context_uri = 0x2;
+ optional int64 end_time = 0x3;
+}
+
+message Seek {
+ optional int64 end_time = 0x1;
+}
+
+message Pause {
+ optional int32 seconds_played = 0x1;
+ optional int64 end_time = 0x2;
+}
+
+message Resume {
+ optional int32 seconds_played = 0x1;
+ optional int64 end_time = 0x2;
+}
+
+message End {
+ optional int32 seconds_played = 0x1;
+ optional int64 end_time = 0x2;
+}
+
+message Event {
+ optional Id id = 0x1;
+ optional Start start = 0x2;
+ optional Seek seek = 0x3;
+ optional Pause pause = 0x4;
+ optional Resume resume = 0x5;
+ optional End end = 0x6;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/facebook.proto b/src/inputs/librespot-c/src/proto/facebook.proto
new file mode 100644
index 00000000..8227c5a1
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/facebook.proto
@@ -0,0 +1,183 @@
+syntax = "proto2";
+
+message Credential {
+ optional string facebook_uid = 0x1;
+ optional string access_token = 0x2;
+}
+
+message EnableRequest {
+ optional Credential credential = 0x1;
+}
+
+message EnableReply {
+ optional Credential credential = 0x1;
+}
+
+message DisableRequest {
+ optional Credential credential = 0x1;
+}
+
+message RevokeRequest {
+ optional Credential credential = 0x1;
+}
+
+message InspectCredentialRequest {
+ optional Credential credential = 0x1;
+}
+
+message InspectCredentialReply {
+ optional Credential alternative_credential = 0x1;
+ optional bool app_user = 0x2;
+ optional bool permanent_error = 0x3;
+ optional bool transient_error = 0x4;
+}
+
+message UserState {
+ optional Credential credential = 0x1;
+}
+
+message UpdateUserStateRequest {
+ optional Credential credential = 0x1;
+}
+
+message OpenGraphError {
+ repeated string permanent = 0x1;
+ repeated string invalid_token = 0x2;
+ repeated string retries = 0x3;
+}
+
+message OpenGraphScrobble {
+ optional int32 create_delay = 0x1;
+}
+
+message OpenGraphConfig {
+ optional OpenGraphError error = 0x1;
+ optional OpenGraphScrobble scrobble = 0x2;
+}
+
+message AuthConfig {
+ optional string url = 0x1;
+ repeated string permissions = 0x2;
+ repeated string blacklist = 0x3;
+ repeated string whitelist = 0x4;
+ repeated string cancel = 0x5;
+}
+
+message ConfigReply {
+ optional string domain = 0x1;
+ optional string app_id = 0x2;
+ optional string app_namespace = 0x3;
+ optional AuthConfig auth = 0x4;
+ optional OpenGraphConfig og = 0x5;
+}
+
+message UserFields {
+ optional bool app_user = 0x1;
+ optional bool display_name = 0x2;
+ optional bool first_name = 0x3;
+ optional bool middle_name = 0x4;
+ optional bool last_name = 0x5;
+ optional bool picture_large = 0x6;
+ optional bool picture_square = 0x7;
+ optional bool gender = 0x8;
+ optional bool email = 0x9;
+}
+
+message UserOptions {
+ optional bool cache_is_king = 0x1;
+}
+
+message UserRequest {
+ optional UserOptions options = 0x1;
+ optional UserFields fields = 0x2;
+}
+
+message User {
+ optional string spotify_username = 0x1;
+ optional string facebook_uid = 0x2;
+ optional bool app_user = 0x3;
+ optional string display_name = 0x4;
+ optional string first_name = 0x5;
+ optional string middle_name = 0x6;
+ optional string last_name = 0x7;
+ optional string picture_large = 0x8;
+ optional string picture_square = 0x9;
+ optional string gender = 0xa;
+ optional string email = 0xb;
+}
+
+message FriendsFields {
+ optional bool app_user = 0x1;
+ optional bool display_name = 0x2;
+ optional bool picture_large = 0x6;
+}
+
+message FriendsOptions {
+ optional int32 limit = 0x1;
+ optional int32 offset = 0x2;
+ optional bool cache_is_king = 0x3;
+ optional bool app_friends = 0x4;
+ optional bool non_app_friends = 0x5;
+}
+
+message FriendsRequest {
+ optional FriendsOptions options = 0x1;
+ optional FriendsFields fields = 0x2;
+}
+
+message FriendsReply {
+ repeated User friends = 0x1;
+ optional bool more = 0x2;
+}
+
+message ShareRequest {
+ optional Credential credential = 0x1;
+ optional string uri = 0x2;
+ optional string message_text = 0x3;
+}
+
+message ShareReply {
+ optional string post_id = 0x1;
+}
+
+message InboxRequest {
+ optional Credential credential = 0x1;
+ repeated string facebook_uids = 0x3;
+ optional string message_text = 0x4;
+ optional string message_link = 0x5;
+}
+
+message InboxReply {
+ optional string message_id = 0x1;
+ optional string thread_id = 0x2;
+}
+
+message PermissionsOptions {
+ optional bool cache_is_king = 0x1;
+}
+
+message PermissionsRequest {
+ optional Credential credential = 0x1;
+ optional PermissionsOptions options = 0x2;
+}
+
+message PermissionsReply {
+ repeated string permissions = 0x1;
+}
+
+message GrantPermissionsRequest {
+ optional Credential credential = 0x1;
+ repeated string permissions = 0x2;
+}
+
+message GrantPermissionsReply {
+ repeated string granted = 0x1;
+ repeated string failed = 0x2;
+}
+
+message TransferRequest {
+ optional Credential credential = 0x1;
+ optional string source_username = 0x2;
+ optional string target_username = 0x3;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/keyexchange.pb-c.c b/src/inputs/librespot-c/src/proto/keyexchange.pb-c.c
new file mode 100644
index 00000000..ebb365d8
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/keyexchange.pb-c.c
@@ -0,0 +1,2933 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/keyexchange.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include "keyexchange.pb-c.h"
+void client_hello__init
+ (ClientHello *message)
+{
+ static const ClientHello init_value = CLIENT_HELLO__INIT;
+ *message = init_value;
+}
+size_t client_hello__get_packed_size
+ (const ClientHello *message)
+{
+ assert(message->base.descriptor == &client_hello__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t client_hello__pack
+ (const ClientHello *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &client_hello__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t client_hello__pack_to_buffer
+ (const ClientHello *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &client_hello__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ClientHello *
+ client_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ClientHello *)
+ protobuf_c_message_unpack (&client_hello__descriptor,
+ allocator, len, data);
+}
+void client_hello__free_unpacked
+ (ClientHello *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &client_hello__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void build_info__init
+ (BuildInfo *message)
+{
+ static const BuildInfo init_value = BUILD_INFO__INIT;
+ *message = init_value;
+}
+size_t build_info__get_packed_size
+ (const BuildInfo *message)
+{
+ assert(message->base.descriptor == &build_info__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t build_info__pack
+ (const BuildInfo *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &build_info__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t build_info__pack_to_buffer
+ (const BuildInfo *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &build_info__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+BuildInfo *
+ build_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (BuildInfo *)
+ protobuf_c_message_unpack (&build_info__descriptor,
+ allocator, len, data);
+}
+void build_info__free_unpacked
+ (BuildInfo *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &build_info__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_hello_union__init
+ (LoginCryptoHelloUnion *message)
+{
+ static const LoginCryptoHelloUnion init_value = LOGIN_CRYPTO_HELLO_UNION__INIT;
+ *message = init_value;
+}
+size_t login_crypto_hello_union__get_packed_size
+ (const LoginCryptoHelloUnion *message)
+{
+ assert(message->base.descriptor == &login_crypto_hello_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_hello_union__pack
+ (const LoginCryptoHelloUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_hello_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_hello_union__pack_to_buffer
+ (const LoginCryptoHelloUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_hello_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoHelloUnion *
+ login_crypto_hello_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoHelloUnion *)
+ protobuf_c_message_unpack (&login_crypto_hello_union__descriptor,
+ allocator, len, data);
+}
+void login_crypto_hello_union__free_unpacked
+ (LoginCryptoHelloUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_hello_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_diffie_hellman_hello__init
+ (LoginCryptoDiffieHellmanHello *message)
+{
+ static const LoginCryptoDiffieHellmanHello init_value = LOGIN_CRYPTO_DIFFIE_HELLMAN_HELLO__INIT;
+ *message = init_value;
+}
+size_t login_crypto_diffie_hellman_hello__get_packed_size
+ (const LoginCryptoDiffieHellmanHello *message)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_hello__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_diffie_hellman_hello__pack
+ (const LoginCryptoDiffieHellmanHello *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_hello__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_diffie_hellman_hello__pack_to_buffer
+ (const LoginCryptoDiffieHellmanHello *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_hello__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoDiffieHellmanHello *
+ login_crypto_diffie_hellman_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoDiffieHellmanHello *)
+ protobuf_c_message_unpack (&login_crypto_diffie_hellman_hello__descriptor,
+ allocator, len, data);
+}
+void login_crypto_diffie_hellman_hello__free_unpacked
+ (LoginCryptoDiffieHellmanHello *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_hello__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void feature_set__init
+ (FeatureSet *message)
+{
+ static const FeatureSet init_value = FEATURE_SET__INIT;
+ *message = init_value;
+}
+size_t feature_set__get_packed_size
+ (const FeatureSet *message)
+{
+ assert(message->base.descriptor == &feature_set__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t feature_set__pack
+ (const FeatureSet *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &feature_set__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t feature_set__pack_to_buffer
+ (const FeatureSet *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &feature_set__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FeatureSet *
+ feature_set__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FeatureSet *)
+ protobuf_c_message_unpack (&feature_set__descriptor,
+ allocator, len, data);
+}
+void feature_set__free_unpacked
+ (FeatureSet *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &feature_set__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void apresponse_message__init
+ (APResponseMessage *message)
+{
+ static const APResponseMessage init_value = APRESPONSE_MESSAGE__INIT;
+ *message = init_value;
+}
+size_t apresponse_message__get_packed_size
+ (const APResponseMessage *message)
+{
+ assert(message->base.descriptor == &apresponse_message__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t apresponse_message__pack
+ (const APResponseMessage *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &apresponse_message__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t apresponse_message__pack_to_buffer
+ (const APResponseMessage *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &apresponse_message__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+APResponseMessage *
+ apresponse_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (APResponseMessage *)
+ protobuf_c_message_unpack (&apresponse_message__descriptor,
+ allocator, len, data);
+}
+void apresponse_message__free_unpacked
+ (APResponseMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &apresponse_message__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void apchallenge__init
+ (APChallenge *message)
+{
+ static const APChallenge init_value = APCHALLENGE__INIT;
+ *message = init_value;
+}
+size_t apchallenge__get_packed_size
+ (const APChallenge *message)
+{
+ assert(message->base.descriptor == &apchallenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t apchallenge__pack
+ (const APChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &apchallenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t apchallenge__pack_to_buffer
+ (const APChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &apchallenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+APChallenge *
+ apchallenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (APChallenge *)
+ protobuf_c_message_unpack (&apchallenge__descriptor,
+ allocator, len, data);
+}
+void apchallenge__free_unpacked
+ (APChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &apchallenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_challenge_union__init
+ (LoginCryptoChallengeUnion *message)
+{
+ static const LoginCryptoChallengeUnion init_value = LOGIN_CRYPTO_CHALLENGE_UNION__INIT;
+ *message = init_value;
+}
+size_t login_crypto_challenge_union__get_packed_size
+ (const LoginCryptoChallengeUnion *message)
+{
+ assert(message->base.descriptor == &login_crypto_challenge_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_challenge_union__pack
+ (const LoginCryptoChallengeUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_challenge_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_challenge_union__pack_to_buffer
+ (const LoginCryptoChallengeUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_challenge_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoChallengeUnion *
+ login_crypto_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoChallengeUnion *)
+ protobuf_c_message_unpack (&login_crypto_challenge_union__descriptor,
+ allocator, len, data);
+}
+void login_crypto_challenge_union__free_unpacked
+ (LoginCryptoChallengeUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_challenge_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_diffie_hellman_challenge__init
+ (LoginCryptoDiffieHellmanChallenge *message)
+{
+ static const LoginCryptoDiffieHellmanChallenge init_value = LOGIN_CRYPTO_DIFFIE_HELLMAN_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t login_crypto_diffie_hellman_challenge__get_packed_size
+ (const LoginCryptoDiffieHellmanChallenge *message)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_diffie_hellman_challenge__pack
+ (const LoginCryptoDiffieHellmanChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_diffie_hellman_challenge__pack_to_buffer
+ (const LoginCryptoDiffieHellmanChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoDiffieHellmanChallenge *
+ login_crypto_diffie_hellman_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoDiffieHellmanChallenge *)
+ protobuf_c_message_unpack (&login_crypto_diffie_hellman_challenge__descriptor,
+ allocator, len, data);
+}
+void login_crypto_diffie_hellman_challenge__free_unpacked
+ (LoginCryptoDiffieHellmanChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_challenge_union__init
+ (FingerprintChallengeUnion *message)
+{
+ static const FingerprintChallengeUnion init_value = FINGERPRINT_CHALLENGE_UNION__INIT;
+ *message = init_value;
+}
+size_t fingerprint_challenge_union__get_packed_size
+ (const FingerprintChallengeUnion *message)
+{
+ assert(message->base.descriptor == &fingerprint_challenge_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_challenge_union__pack
+ (const FingerprintChallengeUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_challenge_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_challenge_union__pack_to_buffer
+ (const FingerprintChallengeUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_challenge_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintChallengeUnion *
+ fingerprint_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintChallengeUnion *)
+ protobuf_c_message_unpack (&fingerprint_challenge_union__descriptor,
+ allocator, len, data);
+}
+void fingerprint_challenge_union__free_unpacked
+ (FingerprintChallengeUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_challenge_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_grain_challenge__init
+ (FingerprintGrainChallenge *message)
+{
+ static const FingerprintGrainChallenge init_value = FINGERPRINT_GRAIN_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t fingerprint_grain_challenge__get_packed_size
+ (const FingerprintGrainChallenge *message)
+{
+ assert(message->base.descriptor == &fingerprint_grain_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_grain_challenge__pack
+ (const FingerprintGrainChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_grain_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_grain_challenge__pack_to_buffer
+ (const FingerprintGrainChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_grain_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintGrainChallenge *
+ fingerprint_grain_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintGrainChallenge *)
+ protobuf_c_message_unpack (&fingerprint_grain_challenge__descriptor,
+ allocator, len, data);
+}
+void fingerprint_grain_challenge__free_unpacked
+ (FingerprintGrainChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_grain_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void fingerprint_hmac_ripemd_challenge__init
+ (FingerprintHmacRipemdChallenge *message)
+{
+ static const FingerprintHmacRipemdChallenge init_value = FINGERPRINT_HMAC_RIPEMD_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t fingerprint_hmac_ripemd_challenge__get_packed_size
+ (const FingerprintHmacRipemdChallenge *message)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t fingerprint_hmac_ripemd_challenge__pack
+ (const FingerprintHmacRipemdChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t fingerprint_hmac_ripemd_challenge__pack_to_buffer
+ (const FingerprintHmacRipemdChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+FingerprintHmacRipemdChallenge *
+ fingerprint_hmac_ripemd_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (FingerprintHmacRipemdChallenge *)
+ protobuf_c_message_unpack (&fingerprint_hmac_ripemd_challenge__descriptor,
+ allocator, len, data);
+}
+void fingerprint_hmac_ripemd_challenge__free_unpacked
+ (FingerprintHmacRipemdChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &fingerprint_hmac_ripemd_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void po_wchallenge_union__init
+ (PoWChallengeUnion *message)
+{
+ static const PoWChallengeUnion init_value = PO_WCHALLENGE_UNION__INIT;
+ *message = init_value;
+}
+size_t po_wchallenge_union__get_packed_size
+ (const PoWChallengeUnion *message)
+{
+ assert(message->base.descriptor == &po_wchallenge_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t po_wchallenge_union__pack
+ (const PoWChallengeUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &po_wchallenge_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t po_wchallenge_union__pack_to_buffer
+ (const PoWChallengeUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &po_wchallenge_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PoWChallengeUnion *
+ po_wchallenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PoWChallengeUnion *)
+ protobuf_c_message_unpack (&po_wchallenge_union__descriptor,
+ allocator, len, data);
+}
+void po_wchallenge_union__free_unpacked
+ (PoWChallengeUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &po_wchallenge_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void po_whash_cash_challenge__init
+ (PoWHashCashChallenge *message)
+{
+ static const PoWHashCashChallenge init_value = PO_WHASH_CASH_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t po_whash_cash_challenge__get_packed_size
+ (const PoWHashCashChallenge *message)
+{
+ assert(message->base.descriptor == &po_whash_cash_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t po_whash_cash_challenge__pack
+ (const PoWHashCashChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &po_whash_cash_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t po_whash_cash_challenge__pack_to_buffer
+ (const PoWHashCashChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &po_whash_cash_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PoWHashCashChallenge *
+ po_whash_cash_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PoWHashCashChallenge *)
+ protobuf_c_message_unpack (&po_whash_cash_challenge__descriptor,
+ allocator, len, data);
+}
+void po_whash_cash_challenge__free_unpacked
+ (PoWHashCashChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &po_whash_cash_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_challenge_union__init
+ (CryptoChallengeUnion *message)
+{
+ static const CryptoChallengeUnion init_value = CRYPTO_CHALLENGE_UNION__INIT;
+ *message = init_value;
+}
+size_t crypto_challenge_union__get_packed_size
+ (const CryptoChallengeUnion *message)
+{
+ assert(message->base.descriptor == &crypto_challenge_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_challenge_union__pack
+ (const CryptoChallengeUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_challenge_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_challenge_union__pack_to_buffer
+ (const CryptoChallengeUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_challenge_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoChallengeUnion *
+ crypto_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoChallengeUnion *)
+ protobuf_c_message_unpack (&crypto_challenge_union__descriptor,
+ allocator, len, data);
+}
+void crypto_challenge_union__free_unpacked
+ (CryptoChallengeUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_challenge_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_shannon_challenge__init
+ (CryptoShannonChallenge *message)
+{
+ static const CryptoShannonChallenge init_value = CRYPTO_SHANNON_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t crypto_shannon_challenge__get_packed_size
+ (const CryptoShannonChallenge *message)
+{
+ assert(message->base.descriptor == &crypto_shannon_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_shannon_challenge__pack
+ (const CryptoShannonChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_shannon_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_shannon_challenge__pack_to_buffer
+ (const CryptoShannonChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_shannon_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoShannonChallenge *
+ crypto_shannon_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoShannonChallenge *)
+ protobuf_c_message_unpack (&crypto_shannon_challenge__descriptor,
+ allocator, len, data);
+}
+void crypto_shannon_challenge__free_unpacked
+ (CryptoShannonChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_shannon_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_rc4_sha1_hmac_challenge__init
+ (CryptoRc4Sha1HmacChallenge *message)
+{
+ static const CryptoRc4Sha1HmacChallenge init_value = CRYPTO_RC4_SHA1_HMAC_CHALLENGE__INIT;
+ *message = init_value;
+}
+size_t crypto_rc4_sha1_hmac_challenge__get_packed_size
+ (const CryptoRc4Sha1HmacChallenge *message)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_challenge__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_rc4_sha1_hmac_challenge__pack
+ (const CryptoRc4Sha1HmacChallenge *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_challenge__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_rc4_sha1_hmac_challenge__pack_to_buffer
+ (const CryptoRc4Sha1HmacChallenge *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_challenge__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoRc4Sha1HmacChallenge *
+ crypto_rc4_sha1_hmac_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoRc4Sha1HmacChallenge *)
+ protobuf_c_message_unpack (&crypto_rc4_sha1_hmac_challenge__descriptor,
+ allocator, len, data);
+}
+void crypto_rc4_sha1_hmac_challenge__free_unpacked
+ (CryptoRc4Sha1HmacChallenge *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_challenge__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void upgrade_required_message__init
+ (UpgradeRequiredMessage *message)
+{
+ static const UpgradeRequiredMessage init_value = UPGRADE_REQUIRED_MESSAGE__INIT;
+ *message = init_value;
+}
+size_t upgrade_required_message__get_packed_size
+ (const UpgradeRequiredMessage *message)
+{
+ assert(message->base.descriptor == &upgrade_required_message__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t upgrade_required_message__pack
+ (const UpgradeRequiredMessage *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &upgrade_required_message__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t upgrade_required_message__pack_to_buffer
+ (const UpgradeRequiredMessage *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &upgrade_required_message__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+UpgradeRequiredMessage *
+ upgrade_required_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (UpgradeRequiredMessage *)
+ protobuf_c_message_unpack (&upgrade_required_message__descriptor,
+ allocator, len, data);
+}
+void upgrade_required_message__free_unpacked
+ (UpgradeRequiredMessage *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &upgrade_required_message__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void aplogin_failed__init
+ (APLoginFailed *message)
+{
+ static const APLoginFailed init_value = APLOGIN_FAILED__INIT;
+ *message = init_value;
+}
+size_t aplogin_failed__get_packed_size
+ (const APLoginFailed *message)
+{
+ assert(message->base.descriptor == &aplogin_failed__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t aplogin_failed__pack
+ (const APLoginFailed *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &aplogin_failed__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t aplogin_failed__pack_to_buffer
+ (const APLoginFailed *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &aplogin_failed__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+APLoginFailed *
+ aplogin_failed__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (APLoginFailed *)
+ protobuf_c_message_unpack (&aplogin_failed__descriptor,
+ allocator, len, data);
+}
+void aplogin_failed__free_unpacked
+ (APLoginFailed *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &aplogin_failed__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void client_response_plaintext__init
+ (ClientResponsePlaintext *message)
+{
+ static const ClientResponsePlaintext init_value = CLIENT_RESPONSE_PLAINTEXT__INIT;
+ *message = init_value;
+}
+size_t client_response_plaintext__get_packed_size
+ (const ClientResponsePlaintext *message)
+{
+ assert(message->base.descriptor == &client_response_plaintext__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t client_response_plaintext__pack
+ (const ClientResponsePlaintext *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &client_response_plaintext__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t client_response_plaintext__pack_to_buffer
+ (const ClientResponsePlaintext *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &client_response_plaintext__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ClientResponsePlaintext *
+ client_response_plaintext__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ClientResponsePlaintext *)
+ protobuf_c_message_unpack (&client_response_plaintext__descriptor,
+ allocator, len, data);
+}
+void client_response_plaintext__free_unpacked
+ (ClientResponsePlaintext *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &client_response_plaintext__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_response_union__init
+ (LoginCryptoResponseUnion *message)
+{
+ static const LoginCryptoResponseUnion init_value = LOGIN_CRYPTO_RESPONSE_UNION__INIT;
+ *message = init_value;
+}
+size_t login_crypto_response_union__get_packed_size
+ (const LoginCryptoResponseUnion *message)
+{
+ assert(message->base.descriptor == &login_crypto_response_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_response_union__pack
+ (const LoginCryptoResponseUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_response_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_response_union__pack_to_buffer
+ (const LoginCryptoResponseUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_response_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoResponseUnion *
+ login_crypto_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoResponseUnion *)
+ protobuf_c_message_unpack (&login_crypto_response_union__descriptor,
+ allocator, len, data);
+}
+void login_crypto_response_union__free_unpacked
+ (LoginCryptoResponseUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_response_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void login_crypto_diffie_hellman_response__init
+ (LoginCryptoDiffieHellmanResponse *message)
+{
+ static const LoginCryptoDiffieHellmanResponse init_value = LOGIN_CRYPTO_DIFFIE_HELLMAN_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t login_crypto_diffie_hellman_response__get_packed_size
+ (const LoginCryptoDiffieHellmanResponse *message)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t login_crypto_diffie_hellman_response__pack
+ (const LoginCryptoDiffieHellmanResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t login_crypto_diffie_hellman_response__pack_to_buffer
+ (const LoginCryptoDiffieHellmanResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+LoginCryptoDiffieHellmanResponse *
+ login_crypto_diffie_hellman_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (LoginCryptoDiffieHellmanResponse *)
+ protobuf_c_message_unpack (&login_crypto_diffie_hellman_response__descriptor,
+ allocator, len, data);
+}
+void login_crypto_diffie_hellman_response__free_unpacked
+ (LoginCryptoDiffieHellmanResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &login_crypto_diffie_hellman_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void po_wresponse_union__init
+ (PoWResponseUnion *message)
+{
+ static const PoWResponseUnion init_value = PO_WRESPONSE_UNION__INIT;
+ *message = init_value;
+}
+size_t po_wresponse_union__get_packed_size
+ (const PoWResponseUnion *message)
+{
+ assert(message->base.descriptor == &po_wresponse_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t po_wresponse_union__pack
+ (const PoWResponseUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &po_wresponse_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t po_wresponse_union__pack_to_buffer
+ (const PoWResponseUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &po_wresponse_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PoWResponseUnion *
+ po_wresponse_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PoWResponseUnion *)
+ protobuf_c_message_unpack (&po_wresponse_union__descriptor,
+ allocator, len, data);
+}
+void po_wresponse_union__free_unpacked
+ (PoWResponseUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &po_wresponse_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void po_whash_cash_response__init
+ (PoWHashCashResponse *message)
+{
+ static const PoWHashCashResponse init_value = PO_WHASH_CASH_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t po_whash_cash_response__get_packed_size
+ (const PoWHashCashResponse *message)
+{
+ assert(message->base.descriptor == &po_whash_cash_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t po_whash_cash_response__pack
+ (const PoWHashCashResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &po_whash_cash_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t po_whash_cash_response__pack_to_buffer
+ (const PoWHashCashResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &po_whash_cash_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+PoWHashCashResponse *
+ po_whash_cash_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (PoWHashCashResponse *)
+ protobuf_c_message_unpack (&po_whash_cash_response__descriptor,
+ allocator, len, data);
+}
+void po_whash_cash_response__free_unpacked
+ (PoWHashCashResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &po_whash_cash_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_response_union__init
+ (CryptoResponseUnion *message)
+{
+ static const CryptoResponseUnion init_value = CRYPTO_RESPONSE_UNION__INIT;
+ *message = init_value;
+}
+size_t crypto_response_union__get_packed_size
+ (const CryptoResponseUnion *message)
+{
+ assert(message->base.descriptor == &crypto_response_union__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_response_union__pack
+ (const CryptoResponseUnion *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_response_union__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_response_union__pack_to_buffer
+ (const CryptoResponseUnion *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_response_union__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoResponseUnion *
+ crypto_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoResponseUnion *)
+ protobuf_c_message_unpack (&crypto_response_union__descriptor,
+ allocator, len, data);
+}
+void crypto_response_union__free_unpacked
+ (CryptoResponseUnion *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_response_union__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_shannon_response__init
+ (CryptoShannonResponse *message)
+{
+ static const CryptoShannonResponse init_value = CRYPTO_SHANNON_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t crypto_shannon_response__get_packed_size
+ (const CryptoShannonResponse *message)
+{
+ assert(message->base.descriptor == &crypto_shannon_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_shannon_response__pack
+ (const CryptoShannonResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_shannon_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_shannon_response__pack_to_buffer
+ (const CryptoShannonResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_shannon_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoShannonResponse *
+ crypto_shannon_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoShannonResponse *)
+ protobuf_c_message_unpack (&crypto_shannon_response__descriptor,
+ allocator, len, data);
+}
+void crypto_shannon_response__free_unpacked
+ (CryptoShannonResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_shannon_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void crypto_rc4_sha1_hmac_response__init
+ (CryptoRc4Sha1HmacResponse *message)
+{
+ static const CryptoRc4Sha1HmacResponse init_value = CRYPTO_RC4_SHA1_HMAC_RESPONSE__INIT;
+ *message = init_value;
+}
+size_t crypto_rc4_sha1_hmac_response__get_packed_size
+ (const CryptoRc4Sha1HmacResponse *message)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_response__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t crypto_rc4_sha1_hmac_response__pack
+ (const CryptoRc4Sha1HmacResponse *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_response__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t crypto_rc4_sha1_hmac_response__pack_to_buffer
+ (const CryptoRc4Sha1HmacResponse *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_response__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+CryptoRc4Sha1HmacResponse *
+ crypto_rc4_sha1_hmac_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (CryptoRc4Sha1HmacResponse *)
+ protobuf_c_message_unpack (&crypto_rc4_sha1_hmac_response__descriptor,
+ allocator, len, data);
+}
+void crypto_rc4_sha1_hmac_response__free_unpacked
+ (CryptoRc4Sha1HmacResponse *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &crypto_rc4_sha1_hmac_response__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor client_hello__field_descriptors[8] =
+{
+ {
+ "build_info",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientHello, build_info),
+ &build_info__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "fingerprints_supported",
+ 20,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(ClientHello, n_fingerprints_supported),
+ offsetof(ClientHello, fingerprints_supported),
+ &fingerprint__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cryptosuites_supported",
+ 30,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(ClientHello, n_cryptosuites_supported),
+ offsetof(ClientHello, cryptosuites_supported),
+ &cryptosuite__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "powschemes_supported",
+ 40,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(ClientHello, n_powschemes_supported),
+ offsetof(ClientHello, powschemes_supported),
+ &powscheme__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "login_crypto_hello",
+ 50,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientHello, login_crypto_hello),
+ &login_crypto_hello_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "client_nonce",
+ 60,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(ClientHello, client_nonce),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "padding",
+ 70,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(ClientHello, has_padding),
+ offsetof(ClientHello, padding),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "feature_set",
+ 80,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientHello, feature_set),
+ &feature_set__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned client_hello__field_indices_by_name[] = {
+ 0, /* field[0] = build_info */
+ 5, /* field[5] = client_nonce */
+ 2, /* field[2] = cryptosuites_supported */
+ 7, /* field[7] = feature_set */
+ 1, /* field[1] = fingerprints_supported */
+ 4, /* field[4] = login_crypto_hello */
+ 6, /* field[6] = padding */
+ 3, /* field[3] = powschemes_supported */
+};
+static const ProtobufCIntRange client_hello__number_ranges[8 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 50, 4 },
+ { 60, 5 },
+ { 70, 6 },
+ { 80, 7 },
+ { 0, 8 }
+};
+const ProtobufCMessageDescriptor client_hello__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ClientHello",
+ "ClientHello",
+ "ClientHello",
+ "",
+ sizeof(ClientHello),
+ 8,
+ client_hello__field_descriptors,
+ client_hello__field_indices_by_name,
+ 8, client_hello__number_ranges,
+ (ProtobufCMessageInit) client_hello__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor build_info__field_descriptors[4] =
+{
+ {
+ "product",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(BuildInfo, product),
+ &product__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "product_flags",
+ 20,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(BuildInfo, n_product_flags),
+ offsetof(BuildInfo, product_flags),
+ &product_flags__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "platform",
+ 30,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(BuildInfo, platform),
+ &platform__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "version",
+ 40,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_UINT64,
+ 0, /* quantifier_offset */
+ offsetof(BuildInfo, version),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned build_info__field_indices_by_name[] = {
+ 2, /* field[2] = platform */
+ 0, /* field[0] = product */
+ 1, /* field[1] = product_flags */
+ 3, /* field[3] = version */
+};
+static const ProtobufCIntRange build_info__number_ranges[4 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor build_info__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "BuildInfo",
+ "BuildInfo",
+ "BuildInfo",
+ "",
+ sizeof(BuildInfo),
+ 4,
+ build_info__field_descriptors,
+ build_info__field_indices_by_name,
+ 4, build_info__number_ranges,
+ (ProtobufCMessageInit) build_info__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_hello_union__field_descriptors[1] =
+{
+ {
+ "diffie_hellman",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoHelloUnion, diffie_hellman),
+ &login_crypto_diffie_hellman_hello__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_hello_union__field_indices_by_name[] = {
+ 0, /* field[0] = diffie_hellman */
+};
+static const ProtobufCIntRange login_crypto_hello_union__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor login_crypto_hello_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoHelloUnion",
+ "LoginCryptoHelloUnion",
+ "LoginCryptoHelloUnion",
+ "",
+ sizeof(LoginCryptoHelloUnion),
+ 1,
+ login_crypto_hello_union__field_descriptors,
+ login_crypto_hello_union__field_indices_by_name,
+ 1, login_crypto_hello_union__number_ranges,
+ (ProtobufCMessageInit) login_crypto_hello_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_diffie_hellman_hello__field_descriptors[2] =
+{
+ {
+ "gc",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanHello, gc),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "server_keys_known",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_UINT32,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanHello, server_keys_known),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_diffie_hellman_hello__field_indices_by_name[] = {
+ 0, /* field[0] = gc */
+ 1, /* field[1] = server_keys_known */
+};
+static const ProtobufCIntRange login_crypto_diffie_hellman_hello__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor login_crypto_diffie_hellman_hello__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoDiffieHellmanHello",
+ "LoginCryptoDiffieHellmanHello",
+ "LoginCryptoDiffieHellmanHello",
+ "",
+ sizeof(LoginCryptoDiffieHellmanHello),
+ 2,
+ login_crypto_diffie_hellman_hello__field_descriptors,
+ login_crypto_diffie_hellman_hello__field_indices_by_name,
+ 2, login_crypto_diffie_hellman_hello__number_ranges,
+ (ProtobufCMessageInit) login_crypto_diffie_hellman_hello__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor feature_set__field_descriptors[2] =
+{
+ {
+ "autoupdate2",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(FeatureSet, has_autoupdate2),
+ offsetof(FeatureSet, autoupdate2),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "current_location",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(FeatureSet, has_current_location),
+ offsetof(FeatureSet, current_location),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned feature_set__field_indices_by_name[] = {
+ 0, /* field[0] = autoupdate2 */
+ 1, /* field[1] = current_location */
+};
+static const ProtobufCIntRange feature_set__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor feature_set__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FeatureSet",
+ "FeatureSet",
+ "FeatureSet",
+ "",
+ sizeof(FeatureSet),
+ 2,
+ feature_set__field_descriptors,
+ feature_set__field_indices_by_name,
+ 1, feature_set__number_ranges,
+ (ProtobufCMessageInit) feature_set__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor apresponse_message__field_descriptors[3] =
+{
+ {
+ "challenge",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APResponseMessage, challenge),
+ &apchallenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "upgrade",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APResponseMessage, upgrade),
+ &upgrade_required_message__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "login_failed",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APResponseMessage, login_failed),
+ &aplogin_failed__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned apresponse_message__field_indices_by_name[] = {
+ 0, /* field[0] = challenge */
+ 2, /* field[2] = login_failed */
+ 1, /* field[1] = upgrade */
+};
+static const ProtobufCIntRange apresponse_message__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor apresponse_message__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "APResponseMessage",
+ "APResponseMessage",
+ "APResponseMessage",
+ "",
+ sizeof(APResponseMessage),
+ 3,
+ apresponse_message__field_descriptors,
+ apresponse_message__field_indices_by_name,
+ 3, apresponse_message__number_ranges,
+ (ProtobufCMessageInit) apresponse_message__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor apchallenge__field_descriptors[6] =
+{
+ {
+ "login_crypto_challenge",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APChallenge, login_crypto_challenge),
+ &login_crypto_challenge_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "fingerprint_challenge",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APChallenge, fingerprint_challenge),
+ &fingerprint_challenge_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "pow_challenge",
+ 30,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APChallenge, pow_challenge),
+ &po_wchallenge_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "crypto_challenge",
+ 40,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(APChallenge, crypto_challenge),
+ &crypto_challenge_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "server_nonce",
+ 50,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(APChallenge, server_nonce),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "padding",
+ 60,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(APChallenge, has_padding),
+ offsetof(APChallenge, padding),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned apchallenge__field_indices_by_name[] = {
+ 3, /* field[3] = crypto_challenge */
+ 1, /* field[1] = fingerprint_challenge */
+ 0, /* field[0] = login_crypto_challenge */
+ 5, /* field[5] = padding */
+ 2, /* field[2] = pow_challenge */
+ 4, /* field[4] = server_nonce */
+};
+static const ProtobufCIntRange apchallenge__number_ranges[6 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 50, 4 },
+ { 60, 5 },
+ { 0, 6 }
+};
+const ProtobufCMessageDescriptor apchallenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "APChallenge",
+ "APChallenge",
+ "APChallenge",
+ "",
+ sizeof(APChallenge),
+ 6,
+ apchallenge__field_descriptors,
+ apchallenge__field_indices_by_name,
+ 6, apchallenge__number_ranges,
+ (ProtobufCMessageInit) apchallenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_challenge_union__field_descriptors[1] =
+{
+ {
+ "diffie_hellman",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoChallengeUnion, diffie_hellman),
+ &login_crypto_diffie_hellman_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_challenge_union__field_indices_by_name[] = {
+ 0, /* field[0] = diffie_hellman */
+};
+static const ProtobufCIntRange login_crypto_challenge_union__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor login_crypto_challenge_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoChallengeUnion",
+ "LoginCryptoChallengeUnion",
+ "LoginCryptoChallengeUnion",
+ "",
+ sizeof(LoginCryptoChallengeUnion),
+ 1,
+ login_crypto_challenge_union__field_descriptors,
+ login_crypto_challenge_union__field_indices_by_name,
+ 1, login_crypto_challenge_union__number_ranges,
+ (ProtobufCMessageInit) login_crypto_challenge_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_diffie_hellman_challenge__field_descriptors[3] =
+{
+ {
+ "gs",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanChallenge, gs),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "server_signature_key",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_INT32,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanChallenge, server_signature_key),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "gs_signature",
+ 30,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanChallenge, gs_signature),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_diffie_hellman_challenge__field_indices_by_name[] = {
+ 0, /* field[0] = gs */
+ 2, /* field[2] = gs_signature */
+ 1, /* field[1] = server_signature_key */
+};
+static const ProtobufCIntRange login_crypto_diffie_hellman_challenge__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor login_crypto_diffie_hellman_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoDiffieHellmanChallenge",
+ "LoginCryptoDiffieHellmanChallenge",
+ "LoginCryptoDiffieHellmanChallenge",
+ "",
+ sizeof(LoginCryptoDiffieHellmanChallenge),
+ 3,
+ login_crypto_diffie_hellman_challenge__field_descriptors,
+ login_crypto_diffie_hellman_challenge__field_indices_by_name,
+ 3, login_crypto_diffie_hellman_challenge__number_ranges,
+ (ProtobufCMessageInit) login_crypto_diffie_hellman_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_challenge_union__field_descriptors[2] =
+{
+ {
+ "grain",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintChallengeUnion, grain),
+ &fingerprint_grain_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "hmac_ripemd",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintChallengeUnion, hmac_ripemd),
+ &fingerprint_hmac_ripemd_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_challenge_union__field_indices_by_name[] = {
+ 0, /* field[0] = grain */
+ 1, /* field[1] = hmac_ripemd */
+};
+static const ProtobufCIntRange fingerprint_challenge_union__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor fingerprint_challenge_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintChallengeUnion",
+ "FingerprintChallengeUnion",
+ "FingerprintChallengeUnion",
+ "",
+ sizeof(FingerprintChallengeUnion),
+ 2,
+ fingerprint_challenge_union__field_descriptors,
+ fingerprint_challenge_union__field_indices_by_name,
+ 2, fingerprint_challenge_union__number_ranges,
+ (ProtobufCMessageInit) fingerprint_challenge_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_grain_challenge__field_descriptors[1] =
+{
+ {
+ "kek",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintGrainChallenge, kek),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_grain_challenge__field_indices_by_name[] = {
+ 0, /* field[0] = kek */
+};
+static const ProtobufCIntRange fingerprint_grain_challenge__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor fingerprint_grain_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintGrainChallenge",
+ "FingerprintGrainChallenge",
+ "FingerprintGrainChallenge",
+ "",
+ sizeof(FingerprintGrainChallenge),
+ 1,
+ fingerprint_grain_challenge__field_descriptors,
+ fingerprint_grain_challenge__field_indices_by_name,
+ 1, fingerprint_grain_challenge__number_ranges,
+ (ProtobufCMessageInit) fingerprint_grain_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor fingerprint_hmac_ripemd_challenge__field_descriptors[1] =
+{
+ {
+ "challenge",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(FingerprintHmacRipemdChallenge, challenge),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned fingerprint_hmac_ripemd_challenge__field_indices_by_name[] = {
+ 0, /* field[0] = challenge */
+};
+static const ProtobufCIntRange fingerprint_hmac_ripemd_challenge__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor fingerprint_hmac_ripemd_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "FingerprintHmacRipemdChallenge",
+ "FingerprintHmacRipemdChallenge",
+ "FingerprintHmacRipemdChallenge",
+ "",
+ sizeof(FingerprintHmacRipemdChallenge),
+ 1,
+ fingerprint_hmac_ripemd_challenge__field_descriptors,
+ fingerprint_hmac_ripemd_challenge__field_indices_by_name,
+ 1, fingerprint_hmac_ripemd_challenge__number_ranges,
+ (ProtobufCMessageInit) fingerprint_hmac_ripemd_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor po_wchallenge_union__field_descriptors[1] =
+{
+ {
+ "hash_cash",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(PoWChallengeUnion, hash_cash),
+ &po_whash_cash_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned po_wchallenge_union__field_indices_by_name[] = {
+ 0, /* field[0] = hash_cash */
+};
+static const ProtobufCIntRange po_wchallenge_union__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor po_wchallenge_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PoWChallengeUnion",
+ "PoWChallengeUnion",
+ "PoWChallengeUnion",
+ "",
+ sizeof(PoWChallengeUnion),
+ 1,
+ po_wchallenge_union__field_descriptors,
+ po_wchallenge_union__field_indices_by_name,
+ 1, po_wchallenge_union__number_ranges,
+ (ProtobufCMessageInit) po_wchallenge_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor po_whash_cash_challenge__field_descriptors[3] =
+{
+ {
+ "prefix",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(PoWHashCashChallenge, has_prefix),
+ offsetof(PoWHashCashChallenge, prefix),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "length",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(PoWHashCashChallenge, has_length),
+ offsetof(PoWHashCashChallenge, length),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "target",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(PoWHashCashChallenge, has_target),
+ offsetof(PoWHashCashChallenge, target),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned po_whash_cash_challenge__field_indices_by_name[] = {
+ 1, /* field[1] = length */
+ 0, /* field[0] = prefix */
+ 2, /* field[2] = target */
+};
+static const ProtobufCIntRange po_whash_cash_challenge__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor po_whash_cash_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PoWHashCashChallenge",
+ "PoWHashCashChallenge",
+ "PoWHashCashChallenge",
+ "",
+ sizeof(PoWHashCashChallenge),
+ 3,
+ po_whash_cash_challenge__field_descriptors,
+ po_whash_cash_challenge__field_indices_by_name,
+ 3, po_whash_cash_challenge__number_ranges,
+ (ProtobufCMessageInit) po_whash_cash_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor crypto_challenge_union__field_descriptors[2] =
+{
+ {
+ "shannon",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(CryptoChallengeUnion, shannon),
+ &crypto_shannon_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "rc4_sha1_hmac",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(CryptoChallengeUnion, rc4_sha1_hmac),
+ &crypto_rc4_sha1_hmac_challenge__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned crypto_challenge_union__field_indices_by_name[] = {
+ 1, /* field[1] = rc4_sha1_hmac */
+ 0, /* field[0] = shannon */
+};
+static const ProtobufCIntRange crypto_challenge_union__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor crypto_challenge_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoChallengeUnion",
+ "CryptoChallengeUnion",
+ "CryptoChallengeUnion",
+ "",
+ sizeof(CryptoChallengeUnion),
+ 2,
+ crypto_challenge_union__field_descriptors,
+ crypto_challenge_union__field_indices_by_name,
+ 2, crypto_challenge_union__number_ranges,
+ (ProtobufCMessageInit) crypto_challenge_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+#define crypto_shannon_challenge__field_descriptors NULL
+#define crypto_shannon_challenge__field_indices_by_name NULL
+#define crypto_shannon_challenge__number_ranges NULL
+const ProtobufCMessageDescriptor crypto_shannon_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoShannonChallenge",
+ "CryptoShannonChallenge",
+ "CryptoShannonChallenge",
+ "",
+ sizeof(CryptoShannonChallenge),
+ 0,
+ crypto_shannon_challenge__field_descriptors,
+ crypto_shannon_challenge__field_indices_by_name,
+ 0, crypto_shannon_challenge__number_ranges,
+ (ProtobufCMessageInit) crypto_shannon_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+#define crypto_rc4_sha1_hmac_challenge__field_descriptors NULL
+#define crypto_rc4_sha1_hmac_challenge__field_indices_by_name NULL
+#define crypto_rc4_sha1_hmac_challenge__number_ranges NULL
+const ProtobufCMessageDescriptor crypto_rc4_sha1_hmac_challenge__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoRc4Sha1HmacChallenge",
+ "CryptoRc4Sha1HmacChallenge",
+ "CryptoRc4Sha1HmacChallenge",
+ "",
+ sizeof(CryptoRc4Sha1HmacChallenge),
+ 0,
+ crypto_rc4_sha1_hmac_challenge__field_descriptors,
+ crypto_rc4_sha1_hmac_challenge__field_indices_by_name,
+ 0, crypto_rc4_sha1_hmac_challenge__number_ranges,
+ (ProtobufCMessageInit) crypto_rc4_sha1_hmac_challenge__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor upgrade_required_message__field_descriptors[3] =
+{
+ {
+ "upgrade_signed_part",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(UpgradeRequiredMessage, upgrade_signed_part),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "signature",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(UpgradeRequiredMessage, signature),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "http_suffix",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(UpgradeRequiredMessage, http_suffix),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned upgrade_required_message__field_indices_by_name[] = {
+ 2, /* field[2] = http_suffix */
+ 1, /* field[1] = signature */
+ 0, /* field[0] = upgrade_signed_part */
+};
+static const ProtobufCIntRange upgrade_required_message__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor upgrade_required_message__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "UpgradeRequiredMessage",
+ "UpgradeRequiredMessage",
+ "UpgradeRequiredMessage",
+ "",
+ sizeof(UpgradeRequiredMessage),
+ 3,
+ upgrade_required_message__field_descriptors,
+ upgrade_required_message__field_indices_by_name,
+ 3, upgrade_required_message__number_ranges,
+ (ProtobufCMessageInit) upgrade_required_message__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor aplogin_failed__field_descriptors[4] =
+{
+ {
+ "error_code",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_ENUM,
+ 0, /* quantifier_offset */
+ offsetof(APLoginFailed, error_code),
+ &error_code__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "retry_delay",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(APLoginFailed, has_retry_delay),
+ offsetof(APLoginFailed, retry_delay),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "expiry",
+ 30,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(APLoginFailed, has_expiry),
+ offsetof(APLoginFailed, expiry),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "error_description",
+ 40,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(APLoginFailed, error_description),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned aplogin_failed__field_indices_by_name[] = {
+ 0, /* field[0] = error_code */
+ 3, /* field[3] = error_description */
+ 2, /* field[2] = expiry */
+ 1, /* field[1] = retry_delay */
+};
+static const ProtobufCIntRange aplogin_failed__number_ranges[4 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 40, 3 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor aplogin_failed__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "APLoginFailed",
+ "APLoginFailed",
+ "APLoginFailed",
+ "",
+ sizeof(APLoginFailed),
+ 4,
+ aplogin_failed__field_descriptors,
+ aplogin_failed__field_indices_by_name,
+ 4, aplogin_failed__number_ranges,
+ (ProtobufCMessageInit) aplogin_failed__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor client_response_plaintext__field_descriptors[3] =
+{
+ {
+ "login_crypto_response",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponsePlaintext, login_crypto_response),
+ &login_crypto_response_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "pow_response",
+ 20,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponsePlaintext, pow_response),
+ &po_wresponse_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "crypto_response",
+ 30,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(ClientResponsePlaintext, crypto_response),
+ &crypto_response_union__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned client_response_plaintext__field_indices_by_name[] = {
+ 2, /* field[2] = crypto_response */
+ 0, /* field[0] = login_crypto_response */
+ 1, /* field[1] = pow_response */
+};
+static const ProtobufCIntRange client_response_plaintext__number_ranges[3 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 30, 2 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor client_response_plaintext__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ClientResponsePlaintext",
+ "ClientResponsePlaintext",
+ "ClientResponsePlaintext",
+ "",
+ sizeof(ClientResponsePlaintext),
+ 3,
+ client_response_plaintext__field_descriptors,
+ client_response_plaintext__field_indices_by_name,
+ 3, client_response_plaintext__number_ranges,
+ (ProtobufCMessageInit) client_response_plaintext__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_response_union__field_descriptors[1] =
+{
+ {
+ "diffie_hellman",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoResponseUnion, diffie_hellman),
+ &login_crypto_diffie_hellman_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_response_union__field_indices_by_name[] = {
+ 0, /* field[0] = diffie_hellman */
+};
+static const ProtobufCIntRange login_crypto_response_union__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor login_crypto_response_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoResponseUnion",
+ "LoginCryptoResponseUnion",
+ "LoginCryptoResponseUnion",
+ "",
+ sizeof(LoginCryptoResponseUnion),
+ 1,
+ login_crypto_response_union__field_descriptors,
+ login_crypto_response_union__field_indices_by_name,
+ 1, login_crypto_response_union__number_ranges,
+ (ProtobufCMessageInit) login_crypto_response_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor login_crypto_diffie_hellman_response__field_descriptors[1] =
+{
+ {
+ "hmac",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(LoginCryptoDiffieHellmanResponse, hmac),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned login_crypto_diffie_hellman_response__field_indices_by_name[] = {
+ 0, /* field[0] = hmac */
+};
+static const ProtobufCIntRange login_crypto_diffie_hellman_response__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor login_crypto_diffie_hellman_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "LoginCryptoDiffieHellmanResponse",
+ "LoginCryptoDiffieHellmanResponse",
+ "LoginCryptoDiffieHellmanResponse",
+ "",
+ sizeof(LoginCryptoDiffieHellmanResponse),
+ 1,
+ login_crypto_diffie_hellman_response__field_descriptors,
+ login_crypto_diffie_hellman_response__field_indices_by_name,
+ 1, login_crypto_diffie_hellman_response__number_ranges,
+ (ProtobufCMessageInit) login_crypto_diffie_hellman_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor po_wresponse_union__field_descriptors[1] =
+{
+ {
+ "hash_cash",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(PoWResponseUnion, hash_cash),
+ &po_whash_cash_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned po_wresponse_union__field_indices_by_name[] = {
+ 0, /* field[0] = hash_cash */
+};
+static const ProtobufCIntRange po_wresponse_union__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor po_wresponse_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PoWResponseUnion",
+ "PoWResponseUnion",
+ "PoWResponseUnion",
+ "",
+ sizeof(PoWResponseUnion),
+ 1,
+ po_wresponse_union__field_descriptors,
+ po_wresponse_union__field_indices_by_name,
+ 1, po_wresponse_union__number_ranges,
+ (ProtobufCMessageInit) po_wresponse_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor po_whash_cash_response__field_descriptors[1] =
+{
+ {
+ "hash_suffix",
+ 10,
+ PROTOBUF_C_LABEL_REQUIRED,
+ PROTOBUF_C_TYPE_BYTES,
+ 0, /* quantifier_offset */
+ offsetof(PoWHashCashResponse, hash_suffix),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned po_whash_cash_response__field_indices_by_name[] = {
+ 0, /* field[0] = hash_suffix */
+};
+static const ProtobufCIntRange po_whash_cash_response__number_ranges[1 + 1] =
+{
+ { 10, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor po_whash_cash_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "PoWHashCashResponse",
+ "PoWHashCashResponse",
+ "PoWHashCashResponse",
+ "",
+ sizeof(PoWHashCashResponse),
+ 1,
+ po_whash_cash_response__field_descriptors,
+ po_whash_cash_response__field_indices_by_name,
+ 1, po_whash_cash_response__number_ranges,
+ (ProtobufCMessageInit) po_whash_cash_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor crypto_response_union__field_descriptors[2] =
+{
+ {
+ "shannon",
+ 10,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(CryptoResponseUnion, shannon),
+ &crypto_shannon_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "rc4_sha1_hmac",
+ 20,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(CryptoResponseUnion, rc4_sha1_hmac),
+ &crypto_rc4_sha1_hmac_response__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned crypto_response_union__field_indices_by_name[] = {
+ 1, /* field[1] = rc4_sha1_hmac */
+ 0, /* field[0] = shannon */
+};
+static const ProtobufCIntRange crypto_response_union__number_ranges[2 + 1] =
+{
+ { 10, 0 },
+ { 20, 1 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor crypto_response_union__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoResponseUnion",
+ "CryptoResponseUnion",
+ "CryptoResponseUnion",
+ "",
+ sizeof(CryptoResponseUnion),
+ 2,
+ crypto_response_union__field_descriptors,
+ crypto_response_union__field_indices_by_name,
+ 2, crypto_response_union__number_ranges,
+ (ProtobufCMessageInit) crypto_response_union__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor crypto_shannon_response__field_descriptors[1] =
+{
+ {
+ "dummy",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(CryptoShannonResponse, has_dummy),
+ offsetof(CryptoShannonResponse, dummy),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned crypto_shannon_response__field_indices_by_name[] = {
+ 0, /* field[0] = dummy */
+};
+static const ProtobufCIntRange crypto_shannon_response__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor crypto_shannon_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoShannonResponse",
+ "CryptoShannonResponse",
+ "CryptoShannonResponse",
+ "",
+ sizeof(CryptoShannonResponse),
+ 1,
+ crypto_shannon_response__field_descriptors,
+ crypto_shannon_response__field_indices_by_name,
+ 1, crypto_shannon_response__number_ranges,
+ (ProtobufCMessageInit) crypto_shannon_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor crypto_rc4_sha1_hmac_response__field_descriptors[1] =
+{
+ {
+ "dummy",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_INT32,
+ offsetof(CryptoRc4Sha1HmacResponse, has_dummy),
+ offsetof(CryptoRc4Sha1HmacResponse, dummy),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned crypto_rc4_sha1_hmac_response__field_indices_by_name[] = {
+ 0, /* field[0] = dummy */
+};
+static const ProtobufCIntRange crypto_rc4_sha1_hmac_response__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor crypto_rc4_sha1_hmac_response__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "CryptoRc4Sha1HmacResponse",
+ "CryptoRc4Sha1HmacResponse",
+ "CryptoRc4Sha1HmacResponse",
+ "",
+ sizeof(CryptoRc4Sha1HmacResponse),
+ 1,
+ crypto_rc4_sha1_hmac_response__field_descriptors,
+ crypto_rc4_sha1_hmac_response__field_indices_by_name,
+ 1, crypto_rc4_sha1_hmac_response__number_ranges,
+ (ProtobufCMessageInit) crypto_rc4_sha1_hmac_response__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue product__enum_values_by_number[5] =
+{
+ { "PRODUCT_CLIENT", "PRODUCT__PRODUCT_CLIENT", 0 },
+ { "PRODUCT_LIBSPOTIFY", "PRODUCT__PRODUCT_LIBSPOTIFY", 1 },
+ { "PRODUCT_MOBILE", "PRODUCT__PRODUCT_MOBILE", 2 },
+ { "PRODUCT_PARTNER", "PRODUCT__PRODUCT_PARTNER", 3 },
+ { "PRODUCT_LIBSPOTIFY_EMBEDDED", "PRODUCT__PRODUCT_LIBSPOTIFY_EMBEDDED", 5 },
+};
+static const ProtobufCIntRange product__value_ranges[] = {
+{0, 0},{5, 4},{0, 5}
+};
+static const ProtobufCEnumValueIndex product__enum_values_by_name[5] =
+{
+ { "PRODUCT_CLIENT", 0 },
+ { "PRODUCT_LIBSPOTIFY", 1 },
+ { "PRODUCT_LIBSPOTIFY_EMBEDDED", 4 },
+ { "PRODUCT_MOBILE", 2 },
+ { "PRODUCT_PARTNER", 3 },
+};
+const ProtobufCEnumDescriptor product__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Product",
+ "Product",
+ "Product",
+ "",
+ 5,
+ product__enum_values_by_number,
+ 5,
+ product__enum_values_by_name,
+ 2,
+ product__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue product_flags__enum_values_by_number[2] =
+{
+ { "PRODUCT_FLAG_NONE", "PRODUCT_FLAGS__PRODUCT_FLAG_NONE", 0 },
+ { "PRODUCT_FLAG_DEV_BUILD", "PRODUCT_FLAGS__PRODUCT_FLAG_DEV_BUILD", 1 },
+};
+static const ProtobufCIntRange product_flags__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex product_flags__enum_values_by_name[2] =
+{
+ { "PRODUCT_FLAG_DEV_BUILD", 1 },
+ { "PRODUCT_FLAG_NONE", 0 },
+};
+const ProtobufCEnumDescriptor product_flags__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "ProductFlags",
+ "ProductFlags",
+ "ProductFlags",
+ "",
+ 2,
+ product_flags__enum_values_by_number,
+ 2,
+ product_flags__enum_values_by_name,
+ 1,
+ product_flags__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue platform__enum_values_by_number[24] =
+{
+ { "PLATFORM_WIN32_X86", "PLATFORM__PLATFORM_WIN32_X86", 0 },
+ { "PLATFORM_OSX_X86", "PLATFORM__PLATFORM_OSX_X86", 1 },
+ { "PLATFORM_LINUX_X86", "PLATFORM__PLATFORM_LINUX_X86", 2 },
+ { "PLATFORM_IPHONE_ARM", "PLATFORM__PLATFORM_IPHONE_ARM", 3 },
+ { "PLATFORM_S60_ARM", "PLATFORM__PLATFORM_S60_ARM", 4 },
+ { "PLATFORM_OSX_PPC", "PLATFORM__PLATFORM_OSX_PPC", 5 },
+ { "PLATFORM_ANDROID_ARM", "PLATFORM__PLATFORM_ANDROID_ARM", 6 },
+ { "PLATFORM_WINDOWS_CE_ARM", "PLATFORM__PLATFORM_WINDOWS_CE_ARM", 7 },
+ { "PLATFORM_LINUX_X86_64", "PLATFORM__PLATFORM_LINUX_X86_64", 8 },
+ { "PLATFORM_OSX_X86_64", "PLATFORM__PLATFORM_OSX_X86_64", 9 },
+ { "PLATFORM_PALM_ARM", "PLATFORM__PLATFORM_PALM_ARM", 10 },
+ { "PLATFORM_LINUX_SH", "PLATFORM__PLATFORM_LINUX_SH", 11 },
+ { "PLATFORM_FREEBSD_X86", "PLATFORM__PLATFORM_FREEBSD_X86", 12 },
+ { "PLATFORM_FREEBSD_X86_64", "PLATFORM__PLATFORM_FREEBSD_X86_64", 13 },
+ { "PLATFORM_BLACKBERRY_ARM", "PLATFORM__PLATFORM_BLACKBERRY_ARM", 14 },
+ { "PLATFORM_SONOS", "PLATFORM__PLATFORM_SONOS", 15 },
+ { "PLATFORM_LINUX_MIPS", "PLATFORM__PLATFORM_LINUX_MIPS", 16 },
+ { "PLATFORM_LINUX_ARM", "PLATFORM__PLATFORM_LINUX_ARM", 17 },
+ { "PLATFORM_LOGITECH_ARM", "PLATFORM__PLATFORM_LOGITECH_ARM", 18 },
+ { "PLATFORM_LINUX_BLACKFIN", "PLATFORM__PLATFORM_LINUX_BLACKFIN", 19 },
+ { "PLATFORM_WP7_ARM", "PLATFORM__PLATFORM_WP7_ARM", 20 },
+ { "PLATFORM_ONKYO_ARM", "PLATFORM__PLATFORM_ONKYO_ARM", 21 },
+ { "PLATFORM_QNXNTO_ARM", "PLATFORM__PLATFORM_QNXNTO_ARM", 22 },
+ { "PLATFORM_BCO_ARM", "PLATFORM__PLATFORM_BCO_ARM", 23 },
+};
+static const ProtobufCIntRange platform__value_ranges[] = {
+{0, 0},{0, 24}
+};
+static const ProtobufCEnumValueIndex platform__enum_values_by_name[24] =
+{
+ { "PLATFORM_ANDROID_ARM", 6 },
+ { "PLATFORM_BCO_ARM", 23 },
+ { "PLATFORM_BLACKBERRY_ARM", 14 },
+ { "PLATFORM_FREEBSD_X86", 12 },
+ { "PLATFORM_FREEBSD_X86_64", 13 },
+ { "PLATFORM_IPHONE_ARM", 3 },
+ { "PLATFORM_LINUX_ARM", 17 },
+ { "PLATFORM_LINUX_BLACKFIN", 19 },
+ { "PLATFORM_LINUX_MIPS", 16 },
+ { "PLATFORM_LINUX_SH", 11 },
+ { "PLATFORM_LINUX_X86", 2 },
+ { "PLATFORM_LINUX_X86_64", 8 },
+ { "PLATFORM_LOGITECH_ARM", 18 },
+ { "PLATFORM_ONKYO_ARM", 21 },
+ { "PLATFORM_OSX_PPC", 5 },
+ { "PLATFORM_OSX_X86", 1 },
+ { "PLATFORM_OSX_X86_64", 9 },
+ { "PLATFORM_PALM_ARM", 10 },
+ { "PLATFORM_QNXNTO_ARM", 22 },
+ { "PLATFORM_S60_ARM", 4 },
+ { "PLATFORM_SONOS", 15 },
+ { "PLATFORM_WIN32_X86", 0 },
+ { "PLATFORM_WINDOWS_CE_ARM", 7 },
+ { "PLATFORM_WP7_ARM", 20 },
+};
+const ProtobufCEnumDescriptor platform__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Platform",
+ "Platform",
+ "Platform",
+ "",
+ 24,
+ platform__enum_values_by_number,
+ 24,
+ platform__enum_values_by_name,
+ 1,
+ platform__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue fingerprint__enum_values_by_number[2] =
+{
+ { "FINGERPRINT_GRAIN", "FINGERPRINT__FINGERPRINT_GRAIN", 0 },
+ { "FINGERPRINT_HMAC_RIPEMD", "FINGERPRINT__FINGERPRINT_HMAC_RIPEMD", 1 },
+};
+static const ProtobufCIntRange fingerprint__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex fingerprint__enum_values_by_name[2] =
+{
+ { "FINGERPRINT_GRAIN", 0 },
+ { "FINGERPRINT_HMAC_RIPEMD", 1 },
+};
+const ProtobufCEnumDescriptor fingerprint__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Fingerprint",
+ "Fingerprint",
+ "Fingerprint",
+ "",
+ 2,
+ fingerprint__enum_values_by_number,
+ 2,
+ fingerprint__enum_values_by_name,
+ 1,
+ fingerprint__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue cryptosuite__enum_values_by_number[2] =
+{
+ { "CRYPTO_SUITE_SHANNON", "CRYPTOSUITE__CRYPTO_SUITE_SHANNON", 0 },
+ { "CRYPTO_SUITE_RC4_SHA1_HMAC", "CRYPTOSUITE__CRYPTO_SUITE_RC4_SHA1_HMAC", 1 },
+};
+static const ProtobufCIntRange cryptosuite__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex cryptosuite__enum_values_by_name[2] =
+{
+ { "CRYPTO_SUITE_RC4_SHA1_HMAC", 1 },
+ { "CRYPTO_SUITE_SHANNON", 0 },
+};
+const ProtobufCEnumDescriptor cryptosuite__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Cryptosuite",
+ "Cryptosuite",
+ "Cryptosuite",
+ "",
+ 2,
+ cryptosuite__enum_values_by_number,
+ 2,
+ cryptosuite__enum_values_by_name,
+ 1,
+ cryptosuite__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue powscheme__enum_values_by_number[1] =
+{
+ { "POW_HASH_CASH", "POWSCHEME__POW_HASH_CASH", 0 },
+};
+static const ProtobufCIntRange powscheme__value_ranges[] = {
+{0, 0},{0, 1}
+};
+static const ProtobufCEnumValueIndex powscheme__enum_values_by_name[1] =
+{
+ { "POW_HASH_CASH", 0 },
+};
+const ProtobufCEnumDescriptor powscheme__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Powscheme",
+ "Powscheme",
+ "Powscheme",
+ "",
+ 1,
+ powscheme__enum_values_by_number,
+ 1,
+ powscheme__enum_values_by_name,
+ 1,
+ powscheme__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue error_code__enum_values_by_number[11] =
+{
+ { "ProtocolError", "ERROR_CODE__ProtocolError", 0 },
+ { "TryAnotherAP", "ERROR_CODE__TryAnotherAP", 2 },
+ { "BadConnectionId", "ERROR_CODE__BadConnectionId", 5 },
+ { "TravelRestriction", "ERROR_CODE__TravelRestriction", 9 },
+ { "PremiumAccountRequired", "ERROR_CODE__PremiumAccountRequired", 11 },
+ { "BadCredentials", "ERROR_CODE__BadCredentials", 12 },
+ { "CouldNotValidateCredentials", "ERROR_CODE__CouldNotValidateCredentials", 13 },
+ { "AccountExists", "ERROR_CODE__AccountExists", 14 },
+ { "ExtraVerificationRequired", "ERROR_CODE__ExtraVerificationRequired", 15 },
+ { "InvalidAppKey", "ERROR_CODE__InvalidAppKey", 16 },
+ { "ApplicationBanned", "ERROR_CODE__ApplicationBanned", 17 },
+};
+static const ProtobufCIntRange error_code__value_ranges[] = {
+{0, 0},{2, 1},{5, 2},{9, 3},{11, 4},{0, 11}
+};
+static const ProtobufCEnumValueIndex error_code__enum_values_by_name[11] =
+{
+ { "AccountExists", 7 },
+ { "ApplicationBanned", 10 },
+ { "BadConnectionId", 2 },
+ { "BadCredentials", 5 },
+ { "CouldNotValidateCredentials", 6 },
+ { "ExtraVerificationRequired", 8 },
+ { "InvalidAppKey", 9 },
+ { "PremiumAccountRequired", 4 },
+ { "ProtocolError", 0 },
+ { "TravelRestriction", 3 },
+ { "TryAnotherAP", 1 },
+};
+const ProtobufCEnumDescriptor error_code__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "ErrorCode",
+ "ErrorCode",
+ "ErrorCode",
+ "",
+ 11,
+ error_code__enum_values_by_number,
+ 11,
+ error_code__enum_values_by_name,
+ 5,
+ error_code__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
diff --git a/src/inputs/librespot-c/src/proto/keyexchange.pb-c.h b/src/inputs/librespot-c/src/proto/keyexchange.pb-c.h
new file mode 100644
index 00000000..45a22ed6
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/keyexchange.pb-c.h
@@ -0,0 +1,1076 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/keyexchange.proto */
+
+#ifndef PROTOBUF_C_proto_2fkeyexchange_2eproto__INCLUDED
+#define PROTOBUF_C_proto_2fkeyexchange_2eproto__INCLUDED
+
+#include
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1000000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct _ClientHello ClientHello;
+typedef struct _BuildInfo BuildInfo;
+typedef struct _LoginCryptoHelloUnion LoginCryptoHelloUnion;
+typedef struct _LoginCryptoDiffieHellmanHello LoginCryptoDiffieHellmanHello;
+typedef struct _FeatureSet FeatureSet;
+typedef struct _APResponseMessage APResponseMessage;
+typedef struct _APChallenge APChallenge;
+typedef struct _LoginCryptoChallengeUnion LoginCryptoChallengeUnion;
+typedef struct _LoginCryptoDiffieHellmanChallenge LoginCryptoDiffieHellmanChallenge;
+typedef struct _FingerprintChallengeUnion FingerprintChallengeUnion;
+typedef struct _FingerprintGrainChallenge FingerprintGrainChallenge;
+typedef struct _FingerprintHmacRipemdChallenge FingerprintHmacRipemdChallenge;
+typedef struct _PoWChallengeUnion PoWChallengeUnion;
+typedef struct _PoWHashCashChallenge PoWHashCashChallenge;
+typedef struct _CryptoChallengeUnion CryptoChallengeUnion;
+typedef struct _CryptoShannonChallenge CryptoShannonChallenge;
+typedef struct _CryptoRc4Sha1HmacChallenge CryptoRc4Sha1HmacChallenge;
+typedef struct _UpgradeRequiredMessage UpgradeRequiredMessage;
+typedef struct _APLoginFailed APLoginFailed;
+typedef struct _ClientResponsePlaintext ClientResponsePlaintext;
+typedef struct _LoginCryptoResponseUnion LoginCryptoResponseUnion;
+typedef struct _LoginCryptoDiffieHellmanResponse LoginCryptoDiffieHellmanResponse;
+typedef struct _PoWResponseUnion PoWResponseUnion;
+typedef struct _PoWHashCashResponse PoWHashCashResponse;
+typedef struct _CryptoResponseUnion CryptoResponseUnion;
+typedef struct _CryptoShannonResponse CryptoShannonResponse;
+typedef struct _CryptoRc4Sha1HmacResponse CryptoRc4Sha1HmacResponse;
+
+
+/* --- enums --- */
+
+typedef enum _Product {
+ PRODUCT__PRODUCT_CLIENT = 0,
+ PRODUCT__PRODUCT_LIBSPOTIFY = 1,
+ PRODUCT__PRODUCT_MOBILE = 2,
+ PRODUCT__PRODUCT_PARTNER = 3,
+ PRODUCT__PRODUCT_LIBSPOTIFY_EMBEDDED = 5
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PRODUCT)
+} Product;
+typedef enum _ProductFlags {
+ PRODUCT_FLAGS__PRODUCT_FLAG_NONE = 0,
+ PRODUCT_FLAGS__PRODUCT_FLAG_DEV_BUILD = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PRODUCT_FLAGS)
+} ProductFlags;
+typedef enum _Platform {
+ PLATFORM__PLATFORM_WIN32_X86 = 0,
+ PLATFORM__PLATFORM_OSX_X86 = 1,
+ PLATFORM__PLATFORM_LINUX_X86 = 2,
+ PLATFORM__PLATFORM_IPHONE_ARM = 3,
+ PLATFORM__PLATFORM_S60_ARM = 4,
+ PLATFORM__PLATFORM_OSX_PPC = 5,
+ PLATFORM__PLATFORM_ANDROID_ARM = 6,
+ PLATFORM__PLATFORM_WINDOWS_CE_ARM = 7,
+ PLATFORM__PLATFORM_LINUX_X86_64 = 8,
+ PLATFORM__PLATFORM_OSX_X86_64 = 9,
+ PLATFORM__PLATFORM_PALM_ARM = 10,
+ PLATFORM__PLATFORM_LINUX_SH = 11,
+ PLATFORM__PLATFORM_FREEBSD_X86 = 12,
+ PLATFORM__PLATFORM_FREEBSD_X86_64 = 13,
+ PLATFORM__PLATFORM_BLACKBERRY_ARM = 14,
+ PLATFORM__PLATFORM_SONOS = 15,
+ PLATFORM__PLATFORM_LINUX_MIPS = 16,
+ PLATFORM__PLATFORM_LINUX_ARM = 17,
+ PLATFORM__PLATFORM_LOGITECH_ARM = 18,
+ PLATFORM__PLATFORM_LINUX_BLACKFIN = 19,
+ PLATFORM__PLATFORM_WP7_ARM = 20,
+ PLATFORM__PLATFORM_ONKYO_ARM = 21,
+ PLATFORM__PLATFORM_QNXNTO_ARM = 22,
+ PLATFORM__PLATFORM_BCO_ARM = 23
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(PLATFORM)
+} Platform;
+typedef enum _Fingerprint {
+ FINGERPRINT__FINGERPRINT_GRAIN = 0,
+ FINGERPRINT__FINGERPRINT_HMAC_RIPEMD = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(FINGERPRINT)
+} Fingerprint;
+typedef enum _Cryptosuite {
+ CRYPTOSUITE__CRYPTO_SUITE_SHANNON = 0,
+ CRYPTOSUITE__CRYPTO_SUITE_RC4_SHA1_HMAC = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(CRYPTOSUITE)
+} Cryptosuite;
+typedef enum _Powscheme {
+ POWSCHEME__POW_HASH_CASH = 0
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(POWSCHEME)
+} Powscheme;
+typedef enum _ErrorCode {
+ ERROR_CODE__ProtocolError = 0,
+ ERROR_CODE__TryAnotherAP = 2,
+ ERROR_CODE__BadConnectionId = 5,
+ ERROR_CODE__TravelRestriction = 9,
+ ERROR_CODE__PremiumAccountRequired = 11,
+ ERROR_CODE__BadCredentials = 12,
+ ERROR_CODE__CouldNotValidateCredentials = 13,
+ ERROR_CODE__AccountExists = 14,
+ ERROR_CODE__ExtraVerificationRequired = 15,
+ ERROR_CODE__InvalidAppKey = 16,
+ ERROR_CODE__ApplicationBanned = 17
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ERROR_CODE)
+} ErrorCode;
+
+/* --- messages --- */
+
+struct _ClientHello
+{
+ ProtobufCMessage base;
+ BuildInfo *build_info;
+ size_t n_fingerprints_supported;
+ Fingerprint *fingerprints_supported;
+ size_t n_cryptosuites_supported;
+ Cryptosuite *cryptosuites_supported;
+ size_t n_powschemes_supported;
+ Powscheme *powschemes_supported;
+ LoginCryptoHelloUnion *login_crypto_hello;
+ ProtobufCBinaryData client_nonce;
+ protobuf_c_boolean has_padding;
+ ProtobufCBinaryData padding;
+ FeatureSet *feature_set;
+};
+#define CLIENT_HELLO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_hello__descriptor) \
+ , NULL, 0,NULL, 0,NULL, 0,NULL, NULL, {0,NULL}, 0, {0,NULL}, NULL }
+
+
+struct _BuildInfo
+{
+ ProtobufCMessage base;
+ Product product;
+ size_t n_product_flags;
+ ProductFlags *product_flags;
+ Platform platform;
+ uint64_t version;
+};
+#define BUILD_INFO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&build_info__descriptor) \
+ , PRODUCT__PRODUCT_CLIENT, 0,NULL, PLATFORM__PLATFORM_WIN32_X86, 0 }
+
+
+struct _LoginCryptoHelloUnion
+{
+ ProtobufCMessage base;
+ LoginCryptoDiffieHellmanHello *diffie_hellman;
+};
+#define LOGIN_CRYPTO_HELLO_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_hello_union__descriptor) \
+ , NULL }
+
+
+struct _LoginCryptoDiffieHellmanHello
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData gc;
+ uint32_t server_keys_known;
+};
+#define LOGIN_CRYPTO_DIFFIE_HELLMAN_HELLO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_diffie_hellman_hello__descriptor) \
+ , {0,NULL}, 0 }
+
+
+struct _FeatureSet
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_autoupdate2;
+ protobuf_c_boolean autoupdate2;
+ protobuf_c_boolean has_current_location;
+ protobuf_c_boolean current_location;
+};
+#define FEATURE_SET__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&feature_set__descriptor) \
+ , 0, 0, 0, 0 }
+
+
+struct _APResponseMessage
+{
+ ProtobufCMessage base;
+ APChallenge *challenge;
+ UpgradeRequiredMessage *upgrade;
+ APLoginFailed *login_failed;
+};
+#define APRESPONSE_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&apresponse_message__descriptor) \
+ , NULL, NULL, NULL }
+
+
+struct _APChallenge
+{
+ ProtobufCMessage base;
+ LoginCryptoChallengeUnion *login_crypto_challenge;
+ FingerprintChallengeUnion *fingerprint_challenge;
+ PoWChallengeUnion *pow_challenge;
+ CryptoChallengeUnion *crypto_challenge;
+ ProtobufCBinaryData server_nonce;
+ protobuf_c_boolean has_padding;
+ ProtobufCBinaryData padding;
+};
+#define APCHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&apchallenge__descriptor) \
+ , NULL, NULL, NULL, NULL, {0,NULL}, 0, {0,NULL} }
+
+
+struct _LoginCryptoChallengeUnion
+{
+ ProtobufCMessage base;
+ LoginCryptoDiffieHellmanChallenge *diffie_hellman;
+};
+#define LOGIN_CRYPTO_CHALLENGE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_challenge_union__descriptor) \
+ , NULL }
+
+
+struct _LoginCryptoDiffieHellmanChallenge
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData gs;
+ int32_t server_signature_key;
+ ProtobufCBinaryData gs_signature;
+};
+#define LOGIN_CRYPTO_DIFFIE_HELLMAN_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_diffie_hellman_challenge__descriptor) \
+ , {0,NULL}, 0, {0,NULL} }
+
+
+struct _FingerprintChallengeUnion
+{
+ ProtobufCMessage base;
+ FingerprintGrainChallenge *grain;
+ FingerprintHmacRipemdChallenge *hmac_ripemd;
+};
+#define FINGERPRINT_CHALLENGE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_challenge_union__descriptor) \
+ , NULL, NULL }
+
+
+struct _FingerprintGrainChallenge
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData kek;
+};
+#define FINGERPRINT_GRAIN_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_grain_challenge__descriptor) \
+ , {0,NULL} }
+
+
+struct _FingerprintHmacRipemdChallenge
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData challenge;
+};
+#define FINGERPRINT_HMAC_RIPEMD_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&fingerprint_hmac_ripemd_challenge__descriptor) \
+ , {0,NULL} }
+
+
+struct _PoWChallengeUnion
+{
+ ProtobufCMessage base;
+ PoWHashCashChallenge *hash_cash;
+};
+#define PO_WCHALLENGE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&po_wchallenge_union__descriptor) \
+ , NULL }
+
+
+struct _PoWHashCashChallenge
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_prefix;
+ ProtobufCBinaryData prefix;
+ protobuf_c_boolean has_length;
+ int32_t length;
+ protobuf_c_boolean has_target;
+ int32_t target;
+};
+#define PO_WHASH_CASH_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&po_whash_cash_challenge__descriptor) \
+ , 0, {0,NULL}, 0, 0, 0, 0 }
+
+
+struct _CryptoChallengeUnion
+{
+ ProtobufCMessage base;
+ CryptoShannonChallenge *shannon;
+ CryptoRc4Sha1HmacChallenge *rc4_sha1_hmac;
+};
+#define CRYPTO_CHALLENGE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_challenge_union__descriptor) \
+ , NULL, NULL }
+
+
+struct _CryptoShannonChallenge
+{
+ ProtobufCMessage base;
+};
+#define CRYPTO_SHANNON_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_shannon_challenge__descriptor) \
+ }
+
+
+struct _CryptoRc4Sha1HmacChallenge
+{
+ ProtobufCMessage base;
+};
+#define CRYPTO_RC4_SHA1_HMAC_CHALLENGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_rc4_sha1_hmac_challenge__descriptor) \
+ }
+
+
+struct _UpgradeRequiredMessage
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData upgrade_signed_part;
+ ProtobufCBinaryData signature;
+ char *http_suffix;
+};
+#define UPGRADE_REQUIRED_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&upgrade_required_message__descriptor) \
+ , {0,NULL}, {0,NULL}, NULL }
+
+
+struct _APLoginFailed
+{
+ ProtobufCMessage base;
+ ErrorCode error_code;
+ protobuf_c_boolean has_retry_delay;
+ int32_t retry_delay;
+ protobuf_c_boolean has_expiry;
+ int32_t expiry;
+ char *error_description;
+};
+#define APLOGIN_FAILED__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&aplogin_failed__descriptor) \
+ , ERROR_CODE__ProtocolError, 0, 0, 0, 0, NULL }
+
+
+struct _ClientResponsePlaintext
+{
+ ProtobufCMessage base;
+ LoginCryptoResponseUnion *login_crypto_response;
+ PoWResponseUnion *pow_response;
+ CryptoResponseUnion *crypto_response;
+};
+#define CLIENT_RESPONSE_PLAINTEXT__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_response_plaintext__descriptor) \
+ , NULL, NULL, NULL }
+
+
+struct _LoginCryptoResponseUnion
+{
+ ProtobufCMessage base;
+ LoginCryptoDiffieHellmanResponse *diffie_hellman;
+};
+#define LOGIN_CRYPTO_RESPONSE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_response_union__descriptor) \
+ , NULL }
+
+
+struct _LoginCryptoDiffieHellmanResponse
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData hmac;
+};
+#define LOGIN_CRYPTO_DIFFIE_HELLMAN_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&login_crypto_diffie_hellman_response__descriptor) \
+ , {0,NULL} }
+
+
+struct _PoWResponseUnion
+{
+ ProtobufCMessage base;
+ PoWHashCashResponse *hash_cash;
+};
+#define PO_WRESPONSE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&po_wresponse_union__descriptor) \
+ , NULL }
+
+
+struct _PoWHashCashResponse
+{
+ ProtobufCMessage base;
+ ProtobufCBinaryData hash_suffix;
+};
+#define PO_WHASH_CASH_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&po_whash_cash_response__descriptor) \
+ , {0,NULL} }
+
+
+struct _CryptoResponseUnion
+{
+ ProtobufCMessage base;
+ CryptoShannonResponse *shannon;
+ CryptoRc4Sha1HmacResponse *rc4_sha1_hmac;
+};
+#define CRYPTO_RESPONSE_UNION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_response_union__descriptor) \
+ , NULL, NULL }
+
+
+struct _CryptoShannonResponse
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_dummy;
+ int32_t dummy;
+};
+#define CRYPTO_SHANNON_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_shannon_response__descriptor) \
+ , 0, 0 }
+
+
+struct _CryptoRc4Sha1HmacResponse
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_dummy;
+ int32_t dummy;
+};
+#define CRYPTO_RC4_SHA1_HMAC_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&crypto_rc4_sha1_hmac_response__descriptor) \
+ , 0, 0 }
+
+
+/* ClientHello methods */
+void client_hello__init
+ (ClientHello *message);
+size_t client_hello__get_packed_size
+ (const ClientHello *message);
+size_t client_hello__pack
+ (const ClientHello *message,
+ uint8_t *out);
+size_t client_hello__pack_to_buffer
+ (const ClientHello *message,
+ ProtobufCBuffer *buffer);
+ClientHello *
+ client_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_hello__free_unpacked
+ (ClientHello *message,
+ ProtobufCAllocator *allocator);
+/* BuildInfo methods */
+void build_info__init
+ (BuildInfo *message);
+size_t build_info__get_packed_size
+ (const BuildInfo *message);
+size_t build_info__pack
+ (const BuildInfo *message,
+ uint8_t *out);
+size_t build_info__pack_to_buffer
+ (const BuildInfo *message,
+ ProtobufCBuffer *buffer);
+BuildInfo *
+ build_info__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void build_info__free_unpacked
+ (BuildInfo *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoHelloUnion methods */
+void login_crypto_hello_union__init
+ (LoginCryptoHelloUnion *message);
+size_t login_crypto_hello_union__get_packed_size
+ (const LoginCryptoHelloUnion *message);
+size_t login_crypto_hello_union__pack
+ (const LoginCryptoHelloUnion *message,
+ uint8_t *out);
+size_t login_crypto_hello_union__pack_to_buffer
+ (const LoginCryptoHelloUnion *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoHelloUnion *
+ login_crypto_hello_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_hello_union__free_unpacked
+ (LoginCryptoHelloUnion *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoDiffieHellmanHello methods */
+void login_crypto_diffie_hellman_hello__init
+ (LoginCryptoDiffieHellmanHello *message);
+size_t login_crypto_diffie_hellman_hello__get_packed_size
+ (const LoginCryptoDiffieHellmanHello *message);
+size_t login_crypto_diffie_hellman_hello__pack
+ (const LoginCryptoDiffieHellmanHello *message,
+ uint8_t *out);
+size_t login_crypto_diffie_hellman_hello__pack_to_buffer
+ (const LoginCryptoDiffieHellmanHello *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoDiffieHellmanHello *
+ login_crypto_diffie_hellman_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_diffie_hellman_hello__free_unpacked
+ (LoginCryptoDiffieHellmanHello *message,
+ ProtobufCAllocator *allocator);
+/* FeatureSet methods */
+void feature_set__init
+ (FeatureSet *message);
+size_t feature_set__get_packed_size
+ (const FeatureSet *message);
+size_t feature_set__pack
+ (const FeatureSet *message,
+ uint8_t *out);
+size_t feature_set__pack_to_buffer
+ (const FeatureSet *message,
+ ProtobufCBuffer *buffer);
+FeatureSet *
+ feature_set__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void feature_set__free_unpacked
+ (FeatureSet *message,
+ ProtobufCAllocator *allocator);
+/* APResponseMessage methods */
+void apresponse_message__init
+ (APResponseMessage *message);
+size_t apresponse_message__get_packed_size
+ (const APResponseMessage *message);
+size_t apresponse_message__pack
+ (const APResponseMessage *message,
+ uint8_t *out);
+size_t apresponse_message__pack_to_buffer
+ (const APResponseMessage *message,
+ ProtobufCBuffer *buffer);
+APResponseMessage *
+ apresponse_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void apresponse_message__free_unpacked
+ (APResponseMessage *message,
+ ProtobufCAllocator *allocator);
+/* APChallenge methods */
+void apchallenge__init
+ (APChallenge *message);
+size_t apchallenge__get_packed_size
+ (const APChallenge *message);
+size_t apchallenge__pack
+ (const APChallenge *message,
+ uint8_t *out);
+size_t apchallenge__pack_to_buffer
+ (const APChallenge *message,
+ ProtobufCBuffer *buffer);
+APChallenge *
+ apchallenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void apchallenge__free_unpacked
+ (APChallenge *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoChallengeUnion methods */
+void login_crypto_challenge_union__init
+ (LoginCryptoChallengeUnion *message);
+size_t login_crypto_challenge_union__get_packed_size
+ (const LoginCryptoChallengeUnion *message);
+size_t login_crypto_challenge_union__pack
+ (const LoginCryptoChallengeUnion *message,
+ uint8_t *out);
+size_t login_crypto_challenge_union__pack_to_buffer
+ (const LoginCryptoChallengeUnion *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoChallengeUnion *
+ login_crypto_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_challenge_union__free_unpacked
+ (LoginCryptoChallengeUnion *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoDiffieHellmanChallenge methods */
+void login_crypto_diffie_hellman_challenge__init
+ (LoginCryptoDiffieHellmanChallenge *message);
+size_t login_crypto_diffie_hellman_challenge__get_packed_size
+ (const LoginCryptoDiffieHellmanChallenge *message);
+size_t login_crypto_diffie_hellman_challenge__pack
+ (const LoginCryptoDiffieHellmanChallenge *message,
+ uint8_t *out);
+size_t login_crypto_diffie_hellman_challenge__pack_to_buffer
+ (const LoginCryptoDiffieHellmanChallenge *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoDiffieHellmanChallenge *
+ login_crypto_diffie_hellman_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_diffie_hellman_challenge__free_unpacked
+ (LoginCryptoDiffieHellmanChallenge *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintChallengeUnion methods */
+void fingerprint_challenge_union__init
+ (FingerprintChallengeUnion *message);
+size_t fingerprint_challenge_union__get_packed_size
+ (const FingerprintChallengeUnion *message);
+size_t fingerprint_challenge_union__pack
+ (const FingerprintChallengeUnion *message,
+ uint8_t *out);
+size_t fingerprint_challenge_union__pack_to_buffer
+ (const FingerprintChallengeUnion *message,
+ ProtobufCBuffer *buffer);
+FingerprintChallengeUnion *
+ fingerprint_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_challenge_union__free_unpacked
+ (FingerprintChallengeUnion *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintGrainChallenge methods */
+void fingerprint_grain_challenge__init
+ (FingerprintGrainChallenge *message);
+size_t fingerprint_grain_challenge__get_packed_size
+ (const FingerprintGrainChallenge *message);
+size_t fingerprint_grain_challenge__pack
+ (const FingerprintGrainChallenge *message,
+ uint8_t *out);
+size_t fingerprint_grain_challenge__pack_to_buffer
+ (const FingerprintGrainChallenge *message,
+ ProtobufCBuffer *buffer);
+FingerprintGrainChallenge *
+ fingerprint_grain_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_grain_challenge__free_unpacked
+ (FingerprintGrainChallenge *message,
+ ProtobufCAllocator *allocator);
+/* FingerprintHmacRipemdChallenge methods */
+void fingerprint_hmac_ripemd_challenge__init
+ (FingerprintHmacRipemdChallenge *message);
+size_t fingerprint_hmac_ripemd_challenge__get_packed_size
+ (const FingerprintHmacRipemdChallenge *message);
+size_t fingerprint_hmac_ripemd_challenge__pack
+ (const FingerprintHmacRipemdChallenge *message,
+ uint8_t *out);
+size_t fingerprint_hmac_ripemd_challenge__pack_to_buffer
+ (const FingerprintHmacRipemdChallenge *message,
+ ProtobufCBuffer *buffer);
+FingerprintHmacRipemdChallenge *
+ fingerprint_hmac_ripemd_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void fingerprint_hmac_ripemd_challenge__free_unpacked
+ (FingerprintHmacRipemdChallenge *message,
+ ProtobufCAllocator *allocator);
+/* PoWChallengeUnion methods */
+void po_wchallenge_union__init
+ (PoWChallengeUnion *message);
+size_t po_wchallenge_union__get_packed_size
+ (const PoWChallengeUnion *message);
+size_t po_wchallenge_union__pack
+ (const PoWChallengeUnion *message,
+ uint8_t *out);
+size_t po_wchallenge_union__pack_to_buffer
+ (const PoWChallengeUnion *message,
+ ProtobufCBuffer *buffer);
+PoWChallengeUnion *
+ po_wchallenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void po_wchallenge_union__free_unpacked
+ (PoWChallengeUnion *message,
+ ProtobufCAllocator *allocator);
+/* PoWHashCashChallenge methods */
+void po_whash_cash_challenge__init
+ (PoWHashCashChallenge *message);
+size_t po_whash_cash_challenge__get_packed_size
+ (const PoWHashCashChallenge *message);
+size_t po_whash_cash_challenge__pack
+ (const PoWHashCashChallenge *message,
+ uint8_t *out);
+size_t po_whash_cash_challenge__pack_to_buffer
+ (const PoWHashCashChallenge *message,
+ ProtobufCBuffer *buffer);
+PoWHashCashChallenge *
+ po_whash_cash_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void po_whash_cash_challenge__free_unpacked
+ (PoWHashCashChallenge *message,
+ ProtobufCAllocator *allocator);
+/* CryptoChallengeUnion methods */
+void crypto_challenge_union__init
+ (CryptoChallengeUnion *message);
+size_t crypto_challenge_union__get_packed_size
+ (const CryptoChallengeUnion *message);
+size_t crypto_challenge_union__pack
+ (const CryptoChallengeUnion *message,
+ uint8_t *out);
+size_t crypto_challenge_union__pack_to_buffer
+ (const CryptoChallengeUnion *message,
+ ProtobufCBuffer *buffer);
+CryptoChallengeUnion *
+ crypto_challenge_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_challenge_union__free_unpacked
+ (CryptoChallengeUnion *message,
+ ProtobufCAllocator *allocator);
+/* CryptoShannonChallenge methods */
+void crypto_shannon_challenge__init
+ (CryptoShannonChallenge *message);
+size_t crypto_shannon_challenge__get_packed_size
+ (const CryptoShannonChallenge *message);
+size_t crypto_shannon_challenge__pack
+ (const CryptoShannonChallenge *message,
+ uint8_t *out);
+size_t crypto_shannon_challenge__pack_to_buffer
+ (const CryptoShannonChallenge *message,
+ ProtobufCBuffer *buffer);
+CryptoShannonChallenge *
+ crypto_shannon_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_shannon_challenge__free_unpacked
+ (CryptoShannonChallenge *message,
+ ProtobufCAllocator *allocator);
+/* CryptoRc4Sha1HmacChallenge methods */
+void crypto_rc4_sha1_hmac_challenge__init
+ (CryptoRc4Sha1HmacChallenge *message);
+size_t crypto_rc4_sha1_hmac_challenge__get_packed_size
+ (const CryptoRc4Sha1HmacChallenge *message);
+size_t crypto_rc4_sha1_hmac_challenge__pack
+ (const CryptoRc4Sha1HmacChallenge *message,
+ uint8_t *out);
+size_t crypto_rc4_sha1_hmac_challenge__pack_to_buffer
+ (const CryptoRc4Sha1HmacChallenge *message,
+ ProtobufCBuffer *buffer);
+CryptoRc4Sha1HmacChallenge *
+ crypto_rc4_sha1_hmac_challenge__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_rc4_sha1_hmac_challenge__free_unpacked
+ (CryptoRc4Sha1HmacChallenge *message,
+ ProtobufCAllocator *allocator);
+/* UpgradeRequiredMessage methods */
+void upgrade_required_message__init
+ (UpgradeRequiredMessage *message);
+size_t upgrade_required_message__get_packed_size
+ (const UpgradeRequiredMessage *message);
+size_t upgrade_required_message__pack
+ (const UpgradeRequiredMessage *message,
+ uint8_t *out);
+size_t upgrade_required_message__pack_to_buffer
+ (const UpgradeRequiredMessage *message,
+ ProtobufCBuffer *buffer);
+UpgradeRequiredMessage *
+ upgrade_required_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void upgrade_required_message__free_unpacked
+ (UpgradeRequiredMessage *message,
+ ProtobufCAllocator *allocator);
+/* APLoginFailed methods */
+void aplogin_failed__init
+ (APLoginFailed *message);
+size_t aplogin_failed__get_packed_size
+ (const APLoginFailed *message);
+size_t aplogin_failed__pack
+ (const APLoginFailed *message,
+ uint8_t *out);
+size_t aplogin_failed__pack_to_buffer
+ (const APLoginFailed *message,
+ ProtobufCBuffer *buffer);
+APLoginFailed *
+ aplogin_failed__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void aplogin_failed__free_unpacked
+ (APLoginFailed *message,
+ ProtobufCAllocator *allocator);
+/* ClientResponsePlaintext methods */
+void client_response_plaintext__init
+ (ClientResponsePlaintext *message);
+size_t client_response_plaintext__get_packed_size
+ (const ClientResponsePlaintext *message);
+size_t client_response_plaintext__pack
+ (const ClientResponsePlaintext *message,
+ uint8_t *out);
+size_t client_response_plaintext__pack_to_buffer
+ (const ClientResponsePlaintext *message,
+ ProtobufCBuffer *buffer);
+ClientResponsePlaintext *
+ client_response_plaintext__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_response_plaintext__free_unpacked
+ (ClientResponsePlaintext *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoResponseUnion methods */
+void login_crypto_response_union__init
+ (LoginCryptoResponseUnion *message);
+size_t login_crypto_response_union__get_packed_size
+ (const LoginCryptoResponseUnion *message);
+size_t login_crypto_response_union__pack
+ (const LoginCryptoResponseUnion *message,
+ uint8_t *out);
+size_t login_crypto_response_union__pack_to_buffer
+ (const LoginCryptoResponseUnion *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoResponseUnion *
+ login_crypto_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_response_union__free_unpacked
+ (LoginCryptoResponseUnion *message,
+ ProtobufCAllocator *allocator);
+/* LoginCryptoDiffieHellmanResponse methods */
+void login_crypto_diffie_hellman_response__init
+ (LoginCryptoDiffieHellmanResponse *message);
+size_t login_crypto_diffie_hellman_response__get_packed_size
+ (const LoginCryptoDiffieHellmanResponse *message);
+size_t login_crypto_diffie_hellman_response__pack
+ (const LoginCryptoDiffieHellmanResponse *message,
+ uint8_t *out);
+size_t login_crypto_diffie_hellman_response__pack_to_buffer
+ (const LoginCryptoDiffieHellmanResponse *message,
+ ProtobufCBuffer *buffer);
+LoginCryptoDiffieHellmanResponse *
+ login_crypto_diffie_hellman_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void login_crypto_diffie_hellman_response__free_unpacked
+ (LoginCryptoDiffieHellmanResponse *message,
+ ProtobufCAllocator *allocator);
+/* PoWResponseUnion methods */
+void po_wresponse_union__init
+ (PoWResponseUnion *message);
+size_t po_wresponse_union__get_packed_size
+ (const PoWResponseUnion *message);
+size_t po_wresponse_union__pack
+ (const PoWResponseUnion *message,
+ uint8_t *out);
+size_t po_wresponse_union__pack_to_buffer
+ (const PoWResponseUnion *message,
+ ProtobufCBuffer *buffer);
+PoWResponseUnion *
+ po_wresponse_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void po_wresponse_union__free_unpacked
+ (PoWResponseUnion *message,
+ ProtobufCAllocator *allocator);
+/* PoWHashCashResponse methods */
+void po_whash_cash_response__init
+ (PoWHashCashResponse *message);
+size_t po_whash_cash_response__get_packed_size
+ (const PoWHashCashResponse *message);
+size_t po_whash_cash_response__pack
+ (const PoWHashCashResponse *message,
+ uint8_t *out);
+size_t po_whash_cash_response__pack_to_buffer
+ (const PoWHashCashResponse *message,
+ ProtobufCBuffer *buffer);
+PoWHashCashResponse *
+ po_whash_cash_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void po_whash_cash_response__free_unpacked
+ (PoWHashCashResponse *message,
+ ProtobufCAllocator *allocator);
+/* CryptoResponseUnion methods */
+void crypto_response_union__init
+ (CryptoResponseUnion *message);
+size_t crypto_response_union__get_packed_size
+ (const CryptoResponseUnion *message);
+size_t crypto_response_union__pack
+ (const CryptoResponseUnion *message,
+ uint8_t *out);
+size_t crypto_response_union__pack_to_buffer
+ (const CryptoResponseUnion *message,
+ ProtobufCBuffer *buffer);
+CryptoResponseUnion *
+ crypto_response_union__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_response_union__free_unpacked
+ (CryptoResponseUnion *message,
+ ProtobufCAllocator *allocator);
+/* CryptoShannonResponse methods */
+void crypto_shannon_response__init
+ (CryptoShannonResponse *message);
+size_t crypto_shannon_response__get_packed_size
+ (const CryptoShannonResponse *message);
+size_t crypto_shannon_response__pack
+ (const CryptoShannonResponse *message,
+ uint8_t *out);
+size_t crypto_shannon_response__pack_to_buffer
+ (const CryptoShannonResponse *message,
+ ProtobufCBuffer *buffer);
+CryptoShannonResponse *
+ crypto_shannon_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_shannon_response__free_unpacked
+ (CryptoShannonResponse *message,
+ ProtobufCAllocator *allocator);
+/* CryptoRc4Sha1HmacResponse methods */
+void crypto_rc4_sha1_hmac_response__init
+ (CryptoRc4Sha1HmacResponse *message);
+size_t crypto_rc4_sha1_hmac_response__get_packed_size
+ (const CryptoRc4Sha1HmacResponse *message);
+size_t crypto_rc4_sha1_hmac_response__pack
+ (const CryptoRc4Sha1HmacResponse *message,
+ uint8_t *out);
+size_t crypto_rc4_sha1_hmac_response__pack_to_buffer
+ (const CryptoRc4Sha1HmacResponse *message,
+ ProtobufCBuffer *buffer);
+CryptoRc4Sha1HmacResponse *
+ crypto_rc4_sha1_hmac_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void crypto_rc4_sha1_hmac_response__free_unpacked
+ (CryptoRc4Sha1HmacResponse *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*ClientHello_Closure)
+ (const ClientHello *message,
+ void *closure_data);
+typedef void (*BuildInfo_Closure)
+ (const BuildInfo *message,
+ void *closure_data);
+typedef void (*LoginCryptoHelloUnion_Closure)
+ (const LoginCryptoHelloUnion *message,
+ void *closure_data);
+typedef void (*LoginCryptoDiffieHellmanHello_Closure)
+ (const LoginCryptoDiffieHellmanHello *message,
+ void *closure_data);
+typedef void (*FeatureSet_Closure)
+ (const FeatureSet *message,
+ void *closure_data);
+typedef void (*APResponseMessage_Closure)
+ (const APResponseMessage *message,
+ void *closure_data);
+typedef void (*APChallenge_Closure)
+ (const APChallenge *message,
+ void *closure_data);
+typedef void (*LoginCryptoChallengeUnion_Closure)
+ (const LoginCryptoChallengeUnion *message,
+ void *closure_data);
+typedef void (*LoginCryptoDiffieHellmanChallenge_Closure)
+ (const LoginCryptoDiffieHellmanChallenge *message,
+ void *closure_data);
+typedef void (*FingerprintChallengeUnion_Closure)
+ (const FingerprintChallengeUnion *message,
+ void *closure_data);
+typedef void (*FingerprintGrainChallenge_Closure)
+ (const FingerprintGrainChallenge *message,
+ void *closure_data);
+typedef void (*FingerprintHmacRipemdChallenge_Closure)
+ (const FingerprintHmacRipemdChallenge *message,
+ void *closure_data);
+typedef void (*PoWChallengeUnion_Closure)
+ (const PoWChallengeUnion *message,
+ void *closure_data);
+typedef void (*PoWHashCashChallenge_Closure)
+ (const PoWHashCashChallenge *message,
+ void *closure_data);
+typedef void (*CryptoChallengeUnion_Closure)
+ (const CryptoChallengeUnion *message,
+ void *closure_data);
+typedef void (*CryptoShannonChallenge_Closure)
+ (const CryptoShannonChallenge *message,
+ void *closure_data);
+typedef void (*CryptoRc4Sha1HmacChallenge_Closure)
+ (const CryptoRc4Sha1HmacChallenge *message,
+ void *closure_data);
+typedef void (*UpgradeRequiredMessage_Closure)
+ (const UpgradeRequiredMessage *message,
+ void *closure_data);
+typedef void (*APLoginFailed_Closure)
+ (const APLoginFailed *message,
+ void *closure_data);
+typedef void (*ClientResponsePlaintext_Closure)
+ (const ClientResponsePlaintext *message,
+ void *closure_data);
+typedef void (*LoginCryptoResponseUnion_Closure)
+ (const LoginCryptoResponseUnion *message,
+ void *closure_data);
+typedef void (*LoginCryptoDiffieHellmanResponse_Closure)
+ (const LoginCryptoDiffieHellmanResponse *message,
+ void *closure_data);
+typedef void (*PoWResponseUnion_Closure)
+ (const PoWResponseUnion *message,
+ void *closure_data);
+typedef void (*PoWHashCashResponse_Closure)
+ (const PoWHashCashResponse *message,
+ void *closure_data);
+typedef void (*CryptoResponseUnion_Closure)
+ (const CryptoResponseUnion *message,
+ void *closure_data);
+typedef void (*CryptoShannonResponse_Closure)
+ (const CryptoShannonResponse *message,
+ void *closure_data);
+typedef void (*CryptoRc4Sha1HmacResponse_Closure)
+ (const CryptoRc4Sha1HmacResponse *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCEnumDescriptor product__descriptor;
+extern const ProtobufCEnumDescriptor product_flags__descriptor;
+extern const ProtobufCEnumDescriptor platform__descriptor;
+extern const ProtobufCEnumDescriptor fingerprint__descriptor;
+extern const ProtobufCEnumDescriptor cryptosuite__descriptor;
+extern const ProtobufCEnumDescriptor powscheme__descriptor;
+extern const ProtobufCEnumDescriptor error_code__descriptor;
+extern const ProtobufCMessageDescriptor client_hello__descriptor;
+extern const ProtobufCMessageDescriptor build_info__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_hello_union__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_diffie_hellman_hello__descriptor;
+extern const ProtobufCMessageDescriptor feature_set__descriptor;
+extern const ProtobufCMessageDescriptor apresponse_message__descriptor;
+extern const ProtobufCMessageDescriptor apchallenge__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_challenge_union__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_diffie_hellman_challenge__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_challenge_union__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_grain_challenge__descriptor;
+extern const ProtobufCMessageDescriptor fingerprint_hmac_ripemd_challenge__descriptor;
+extern const ProtobufCMessageDescriptor po_wchallenge_union__descriptor;
+extern const ProtobufCMessageDescriptor po_whash_cash_challenge__descriptor;
+extern const ProtobufCMessageDescriptor crypto_challenge_union__descriptor;
+extern const ProtobufCMessageDescriptor crypto_shannon_challenge__descriptor;
+extern const ProtobufCMessageDescriptor crypto_rc4_sha1_hmac_challenge__descriptor;
+extern const ProtobufCMessageDescriptor upgrade_required_message__descriptor;
+extern const ProtobufCMessageDescriptor aplogin_failed__descriptor;
+extern const ProtobufCMessageDescriptor client_response_plaintext__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_response_union__descriptor;
+extern const ProtobufCMessageDescriptor login_crypto_diffie_hellman_response__descriptor;
+extern const ProtobufCMessageDescriptor po_wresponse_union__descriptor;
+extern const ProtobufCMessageDescriptor po_whash_cash_response__descriptor;
+extern const ProtobufCMessageDescriptor crypto_response_union__descriptor;
+extern const ProtobufCMessageDescriptor crypto_shannon_response__descriptor;
+extern const ProtobufCMessageDescriptor crypto_rc4_sha1_hmac_response__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_proto_2fkeyexchange_2eproto__INCLUDED */
diff --git a/src/inputs/librespot-c/src/proto/keyexchange.proto b/src/inputs/librespot-c/src/proto/keyexchange.proto
new file mode 100644
index 00000000..0907c912
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/keyexchange.proto
@@ -0,0 +1,228 @@
+syntax = "proto2";
+
+message ClientHello {
+ required BuildInfo build_info = 0xa;
+ repeated Fingerprint fingerprints_supported = 0x14;
+ repeated Cryptosuite cryptosuites_supported = 0x1e;
+ repeated Powscheme powschemes_supported = 0x28;
+ required LoginCryptoHelloUnion login_crypto_hello = 0x32;
+ required bytes client_nonce = 0x3c;
+ optional bytes padding = 0x46;
+ optional FeatureSet feature_set = 0x50;
+}
+
+
+message BuildInfo {
+ required Product product = 0xa;
+ repeated ProductFlags product_flags = 0x14;
+ required Platform platform = 0x1e;
+ required uint64 version = 0x28;
+}
+
+enum Product {
+ PRODUCT_CLIENT = 0x0;
+ PRODUCT_LIBSPOTIFY= 0x1;
+ PRODUCT_MOBILE = 0x2;
+ PRODUCT_PARTNER = 0x3;
+ PRODUCT_LIBSPOTIFY_EMBEDDED = 0x5;
+}
+
+enum ProductFlags {
+ PRODUCT_FLAG_NONE = 0x0;
+ PRODUCT_FLAG_DEV_BUILD = 0x1;
+}
+
+enum Platform {
+ PLATFORM_WIN32_X86 = 0x0;
+ PLATFORM_OSX_X86 = 0x1;
+ PLATFORM_LINUX_X86 = 0x2;
+ PLATFORM_IPHONE_ARM = 0x3;
+ PLATFORM_S60_ARM = 0x4;
+ PLATFORM_OSX_PPC = 0x5;
+ PLATFORM_ANDROID_ARM = 0x6;
+ PLATFORM_WINDOWS_CE_ARM = 0x7;
+ PLATFORM_LINUX_X86_64 = 0x8;
+ PLATFORM_OSX_X86_64 = 0x9;
+ PLATFORM_PALM_ARM = 0xa;
+ PLATFORM_LINUX_SH = 0xb;
+ PLATFORM_FREEBSD_X86 = 0xc;
+ PLATFORM_FREEBSD_X86_64 = 0xd;
+ PLATFORM_BLACKBERRY_ARM = 0xe;
+ PLATFORM_SONOS = 0xf;
+ PLATFORM_LINUX_MIPS = 0x10;
+ PLATFORM_LINUX_ARM = 0x11;
+ PLATFORM_LOGITECH_ARM = 0x12;
+ PLATFORM_LINUX_BLACKFIN = 0x13;
+ PLATFORM_WP7_ARM = 0x14;
+ PLATFORM_ONKYO_ARM = 0x15;
+ PLATFORM_QNXNTO_ARM = 0x16;
+ PLATFORM_BCO_ARM = 0x17;
+}
+
+enum Fingerprint {
+ FINGERPRINT_GRAIN = 0x0;
+ FINGERPRINT_HMAC_RIPEMD = 0x1;
+}
+
+enum Cryptosuite {
+ CRYPTO_SUITE_SHANNON = 0x0;
+ CRYPTO_SUITE_RC4_SHA1_HMAC = 0x1;
+}
+
+enum Powscheme {
+ POW_HASH_CASH = 0x0;
+}
+
+
+message LoginCryptoHelloUnion {
+ optional LoginCryptoDiffieHellmanHello diffie_hellman = 0xa;
+}
+
+
+message LoginCryptoDiffieHellmanHello {
+ required bytes gc = 0xa;
+ required uint32 server_keys_known = 0x14;
+}
+
+
+message FeatureSet {
+ optional bool autoupdate2 = 0x1;
+ optional bool current_location = 0x2;
+}
+
+
+message APResponseMessage {
+ optional APChallenge challenge = 0xa;
+ optional UpgradeRequiredMessage upgrade = 0x14;
+ optional APLoginFailed login_failed = 0x1e;
+}
+
+message APChallenge {
+ required LoginCryptoChallengeUnion login_crypto_challenge = 0xa;
+ required FingerprintChallengeUnion fingerprint_challenge = 0x14;
+ required PoWChallengeUnion pow_challenge = 0x1e;
+ required CryptoChallengeUnion crypto_challenge = 0x28;
+ required bytes server_nonce = 0x32;
+ optional bytes padding = 0x3c;
+}
+
+message LoginCryptoChallengeUnion {
+ optional LoginCryptoDiffieHellmanChallenge diffie_hellman = 0xa;
+}
+
+message LoginCryptoDiffieHellmanChallenge {
+ required bytes gs = 0xa;
+ required int32 server_signature_key = 0x14;
+ required bytes gs_signature = 0x1e;
+}
+
+message FingerprintChallengeUnion {
+ optional FingerprintGrainChallenge grain = 0xa;
+ optional FingerprintHmacRipemdChallenge hmac_ripemd = 0x14;
+}
+
+
+message FingerprintGrainChallenge {
+ required bytes kek = 0xa;
+}
+
+
+message FingerprintHmacRipemdChallenge {
+ required bytes challenge = 0xa;
+}
+
+
+message PoWChallengeUnion {
+ optional PoWHashCashChallenge hash_cash = 0xa;
+}
+
+message PoWHashCashChallenge {
+ optional bytes prefix = 0xa;
+ optional int32 length = 0x14;
+ optional int32 target = 0x1e;
+}
+
+
+message CryptoChallengeUnion {
+ optional CryptoShannonChallenge shannon = 0xa;
+ optional CryptoRc4Sha1HmacChallenge rc4_sha1_hmac = 0x14;
+}
+
+
+message CryptoShannonChallenge {
+}
+
+
+message CryptoRc4Sha1HmacChallenge {
+}
+
+
+message UpgradeRequiredMessage {
+ required bytes upgrade_signed_part = 0xa;
+ required bytes signature = 0x14;
+ optional string http_suffix = 0x1e;
+}
+
+message APLoginFailed {
+ required ErrorCode error_code = 0xa;
+ optional int32 retry_delay = 0x14;
+ optional int32 expiry = 0x1e;
+ optional string error_description = 0x28;
+}
+
+enum ErrorCode {
+ ProtocolError = 0x0;
+ TryAnotherAP = 0x2;
+ BadConnectionId = 0x5;
+ TravelRestriction = 0x9;
+ PremiumAccountRequired = 0xb;
+ BadCredentials = 0xc;
+ CouldNotValidateCredentials = 0xd;
+ AccountExists = 0xe;
+ ExtraVerificationRequired = 0xf;
+ InvalidAppKey = 0x10;
+ ApplicationBanned = 0x11;
+}
+
+message ClientResponsePlaintext {
+ required LoginCryptoResponseUnion login_crypto_response = 0xa;
+ required PoWResponseUnion pow_response = 0x14;
+ required CryptoResponseUnion crypto_response = 0x1e;
+}
+
+
+message LoginCryptoResponseUnion {
+ optional LoginCryptoDiffieHellmanResponse diffie_hellman = 0xa;
+}
+
+
+message LoginCryptoDiffieHellmanResponse {
+ required bytes hmac = 0xa;
+}
+
+
+message PoWResponseUnion {
+ optional PoWHashCashResponse hash_cash = 0xa;
+}
+
+
+message PoWHashCashResponse {
+ required bytes hash_suffix = 0xa;
+}
+
+
+message CryptoResponseUnion {
+ optional CryptoShannonResponse shannon = 0xa;
+ optional CryptoRc4Sha1HmacResponse rc4_sha1_hmac = 0x14;
+}
+
+
+message CryptoShannonResponse {
+ optional int32 dummy = 0x1;
+}
+
+
+message CryptoRc4Sha1HmacResponse {
+ optional int32 dummy = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/mercury.pb-c.c b/src/inputs/librespot-c/src/proto/mercury.pb-c.c
new file mode 100644
index 00000000..b22a8274
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/mercury.pb-c.c
@@ -0,0 +1,720 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/mercury.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include "mercury.pb-c.h"
+void mercury_multi_get_request__init
+ (MercuryMultiGetRequest *message)
+{
+ static const MercuryMultiGetRequest init_value = MERCURY_MULTI_GET_REQUEST__INIT;
+ *message = init_value;
+}
+size_t mercury_multi_get_request__get_packed_size
+ (const MercuryMultiGetRequest *message)
+{
+ assert(message->base.descriptor == &mercury_multi_get_request__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t mercury_multi_get_request__pack
+ (const MercuryMultiGetRequest *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &mercury_multi_get_request__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t mercury_multi_get_request__pack_to_buffer
+ (const MercuryMultiGetRequest *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &mercury_multi_get_request__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+MercuryMultiGetRequest *
+ mercury_multi_get_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (MercuryMultiGetRequest *)
+ protobuf_c_message_unpack (&mercury_multi_get_request__descriptor,
+ allocator, len, data);
+}
+void mercury_multi_get_request__free_unpacked
+ (MercuryMultiGetRequest *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &mercury_multi_get_request__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void mercury_multi_get_reply__init
+ (MercuryMultiGetReply *message)
+{
+ static const MercuryMultiGetReply init_value = MERCURY_MULTI_GET_REPLY__INIT;
+ *message = init_value;
+}
+size_t mercury_multi_get_reply__get_packed_size
+ (const MercuryMultiGetReply *message)
+{
+ assert(message->base.descriptor == &mercury_multi_get_reply__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t mercury_multi_get_reply__pack
+ (const MercuryMultiGetReply *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &mercury_multi_get_reply__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t mercury_multi_get_reply__pack_to_buffer
+ (const MercuryMultiGetReply *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &mercury_multi_get_reply__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+MercuryMultiGetReply *
+ mercury_multi_get_reply__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (MercuryMultiGetReply *)
+ protobuf_c_message_unpack (&mercury_multi_get_reply__descriptor,
+ allocator, len, data);
+}
+void mercury_multi_get_reply__free_unpacked
+ (MercuryMultiGetReply *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &mercury_multi_get_reply__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void mercury_request__init
+ (MercuryRequest *message)
+{
+ static const MercuryRequest init_value = MERCURY_REQUEST__INIT;
+ *message = init_value;
+}
+size_t mercury_request__get_packed_size
+ (const MercuryRequest *message)
+{
+ assert(message->base.descriptor == &mercury_request__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t mercury_request__pack
+ (const MercuryRequest *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &mercury_request__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t mercury_request__pack_to_buffer
+ (const MercuryRequest *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &mercury_request__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+MercuryRequest *
+ mercury_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (MercuryRequest *)
+ protobuf_c_message_unpack (&mercury_request__descriptor,
+ allocator, len, data);
+}
+void mercury_request__free_unpacked
+ (MercuryRequest *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &mercury_request__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void mercury_reply__init
+ (MercuryReply *message)
+{
+ static const MercuryReply init_value = MERCURY_REPLY__INIT;
+ *message = init_value;
+}
+size_t mercury_reply__get_packed_size
+ (const MercuryReply *message)
+{
+ assert(message->base.descriptor == &mercury_reply__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t mercury_reply__pack
+ (const MercuryReply *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &mercury_reply__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t mercury_reply__pack_to_buffer
+ (const MercuryReply *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &mercury_reply__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+MercuryReply *
+ mercury_reply__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (MercuryReply *)
+ protobuf_c_message_unpack (&mercury_reply__descriptor,
+ allocator, len, data);
+}
+void mercury_reply__free_unpacked
+ (MercuryReply *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &mercury_reply__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void header__init
+ (Header *message)
+{
+ static const Header init_value = HEADER__INIT;
+ *message = init_value;
+}
+size_t header__get_packed_size
+ (const Header *message)
+{
+ assert(message->base.descriptor == &header__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t header__pack
+ (const Header *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &header__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t header__pack_to_buffer
+ (const Header *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &header__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Header *
+ header__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Header *)
+ protobuf_c_message_unpack (&header__descriptor,
+ allocator, len, data);
+}
+void header__free_unpacked
+ (Header *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &header__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void user_field__init
+ (UserField *message)
+{
+ static const UserField init_value = USER_FIELD__INIT;
+ *message = init_value;
+}
+size_t user_field__get_packed_size
+ (const UserField *message)
+{
+ assert(message->base.descriptor == &user_field__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t user_field__pack
+ (const UserField *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &user_field__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t user_field__pack_to_buffer
+ (const UserField *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &user_field__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+UserField *
+ user_field__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (UserField *)
+ protobuf_c_message_unpack (&user_field__descriptor,
+ allocator, len, data);
+}
+void user_field__free_unpacked
+ (UserField *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &user_field__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor mercury_multi_get_request__field_descriptors[1] =
+{
+ {
+ "request",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(MercuryMultiGetRequest, n_request),
+ offsetof(MercuryMultiGetRequest, request),
+ &mercury_request__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned mercury_multi_get_request__field_indices_by_name[] = {
+ 0, /* field[0] = request */
+};
+static const ProtobufCIntRange mercury_multi_get_request__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor mercury_multi_get_request__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "MercuryMultiGetRequest",
+ "MercuryMultiGetRequest",
+ "MercuryMultiGetRequest",
+ "",
+ sizeof(MercuryMultiGetRequest),
+ 1,
+ mercury_multi_get_request__field_descriptors,
+ mercury_multi_get_request__field_indices_by_name,
+ 1, mercury_multi_get_request__number_ranges,
+ (ProtobufCMessageInit) mercury_multi_get_request__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor mercury_multi_get_reply__field_descriptors[1] =
+{
+ {
+ "reply",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(MercuryMultiGetReply, n_reply),
+ offsetof(MercuryMultiGetReply, reply),
+ &mercury_reply__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned mercury_multi_get_reply__field_indices_by_name[] = {
+ 0, /* field[0] = reply */
+};
+static const ProtobufCIntRange mercury_multi_get_reply__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor mercury_multi_get_reply__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "MercuryMultiGetReply",
+ "MercuryMultiGetReply",
+ "MercuryMultiGetReply",
+ "",
+ sizeof(MercuryMultiGetReply),
+ 1,
+ mercury_multi_get_reply__field_descriptors,
+ mercury_multi_get_reply__field_indices_by_name,
+ 1, mercury_multi_get_reply__number_ranges,
+ (ProtobufCMessageInit) mercury_multi_get_reply__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor mercury_request__field_descriptors[4] =
+{
+ {
+ "uri",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(MercuryRequest, uri),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "content_type",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(MercuryRequest, content_type),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "body",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(MercuryRequest, has_body),
+ offsetof(MercuryRequest, body),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "etag",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(MercuryRequest, has_etag),
+ offsetof(MercuryRequest, etag),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned mercury_request__field_indices_by_name[] = {
+ 2, /* field[2] = body */
+ 1, /* field[1] = content_type */
+ 3, /* field[3] = etag */
+ 0, /* field[0] = uri */
+};
+static const ProtobufCIntRange mercury_request__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor mercury_request__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "MercuryRequest",
+ "MercuryRequest",
+ "MercuryRequest",
+ "",
+ sizeof(MercuryRequest),
+ 4,
+ mercury_request__field_descriptors,
+ mercury_request__field_indices_by_name,
+ 1, mercury_request__number_ranges,
+ (ProtobufCMessageInit) mercury_request__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue mercury_reply__cache_policy__enum_values_by_number[3] =
+{
+ { "CACHE_NO", "MERCURY_REPLY__CACHE_POLICY__CACHE_NO", 1 },
+ { "CACHE_PRIVATE", "MERCURY_REPLY__CACHE_POLICY__CACHE_PRIVATE", 2 },
+ { "CACHE_PUBLIC", "MERCURY_REPLY__CACHE_POLICY__CACHE_PUBLIC", 3 },
+};
+static const ProtobufCIntRange mercury_reply__cache_policy__value_ranges[] = {
+{1, 0},{0, 3}
+};
+static const ProtobufCEnumValueIndex mercury_reply__cache_policy__enum_values_by_name[3] =
+{
+ { "CACHE_NO", 0 },
+ { "CACHE_PRIVATE", 1 },
+ { "CACHE_PUBLIC", 2 },
+};
+const ProtobufCEnumDescriptor mercury_reply__cache_policy__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "MercuryReply.CachePolicy",
+ "CachePolicy",
+ "MercuryReply__CachePolicy",
+ "",
+ 3,
+ mercury_reply__cache_policy__enum_values_by_number,
+ 3,
+ mercury_reply__cache_policy__enum_values_by_name,
+ 1,
+ mercury_reply__cache_policy__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor mercury_reply__field_descriptors[7] =
+{
+ {
+ "status_code",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(MercuryReply, has_status_code),
+ offsetof(MercuryReply, status_code),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "status_message",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(MercuryReply, status_message),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cache_policy",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(MercuryReply, has_cache_policy),
+ offsetof(MercuryReply, cache_policy),
+ &mercury_reply__cache_policy__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "ttl",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(MercuryReply, has_ttl),
+ offsetof(MercuryReply, ttl),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "etag",
+ 5,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(MercuryReply, has_etag),
+ offsetof(MercuryReply, etag),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "content_type",
+ 6,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(MercuryReply, content_type),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "body",
+ 7,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(MercuryReply, has_body),
+ offsetof(MercuryReply, body),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned mercury_reply__field_indices_by_name[] = {
+ 6, /* field[6] = body */
+ 2, /* field[2] = cache_policy */
+ 5, /* field[5] = content_type */
+ 4, /* field[4] = etag */
+ 0, /* field[0] = status_code */
+ 1, /* field[1] = status_message */
+ 3, /* field[3] = ttl */
+};
+static const ProtobufCIntRange mercury_reply__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 7 }
+};
+const ProtobufCMessageDescriptor mercury_reply__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "MercuryReply",
+ "MercuryReply",
+ "MercuryReply",
+ "",
+ sizeof(MercuryReply),
+ 7,
+ mercury_reply__field_descriptors,
+ mercury_reply__field_indices_by_name,
+ 1, mercury_reply__number_ranges,
+ (ProtobufCMessageInit) mercury_reply__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor header__field_descriptors[5] =
+{
+ {
+ "uri",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Header, uri),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "content_type",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Header, content_type),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "method",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Header, method),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "status_code",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Header, has_status_code),
+ offsetof(Header, status_code),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "user_fields",
+ 6,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Header, n_user_fields),
+ offsetof(Header, user_fields),
+ &user_field__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned header__field_indices_by_name[] = {
+ 1, /* field[1] = content_type */
+ 2, /* field[2] = method */
+ 3, /* field[3] = status_code */
+ 0, /* field[0] = uri */
+ 4, /* field[4] = user_fields */
+};
+static const ProtobufCIntRange header__number_ranges[2 + 1] =
+{
+ { 1, 0 },
+ { 6, 4 },
+ { 0, 5 }
+};
+const ProtobufCMessageDescriptor header__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Header",
+ "Header",
+ "Header",
+ "",
+ sizeof(Header),
+ 5,
+ header__field_descriptors,
+ header__field_indices_by_name,
+ 2, header__number_ranges,
+ (ProtobufCMessageInit) header__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor user_field__field_descriptors[2] =
+{
+ {
+ "key",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(UserField, key),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "value",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(UserField, has_value),
+ offsetof(UserField, value),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned user_field__field_indices_by_name[] = {
+ 0, /* field[0] = key */
+ 1, /* field[1] = value */
+};
+static const ProtobufCIntRange user_field__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor user_field__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "UserField",
+ "UserField",
+ "UserField",
+ "",
+ sizeof(UserField),
+ 2,
+ user_field__field_descriptors,
+ user_field__field_indices_by_name,
+ 1, user_field__number_ranges,
+ (ProtobufCMessageInit) user_field__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/inputs/librespot-c/src/proto/mercury.pb-c.h b/src/inputs/librespot-c/src/proto/mercury.pb-c.h
new file mode 100644
index 00000000..e0fb8621
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/mercury.pb-c.h
@@ -0,0 +1,274 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/mercury.proto */
+
+#ifndef PROTOBUF_C_proto_2fmercury_2eproto__INCLUDED
+#define PROTOBUF_C_proto_2fmercury_2eproto__INCLUDED
+
+#include
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1000000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct _MercuryMultiGetRequest MercuryMultiGetRequest;
+typedef struct _MercuryMultiGetReply MercuryMultiGetReply;
+typedef struct _MercuryRequest MercuryRequest;
+typedef struct _MercuryReply MercuryReply;
+typedef struct _Header Header;
+typedef struct _UserField UserField;
+
+
+/* --- enums --- */
+
+typedef enum _MercuryReply__CachePolicy {
+ MERCURY_REPLY__CACHE_POLICY__CACHE_NO = 1,
+ MERCURY_REPLY__CACHE_POLICY__CACHE_PRIVATE = 2,
+ MERCURY_REPLY__CACHE_POLICY__CACHE_PUBLIC = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(MERCURY_REPLY__CACHE_POLICY)
+} MercuryReply__CachePolicy;
+
+/* --- messages --- */
+
+struct _MercuryMultiGetRequest
+{
+ ProtobufCMessage base;
+ size_t n_request;
+ MercuryRequest **request;
+};
+#define MERCURY_MULTI_GET_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&mercury_multi_get_request__descriptor) \
+ , 0,NULL }
+
+
+struct _MercuryMultiGetReply
+{
+ ProtobufCMessage base;
+ size_t n_reply;
+ MercuryReply **reply;
+};
+#define MERCURY_MULTI_GET_REPLY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&mercury_multi_get_reply__descriptor) \
+ , 0,NULL }
+
+
+struct _MercuryRequest
+{
+ ProtobufCMessage base;
+ char *uri;
+ char *content_type;
+ protobuf_c_boolean has_body;
+ ProtobufCBinaryData body;
+ protobuf_c_boolean has_etag;
+ ProtobufCBinaryData etag;
+};
+#define MERCURY_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&mercury_request__descriptor) \
+ , NULL, NULL, 0, {0,NULL}, 0, {0,NULL} }
+
+
+struct _MercuryReply
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_status_code;
+ int32_t status_code;
+ char *status_message;
+ protobuf_c_boolean has_cache_policy;
+ MercuryReply__CachePolicy cache_policy;
+ protobuf_c_boolean has_ttl;
+ int32_t ttl;
+ protobuf_c_boolean has_etag;
+ ProtobufCBinaryData etag;
+ char *content_type;
+ protobuf_c_boolean has_body;
+ ProtobufCBinaryData body;
+};
+#define MERCURY_REPLY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&mercury_reply__descriptor) \
+ , 0, 0, NULL, 0, MERCURY_REPLY__CACHE_POLICY__CACHE_NO, 0, 0, 0, {0,NULL}, NULL, 0, {0,NULL} }
+
+
+struct _Header
+{
+ ProtobufCMessage base;
+ char *uri;
+ char *content_type;
+ char *method;
+ protobuf_c_boolean has_status_code;
+ int32_t status_code;
+ size_t n_user_fields;
+ UserField **user_fields;
+};
+#define HEADER__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&header__descriptor) \
+ , NULL, NULL, NULL, 0, 0, 0,NULL }
+
+
+struct _UserField
+{
+ ProtobufCMessage base;
+ char *key;
+ protobuf_c_boolean has_value;
+ ProtobufCBinaryData value;
+};
+#define USER_FIELD__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&user_field__descriptor) \
+ , NULL, 0, {0,NULL} }
+
+
+/* MercuryMultiGetRequest methods */
+void mercury_multi_get_request__init
+ (MercuryMultiGetRequest *message);
+size_t mercury_multi_get_request__get_packed_size
+ (const MercuryMultiGetRequest *message);
+size_t mercury_multi_get_request__pack
+ (const MercuryMultiGetRequest *message,
+ uint8_t *out);
+size_t mercury_multi_get_request__pack_to_buffer
+ (const MercuryMultiGetRequest *message,
+ ProtobufCBuffer *buffer);
+MercuryMultiGetRequest *
+ mercury_multi_get_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void mercury_multi_get_request__free_unpacked
+ (MercuryMultiGetRequest *message,
+ ProtobufCAllocator *allocator);
+/* MercuryMultiGetReply methods */
+void mercury_multi_get_reply__init
+ (MercuryMultiGetReply *message);
+size_t mercury_multi_get_reply__get_packed_size
+ (const MercuryMultiGetReply *message);
+size_t mercury_multi_get_reply__pack
+ (const MercuryMultiGetReply *message,
+ uint8_t *out);
+size_t mercury_multi_get_reply__pack_to_buffer
+ (const MercuryMultiGetReply *message,
+ ProtobufCBuffer *buffer);
+MercuryMultiGetReply *
+ mercury_multi_get_reply__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void mercury_multi_get_reply__free_unpacked
+ (MercuryMultiGetReply *message,
+ ProtobufCAllocator *allocator);
+/* MercuryRequest methods */
+void mercury_request__init
+ (MercuryRequest *message);
+size_t mercury_request__get_packed_size
+ (const MercuryRequest *message);
+size_t mercury_request__pack
+ (const MercuryRequest *message,
+ uint8_t *out);
+size_t mercury_request__pack_to_buffer
+ (const MercuryRequest *message,
+ ProtobufCBuffer *buffer);
+MercuryRequest *
+ mercury_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void mercury_request__free_unpacked
+ (MercuryRequest *message,
+ ProtobufCAllocator *allocator);
+/* MercuryReply methods */
+void mercury_reply__init
+ (MercuryReply *message);
+size_t mercury_reply__get_packed_size
+ (const MercuryReply *message);
+size_t mercury_reply__pack
+ (const MercuryReply *message,
+ uint8_t *out);
+size_t mercury_reply__pack_to_buffer
+ (const MercuryReply *message,
+ ProtobufCBuffer *buffer);
+MercuryReply *
+ mercury_reply__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void mercury_reply__free_unpacked
+ (MercuryReply *message,
+ ProtobufCAllocator *allocator);
+/* Header methods */
+void header__init
+ (Header *message);
+size_t header__get_packed_size
+ (const Header *message);
+size_t header__pack
+ (const Header *message,
+ uint8_t *out);
+size_t header__pack_to_buffer
+ (const Header *message,
+ ProtobufCBuffer *buffer);
+Header *
+ header__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void header__free_unpacked
+ (Header *message,
+ ProtobufCAllocator *allocator);
+/* UserField methods */
+void user_field__init
+ (UserField *message);
+size_t user_field__get_packed_size
+ (const UserField *message);
+size_t user_field__pack
+ (const UserField *message,
+ uint8_t *out);
+size_t user_field__pack_to_buffer
+ (const UserField *message,
+ ProtobufCBuffer *buffer);
+UserField *
+ user_field__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void user_field__free_unpacked
+ (UserField *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*MercuryMultiGetRequest_Closure)
+ (const MercuryMultiGetRequest *message,
+ void *closure_data);
+typedef void (*MercuryMultiGetReply_Closure)
+ (const MercuryMultiGetReply *message,
+ void *closure_data);
+typedef void (*MercuryRequest_Closure)
+ (const MercuryRequest *message,
+ void *closure_data);
+typedef void (*MercuryReply_Closure)
+ (const MercuryReply *message,
+ void *closure_data);
+typedef void (*Header_Closure)
+ (const Header *message,
+ void *closure_data);
+typedef void (*UserField_Closure)
+ (const UserField *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor mercury_multi_get_request__descriptor;
+extern const ProtobufCMessageDescriptor mercury_multi_get_reply__descriptor;
+extern const ProtobufCMessageDescriptor mercury_request__descriptor;
+extern const ProtobufCMessageDescriptor mercury_reply__descriptor;
+extern const ProtobufCEnumDescriptor mercury_reply__cache_policy__descriptor;
+extern const ProtobufCMessageDescriptor header__descriptor;
+extern const ProtobufCMessageDescriptor user_field__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_proto_2fmercury_2eproto__INCLUDED */
diff --git a/src/inputs/librespot-c/src/proto/mercury.proto b/src/inputs/librespot-c/src/proto/mercury.proto
new file mode 100644
index 00000000..2abdbcc4
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/mercury.proto
@@ -0,0 +1,46 @@
+syntax = "proto2";
+
+message MercuryMultiGetRequest {
+ repeated MercuryRequest request = 0x1;
+}
+
+message MercuryMultiGetReply {
+ repeated MercuryReply reply = 0x1;
+}
+
+message MercuryRequest {
+ optional string uri = 0x1;
+ optional string content_type = 0x2;
+ optional bytes body = 0x3;
+ optional bytes etag = 0x4;
+}
+
+message MercuryReply {
+ optional sint32 status_code = 0x1;
+ optional string status_message = 0x2;
+ optional CachePolicy cache_policy = 0x3;
+ enum CachePolicy {
+ CACHE_NO = 0x1;
+ CACHE_PRIVATE = 0x2;
+ CACHE_PUBLIC = 0x3;
+ }
+ optional sint32 ttl = 0x4;
+ optional bytes etag = 0x5;
+ optional string content_type = 0x6;
+ optional bytes body = 0x7;
+}
+
+
+message Header {
+ optional string uri = 0x01;
+ optional string content_type = 0x02;
+ optional string method = 0x03;
+ optional sint32 status_code = 0x04;
+ repeated UserField user_fields = 0x06;
+}
+
+message UserField {
+ optional string key = 0x01;
+ optional bytes value = 0x02;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/mergedprofile.proto b/src/inputs/librespot-c/src/proto/mergedprofile.proto
new file mode 100644
index 00000000..e283e1de
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/mergedprofile.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+message MergedProfileRequest {
+}
+
+message MergedProfileReply {
+ optional string username = 0x1;
+ optional string artistid = 0x2;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/metadata.pb-c.c b/src/inputs/librespot-c/src/proto/metadata.pb-c.c
new file mode 100644
index 00000000..d2b26880
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/metadata.pb-c.c
@@ -0,0 +1,3604 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/metadata.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include "metadata.pb-c.h"
+void top_tracks__init
+ (TopTracks *message)
+{
+ static const TopTracks init_value = TOP_TRACKS__INIT;
+ *message = init_value;
+}
+size_t top_tracks__get_packed_size
+ (const TopTracks *message)
+{
+ assert(message->base.descriptor == &top_tracks__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t top_tracks__pack
+ (const TopTracks *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &top_tracks__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t top_tracks__pack_to_buffer
+ (const TopTracks *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &top_tracks__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+TopTracks *
+ top_tracks__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (TopTracks *)
+ protobuf_c_message_unpack (&top_tracks__descriptor,
+ allocator, len, data);
+}
+void top_tracks__free_unpacked
+ (TopTracks *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &top_tracks__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void activity_period__init
+ (ActivityPeriod *message)
+{
+ static const ActivityPeriod init_value = ACTIVITY_PERIOD__INIT;
+ *message = init_value;
+}
+size_t activity_period__get_packed_size
+ (const ActivityPeriod *message)
+{
+ assert(message->base.descriptor == &activity_period__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t activity_period__pack
+ (const ActivityPeriod *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &activity_period__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t activity_period__pack_to_buffer
+ (const ActivityPeriod *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &activity_period__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ActivityPeriod *
+ activity_period__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ActivityPeriod *)
+ protobuf_c_message_unpack (&activity_period__descriptor,
+ allocator, len, data);
+}
+void activity_period__free_unpacked
+ (ActivityPeriod *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &activity_period__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void artist__init
+ (Artist *message)
+{
+ static const Artist init_value = ARTIST__INIT;
+ *message = init_value;
+}
+size_t artist__get_packed_size
+ (const Artist *message)
+{
+ assert(message->base.descriptor == &artist__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t artist__pack
+ (const Artist *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &artist__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t artist__pack_to_buffer
+ (const Artist *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &artist__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Artist *
+ artist__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Artist *)
+ protobuf_c_message_unpack (&artist__descriptor,
+ allocator, len, data);
+}
+void artist__free_unpacked
+ (Artist *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &artist__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void album_group__init
+ (AlbumGroup *message)
+{
+ static const AlbumGroup init_value = ALBUM_GROUP__INIT;
+ *message = init_value;
+}
+size_t album_group__get_packed_size
+ (const AlbumGroup *message)
+{
+ assert(message->base.descriptor == &album_group__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t album_group__pack
+ (const AlbumGroup *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &album_group__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t album_group__pack_to_buffer
+ (const AlbumGroup *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &album_group__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+AlbumGroup *
+ album_group__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (AlbumGroup *)
+ protobuf_c_message_unpack (&album_group__descriptor,
+ allocator, len, data);
+}
+void album_group__free_unpacked
+ (AlbumGroup *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &album_group__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void date__init
+ (Date *message)
+{
+ static const Date init_value = DATE__INIT;
+ *message = init_value;
+}
+size_t date__get_packed_size
+ (const Date *message)
+{
+ assert(message->base.descriptor == &date__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t date__pack
+ (const Date *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &date__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t date__pack_to_buffer
+ (const Date *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &date__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Date *
+ date__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Date *)
+ protobuf_c_message_unpack (&date__descriptor,
+ allocator, len, data);
+}
+void date__free_unpacked
+ (Date *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &date__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void album__init
+ (Album *message)
+{
+ static const Album init_value = ALBUM__INIT;
+ *message = init_value;
+}
+size_t album__get_packed_size
+ (const Album *message)
+{
+ assert(message->base.descriptor == &album__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t album__pack
+ (const Album *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &album__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t album__pack_to_buffer
+ (const Album *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &album__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Album *
+ album__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Album *)
+ protobuf_c_message_unpack (&album__descriptor,
+ allocator, len, data);
+}
+void album__free_unpacked
+ (Album *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &album__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void track__init
+ (Track *message)
+{
+ static const Track init_value = TRACK__INIT;
+ *message = init_value;
+}
+size_t track__get_packed_size
+ (const Track *message)
+{
+ assert(message->base.descriptor == &track__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t track__pack
+ (const Track *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &track__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t track__pack_to_buffer
+ (const Track *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &track__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Track *
+ track__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Track *)
+ protobuf_c_message_unpack (&track__descriptor,
+ allocator, len, data);
+}
+void track__free_unpacked
+ (Track *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &track__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void image__init
+ (Image *message)
+{
+ static const Image init_value = IMAGE__INIT;
+ *message = init_value;
+}
+size_t image__get_packed_size
+ (const Image *message)
+{
+ assert(message->base.descriptor == &image__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t image__pack
+ (const Image *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &image__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t image__pack_to_buffer
+ (const Image *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &image__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Image *
+ image__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Image *)
+ protobuf_c_message_unpack (&image__descriptor,
+ allocator, len, data);
+}
+void image__free_unpacked
+ (Image *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &image__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void image_group__init
+ (ImageGroup *message)
+{
+ static const ImageGroup init_value = IMAGE_GROUP__INIT;
+ *message = init_value;
+}
+size_t image_group__get_packed_size
+ (const ImageGroup *message)
+{
+ assert(message->base.descriptor == &image_group__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t image_group__pack
+ (const ImageGroup *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &image_group__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t image_group__pack_to_buffer
+ (const ImageGroup *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &image_group__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ImageGroup *
+ image_group__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ImageGroup *)
+ protobuf_c_message_unpack (&image_group__descriptor,
+ allocator, len, data);
+}
+void image_group__free_unpacked
+ (ImageGroup *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &image_group__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void biography__init
+ (Biography *message)
+{
+ static const Biography init_value = BIOGRAPHY__INIT;
+ *message = init_value;
+}
+size_t biography__get_packed_size
+ (const Biography *message)
+{
+ assert(message->base.descriptor == &biography__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t biography__pack
+ (const Biography *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &biography__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t biography__pack_to_buffer
+ (const Biography *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &biography__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Biography *
+ biography__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Biography *)
+ protobuf_c_message_unpack (&biography__descriptor,
+ allocator, len, data);
+}
+void biography__free_unpacked
+ (Biography *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &biography__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void disc__init
+ (Disc *message)
+{
+ static const Disc init_value = DISC__INIT;
+ *message = init_value;
+}
+size_t disc__get_packed_size
+ (const Disc *message)
+{
+ assert(message->base.descriptor == &disc__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t disc__pack
+ (const Disc *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &disc__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t disc__pack_to_buffer
+ (const Disc *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &disc__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Disc *
+ disc__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Disc *)
+ protobuf_c_message_unpack (&disc__descriptor,
+ allocator, len, data);
+}
+void disc__free_unpacked
+ (Disc *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &disc__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void copyright__init
+ (Copyright *message)
+{
+ static const Copyright init_value = COPYRIGHT__INIT;
+ *message = init_value;
+}
+size_t copyright__get_packed_size
+ (const Copyright *message)
+{
+ assert(message->base.descriptor == ©right__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t copyright__pack
+ (const Copyright *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == ©right__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t copyright__pack_to_buffer
+ (const Copyright *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == ©right__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Copyright *
+ copyright__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Copyright *)
+ protobuf_c_message_unpack (©right__descriptor,
+ allocator, len, data);
+}
+void copyright__free_unpacked
+ (Copyright *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == ©right__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void restriction__init
+ (Restriction *message)
+{
+ static const Restriction init_value = RESTRICTION__INIT;
+ *message = init_value;
+}
+size_t restriction__get_packed_size
+ (const Restriction *message)
+{
+ assert(message->base.descriptor == &restriction__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t restriction__pack
+ (const Restriction *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &restriction__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t restriction__pack_to_buffer
+ (const Restriction *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &restriction__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Restriction *
+ restriction__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Restriction *)
+ protobuf_c_message_unpack (&restriction__descriptor,
+ allocator, len, data);
+}
+void restriction__free_unpacked
+ (Restriction *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &restriction__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void availability__init
+ (Availability *message)
+{
+ static const Availability init_value = AVAILABILITY__INIT;
+ *message = init_value;
+}
+size_t availability__get_packed_size
+ (const Availability *message)
+{
+ assert(message->base.descriptor == &availability__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t availability__pack
+ (const Availability *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &availability__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t availability__pack_to_buffer
+ (const Availability *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &availability__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Availability *
+ availability__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Availability *)
+ protobuf_c_message_unpack (&availability__descriptor,
+ allocator, len, data);
+}
+void availability__free_unpacked
+ (Availability *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &availability__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void sale_period__init
+ (SalePeriod *message)
+{
+ static const SalePeriod init_value = SALE_PERIOD__INIT;
+ *message = init_value;
+}
+size_t sale_period__get_packed_size
+ (const SalePeriod *message)
+{
+ assert(message->base.descriptor == &sale_period__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t sale_period__pack
+ (const SalePeriod *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &sale_period__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t sale_period__pack_to_buffer
+ (const SalePeriod *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &sale_period__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+SalePeriod *
+ sale_period__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (SalePeriod *)
+ protobuf_c_message_unpack (&sale_period__descriptor,
+ allocator, len, data);
+}
+void sale_period__free_unpacked
+ (SalePeriod *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &sale_period__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void external_id__init
+ (ExternalId *message)
+{
+ static const ExternalId init_value = EXTERNAL_ID__INIT;
+ *message = init_value;
+}
+size_t external_id__get_packed_size
+ (const ExternalId *message)
+{
+ assert(message->base.descriptor == &external_id__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t external_id__pack
+ (const ExternalId *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &external_id__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t external_id__pack_to_buffer
+ (const ExternalId *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &external_id__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+ExternalId *
+ external_id__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (ExternalId *)
+ protobuf_c_message_unpack (&external_id__descriptor,
+ allocator, len, data);
+}
+void external_id__free_unpacked
+ (ExternalId *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &external_id__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void audio_file__init
+ (AudioFile *message)
+{
+ static const AudioFile init_value = AUDIO_FILE__INIT;
+ *message = init_value;
+}
+size_t audio_file__get_packed_size
+ (const AudioFile *message)
+{
+ assert(message->base.descriptor == &audio_file__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t audio_file__pack
+ (const AudioFile *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &audio_file__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t audio_file__pack_to_buffer
+ (const AudioFile *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &audio_file__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+AudioFile *
+ audio_file__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (AudioFile *)
+ protobuf_c_message_unpack (&audio_file__descriptor,
+ allocator, len, data);
+}
+void audio_file__free_unpacked
+ (AudioFile *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &audio_file__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void video_file__init
+ (VideoFile *message)
+{
+ static const VideoFile init_value = VIDEO_FILE__INIT;
+ *message = init_value;
+}
+size_t video_file__get_packed_size
+ (const VideoFile *message)
+{
+ assert(message->base.descriptor == &video_file__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t video_file__pack
+ (const VideoFile *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &video_file__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t video_file__pack_to_buffer
+ (const VideoFile *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &video_file__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+VideoFile *
+ video_file__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (VideoFile *)
+ protobuf_c_message_unpack (&video_file__descriptor,
+ allocator, len, data);
+}
+void video_file__free_unpacked
+ (VideoFile *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &video_file__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void show__init
+ (Show *message)
+{
+ static const Show init_value = SHOW__INIT;
+ *message = init_value;
+}
+size_t show__get_packed_size
+ (const Show *message)
+{
+ assert(message->base.descriptor == &show__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t show__pack
+ (const Show *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &show__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t show__pack_to_buffer
+ (const Show *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &show__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Show *
+ show__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Show *)
+ protobuf_c_message_unpack (&show__descriptor,
+ allocator, len, data);
+}
+void show__free_unpacked
+ (Show *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &show__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void episode__init
+ (Episode *message)
+{
+ static const Episode init_value = EPISODE__INIT;
+ *message = init_value;
+}
+size_t episode__get_packed_size
+ (const Episode *message)
+{
+ assert(message->base.descriptor == &episode__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t episode__pack
+ (const Episode *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &episode__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t episode__pack_to_buffer
+ (const Episode *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &episode__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Episode *
+ episode__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Episode *)
+ protobuf_c_message_unpack (&episode__descriptor,
+ allocator, len, data);
+}
+void episode__free_unpacked
+ (Episode *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &episode__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void category__init
+ (Category *message)
+{
+ static const Category init_value = CATEGORY__INIT;
+ *message = init_value;
+}
+size_t category__get_packed_size
+ (const Category *message)
+{
+ assert(message->base.descriptor == &category__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t category__pack
+ (const Category *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &category__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t category__pack_to_buffer
+ (const Category *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &category__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Category *
+ category__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (Category *)
+ protobuf_c_message_unpack (&category__descriptor,
+ allocator, len, data);
+}
+void category__free_unpacked
+ (Category *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &category__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+void original_audio__init
+ (OriginalAudio *message)
+{
+ static const OriginalAudio init_value = ORIGINAL_AUDIO__INIT;
+ *message = init_value;
+}
+size_t original_audio__get_packed_size
+ (const OriginalAudio *message)
+{
+ assert(message->base.descriptor == &original_audio__descriptor);
+ return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t original_audio__pack
+ (const OriginalAudio *message,
+ uint8_t *out)
+{
+ assert(message->base.descriptor == &original_audio__descriptor);
+ return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t original_audio__pack_to_buffer
+ (const OriginalAudio *message,
+ ProtobufCBuffer *buffer)
+{
+ assert(message->base.descriptor == &original_audio__descriptor);
+ return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+OriginalAudio *
+ original_audio__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data)
+{
+ return (OriginalAudio *)
+ protobuf_c_message_unpack (&original_audio__descriptor,
+ allocator, len, data);
+}
+void original_audio__free_unpacked
+ (OriginalAudio *message,
+ ProtobufCAllocator *allocator)
+{
+ if(!message)
+ return;
+ assert(message->base.descriptor == &original_audio__descriptor);
+ protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
+static const ProtobufCFieldDescriptor top_tracks__field_descriptors[2] =
+{
+ {
+ "country",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(TopTracks, country),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "track",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(TopTracks, n_track),
+ offsetof(TopTracks, track),
+ &track__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned top_tracks__field_indices_by_name[] = {
+ 0, /* field[0] = country */
+ 1, /* field[1] = track */
+};
+static const ProtobufCIntRange top_tracks__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor top_tracks__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "TopTracks",
+ "TopTracks",
+ "TopTracks",
+ "",
+ sizeof(TopTracks),
+ 2,
+ top_tracks__field_descriptors,
+ top_tracks__field_indices_by_name,
+ 1, top_tracks__number_ranges,
+ (ProtobufCMessageInit) top_tracks__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor activity_period__field_descriptors[3] =
+{
+ {
+ "start_year",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(ActivityPeriod, has_start_year),
+ offsetof(ActivityPeriod, start_year),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "end_year",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(ActivityPeriod, has_end_year),
+ offsetof(ActivityPeriod, end_year),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "decade",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(ActivityPeriod, has_decade),
+ offsetof(ActivityPeriod, decade),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned activity_period__field_indices_by_name[] = {
+ 2, /* field[2] = decade */
+ 1, /* field[1] = end_year */
+ 0, /* field[0] = start_year */
+};
+static const ProtobufCIntRange activity_period__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor activity_period__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ActivityPeriod",
+ "ActivityPeriod",
+ "ActivityPeriod",
+ "",
+ sizeof(ActivityPeriod),
+ 3,
+ activity_period__field_descriptors,
+ activity_period__field_indices_by_name,
+ 1, activity_period__number_ranges,
+ (ProtobufCMessageInit) activity_period__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor artist__field_descriptors[17] =
+{
+ {
+ "gid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Artist, has_gid),
+ offsetof(Artist, gid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Artist, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "popularity",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Artist, has_popularity),
+ offsetof(Artist, popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "top_track",
+ 4,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_top_track),
+ offsetof(Artist, top_track),
+ &top_tracks__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "album_group",
+ 5,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_album_group),
+ offsetof(Artist, album_group),
+ &album_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "single_group",
+ 6,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_single_group),
+ offsetof(Artist, single_group),
+ &album_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "compilation_group",
+ 7,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_compilation_group),
+ offsetof(Artist, compilation_group),
+ &album_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "appears_on_group",
+ 8,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_appears_on_group),
+ offsetof(Artist, appears_on_group),
+ &album_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "genre",
+ 9,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Artist, n_genre),
+ offsetof(Artist, genre),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "external_id",
+ 10,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_external_id),
+ offsetof(Artist, external_id),
+ &external_id__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "portrait",
+ 11,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_portrait),
+ offsetof(Artist, portrait),
+ &image__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "biography",
+ 12,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_biography),
+ offsetof(Artist, biography),
+ &biography__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "activity_period",
+ 13,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_activity_period),
+ offsetof(Artist, activity_period),
+ &activity_period__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "restriction",
+ 14,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_restriction),
+ offsetof(Artist, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "related",
+ 15,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Artist, n_related),
+ offsetof(Artist, related),
+ &artist__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "is_portrait_album_cover",
+ 16,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Artist, has_is_portrait_album_cover),
+ offsetof(Artist, is_portrait_album_cover),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "portrait_group",
+ 17,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Artist, portrait_group),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned artist__field_indices_by_name[] = {
+ 12, /* field[12] = activity_period */
+ 4, /* field[4] = album_group */
+ 7, /* field[7] = appears_on_group */
+ 11, /* field[11] = biography */
+ 6, /* field[6] = compilation_group */
+ 9, /* field[9] = external_id */
+ 8, /* field[8] = genre */
+ 0, /* field[0] = gid */
+ 15, /* field[15] = is_portrait_album_cover */
+ 1, /* field[1] = name */
+ 2, /* field[2] = popularity */
+ 10, /* field[10] = portrait */
+ 16, /* field[16] = portrait_group */
+ 14, /* field[14] = related */
+ 13, /* field[13] = restriction */
+ 5, /* field[5] = single_group */
+ 3, /* field[3] = top_track */
+};
+static const ProtobufCIntRange artist__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 17 }
+};
+const ProtobufCMessageDescriptor artist__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Artist",
+ "Artist",
+ "Artist",
+ "",
+ sizeof(Artist),
+ 17,
+ artist__field_descriptors,
+ artist__field_indices_by_name,
+ 1, artist__number_ranges,
+ (ProtobufCMessageInit) artist__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor album_group__field_descriptors[1] =
+{
+ {
+ "album",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(AlbumGroup, n_album),
+ offsetof(AlbumGroup, album),
+ &album__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned album_group__field_indices_by_name[] = {
+ 0, /* field[0] = album */
+};
+static const ProtobufCIntRange album_group__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor album_group__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "AlbumGroup",
+ "AlbumGroup",
+ "AlbumGroup",
+ "",
+ sizeof(AlbumGroup),
+ 1,
+ album_group__field_descriptors,
+ album_group__field_indices_by_name,
+ 1, album_group__number_ranges,
+ (ProtobufCMessageInit) album_group__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor date__field_descriptors[5] =
+{
+ {
+ "year",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Date, has_year),
+ offsetof(Date, year),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "month",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Date, has_month),
+ offsetof(Date, month),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "day",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Date, has_day),
+ offsetof(Date, day),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "hour",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Date, has_hour),
+ offsetof(Date, hour),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "minute",
+ 5,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Date, has_minute),
+ offsetof(Date, minute),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned date__field_indices_by_name[] = {
+ 2, /* field[2] = day */
+ 3, /* field[3] = hour */
+ 4, /* field[4] = minute */
+ 1, /* field[1] = month */
+ 0, /* field[0] = year */
+};
+static const ProtobufCIntRange date__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 5 }
+};
+const ProtobufCMessageDescriptor date__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Date",
+ "Date",
+ "Date",
+ "",
+ sizeof(Date),
+ 5,
+ date__field_descriptors,
+ date__field_indices_by_name,
+ 1, date__number_ranges,
+ (ProtobufCMessageInit) date__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue album__type__enum_values_by_number[4] =
+{
+ { "ALBUM", "ALBUM__TYPE__ALBUM", 1 },
+ { "SINGLE", "ALBUM__TYPE__SINGLE", 2 },
+ { "COMPILATION", "ALBUM__TYPE__COMPILATION", 3 },
+ { "EP", "ALBUM__TYPE__EP", 4 },
+};
+static const ProtobufCIntRange album__type__value_ranges[] = {
+{1, 0},{0, 4}
+};
+static const ProtobufCEnumValueIndex album__type__enum_values_by_name[4] =
+{
+ { "ALBUM", 0 },
+ { "COMPILATION", 2 },
+ { "EP", 3 },
+ { "SINGLE", 1 },
+};
+const ProtobufCEnumDescriptor album__type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Album.Type",
+ "Type",
+ "Album__Type",
+ "",
+ 4,
+ album__type__enum_values_by_number,
+ 4,
+ album__type__enum_values_by_name,
+ 1,
+ album__type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor album__field_descriptors[17] =
+{
+ {
+ "gid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Album, has_gid),
+ offsetof(Album, gid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Album, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "artist",
+ 3,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_artist),
+ offsetof(Album, artist),
+ &artist__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "typ",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Album, has_typ),
+ offsetof(Album, typ),
+ &album__type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "label",
+ 5,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Album, label),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "date",
+ 6,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Album, date),
+ &date__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "popularity",
+ 7,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Album, has_popularity),
+ offsetof(Album, popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "genre",
+ 8,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Album, n_genre),
+ offsetof(Album, genre),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cover",
+ 9,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_cover),
+ offsetof(Album, cover),
+ &image__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "external_id",
+ 10,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_external_id),
+ offsetof(Album, external_id),
+ &external_id__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "disc",
+ 11,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_disc),
+ offsetof(Album, disc),
+ &disc__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "review",
+ 12,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Album, n_review),
+ offsetof(Album, review),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "copyright",
+ 13,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_copyright),
+ offsetof(Album, copyright),
+ ©right__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "restriction",
+ 14,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_restriction),
+ offsetof(Album, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "related",
+ 15,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_related),
+ offsetof(Album, related),
+ &album__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sale_period",
+ 16,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Album, n_sale_period),
+ offsetof(Album, sale_period),
+ &sale_period__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "cover_group",
+ 17,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Album, cover_group),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned album__field_indices_by_name[] = {
+ 2, /* field[2] = artist */
+ 12, /* field[12] = copyright */
+ 8, /* field[8] = cover */
+ 16, /* field[16] = cover_group */
+ 5, /* field[5] = date */
+ 10, /* field[10] = disc */
+ 9, /* field[9] = external_id */
+ 7, /* field[7] = genre */
+ 0, /* field[0] = gid */
+ 4, /* field[4] = label */
+ 1, /* field[1] = name */
+ 6, /* field[6] = popularity */
+ 14, /* field[14] = related */
+ 13, /* field[13] = restriction */
+ 11, /* field[11] = review */
+ 15, /* field[15] = sale_period */
+ 3, /* field[3] = typ */
+};
+static const ProtobufCIntRange album__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 17 }
+};
+const ProtobufCMessageDescriptor album__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Album",
+ "Album",
+ "Album",
+ "",
+ sizeof(Album),
+ 17,
+ album__field_descriptors,
+ album__field_indices_by_name,
+ 1, album__number_ranges,
+ (ProtobufCMessageInit) album__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor track__field_descriptors[15] =
+{
+ {
+ "gid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Track, has_gid),
+ offsetof(Track, gid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Track, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "album",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Track, album),
+ &album__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "artist",
+ 4,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_artist),
+ offsetof(Track, artist),
+ &artist__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "number",
+ 5,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Track, has_number),
+ offsetof(Track, number),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "disc_number",
+ 6,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Track, has_disc_number),
+ offsetof(Track, disc_number),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "duration",
+ 7,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Track, has_duration),
+ offsetof(Track, duration),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "popularity",
+ 8,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Track, has_popularity),
+ offsetof(Track, popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "explicit",
+ 9,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Track, has_explicit_),
+ offsetof(Track, explicit_),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "external_id",
+ 10,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_external_id),
+ offsetof(Track, external_id),
+ &external_id__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "restriction",
+ 11,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_restriction),
+ offsetof(Track, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "file",
+ 12,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_file),
+ offsetof(Track, file),
+ &audio_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "alternative",
+ 13,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_alternative),
+ offsetof(Track, alternative),
+ &track__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "sale_period",
+ 14,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_sale_period),
+ offsetof(Track, sale_period),
+ &sale_period__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "preview",
+ 15,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Track, n_preview),
+ offsetof(Track, preview),
+ &audio_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned track__field_indices_by_name[] = {
+ 2, /* field[2] = album */
+ 12, /* field[12] = alternative */
+ 3, /* field[3] = artist */
+ 5, /* field[5] = disc_number */
+ 6, /* field[6] = duration */
+ 8, /* field[8] = explicit */
+ 9, /* field[9] = external_id */
+ 11, /* field[11] = file */
+ 0, /* field[0] = gid */
+ 1, /* field[1] = name */
+ 4, /* field[4] = number */
+ 7, /* field[7] = popularity */
+ 14, /* field[14] = preview */
+ 10, /* field[10] = restriction */
+ 13, /* field[13] = sale_period */
+};
+static const ProtobufCIntRange track__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 15 }
+};
+const ProtobufCMessageDescriptor track__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Track",
+ "Track",
+ "Track",
+ "",
+ sizeof(Track),
+ 15,
+ track__field_descriptors,
+ track__field_indices_by_name,
+ 1, track__number_ranges,
+ (ProtobufCMessageInit) track__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue image__size__enum_values_by_number[4] =
+{
+ { "DEFAULT", "IMAGE__SIZE__DEFAULT", 0 },
+ { "SMALL", "IMAGE__SIZE__SMALL", 1 },
+ { "LARGE", "IMAGE__SIZE__LARGE", 2 },
+ { "XLARGE", "IMAGE__SIZE__XLARGE", 3 },
+};
+static const ProtobufCIntRange image__size__value_ranges[] = {
+{0, 0},{0, 4}
+};
+static const ProtobufCEnumValueIndex image__size__enum_values_by_name[4] =
+{
+ { "DEFAULT", 0 },
+ { "LARGE", 2 },
+ { "SMALL", 1 },
+ { "XLARGE", 3 },
+};
+const ProtobufCEnumDescriptor image__size__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Image.Size",
+ "Size",
+ "Image__Size",
+ "",
+ 4,
+ image__size__enum_values_by_number,
+ 4,
+ image__size__enum_values_by_name,
+ 1,
+ image__size__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor image__field_descriptors[4] =
+{
+ {
+ "file_id",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Image, has_file_id),
+ offsetof(Image, file_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "size",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Image, has_size),
+ offsetof(Image, size),
+ &image__size__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "width",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Image, has_width),
+ offsetof(Image, width),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "height",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Image, has_height),
+ offsetof(Image, height),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned image__field_indices_by_name[] = {
+ 0, /* field[0] = file_id */
+ 3, /* field[3] = height */
+ 1, /* field[1] = size */
+ 2, /* field[2] = width */
+};
+static const ProtobufCIntRange image__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 4 }
+};
+const ProtobufCMessageDescriptor image__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Image",
+ "Image",
+ "Image",
+ "",
+ sizeof(Image),
+ 4,
+ image__field_descriptors,
+ image__field_indices_by_name,
+ 1, image__number_ranges,
+ (ProtobufCMessageInit) image__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor image_group__field_descriptors[1] =
+{
+ {
+ "image",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(ImageGroup, n_image),
+ offsetof(ImageGroup, image),
+ &image__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned image_group__field_indices_by_name[] = {
+ 0, /* field[0] = image */
+};
+static const ProtobufCIntRange image_group__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor image_group__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ImageGroup",
+ "ImageGroup",
+ "ImageGroup",
+ "",
+ sizeof(ImageGroup),
+ 1,
+ image_group__field_descriptors,
+ image_group__field_indices_by_name,
+ 1, image_group__number_ranges,
+ (ProtobufCMessageInit) image_group__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor biography__field_descriptors[3] =
+{
+ {
+ "text",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Biography, text),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "portrait",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Biography, n_portrait),
+ offsetof(Biography, portrait),
+ &image__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "portrait_group",
+ 3,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Biography, n_portrait_group),
+ offsetof(Biography, portrait_group),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned biography__field_indices_by_name[] = {
+ 1, /* field[1] = portrait */
+ 2, /* field[2] = portrait_group */
+ 0, /* field[0] = text */
+};
+static const ProtobufCIntRange biography__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor biography__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Biography",
+ "Biography",
+ "Biography",
+ "",
+ sizeof(Biography),
+ 3,
+ biography__field_descriptors,
+ biography__field_indices_by_name,
+ 1, biography__number_ranges,
+ (ProtobufCMessageInit) biography__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor disc__field_descriptors[3] =
+{
+ {
+ "number",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Disc, has_number),
+ offsetof(Disc, number),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Disc, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "track",
+ 3,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Disc, n_track),
+ offsetof(Disc, track),
+ &track__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned disc__field_indices_by_name[] = {
+ 1, /* field[1] = name */
+ 0, /* field[0] = number */
+ 2, /* field[2] = track */
+};
+static const ProtobufCIntRange disc__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor disc__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Disc",
+ "Disc",
+ "Disc",
+ "",
+ sizeof(Disc),
+ 3,
+ disc__field_descriptors,
+ disc__field_indices_by_name,
+ 1, disc__number_ranges,
+ (ProtobufCMessageInit) disc__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue copyright__type__enum_values_by_number[2] =
+{
+ { "P", "COPYRIGHT__TYPE__P", 0 },
+ { "C", "COPYRIGHT__TYPE__C", 1 },
+};
+static const ProtobufCIntRange copyright__type__value_ranges[] = {
+{0, 0},{0, 2}
+};
+static const ProtobufCEnumValueIndex copyright__type__enum_values_by_name[2] =
+{
+ { "C", 1 },
+ { "P", 0 },
+};
+const ProtobufCEnumDescriptor copyright__type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Copyright.Type",
+ "Type",
+ "Copyright__Type",
+ "",
+ 2,
+ copyright__type__enum_values_by_number,
+ 2,
+ copyright__type__enum_values_by_name,
+ 1,
+ copyright__type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor copyright__field_descriptors[2] =
+{
+ {
+ "typ",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Copyright, has_typ),
+ offsetof(Copyright, typ),
+ ©right__type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "text",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Copyright, text),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned copyright__field_indices_by_name[] = {
+ 1, /* field[1] = text */
+ 0, /* field[0] = typ */
+};
+static const ProtobufCIntRange copyright__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor copyright__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Copyright",
+ "Copyright",
+ "Copyright",
+ "",
+ sizeof(Copyright),
+ 2,
+ copyright__field_descriptors,
+ copyright__field_indices_by_name,
+ 1, copyright__number_ranges,
+ (ProtobufCMessageInit) copyright__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue restriction__catalogue__enum_values_by_number[5] =
+{
+ { "AD", "RESTRICTION__CATALOGUE__AD", 0 },
+ { "SUBSCRIPTION", "RESTRICTION__CATALOGUE__SUBSCRIPTION", 1 },
+ { "CATALOGUE_ALL", "RESTRICTION__CATALOGUE__CATALOGUE_ALL", 2 },
+ { "SHUFFLE", "RESTRICTION__CATALOGUE__SHUFFLE", 3 },
+ { "COMMERCIAL", "RESTRICTION__CATALOGUE__COMMERCIAL", 4 },
+};
+static const ProtobufCIntRange restriction__catalogue__value_ranges[] = {
+{0, 0},{0, 5}
+};
+static const ProtobufCEnumValueIndex restriction__catalogue__enum_values_by_name[5] =
+{
+ { "AD", 0 },
+ { "CATALOGUE_ALL", 2 },
+ { "COMMERCIAL", 4 },
+ { "SHUFFLE", 3 },
+ { "SUBSCRIPTION", 1 },
+};
+const ProtobufCEnumDescriptor restriction__catalogue__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Restriction.Catalogue",
+ "Catalogue",
+ "Restriction__Catalogue",
+ "",
+ 5,
+ restriction__catalogue__enum_values_by_number,
+ 5,
+ restriction__catalogue__enum_values_by_name,
+ 1,
+ restriction__catalogue__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue restriction__type__enum_values_by_number[1] =
+{
+ { "STREAMING", "RESTRICTION__TYPE__STREAMING", 0 },
+};
+static const ProtobufCIntRange restriction__type__value_ranges[] = {
+{0, 0},{0, 1}
+};
+static const ProtobufCEnumValueIndex restriction__type__enum_values_by_name[1] =
+{
+ { "STREAMING", 0 },
+};
+const ProtobufCEnumDescriptor restriction__type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Restriction.Type",
+ "Type",
+ "Restriction__Type",
+ "",
+ 1,
+ restriction__type__enum_values_by_number,
+ 1,
+ restriction__type__enum_values_by_name,
+ 1,
+ restriction__type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor restriction__field_descriptors[5] =
+{
+ {
+ "catalogue",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Restriction, n_catalogue),
+ offsetof(Restriction, catalogue),
+ &restriction__catalogue__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "countries_allowed",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Restriction, countries_allowed),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "countries_forbidden",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Restriction, countries_forbidden),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "typ",
+ 4,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Restriction, has_typ),
+ offsetof(Restriction, typ),
+ &restriction__type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "catalogue_str",
+ 5,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Restriction, n_catalogue_str),
+ offsetof(Restriction, catalogue_str),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned restriction__field_indices_by_name[] = {
+ 0, /* field[0] = catalogue */
+ 4, /* field[4] = catalogue_str */
+ 1, /* field[1] = countries_allowed */
+ 2, /* field[2] = countries_forbidden */
+ 3, /* field[3] = typ */
+};
+static const ProtobufCIntRange restriction__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 5 }
+};
+const ProtobufCMessageDescriptor restriction__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Restriction",
+ "Restriction",
+ "Restriction",
+ "",
+ sizeof(Restriction),
+ 5,
+ restriction__field_descriptors,
+ restriction__field_indices_by_name,
+ 1, restriction__number_ranges,
+ (ProtobufCMessageInit) restriction__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor availability__field_descriptors[2] =
+{
+ {
+ "catalogue_str",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Availability, n_catalogue_str),
+ offsetof(Availability, catalogue_str),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "start",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Availability, start),
+ &date__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned availability__field_indices_by_name[] = {
+ 0, /* field[0] = catalogue_str */
+ 1, /* field[1] = start */
+};
+static const ProtobufCIntRange availability__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor availability__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Availability",
+ "Availability",
+ "Availability",
+ "",
+ sizeof(Availability),
+ 2,
+ availability__field_descriptors,
+ availability__field_indices_by_name,
+ 1, availability__number_ranges,
+ (ProtobufCMessageInit) availability__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor sale_period__field_descriptors[3] =
+{
+ {
+ "restriction",
+ 1,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(SalePeriod, n_restriction),
+ offsetof(SalePeriod, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "start",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(SalePeriod, start),
+ &date__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "end",
+ 3,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(SalePeriod, end),
+ &date__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned sale_period__field_indices_by_name[] = {
+ 2, /* field[2] = end */
+ 0, /* field[0] = restriction */
+ 1, /* field[1] = start */
+};
+static const ProtobufCIntRange sale_period__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 3 }
+};
+const ProtobufCMessageDescriptor sale_period__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "SalePeriod",
+ "SalePeriod",
+ "SalePeriod",
+ "",
+ sizeof(SalePeriod),
+ 3,
+ sale_period__field_descriptors,
+ sale_period__field_indices_by_name,
+ 1, sale_period__number_ranges,
+ (ProtobufCMessageInit) sale_period__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor external_id__field_descriptors[2] =
+{
+ {
+ "typ",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ExternalId, typ),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "id",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(ExternalId, id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned external_id__field_indices_by_name[] = {
+ 1, /* field[1] = id */
+ 0, /* field[0] = typ */
+};
+static const ProtobufCIntRange external_id__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor external_id__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "ExternalId",
+ "ExternalId",
+ "ExternalId",
+ "",
+ sizeof(ExternalId),
+ 2,
+ external_id__field_descriptors,
+ external_id__field_indices_by_name,
+ 1, external_id__number_ranges,
+ (ProtobufCMessageInit) external_id__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue audio_file__format__enum_values_by_number[14] =
+{
+ { "OGG_VORBIS_96", "AUDIO_FILE__FORMAT__OGG_VORBIS_96", 0 },
+ { "OGG_VORBIS_160", "AUDIO_FILE__FORMAT__OGG_VORBIS_160", 1 },
+ { "OGG_VORBIS_320", "AUDIO_FILE__FORMAT__OGG_VORBIS_320", 2 },
+ { "MP3_256", "AUDIO_FILE__FORMAT__MP3_256", 3 },
+ { "MP3_320", "AUDIO_FILE__FORMAT__MP3_320", 4 },
+ { "MP3_160", "AUDIO_FILE__FORMAT__MP3_160", 5 },
+ { "MP3_96", "AUDIO_FILE__FORMAT__MP3_96", 6 },
+ { "MP3_160_ENC", "AUDIO_FILE__FORMAT__MP3_160_ENC", 7 },
+ { "MP4_128_DUAL", "AUDIO_FILE__FORMAT__MP4_128_DUAL", 8 },
+ { "OTHER3", "AUDIO_FILE__FORMAT__OTHER3", 9 },
+ { "AAC_160", "AUDIO_FILE__FORMAT__AAC_160", 10 },
+ { "AAC_320", "AUDIO_FILE__FORMAT__AAC_320", 11 },
+ { "MP4_128", "AUDIO_FILE__FORMAT__MP4_128", 12 },
+ { "OTHER5", "AUDIO_FILE__FORMAT__OTHER5", 13 },
+};
+static const ProtobufCIntRange audio_file__format__value_ranges[] = {
+{0, 0},{0, 14}
+};
+static const ProtobufCEnumValueIndex audio_file__format__enum_values_by_name[14] =
+{
+ { "AAC_160", 10 },
+ { "AAC_320", 11 },
+ { "MP3_160", 5 },
+ { "MP3_160_ENC", 7 },
+ { "MP3_256", 3 },
+ { "MP3_320", 4 },
+ { "MP3_96", 6 },
+ { "MP4_128", 12 },
+ { "MP4_128_DUAL", 8 },
+ { "OGG_VORBIS_160", 1 },
+ { "OGG_VORBIS_320", 2 },
+ { "OGG_VORBIS_96", 0 },
+ { "OTHER3", 9 },
+ { "OTHER5", 13 },
+};
+const ProtobufCEnumDescriptor audio_file__format__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "AudioFile.Format",
+ "Format",
+ "AudioFile__Format",
+ "",
+ 14,
+ audio_file__format__enum_values_by_number,
+ 14,
+ audio_file__format__enum_values_by_name,
+ 1,
+ audio_file__format__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor audio_file__field_descriptors[2] =
+{
+ {
+ "file_id",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(AudioFile, has_file_id),
+ offsetof(AudioFile, file_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "format",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(AudioFile, has_format),
+ offsetof(AudioFile, format),
+ &audio_file__format__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned audio_file__field_indices_by_name[] = {
+ 0, /* field[0] = file_id */
+ 1, /* field[1] = format */
+};
+static const ProtobufCIntRange audio_file__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor audio_file__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "AudioFile",
+ "AudioFile",
+ "AudioFile",
+ "",
+ sizeof(AudioFile),
+ 2,
+ audio_file__field_descriptors,
+ audio_file__field_indices_by_name,
+ 1, audio_file__number_ranges,
+ (ProtobufCMessageInit) audio_file__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor video_file__field_descriptors[1] =
+{
+ {
+ "file_id",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(VideoFile, has_file_id),
+ offsetof(VideoFile, file_id),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned video_file__field_indices_by_name[] = {
+ 0, /* field[0] = file_id */
+};
+static const ProtobufCIntRange video_file__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor video_file__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "VideoFile",
+ "VideoFile",
+ "VideoFile",
+ "",
+ sizeof(VideoFile),
+ 1,
+ video_file__field_descriptors,
+ video_file__field_indices_by_name,
+ 1, video_file__number_ranges,
+ (ProtobufCMessageInit) video_file__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCEnumValue show__media_type__enum_values_by_number[3] =
+{
+ { "MIXED", "SHOW__MEDIA_TYPE__MIXED", 0 },
+ { "AUDIO", "SHOW__MEDIA_TYPE__AUDIO", 1 },
+ { "VIDEO", "SHOW__MEDIA_TYPE__VIDEO", 2 },
+};
+static const ProtobufCIntRange show__media_type__value_ranges[] = {
+{0, 0},{0, 3}
+};
+static const ProtobufCEnumValueIndex show__media_type__enum_values_by_name[3] =
+{
+ { "AUDIO", 1 },
+ { "MIXED", 0 },
+ { "VIDEO", 2 },
+};
+const ProtobufCEnumDescriptor show__media_type__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Show.MediaType",
+ "MediaType",
+ "Show__MediaType",
+ "",
+ 3,
+ show__media_type__enum_values_by_number,
+ 3,
+ show__media_type__enum_values_by_name,
+ 1,
+ show__media_type__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue show__consumption_order__enum_values_by_number[3] =
+{
+ { "SEQUENTIAL", "SHOW__CONSUMPTION_ORDER__SEQUENTIAL", 1 },
+ { "EPISODIC", "SHOW__CONSUMPTION_ORDER__EPISODIC", 2 },
+ { "RECENT", "SHOW__CONSUMPTION_ORDER__RECENT", 3 },
+};
+static const ProtobufCIntRange show__consumption_order__value_ranges[] = {
+{1, 0},{0, 3}
+};
+static const ProtobufCEnumValueIndex show__consumption_order__enum_values_by_name[3] =
+{
+ { "EPISODIC", 1 },
+ { "RECENT", 2 },
+ { "SEQUENTIAL", 0 },
+};
+const ProtobufCEnumDescriptor show__consumption_order__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Show.ConsumptionOrder",
+ "ConsumptionOrder",
+ "Show__ConsumptionOrder",
+ "",
+ 3,
+ show__consumption_order__enum_values_by_number,
+ 3,
+ show__consumption_order__enum_values_by_name,
+ 1,
+ show__consumption_order__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCEnumValue show__passthrough_enum__enum_values_by_number[3] =
+{
+ { "UNKNOWN", "SHOW__PASSTHROUGH_ENUM__UNKNOWN", 0 },
+ { "NONE", "SHOW__PASSTHROUGH_ENUM__NONE", 1 },
+ { "ALLOWED", "SHOW__PASSTHROUGH_ENUM__ALLOWED", 2 },
+};
+static const ProtobufCIntRange show__passthrough_enum__value_ranges[] = {
+{0, 0},{0, 3}
+};
+static const ProtobufCEnumValueIndex show__passthrough_enum__enum_values_by_name[3] =
+{
+ { "ALLOWED", 2 },
+ { "NONE", 1 },
+ { "UNKNOWN", 0 },
+};
+const ProtobufCEnumDescriptor show__passthrough_enum__descriptor =
+{
+ PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+ "Show.PassthroughEnum",
+ "PassthroughEnum",
+ "Show__PassthroughEnum",
+ "",
+ 3,
+ show__passthrough_enum__enum_values_by_number,
+ 3,
+ show__passthrough_enum__enum_values_by_name,
+ 1,
+ show__passthrough_enum__value_ranges,
+ NULL,NULL,NULL,NULL /* reserved[1234] */
+};
+static const ProtobufCFieldDescriptor show__field_descriptors[19] =
+{
+ {
+ "gid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Show, has_gid),
+ offsetof(Show, gid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Show, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "description",
+ 64,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Show, description),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "deprecated_popularity",
+ 65,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Show, has_deprecated_popularity),
+ offsetof(Show, deprecated_popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "publisher",
+ 66,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Show, publisher),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "language",
+ 67,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Show, language),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "explicit",
+ 68,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Show, has_explicit_),
+ offsetof(Show, explicit_),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "covers",
+ 69,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Show, covers),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "episode",
+ 70,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Show, n_episode),
+ offsetof(Show, episode),
+ &episode__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "copyright",
+ 71,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Show, n_copyright),
+ offsetof(Show, copyright),
+ ©right__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "restriction",
+ 72,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Show, n_restriction),
+ offsetof(Show, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "keyword",
+ 73,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Show, n_keyword),
+ offsetof(Show, keyword),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "media_type",
+ 74,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Show, has_media_type),
+ offsetof(Show, media_type),
+ &show__media_type__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "consumption_order",
+ 75,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Show, has_consumption_order),
+ offsetof(Show, consumption_order),
+ &show__consumption_order__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "interpret_restriction_using_geoip",
+ 76,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Show, has_interpret_restriction_using_geoip),
+ offsetof(Show, interpret_restriction_using_geoip),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "availability",
+ 78,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Show, n_availability),
+ offsetof(Show, availability),
+ &availability__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "country_of_origin",
+ 79,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Show, country_of_origin),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "categories",
+ 80,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Show, n_categories),
+ offsetof(Show, categories),
+ &category__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "passthrough",
+ 81,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_ENUM,
+ offsetof(Show, has_passthrough),
+ offsetof(Show, passthrough),
+ &show__passthrough_enum__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned show__field_indices_by_name[] = {
+ 15, /* field[15] = availability */
+ 17, /* field[17] = categories */
+ 13, /* field[13] = consumption_order */
+ 9, /* field[9] = copyright */
+ 16, /* field[16] = country_of_origin */
+ 7, /* field[7] = covers */
+ 3, /* field[3] = deprecated_popularity */
+ 2, /* field[2] = description */
+ 8, /* field[8] = episode */
+ 6, /* field[6] = explicit */
+ 0, /* field[0] = gid */
+ 14, /* field[14] = interpret_restriction_using_geoip */
+ 11, /* field[11] = keyword */
+ 5, /* field[5] = language */
+ 12, /* field[12] = media_type */
+ 1, /* field[1] = name */
+ 18, /* field[18] = passthrough */
+ 4, /* field[4] = publisher */
+ 10, /* field[10] = restriction */
+};
+static const ProtobufCIntRange show__number_ranges[3 + 1] =
+{
+ { 1, 0 },
+ { 64, 2 },
+ { 78, 15 },
+ { 0, 19 }
+};
+const ProtobufCMessageDescriptor show__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Show",
+ "Show",
+ "Show",
+ "",
+ sizeof(Show),
+ 19,
+ show__field_descriptors,
+ show__field_indices_by_name,
+ 3, show__number_ranges,
+ (ProtobufCMessageInit) show__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor episode__field_descriptors[25] =
+{
+ {
+ "gid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(Episode, has_gid),
+ offsetof(Episode, gid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "name",
+ 2,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Episode, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "duration",
+ 7,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Episode, has_duration),
+ offsetof(Episode, duration),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "popularity",
+ 8,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Episode, has_popularity),
+ offsetof(Episode, popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "file",
+ 12,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_file),
+ offsetof(Episode, file),
+ &audio_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "description",
+ 64,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Episode, description),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "number",
+ 65,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Episode, has_number),
+ offsetof(Episode, number),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "publish_time",
+ 66,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Episode, publish_time),
+ &date__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "deprecated_popularity",
+ 67,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_SINT32,
+ offsetof(Episode, has_deprecated_popularity),
+ offsetof(Episode, deprecated_popularity),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "covers",
+ 68,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Episode, covers),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "language",
+ 69,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Episode, language),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "explicit",
+ 70,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Episode, has_explicit_),
+ offsetof(Episode, explicit_),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "show",
+ 71,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Episode, show),
+ &show__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "video",
+ 72,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_video),
+ offsetof(Episode, video),
+ &video_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "video_preview",
+ 73,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_video_preview),
+ offsetof(Episode, video_preview),
+ &video_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "audio_preview",
+ 74,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_audio_preview),
+ offsetof(Episode, audio_preview),
+ &audio_file__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "restriction",
+ 75,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_restriction),
+ offsetof(Episode, restriction),
+ &restriction__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "freeze_frame",
+ 76,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Episode, freeze_frame),
+ &image_group__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "keyword",
+ 77,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_STRING,
+ offsetof(Episode, n_keyword),
+ offsetof(Episode, keyword),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "suppress_monetization",
+ 78,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Episode, has_suppress_monetization),
+ offsetof(Episode, suppress_monetization),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "interpret_restriction_using_geoip",
+ 79,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Episode, has_interpret_restriction_using_geoip),
+ offsetof(Episode, interpret_restriction_using_geoip),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "allow_background_playback",
+ 81,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BOOL,
+ offsetof(Episode, has_allow_background_playback),
+ offsetof(Episode, allow_background_playback),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "availability",
+ 82,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Episode, n_availability),
+ offsetof(Episode, availability),
+ &availability__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "external_url",
+ 83,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Episode, external_url),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "original_audio",
+ 84,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_MESSAGE,
+ 0, /* quantifier_offset */
+ offsetof(Episode, original_audio),
+ &original_audio__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned episode__field_indices_by_name[] = {
+ 21, /* field[21] = allow_background_playback */
+ 15, /* field[15] = audio_preview */
+ 22, /* field[22] = availability */
+ 9, /* field[9] = covers */
+ 8, /* field[8] = deprecated_popularity */
+ 5, /* field[5] = description */
+ 2, /* field[2] = duration */
+ 11, /* field[11] = explicit */
+ 23, /* field[23] = external_url */
+ 4, /* field[4] = file */
+ 17, /* field[17] = freeze_frame */
+ 0, /* field[0] = gid */
+ 20, /* field[20] = interpret_restriction_using_geoip */
+ 18, /* field[18] = keyword */
+ 10, /* field[10] = language */
+ 1, /* field[1] = name */
+ 6, /* field[6] = number */
+ 24, /* field[24] = original_audio */
+ 3, /* field[3] = popularity */
+ 7, /* field[7] = publish_time */
+ 16, /* field[16] = restriction */
+ 12, /* field[12] = show */
+ 19, /* field[19] = suppress_monetization */
+ 13, /* field[13] = video */
+ 14, /* field[14] = video_preview */
+};
+static const ProtobufCIntRange episode__number_ranges[5 + 1] =
+{
+ { 1, 0 },
+ { 7, 2 },
+ { 12, 4 },
+ { 64, 5 },
+ { 81, 21 },
+ { 0, 25 }
+};
+const ProtobufCMessageDescriptor episode__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Episode",
+ "Episode",
+ "Episode",
+ "",
+ sizeof(Episode),
+ 25,
+ episode__field_descriptors,
+ episode__field_indices_by_name,
+ 5, episode__number_ranges,
+ (ProtobufCMessageInit) episode__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor category__field_descriptors[2] =
+{
+ {
+ "name",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_STRING,
+ 0, /* quantifier_offset */
+ offsetof(Category, name),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+ {
+ "subcategories",
+ 2,
+ PROTOBUF_C_LABEL_REPEATED,
+ PROTOBUF_C_TYPE_MESSAGE,
+ offsetof(Category, n_subcategories),
+ offsetof(Category, subcategories),
+ &category__descriptor,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned category__field_indices_by_name[] = {
+ 0, /* field[0] = name */
+ 1, /* field[1] = subcategories */
+};
+static const ProtobufCIntRange category__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 2 }
+};
+const ProtobufCMessageDescriptor category__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "Category",
+ "Category",
+ "Category",
+ "",
+ sizeof(Category),
+ 2,
+ category__field_descriptors,
+ category__field_indices_by_name,
+ 1, category__number_ranges,
+ (ProtobufCMessageInit) category__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor original_audio__field_descriptors[1] =
+{
+ {
+ "uuid",
+ 1,
+ PROTOBUF_C_LABEL_OPTIONAL,
+ PROTOBUF_C_TYPE_BYTES,
+ offsetof(OriginalAudio, has_uuid),
+ offsetof(OriginalAudio, uuid),
+ NULL,
+ NULL,
+ 0, /* flags */
+ 0,NULL,NULL /* reserved1,reserved2, etc */
+ },
+};
+static const unsigned original_audio__field_indices_by_name[] = {
+ 0, /* field[0] = uuid */
+};
+static const ProtobufCIntRange original_audio__number_ranges[1 + 1] =
+{
+ { 1, 0 },
+ { 0, 1 }
+};
+const ProtobufCMessageDescriptor original_audio__descriptor =
+{
+ PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+ "OriginalAudio",
+ "OriginalAudio",
+ "OriginalAudio",
+ "",
+ sizeof(OriginalAudio),
+ 1,
+ original_audio__field_descriptors,
+ original_audio__field_indices_by_name,
+ 1, original_audio__number_ranges,
+ (ProtobufCMessageInit) original_audio__init,
+ NULL,NULL,NULL /* reserved[123] */
+};
diff --git a/src/inputs/librespot-c/src/proto/metadata.pb-c.h b/src/inputs/librespot-c/src/proto/metadata.pb-c.h
new file mode 100644
index 00000000..530ddceb
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/metadata.pb-c.h
@@ -0,0 +1,1084 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: proto/metadata.proto */
+
+#ifndef PROTOBUF_C_proto_2fmetadata_2eproto__INCLUDED
+#define PROTOBUF_C_proto_2fmetadata_2eproto__INCLUDED
+
+#include
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1000000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct _TopTracks TopTracks;
+typedef struct _ActivityPeriod ActivityPeriod;
+typedef struct _Artist Artist;
+typedef struct _AlbumGroup AlbumGroup;
+typedef struct _Date Date;
+typedef struct _Album Album;
+typedef struct _Track Track;
+typedef struct _Image Image;
+typedef struct _ImageGroup ImageGroup;
+typedef struct _Biography Biography;
+typedef struct _Disc Disc;
+typedef struct _Copyright Copyright;
+typedef struct _Restriction Restriction;
+typedef struct _Availability Availability;
+typedef struct _SalePeriod SalePeriod;
+typedef struct _ExternalId ExternalId;
+typedef struct _AudioFile AudioFile;
+typedef struct _VideoFile VideoFile;
+typedef struct _Show Show;
+typedef struct _Episode Episode;
+typedef struct _Category Category;
+typedef struct _OriginalAudio OriginalAudio;
+
+
+/* --- enums --- */
+
+typedef enum _Album__Type {
+ ALBUM__TYPE__ALBUM = 1,
+ ALBUM__TYPE__SINGLE = 2,
+ ALBUM__TYPE__COMPILATION = 3,
+ ALBUM__TYPE__EP = 4
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ALBUM__TYPE)
+} Album__Type;
+typedef enum _Image__Size {
+ IMAGE__SIZE__DEFAULT = 0,
+ IMAGE__SIZE__SMALL = 1,
+ IMAGE__SIZE__LARGE = 2,
+ IMAGE__SIZE__XLARGE = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(IMAGE__SIZE)
+} Image__Size;
+typedef enum _Copyright__Type {
+ COPYRIGHT__TYPE__P = 0,
+ COPYRIGHT__TYPE__C = 1
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(COPYRIGHT__TYPE)
+} Copyright__Type;
+typedef enum _Restriction__Catalogue {
+ RESTRICTION__CATALOGUE__AD = 0,
+ RESTRICTION__CATALOGUE__SUBSCRIPTION = 1,
+ RESTRICTION__CATALOGUE__CATALOGUE_ALL = 2,
+ RESTRICTION__CATALOGUE__SHUFFLE = 3,
+ RESTRICTION__CATALOGUE__COMMERCIAL = 4
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RESTRICTION__CATALOGUE)
+} Restriction__Catalogue;
+typedef enum _Restriction__Type {
+ RESTRICTION__TYPE__STREAMING = 0
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RESTRICTION__TYPE)
+} Restriction__Type;
+typedef enum _AudioFile__Format {
+ AUDIO_FILE__FORMAT__OGG_VORBIS_96 = 0,
+ AUDIO_FILE__FORMAT__OGG_VORBIS_160 = 1,
+ AUDIO_FILE__FORMAT__OGG_VORBIS_320 = 2,
+ AUDIO_FILE__FORMAT__MP3_256 = 3,
+ AUDIO_FILE__FORMAT__MP3_320 = 4,
+ AUDIO_FILE__FORMAT__MP3_160 = 5,
+ AUDIO_FILE__FORMAT__MP3_96 = 6,
+ AUDIO_FILE__FORMAT__MP3_160_ENC = 7,
+ /*
+ * v4
+ * AAC_24 = 0x8;
+ * AAC_48 = 0x9;
+ */
+ AUDIO_FILE__FORMAT__MP4_128_DUAL = 8,
+ AUDIO_FILE__FORMAT__OTHER3 = 9,
+ AUDIO_FILE__FORMAT__AAC_160 = 10,
+ AUDIO_FILE__FORMAT__AAC_320 = 11,
+ AUDIO_FILE__FORMAT__MP4_128 = 12,
+ AUDIO_FILE__FORMAT__OTHER5 = 13
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(AUDIO_FILE__FORMAT)
+} AudioFile__Format;
+typedef enum _Show__MediaType {
+ SHOW__MEDIA_TYPE__MIXED = 0,
+ SHOW__MEDIA_TYPE__AUDIO = 1,
+ SHOW__MEDIA_TYPE__VIDEO = 2
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(SHOW__MEDIA_TYPE)
+} Show__MediaType;
+typedef enum _Show__ConsumptionOrder {
+ SHOW__CONSUMPTION_ORDER__SEQUENTIAL = 1,
+ SHOW__CONSUMPTION_ORDER__EPISODIC = 2,
+ SHOW__CONSUMPTION_ORDER__RECENT = 3
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(SHOW__CONSUMPTION_ORDER)
+} Show__ConsumptionOrder;
+typedef enum _Show__PassthroughEnum {
+ SHOW__PASSTHROUGH_ENUM__UNKNOWN = 0,
+ SHOW__PASSTHROUGH_ENUM__NONE = 1,
+ SHOW__PASSTHROUGH_ENUM__ALLOWED = 2
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(SHOW__PASSTHROUGH_ENUM)
+} Show__PassthroughEnum;
+
+/* --- messages --- */
+
+struct _TopTracks
+{
+ ProtobufCMessage base;
+ char *country;
+ size_t n_track;
+ Track **track;
+};
+#define TOP_TRACKS__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&top_tracks__descriptor) \
+ , NULL, 0,NULL }
+
+
+struct _ActivityPeriod
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_start_year;
+ int32_t start_year;
+ protobuf_c_boolean has_end_year;
+ int32_t end_year;
+ protobuf_c_boolean has_decade;
+ int32_t decade;
+};
+#define ACTIVITY_PERIOD__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&activity_period__descriptor) \
+ , 0, 0, 0, 0, 0, 0 }
+
+
+struct _Artist
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_gid;
+ ProtobufCBinaryData gid;
+ char *name;
+ protobuf_c_boolean has_popularity;
+ int32_t popularity;
+ size_t n_top_track;
+ TopTracks **top_track;
+ size_t n_album_group;
+ AlbumGroup **album_group;
+ size_t n_single_group;
+ AlbumGroup **single_group;
+ size_t n_compilation_group;
+ AlbumGroup **compilation_group;
+ size_t n_appears_on_group;
+ AlbumGroup **appears_on_group;
+ size_t n_genre;
+ char **genre;
+ size_t n_external_id;
+ ExternalId **external_id;
+ size_t n_portrait;
+ Image **portrait;
+ size_t n_biography;
+ Biography **biography;
+ size_t n_activity_period;
+ ActivityPeriod **activity_period;
+ size_t n_restriction;
+ Restriction **restriction;
+ size_t n_related;
+ Artist **related;
+ protobuf_c_boolean has_is_portrait_album_cover;
+ protobuf_c_boolean is_portrait_album_cover;
+ ImageGroup *portrait_group;
+};
+#define ARTIST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&artist__descriptor) \
+ , 0, {0,NULL}, NULL, 0, 0, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0, 0, NULL }
+
+
+struct _AlbumGroup
+{
+ ProtobufCMessage base;
+ size_t n_album;
+ Album **album;
+};
+#define ALBUM_GROUP__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&album_group__descriptor) \
+ , 0,NULL }
+
+
+struct _Date
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_year;
+ int32_t year;
+ protobuf_c_boolean has_month;
+ int32_t month;
+ protobuf_c_boolean has_day;
+ int32_t day;
+ protobuf_c_boolean has_hour;
+ int32_t hour;
+ protobuf_c_boolean has_minute;
+ int32_t minute;
+};
+#define DATE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&date__descriptor) \
+ , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+
+struct _Album
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_gid;
+ ProtobufCBinaryData gid;
+ char *name;
+ size_t n_artist;
+ Artist **artist;
+ protobuf_c_boolean has_typ;
+ Album__Type typ;
+ char *label;
+ Date *date;
+ protobuf_c_boolean has_popularity;
+ int32_t popularity;
+ size_t n_genre;
+ char **genre;
+ size_t n_cover;
+ Image **cover;
+ size_t n_external_id;
+ ExternalId **external_id;
+ size_t n_disc;
+ Disc **disc;
+ size_t n_review;
+ char **review;
+ size_t n_copyright;
+ Copyright **copyright;
+ size_t n_restriction;
+ Restriction **restriction;
+ size_t n_related;
+ Album **related;
+ size_t n_sale_period;
+ SalePeriod **sale_period;
+ ImageGroup *cover_group;
+};
+#define ALBUM__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&album__descriptor) \
+ , 0, {0,NULL}, NULL, 0,NULL, 0, ALBUM__TYPE__ALBUM, NULL, NULL, 0, 0, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, NULL }
+
+
+struct _Track
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_gid;
+ ProtobufCBinaryData gid;
+ char *name;
+ Album *album;
+ size_t n_artist;
+ Artist **artist;
+ protobuf_c_boolean has_number;
+ int32_t number;
+ protobuf_c_boolean has_disc_number;
+ int32_t disc_number;
+ protobuf_c_boolean has_duration;
+ int32_t duration;
+ protobuf_c_boolean has_popularity;
+ int32_t popularity;
+ protobuf_c_boolean has_explicit_;
+ protobuf_c_boolean explicit_;
+ size_t n_external_id;
+ ExternalId **external_id;
+ size_t n_restriction;
+ Restriction **restriction;
+ size_t n_file;
+ AudioFile **file;
+ size_t n_alternative;
+ Track **alternative;
+ size_t n_sale_period;
+ SalePeriod **sale_period;
+ size_t n_preview;
+ AudioFile **preview;
+};
+#define TRACK__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&track__descriptor) \
+ , 0, {0,NULL}, NULL, NULL, 0,NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL }
+
+
+struct _Image
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_file_id;
+ ProtobufCBinaryData file_id;
+ protobuf_c_boolean has_size;
+ Image__Size size;
+ protobuf_c_boolean has_width;
+ int32_t width;
+ protobuf_c_boolean has_height;
+ int32_t height;
+};
+#define IMAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&image__descriptor) \
+ , 0, {0,NULL}, 0, IMAGE__SIZE__DEFAULT, 0, 0, 0, 0 }
+
+
+struct _ImageGroup
+{
+ ProtobufCMessage base;
+ size_t n_image;
+ Image **image;
+};
+#define IMAGE_GROUP__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&image_group__descriptor) \
+ , 0,NULL }
+
+
+struct _Biography
+{
+ ProtobufCMessage base;
+ char *text;
+ size_t n_portrait;
+ Image **portrait;
+ size_t n_portrait_group;
+ ImageGroup **portrait_group;
+};
+#define BIOGRAPHY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&biography__descriptor) \
+ , NULL, 0,NULL, 0,NULL }
+
+
+struct _Disc
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_number;
+ int32_t number;
+ char *name;
+ size_t n_track;
+ Track **track;
+};
+#define DISC__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&disc__descriptor) \
+ , 0, 0, NULL, 0,NULL }
+
+
+struct _Copyright
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_typ;
+ Copyright__Type typ;
+ char *text;
+};
+#define COPYRIGHT__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (©right__descriptor) \
+ , 0, COPYRIGHT__TYPE__P, NULL }
+
+
+struct _Restriction
+{
+ ProtobufCMessage base;
+ size_t n_catalogue;
+ Restriction__Catalogue *catalogue;
+ char *countries_allowed;
+ char *countries_forbidden;
+ protobuf_c_boolean has_typ;
+ Restriction__Type typ;
+ size_t n_catalogue_str;
+ char **catalogue_str;
+};
+#define RESTRICTION__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&restriction__descriptor) \
+ , 0,NULL, NULL, NULL, 0, RESTRICTION__TYPE__STREAMING, 0,NULL }
+
+
+struct _Availability
+{
+ ProtobufCMessage base;
+ size_t n_catalogue_str;
+ char **catalogue_str;
+ Date *start;
+};
+#define AVAILABILITY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&availability__descriptor) \
+ , 0,NULL, NULL }
+
+
+struct _SalePeriod
+{
+ ProtobufCMessage base;
+ size_t n_restriction;
+ Restriction **restriction;
+ Date *start;
+ Date *end;
+};
+#define SALE_PERIOD__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&sale_period__descriptor) \
+ , 0,NULL, NULL, NULL }
+
+
+struct _ExternalId
+{
+ ProtobufCMessage base;
+ char *typ;
+ char *id;
+};
+#define EXTERNAL_ID__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&external_id__descriptor) \
+ , NULL, NULL }
+
+
+struct _AudioFile
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_file_id;
+ ProtobufCBinaryData file_id;
+ protobuf_c_boolean has_format;
+ AudioFile__Format format;
+};
+#define AUDIO_FILE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&audio_file__descriptor) \
+ , 0, {0,NULL}, 0, AUDIO_FILE__FORMAT__OGG_VORBIS_96 }
+
+
+struct _VideoFile
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_file_id;
+ ProtobufCBinaryData file_id;
+};
+#define VIDEO_FILE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&video_file__descriptor) \
+ , 0, {0,NULL} }
+
+
+/*
+ * Podcast Protos
+ */
+struct _Show
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_gid;
+ ProtobufCBinaryData gid;
+ char *name;
+ char *description;
+ protobuf_c_boolean has_deprecated_popularity;
+ int32_t deprecated_popularity;
+ char *publisher;
+ char *language;
+ protobuf_c_boolean has_explicit_;
+ protobuf_c_boolean explicit_;
+ ImageGroup *covers;
+ size_t n_episode;
+ Episode **episode;
+ size_t n_copyright;
+ Copyright **copyright;
+ size_t n_restriction;
+ Restriction **restriction;
+ size_t n_keyword;
+ char **keyword;
+ protobuf_c_boolean has_media_type;
+ Show__MediaType media_type;
+ protobuf_c_boolean has_consumption_order;
+ Show__ConsumptionOrder consumption_order;
+ protobuf_c_boolean has_interpret_restriction_using_geoip;
+ protobuf_c_boolean interpret_restriction_using_geoip;
+ size_t n_availability;
+ Availability **availability;
+ char *country_of_origin;
+ size_t n_categories;
+ Category **categories;
+ protobuf_c_boolean has_passthrough;
+ Show__PassthroughEnum passthrough;
+};
+#define SHOW__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&show__descriptor) \
+ , 0, {0,NULL}, NULL, NULL, 0, 0, NULL, NULL, 0, 0, NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0, SHOW__MEDIA_TYPE__MIXED, 0, SHOW__CONSUMPTION_ORDER__SEQUENTIAL, 0, 0, 0,NULL, NULL, 0,NULL, 0, SHOW__PASSTHROUGH_ENUM__UNKNOWN }
+
+
+struct _Episode
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_gid;
+ ProtobufCBinaryData gid;
+ char *name;
+ protobuf_c_boolean has_duration;
+ int32_t duration;
+ protobuf_c_boolean has_popularity;
+ int32_t popularity;
+ size_t n_file;
+ AudioFile **file;
+ char *description;
+ protobuf_c_boolean has_number;
+ int32_t number;
+ Date *publish_time;
+ protobuf_c_boolean has_deprecated_popularity;
+ int32_t deprecated_popularity;
+ ImageGroup *covers;
+ char *language;
+ protobuf_c_boolean has_explicit_;
+ protobuf_c_boolean explicit_;
+ Show *show;
+ size_t n_video;
+ VideoFile **video;
+ size_t n_video_preview;
+ VideoFile **video_preview;
+ size_t n_audio_preview;
+ AudioFile **audio_preview;
+ size_t n_restriction;
+ Restriction **restriction;
+ ImageGroup *freeze_frame;
+ size_t n_keyword;
+ char **keyword;
+ /*
+ * Order of these two flags might be wrong!
+ */
+ protobuf_c_boolean has_suppress_monetization;
+ protobuf_c_boolean suppress_monetization;
+ protobuf_c_boolean has_interpret_restriction_using_geoip;
+ protobuf_c_boolean interpret_restriction_using_geoip;
+ protobuf_c_boolean has_allow_background_playback;
+ protobuf_c_boolean allow_background_playback;
+ size_t n_availability;
+ Availability **availability;
+ char *external_url;
+ OriginalAudio *original_audio;
+};
+#define EPISODE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&episode__descriptor) \
+ , 0, {0,NULL}, NULL, 0, 0, 0, 0, 0,NULL, NULL, 0, 0, NULL, 0, 0, NULL, NULL, 0, 0, NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, NULL, 0,NULL, 0, 0, 0, 0, 0, 0, 0,NULL, NULL, NULL }
+
+
+struct _Category
+{
+ ProtobufCMessage base;
+ char *name;
+ size_t n_subcategories;
+ Category **subcategories;
+};
+#define CATEGORY__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&category__descriptor) \
+ , NULL, 0,NULL }
+
+
+struct _OriginalAudio
+{
+ ProtobufCMessage base;
+ protobuf_c_boolean has_uuid;
+ ProtobufCBinaryData uuid;
+};
+#define ORIGINAL_AUDIO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&original_audio__descriptor) \
+ , 0, {0,NULL} }
+
+
+/* TopTracks methods */
+void top_tracks__init
+ (TopTracks *message);
+size_t top_tracks__get_packed_size
+ (const TopTracks *message);
+size_t top_tracks__pack
+ (const TopTracks *message,
+ uint8_t *out);
+size_t top_tracks__pack_to_buffer
+ (const TopTracks *message,
+ ProtobufCBuffer *buffer);
+TopTracks *
+ top_tracks__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void top_tracks__free_unpacked
+ (TopTracks *message,
+ ProtobufCAllocator *allocator);
+/* ActivityPeriod methods */
+void activity_period__init
+ (ActivityPeriod *message);
+size_t activity_period__get_packed_size
+ (const ActivityPeriod *message);
+size_t activity_period__pack
+ (const ActivityPeriod *message,
+ uint8_t *out);
+size_t activity_period__pack_to_buffer
+ (const ActivityPeriod *message,
+ ProtobufCBuffer *buffer);
+ActivityPeriod *
+ activity_period__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void activity_period__free_unpacked
+ (ActivityPeriod *message,
+ ProtobufCAllocator *allocator);
+/* Artist methods */
+void artist__init
+ (Artist *message);
+size_t artist__get_packed_size
+ (const Artist *message);
+size_t artist__pack
+ (const Artist *message,
+ uint8_t *out);
+size_t artist__pack_to_buffer
+ (const Artist *message,
+ ProtobufCBuffer *buffer);
+Artist *
+ artist__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void artist__free_unpacked
+ (Artist *message,
+ ProtobufCAllocator *allocator);
+/* AlbumGroup methods */
+void album_group__init
+ (AlbumGroup *message);
+size_t album_group__get_packed_size
+ (const AlbumGroup *message);
+size_t album_group__pack
+ (const AlbumGroup *message,
+ uint8_t *out);
+size_t album_group__pack_to_buffer
+ (const AlbumGroup *message,
+ ProtobufCBuffer *buffer);
+AlbumGroup *
+ album_group__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void album_group__free_unpacked
+ (AlbumGroup *message,
+ ProtobufCAllocator *allocator);
+/* Date methods */
+void date__init
+ (Date *message);
+size_t date__get_packed_size
+ (const Date *message);
+size_t date__pack
+ (const Date *message,
+ uint8_t *out);
+size_t date__pack_to_buffer
+ (const Date *message,
+ ProtobufCBuffer *buffer);
+Date *
+ date__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void date__free_unpacked
+ (Date *message,
+ ProtobufCAllocator *allocator);
+/* Album methods */
+void album__init
+ (Album *message);
+size_t album__get_packed_size
+ (const Album *message);
+size_t album__pack
+ (const Album *message,
+ uint8_t *out);
+size_t album__pack_to_buffer
+ (const Album *message,
+ ProtobufCBuffer *buffer);
+Album *
+ album__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void album__free_unpacked
+ (Album *message,
+ ProtobufCAllocator *allocator);
+/* Track methods */
+void track__init
+ (Track *message);
+size_t track__get_packed_size
+ (const Track *message);
+size_t track__pack
+ (const Track *message,
+ uint8_t *out);
+size_t track__pack_to_buffer
+ (const Track *message,
+ ProtobufCBuffer *buffer);
+Track *
+ track__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void track__free_unpacked
+ (Track *message,
+ ProtobufCAllocator *allocator);
+/* Image methods */
+void image__init
+ (Image *message);
+size_t image__get_packed_size
+ (const Image *message);
+size_t image__pack
+ (const Image *message,
+ uint8_t *out);
+size_t image__pack_to_buffer
+ (const Image *message,
+ ProtobufCBuffer *buffer);
+Image *
+ image__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void image__free_unpacked
+ (Image *message,
+ ProtobufCAllocator *allocator);
+/* ImageGroup methods */
+void image_group__init
+ (ImageGroup *message);
+size_t image_group__get_packed_size
+ (const ImageGroup *message);
+size_t image_group__pack
+ (const ImageGroup *message,
+ uint8_t *out);
+size_t image_group__pack_to_buffer
+ (const ImageGroup *message,
+ ProtobufCBuffer *buffer);
+ImageGroup *
+ image_group__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void image_group__free_unpacked
+ (ImageGroup *message,
+ ProtobufCAllocator *allocator);
+/* Biography methods */
+void biography__init
+ (Biography *message);
+size_t biography__get_packed_size
+ (const Biography *message);
+size_t biography__pack
+ (const Biography *message,
+ uint8_t *out);
+size_t biography__pack_to_buffer
+ (const Biography *message,
+ ProtobufCBuffer *buffer);
+Biography *
+ biography__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void biography__free_unpacked
+ (Biography *message,
+ ProtobufCAllocator *allocator);
+/* Disc methods */
+void disc__init
+ (Disc *message);
+size_t disc__get_packed_size
+ (const Disc *message);
+size_t disc__pack
+ (const Disc *message,
+ uint8_t *out);
+size_t disc__pack_to_buffer
+ (const Disc *message,
+ ProtobufCBuffer *buffer);
+Disc *
+ disc__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void disc__free_unpacked
+ (Disc *message,
+ ProtobufCAllocator *allocator);
+/* Copyright methods */
+void copyright__init
+ (Copyright *message);
+size_t copyright__get_packed_size
+ (const Copyright *message);
+size_t copyright__pack
+ (const Copyright *message,
+ uint8_t *out);
+size_t copyright__pack_to_buffer
+ (const Copyright *message,
+ ProtobufCBuffer *buffer);
+Copyright *
+ copyright__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void copyright__free_unpacked
+ (Copyright *message,
+ ProtobufCAllocator *allocator);
+/* Restriction methods */
+void restriction__init
+ (Restriction *message);
+size_t restriction__get_packed_size
+ (const Restriction *message);
+size_t restriction__pack
+ (const Restriction *message,
+ uint8_t *out);
+size_t restriction__pack_to_buffer
+ (const Restriction *message,
+ ProtobufCBuffer *buffer);
+Restriction *
+ restriction__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void restriction__free_unpacked
+ (Restriction *message,
+ ProtobufCAllocator *allocator);
+/* Availability methods */
+void availability__init
+ (Availability *message);
+size_t availability__get_packed_size
+ (const Availability *message);
+size_t availability__pack
+ (const Availability *message,
+ uint8_t *out);
+size_t availability__pack_to_buffer
+ (const Availability *message,
+ ProtobufCBuffer *buffer);
+Availability *
+ availability__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void availability__free_unpacked
+ (Availability *message,
+ ProtobufCAllocator *allocator);
+/* SalePeriod methods */
+void sale_period__init
+ (SalePeriod *message);
+size_t sale_period__get_packed_size
+ (const SalePeriod *message);
+size_t sale_period__pack
+ (const SalePeriod *message,
+ uint8_t *out);
+size_t sale_period__pack_to_buffer
+ (const SalePeriod *message,
+ ProtobufCBuffer *buffer);
+SalePeriod *
+ sale_period__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void sale_period__free_unpacked
+ (SalePeriod *message,
+ ProtobufCAllocator *allocator);
+/* ExternalId methods */
+void external_id__init
+ (ExternalId *message);
+size_t external_id__get_packed_size
+ (const ExternalId *message);
+size_t external_id__pack
+ (const ExternalId *message,
+ uint8_t *out);
+size_t external_id__pack_to_buffer
+ (const ExternalId *message,
+ ProtobufCBuffer *buffer);
+ExternalId *
+ external_id__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void external_id__free_unpacked
+ (ExternalId *message,
+ ProtobufCAllocator *allocator);
+/* AudioFile methods */
+void audio_file__init
+ (AudioFile *message);
+size_t audio_file__get_packed_size
+ (const AudioFile *message);
+size_t audio_file__pack
+ (const AudioFile *message,
+ uint8_t *out);
+size_t audio_file__pack_to_buffer
+ (const AudioFile *message,
+ ProtobufCBuffer *buffer);
+AudioFile *
+ audio_file__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void audio_file__free_unpacked
+ (AudioFile *message,
+ ProtobufCAllocator *allocator);
+/* VideoFile methods */
+void video_file__init
+ (VideoFile *message);
+size_t video_file__get_packed_size
+ (const VideoFile *message);
+size_t video_file__pack
+ (const VideoFile *message,
+ uint8_t *out);
+size_t video_file__pack_to_buffer
+ (const VideoFile *message,
+ ProtobufCBuffer *buffer);
+VideoFile *
+ video_file__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void video_file__free_unpacked
+ (VideoFile *message,
+ ProtobufCAllocator *allocator);
+/* Show methods */
+void show__init
+ (Show *message);
+size_t show__get_packed_size
+ (const Show *message);
+size_t show__pack
+ (const Show *message,
+ uint8_t *out);
+size_t show__pack_to_buffer
+ (const Show *message,
+ ProtobufCBuffer *buffer);
+Show *
+ show__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void show__free_unpacked
+ (Show *message,
+ ProtobufCAllocator *allocator);
+/* Episode methods */
+void episode__init
+ (Episode *message);
+size_t episode__get_packed_size
+ (const Episode *message);
+size_t episode__pack
+ (const Episode *message,
+ uint8_t *out);
+size_t episode__pack_to_buffer
+ (const Episode *message,
+ ProtobufCBuffer *buffer);
+Episode *
+ episode__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void episode__free_unpacked
+ (Episode *message,
+ ProtobufCAllocator *allocator);
+/* Category methods */
+void category__init
+ (Category *message);
+size_t category__get_packed_size
+ (const Category *message);
+size_t category__pack
+ (const Category *message,
+ uint8_t *out);
+size_t category__pack_to_buffer
+ (const Category *message,
+ ProtobufCBuffer *buffer);
+Category *
+ category__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void category__free_unpacked
+ (Category *message,
+ ProtobufCAllocator *allocator);
+/* OriginalAudio methods */
+void original_audio__init
+ (OriginalAudio *message);
+size_t original_audio__get_packed_size
+ (const OriginalAudio *message);
+size_t original_audio__pack
+ (const OriginalAudio *message,
+ uint8_t *out);
+size_t original_audio__pack_to_buffer
+ (const OriginalAudio *message,
+ ProtobufCBuffer *buffer);
+OriginalAudio *
+ original_audio__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void original_audio__free_unpacked
+ (OriginalAudio *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*TopTracks_Closure)
+ (const TopTracks *message,
+ void *closure_data);
+typedef void (*ActivityPeriod_Closure)
+ (const ActivityPeriod *message,
+ void *closure_data);
+typedef void (*Artist_Closure)
+ (const Artist *message,
+ void *closure_data);
+typedef void (*AlbumGroup_Closure)
+ (const AlbumGroup *message,
+ void *closure_data);
+typedef void (*Date_Closure)
+ (const Date *message,
+ void *closure_data);
+typedef void (*Album_Closure)
+ (const Album *message,
+ void *closure_data);
+typedef void (*Track_Closure)
+ (const Track *message,
+ void *closure_data);
+typedef void (*Image_Closure)
+ (const Image *message,
+ void *closure_data);
+typedef void (*ImageGroup_Closure)
+ (const ImageGroup *message,
+ void *closure_data);
+typedef void (*Biography_Closure)
+ (const Biography *message,
+ void *closure_data);
+typedef void (*Disc_Closure)
+ (const Disc *message,
+ void *closure_data);
+typedef void (*Copyright_Closure)
+ (const Copyright *message,
+ void *closure_data);
+typedef void (*Restriction_Closure)
+ (const Restriction *message,
+ void *closure_data);
+typedef void (*Availability_Closure)
+ (const Availability *message,
+ void *closure_data);
+typedef void (*SalePeriod_Closure)
+ (const SalePeriod *message,
+ void *closure_data);
+typedef void (*ExternalId_Closure)
+ (const ExternalId *message,
+ void *closure_data);
+typedef void (*AudioFile_Closure)
+ (const AudioFile *message,
+ void *closure_data);
+typedef void (*VideoFile_Closure)
+ (const VideoFile *message,
+ void *closure_data);
+typedef void (*Show_Closure)
+ (const Show *message,
+ void *closure_data);
+typedef void (*Episode_Closure)
+ (const Episode *message,
+ void *closure_data);
+typedef void (*Category_Closure)
+ (const Category *message,
+ void *closure_data);
+typedef void (*OriginalAudio_Closure)
+ (const OriginalAudio *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor top_tracks__descriptor;
+extern const ProtobufCMessageDescriptor activity_period__descriptor;
+extern const ProtobufCMessageDescriptor artist__descriptor;
+extern const ProtobufCMessageDescriptor album_group__descriptor;
+extern const ProtobufCMessageDescriptor date__descriptor;
+extern const ProtobufCMessageDescriptor album__descriptor;
+extern const ProtobufCEnumDescriptor album__type__descriptor;
+extern const ProtobufCMessageDescriptor track__descriptor;
+extern const ProtobufCMessageDescriptor image__descriptor;
+extern const ProtobufCEnumDescriptor image__size__descriptor;
+extern const ProtobufCMessageDescriptor image_group__descriptor;
+extern const ProtobufCMessageDescriptor biography__descriptor;
+extern const ProtobufCMessageDescriptor disc__descriptor;
+extern const ProtobufCMessageDescriptor copyright__descriptor;
+extern const ProtobufCEnumDescriptor copyright__type__descriptor;
+extern const ProtobufCMessageDescriptor restriction__descriptor;
+extern const ProtobufCEnumDescriptor restriction__catalogue__descriptor;
+extern const ProtobufCEnumDescriptor restriction__type__descriptor;
+extern const ProtobufCMessageDescriptor availability__descriptor;
+extern const ProtobufCMessageDescriptor sale_period__descriptor;
+extern const ProtobufCMessageDescriptor external_id__descriptor;
+extern const ProtobufCMessageDescriptor audio_file__descriptor;
+extern const ProtobufCEnumDescriptor audio_file__format__descriptor;
+extern const ProtobufCMessageDescriptor video_file__descriptor;
+extern const ProtobufCMessageDescriptor show__descriptor;
+extern const ProtobufCEnumDescriptor show__media_type__descriptor;
+extern const ProtobufCEnumDescriptor show__consumption_order__descriptor;
+extern const ProtobufCEnumDescriptor show__passthrough_enum__descriptor;
+extern const ProtobufCMessageDescriptor episode__descriptor;
+extern const ProtobufCMessageDescriptor category__descriptor;
+extern const ProtobufCMessageDescriptor original_audio__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_proto_2fmetadata_2eproto__INCLUDED */
diff --git a/src/inputs/librespot-c/src/proto/metadata.proto b/src/inputs/librespot-c/src/proto/metadata.proto
new file mode 100644
index 00000000..3812f94e
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/metadata.proto
@@ -0,0 +1,266 @@
+syntax = "proto2";
+
+message TopTracks {
+ optional string country = 0x1;
+ repeated Track track = 0x2;
+}
+
+message ActivityPeriod {
+ optional sint32 start_year = 0x1;
+ optional sint32 end_year = 0x2;
+ optional sint32 decade = 0x3;
+}
+
+message Artist {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional sint32 popularity = 0x3;
+ repeated TopTracks top_track = 0x4;
+ repeated AlbumGroup album_group = 0x5;
+ repeated AlbumGroup single_group = 0x6;
+ repeated AlbumGroup compilation_group = 0x7;
+ repeated AlbumGroup appears_on_group = 0x8;
+ repeated string genre = 0x9;
+ repeated ExternalId external_id = 0xa;
+ repeated Image portrait = 0xb;
+ repeated Biography biography = 0xc;
+ repeated ActivityPeriod activity_period = 0xd;
+ repeated Restriction restriction = 0xe;
+ repeated Artist related = 0xf;
+ optional bool is_portrait_album_cover = 0x10;
+ optional ImageGroup portrait_group = 0x11;
+}
+
+message AlbumGroup {
+ repeated Album album = 0x1;
+}
+
+message Date {
+ optional sint32 year = 0x1;
+ optional sint32 month = 0x2;
+ optional sint32 day = 0x3;
+ optional sint32 hour = 0x4;
+ optional sint32 minute = 0x5;
+}
+
+message Album {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ repeated Artist artist = 0x3;
+ optional Type typ = 0x4;
+ enum Type {
+ ALBUM = 0x1;
+ SINGLE = 0x2;
+ COMPILATION = 0x3;
+ EP = 0x4;
+ }
+ optional string label = 0x5;
+ optional Date date = 0x6;
+ optional sint32 popularity = 0x7;
+ repeated string genre = 0x8;
+ repeated Image cover = 0x9;
+ repeated ExternalId external_id = 0xa;
+ repeated Disc disc = 0xb;
+ repeated string review = 0xc;
+ repeated Copyright copyright = 0xd;
+ repeated Restriction restriction = 0xe;
+ repeated Album related = 0xf;
+ repeated SalePeriod sale_period = 0x10;
+ optional ImageGroup cover_group = 0x11;
+}
+
+message Track {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional Album album = 0x3;
+ repeated Artist artist = 0x4;
+ optional sint32 number = 0x5;
+ optional sint32 disc_number = 0x6;
+ optional sint32 duration = 0x7;
+ optional sint32 popularity = 0x8;
+ optional bool explicit = 0x9;
+ repeated ExternalId external_id = 0xa;
+ repeated Restriction restriction = 0xb;
+ repeated AudioFile file = 0xc;
+ repeated Track alternative = 0xd;
+ repeated SalePeriod sale_period = 0xe;
+ repeated AudioFile preview = 0xf;
+}
+
+message Image {
+ optional bytes file_id = 0x1;
+ optional Size size = 0x2;
+ enum Size {
+ DEFAULT = 0x0;
+ SMALL = 0x1;
+ LARGE = 0x2;
+ XLARGE = 0x3;
+ }
+ optional sint32 width = 0x3;
+ optional sint32 height = 0x4;
+}
+
+message ImageGroup {
+ repeated Image image = 0x1;
+}
+
+message Biography {
+ optional string text = 0x1;
+ repeated Image portrait = 0x2;
+ repeated ImageGroup portrait_group = 0x3;
+}
+
+message Disc {
+ optional sint32 number = 0x1;
+ optional string name = 0x2;
+ repeated Track track = 0x3;
+}
+
+message Copyright {
+ optional Type typ = 0x1;
+ enum Type {
+ P = 0x0;
+ C = 0x1;
+ }
+ optional string text = 0x2;
+}
+
+message Restriction {
+ enum Catalogue {
+ AD = 0;
+ SUBSCRIPTION = 1;
+ CATALOGUE_ALL = 2;
+ SHUFFLE = 3;
+ COMMERCIAL = 4;
+ }
+ enum Type {
+ STREAMING = 0x0;
+ }
+ repeated Catalogue catalogue = 0x1;
+ optional string countries_allowed = 0x2;
+ optional string countries_forbidden = 0x3;
+ optional Type typ = 0x4;
+
+ repeated string catalogue_str = 0x5;
+}
+
+message Availability {
+ repeated string catalogue_str = 0x1;
+ optional Date start = 0x2;
+}
+
+message SalePeriod {
+ repeated Restriction restriction = 0x1;
+ optional Date start = 0x2;
+ optional Date end = 0x3;
+}
+
+message ExternalId {
+ optional string typ = 0x1;
+ optional string id = 0x2;
+}
+
+message AudioFile {
+ optional bytes file_id = 0x1;
+ optional Format format = 0x2;
+ enum Format {
+ OGG_VORBIS_96 = 0x0;
+ OGG_VORBIS_160 = 0x1;
+ OGG_VORBIS_320 = 0x2;
+ MP3_256 = 0x3;
+ MP3_320 = 0x4;
+ MP3_160 = 0x5;
+ MP3_96 = 0x6;
+ MP3_160_ENC = 0x7;
+ // v4
+ // AAC_24 = 0x8;
+ // AAC_48 = 0x9;
+ MP4_128_DUAL = 0x8;
+ OTHER3 = 0x9;
+ AAC_160 = 0xa;
+ AAC_320 = 0xb;
+ MP4_128 = 0xc;
+ OTHER5 = 0xd;
+ }
+}
+
+message VideoFile {
+ optional bytes file_id = 1;
+}
+
+// Podcast Protos
+message Show {
+ enum MediaType {
+ MIXED = 0;
+ AUDIO = 1;
+ VIDEO = 2;
+ }
+ enum ConsumptionOrder {
+ SEQUENTIAL = 1;
+ EPISODIC = 2;
+ RECENT = 3;
+ }
+ enum PassthroughEnum {
+ UNKNOWN = 0;
+ NONE = 1;
+ ALLOWED = 2;
+ }
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional string description = 0x40;
+ optional sint32 deprecated_popularity = 0x41;
+ optional string publisher = 0x42;
+ optional string language = 0x43;
+ optional bool explicit = 0x44;
+ optional ImageGroup covers = 0x45;
+ repeated Episode episode = 0x46;
+ repeated Copyright copyright = 0x47;
+ repeated Restriction restriction = 0x48;
+ repeated string keyword = 0x49;
+ optional MediaType media_type = 0x4A;
+ optional ConsumptionOrder consumption_order = 0x4B;
+ optional bool interpret_restriction_using_geoip = 0x4C;
+ repeated Availability availability = 0x4E;
+ optional string country_of_origin = 0x4F;
+ repeated Category categories = 0x50;
+ optional PassthroughEnum passthrough = 0x51;
+}
+
+message Episode {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional sint32 duration = 0x7;
+ optional sint32 popularity = 0x8;
+ repeated AudioFile file = 0xc;
+ optional string description = 0x40;
+ optional sint32 number = 0x41;
+ optional Date publish_time = 0x42;
+ optional sint32 deprecated_popularity = 0x43;
+ optional ImageGroup covers = 0x44;
+ optional string language = 0x45;
+ optional bool explicit = 0x46;
+ optional Show show = 0x47;
+ repeated VideoFile video = 0x48;
+ repeated VideoFile video_preview = 0x49;
+ repeated AudioFile audio_preview = 0x4A;
+ repeated Restriction restriction = 0x4B;
+ optional ImageGroup freeze_frame = 0x4C;
+ repeated string keyword = 0x4D;
+ // Order of these two flags might be wrong!
+ optional bool suppress_monetization = 0x4E;
+ optional bool interpret_restriction_using_geoip = 0x4F;
+
+ optional bool allow_background_playback = 0x51;
+ repeated Availability availability = 0x52;
+ optional string external_url = 0x53;
+ optional OriginalAudio original_audio = 0x54;
+}
+
+message Category {
+ optional string name = 0x1;
+ repeated Category subcategories = 0x2;
+}
+
+message OriginalAudio {
+ optional bytes uuid = 0x1;
+}
diff --git a/src/inputs/librespot-c/src/proto/playlist4changes.proto b/src/inputs/librespot-c/src/proto/playlist4changes.proto
new file mode 100644
index 00000000..6b424b71
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/playlist4changes.proto
@@ -0,0 +1,87 @@
+syntax = "proto2";
+
+import "playlist4ops.proto";
+import "playlist4meta.proto";
+import "playlist4content.proto";
+import "playlist4issues.proto";
+
+message ChangeInfo {
+ optional string user = 0x1;
+ optional int32 timestamp = 0x2;
+ optional bool admin = 0x3;
+ optional bool undo = 0x4;
+ optional bool redo = 0x5;
+ optional bool merge = 0x6;
+ optional bool compressed = 0x7;
+ optional bool migration = 0x8;
+}
+
+message Delta {
+ optional bytes base_version = 0x1;
+ repeated Op ops = 0x2;
+ optional ChangeInfo info = 0x4;
+}
+
+message Merge {
+ optional bytes base_version = 0x1;
+ optional bytes merge_version = 0x2;
+ optional ChangeInfo info = 0x4;
+}
+
+message ChangeSet {
+ optional Kind kind = 0x1;
+ enum Kind {
+ KIND_UNKNOWN = 0x0;
+ DELTA = 0x2;
+ MERGE = 0x3;
+ }
+ optional Delta delta = 0x2;
+ optional Merge merge = 0x3;
+}
+
+message RevisionTaggedChangeSet {
+ optional bytes revision = 0x1;
+ optional ChangeSet change_set = 0x2;
+}
+
+message Diff {
+ optional bytes from_revision = 0x1;
+ repeated Op ops = 0x2;
+ optional bytes to_revision = 0x3;
+}
+
+message ListDump {
+ optional bytes latestRevision = 0x1;
+ optional int32 length = 0x2;
+ optional ListAttributes attributes = 0x3;
+ optional ListChecksum checksum = 0x4;
+ optional ListItems contents = 0x5;
+ repeated Delta pendingDeltas = 0x7;
+}
+
+message ListChanges {
+ optional bytes baseRevision = 0x1;
+ repeated Delta deltas = 0x2;
+ optional bool wantResultingRevisions = 0x3;
+ optional bool wantSyncResult = 0x4;
+ optional ListDump dump = 0x5;
+ repeated int32 nonces = 0x6;
+}
+
+message SelectedListContent {
+ optional bytes revision = 0x1;
+ optional int32 length = 0x2;
+ optional ListAttributes attributes = 0x3;
+ optional ListChecksum checksum = 0x4;
+ optional ListItems contents = 0x5;
+ optional Diff diff = 0x6;
+ optional Diff syncResult = 0x7;
+ repeated bytes resultingRevisions = 0x8;
+ optional bool multipleHeads = 0x9;
+ optional bool upToDate = 0xa;
+ repeated ClientResolveAction resolveAction = 0xc;
+ repeated ClientIssue issues = 0xd;
+ repeated int32 nonces = 0xe;
+ optional string owner_username =0x10;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/playlist4content.proto b/src/inputs/librespot-c/src/proto/playlist4content.proto
new file mode 100644
index 00000000..50d197fa
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/playlist4content.proto
@@ -0,0 +1,37 @@
+syntax = "proto2";
+
+import "playlist4meta.proto";
+import "playlist4issues.proto";
+
+message Item {
+ optional string uri = 0x1;
+ optional ItemAttributes attributes = 0x2;
+}
+
+message ListItems {
+ optional int32 pos = 0x1;
+ optional bool truncated = 0x2;
+ repeated Item items = 0x3;
+}
+
+message ContentRange {
+ optional int32 pos = 0x1;
+ optional int32 length = 0x2;
+}
+
+message ListContentSelection {
+ optional bool wantRevision = 0x1;
+ optional bool wantLength = 0x2;
+ optional bool wantAttributes = 0x3;
+ optional bool wantChecksum = 0x4;
+ optional bool wantContent = 0x5;
+ optional ContentRange contentRange = 0x6;
+ optional bool wantDiff = 0x7;
+ optional bytes baseRevision = 0x8;
+ optional bytes hintRevision = 0x9;
+ optional bool wantNothingIfUpToDate = 0xa;
+ optional bool wantResolveAction = 0xc;
+ repeated ClientIssue issues = 0xd;
+ repeated ClientResolveAction resolveAction = 0xe;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/playlist4issues.proto b/src/inputs/librespot-c/src/proto/playlist4issues.proto
new file mode 100644
index 00000000..3808d532
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/playlist4issues.proto
@@ -0,0 +1,43 @@
+syntax = "proto2";
+
+message ClientIssue {
+ optional Level level = 0x1;
+ enum Level {
+ LEVEL_UNKNOWN = 0x0;
+ LEVEL_DEBUG = 0x1;
+ LEVEL_INFO = 0x2;
+ LEVEL_NOTICE = 0x3;
+ LEVEL_WARNING = 0x4;
+ LEVEL_ERROR = 0x5;
+ }
+ optional Code code = 0x2;
+ enum Code {
+ CODE_UNKNOWN = 0x0;
+ CODE_INDEX_OUT_OF_BOUNDS = 0x1;
+ CODE_VERSION_MISMATCH = 0x2;
+ CODE_CACHED_CHANGE = 0x3;
+ CODE_OFFLINE_CHANGE = 0x4;
+ CODE_CONCURRENT_CHANGE = 0x5;
+ }
+ optional int32 repeatCount = 0x3;
+}
+
+message ClientResolveAction {
+ optional Code code = 0x1;
+ enum Code {
+ CODE_UNKNOWN = 0x0;
+ CODE_NO_ACTION = 0x1;
+ CODE_RETRY = 0x2;
+ CODE_RELOAD = 0x3;
+ CODE_DISCARD_LOCAL_CHANGES = 0x4;
+ CODE_SEND_DUMP = 0x5;
+ CODE_DISPLAY_ERROR_MESSAGE = 0x6;
+ }
+ optional Initiator initiator = 0x2;
+ enum Initiator {
+ INITIATOR_UNKNOWN = 0x0;
+ INITIATOR_SERVER = 0x1;
+ INITIATOR_CLIENT = 0x2;
+ }
+}
+
diff --git a/src/inputs/librespot-c/src/proto/playlist4meta.proto b/src/inputs/librespot-c/src/proto/playlist4meta.proto
new file mode 100644
index 00000000..4c22a9f0
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/playlist4meta.proto
@@ -0,0 +1,52 @@
+syntax = "proto2";
+
+message ListChecksum {
+ optional int32 version = 0x1;
+ optional bytes sha1 = 0x4;
+}
+
+message DownloadFormat {
+ optional Codec codec = 0x1;
+ enum Codec {
+ CODEC_UNKNOWN = 0x0;
+ OGG_VORBIS = 0x1;
+ FLAC = 0x2;
+ MPEG_1_LAYER_3 = 0x3;
+ }
+}
+
+message ListAttributes {
+ optional string name = 0x1;
+ optional string description = 0x2;
+ optional bytes picture = 0x3;
+ optional bool collaborative = 0x4;
+ optional string pl3_version = 0x5;
+ optional bool deleted_by_owner = 0x6;
+ optional bool restricted_collaborative = 0x7;
+ optional int64 deprecated_client_id = 0x8;
+ optional bool public_starred = 0x9;
+ optional string client_id = 0xa;
+}
+
+message ItemAttributes {
+ optional string added_by = 0x1;
+ optional int64 timestamp = 0x2;
+ optional string message = 0x3;
+ optional bool seen = 0x4;
+ optional int64 download_count = 0x5;
+ optional DownloadFormat download_format = 0x6;
+ optional string sevendigital_id = 0x7;
+ optional int64 sevendigital_left = 0x8;
+ optional int64 seen_at = 0x9;
+ optional bool public = 0xa;
+}
+
+message StringAttribute {
+ optional string key = 0x1;
+ optional string value = 0x2;
+}
+
+message StringAttributes {
+ repeated StringAttribute attribute = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/playlist4ops.proto b/src/inputs/librespot-c/src/proto/playlist4ops.proto
new file mode 100644
index 00000000..dbbfcaa9
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/playlist4ops.proto
@@ -0,0 +1,103 @@
+syntax = "proto2";
+
+import "playlist4meta.proto";
+import "playlist4content.proto";
+
+message Add {
+ optional int32 fromIndex = 0x1;
+ repeated Item items = 0x2;
+ optional ListChecksum list_checksum = 0x3;
+ optional bool addLast = 0x4;
+ optional bool addFirst = 0x5;
+}
+
+message Rem {
+ optional int32 fromIndex = 0x1;
+ optional int32 length = 0x2;
+ repeated Item items = 0x3;
+ optional ListChecksum list_checksum = 0x4;
+ optional ListChecksum items_checksum = 0x5;
+ optional ListChecksum uris_checksum = 0x6;
+ optional bool itemsAsKey = 0x7;
+}
+
+message Mov {
+ optional int32 fromIndex = 0x1;
+ optional int32 length = 0x2;
+ optional int32 toIndex = 0x3;
+ optional ListChecksum list_checksum = 0x4;
+ optional ListChecksum items_checksum = 0x5;
+ optional ListChecksum uris_checksum = 0x6;
+}
+
+message ItemAttributesPartialState {
+ optional ItemAttributes values = 0x1;
+ repeated ItemAttributeKind no_value = 0x2;
+
+ enum ItemAttributeKind {
+ ITEM_UNKNOWN = 0x0;
+ ITEM_ADDED_BY = 0x1;
+ ITEM_TIMESTAMP = 0x2;
+ ITEM_MESSAGE = 0x3;
+ ITEM_SEEN = 0x4;
+ ITEM_DOWNLOAD_COUNT = 0x5;
+ ITEM_DOWNLOAD_FORMAT = 0x6;
+ ITEM_SEVENDIGITAL_ID = 0x7;
+ ITEM_SEVENDIGITAL_LEFT = 0x8;
+ ITEM_SEEN_AT = 0x9;
+ ITEM_PUBLIC = 0xa;
+ }
+}
+
+message ListAttributesPartialState {
+ optional ListAttributes values = 0x1;
+ repeated ListAttributeKind no_value = 0x2;
+
+ enum ListAttributeKind {
+ LIST_UNKNOWN = 0x0;
+ LIST_NAME = 0x1;
+ LIST_DESCRIPTION = 0x2;
+ LIST_PICTURE = 0x3;
+ LIST_COLLABORATIVE = 0x4;
+ LIST_PL3_VERSION = 0x5;
+ LIST_DELETED_BY_OWNER = 0x6;
+ LIST_RESTRICTED_COLLABORATIVE = 0x7;
+ }
+}
+
+message UpdateItemAttributes {
+ optional int32 index = 0x1;
+ optional ItemAttributesPartialState new_attributes = 0x2;
+ optional ItemAttributesPartialState old_attributes = 0x3;
+ optional ListChecksum list_checksum = 0x4;
+ optional ListChecksum old_attributes_checksum = 0x5;
+}
+
+message UpdateListAttributes {
+ optional ListAttributesPartialState new_attributes = 0x1;
+ optional ListAttributesPartialState old_attributes = 0x2;
+ optional ListChecksum list_checksum = 0x3;
+ optional ListChecksum old_attributes_checksum = 0x4;
+}
+
+message Op {
+ optional Kind kind = 0x1;
+ enum Kind {
+ KIND_UNKNOWN = 0x0;
+ ADD = 0x2;
+ REM = 0x3;
+ MOV = 0x4;
+ UPDATE_ITEM_ATTRIBUTES = 0x5;
+ UPDATE_LIST_ATTRIBUTES = 0x6;
+ }
+ optional Add add = 0x2;
+ optional Rem rem = 0x3;
+ optional Mov mov = 0x4;
+ optional UpdateItemAttributes update_item_attributes = 0x5;
+ optional UpdateListAttributes update_list_attributes = 0x6;
+}
+
+message OpList {
+ repeated Op ops = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/popcount.proto b/src/inputs/librespot-c/src/proto/popcount.proto
new file mode 100644
index 00000000..7a0bac84
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/popcount.proto
@@ -0,0 +1,13 @@
+syntax = "proto2";
+
+message PopcountRequest {
+}
+
+message PopcountResult {
+ optional sint64 count = 0x1;
+ optional bool truncated = 0x2;
+ repeated string user = 0x3;
+ repeated sint64 subscriptionTimestamps = 0x4;
+ repeated sint64 insertionTimestamps = 0x5;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/presence.proto b/src/inputs/librespot-c/src/proto/presence.proto
new file mode 100644
index 00000000..5e9be377
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/presence.proto
@@ -0,0 +1,94 @@
+syntax = "proto2";
+
+message PlaylistPublishedState {
+ optional string uri = 0x1;
+ optional int64 timestamp = 0x2;
+}
+
+message PlaylistTrackAddedState {
+ optional string playlist_uri = 0x1;
+ optional string track_uri = 0x2;
+ optional int64 timestamp = 0x3;
+}
+
+message TrackFinishedPlayingState {
+ optional string uri = 0x1;
+ optional string context_uri = 0x2;
+ optional int64 timestamp = 0x3;
+ optional string referrer_uri = 0x4;
+}
+
+message FavoriteAppAddedState {
+ optional string app_uri = 0x1;
+ optional int64 timestamp = 0x2;
+}
+
+message TrackStartedPlayingState {
+ optional string uri = 0x1;
+ optional string context_uri = 0x2;
+ optional int64 timestamp = 0x3;
+ optional string referrer_uri = 0x4;
+}
+
+message UriSharedState {
+ optional string uri = 0x1;
+ optional string message = 0x2;
+ optional int64 timestamp = 0x3;
+}
+
+message ArtistFollowedState {
+ optional string uri = 0x1;
+ optional string artist_name = 0x2;
+ optional string artist_cover_uri = 0x3;
+ optional int64 timestamp = 0x4;
+}
+
+message DeviceInformation {
+ optional string os = 0x1;
+ optional string type = 0x2;
+}
+
+message GenericPresenceState {
+ optional int32 type = 0x1;
+ optional int64 timestamp = 0x2;
+ optional string item_uri = 0x3;
+ optional string item_name = 0x4;
+ optional string item_image = 0x5;
+ optional string context_uri = 0x6;
+ optional string context_name = 0x7;
+ optional string context_image = 0x8;
+ optional string referrer_uri = 0x9;
+ optional string referrer_name = 0xa;
+ optional string referrer_image = 0xb;
+ optional string message = 0xc;
+ optional DeviceInformation device_information = 0xd;
+}
+
+message State {
+ optional int64 timestamp = 0x1;
+ optional Type type = 0x2;
+ enum Type {
+ PLAYLIST_PUBLISHED = 0x1;
+ PLAYLIST_TRACK_ADDED = 0x2;
+ TRACK_FINISHED_PLAYING = 0x3;
+ FAVORITE_APP_ADDED = 0x4;
+ TRACK_STARTED_PLAYING = 0x5;
+ URI_SHARED = 0x6;
+ ARTIST_FOLLOWED = 0x7;
+ GENERIC = 0xb;
+ }
+ optional string uri = 0x3;
+ optional PlaylistPublishedState playlist_published = 0x4;
+ optional PlaylistTrackAddedState playlist_track_added = 0x5;
+ optional TrackFinishedPlayingState track_finished_playing = 0x6;
+ optional FavoriteAppAddedState favorite_app_added = 0x7;
+ optional TrackStartedPlayingState track_started_playing = 0x8;
+ optional UriSharedState uri_shared = 0x9;
+ optional ArtistFollowedState artist_followed = 0xa;
+ optional GenericPresenceState generic = 0xb;
+}
+
+message StateList {
+ repeated State states = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/pubsub.proto b/src/inputs/librespot-c/src/proto/pubsub.proto
new file mode 100644
index 00000000..a781c377
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/pubsub.proto
@@ -0,0 +1,8 @@
+syntax = "proto2";
+
+message Subscription {
+ optional string uri = 0x1;
+ optional int32 expiry = 0x2;
+ optional int32 status_code = 0x3;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/radio.proto b/src/inputs/librespot-c/src/proto/radio.proto
new file mode 100644
index 00000000..7a8f3bde
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/radio.proto
@@ -0,0 +1,58 @@
+syntax = "proto2";
+
+message RadioRequest {
+ repeated string uris = 0x1;
+ optional int32 salt = 0x2;
+ optional int32 length = 0x4;
+ optional string stationId = 0x5;
+ repeated string lastTracks = 0x6;
+}
+
+message MultiSeedRequest {
+ repeated string uris = 0x1;
+}
+
+message Feedback {
+ optional string uri = 0x1;
+ optional string type = 0x2;
+ optional double timestamp = 0x3;
+}
+
+message Tracks {
+ repeated string gids = 0x1;
+ optional string source = 0x2;
+ optional string identity = 0x3;
+ repeated string tokens = 0x4;
+ repeated Feedback feedback = 0x5;
+}
+
+message Station {
+ optional string id = 0x1;
+ optional string title = 0x2;
+ optional string titleUri = 0x3;
+ optional string subtitle = 0x4;
+ optional string subtitleUri = 0x5;
+ optional string imageUri = 0x6;
+ optional double lastListen = 0x7;
+ repeated string seeds = 0x8;
+ optional int32 thumbsUp = 0x9;
+ optional int32 thumbsDown = 0xa;
+}
+
+message Rules {
+ optional string js = 0x1;
+}
+
+message StationResponse {
+ optional Station station = 0x1;
+ repeated Feedback feedback = 0x2;
+}
+
+message StationList {
+ repeated Station stations = 0x1;
+}
+
+message LikedPlaylist {
+ optional string uri = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/search.proto b/src/inputs/librespot-c/src/proto/search.proto
new file mode 100644
index 00000000..38b717f7
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/search.proto
@@ -0,0 +1,44 @@
+syntax = "proto2";
+
+message SearchRequest {
+ optional string query = 0x1;
+ optional Type type = 0x2;
+ enum Type {
+ TRACK = 0x0;
+ ALBUM = 0x1;
+ ARTIST = 0x2;
+ PLAYLIST = 0x3;
+ USER = 0x4;
+ }
+ optional int32 limit = 0x3;
+ optional int32 offset = 0x4;
+ optional bool did_you_mean = 0x5;
+ optional string spotify_uri = 0x2;
+ repeated bytes file_id = 0x3;
+ optional string url = 0x4;
+ optional string slask_id = 0x5;
+}
+
+message Playlist {
+ optional string uri = 0x1;
+ optional string name = 0x2;
+ repeated Image image = 0x3;
+}
+
+message User {
+ optional string username = 0x1;
+ optional string full_name = 0x2;
+ repeated Image image = 0x3;
+ optional sint32 followers = 0x4;
+}
+
+message SearchReply {
+ optional sint32 hits = 0x1;
+ repeated Track track = 0x2;
+ repeated Album album = 0x3;
+ repeated Artist artist = 0x4;
+ repeated Playlist playlist = 0x5;
+ optional string did_you_mean = 0x6;
+ repeated User user = 0x7;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/social.proto b/src/inputs/librespot-c/src/proto/social.proto
new file mode 100644
index 00000000..58d39a18
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/social.proto
@@ -0,0 +1,12 @@
+syntax = "proto2";
+
+message DecorationData {
+ optional string username = 0x1;
+ optional string full_name = 0x2;
+ optional string image_url = 0x3;
+ optional string large_image_url = 0x5;
+ optional string first_name = 0x6;
+ optional string last_name = 0x7;
+ optional string facebook_uid = 0x8;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/socialgraph.proto b/src/inputs/librespot-c/src/proto/socialgraph.proto
new file mode 100644
index 00000000..3adc1306
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/socialgraph.proto
@@ -0,0 +1,49 @@
+syntax = "proto2";
+
+message CountReply {
+ repeated int32 counts = 0x1;
+}
+
+message UserListRequest {
+ optional string last_result = 0x1;
+ optional int32 count = 0x2;
+ optional bool include_length = 0x3;
+}
+
+message UserListReply {
+ repeated User users = 0x1;
+ optional int32 length = 0x2;
+}
+
+message User {
+ optional string username = 0x1;
+ optional int32 subscriber_count = 0x2;
+ optional int32 subscription_count = 0x3;
+}
+
+message ArtistListReply {
+ repeated Artist artists = 0x1;
+}
+
+message Artist {
+ optional string artistid = 0x1;
+ optional int32 subscriber_count = 0x2;
+}
+
+message StringListRequest {
+ repeated string args = 0x1;
+}
+
+message StringListReply {
+ repeated string reply = 0x1;
+}
+
+message TopPlaylistsRequest {
+ optional string username = 0x1;
+ optional int32 count = 0x2;
+}
+
+message TopPlaylistsReply {
+ repeated string uris = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/spirc.proto b/src/inputs/librespot-c/src/proto/spirc.proto
new file mode 100644
index 00000000..acaeae1f
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/spirc.proto
@@ -0,0 +1,133 @@
+syntax = "proto2";
+
+message Frame {
+ optional uint32 version = 0x1;
+ optional string ident = 0x2;
+ optional string protocol_version = 0x3;
+ optional uint32 seq_nr = 0x4;
+ optional MessageType typ = 0x5;
+ optional DeviceState device_state = 0x7;
+ optional Goodbye goodbye = 0xb;
+ optional State state = 0xc;
+ optional uint32 position = 0xd;
+ optional uint32 volume = 0xe;
+ optional int64 state_update_id = 0x11;
+ repeated string recipient = 0x12;
+ optional bytes context_player_state = 0x13;
+ optional string new_name = 0x14;
+ optional Metadata metadata = 0x19;
+}
+
+enum MessageType {
+ kMessageTypeHello = 0x1;
+ kMessageTypeGoodbye = 0x2;
+ kMessageTypeProbe = 0x3;
+ kMessageTypeNotify = 0xa;
+ kMessageTypeLoad = 0x14;
+ kMessageTypePlay = 0x15;
+ kMessageTypePause = 0x16;
+ kMessageTypePlayPause = 0x17;
+ kMessageTypeSeek = 0x18;
+ kMessageTypePrev = 0x19;
+ kMessageTypeNext = 0x1a;
+ kMessageTypeVolume = 0x1b;
+ kMessageTypeShuffle = 0x1c;
+ kMessageTypeRepeat = 0x1d;
+ kMessageTypeVolumeDown = 0x1f;
+ kMessageTypeVolumeUp = 0x20;
+ kMessageTypeReplace = 0x21;
+ kMessageTypeLogout = 0x22;
+ kMessageTypeAction = 0x23;
+ kMessageTypeRename = 0x24;
+ kMessageTypeUpdateMetadata = 0x80;
+}
+
+message DeviceState {
+ optional string sw_version = 0x1;
+ optional bool is_active = 0xa;
+ optional bool can_play = 0xb;
+ optional uint32 volume = 0xc;
+ optional string name = 0xd;
+ optional uint32 error_code = 0xe;
+ optional int64 became_active_at = 0xf;
+ optional string error_message = 0x10;
+ repeated Capability capabilities = 0x11;
+ optional string context_player_error = 0x14;
+ repeated Metadata metadata = 0x19;
+}
+
+message Capability {
+ optional CapabilityType typ = 0x1;
+ repeated int64 intValue = 0x2;
+ repeated string stringValue = 0x3;
+}
+
+enum CapabilityType {
+ kSupportedContexts = 0x1;
+ kCanBePlayer = 0x2;
+ kRestrictToLocal = 0x3;
+ kDeviceType = 0x4;
+ kGaiaEqConnectId = 0x5;
+ kSupportsLogout = 0x6;
+ kIsObservable = 0x7;
+ kVolumeSteps = 0x8;
+ kSupportedTypes = 0x9;
+ kCommandAcks = 0xa;
+ kSupportsRename = 0xb;
+ kHidden = 0xc;
+ kSupportsPlaylistV2 = 0xd;
+ kSupportsExternalEpisodes = 0xe;
+}
+
+message Goodbye {
+ optional string reason = 0x1;
+}
+
+message State {
+ optional string context_uri = 0x2;
+ optional uint32 index = 0x3;
+ optional uint32 position_ms = 0x4;
+ optional PlayStatus status = 0x5;
+ optional uint64 position_measured_at = 0x7;
+ optional string context_description = 0x8;
+ optional bool shuffle = 0xd;
+ optional bool repeat = 0xe;
+ optional string last_command_ident = 0x14;
+ optional uint32 last_command_msgid = 0x15;
+ optional bool playing_from_fallback = 0x18;
+ optional uint32 row = 0x19;
+ optional uint32 playing_track_index = 0x1a;
+ repeated TrackRef track = 0x1b;
+ optional Ad ad = 0x1c;
+}
+
+enum PlayStatus {
+ kPlayStatusStop = 0x0;
+ kPlayStatusPlay = 0x1;
+ kPlayStatusPause = 0x2;
+ kPlayStatusLoading = 0x3;
+}
+
+message TrackRef {
+ optional bytes gid = 0x1;
+ optional string uri = 0x2;
+ optional bool queued = 0x3;
+ optional string context = 0x4;
+}
+
+message Ad {
+ optional int32 next = 0x1;
+ optional bytes ogg_fid = 0x2;
+ optional bytes image_fid = 0x3;
+ optional int32 duration = 0x4;
+ optional string click_url = 0x5;
+ optional string impression_url = 0x6;
+ optional string product = 0x7;
+ optional string advertiser = 0x8;
+ optional bytes gid = 0x9;
+}
+
+message Metadata {
+ optional string type = 0x1;
+ optional string metadata = 0x2;
+}
diff --git a/src/inputs/librespot-c/src/proto/suggest.proto b/src/inputs/librespot-c/src/proto/suggest.proto
new file mode 100644
index 00000000..ef45f1e2
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/suggest.proto
@@ -0,0 +1,43 @@
+syntax = "proto2";
+
+message Track {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional bytes image = 0x3;
+ repeated string artist_name = 0x4;
+ repeated bytes artist_gid = 0x5;
+ optional uint32 rank = 0x6;
+}
+
+message Artist {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional bytes image = 0x3;
+ optional uint32 rank = 0x6;
+}
+
+message Album {
+ optional bytes gid = 0x1;
+ optional string name = 0x2;
+ optional bytes image = 0x3;
+ repeated string artist_name = 0x4;
+ repeated bytes artist_gid = 0x5;
+ optional uint32 rank = 0x6;
+}
+
+message Playlist {
+ optional string uri = 0x1;
+ optional string name = 0x2;
+ optional string image_uri = 0x3;
+ optional string owner_name = 0x4;
+ optional string owner_uri = 0x5;
+ optional uint32 rank = 0x6;
+}
+
+message Suggestions {
+ repeated Track track = 0x1;
+ repeated Album album = 0x2;
+ repeated Artist artist = 0x3;
+ repeated Playlist playlist = 0x4;
+}
+
diff --git a/src/inputs/librespot-c/src/proto/toplist.proto b/src/inputs/librespot-c/src/proto/toplist.proto
new file mode 100644
index 00000000..1a12159f
--- /dev/null
+++ b/src/inputs/librespot-c/src/proto/toplist.proto
@@ -0,0 +1,6 @@
+syntax = "proto2";
+
+message Toplist {
+ repeated string items = 0x1;
+}
+
diff --git a/src/inputs/librespot-c/src/shannon/Shannon.h b/src/inputs/librespot-c/src/shannon/Shannon.h
new file mode 100644
index 00000000..d5d0fe36
--- /dev/null
+++ b/src/inputs/librespot-c/src/shannon/Shannon.h
@@ -0,0 +1,52 @@
+/* $Id: $ */
+/* Shannon: Shannon stream cipher and MAC header files */
+
+/*
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND AGAINST
+INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _SHN_DEFINED
+#define _SHN_DEFINED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define SHANNON_N 16
+
+typedef struct {
+ uint32_t R[SHANNON_N]; /* Working storage for the shift register */
+ uint32_t CRC[SHANNON_N]; /* Working storage for CRC accumulation */
+ uint32_t initR[SHANNON_N]; /* saved register contents */
+ uint32_t konst; /* key dependent semi-constant */
+ uint32_t sbuf; /* encryption buffer */
+ uint32_t mbuf; /* partial word MAC buffer */
+ int nbuf; /* number of part-word stream bits buffered */
+} shn_ctx;
+
+/* interface definitions */
+void shn_key(shn_ctx *c, const uint8_t key[], int keylen); /* set key */
+void shn_nonce(shn_ctx *c, const uint8_t nonce[], int nlen); /* set Init Vector */
+void shn_stream(shn_ctx *c, uint8_t *buf, int nbytes); /* stream cipher */
+void shn_maconly(shn_ctx *c, uint8_t *buf, int nbytes); /* accumulate MAC */
+void shn_encrypt(shn_ctx *c, uint8_t *buf, int nbytes); /* encrypt + MAC */
+void shn_decrypt(shn_ctx *c, uint8_t *buf, int nbytes); /* decrypt + MAC */
+void shn_finish(shn_ctx *c, uint8_t *buf, int nbytes); /* finalise MAC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHN_DEFINED */
diff --git a/src/inputs/librespot-c/src/shannon/ShannonFast.c b/src/inputs/librespot-c/src/shannon/ShannonFast.c
new file mode 100644
index 00000000..ca91bf7b
--- /dev/null
+++ b/src/inputs/librespot-c/src/shannon/ShannonFast.c
@@ -0,0 +1,626 @@
+/* $Id: shnfast.c 442 2006-05-12 23:22:21Z ggr $ */
+/* ShannonFast: Shannon stream cipher and MAC -- fast implementation */
+
+/*
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND AGAINST
+INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* interface, multiplication table and SBox */
+#include
+#include
+#include "ShannonInternal.h"
+
+/*
+ * FOLD is how many register cycles need to be performed after combining the
+ * last byte of key and non-linear feedback, before every byte depends on every
+ * byte of the key. This depends on the feedback and nonlinear functions, and
+ * on where they are combined into the register. Making it same as the
+ * register length is a safe and conservative choice.
+ */
+#define FOLD N /* how many iterations of folding to do */
+#define INITKONST 0x6996c53a /* value of KONST to use during key loading */
+#define KEYP 13 /* where to insert key/MAC words */
+
+#define Byte(x,i) ((UCHAR)(((x) >> (8*i)) & 0xFF))
+
+/* define IS_LITTLE_ENDIAN for faster operation when appropriate */
+#ifdef IS_LITTLE_ENDIAN
+/* Useful macros -- little endian words on a little endian machine */
+#define BYTE2WORD(b) (*(WORD *)(b))
+#define WORD2BYTE(w, b) ((*(WORD *)(b)) = w)
+#define XORWORD(w, b) ((*(WORD *)(b)) ^= w)
+#else
+/* Useful macros -- machine independent little-endian version */
+#define BYTE2WORD(b) ( \
+ (((WORD)(b)[3] & 0xFF)<<24) | \
+ (((WORD)(b)[2] & 0xFF)<<16) | \
+ (((WORD)(b)[1] & 0xFF)<<8) | \
+ (((WORD)(b)[0] & 0xFF)) \
+)
+#define WORD2BYTE(w, b) { \
+ (b)[3] = Byte(w,3); \
+ (b)[2] = Byte(w,2); \
+ (b)[1] = Byte(w,1); \
+ (b)[0] = Byte(w,0); \
+}
+#define XORWORD(w, b) { \
+ (b)[3] ^= Byte(w,3); \
+ (b)[2] ^= Byte(w,2); \
+ (b)[1] ^= Byte(w,1); \
+ (b)[0] ^= Byte(w,0); \
+}
+#endif
+
+/* give correct offset for the current position of the register,
+ * where logically R[0] is at position "zero". Note that this works for
+ * both the stream register and the CRC register.
+ */
+#define OFF(zero, i) (((zero)+(i)) % N)
+
+/* step the shift register */
+/* After stepping, "zero" moves right one place */
+#define STEP(c,z) \
+ { \
+ t = c->R[OFF(z,12)] ^ c->R[OFF(z,13)] ^ c->konst; \
+ /* Sbox 1 */ \
+ t ^= ROTL(t, 5) | ROTL(t, 7); \
+ t ^= ROTL(t, 19) | ROTL(t, 22); \
+ c->R[OFF(z,0)] = t ^ ROTL(c->R[OFF(z,0)],1); \
+ t = c->R[OFF((z+1),2)] ^ c->R[OFF((z+1),15)]; \
+ /* Sbox 2 */ \
+ t ^= ROTL(t, 7) | ROTL(t, 22); \
+ t ^= ROTL(t, 5) | ROTL(t, 19); \
+ c->R[OFF((z+1),0)] ^= t; \
+ c->sbuf = t ^ c->R[OFF((z+1),8)] ^ c->R[OFF((z+1),12)]; \
+ }
+
+static void
+cycle(shn_ctx *c)
+{
+ WORD t;
+ int i;
+
+ /* nonlinear feedback function */
+ STEP(c,0);
+ /* shift register */
+ t = c->R[0];
+ for (i = 1; i < N; ++i)
+ c->R[i-1] = c->R[i];
+ c->R[N-1] = t;
+}
+
+/* The Shannon MAC function is modelled after the concepts of Phelix and SHA.
+ * Basically, words to be accumulated in the MAC are incorporated in two
+ * different ways:
+ * 1. They are incorporated into the stream cipher register at a place
+ * where they will immediately have a nonlinear effect on the state
+ * 2. They are incorporated into bit-parallel CRC-16 registers; the
+ * contents of these registers will be used in MAC finalization.
+ */
+
+
+/* Accumulate a CRC of input words, later to be fed into MAC.
+ * This is actually 32 parallel CRC-16s, using the IBM CRC-16
+ * polynomial x^16 + x^15 + x^2 + 1.
+ */
+#define CRCFUNC(c,i,z) \
+ { \
+ c->CRC[OFF(z,0)] ^= c->CRC[OFF(z,2)] ^ c->CRC[OFF(z,15)] ^ i; \
+ }
+
+static void
+crcfunc(shn_ctx *c, WORD i)
+{
+ WORD t;
+
+ CRCFUNC(c, i, 0);
+ /* now correct alignment of CRC accumulator */
+ t = c->CRC[0];
+ for (i = 1; i < N; ++i)
+ c->CRC[i-1] = c->CRC[i];
+ c->CRC[N-1] = t;
+}
+
+/* Normal MAC word processing: do both SHA and CRC.
+ */
+static void
+macfunc(shn_ctx *c, WORD i)
+{
+ crcfunc(c, i);
+ c->R[KEYP] ^= i;
+}
+
+/* initialise to known state
+ */
+static void
+shn_initstate(shn_ctx *c)
+{
+ int i;
+
+ /* Register initialised to Fibonacci numbers; Counter zeroed. */
+ c->R[0] = 1;
+ c->R[1] = 1;
+ for (i = 2; i < N; ++i)
+ c->R[i] = c->R[i-1] + c->R[i-2];
+ c->konst = INITKONST;
+}
+
+/* Save the current register state
+ */
+static void
+shn_savestate(shn_ctx *c)
+{
+ int i;
+
+ for (i = 0; i < N; ++i)
+ c->initR[i] = c->R[i];
+}
+
+/* initialise to previously saved register state
+ */
+static void
+shn_reloadstate(shn_ctx *c)
+{
+ int i;
+
+ for (i = 0; i < N; ++i)
+ c->R[i] = c->initR[i];
+}
+
+/* Initialise "konst"
+ */
+static void
+shn_genkonst(shn_ctx *c)
+{
+ c->konst = c->R[0];
+}
+
+/* Load key material into the register
+ */
+#define ADDKEY(k) \
+ c->R[KEYP] ^= (k);
+
+/* nonlinear diffusion of register for key and MAC */
+#define DROUND(z) { register WORD t; STEP(c,z); }
+static void
+shn_diffuse(shn_ctx *c)
+{
+ /* relies on FOLD == N! */
+ DROUND(0);
+ DROUND(1);
+ DROUND(2);
+ DROUND(3);
+ DROUND(4);
+ DROUND(5);
+ DROUND(6);
+ DROUND(7);
+ DROUND(8);
+ DROUND(9);
+ DROUND(10);
+ DROUND(11);
+ DROUND(12);
+ DROUND(13);
+ DROUND(14);
+ DROUND(15);
+}
+
+/* common actions for loading key material
+ * Allow non-word-multiple key and nonce materianl
+ * Note also initializes the CRC register as a side effect.
+ */
+static void
+shn_loadkey(shn_ctx *c, const uint8_t key[], int keylen)
+{
+ int i, j;
+ WORD k;
+ uint8_t xtra[4];
+
+ /* start folding in key */
+ for (i = 0; i < (keylen & ~0x3); i += 4)
+ {
+ k = BYTE2WORD(&key[i]);
+ ADDKEY(k);
+ cycle(c);
+ }
+
+ /* if there were any extra key bytes, zero pad to a word */
+ if (i < keylen) {
+ for (j = 0 /* i unchanged */; i < keylen; ++i)
+ xtra[j++] = key[i];
+ for (/* j unchanged */; j < 4; ++j)
+ xtra[j] = 0;
+ k = BYTE2WORD(xtra);
+ ADDKEY(k);
+ cycle(c);
+ }
+
+ /* also fold in the length of the key */
+ ADDKEY(keylen);
+ cycle(c);
+
+ /* save a copy of the register */
+ for (i = 0; i < N; ++i)
+ c->CRC[i] = c->R[i];
+
+ /* now diffuse */
+ shn_diffuse(c);
+
+ /* now xor the copy back -- makes key loading irreversible */
+ for (i = 0; i < N; ++i)
+ c->R[i] ^= c->CRC[i];
+}
+
+/* Published "key" interface
+ */
+void
+shn_key(shn_ctx *c, const uint8_t key[], int keylen)
+{
+ shn_initstate(c);
+ shn_loadkey(c, key, keylen);
+ shn_genkonst(c);
+ shn_savestate(c);
+ c->nbuf = 0;
+}
+
+/* Published "nonce" interface
+ */
+void
+shn_nonce(shn_ctx *c, const uint8_t nonce[], int noncelen)
+{
+ shn_reloadstate(c);
+ c->konst = INITKONST;
+ shn_loadkey(c, nonce, noncelen);
+ shn_genkonst(c);
+ c->nbuf = 0;
+}
+
+/* XOR pseudo-random bytes into buffer
+ * Note: doesn't play well with MAC functions.
+ */
+#define SROUND(z) \
+ { register WORD t; \
+ STEP(c,z); \
+ XORWORD(c->sbuf, buf+(z*4)); \
+ }
+void
+shn_stream(shn_ctx *c, uint8_t *buf, int nbytes)
+{
+ /* handle any previously buffered bytes */
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+
+ /* do lots at a time, if there's enough to do */
+ while (nbytes >= N*4)
+ {
+ SROUND(0);
+ SROUND(1);
+ SROUND(2);
+ SROUND(3);
+ SROUND(4);
+ SROUND(5);
+ SROUND(6);
+ SROUND(7);
+ SROUND(8);
+ SROUND(9);
+ SROUND(10);
+ SROUND(11);
+ SROUND(12);
+ SROUND(13);
+ SROUND(14);
+ SROUND(15);
+ buf += 4*N;
+ nbytes -= N*4;
+ }
+
+ /* do small or odd size buffers the slow way */
+ while (4 <= nbytes) {
+ cycle(c);
+ XORWORD(c->sbuf, buf);
+ buf += 4;
+ nbytes -= 4;
+ }
+
+ /* handle any trailing bytes */
+ if (nbytes != 0) {
+ cycle(c);
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* accumulate words into MAC without encryption
+ * Note that plaintext is accumulated for MAC.
+ */
+#define MROUND(z) \
+ { register WORD t, t1; \
+ t1 = BYTE2WORD(buf+(z*4)); \
+ STEP(c,z); \
+ CRCFUNC(c,t1,z); \
+ c->R[OFF(z+1,KEYP)] ^= t1; \
+ }
+void
+shn_maconly(shn_ctx *c, uint8_t *buf, int nbytes)
+{
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= (*buf++) << (32 - c->nbuf);
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc(c, c->mbuf);
+ }
+
+ /* do lots at a time, if there's enough to do */
+ while (4*N <= nbytes)
+ {
+ MROUND( 0);
+ MROUND( 1);
+ MROUND( 2);
+ MROUND( 3);
+ MROUND( 4);
+ MROUND( 5);
+ MROUND( 6);
+ MROUND( 7);
+ MROUND( 8);
+ MROUND( 9);
+ MROUND(10);
+ MROUND(11);
+ MROUND(12);
+ MROUND(13);
+ MROUND(14);
+ MROUND(15);
+ buf += 4*N;
+ nbytes -= 4*N;
+ }
+
+ /* do small or odd size buffers the slow way */
+ while (4 <= nbytes) {
+ cycle(c);
+ macfunc(c, BYTE2WORD(buf));
+ buf += 4;
+ nbytes -= 4;
+ }
+
+ /* handle any trailing bytes */
+ if (nbytes != 0) {
+ cycle(c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (nbytes != 0) {
+ c->mbuf ^= (*buf++) << (32 - c->nbuf);
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Combined MAC and encryption.
+ * Note that plaintext is accumulated for MAC.
+ */
+#define EROUND(z) \
+ { register WORD t, t3; \
+ STEP(c,z); \
+ t3 = BYTE2WORD(buf+(z*4)); \
+ CRCFUNC(c,t3,z); \
+ c->R[OFF((z+1),KEYP)] ^= t3; \
+ t3 ^= c->sbuf; \
+ WORD2BYTE(t3,buf+(z*4)); \
+ }
+void
+shn_encrypt(shn_ctx *c, uint8_t *buf, int nbytes)
+{
+ WORD t3 = 0;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc(c, c->mbuf);
+ }
+
+ /* do lots at a time, if there's enough to do */
+ while (4*N <= nbytes)
+ {
+ EROUND( 0);
+ EROUND( 1);
+ EROUND( 2);
+ EROUND( 3);
+ EROUND( 4);
+ EROUND( 5);
+ EROUND( 6);
+ EROUND( 7);
+ EROUND( 8);
+ EROUND( 9);
+ EROUND(10);
+ EROUND(11);
+ EROUND(12);
+ EROUND(13);
+ EROUND(14);
+ EROUND(15);
+ buf += 4*N;
+ nbytes -= 4*N;
+ }
+
+ /* do small or odd size buffers the slow way */
+ while (4 <= nbytes) {
+ cycle(c);
+ t3 = BYTE2WORD(buf);
+ macfunc(c, t3);
+ t3 ^= c->sbuf;
+ WORD2BYTE(t3, buf);
+ nbytes -= 4;
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ if (nbytes != 0) {
+ cycle(c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Combined MAC and decryption.
+ * Note that plaintext is accumulated for MAC.
+ */
+#undef DROUND
+#define DROUND(z) \
+ { register WORD t, t3; \
+ STEP(c,z); \
+ t3 = BYTE2WORD(buf+(z*4)); \
+ t3 ^= c->sbuf; \
+ CRCFUNC(c,t3,z); \
+ c->R[OFF((z+1),KEYP)] ^= t3; \
+ WORD2BYTE(t3, buf+(z*4)); \
+ }
+void
+shn_decrypt(shn_ctx *c, uint8_t *buf, int nbytes)
+{
+ WORD t3 = 0;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc(c, c->mbuf);
+ }
+
+ /* now do lots at a time, if there's enough */
+ while (4*N <= nbytes)
+ {
+ DROUND( 0);
+ DROUND( 1);
+ DROUND( 2);
+ DROUND( 3);
+ DROUND( 4);
+ DROUND( 5);
+ DROUND( 6);
+ DROUND( 7);
+ DROUND( 8);
+ DROUND( 9);
+ DROUND(10);
+ DROUND(11);
+ DROUND(12);
+ DROUND(13);
+ DROUND(14);
+ DROUND(15);
+ buf += 4*N;
+ nbytes -= 4*N;
+ }
+
+ /* do small or odd size buffers the slow way */
+ while (4 <= nbytes) {
+ cycle(c);
+ t3 = BYTE2WORD(buf);
+ t3 ^= c->sbuf;
+ macfunc(c, t3);
+ WORD2BYTE(t3, buf);
+ nbytes -= 4;
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ if (nbytes != 0) {
+ cycle(c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Having accumulated a MAC, finish processing and return it.
+ * Note that any unprocessed bytes are treated as if
+ * they were encrypted zero bytes, so plaintext (zero) is accumulated.
+ */
+void
+shn_finish(shn_ctx *c, uint8_t *buf, int nbytes)
+{
+ int i;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ /* LFSR already cycled */
+ macfunc(c, c->mbuf);
+ }
+
+ /* perturb the MAC to mark end of input.
+ * Note that only the stream register is updated, not the CRC. This is an
+ * action that can't be duplicated by passing in plaintext, hence
+ * defeating any kind of extension attack.
+ */
+ cycle(c);
+ ADDKEY(INITKONST ^ (c->nbuf << 3));
+ c->nbuf = 0;
+
+ /* now add the CRC to the stream register and diffuse it */
+ for (i = 0; i < N; ++i)
+ c->R[i] ^= c->CRC[i];
+ shn_diffuse(c);
+
+ /* produce output from the stream buffer */
+ while (nbytes > 0) {
+ cycle(c);
+ if (nbytes >= 4) {
+ WORD2BYTE(c->sbuf, buf);
+ nbytes -= 4;
+ buf += 4;
+ }
+ else {
+ for (i = 0; i < nbytes; ++i)
+ buf[i] = Byte(c->sbuf, i);
+ break;
+ }
+ }
+}
diff --git a/src/inputs/librespot-c/src/shannon/ShannonInternal.h b/src/inputs/librespot-c/src/shannon/ShannonInternal.h
new file mode 100644
index 00000000..6dd1b0ca
--- /dev/null
+++ b/src/inputs/librespot-c/src/shannon/ShannonInternal.h
@@ -0,0 +1,23 @@
+#ifndef SHANNONINTERNAL_H
+#define SHANNONINTERNAL_H
+
+#include "Shannon.h"
+
+#include
+
+#define N SHANNON_N
+#define WORDSIZE 32
+#define UCHAR unsigned char
+
+#define WORD uint32_t
+#define WORD_MAX UINT32_MAX
+
+#if WORD_MAX == 0xffffffff
+#define ROTL(w,x) (((w) << (x))|((w) >> (32 - (x))))
+#define ROTR(w,x) (((w) >> (x))|((w) << (32 - (x))))
+#else
+#define ROTL(w,x) (((w) << (x))|(((w) & 0xffffffff) >> (32 - (x))))
+#define ROTR(w,x) ((((w) & 0xffffffff) >> (x))|((w) << (32 - (x))))
+#endif
+
+#endif
diff --git a/src/inputs/librespot-c/tests/.gitignore b/src/inputs/librespot-c/tests/.gitignore
new file mode 100644
index 00000000..a5bce3fd
--- /dev/null
+++ b/src/inputs/librespot-c/tests/.gitignore
@@ -0,0 +1 @@
+test1
diff --git a/src/inputs/librespot-c/tests/Makefile.am b/src/inputs/librespot-c/tests/Makefile.am
new file mode 100644
index 00000000..893e0857
--- /dev/null
+++ b/src/inputs/librespot-c/tests/Makefile.am
@@ -0,0 +1,10 @@
+TEST_CFLAGS = $(CFLAGS) $(JSON_C_CFLAGS) $(LIBCURL_CFLAGS) $(LIBEVENT_CFLAGS) $(LIBGCRYPT_CFLAGS) $(LIBPROTOBUF_C_CFLAGS)
+TEST_LIBS = $(LIBS) $(JSON_C_LIBS) $(LIBCURL_LIBS) $(LIBEVENT_LIBS) $(LIBGCRYPT_LIBS) $(LIBPROTOBUF_C_LIBS)
+
+AM_CPPFLAGS = -I$(top_srcdir)
+
+test1_SOURCES = test1.c
+test1_LDADD = $(top_builddir)/librespot-c.a -lpthread $(TEST_LIBS)
+test1_CFLAGS = $(TEST_CFLAGS)
+
+check_PROGRAMS = test1
diff --git a/src/inputs/librespot-c/tests/test1.c b/src/inputs/librespot-c/tests/test1.c
new file mode 100644
index 00000000..6d645143
--- /dev/null
+++ b/src/inputs/librespot-c/tests/test1.c
@@ -0,0 +1,357 @@
+#include
+#include
+#include
+#include
+#include
+
+// For file output
+#include
+#include
+
+#include
+#include
+#include
+
+#include "librespot-c.h"
+
+static int audio_fd = -1;
+static int test_file = -1;
+static struct event_base *evbase;
+static struct evbuffer *audio_buf;
+
+static int total_bytes;
+
+#include // for isprint()
+
+static void
+hexdump_dummy(const char *msg, uint8_t *mem, size_t len)
+{
+ return;
+}
+
+static void
+hexdump(const char *msg, uint8_t *mem, size_t len)
+{
+ int i, j;
+ int hexdump_cols = 16;
+
+ if (msg)
+ printf("%s", msg);
+
+ for (i = 0; i < len + ((len % hexdump_cols) ? (hexdump_cols - len % hexdump_cols) : 0); i++)
+ {
+ if(i % hexdump_cols == 0)
+ printf("0x%06x: ", i);
+
+ if (i < len)
+ printf("%02x ", 0xFF & ((char*)mem)[i]);
+ else
+ printf(" ");
+
+ if (i % hexdump_cols == (hexdump_cols - 1))
+ {
+ for (j = i - (hexdump_cols - 1); j <= i; j++)
+ {
+ if (j >= len)
+ putchar(' ');
+ else if (isprint(((char*)mem)[j]))
+ putchar(0xFF & ((char*)mem)[j]);
+ else
+ putchar('.');
+ }
+
+ putchar('\n');
+ }
+ }
+}
+
+static void
+logmsg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static size_t
+https_write_cb(char *data, size_t size, size_t nmemb, void *userdata)
+{
+ char **body;
+ size_t realsize;
+
+ realsize = size * nmemb;
+ body = (char **)userdata;
+
+ *body = malloc(realsize + 1);
+ memcpy(*body, data, realsize);
+ (*body)[realsize] = 0;
+
+ return realsize;
+}
+
+static int
+https_get(char **body, const char *url)
+{
+ CURL *curl;
+ CURLcode res;
+ long response_code;
+
+ curl = curl_easy_init();
+ if (!curl)
+ {
+ printf("Could not initialize CURL\n");
+ goto error;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, https_write_cb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, body);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ {
+ printf("CURL could not make request (%d)\n", (int)res);
+ goto error;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+ if (response_code != 200)
+ {
+ printf("HTTP response code %d\n", (int)response_code);
+ goto error;
+ }
+
+ curl_easy_cleanup(curl);
+
+ return 0;
+
+ error:
+ curl_easy_cleanup(curl);
+ return -1;
+}
+
+static int
+tcp_connect(const char *address, unsigned short port)
+{
+ struct addrinfo hints = { 0 };
+ struct addrinfo *servinfo;
+ struct addrinfo *ptr;
+ char strport[8];
+ int fd;
+ int ret;
+
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+
+ snprintf(strport, sizeof(strport), "%hu", port);
+ ret = getaddrinfo(address, strport, &hints, &servinfo);
+ if (ret < 0)
+ {
+ printf("Could not connect to %s (port %u): %s\n", address, port, gai_strerror(ret));
+ return -1;
+ }
+
+ for (ptr = servinfo; ptr; ptr = ptr->ai_next)
+ {
+ fd = socket(ptr->ai_family, SOCK_STREAM, ptr->ai_protocol);
+ if (fd < 0)
+ {
+ continue;
+ }
+
+ ret = connect(fd, ptr->ai_addr, ptr->ai_addrlen);
+ if (ret < 0)
+ {
+ close(fd);
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(servinfo);
+
+ if (!ptr)
+ {
+ printf("Could not connect to '%s' (port %u): %s\n", address, port, strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
+
+static void
+tcp_disconnect(int fd)
+{
+ if (fd < 0)
+ return;
+
+ close(fd);
+}
+
+static void
+progress_cb(int fd, void *arg, size_t received, size_t len)
+{
+ printf("Progress on fd %d is %zu/%zu\n", fd, received, len);
+}
+
+// This thread
+static void
+audio_read_cb(int fd, short what, void *arg)
+{
+ int got;
+
+ got = evbuffer_read(audio_buf, fd, -1);
+ if (got <= 0)
+ {
+ printf("Playback ended (%d)\n", got);
+ event_base_loopbreak(evbase);
+ return;
+ }
+
+ total_bytes += got;
+
+ printf("Got %d bytes of audio, total received is %d bytes\n", got, total_bytes);
+
+ evbuffer_write(audio_buf, test_file);
+}
+
+struct sp_callbacks callbacks =
+{
+ .https_get = https_get,
+ .tcp_connect = tcp_connect,
+ .tcp_disconnect = tcp_disconnect,
+
+ .thread_name_set = NULL,
+
+ .hexdump = hexdump,
+ .logmsg = logmsg,
+};
+
+int
+main(int argc, char * argv[])
+{
+ struct sp_session *session = NULL;
+ struct sp_sysinfo sysinfo;
+ struct sp_credentials credentials;
+ struct sp_metadata metadata;
+ struct event *read_ev;
+// struct event *stop_ev;
+// struct timeval tv = { 0 };
+ int ret;
+
+ if (argc != 4)
+ {
+ printf("%s spotify_path username password|token\n", argv[0]);
+ goto error;
+ }
+
+ test_file = open("testfile.ogg", O_CREAT | O_RDWR, 0664);
+ if (test_file < 0)
+ {
+ printf("Error opening file: %s\n", strerror(errno));
+ goto error;
+ }
+
+ snprintf(sysinfo.client_name, sizeof(sysinfo.client_name), "myclient");
+ snprintf(sysinfo.client_version, sizeof(sysinfo.client_version), "0.1");
+ snprintf(sysinfo.client_build_id, sizeof(sysinfo.client_build_id), "a");
+ snprintf(sysinfo.device_id, sizeof(sysinfo.device_id), "aabbccddeeff");
+
+ ret = librespotc_init(&sysinfo, &callbacks);
+ if (ret < 0)
+ {
+ printf("Error initializing Spotify: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ if (strlen(argv[3]) < 100)
+ session = librespotc_login_password(argv[2], argv[3]);
+ else
+ session = librespotc_login_token(argv[2], argv[3]); // Length of token should be 194
+ if (!session)
+ {
+ printf("Error logging in: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ printf("\n --- Login OK --- \n");
+
+ ret = librespotc_credentials_get(&credentials, session);
+ if (ret < 0)
+ {
+ printf("Error getting session credentials: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ printf("Username is %s\n", credentials.username);
+
+ audio_fd = librespotc_open(argv[1], session);
+ if (audio_fd < 0)
+ {
+ printf("Error opening file: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ ret = librespotc_metadata_get(&metadata, audio_fd);
+ if (ret < 0)
+ {
+ printf("Error getting track metadata: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ printf("File is open, length is %zu\n", metadata.file_len);
+
+ ret = librespotc_seek(audio_fd, 1000000);
+ if (ret < 0)
+ {
+ printf("Error seeking: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ evbase = event_base_new();
+ audio_buf = evbuffer_new();
+
+ read_ev = event_new(evbase, audio_fd, EV_READ | EV_PERSIST, audio_read_cb, NULL);
+ event_add(read_ev, NULL);
+
+ librespotc_write(audio_fd, progress_cb, NULL);
+
+// stop_ev = evtimer_new(evbase, stop, &audio_fd);
+// tv.tv_sec = 2;
+// event_add(stop_ev, &tv);
+
+ event_base_dispatch(evbase);
+
+// event_free(stop_ev);
+ event_free(read_ev);
+
+ evbuffer_free(audio_buf);
+
+ event_base_free(evbase);
+
+ librespotc_close(audio_fd);
+
+ close(test_file);
+
+ librespotc_logout(session);
+
+ librespotc_deinit();
+
+ return 0;
+
+ error:
+ if (audio_fd >= 0)
+ librespotc_close(audio_fd);
+ if (test_file >= 0)
+ close(test_file);
+ if (session)
+ librespotc_logout(session);
+
+ librespotc_deinit();
+
+ return -1;
+}
diff --git a/src/spotify.c b/src/inputs/libspotify/libspotify.c
similarity index 97%
rename from src/spotify.c
rename to src/inputs/libspotify/libspotify.c
index 53ba6655..d4252451 100644
--- a/src/spotify.c
+++ b/src/inputs/libspotify/libspotify.c
@@ -43,15 +43,15 @@
#include
#include
-#include "spotify.h"
-#include "spotify_webapi.h"
+#include "libspotify.h"
+#include "library.h"
+#include "library/spotify_webapi.h"
#include "logger.h"
#include "misc.h"
#include "http.h"
#include "conffile.h"
#include "cache.h"
#include "commands.h"
-#include "library.h"
#include "input.h"
#include "listener.h"
@@ -86,6 +86,9 @@ struct artwork_get_param
int is_loaded;
};
+static void
+libspotify_playback_stop_nonblock(void);
+
/* --- Globals --- */
// Spotify thread
static pthread_t tid_spotify;
@@ -604,7 +607,7 @@ playback_setup(void *arg, int *retval)
if (SP_ERROR_OK != err)
{
DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed: %s\n", fptr_sp_error_message(err));
- *retval = (SP_ERROR_IS_LOADING == err) ? SPOTIFY_SETUP_ERROR_IS_LOADING : -1;
+ *retval = (SP_ERROR_IS_LOADING == err) ? LIBSPOTIFY_SETUP_ERROR_IS_LOADING : -1;
return COMMAND_END;
}
@@ -1014,7 +1017,7 @@ static int music_delivery(sp_session *sess, const sp_audioformat *format,
if ((format->sample_type != SP_SAMPLETYPE_INT16_NATIVE_ENDIAN) || (format->channels != 2))
{
DPRINTF(E_LOG, L_SPOTIFY, "Got music with unsupported sample format or number of channels, stopping playback\n");
- spotify_playback_stop_nonblock();
+ libspotify_playback_stop_nonblock();
return num_frames;
}
@@ -1081,7 +1084,7 @@ static void play_token_lost(sp_session *sess)
{
DPRINTF(E_LOG, L_SPOTIFY, "Music interrupted - some other session is playing on the account\n");
- spotify_playback_stop_nonblock();
+ libspotify_playback_stop_nonblock();
}
static void connectionstate_updated(sp_session *session)
@@ -1093,7 +1096,7 @@ static void connectionstate_updated(sp_session *session)
else if (g_state == SPOTIFY_STATE_PLAYING)
{
DPRINTF(E_LOG, L_SPOTIFY, "Music interrupted - connection error or logged out\n");
- spotify_playback_stop_nonblock();
+ libspotify_playback_stop_nonblock();
}
}
@@ -1214,7 +1217,7 @@ notify_cb(int fd, short what, void *arg)
/* Thread: player */
int
-spotify_playback_setup(const char *path)
+libspotify_playback_setup(const char *path)
{
sp_link *link;
@@ -1231,7 +1234,7 @@ spotify_playback_setup(const char *path)
}
int
-spotify_playback_play()
+libspotify_playback_play()
{
DPRINTF(E_DBG, L_SPOTIFY, "Playback request\n");
@@ -1239,7 +1242,7 @@ spotify_playback_play()
}
int
-spotify_playback_pause()
+libspotify_playback_pause()
{
DPRINTF(E_DBG, L_SPOTIFY, "Pause request\n");
@@ -1248,7 +1251,7 @@ spotify_playback_pause()
/* Thread: libspotify */
void
-spotify_playback_pause_nonblock(void)
+libspotify_playback_pause_nonblock(void)
{
DPRINTF(E_DBG, L_SPOTIFY, "Nonblock pause request\n");
@@ -1257,7 +1260,7 @@ spotify_playback_pause_nonblock(void)
/* Thread: player and libspotify */
int
-spotify_playback_stop(void)
+libspotify_playback_stop(void)
{
DPRINTF(E_DBG, L_SPOTIFY, "Stop request\n");
@@ -1266,7 +1269,7 @@ spotify_playback_stop(void)
/* Thread: player and libspotify */
void
-spotify_playback_stop_nonblock(void)
+libspotify_playback_stop_nonblock(void)
{
DPRINTF(E_DBG, L_SPOTIFY, "Nonblock stop request\n");
@@ -1275,7 +1278,7 @@ spotify_playback_stop_nonblock(void)
/* Thread: player */
int
-spotify_playback_seek(int ms)
+libspotify_playback_seek(int ms)
{
int ret;
@@ -1289,7 +1292,7 @@ spotify_playback_seek(int ms)
/* Thread: httpd (artwork) and worker */
int
-spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
+libspotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
{
struct artwork_get_param artwork;
struct timespec ts;
@@ -1324,7 +1327,7 @@ spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
/* Thread: httpd */
void
-spotify_uri_register(const char *uri)
+libspotify_uri_register(const char *uri)
{
char *tmp;
@@ -1333,7 +1336,7 @@ spotify_uri_register(const char *uri)
}
void
-spotify_status_info_get(struct spotify_status_info *info)
+libspotify_status_info_get(struct spotify_status_info *info)
{
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
memcpy(info, &spotify_status_info, sizeof(struct spotify_status_info));
@@ -1342,7 +1345,7 @@ spotify_status_info_get(struct spotify_status_info *info)
/* Thread: library, httpd */
static int
-logout(char **errmsg)
+logout(const char **errmsg)
{
sp_error err;
@@ -1362,7 +1365,7 @@ logout(char **errmsg)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not logout of Spotify: %s\n", fptr_sp_error_message(err));
if (errmsg)
- *errmsg = safe_asprintf("Could not logout of Spotify: %s", fptr_sp_error_message(err));
+ *errmsg = fptr_sp_error_message(err);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
return -1;
@@ -1376,7 +1379,7 @@ logout(char **errmsg)
/* Thread: library, httpd */
static int
-login_user(const char *user, const char *password, char **errmsg)
+login_user(const char *user, const char *password, const char **errmsg)
{
sp_error err;
int ret;
@@ -1387,13 +1390,13 @@ login_user(const char *user, const char *password, char **errmsg)
{
DPRINTF(E_LOG, L_SPOTIFY, "Can't login! - could not find libspotify\n");
if (errmsg)
- *errmsg = safe_asprintf("Could not find libspotify");
+ *errmsg = "Could not find libspotify";
}
else
{
DPRINTF(E_LOG, L_SPOTIFY, "Can't login! - no valid Spotify session\n");
if (errmsg)
- *errmsg = safe_asprintf("No valid Spotify session");
+ *errmsg = "No valid Spotify session";
}
return -1;
@@ -1420,7 +1423,7 @@ login_user(const char *user, const char *password, char **errmsg)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not login into Spotify: %s\n", fptr_sp_error_message(err));
if (errmsg)
- *errmsg = safe_asprintf("Could not login into Spotify: %s", fptr_sp_error_message(err));
+ *errmsg = fptr_sp_error_message(err);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
return -1;
@@ -1434,14 +1437,14 @@ login_user(const char *user, const char *password, char **errmsg)
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
if (ret < 0 && errmsg)
- *errmsg = safe_asprintf("Login failed");
+ *errmsg = "Login failed";
return ret;
}
/* Thread: httpd, library */
int
-spotify_login_user(const char *user, const char *password, char **errmsg)
+libspotify_login(const char *user, const char *password, const char **errmsg)
{
int ret;
@@ -1457,32 +1460,20 @@ spotify_login_user(const char *user, const char *password, char **errmsg)
/* Thread: library */
int
-spotify_relogin()
+libspotify_relogin(void)
{
return login_user(NULL, NULL, NULL);
}
-/* Thread: library */
void
-spotify_login(char **arglist)
-{
- if (arglist)
- spotify_login_user(arglist[0], arglist[1], NULL);
- else
- spotify_login_user(NULL, NULL, NULL);
-}
-
-void
-spotify_logout(void)
+libspotify_logout(void)
{
logout(NULL);
-
- spotifywebapi_purge();
}
/* Thread: main */
int
-spotify_init(void)
+libspotify_init(void)
{
cfg_t *spotify_cfg;
sp_session *sp;
@@ -1625,7 +1616,7 @@ spotify_init(void)
}
void
-spotify_deinit(void)
+libspotify_deinit(void)
{
int ret;
diff --git a/src/inputs/libspotify/libspotify.h b/src/inputs/libspotify/libspotify.h
new file mode 100644
index 00000000..1b1d3509
--- /dev/null
+++ b/src/inputs/libspotify/libspotify.h
@@ -0,0 +1,65 @@
+
+#ifndef __LIBSPOTIFY_H__
+#define __LIBSPOTIFY_H__
+
+#include
+#include
+#include
+#include
+
+
+struct spotify_status_info
+{
+ bool libspotify_installed;
+ bool libspotify_logged_in;
+ char libspotify_user[100];
+};
+
+#define LIBSPOTIFY_SETUP_ERROR_IS_LOADING -2
+
+int
+libspotify_playback_setup(const char *path);
+
+int
+libspotify_playback_play(void);
+
+int
+libspotify_playback_pause(void);
+
+//void
+//spotify_playback_pause_nonblock(void);
+
+int
+libspotify_playback_stop(void);
+
+//void
+//spotify_playback_stop_nonblock(void);
+
+int
+libspotify_playback_seek(int ms);
+
+//int
+//spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
+
+int
+libspotify_relogin(void);
+
+int
+libspotify_login(const char *user, const char *password, const char **errmsg);
+
+void
+libspotify_logout(void);
+
+void
+libspotify_status_info_get(struct spotify_status_info *info);
+
+void
+libspotify_uri_register(const char *uri);
+
+int
+libspotify_init(void);
+
+void
+libspotify_deinit(void);
+
+#endif /* !__LIBSPOTIFY_H__ */
diff --git a/src/inputs/spotify.c b/src/inputs/spotify.c
index 60cd3ecc..633c05fe 100644
--- a/src/inputs/spotify.c
+++ b/src/inputs/spotify.c
@@ -1,6 +1,4 @@
/*
- * Copyright (C) 2017 Espen Jurgensen
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -16,77 +14,133 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
#include
#include
#include
#include
+#include
-#include "input.h"
#include "logger.h"
+#include "conffile.h"
#include "spotify.h"
-// How many retries to start playback if resource is still loading
-#define SPOTIFY_SETUP_RETRIES 5
-// How long to wait between retries in microseconds (500000 = 0.5 seconds)
-#define SPOTIFY_SETUP_RETRY_WAIT 500000
+#ifdef SPOTIFY_LIBRESPOTC
+extern struct spotify_backend spotify_librespotc;
+#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+extern struct spotify_backend spotify_libspotify;
+#endif
-static int
-setup(struct input_source *source)
+static struct spotify_backend *
+backend_set(void)
{
- int i = 0;
- int ret;
-
- while((ret = spotify_playback_setup(source->path)) == SPOTIFY_SETUP_ERROR_IS_LOADING)
- {
- if (i >= SPOTIFY_SETUP_RETRIES)
- break;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Resource still loading (%d)\n", i);
- usleep(SPOTIFY_SETUP_RETRY_WAIT);
- i++;
- }
-
- if (ret < 0)
- return -1;
-
- ret = spotify_playback_play();
- if (ret < 0)
- return -1;
-
- return 0;
+#ifdef SPOTIFY_LIBRESPOTC
+ if (!cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify"))
+ return &spotify_librespotc;
+#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+ if (cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify"))
+ return &spotify_libspotify;
+#endif
+ DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify configuration (not built with the configured backend)\n");
+ return NULL;
}
-static int
-stop(struct input_source *source)
+
+/* -------------- Dispatches functions exposed via spotify.h ---------------- */
+/* (probably not necessary when libspotify is removed) */
+/* Called from other threads than the input thread */
+
+int
+spotify_init(void)
{
- int ret;
+ struct spotify_backend *backend = backend_set();
- ret = spotify_playback_stop();
- if (ret < 0)
- return -1;
+ if (!backend || !backend->init)
+ return 0; // Just a no-op
- return 0;
+ return backend->init();
}
-static int
-seek(struct input_source *source, int seek_ms)
+void
+spotify_deinit(void)
{
- int ret;
+ struct spotify_backend *backend = backend_set();
- ret = spotify_playback_seek(seek_ms);
- if (ret < 0)
- return -1;
+ if (!backend || !backend->deinit)
+ return;
- return ret;
+ backend->deinit();
}
-struct input_definition input_spotify =
+int
+spotify_login(const char *username, const char *password, const char **errmsg)
{
- .name = "Spotify",
- .type = INPUT_TYPE_SPOTIFY,
- .disabled = 0,
- .setup = setup,
- .stop = stop,
- .seek = seek,
-};
+ struct spotify_backend *backend = backend_set();
+ if (!backend || !backend->login)
+ return -1;
+
+ return backend->login(username, password, errmsg);
+}
+
+int
+spotify_login_token(const char *username, const char *token, const char **errmsg)
+{
+ struct spotify_backend *backend = backend_set();
+
+ if (!backend || !backend->login_token)
+ return -1;
+
+ return backend->login_token(username, token, errmsg);
+}
+
+void
+spotify_logout(void)
+{
+ struct spotify_backend *backend = backend_set();
+
+ if (!backend || !backend->logout)
+ return;
+
+ backend->logout();
+}
+
+int
+spotify_relogin(void)
+{
+ struct spotify_backend *backend = backend_set();
+
+ if (!backend || !backend->relogin)
+ return -1;
+
+ return backend->relogin();
+}
+
+void
+spotify_uri_register(const char *uri)
+{
+ struct spotify_backend *backend = backend_set();
+
+ if (!backend || !backend->uri_register)
+ return;
+
+ backend->uri_register(uri);
+}
+
+void
+spotify_status_get(struct spotify_status *status)
+{
+ struct spotify_backend *backend = backend_set();
+
+ memset(status, 0, sizeof(struct spotify_status));
+
+ if (!backend || !backend->status_get)
+ return;
+
+ backend->status_get(status);
+}
diff --git a/src/inputs/spotify.h b/src/inputs/spotify.h
new file mode 100644
index 00000000..31d1614e
--- /dev/null
+++ b/src/inputs/spotify.h
@@ -0,0 +1,50 @@
+#ifndef __SPOTIFY_H__
+#define __SPOTIFY_H__
+
+#include
+#include
+
+struct spotify_status
+{
+ bool installed;
+ bool logged_in;
+ char username[128];
+};
+
+struct spotify_backend
+{
+ int (*init)(void);
+ void (*deinit)(void);
+ int (*login)(const char *username, const char *password, const char **errmsg);
+ int (*login_token)(const char *username, const char *token, const char **errmsg);
+ void (*logout)(void);
+ int (*relogin)(void);
+ void (*uri_register)(const char *uri);
+ void (*status_get)(struct spotify_status *status);
+};
+
+int
+spotify_init(void);
+
+void
+spotify_deinit(void);
+
+int
+spotify_login(const char *username, const char *password, const char **errmsg);
+
+int
+spotify_login_token(const char *username, const char *token, const char **errmsg);
+
+void
+spotify_logout(void);
+
+int
+spotify_relogin(void);
+
+void
+spotify_uri_register(const char *uri);
+
+void
+spotify_status_get(struct spotify_status *status);
+
+#endif /* !__SPOTIFY_H__ */
diff --git a/src/inputs/spotify_librespotc.c b/src/inputs/spotify_librespotc.c
new file mode 100644
index 00000000..a89df8c4
--- /dev/null
+++ b/src/inputs/spotify_librespotc.c
@@ -0,0 +1,756 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef HAVE_PTHREAD_NP_H
+# include
+#endif
+
+#include
+
+#include "input.h"
+#include "misc.h"
+#include "logger.h"
+#include "conffile.h"
+#include "listener.h"
+#include "http.h"
+#include "db.h"
+#include "transcode.h"
+#include "spotify.h"
+#include "librespot-c/librespot-c.h"
+
+// Haven't actually studied ffmpeg's probe size requirements, this is just a
+// guess
+#define SPOTIFY_PROBE_SIZE_MIN 16384
+
+// The transcoder will say EOF if too little data is provided to it
+#define SPOTIFY_BUF_MIN 4096
+
+// Limits how much of the Spotify Ogg file we fetch and buffer (in read_buf).
+// This will also in effect throttle in librespot-c.
+#define SPOTIFY_BUF_MAX (512 * 1024)
+
+struct global_ctx
+{
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+
+ struct spotify_status status;
+
+ struct sp_session *session;
+ enum sp_bitrates bitrate_preferred;
+};
+
+struct download_ctx
+{
+ bool is_started;
+ bool is_ended;
+ struct transcode_ctx *xcode;
+
+ struct evbuffer *read_buf;
+ int read_fd;
+
+ uint32_t len_ms;
+ size_t len_bytes;
+};
+
+static struct global_ctx spotify_ctx;
+
+static struct media_quality spotify_quality = { 44100, 16, 2, 0 };
+
+
+/* ------------------------------ Utility funcs ----------------------------- */
+
+static void
+hextobin(uint8_t *data, size_t data_len, const char *hexstr, size_t hexstr_len)
+{
+ char hex[] = { 0, 0, 0 };
+ const char *ptr;
+ int i;
+
+ if (2 * data_len < hexstr_len)
+ {
+ memset(data, 0, data_len);
+ return;
+ }
+
+ ptr = hexstr;
+ for (i = 0; i < data_len; i++, ptr+=2)
+ {
+ memcpy(hex, ptr, 2);
+ data[i] = strtol(hex, NULL, 16);
+ }
+}
+
+static int
+postlogin(struct global_ctx *ctx)
+{
+ struct sp_credentials credentials;
+ char *db_stored_cred;
+ char *ptr;
+ int i;
+ int ret;
+
+ ret = librespotc_credentials_get(&credentials, ctx->session);
+ if (ret < 0)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Error getting Spotify credentials: %s\n", librespotc_last_errmsg());
+ return -1;
+ }
+
+ CHECK_NULL(L_SPOTIFY, db_stored_cred = malloc(2 * credentials.stored_cred_len + 1));
+ for (i = 0, ptr = db_stored_cred; i < credentials.stored_cred_len; i++)
+ ptr += sprintf(ptr, "%02x", credentials.stored_cred[i]);
+
+ db_admin_set("spotify_username", credentials.username);
+ db_admin_set("spotify_stored_cred", db_stored_cred);
+
+ free(db_stored_cred);
+
+ ctx->status.logged_in = true;
+ snprintf(ctx->status.username, sizeof(ctx->status.username), "%s", credentials.username);
+
+ librespotc_bitrate_set(ctx->session, ctx->bitrate_preferred);
+
+ DPRINTF(E_LOG, L_SPOTIFY, "Logged into Spotify succesfully with username %s\n", credentials.username);
+
+ listener_notify(LISTENER_SPOTIFY);
+
+ return 0;
+}
+
+// If there is evbuf size is below max, reads from a non-blocking fd until error,
+// EAGAIN or evbuf full
+static int
+fd_read(bool *eofptr, struct evbuffer *evbuf, int fd)
+{
+ size_t len = evbuffer_get_length(evbuf);
+ bool eof = false;
+ int total = 0;
+ int ret = 0;
+
+ while (len + total < SPOTIFY_BUF_MAX && !eof)
+ {
+ ret = evbuffer_read(evbuf, fd, -1); // Each read is 4096 bytes (EVBUFFER_READ_MAX)
+
+ if (ret == 0)
+ eof = true;
+ else if (ret < 0)
+ break;
+
+ total += ret;
+ }
+
+ if (eofptr)
+ *eofptr = eof;
+
+ if (ret < 0 && errno != EAGAIN)
+ return ret;
+
+ return total;
+}
+
+/* ------------------ Callbacks from librespot-c thread --------------------- */
+
+static void
+progress_cb(int fd, void *cb_arg, size_t received, size_t len)
+{
+ DPRINTF(E_SPAM, L_SPOTIFY, "Progress %zu/%zu\n", received, len);
+}
+
+static int
+https_get_cb(char **out, const char *url)
+{
+ struct http_client_ctx ctx = { 0 };
+ char *body;
+ size_t len;
+ int ret;
+
+ ctx.url = url;
+ ctx.input_body = evbuffer_new();
+
+ ret = http_client_request(&ctx);
+ if (ret < 0 || ctx.response_code != HTTP_OK)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Failed to AP list from '%s' (return %d, error code %d)\n", ctx.url, ret, ctx.response_code);
+ goto error;
+ }
+
+ len = evbuffer_get_length(ctx.input_body);
+ body = malloc(len + 1);
+
+ evbuffer_remove(ctx.input_body, body, len);
+ body[len] = '\0'; // For safety
+
+ *out = body;
+
+ evbuffer_free(ctx.input_body);
+ return 0;
+
+ error:
+ evbuffer_free(ctx.input_body);
+ return -1;
+}
+
+static int
+tcp_connect(const char *address, unsigned short port)
+{
+ return net_connect(address, port, SOCK_STREAM, "spotify");
+}
+
+static void
+tcp_disconnect(int fd)
+{
+ close(fd);
+}
+
+static void
+thread_name_set(pthread_t thread)
+{
+#if defined(HAVE_PTHREAD_SETNAME_NP)
+ pthread_setname_np(thread, "spotify");
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ pthread_set_name_np(thread, "spotify");
+#endif
+}
+
+static void
+logmsg_cb(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ DVPRINTF(E_DBG, L_SPOTIFY, fmt, ap);
+ va_end(ap);
+}
+
+static void
+hexdump_cb(const char *msg, uint8_t *data, size_t data_len)
+{
+// DHEXDUMP(E_DBG, L_SPOTIFY, data, data_len, msg);
+}
+
+
+/* --------------------- Implementation (input thread) ---------------------- */
+
+struct sp_callbacks callbacks = {
+ .https_get = https_get_cb,
+ .tcp_connect = tcp_connect,
+ .tcp_disconnect = tcp_disconnect,
+
+ .thread_name_set = thread_name_set,
+
+ .hexdump = hexdump_cb,
+ .logmsg = logmsg_cb,
+};
+
+static int64_t
+download_seek(void *arg, int64_t offset, enum transcode_seek_type type)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+ struct download_ctx *download = arg;
+ int64_t out;
+ int ret;
+
+ pthread_mutex_lock(&ctx->lock);
+
+ switch (type)
+ {
+ case XCODE_SEEK_SIZE:
+ out = download->len_bytes;
+ break;
+ case XCODE_SEEK_SET:
+ // Flush read buffer
+ evbuffer_drain(download->read_buf, -1);
+
+ ret = librespotc_seek(download->read_fd, offset);
+ if (ret < 0)
+ goto error;
+
+ fd_read(NULL, download->read_buf, download->read_fd);
+
+ out = offset;
+ break;
+ default:
+ goto error;
+ }
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ DPRINTF(E_DBG, L_SPOTIFY, "Seek to offset %" PRIi64 " requested, type %d, returning %" PRIi64 "\n", offset, type, out);
+
+ return out;
+
+ error:
+ DPRINTF(E_WARN, L_SPOTIFY, "Seek error\n");
+
+ pthread_mutex_unlock(&ctx->lock);
+ return -1;
+}
+
+// Has to be called after we have started receiving data, since ffmpeg needs to
+// probe the data to find the audio streams
+static int
+download_xcode_setup(struct download_ctx *download)
+{
+ struct transcode_ctx *xcode;
+ struct transcode_evbuf_io xcode_evbuf_io = { 0 };
+
+ CHECK_NULL(L_SPOTIFY, xcode = malloc(sizeof(struct transcode_ctx)));
+
+ xcode_evbuf_io.evbuf = download->read_buf;
+ xcode_evbuf_io.seekfn = download_seek;
+ xcode_evbuf_io.seekfn_arg = download;
+
+ xcode->decode_ctx = transcode_decode_setup(XCODE_OGG, NULL, DATA_KIND_SPOTIFY, NULL, &xcode_evbuf_io, download->len_ms);
+ if (!xcode->decode_ctx)
+ goto error;
+
+ xcode->encode_ctx = transcode_encode_setup(XCODE_PCM16, NULL, xcode->decode_ctx, NULL, 0, 0);
+ if (!xcode->encode_ctx)
+ goto error;
+
+ download->xcode = xcode;
+
+ return 0;
+
+ error:
+ transcode_cleanup(&xcode);
+ return -1;
+}
+
+static void
+download_free(struct download_ctx *download)
+{
+ if (!download)
+ return;
+
+ if (download->read_fd >= 0)
+ librespotc_close(download->read_fd);
+
+ if (download->read_buf)
+ evbuffer_free(download->read_buf);
+
+ transcode_cleanup(&download->xcode);
+ free(download);
+}
+
+static struct download_ctx *
+download_new(int fd, uint32_t len_ms, size_t len_bytes)
+{
+ struct download_ctx *download;
+
+ CHECK_NULL(L_SPOTIFY, download = calloc(1, sizeof(struct download_ctx)));
+ CHECK_NULL(L_SPOTIFY, download->read_buf = evbuffer_new());
+
+ download->read_fd = fd;
+ download->len_ms = len_ms;
+ download->len_bytes = len_bytes;
+
+ return download;
+}
+
+static int
+stop(struct input_source *source)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+ struct download_ctx *download = source->input_ctx;
+
+ DPRINTF(E_DBG, L_SPOTIFY, "stop()\n");
+
+ pthread_mutex_lock(&ctx->lock);
+
+ download_free(download);
+
+ if (source->evbuf)
+ evbuffer_free(source->evbuf);
+
+ source->input_ctx = NULL;
+ source->evbuf = NULL;
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ return 0;
+}
+
+static int
+setup(struct input_source *source)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+ struct download_ctx *download;
+ struct sp_metadata metadata;
+ int probe_bytes;
+ int fd;
+ int ret;
+
+ DPRINTF(E_DBG, L_SPOTIFY, "setup()\n");
+
+ pthread_mutex_lock(&ctx->lock);
+
+ fd = librespotc_open(source->path, ctx->session);
+ if (fd < 0)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Eror opening source: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ ret = librespotc_metadata_get(&metadata, fd);
+ if (ret < 0)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Error getting track metadata: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ // Seems we have a valid source, now setup a read + decoding context. The
+ // closing of the fd is from now on part of closing the download_ctx, which is
+ // done in stop().
+ download = download_new(fd, source->len_ms, metadata.file_len);
+
+ CHECK_NULL(L_SPOTIFY, source->evbuf = evbuffer_new());
+ CHECK_NULL(L_SPOTIFY, source->input_ctx = download);
+
+ source->quality = spotify_quality;
+
+ // At this point enough bytes should be ready for transcode setup (ffmpeg probing)
+ probe_bytes = fd_read(NULL, download->read_buf, fd);
+ if (probe_bytes < SPOTIFY_PROBE_SIZE_MIN)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Not enough audio data for ffmpeg probing (%d)\n", probe_bytes);
+ goto error;
+ }
+
+ ret = download_xcode_setup(download);
+ if (ret < 0)
+ goto error;
+
+ pthread_mutex_unlock(&ctx->lock);
+ return 0;
+
+ error:
+ pthread_mutex_unlock(&ctx->lock);
+ stop(source);
+
+ return -1;
+}
+
+static int
+play(struct input_source *source)
+{
+ struct download_ctx *download = source->input_ctx;
+ size_t buflen;
+ int ret;
+
+ // Starts the download. We don't do that in setup because the player/input
+ // might run seek() before starting download.
+ if (!download->is_started)
+ {
+ librespotc_write(download->read_fd, progress_cb, download);
+ download->is_started = true;
+ }
+
+ if (!download->is_ended)
+ {
+ ret = fd_read(&download->is_ended, download->read_buf, download->read_fd);
+ if (ret < 0)
+ goto error;
+
+ buflen = evbuffer_get_length(download->read_buf);
+ if (buflen < SPOTIFY_BUF_MIN)
+ goto wait;
+ }
+
+ // Decode the Ogg Vorbis to PCM in chunks of 16 packets, which is pretty much
+ // a randomly chosen chunk size
+ ret = transcode(source->evbuf, NULL, download->xcode, 16);
+ if (ret == 0)
+ {
+ input_write(source->evbuf, &source->quality, INPUT_FLAG_EOF);
+ stop(source);
+ return -1;
+ }
+ else if (ret < 0)
+ goto error;
+
+ ret = input_write(source->evbuf, &source->quality, 0);
+ if (ret == EAGAIN)
+ goto wait;
+
+ return 0;
+
+ error:
+ input_write(NULL, NULL, INPUT_FLAG_ERROR);
+ stop(source);
+ return -1;
+
+ wait:
+ DPRINTF(E_DBG, L_SPOTIFY, "Waiting for data\n");
+ input_wait();
+ return 0;
+}
+
+static int
+seek(struct input_source *source, int seek_ms)
+{
+ struct download_ctx *download = source->input_ctx;
+
+ // This will make transcode call back to download_seek(), but with a byte
+ // offset instead of a ms position, which is what librespot-c requires
+ return transcode_seek(download->xcode, seek_ms);
+}
+
+static int
+login_stored_cred(struct global_ctx *ctx, const char *username, const char *db_stored_cred)
+{
+ size_t db_stored_cred_len;
+ uint8_t *stored_cred = NULL;
+ size_t stored_cred_len;
+ int ret;
+
+ db_stored_cred_len = strlen(db_stored_cred);
+ stored_cred_len = db_stored_cred_len / 2;
+
+ CHECK_NULL(L_SPOTIFY, stored_cred = malloc(stored_cred_len));
+ hextobin(stored_cred, stored_cred_len, db_stored_cred, db_stored_cred_len);
+
+ ctx->session = librespotc_login_stored_cred(username, stored_cred, stored_cred_len);
+ if (!ctx->session)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Error logging into Spotify: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ ret = postlogin(ctx);
+ if (ret < 0)
+ goto error;
+
+ free(stored_cred);
+ return 0;
+
+ error:
+ free(stored_cred);
+ if (ctx->session)
+ librespotc_logout(ctx->session);
+ ctx->session = NULL;
+ return -1;
+}
+
+
+static int
+init(void)
+{
+ struct sp_sysinfo sysinfo;
+ cfg_t *spotify_cfg;
+ char *username = NULL;
+ char *db_stored_cred = NULL;
+ int ret;
+
+ spotify_cfg = cfg_getsec(cfg, "spotify");
+
+ if (cfg_getbool(spotify_cfg, "use_libspotify"))
+ return -1;
+
+ CHECK_ERR(L_SPOTIFY, mutex_init(&spotify_ctx.lock));
+ CHECK_ERR(L_SPOTIFY, pthread_cond_init(&spotify_ctx.cond, NULL));
+
+ snprintf(sysinfo.client_name, sizeof(sysinfo.client_name), PACKAGE_NAME);
+ snprintf(sysinfo.client_version, sizeof(sysinfo.client_version), PACKAGE_VERSION);
+ snprintf(sysinfo.client_build_id, sizeof(sysinfo.client_build_id), "0");
+ snprintf(sysinfo.device_id, sizeof(sysinfo.device_id), "%" PRIx64, libhash); // TODO use a UUID instead
+
+ ret = librespotc_init(&sysinfo, &callbacks);
+ if (ret < 0)
+ {
+ DPRINTF(E_LOG, L_SPOTIFY, "Error initializing Spotify: %s\n", librespotc_last_errmsg());
+ goto error;
+ }
+
+ switch (cfg_getint(spotify_cfg, "bitrate"))
+ {
+ case 1:
+ spotify_ctx.bitrate_preferred = SP_BITRATE_96;
+ break;
+ case 2:
+ spotify_ctx.bitrate_preferred = SP_BITRATE_160;
+ break;
+ case 3:
+ spotify_ctx.bitrate_preferred = SP_BITRATE_320;
+ break;
+ default:
+ spotify_ctx.bitrate_preferred = SP_BITRATE_ANY;
+ }
+
+ // Re-login if we have stored credentials
+ db_admin_get(&username, "spotify_username");
+ db_admin_get(&db_stored_cred, "spotify_stored_cred");
+ if (username && db_stored_cred)
+ {
+ ret = login_stored_cred(&spotify_ctx, username, db_stored_cred);
+ if (ret < 0)
+ goto error;
+ }
+
+ free(username);
+ free(db_stored_cred);
+ return 0;
+
+ error:
+ free(username);
+ free(db_stored_cred);
+ return -1;
+}
+
+static void
+deinit(void)
+{
+ librespotc_deinit();
+
+ CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&spotify_ctx.cond));
+ CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&spotify_ctx.lock));
+}
+
+struct input_definition input_spotify =
+{
+ .name = "Spotify",
+ .type = INPUT_TYPE_SPOTIFY,
+ .disabled = 0,
+ .setup = setup,
+ .stop = stop,
+ .play = play,
+ .seek = seek,
+ .init = init,
+ .deinit = deinit,
+};
+
+
+/* -------------------- Functions exposed via spotify.h --------------------- */
+/* Called from other threads than the input thread */
+
+static int
+login(const char *username, const char *password, const char **errmsg)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+ int ret;
+
+ pthread_mutex_lock(&ctx->lock);
+
+ ctx->session = librespotc_login_password(username, password);
+ if (!ctx->session)
+ goto error;
+
+ ret = postlogin(ctx);
+ if (ret < 0)
+ goto error;
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ return 0;
+
+ error:
+ if (ctx->session)
+ librespotc_logout(ctx->session);
+ ctx->session = NULL;
+
+ if (errmsg)
+ *errmsg = librespotc_last_errmsg();
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ return -1;
+}
+
+static int
+login_token(const char *username, const char *token, const char **errmsg)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+ int ret;
+
+ pthread_mutex_lock(&ctx->lock);
+
+ ctx->session = librespotc_login_token(username, token);
+ if (!ctx->session)
+ goto error;
+
+ ret = postlogin(ctx);
+ if (ret < 0)
+ goto error;
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ return 0;
+
+ error:
+ if (ctx->session)
+ librespotc_logout(ctx->session);
+ ctx->session = NULL;
+
+ if (errmsg)
+ *errmsg = librespotc_last_errmsg();
+
+ pthread_mutex_unlock(&ctx->lock);
+
+ return -1;
+}
+
+static void
+logout(void)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+
+ db_admin_delete("spotify_username");
+ db_admin_delete("spotify_stored_cred");
+
+ pthread_mutex_lock(&ctx->lock);
+
+ librespotc_logout(ctx->session);
+ ctx->session = NULL;
+
+ pthread_mutex_unlock(&ctx->lock);
+}
+
+static int
+relogin(void)
+{
+ return 0; // re-login is only relevant for libspotify, here it is just a no-op
+}
+
+static void
+status_get(struct spotify_status *status)
+{
+ struct global_ctx *ctx = &spotify_ctx;
+
+ pthread_mutex_lock(&ctx->lock);
+
+ memcpy(status->username, ctx->status.username, sizeof(status->username));
+ status->logged_in = ctx->status.logged_in;
+ status->installed = true;
+
+ pthread_mutex_unlock(&ctx->lock);
+}
+
+struct spotify_backend spotify_librespotc =
+{
+ .login = login,
+ .login_token = login_token,
+ .logout = logout,
+ .relogin = relogin,
+ .status_get = status_get,
+};
+
diff --git a/src/inputs/spotify_libspotify.c b/src/inputs/spotify_libspotify.c
new file mode 100644
index 00000000..2b975711
--- /dev/null
+++ b/src/inputs/spotify_libspotify.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Espen Jurgensen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+#include
+
+#include "input.h"
+#include "conffile.h"
+#include "logger.h"
+#include "spotify.h"
+#include "libspotify/libspotify.h"
+
+// How many retries to start playback if resource is still loading
+#define LIBSPOTIFY_SETUP_RETRIES 5
+// How long to wait between retries in microseconds (500000 = 0.5 seconds)
+#define LIBSPOTIFY_SETUP_RETRY_WAIT 500000
+
+static int
+init(void)
+{
+ return cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify") ? 0 : -1;
+}
+
+static int
+setup(struct input_source *source)
+{
+ int i = 0;
+ int ret;
+
+ while((ret = libspotify_playback_setup(source->path)) == LIBSPOTIFY_SETUP_ERROR_IS_LOADING)
+ {
+ if (i >= LIBSPOTIFY_SETUP_RETRIES)
+ break;
+
+ DPRINTF(E_DBG, L_SPOTIFY, "Resource still loading (%d)\n", i);
+ usleep(LIBSPOTIFY_SETUP_RETRY_WAIT);
+ i++;
+ }
+
+ if (ret < 0)
+ return -1;
+
+ ret = libspotify_playback_play();
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+stop(struct input_source *source)
+{
+ int ret;
+
+ ret = libspotify_playback_stop();
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+seek(struct input_source *source, int seek_ms)
+{
+ int ret;
+
+ ret = libspotify_playback_seek(seek_ms);
+ if (ret < 0)
+ return -1;
+
+ return ret;
+}
+
+struct input_definition input_libspotify =
+{
+ .name = "libspotify",
+ .type = INPUT_TYPE_LIBSPOTIFY,
+ .disabled = 0,
+ .init = init,
+ .setup = setup,
+ .stop = stop,
+ .seek = seek,
+};
+
+
+// No-op for libspotify since it doesn't support logging in with the web api token
+static int
+login_token(const char *username, const char *token, const char **errmsg)
+{
+ return 0;
+}
+
+static void
+status_get(struct spotify_status *status)
+{
+ struct spotify_status_info info = { 0 };
+
+ libspotify_status_info_get(&info);
+
+ status->installed = info.libspotify_installed;
+ status->logged_in = info.libspotify_logged_in;
+ snprintf(status->username, sizeof(status->username), "%s", info.libspotify_user);
+}
+
+struct spotify_backend spotify_libspotify =
+{
+ .init = libspotify_init,
+ .deinit = libspotify_deinit,
+ .login = libspotify_login,
+ .login_token = login_token,
+ .logout = libspotify_logout,
+ .relogin = libspotify_relogin,
+ .uri_register = libspotify_uri_register,
+ .status_get = status_get,
+};
+
diff --git a/src/library.c b/src/library.c
index c12cfc16..c704958e 100644
--- a/src/library.c
+++ b/src/library.c
@@ -79,14 +79,14 @@ static pthread_t tid_library;
struct event_base *evbase_lib;
extern struct library_source filescanner;
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
extern struct library_source spotifyscanner;
#endif
extern struct library_source rssscanner;
static struct library_source *sources[] = {
&filescanner,
-#ifdef HAVE_SPOTIFY_H
+#ifdef SPOTIFY
&spotifyscanner,
#endif
&rssscanner,
diff --git a/src/library/filescanner.c b/src/library/filescanner.c
index 184c05a4..3dfe672d 100644
--- a/src/library/filescanner.c
+++ b/src/library/filescanner.c
@@ -67,9 +67,6 @@
#ifdef LASTFM
# include "lastfm.h"
#endif
-#ifdef HAVE_SPOTIFY_H
-# include "spotify.h"
-#endif
#define F_SCAN_BULK (1 << 0)
@@ -95,7 +92,6 @@ enum file_type {
FILE_CTRL_REMOTE,
FILE_CTRL_RAOP_VERIFICATION,
FILE_CTRL_LASTFM,
- FILE_CTRL_SPOTIFY,
FILE_CTRL_INITSCAN,
FILE_CTRL_METASCAN, // forced scan for meta, preserves existing db records
FILE_CTRL_FULLSCAN,
@@ -350,9 +346,6 @@ file_type_get(const char *path) {
if (strcasecmp(ext, ".lastfm") == 0)
return FILE_CTRL_LASTFM;
- if (strcasecmp(ext, ".spotify") == 0)
- return FILE_CTRL_SPOTIFY;
-
if (strcasecmp(ext, ".init-rescan") == 0)
return FILE_CTRL_INITSCAN;
@@ -700,17 +693,6 @@ process_file(char *file, struct stat *sb, enum file_type file_type, int scan_typ
#endif
break;
- case FILE_CTRL_SPOTIFY:
-#ifdef HAVE_SPOTIFY_H
- if (flags & F_SCAN_BULK)
- DPRINTF(E_LOG, L_SCAN, "Bulk scan will ignore '%s' (to process, add it after startup)\n", file);
- else
- kickoff(spotify_login, file, 2);
-#else
- DPRINTF(E_LOG, L_SCAN, "Found '%s', but this version was built without Spotify support\n", file);
-#endif
- break;
-
case FILE_CTRL_INITSCAN:
if (flags & F_SCAN_BULK)
break;
diff --git a/src/spotify_webapi.c b/src/library/spotify_webapi.c
similarity index 98%
rename from src/spotify_webapi.c
rename to src/library/spotify_webapi.c
index 202dcb71..e51165ef 100644
--- a/src/spotify_webapi.c
+++ b/src/library/spotify_webapi.c
@@ -35,7 +35,7 @@
#include "listener.h"
#include "logger.h"
#include "misc_json.h"
-#include "spotify.h"
+#include "inputs/spotify.h"
enum spotify_request_type {
@@ -135,7 +135,7 @@ static bool scanning;
// Endpoints and credentials for the web api
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
-static const char *spotify_scope = "playlist-read-private playlist-read-collaborative user-library-read user-read-private";
+static const char *spotify_scope = "playlist-read-private playlist-read-collaborative user-library-read user-read-private streaming";
static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize";
static const char *spotify_token_uri = "https://accounts.spotify.com/api/token";
@@ -975,10 +975,9 @@ spotifywebapi_oauth_uri_get(const char *redirect_uri)
/* Thread: httpd */
int
-spotifywebapi_oauth_callback(struct evkeyvalq *param, const char *redirect_uri, char **errmsg)
+spotifywebapi_oauth_callback(struct evkeyvalq *param, const char *redirect_uri, const char **errmsg)
{
const char *code;
- const char *err;
int ret;
*errmsg = NULL;
@@ -986,18 +985,19 @@ spotifywebapi_oauth_callback(struct evkeyvalq *param, const char *redirect_uri,
code = evhttp_find_header(param, "code");
if (!code)
{
- *errmsg = safe_asprintf("Error: Didn't receive a code from Spotify");
+ *errmsg = "Error: Didn't receive a code from Spotify";
return -1;
}
DPRINTF(E_DBG, L_SPOTIFY, "Received OAuth code: %s\n", code);
- ret = token_get(code, redirect_uri, &err);
+ ret = token_get(code, redirect_uri, errmsg);
if (ret < 0)
- {
- *errmsg = safe_asprintf("Error: %s", err);
- return -1;
- }
+ return -1;
+
+ ret = spotify_login_token(spotify_credentials.user, spotify_credentials.access_token, errmsg);
+ if (ret < 0)
+ return -1;
// Trigger scan after successful access to spotifywebapi
spotifywebapi_fullrescan();
@@ -1471,6 +1471,7 @@ track_add(struct spotify_track *track, struct spotify_album *album, const char *
free_mfi(&mfi, 1);
}
+ // This is only required for the libspotify backend
spotify_uri_register(track->uri);
if (album && album->uri)
@@ -1712,7 +1713,7 @@ scan_playlists(enum spotify_request_type request_type)
}
static void
-create_saved_tracks_playlist()
+create_saved_tracks_playlist(void)
{
struct playlist_info pli =
{
@@ -1738,7 +1739,7 @@ create_saved_tracks_playlist()
* Add or update playlist folder for all spotify playlists (if enabled in config)
*/
static void
-create_base_playlist()
+create_base_playlist(void)
{
cfg_t *spotify_cfg;
struct playlist_info pli =
@@ -1795,17 +1796,17 @@ scan(enum spotify_request_type request_type)
/* Thread: library */
static int
-initscan()
+initscan(void)
{
int ret;
/* Refresh access token for the spotify webapi */
- ret = token_refresh();
+ ret = token_refresh();
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Spotify webapi token refresh failed. "
- "In order to use the web api, authorize the server to access "
- "your saved tracks by visiting http://owntone.local:3689\n");
+ "In order to use Spotify, authorize the server to access your saved "
+ "tracks by visiting http://owntone.local:3689\n");
db_spotify_purge();
@@ -1815,8 +1816,8 @@ initscan()
spotify_saved_plid = 0;
/*
- * Login to spotify needs to be done before scanning tracks from the web api.
- * (Scanned tracks need to be registered with libspotify for playback)
+ * libspotify needs to be logged in before before scanning tracks from the web
+ * since scanned tracks need to be registered for playback
*/
ret = spotify_relogin();
if (ret < 0)
@@ -1839,7 +1840,7 @@ initscan()
/* Thread: library */
static int
-rescan()
+rescan(void)
{
scan(SPOTIFY_REQUEST_TYPE_RESCAN);
return 0;
@@ -1847,7 +1848,7 @@ rescan()
/* Thread: library */
static int
-metarescan()
+metarescan(void)
{
scan(SPOTIFY_REQUEST_TYPE_METARESCAN);
return 0;
@@ -1855,7 +1856,7 @@ metarescan()
/* Thread: library */
static int
-fullrescan()
+fullrescan(void)
{
db_spotify_purge();
scan(SPOTIFY_REQUEST_TYPE_RESCAN);
@@ -2066,12 +2067,10 @@ spotifywebapi_access_token_get(struct spotifywebapi_access_token *info)
static int
spotifywebapi_init()
{
- int ret;
-
CHECK_ERR(L_SPOTIFY, mutex_init(&token_lck));
- ret = spotify_init();
- return ret;
+ // Required for libspotify backend
+ return spotify_init();
}
static void
diff --git a/src/spotify_webapi.h b/src/library/spotify_webapi.h
similarity index 97%
rename from src/spotify_webapi.h
rename to src/library/spotify_webapi.h
index 894f1f6b..f8736771 100644
--- a/src/spotify_webapi.h
+++ b/src/library/spotify_webapi.h
@@ -45,7 +45,7 @@ struct spotifywebapi_access_token
char *
spotifywebapi_oauth_uri_get(const char *redirect_uri);
int
-spotifywebapi_oauth_callback(struct evkeyvalq *param, const char *redirect_uri, char **errmsg);
+spotifywebapi_oauth_callback(struct evkeyvalq *param, const char *redirect_uri, const char **errmsg);
void
spotifywebapi_fullrescan(void);
diff --git a/src/misc.c b/src/misc.c
index d904953d..099b94c9 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -70,6 +70,12 @@ static char *buildopts[] =
#else
"Without Spotify",
#endif
+#ifdef SPOTIFY_LIBRESPOTC
+ "librespot-c",
+#endif
+#ifdef SPOTIFY_LIBSPOTIFY
+ "libspotify",
+#endif
#ifdef LASTFM
"LastFM",
#else
diff --git a/src/spotify.h b/src/spotify.h
deleted file mode 100644
index 610d17b2..00000000
--- a/src/spotify.h
+++ /dev/null
@@ -1,68 +0,0 @@
-
-#ifndef __SPOTIFY_H__
-#define __SPOTIFY_H__
-
-#include
-#include
-#include
-#include
-
-
-struct spotify_status_info
-{
- bool libspotify_installed;
- bool libspotify_logged_in;
- char libspotify_user[100];
-};
-
-#define SPOTIFY_SETUP_ERROR_IS_LOADING -2
-
-int
-spotify_playback_setup(const char *path);
-
-int
-spotify_playback_play();
-
-int
-spotify_playback_pause();
-
-void
-spotify_playback_pause_nonblock(void);
-
-int
-spotify_playback_stop(void);
-
-void
-spotify_playback_stop_nonblock(void);
-
-int
-spotify_playback_seek(int ms);
-
-int
-spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
-
-int
-spotify_relogin();
-
-int
-spotify_login_user(const char *user, const char *password, char **errmsg);
-
-void
-spotify_login(char **arglist);
-
-void
-spotify_logout(void);
-
-void
-spotify_status_info_get(struct spotify_status_info *info);
-
-void
-spotify_uri_register(const char *uri);
-
-int
-spotify_init(void);
-
-void
-spotify_deinit(void);
-
-#endif /* !__SPOTIFY_H__ */
diff --git a/src/transcode.c b/src/transcode.c
index f9cf20be..b4c31e31 100644
--- a/src/transcode.c
+++ b/src/transcode.c
@@ -39,7 +39,6 @@
#include "logger.h"
#include "conffile.h"
#include "db.h"
-#include "avio_evbuffer.h"
#include "misc.h"
#include "transcode.h"
@@ -51,6 +50,8 @@
#define MAX_BAD_PACKETS 5
// How long to wait (in microsec) before interrupting av_read_frame
#define READ_TIMEOUT 30000000
+// Buffer size for reading/writing input and output evbuffers
+#define AVIO_BUFFER_SIZE 4096
static const char *default_codecs = "mpeg,wav";
static const char *roku_codecs = "mpeg,mp4a,wma,alac,wav";
@@ -184,6 +185,14 @@ enum probe_type
PROBE_TYPE_QUICK,
};
+struct avio_evbuffer {
+ struct evbuffer *evbuf;
+ uint8_t *buffer;
+ transcode_seekfn seekfn;
+ void *seekfn_arg;
+};
+
+
/* -------------------------- PROFILE CONFIGURATION ------------------------ */
static int
@@ -242,6 +251,11 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile, str
settings->sample_format = AV_SAMPLE_FMT_S16P;
break;
+ case XCODE_OGG:
+ settings->encode_audio = 1;
+ settings->in_format = "ogg";
+ break;
+
case XCODE_JPEG:
settings->encode_video = 1;
settings->silent = 1;
@@ -771,6 +785,131 @@ read_decode_filter_encode_write(struct transcode_ctx *ctx)
return ret;
}
+/* ------------------------------- CUSTOM I/O ------------------------------ */
+/* For using ffmpeg with evbuffer input/output instead of files */
+
+static int
+avio_evbuffer_read(void *opaque, uint8_t *buf, int size)
+{
+ struct avio_evbuffer *ae = (struct avio_evbuffer *)opaque;
+ int ret;
+
+ ret = evbuffer_remove(ae->evbuf, buf, size);
+
+ // Must return AVERROR, see avio.h: avio_alloc_context()
+ return (ret > 0) ? ret : AVERROR_EOF;
+}
+
+static int
+avio_evbuffer_write(void *opaque, uint8_t *buf, int size)
+{
+ struct avio_evbuffer *ae = (struct avio_evbuffer *)opaque;
+ int ret;
+
+ ret = evbuffer_add(ae->evbuf, buf, size);
+
+ return (ret == 0) ? size : -1;
+}
+
+static int64_t
+avio_evbuffer_seek(void *opaque, int64_t offset, int whence)
+{
+ struct avio_evbuffer *ae = (struct avio_evbuffer *)opaque;
+ enum transcode_seek_type seek_type;
+
+ // Caller shouldn't need to know about ffmpeg defines
+ if (whence & AVSEEK_SIZE)
+ seek_type = XCODE_SEEK_SIZE;
+ else if (whence == SEEK_SET)
+ seek_type = XCODE_SEEK_SET;
+ else if (whence == SEEK_CUR)
+ seek_type = XCODE_SEEK_CUR;
+ else
+ return -1;
+
+ return ae->seekfn(ae->seekfn_arg, offset, seek_type);
+}
+
+static AVIOContext *
+avio_evbuffer_open(struct transcode_evbuf_io *evbuf_io, int is_output)
+{
+ struct avio_evbuffer *ae;
+ AVIOContext *s;
+
+ ae = calloc(1, sizeof(struct avio_evbuffer));
+ if (!ae)
+ {
+ DPRINTF(E_LOG, L_FFMPEG, "Out of memory for avio_evbuffer\n");
+
+ return NULL;
+ }
+
+ ae->buffer = av_mallocz(AVIO_BUFFER_SIZE);
+ if (!ae->buffer)
+ {
+ DPRINTF(E_LOG, L_FFMPEG, "Out of memory for avio buffer\n");
+
+ free(ae);
+ return NULL;
+ }
+
+ ae->evbuf = evbuf_io->evbuf;
+ ae->seekfn = evbuf_io->seekfn;
+ ae->seekfn_arg = evbuf_io->seekfn_arg;
+
+ if (is_output)
+ s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 1, ae, NULL, avio_evbuffer_write, NULL);
+ else
+ s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 0, ae, avio_evbuffer_read, NULL, (evbuf_io->seekfn ? avio_evbuffer_seek : NULL));
+
+ if (!s)
+ {
+ DPRINTF(E_LOG, L_FFMPEG, "Could not allocate AVIOContext\n");
+
+ av_free(ae->buffer);
+ free(ae);
+ return NULL;
+ }
+
+ s->seekable = (evbuf_io->seekfn ? AVIO_SEEKABLE_NORMAL : 0);
+
+ return s;
+}
+
+static AVIOContext *
+avio_input_evbuffer_open(struct transcode_evbuf_io *evbuf_io)
+{
+ return avio_evbuffer_open(evbuf_io, 0);
+}
+
+static AVIOContext *
+avio_output_evbuffer_open(struct evbuffer *evbuf)
+{
+ struct transcode_evbuf_io evbuf_io = { 0 };
+
+ evbuf_io.evbuf = evbuf;
+
+ return avio_evbuffer_open(&evbuf_io, 1);
+}
+
+static void
+avio_evbuffer_close(AVIOContext *s)
+{
+ struct avio_evbuffer *ae;
+
+ if (!s)
+ return;
+
+ ae = (struct avio_evbuffer *)s->opaque;
+
+ avio_flush(s);
+
+ av_free(s->buffer);
+ free(ae);
+
+ av_free(s);
+}
+
/* --------------------------- INPUT/OUTPUT INIT --------------------------- */
@@ -821,7 +960,7 @@ open_decoder(AVCodecContext **dec_ctx, unsigned int *stream_index, struct decode
}
static int
-open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enum probe_type probe_type)
+open_input(struct decode_ctx *ctx, const char *path, struct transcode_evbuf_io *evbuf_io, enum probe_type probe_type)
{
AVDictionary *options = NULL;
AVCodecContext *dec_ctx;
@@ -861,7 +1000,7 @@ open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enu
ctx->ifmt_ctx->interrupt_callback.opaque = ctx;
ctx->timestamp = av_gettime();
- if (evbuf)
+ if (evbuf_io)
{
ifmt = av_find_input_format(ctx->settings.in_format);
if (!ifmt)
@@ -870,7 +1009,7 @@ open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enu
goto out_fail;
}
- CHECK_NULL(L_XCODE, ctx->avio = avio_input_evbuffer_open(evbuf));
+ CHECK_NULL(L_XCODE, ctx->avio = avio_input_evbuffer_open(evbuf_io));
ctx->ifmt_ctx->pb = ctx->avio;
ret = avformat_open_input(&ctx->ifmt_ctx, NULL, ifmt, &options);
@@ -1232,7 +1371,7 @@ close_filters(struct encode_ctx *ctx)
/* Setup */
struct decode_ctx *
-transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length)
+transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct transcode_evbuf_io *evbuf_io, uint32_t song_length)
{
struct decode_ctx *ctx;
int ret;
@@ -1250,14 +1389,14 @@ transcode_decode_setup(enum transcode_profile profile, struct media_quality *qua
if (data_kind == DATA_KIND_HTTP)
{
- ret = open_input(ctx, path, evbuf, PROBE_TYPE_QUICK);
+ ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_QUICK);
// Retry with a default, slower probe size
if (ret == AVERROR_STREAM_NOT_FOUND)
- ret = open_input(ctx, path, evbuf, PROBE_TYPE_DEFAULT);
+ ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_DEFAULT);
}
else
- ret = open_input(ctx, path, evbuf, PROBE_TYPE_DEFAULT);
+ ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_DEFAULT);
if (ret < 0)
goto fail_free;
diff --git a/src/transcode.h b/src/transcode.h
index 6707189c..d2d10169 100644
--- a/src/transcode.h
+++ b/src/transcode.h
@@ -19,18 +19,30 @@ enum transcode_profile
XCODE_PCM16,
XCODE_PCM24,
XCODE_PCM32,
- // Transcodes the best audio stream into MP3
+ // Transcodes the best audio stream to MP3
XCODE_MP3,
- // Transcodes the best audio stream into OPUS
+ // Transcodes the best audio stream to OPUS
XCODE_OPUS,
- // Transcodes the best audio stream into ALAC
+ // Transcodes the best audio stream to ALAC
XCODE_ALAC,
- // Transcodes the best video stream into JPEG/PNG/VP8
+ // Transcodes the best audio stream from OGG
+ XCODE_OGG,
+ // Transcodes the best video stream to JPEG/PNG/VP8
XCODE_JPEG,
XCODE_PNG,
XCODE_VP8,
};
+enum transcode_seek_type
+{
+ XCODE_SEEK_SIZE,
+ XCODE_SEEK_SET,
+ XCODE_SEEK_CUR,
+};
+
+typedef void transcode_frame;
+typedef int64_t(*transcode_seekfn)(void *arg, int64_t offset, enum transcode_seek_type seek_type);
+
struct decode_ctx;
struct encode_ctx;
struct transcode_ctx
@@ -39,11 +51,18 @@ struct transcode_ctx
struct encode_ctx *encode_ctx;
};
-typedef void transcode_frame;
+struct transcode_evbuf_io
+{
+ struct evbuffer *evbuf;
+
+ // Set to null if no seek support required
+ transcode_seekfn seekfn;
+ void *seekfn_arg;
+};
// Setting up
struct decode_ctx *
-transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length);
+transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct transcode_evbuf_io *evbuf_io, uint32_t song_length);
struct encode_ctx *
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);