~ubuntu-branches/ubuntu/trusty/pianobar/trusty

« back to all changes in this revision

Viewing changes to src/libpiano/request.c

  • Committer: Package Import Robot
  • Author(s): Luke Faraone
  • Date: 2012-05-06 14:24:34 UTC
  • mfrom: (1.3.9)
  • Revision ID: package-import@ubuntu.com-20120506142434-74kwucnyp97msxdi
Tags: 2012.05.06-1
* New upstream version.
  - JSON api support (closes: #670483, LP: #988395)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008-2012
 
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
#ifndef __FreeBSD__
 
25
#define _BSD_SOURCE /* required by strdup() */
 
26
#define _DARWIN_C_SOURCE /* strdup() on OS X */
 
27
#endif
 
28
 
 
29
#include <json.h>
 
30
#include <assert.h>
 
31
#include <stdio.h>
 
32
#include <string.h>
 
33
/* needed for urlencode */
 
34
#include <waitress.h>
 
35
 
 
36
#include "piano.h"
 
37
#include "crypt.h"
 
38
 
 
39
/*      convert audio format id to string
 
40
 *      @param format id
 
41
 *      @return constant string
 
42
 */
 
43
static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
 
44
        switch (format) {
 
45
                case PIANO_AF_AACPLUS_LO:
 
46
                        return "HTTP_32_AACPLUS";
 
47
                        break;
 
48
 
 
49
                case PIANO_AF_AACPLUS:
 
50
                        return "HTTP_64_AACPLUS";
 
51
                        break;
 
52
 
 
53
                case PIANO_AF_MP3:
 
54
                        return "HTTP_128_MP3";
 
55
                        break;
 
56
 
 
57
                case PIANO_AF_MP3_HI:
 
58
                        return "HTTP_192_MP3";
 
59
                        break;
 
60
 
 
61
                default:
 
62
                        return NULL;
 
63
                        break;
 
64
        }
 
65
}
 
66
 
 
67
/*      prepare piano request (initializes request type, urlpath and postData)
 
68
 *      @param piano handle
 
69
 *      @param request structure
 
70
 *      @param request type
 
71
 */
 
72
PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
 
