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);