1 /*
2 Copyright (c) 2008-2017
3 Lars-Dominik Braun <lars@6xq.net>
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22 */
23
24 #include "../config.h"
25
26 #include <json.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <time.h>
30 #include <stdlib.h>
31
32 #include "piano.h"
33 #include "piano_private.h"
34 #include "crypt.h"
35
PianoJsonStrdup(json_object * j,const char * key)36 static char *PianoJsonStrdup (json_object *j, const char *key) {
37 assert (j != NULL);
38 assert (key != NULL);
39
40 json_object *v;
41 if (json_object_object_get_ex (j, key, &v)) {
42 return strdup (json_object_get_string (v));
43 } else {
44 return NULL;
45 }
46 }
47
getBoolDefault(json_object * const j,const char * const key,const bool def)48 static bool getBoolDefault (json_object * const j, const char * const key, const bool def) {
49 assert (j != NULL);
50 assert (key != NULL);
51
52 json_object *v;
53 if (json_object_object_get_ex (j, key, &v)) {
54 return json_object_get_boolean (v);
55 } else {
56 return def;
57 }
58 }
59
PianoJsonParseStation(json_object * j,PianoStation_t * s)60 static void PianoJsonParseStation (json_object *j, PianoStation_t *s) {
61 s->name = PianoJsonStrdup (j, "stationName");
62 s->id = PianoJsonStrdup (j, "stationToken");
63 s->isCreator = !getBoolDefault (j, "isShared", !false);
64 s->isQuickMix = getBoolDefault (j, "isQuickMix", false);
65 }
66
67 /* concat strings
68 * @param destination
69 * @param source string
70 * @param destination size
71 */
PianoStrpcat(char * restrict dest,const char * restrict src,size_t len)72 static void PianoStrpcat (char * restrict dest, const char * restrict src,
73 size_t len) {
74 /* skip to end of string */
75 while (*dest != '\0' && len > 1) {
76 ++dest;
77 --len;
78 }
79
80 /* append until source exhausted or destination full */
81 while (*src != '\0' && len > 1) {
82 *dest = *src;
83 ++dest;
84 ++src;
85 --len;
86 }
87
88 *dest = '\0';
89 }
90
91 /* parse xml response and update data structures/return new data structure
92 * @param piano handle
93 * @param initialized request (expects responseData to be a NUL-terminated
94 * string)
95 */
PianoResponse(PianoHandle_t * ph,PianoRequest_t * req)96 PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
97 PianoReturn_t ret = PIANO_RET_OK;
98
99 assert (ph != NULL);
100 assert (req != NULL);
101
102 json_object * const j = json_tokener_parse (req->responseData);
103
104 json_object *status;
105 if (!json_object_object_get_ex (j, "stat", &status)) {
106 ret = PIANO_RET_INVALID_RESPONSE;
107 goto cleanup;
108 }
109
110 /* error handling */
111 if (strcmp (json_object_get_string (status), "ok") != 0) {
112 json_object *code;
113 if (!json_object_object_get_ex (j, "code", &code)) {
114 ret = PIANO_RET_INVALID_RESPONSE;
115 } else {
116 ret = json_object_get_int (code)+PIANO_RET_OFFSET;
117
118 if (ret == PIANO_RET_P_INVALID_PARTNER_LOGIN &&
119 req->type == PIANO_REQUEST_LOGIN) {
120 PianoRequestDataLogin_t *reqData = req->data;
121 if (reqData->step == 1) {
122 /* return value is ambiguous, as both, partnerLogin and
123 * userLogin return INVALID_PARTNER_LOGIN. Fix that to provide
124 * better error messages. */
125 ret = PIANO_RET_INVALID_LOGIN;
126 }
127 }
128 }
129
130 goto cleanup;
131 }
132
133 json_object *result = NULL;
134 /* missing for some request types */
135 json_object_object_get_ex (j, "result", &result);
136
137 switch (req->type) {
138 case PIANO_REQUEST_LOGIN: {
139 /* authenticate user */
140 PianoRequestDataLogin_t *reqData = req->data;
141
142 assert (req->responseData != NULL);
143 assert (reqData != NULL);
144
145 switch (reqData->step) {
146 case 0: {
147 /* decrypt timestamp */
148 json_object *jsonTimestamp;
149 if (!json_object_object_get_ex (result, "syncTime", &jsonTimestamp)) {
150 ret = PIANO_RET_INVALID_RESPONSE;
151 break;
152 }
153 assert (jsonTimestamp != NULL);
154 const char * const cryptedTimestamp = json_object_get_string (jsonTimestamp);
155 assert (cryptedTimestamp != NULL);
156 const time_t realTimestamp = time (NULL);
157 char *decryptedTimestamp = NULL;
158 size_t decryptedSize;
159
160 ret = PIANO_RET_ERR;
161 if ((decryptedTimestamp = PianoDecryptString (ph->partner.in,
162 cryptedTimestamp, &decryptedSize)) != NULL &&
163 decryptedSize > 4) {
164 /* skip four bytes garbage(?) at beginning */
165 const unsigned long timestamp = strtoul (
166 decryptedTimestamp+4, NULL, 0);
167 ph->timeOffset = (long int) realTimestamp -
168 (long int) timestamp;
169 ret = PIANO_RET_CONTINUE_REQUEST;
170 }
171 free (decryptedTimestamp);
172 /* get auth token */
173 ph->partner.authToken = PianoJsonStrdup (result,
174 "partnerAuthToken");
175 json_object *partnerId;
176 if (!json_object_object_get_ex (result, "partnerId", &partnerId)) {
177 ret = PIANO_RET_INVALID_RESPONSE;
178 break;
179 }
180 ph->partner.id = json_object_get_int (partnerId);
181 ++reqData->step;
182 break;
183 }
184
185 case 1:
186 /* information exists when reauthenticating, destroy to
187 * avoid memleak */
188 if (ph->user.listenerId != NULL) {
189 PianoDestroyUserInfo (&ph->user);
190 }
191 ph->user.listenerId = PianoJsonStrdup (result, "userId");
192 ph->user.authToken = PianoJsonStrdup (result,
193 "userAuthToken");
194 break;
195 }
196 break;
197 }
198
199 case PIANO_REQUEST_GET_STATIONS: {
200 /* get stations */
201 assert (req->responseData != NULL);
202
203 json_object *stations, *mix = NULL;
204
205 if (!json_object_object_get_ex (result, "stations", &stations)) {
206 break;
207 }
208
209 for (int i = 0; i < json_object_array_length (stations); i++) {
210 PianoStation_t *tmpStation;
211 json_object *s = json_object_array_get_idx (stations, i);
212
213 if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
214 return PIANO_RET_OUT_OF_MEMORY;
215 }
216
217 PianoJsonParseStation (s, tmpStation);
218
219 if (tmpStation->isQuickMix) {
220 /* fix flags on other stations later */
221 json_object_object_get_ex (s, "quickMixStationIds", &mix);
222 }
223
224 /* start new linked list or append */
225 ph->stations = PianoListAppendP (ph->stations, tmpStation);
226 }
227
228 /* fix quickmix flags */
229 if (mix != NULL) {
230 PianoStation_t *curStation = ph->stations;
231 PianoListForeachP (curStation) {
232 for (int i = 0; i < json_object_array_length (mix); i++) {
233 json_object *id = json_object_array_get_idx (mix, i);
234 if (strcmp (json_object_get_string (id),
235 curStation->id) == 0) {
236 curStation->useQuickMix = true;
237 }
238 }
239 }
240 }
241 break;
242 }
243
244 case PIANO_REQUEST_GET_PLAYLIST: {
245 /* get playlist, usually four songs */
246 PianoRequestDataGetPlaylist_t *reqData = req->data;
247 PianoSong_t *playlist = NULL;
248
249 assert (req->responseData != NULL);
250 assert (reqData != NULL);
251 assert (reqData->quality != PIANO_AQ_UNKNOWN);
252
253 json_object *items = NULL;
254 if (!json_object_object_get_ex (result, "items", &items)) {
255 break;
256 }
257 assert (items != NULL);
258
259 for (int i = 0; i < json_object_array_length (items); i++) {
260 json_object *s = json_object_array_get_idx (items, i);
261 PianoSong_t *song;
262
263 if ((song = calloc (1, sizeof (*song))) == NULL) {
264 return PIANO_RET_OUT_OF_MEMORY;
265 }
266
267 if (!json_object_object_get_ex (s, "artistName", NULL)) {
268 free (song);
269 continue;
270 }
271
272 /* get audio url based on selected quality */
273 static const char *qualityMap[] = {"", "lowQuality", "mediumQuality",
274 "highQuality"};
275 assert (reqData->quality < sizeof (qualityMap)/sizeof (*qualityMap));
276 static const char *formatMap[] = {"", "aacplus", "mp3"};
277
278 json_object *umap;
279 if (json_object_object_get_ex (s, "audioUrlMap", &umap)) {
280 assert (umap != NULL);
281 json_object *jsonEncoding, *qmap;
282 if (json_object_object_get_ex (umap, qualityMap[reqData->quality], &qmap) &&
283 json_object_object_get_ex (qmap, "encoding", &jsonEncoding)) {
284 assert (qmap != NULL);
285 const char *encoding = json_object_get_string (jsonEncoding);
286 assert (encoding != NULL);
287 for (size_t k = 0; k < sizeof (formatMap)/sizeof (*formatMap); k++) {
288 if (strcmp (formatMap[k], encoding) == 0) {
289 song->audioFormat = k;
290 break;
291 }
292 }
293 song->audioUrl = PianoJsonStrdup (qmap, "audioUrl");
294 } else {
295 /* requested quality is not available */
296 ret = PIANO_RET_QUALITY_UNAVAILABLE;
297 free (song);
298 PianoDestroyPlaylist (playlist);
299 goto cleanup;
300 }
301 }
302
303 json_object *v;
304 song->artist = PianoJsonStrdup (s, "artistName");
305 song->album = PianoJsonStrdup (s, "albumName");
306 song->title = PianoJsonStrdup (s, "songName");
307 song->trackToken = PianoJsonStrdup (s, "trackToken");
308 song->stationId = PianoJsonStrdup (s, "stationId");
309 song->coverArt = PianoJsonStrdup (s, "albumArtUrl");
310 song->detailUrl = PianoJsonStrdup (s, "songDetailUrl");
311 song->fileGain = json_object_object_get_ex (s, "trackGain", &v) ?
312 json_object_get_double (v) : 0.0;
313 song->length = json_object_object_get_ex (s, "trackLength", &v) ?
314 json_object_get_int (v) : 0;
315 switch (json_object_object_get_ex (s, "songRating", &v) ?
316 json_object_get_int (v) : 0) {
317 case 1:
318 song->rating = PIANO_RATE_LOVE;
319 break;
320 }
321
322 playlist = PianoListAppendP (playlist, song);
323 }
324
325 reqData->retPlaylist = playlist;
326 break;
327 }
328
329 case PIANO_REQUEST_RATE_SONG: {
330 /* love/ban song */
331 PianoRequestDataRateSong_t *reqData = req->data;
332 reqData->song->rating = reqData->rating;
333 break;
334 }
335
336 case PIANO_REQUEST_ADD_FEEDBACK:
337 /* never ever use this directly, low-level call */
338 assert (0);
339 break;
340
341 case PIANO_REQUEST_RENAME_STATION: {
342 /* rename station and update PianoStation_t structure */
343 PianoRequestDataRenameStation_t *reqData = req->data;
344
345 assert (reqData != NULL);
346 assert (reqData->station != NULL);
347 assert (reqData->newName != NULL);
348
349 free (reqData->station->name);
350 reqData->station->name = strdup (reqData->newName);
351 break;
352 }
353
354 case PIANO_REQUEST_DELETE_STATION: {
355 /* delete station from server and station list */
356 PianoStation_t *station = req->data;
357
358 assert (station != NULL);
359
360 ph->stations = PianoListDeleteP (ph->stations, station);
361 PianoDestroyStation (station);
362 free (station);
363 break;
364 }
365
366 case PIANO_REQUEST_SEARCH: {
367 /* search artist/song */
368 PianoRequestDataSearch_t *reqData = req->data;
369 PianoSearchResult_t *searchResult;
370
371 assert (req->responseData != NULL);
372 assert (reqData != NULL);
373
374 searchResult = &reqData->searchResult;
375 memset (searchResult, 0, sizeof (*searchResult));
376
377 /* get artists */
378 json_object *artists;
379 if (json_object_object_get_ex (result, "artists", &artists)) {
380 for (int i = 0; i < json_object_array_length (artists); i++) {
381 json_object *a = json_object_array_get_idx (artists, i);
382 PianoArtist_t *artist;
383
384 if ((artist = calloc (1, sizeof (*artist))) == NULL) {
385 return PIANO_RET_OUT_OF_MEMORY;
386 }
387
388 artist->name = PianoJsonStrdup (a, "artistName");
389 artist->musicId = PianoJsonStrdup (a, "musicToken");
390
391 searchResult->artists =
392 PianoListAppendP (searchResult->artists, artist);
393 }
394 }
395
396 /* get songs */
397 json_object *songs;
398 if (json_object_object_get_ex (result, "songs", &songs)) {
399 for (int i = 0; i < json_object_array_length (songs); i++) {
400 json_object *s = json_object_array_get_idx (songs, i);
401 PianoSong_t *song;
402
403 if ((song = calloc (1, sizeof (*song))) == NULL) {
404 return PIANO_RET_OUT_OF_MEMORY;
405 }
406
407 song->title = PianoJsonStrdup (s, "songName");
408 song->artist = PianoJsonStrdup (s, "artistName");
409 song->musicId = PianoJsonStrdup (s, "musicToken");
410
411 searchResult->songs =
412 PianoListAppendP (searchResult->songs, song);
413 }
414 }
415 break;
416 }
417
418 case PIANO_REQUEST_CREATE_STATION: {
419 /* create station, insert new station into station list on success */
420 PianoStation_t *tmpStation;
421
422 if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
423 return PIANO_RET_OUT_OF_MEMORY;
424 }
425
426 PianoJsonParseStation (result, tmpStation);
427
428 PianoStation_t *search = PianoFindStationById (ph->stations,
429 tmpStation->id);
430 if (search != NULL) {
431 ph->stations = PianoListDeleteP (ph->stations, search);
432 PianoDestroyStation (search);
433 free (search);
434 }
435 ph->stations = PianoListAppendP (ph->stations, tmpStation);
436 break;
437 }
438
439 case PIANO_REQUEST_ADD_TIRED_SONG: {
440 PianoSong_t * const song = req->data;
441 song->rating = PIANO_RATE_TIRED;
442 break;
443 }
444
445 case PIANO_REQUEST_ADD_SEED:
446 case PIANO_REQUEST_SET_QUICKMIX:
447 case PIANO_REQUEST_BOOKMARK_SONG:
448 case PIANO_REQUEST_BOOKMARK_ARTIST:
449 case PIANO_REQUEST_DELETE_FEEDBACK:
450 case PIANO_REQUEST_DELETE_SEED:
451 case PIANO_REQUEST_CHANGE_SETTINGS:
452 /* response unused */
453 break;
454
455 case PIANO_REQUEST_GET_GENRE_STATIONS: {
456 /* get genre stations */
457 json_object *categories;
458 if (json_object_object_get_ex (result, "categories", &categories)) {
459 for (int i = 0; i < json_object_array_length (categories); i++) {
460 json_object *c = json_object_array_get_idx (categories, i);
461 PianoGenreCategory_t *tmpGenreCategory;
462
463 if ((tmpGenreCategory = calloc (1,
464 sizeof (*tmpGenreCategory))) == NULL) {
465 return PIANO_RET_OUT_OF_MEMORY;
466 }
467
468 tmpGenreCategory->name = PianoJsonStrdup (c,
469 "categoryName");
470
471 /* get genre subnodes */
472 json_object *stations;
473 if (json_object_object_get_ex (c, "stations", &stations)) {
474 for (int k = 0;
475 k < json_object_array_length (stations); k++) {
476 json_object *s =
477 json_object_array_get_idx (stations, k);
478 PianoGenre_t *tmpGenre;
479
480 if ((tmpGenre = calloc (1,
481 sizeof (*tmpGenre))) == NULL) {
482 return PIANO_RET_OUT_OF_MEMORY;
483 }
484
485 /* get genre attributes */
486 tmpGenre->name = PianoJsonStrdup (s,
487 "stationName");
488 tmpGenre->musicId = PianoJsonStrdup (s,
489 "stationToken");
490
491 tmpGenreCategory->genres =
492 PianoListAppendP (tmpGenreCategory->genres,
493 tmpGenre);
494 }
495 }
496
497 ph->genreStations = PianoListAppendP (ph->genreStations,
498 tmpGenreCategory);
499 }
500 }
501 break;
502 }
503
504 case PIANO_REQUEST_TRANSFORM_STATION: {
505 /* transform shared station into private and update isCreator flag */
506 PianoStation_t *station = req->data;
507
508 assert (req->responseData != NULL);
509 assert (station != NULL);
510
511 station->isCreator = 1;
512 break;
513 }
514
515 case PIANO_REQUEST_EXPLAIN: {
516 /* explain why song was selected */
517 PianoRequestDataExplain_t *reqData = req->data;
518 const size_t strSize = 768;
519
520 assert (reqData != NULL);
521
522 json_object *explanations;
523 if (json_object_object_get_ex (result, "explanations", &explanations)) {
524 reqData->retExplain = malloc (strSize *
525 sizeof (*reqData->retExplain));
526 strncpy (reqData->retExplain, "We're playing this track "
527 "because it features ", strSize);
528 for (int i = 0; i < json_object_array_length (explanations); i++) {
529 json_object *e = json_object_array_get_idx (explanations,
530 i);
531 json_object *f;
532 if (!json_object_object_get_ex (e, "focusTraitName", &f)) {
533 continue;
534 }
535 const char *s = json_object_get_string (f);
536 PianoStrpcat (reqData->retExplain, s, strSize);
537 if (i < json_object_array_length (explanations)-2) {
538 PianoStrpcat (reqData->retExplain, ", ", strSize);
539 } else if (i == json_object_array_length (explanations)-2) {
540 PianoStrpcat (reqData->retExplain, " and ", strSize);
541 } else {
542 PianoStrpcat (reqData->retExplain, ".", strSize);
543 }
544 }
545 }
546 break;
547 }
548
549 case PIANO_REQUEST_GET_SETTINGS: {
550 PianoSettings_t * const settings = req->data;
551
552 assert (settings != NULL);
553
554 settings->explicitContentFilter = getBoolDefault (result,
555 "isExplicitContentFilterEnabled", false);
556 settings->username = PianoJsonStrdup (result, "username");
557 break;
558 }
559
560 case PIANO_REQUEST_GET_STATION_INFO: {
561 /* get station information (seeds and feedback) */
562 PianoRequestDataGetStationInfo_t *reqData = req->data;
563 PianoStationInfo_t *info;
564
565 assert (reqData != NULL);
566
567 info = &reqData->info;
568 assert (info != NULL);
569
570 /* parse music seeds */
571 json_object *music;
572 if (json_object_object_get_ex (result, "music", &music)) {
573 /* songs */
574 json_object *songs;
575 if (json_object_object_get_ex (music, "songs", &songs)) {
576 for (int i = 0; i < json_object_array_length (songs); i++) {
577 json_object *s = json_object_array_get_idx (songs, i);
578 PianoSong_t *seedSong;
579
580 seedSong = calloc (1, sizeof (*seedSong));
581 if (seedSong == NULL) {
582 return PIANO_RET_OUT_OF_MEMORY;
583 }
584
585 seedSong->title = PianoJsonStrdup (s, "songName");
586 seedSong->artist = PianoJsonStrdup (s, "artistName");
587 seedSong->seedId = PianoJsonStrdup (s, "seedId");
588
589 info->songSeeds = PianoListAppendP (info->songSeeds,
590 seedSong);
591 }
592 }
593
594 /* artists */
595 json_object *artists;
596 if (json_object_object_get_ex (music, "artists", &artists)) {
597 for (int i = 0; i < json_object_array_length (artists); i++) {
598 json_object *a = json_object_array_get_idx (artists, i);
599 PianoArtist_t *seedArtist;
600
601 seedArtist = calloc (1, sizeof (*seedArtist));
602 if (seedArtist == NULL) {
603 return PIANO_RET_OUT_OF_MEMORY;
604 }
605
606 seedArtist->name = PianoJsonStrdup (a, "artistName");
607 seedArtist->seedId = PianoJsonStrdup (a, "seedId");
608
609 info->artistSeeds =
610 PianoListAppendP (info->artistSeeds, seedArtist);
611 }
612 }
613 }
614
615 /* parse feedback */
616 json_object *feedback;
617 if (json_object_object_get_ex (result, "feedback", &feedback)) {
618 static const char * const keys[] = {"thumbsUp", "thumbsDown"};
619 for (size_t i = 0; i < sizeof (keys)/sizeof (*keys); i++) {
620 json_object *val;
621 if (!json_object_object_get_ex (feedback, keys[i], &val)) {
622 continue;
623 }
624 assert (json_object_is_type (val, json_type_array));
625 for (int i = 0; i < json_object_array_length (val); i++) {
626 json_object *s = json_object_array_get_idx (val, i);
627 PianoSong_t *feedbackSong;
628
629 feedbackSong = calloc (1, sizeof (*feedbackSong));
630 if (feedbackSong == NULL) {
631 return PIANO_RET_OUT_OF_MEMORY;
632 }
633
634 feedbackSong->title = PianoJsonStrdup (s, "songName");
635 feedbackSong->artist = PianoJsonStrdup (s,
636 "artistName");
637 feedbackSong->feedbackId = PianoJsonStrdup (s,
638 "feedbackId");
639 feedbackSong->rating = getBoolDefault (s, "isPositive",
640 false) ? PIANO_RATE_LOVE : PIANO_RATE_BAN;
641
642 json_object *v;
643 feedbackSong->length =
644 json_object_object_get_ex (s, "trackLength", &v) ?
645 json_object_get_int (v) : 0;
646
647 info->feedback = PianoListAppendP (info->feedback,
648 feedbackSong);
649 }
650 }
651 }
652 break;
653 }
654
655 case PIANO_REQUEST_GET_STATION_MODES: {
656 PianoRequestDataGetStationModes_t *reqData = req->data;
657 assert (reqData != NULL);
658
659 int active = -1;
660
661 json_object *activeMode;
662 if (json_object_object_get_ex (result, "currentModeId", &activeMode)) {
663 active = json_object_get_int (activeMode);
664 }
665
666 json_object *availableModes;
667 if (json_object_object_get_ex (result, "availableModes", &availableModes)) {
668 for (int i = 0; i < json_object_array_length (availableModes); i++) {
669 json_object *val = json_object_array_get_idx (availableModes, i);
670
671 assert (json_object_is_type (val, json_type_object));
672
673 PianoStationMode_t *mode;
674 if ((mode = calloc (1, sizeof (*mode))) == NULL) {
675 return PIANO_RET_OUT_OF_MEMORY;
676 }
677
678 json_object *modeId;
679 if (json_object_object_get_ex (val, "modeId", &modeId)) {
680 mode->id = json_object_get_int (modeId);
681 mode->name = PianoJsonStrdup (val, "modeName");
682 mode->description = PianoJsonStrdup (val, "modeDescription");
683 mode->isAlgorithmic = getBoolDefault (val, "isAlgorithmicMode",
684 false);
685 mode->isTakeover = getBoolDefault (val, "isTakeoverMode",
686 false);
687 mode->active = active == mode->id;
688 }
689
690 reqData->retModes = PianoListAppendP (reqData->retModes,
691 mode);
692 }
693 }
694 break;
695 }
696
697 case PIANO_REQUEST_SET_STATION_MODE: {
698 PianoRequestDataSetStationMode_t *reqData = req->data;
699 assert (reqData != NULL);
700
701 int active = -1;
702
703 json_object *activeMode;
704 if (json_object_object_get_ex (result, "currentModeId", &activeMode)) {
705 active = json_object_get_int (activeMode);
706 }
707
708 if (active != reqData->id) {
709 /* this did not work */
710 return PIANO_RET_ERR;
711 }
712 break;
713 }
714 }
715
716 cleanup:
717 json_object_put (j);
718
719 return ret;
720 }
721
722