73
                PianoRequestType_t type) {
 
74
        PianoReturn_t ret = PIANO_RET_OK;
 
75
        const char *jsonSendBuf;
 
76
        const char *method = NULL;
 
77
        json_object *j = json_object_new_object ();
 
78
        /* corrected timestamp */
 
79
        time_t timestamp = time (NULL) - ph->timeOffset;
 
80
        bool encrypted = true;
 
81
 
 
82
        assert (ph != NULL);
 
83
        assert (req != NULL);
 
84
 
 
85
        req->type = type;
 
86
        /* no tls by default */
 
87
        req->secure = false;
 
88
 
 
89
        switch (req->type) {
 
90
                case PIANO_REQUEST_LOGIN: {
 
91
                        /* authenticate user */
 
92
                        PianoRequestDataLogin_t *logindata = req->data;
 
93
 
 
94
                        assert (logindata != NULL);
 
95
 
 
96
                        switch (logindata->step) {
 
97
                                case 0:
 
98
                                        encrypted = false;
 
99
                                        req->secure = true;
 
100
 
 
101
                                        json_object_object_add (j, "username",
 
102
                                                        json_object_new_string (ph->partner.user));
 
103
                                        json_object_object_add (j, "password",
 
104
                                                        json_object_new_string (ph->partner.password));
 
105
                                        json_object_object_add (j, "deviceModel",
 
106
                                                        json_object_new_string (ph->partner.device));
 
107
                                        json_object_object_add (j, "version",
 
108
                                                        json_object_new_string ("5"));
 
109
                                        json_object_object_add (j, "includeUrls",
 
110
                                                        json_object_new_boolean (true));
 
111
                                        snprintf (req->urlPath, sizeof (req->urlPath),
 
112
                                                        PIANO_RPC_PATH "method=auth.partnerLogin");
 
113
                                        break;
 
114
 
 
115
                                case 1: {
 
116
                                        char *urlencAuthToken;
 
117
 
 
118
                                        req->secure = true;
 
119
 
 
120
                                        json_object_object_add (j, "loginType",
 
121
                                                        json_object_new_string ("user"));
 
122
                                        json_object_object_add (j, "username",
 
123
                                                        json_object_new_string (logindata->user));
 
124
                                        json_object_object_add (j, "password",
 
125
                                                        json_object_new_string (logindata->password));
 
126
                                        json_object_object_add (j, "partnerAuthToken",
 
127
                                                        json_object_new_string (ph->partner.authToken));
 
128
                                        json_object_object_add (j, "syncTime",
 
129
                                                        json_object_new_int (timestamp));
 
130
 
 
131
                                        urlencAuthToken = WaitressUrlEncode (ph->partner.authToken);
 
132
                                        assert (urlencAuthToken != NULL);
 
133
                                        snprintf (req->urlPath, sizeof (req->urlPath),
 
134
                                                        PIANO_RPC_PATH "method=auth.userLogin&"
 
135
                                                        "auth_token=%s&partner_id=%i", urlencAuthToken,
 
136
                                                        ph->partner.id);
 
137
                                        free (urlencAuthToken);
 
138
 
 
139
                                        break;
 
140
                                }
 
141
                        }
 
142
                        break;
 
143
                }
 
144
 
 
145
                case PIANO_REQUEST_GET_STATIONS: {
 
146
                        /* get stations, user must be authenticated */
 
147
                        assert (ph->user.listenerId != NULL);
 
148
                        method = "user.getStationList";
 
149
                        break;
 
150
                }
 
151
 
 
152
                case PIANO_REQUEST_GET_PLAYLIST: {
 
153
                        /* get playlist for specified station */
 
154
                        PianoRequestDataGetPlaylist_t *reqData = req->data;
 
155
 
 
156
                        assert (reqData != NULL);
 
157
                        assert (reqData->station != NULL);
 
158
                        assert (reqData->station->id != NULL);
 
159
                        assert (reqData->format != PIANO_AF_UNKNOWN);
 
160
 
 
161
                        req->secure = true;
 
162
 
 
163
                        json_object_object_add (j, "stationToken",
 
164
                                        json_object_new_string (reqData->station->id));
 
165
                        json_object_object_add (j, "additionalAudioUrl",
 
166
                                        json_object_new_string (PianoAudioFormatToString (reqData->format)));
 
167
 
 
168
                        method = "station.getPlaylist";
 
169
                        break;
 
170
                }
 
171
 
 
172
                case PIANO_REQUEST_ADD_FEEDBACK: {
 
173
                        /* low-level, don't use directly (see _RATE_SONG and _MOVE_SONG) */
 
174
                        PianoRequestDataAddFeedback_t *reqData = req->data;
 
175
                        
 
176
                        assert (reqData != NULL);
 
177
                        assert (reqData->trackToken != NULL);
 
178
                        assert (reqData->rating != PIANO_RATE_NONE);
 
179
 
 
180
                        json_object_object_add (j, "trackToken",
 
181
                                        json_object_new_string (reqData->trackToken));
 
182
                        json_object_object_add (j, "isPositive",
 
183
                                        json_object_new_boolean (reqData->rating == PIANO_RATE_LOVE));
 
184
 
 
185
                        method = "station.addFeedback";
 
186
                        break;
 
187
                }
 
188
 
 
189
                case PIANO_REQUEST_RENAME_STATION: {
 
190
                        PianoRequestDataRenameStation_t *reqData = req->data;
 
191
 
 
192
                        assert (reqData != NULL);
 
193
                        assert (reqData->station != NULL);
 
194
                        assert (reqData->newName != NULL);
 
195
 
 
196
                        json_object_object_add (j, "stationToken",
 
197
                                        json_object_new_string (reqData->station->id));
 
198
                        json_object_object_add (j, "stationName",
 
199
                                        json_object_new_string (reqData->newName));
 
200
 
 
201
                        method = "station.renameStation";
 
202
                        break;
 
203
                }
 
204
 
 
205
                case PIANO_REQUEST_DELETE_STATION: {
 
206
                        /* delete station */
 
207
                        PianoStation_t *station = req->data;
 
208
 
 
209
                        assert (station != NULL);
 
210
                        assert (station->id != NULL);
 
211
 
 
212
                        json_object_object_add (j, "stationToken",
 
213
                                        json_object_new_string (station->id));
 
214
 
 
215
                        method = "station.deleteStation";
 
216
                        break;
 
217
                }
 
218
 
 
219
                case PIANO_REQUEST_SEARCH: {
 
220
                        /* search for artist/song title */
 
221
                        PianoRequestDataSearch_t *reqData = req->data;
 
222
 
 
223
                        assert (reqData != NULL);
 
224
                        assert (reqData->searchStr != NULL);
 
225
 
 
226
                        json_object_object_add (j, "searchText",
 
227
                                        json_object_new_string (reqData->searchStr));
 
228
 
 
229
                        method = "music.search";
 
230
                        break;
 
231
                }
 
232
 
 
233
                case PIANO_REQUEST_CREATE_STATION: {
 
234
                        /* create new station from specified musicid (type=mi, get one by
 
235
                         * performing a search) or shared station id (type=sh) */
 
236
                        PianoRequestDataCreateStation_t *reqData = req->data;
 
237
 
 
238
                        assert (reqData != NULL);
 
239
                        assert (reqData->id != NULL);
 
240
 
 
241
                        json_object_object_add (j, "musicToken",
 
242
                                        json_object_new_string (reqData->id));
 
243
 
 
244
                        method = "station.createStation";
 
245
                        break;
 
246
                }
 
247
 
 
248
                case PIANO_REQUEST_ADD_SEED: {
 
249
                        /* add another seed to specified station */
 
250
                        PianoRequestDataAddSeed_t *reqData = req->data;
 
251
 
 
252
                        assert (reqData != NULL);
 
253
                        assert (reqData->station != NULL);
 
254
                        assert (reqData->musicId != NULL);
 
255
 
 
256
                        json_object_object_add (j, "musicToken",
 
257
                                        json_object_new_string (reqData->musicId));
 
258
                        json_object_object_add (j, "stationToken",
 
259
                                        json_object_new_string (reqData->station->id));
 
260
 
 
261
                        method = "station.addMusic";
 
262
                        break;
 
263
                }
 
264
 
 
265
                case PIANO_REQUEST_ADD_TIRED_SONG: {
 
266
                        /* ban song for a month from all stations */
 
267
                        PianoSong_t *song = req->data;
 
268
 
 
269
                        assert (song != NULL);
 
270
 
 
271
                        json_object_object_add (j, "trackToken",
 
272
                                        json_object_new_string (song->trackToken));
 
273
 
 
274
                        method = "user.sleepSong";
 
275
                        break;
 
276
                }
 
277
 
 
278
                case PIANO_REQUEST_SET_QUICKMIX: {
 
279
                        /* select stations included in quickmix (see useQuickMix flag of
 
280
                         * PianoStation_t) */
 
281
                        PianoStation_t *curStation = ph->stations;
 
282
                        json_object *a = json_object_new_array ();
 
283
 
 
284
                        while (curStation != NULL) {
 
285
                                /* quick mix can't contain itself */
 
286
                                if (curStation->useQuickMix && !curStation->isQuickMix) {
 
287
                                        json_object_array_add (a,
 
288
                                                        json_object_new_string (curStation->id));
 
289
                                }
 
290
 
 
291
                                curStation = curStation->next;
 
292
                        }
 
293
 
 
294
                        json_object_object_add (j, "quickMixStationIds", a);
 
295
 
 
296
                        method = "user.setQuickMix";
 
297
                        break;
 
298
                }
 
299
 
 
300
                case PIANO_REQUEST_GET_GENRE_STATIONS: {
 
301
                        /* receive list of pandora's genre stations */
 
302
                        method = "station.getGenreStations";
 
303
                        break;
 
304
                }
 
305
 
 
306
                case PIANO_REQUEST_TRANSFORM_STATION: {
 
307
                        /* transform shared station into private */
 
308
                        PianoStation_t *station = req->data;
 
309
 
 
310
                        assert (station != NULL);
 
311
 
 
312
                        json_object_object_add (j, "stationToken",
 
313
                                        json_object_new_string (station->id));
 
314
 
 
315
                        method = "station.transformSharedStation";
 
316
                        break;
 
317
                }
 
318
 
 
319
                case PIANO_REQUEST_EXPLAIN: {
 
320
                        /* explain why particular song was played */
 
321
                        PianoRequestDataExplain_t *reqData = req->data;
 
322
 
 
323
                        assert (reqData != NULL);
 
324
                        assert (reqData->song != NULL);
 
325
 
 
326
                        json_object_object_add (j, "trackToken",
 
327
                                        json_object_new_string (reqData->song->trackToken));
 
328
 
 
329
                        method = "track.explainTrack";
 
330
                        break;
 
331
                }
 
332
 
 
333
                case PIANO_REQUEST_BOOKMARK_SONG: {
 
334
                        /* bookmark song */
 
335
                        PianoSong_t *song = req->data;
 
336
 
 
337
                        assert (song != NULL);
 
338
 
 
339
                        json_object_object_add (j, "trackToken",
 
340
                                        json_object_new_string (song->trackToken));
 
341
 
 
342
                        method = "bookmark.addSongBookmark";
 
343
                        break;
 
344
                }
 
345
 
 
346
                case PIANO_REQUEST_BOOKMARK_ARTIST: {
 
347
                        /* bookmark artist */
 
348
                        PianoSong_t *song = req->data;
 
349
 
 
350
                        assert (song != NULL);
 
351
 
 
352
                        json_object_object_add (j, "trackToken",
 
353
                                        json_object_new_string (song->trackToken));
 
354
 
 
355
                        method = "bookmark.addArtistBookmark";
 
356
                        break;
 
357
                }
 
358
 
 
359
                case PIANO_REQUEST_GET_STATION_INFO: {
 
360
                        /* get station information (seeds and feedback) */
 
361
                        PianoRequestDataGetStationInfo_t *reqData = req->data;
 
362
 
 
363
                        assert (reqData != NULL);
 
364
                        assert (reqData->station != NULL);
 
365
 
 
366
                        json_object_object_add (j, "stationToken",
 
367
                                        json_object_new_string (reqData->station->id));
 
368
                        json_object_object_add (j, "includeExtendedAttributes",
 
369
                                        json_object_new_boolean (true));
 
370
 
 
371
                        method = "station.getStation";
 
372
                        break;
 
373
                }
 
374
 
 
375
                case PIANO_REQUEST_DELETE_FEEDBACK: {
 
376
                        PianoSong_t *song = req->data;
 
377
 
 
378
                        assert (song != NULL);
 
379
 
 
380
                        json_object_object_add (j, "feedbackId",
 
381
                                        json_object_new_string (song->feedbackId));
 
382
 
 
383
                        method = "station.deleteFeedback";
 
384
                        break;
 
385
                }
 
386
 
 
387
                case PIANO_REQUEST_DELETE_SEED: {
 
388
                        PianoRequestDataDeleteSeed_t *reqData = req->data;
 
389
                        char *seedId = NULL;
 
390
 
 
391
                        assert (reqData != NULL);
 
392
                        assert (reqData->song != NULL || reqData->artist != NULL ||
 
393
                                        reqData->station != NULL);
 
394
 
 
395
                        if (reqData->song != NULL) {
 
396
                                seedId = reqData->song->seedId;
 
397
                        } else if (reqData->artist != NULL) {
 
398
                                seedId = reqData->artist->seedId;
 
399
                        } else if (reqData->station != NULL) {
 
400
                                seedId = reqData->station->seedId;
 
401
                        }
 
402
 
 
403
                        assert (seedId != NULL);
 
404
 
 
405
                        json_object_object_add (j, "seedId",
 
406
                                        json_object_new_string (seedId));
 
407
 
 
408
                        method = "station.deleteMusic";
 
409
                        break;
 
410
                }
 
411
 
 
412
                /* "high-level" wrapper */
 
413
                case PIANO_REQUEST_RATE_SONG: {
 
414
                        /* love/ban song */
 
415
                        PianoRequestDataRateSong_t *reqData = req->data;
 
416
 
 
417
                        assert (reqData != NULL);
 
418
                        assert (reqData->song != NULL);
 
419
                        assert (reqData->rating != PIANO_RATE_NONE);
 
420
 
 
421
                        PianoRequestDataAddFeedback_t transformedReqData;
 
422
                        transformedReqData.stationId = reqData->song->stationId;
 
423
                        transformedReqData.trackToken = reqData->song->trackToken;
 
424
                        transformedReqData.rating = reqData->rating;
 
425
                        req->data = &transformedReqData;
 
426
 
 
427
                        /* create request data (url, post data) */
 
428
                        ret = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
 
429
                        /* and reset request type/data */
 
430
                        req->type = PIANO_REQUEST_RATE_SONG;
 
431
                        req->data = reqData;
 
432
 
 
433
                        goto cleanup;
 
434
                        break;
 
435
                }
 
436
 
 
437
                case PIANO_REQUEST_MOVE_SONG: {
 
438
                        /* move song to a different station, needs two requests */
 
439
                        PianoRequestDataMoveSong_t *reqData = req->data;
 
440
                        PianoRequestDataAddFeedback_t transformedReqData;
 
441
 
 
442
                        assert (reqData != NULL);
 
443
                        assert (reqData->song != NULL);
 
444
                        assert (reqData->from != NULL);
 
445
                        assert (reqData->to != NULL);
 
446
                        assert (reqData->step < 2);
 
447
 
 
448
                        transformedReqData.trackToken = reqData->song->trackToken;
 
449
                        req->data = &transformedReqData;
 
450
 
 
451
                        switch (reqData->step) {
 
452
                                case 0:
 
453
                                        transformedReqData.stationId = reqData->from->id;
 
454
                                        transformedReqData.rating = PIANO_RATE_BAN;
 
455
                                        break;
 
456
 
 
457
                                case 1:
 
458
                                        transformedReqData.stationId = reqData->to->id;
 
459
                                        transformedReqData.rating = PIANO_RATE_LOVE;
 
460
                                        break;
 
461
                        }
 
462
 
 
463
                        /* create request data (url, post data) */
 
464
                        ret = PianoRequest (ph, req, PIANO_REQUEST_ADD_FEEDBACK);
 
465
                        /* and reset request type/data */
 
466
                        req->type = PIANO_REQUEST_MOVE_SONG;
 
467
                        req->data = reqData;
 
468
 
 
469
                        goto cleanup;
 
470
                        break;
 
471
                }
 
472
        }
 
473
 
 
474
        /* standard parameter */
 
475
        if (method != NULL) {
 
476
                char *urlencAuthToken;
 
477
 
 
478
                assert (ph->user.authToken != NULL);
 
479
 
 
480
                urlencAuthToken = WaitressUrlEncode (ph->user.authToken);
 
481
                assert (urlencAuthToken != NULL);
 
482
 
 
483
                snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
 
484
                                "method=%s&auth_token=%s&partner_id=%i&user_id=%s", method,
 
485
                                urlencAuthToken, ph->partner.id, ph->user.listenerId);
 
486
 
 
487
                free (urlencAuthToken);
 
488
 
 
489
                json_object_object_add (j, "userAuthToken",
 
490
                                json_object_new_string (ph->user.authToken));
 
491
                json_object_object_add (j, "syncTime",
 
492
                                json_object_new_int (timestamp));
 
493
        }
 
494
 
 
495
        /* json to string */
 
496
        jsonSendBuf = json_object_to_json_string (j);
 
497
        if (encrypted) {
 
498
                if ((req->postData = PianoEncryptString (ph->partner.out,
 
499
                                jsonSendBuf)) == NULL) {
 
500
                        ret = PIANO_RET_OUT_OF_MEMORY;
 
501
                }
 
502
        } else {
 
503
                req->postData = strdup (jsonSendBuf);
 
504
        }
 
505
 
 
506
cleanup:
 
507
        json_object_put (j);
 
508
 
 
509
        return ret;
 
510
}
 
511