summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile47
-rw-r--r--src/libezxml/COPYING20
-rw-r--r--src/libezxml/ezxml.c718
-rw-r--r--src/libezxml/ezxml.h93
-rw-r--r--src/libpiano/crypt.c205
-rw-r--r--src/libpiano/piano.c1146
-rw-r--r--src/libpiano/piano.h79
-rw-r--r--src/libpiano/xml.c980
-rw-r--r--src/libpiano/xml.h49
-rw-r--r--src/settings.c4
-rw-r--r--src/ui.c4
-rw-r--r--src/ui_act.c6
12 files changed, 778 insertions, 2573 deletions
diff --git a/Makefile b/Makefile
index 3bfd7fe..b503e7f 100644
--- a/Makefile
+++ b/Makefile
@@ -39,12 +39,10 @@ PIANOBAR_OBJ=${PIANOBAR_SRC:.c=.o}
LIBPIANO_DIR=src/libpiano
LIBPIANO_SRC=\
${LIBPIANO_DIR}/crypt.c \
- ${LIBPIANO_DIR}/piano.c \
- ${LIBPIANO_DIR}/xml.c
+ ${LIBPIANO_DIR}/piano.c
LIBPIANO_HDR=\
${LIBPIANO_DIR}/config.h \
${LIBPIANO_DIR}/crypt_key_output.h \
- ${LIBPIANO_DIR}/xml.h \
${LIBPIANO_DIR}/crypt.h \
${LIBPIANO_DIR}/piano.h \
${LIBPIANO_DIR}/crypt_key_input.h \
@@ -62,13 +60,6 @@ LIBWAITRESS_OBJ=${LIBWAITRESS_SRC:.c=.o}
LIBWAITRESS_RELOBJ=${LIBWAITRESS_SRC:.c=.lo}
LIBWAITRESS_INCLUDE=${LIBWAITRESS_DIR}
-LIBEZXML_DIR=src/libezxml
-LIBEZXML_SRC=${LIBEZXML_DIR}/ezxml.c
-LIBEZXML_HDR=${LIBEZXML_DIR}/ezxml.h
-LIBEZXML_OBJ=${LIBEZXML_SRC:.c=.o}
-LIBEZXML_RELOBJ=${LIBEZXML_SRC:.c=.lo}
-LIBEZXML_INCLUDE=${LIBEZXML_DIR}
-
ifeq (${DISABLE_FAAD}, 1)
LIBFAAD_CFLAGS=
LIBFAAD_LDFLAGS=
@@ -88,51 +79,59 @@ endif
LIBGNUTLS_CFLAGS=
LIBGNUTLS_LDFLAGS=-lgnutls
+LIBGCRYPT_CFLAGS=
+LIBGCRYPT_LDFLAGS=-lgcrypt
+
+LIBJSONC_CFLAGS=$(shell pkg-config --cflags json)
+LIBJSONC_LDFLAGS=$(shell pkg-config --libs json)
+
# build pianobar
ifeq (${DYNLINK},1)
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} libpiano.so.0
@echo " LINK $@"
@${CC} -o $@ ${PIANOBAR_OBJ} ${LDFLAGS} -lao -lpthread -lm -L. -lpiano \
- ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS}
+ ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
+ ${LIBGCRYPT_LDFLAGS} ${LIBJSONC_LDFLAGS}
else
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} \
- ${LIBWAITRESS_HDR} ${LIBEZXML_OBJ} ${LIBEZXML_HDR}
+ ${LIBWAITRESS_HDR}
@echo " LINK $@"
@${CC} ${CFLAGS} ${LDFLAGS} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} \
- ${LIBWAITRESS_OBJ} ${LIBEZXML_OBJ} -lao -lpthread -lm \
- ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS} -o $@
+ ${LIBWAITRESS_OBJ} -lao -lpthread -lm \
+ ${LIBFAAD_LDFLAGS} ${LIBMAD_LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
+ ${LIBGCRYPT_LDFLAGS} ${LIBJSONC_LDFLAGS} -o $@
endif
# build shared and static libpiano
libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \
- ${LIBWAITRESS_HDR} ${LIBEZXML_RELOBJ} ${LIBEZXML_HDR} \
- ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} ${LIBEZXML_OBJ}
+ ${LIBWAITRESS_HDR} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ}
@echo " LINK $@"
- @${CC} -shared -Wl,-soname,libpiano.so.0 ${CFLAGS} ${LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
+ @${CC} -shared -Wl,-soname,libpiano.so.0 ${CFLAGS} ${LDFLAGS} \
+ ${LIBGNUTLS_LDFLAGS} ${LIBGCRYPT_LDFLAGS} \
-o libpiano.so.0.0.0 ${LIBPIANO_RELOBJ} \
- ${LIBWAITRESS_RELOBJ} ${LIBEZXML_RELOBJ}
+ ${LIBWAITRESS_RELOBJ}
@ln -s libpiano.so.0.0.0 libpiano.so.0
@ln -s libpiano.so.0 libpiano.so
@echo " AR libpiano.a"
- @${AR} rcs libpiano.a ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} ${LIBEZXML_OBJ}
+ @${AR} rcs libpiano.a ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ}
%.o: %.c
@echo " CC $<"
@${CC} ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
- -I ${LIBEZXML_INCLUDE} ${LIBFAAD_CFLAGS} \
- ${LIBMAD_CFLAGS} ${LIBGNUTLS_CFLAGS} -c -o $@ $<
+ ${LIBFAAD_CFLAGS} ${LIBMAD_CFLAGS} ${LIBGNUTLS_CFLAGS} \
+ ${LIBJSONC_CFLAGS} -c -o $@ $<
# create position independent code (for shared libraries)
%.lo: %.c
@echo " CC $< (PIC)"
@${CC} ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
- -I ${LIBEZXML_INCLUDE} -c -fPIC -o $@ $<
+ -c -fPIC -o $@ $<
clean:
@echo " CLEAN"
@${RM} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} ${LIBWAITRESS_OBJ}/test.o \
- ${LIBEZXML_OBJ} ${LIBPIANO_RELOBJ} ${LIBWAITRESS_RELOBJ} \
- ${LIBEZXML_RELOBJ} pianobar libpiano.so* libpiano.a waitress-test
+ ${LIBPIANO_RELOBJ} ${LIBWAITRESS_RELOBJ} pianobar libpiano.so* \
+ libpiano.a waitress-test
all: pianobar
diff --git a/src/libezxml/COPYING b/src/libezxml/COPYING
deleted file mode 100644
index 80e4e88..0000000
--- a/src/libezxml/COPYING
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 2004-2006 Aaron Voisine <aaron@voisine.org>
-
-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/libezxml/ezxml.c b/src/libezxml/ezxml.c
deleted file mode 100644
index 0e1dd5d..0000000
--- a/src/libezxml/ezxml.c
+++ /dev/null
@@ -1,718 +0,0 @@
-/* ezxml.c
- *
- * Copyright 2004-2006 Aaron Voisine <aaron@voisine.org>
- *
- * 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.
- */
-
-#ifndef __FreeBSD__
-#define _BSD_SOURCE /* required by strdup() */
-#define _DARWIN_C_SOURCE /* required by strdup() on OS X */
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "ezxml.h"
-
-#define EZXML_WS "\t\r\n " // whitespace
-#define EZXML_ERRL 128 // maximum error string length
-
-typedef struct ezxml_root *ezxml_root_t;
-struct ezxml_root { // additional data for the root tag
- struct ezxml xml; // is a super-struct built on top of ezxml struct
- ezxml_t cur; // current xml tree insertion point
- char *m; // original xml string
- size_t len; // length of allocated memory for mmap, -1 for malloc
- char *u; // UTF-8 conversion of string if original was UTF-16
- char *s; // start of work area
- char *e; // end of work area
- char **ent; // general entities (ampersand sequences)
- char ***attr; // default attributes
- char ***pi; // processing instructions
- short standalone; // non-zero if <?xml standalone="yes"?>
- char err[EZXML_ERRL]; // error string
-};
-
-char *EZXML_NIL[] = { NULL }; // empty, null terminated array of strings
-
-// sets a flag for the given tag and returns the tag
-static ezxml_t ezxml_set_flag(ezxml_t xml, short flag) {
- if (xml) xml->flags |= flag;
- return xml;
-}
-
-// inserts an existing tag into an ezxml structure
-static ezxml_t ezxml_insert(ezxml_t xml, ezxml_t dest, size_t off)
-{
- ezxml_t cur, prev, head;
-
- xml->next = xml->sibling = xml->ordered = NULL;
- xml->off = off;
- xml->parent = dest;
-
- if ((head = dest->child)) { // already have sub tags
- if (head->off <= off) { // not first subtag
- for (cur = head; cur->ordered && cur->ordered->off <= off;
- cur = cur->ordered);
- xml->ordered = cur->ordered;
- cur->ordered = xml;
- }
- else { // first subtag
- xml->ordered = head;
- dest->child = xml;
- }
-
- for (cur = head, prev = NULL; cur && strcmp(cur->name, xml->name);
- prev = cur, cur = cur->sibling); // find tag type
- if (cur && cur->off <= off) { // not first of type
- while (cur->next && cur->next->off <= off) cur = cur->next;
- xml->next = cur->next;
- cur->next = xml;
- }
- else { // first tag of this type
- if (prev && cur) prev->sibling = cur->sibling; // remove old first
- xml->next = cur; // old first tag is now next
- for (cur = head, prev = NULL; cur && cur->off <= off;
- prev = cur, cur = cur->sibling); // new sibling insert point
- xml->sibling = cur;
- if (prev) prev->sibling = xml;
- }
- }
- else dest->child = xml; // only sub tag
-
- return xml;
-}
-
-// Adds a child tag. off is the offset of the child tag relative to the start
-// of the parent tag's character content. Returns the child tag.
-static ezxml_t ezxml_add_child(ezxml_t xml, char *name, size_t off)
-{
- ezxml_t child;
-
- if (! xml) return NULL;
- child = (ezxml_t)memset(malloc(sizeof(struct ezxml)), '\0',
- sizeof(struct ezxml));
- child->name = (char *)name;
- child->attr = EZXML_NIL;
- child->txt = "";
-
- return ezxml_insert(child, xml, off);
-}
-
-// returns the first child tag with the given name or NULL if not found
-ezxml_t ezxml_child(ezxml_t xml, const char *name)
-{
- xml = (xml) ? xml->child : NULL;
- while (xml && strcmp(name, xml->name)) xml = xml->sibling;
- return xml;
-}
-
-// returns a new empty ezxml structure with the given root tag name
-static ezxml_t ezxml_new(char *name)
-{
- static char *ent[] = { "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
- "apos;", "&#39;", "amp;", "&#38;", NULL };
- ezxml_root_t root = (ezxml_root_t)memset(malloc(sizeof(struct ezxml_root)),
- '\0', sizeof(struct ezxml_root));
- root->xml.name = (char *)name;
- root->cur = &root->xml;
- strcpy(root->err, root->xml.txt = "");
- root->ent = memcpy(malloc(sizeof(ent)), ent, sizeof(ent));
- root->attr = root->pi = (char ***)(root->xml.attr = EZXML_NIL);
- return &root->xml;
-}
-
-// returns the Nth tag with the same name in the same subsection or NULL if not
-// found
-ezxml_t ezxml_idx(ezxml_t xml, int idx)
-{
- for (; xml && idx; idx--) xml = xml->next;
- return xml;
-}
-
-// returns the value of the requested tag attribute or NULL if not found
-const char *ezxml_attr(ezxml_t xml, const char *attr)
-{
- int i = 0, j = 1;
- ezxml_root_t root = (ezxml_root_t)xml;
-
- if (! xml || ! xml->attr) return NULL;
- while (xml->attr[i] && strcmp(attr, xml->attr[i])) i += 2;
- if (xml->attr[i]) return xml->attr[i + 1]; // found attribute
-
- while (root->xml.parent) root = (ezxml_root_t)root->xml.parent; // root tag
- for (i = 0; root->attr[i] && strcmp(xml->name, root->attr[i][0]); i++);
- if (! root->attr[i]) return NULL; // no matching default attributes
- while (root->attr[i][j] && strcmp(attr, root->attr[i][j])) j += 3;
- return (root->attr[i][j]) ? root->attr[i][j + 1] : NULL; // found default
-}
-
-// same as ezxml_get but takes an already initialized va_list
-static ezxml_t ezxml_vget(ezxml_t xml, va_list ap)
-{
- char *name = va_arg(ap, char *);
- int idx = -1;
-
- if (name && *name) {
- idx = va_arg(ap, int);
- xml = ezxml_child(xml, name);
- }
- return (idx < 0) ? xml : ezxml_vget(ezxml_idx(xml, idx), ap);
-}
-
-// Traverses the xml tree to retrieve a specific subtag. Takes a variable
-// length list of tag names and indexes. The argument list must be terminated
-// by either an index of -1 or an empty string tag name. Example:
-// title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1);
-// This retrieves the title of the 3rd book on the 1st shelf of library.
-// Returns NULL if not found.
-ezxml_t ezxml_get(ezxml_t xml, ...)
-{
- va_list ap;
- ezxml_t r;
-
- va_start(ap, xml);
- r = ezxml_vget(xml, ap);
- va_end(ap);
- return r;
-}
-
-// set an error string and return root
-static ezxml_t ezxml_err(ezxml_root_t root, char *s, const char *err, ...)
-{
- va_list ap;
- int line = 1;
- char *t, fmt[EZXML_ERRL];
-
- for (t = root->s; t < s; t++) if (*t == '\n') line++;
- snprintf(fmt, EZXML_ERRL, "[error near line %d]: %s", line, err);
-
- va_start(ap, err);
- vsnprintf(root->err, EZXML_ERRL, fmt, ap);
- va_end(ap);
-
- return &root->xml;
-}
-
-// Recursively decodes entity and character references and normalizes new lines
-// ent is a null terminated array of alternating entity names and values. set t
-// to '&' for general entity decoding, '%' for parameter entity decoding, 'c'
-// for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
-// attribute normalization. Returns s, or if the decoded string is longer than
-// s, returns a malloced string that must be freed.
-static char *ezxml_decode(char *s, char **ent, char t)
-{
- char *e, *r = s, *m = s;
- long b, c, d, l;
-
- for (; *s; s++) { // normalize line endings
- while (*s == '\r') {
- *(s++) = '\n';
- if (*s == '\n') memmove(s, (s + 1), strlen(s));
- }
- }
-
- for (s = r; ; ) {
- while (*s && *s != '&' && (*s != '%' || t != '%') && !isspace(*s)) s++;
-
- if (! *s) break;
- else if (t != 'c' && ! strncmp(s, "&#", 2)) { // character reference
- if (s[2] == 'x') c = strtol(s + 3, &e, 16); // base 16
- else c = strtol(s + 2, &e, 10); // base 10
- if (! c || *e != ';') { s++; continue; } // not a character ref
-
- if (c < 0x80) *(s++) = c; // US-ASCII subset
- else { // multi-byte UTF-8 sequence
- for (b = 0, d = c; d; d /= 2) b++; // number of bits in c
- b = (b - 2) / 5; // number of bytes in payload
- *(s++) = (0xFF << (7 - b)) | (c >> (6 * b)); // head
- while (b) *(s++) = 0x80 | ((c >> (6 * --b)) & 0x3F); // payload
- }
-
- memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
- }
- else if ((*s == '&' && (t == '&' || t == ' ' || t == '*')) ||
- (*s == '%' && t == '%')) { // entity reference
- for (b = 0; ent[b] && strncmp(s + 1, ent[b], strlen(ent[b]));
- b += 2); // find entity in entity list
-
- if (ent[b++]) { // found a match
- if ((c = strlen(ent[b])) - 1 > (e = strchr(s, ';')) - s) {
- l = (d = (s - r)) + c + strlen(e); // new length
- r = (r == m) ? strcpy(malloc(l), r) : realloc(r, l);
- e = strchr((s = r + d), ';'); // fix up pointers
- }
-
- memmove(s + c, e + 1, strlen(e)); // shift rest of string
- strncpy(s, ent[b], c); // copy in replacement text
- }
- else s++; // not a known entity
- }
- else if ((t == ' ' || t == '*') && isspace(*s)) *(s++) = ' ';
- else s++; // no decoding needed
- }
-
- if (t == '*') { // normalize spaces for non-cdata attributes
- for (s = r; *s; s++) {
- if ((l = strspn(s, " "))) memmove(s, s + l, strlen(s + l) + 1);
- while (*s && *s != ' ') s++;
- }
- if (--s >= r && *s == ' ') *s = '\0'; // trim any trailing space
- }
- return r;
-}
-
-// called when parser finds start of new tag
-static void ezxml_open_tag(ezxml_root_t root, char *name, char **attr)
-{
- ezxml_t xml = root->cur;
-
- if (xml->name) xml = ezxml_add_child(xml, name, strlen(xml->txt));
- else xml->name = name; // first open tag
-
- xml->attr = attr;
- root->cur = xml; // update tag insertion point
-}
-
-// called when parser finds character content between open and closing tag
-static void ezxml_char_content(ezxml_root_t root, char *s, size_t len, char t)
-{
- ezxml_t xml = root->cur;
- char *m = s;
- size_t l;
-
- if (! xml || ! xml->name || ! len) return; // sanity check
-
- s[len] = '\0'; // null terminate text (calling functions anticipate this)
- len = strlen(s = ezxml_decode(s, root->ent, t)) + 1;
-
- if (! *(xml->txt)) xml->txt = s; // initial character content
- else { // allocate our own memory and make a copy
- xml->txt = (xml->flags & EZXML_TXTM) // allocate some space
- ? realloc(xml->txt, (l = strlen(xml->txt)) + len)
- : strcpy(malloc((l = strlen(xml->txt)) + len), xml->txt);
- strcpy(xml->txt + l, s); // add new char content
- if (s != m) free(s); // free s if it was malloced by ezxml_decode()
- }
-
- if (xml->txt != m) ezxml_set_flag(xml, EZXML_TXTM);
-}
-
-// called when parser finds closing tag
-static ezxml_t ezxml_close_tag(ezxml_root_t root, char *name, char *s)
-{
- if (! root->cur || ! root->cur->name || strcmp(name, root->cur->name))
- return ezxml_err(root, s, "unexpected closing tag </%s>", name);
-
- root->cur = root->cur->parent;
- return NULL;
-}
-
-// checks for circular entity references, returns non-zero if no circular
-// references are found, zero otherwise
-static int ezxml_ent_ok(char *name, char *s, char **ent)
-{
- int i;
-
- for (; ; s++) {
- while (*s && *s != '&') s++; // find next entity reference
- if (! *s) return 1;
- if (! strncmp(s + 1, name, strlen(name))) return 0; // circular ref.
- for (i = 0; ent[i] && strncmp(ent[i], s + 1, strlen(ent[i])); i += 2);
- if (ent[i] && ! ezxml_ent_ok(name, ent[i + 1], ent)) return 0;
- }
-}
-
-// called when the parser finds a processing instruction
-static void ezxml_proc_inst(ezxml_root_t root, char *s, size_t len)
-{
- int i = 0, j = 1;
- char *target = s;
-
- s[len] = '\0'; // null terminate instruction
- if (*(s += strcspn(s, EZXML_WS))) {
- *s = '\0'; // null terminate target
- s += strspn(s + 1, EZXML_WS) + 1; // skip whitespace after target
- }
-
- if (! strcmp(target, "xml")) { // <?xml ... ?>
- if ((s = strstr(s, "standalone")) && ! strncmp(s + strspn(s + 10,
- EZXML_WS "='\"") + 10, "yes", 3)) root->standalone = 1;
- return;
- }
-
- if (! root->pi[0]) *(root->pi = malloc(sizeof(char **))) = NULL; //first pi
-
- while (root->pi[i] && strcmp(target, root->pi[i][0])) i++; // find target
- if (! root->pi[i]) { // new target
- root->pi = realloc(root->pi, sizeof(char **) * (i + 2));
- root->pi[i] = malloc(sizeof(char *) * 3);
- root->pi[i][0] = target;
- root->pi[i][1] = (char *)(root->pi[i + 1] = NULL); // terminate pi list
- root->pi[i][2] = strdup(""); // empty document position list
- }
-
- while (root->pi[i][j]) j++; // find end of instruction list for this target
- root->pi[i] = realloc(root->pi[i], sizeof(char *) * (j + 3));
- root->pi[i][j + 2] = realloc(root->pi[i][j + 1], j + 1);
- strcpy(root->pi[i][j + 2] + j - 1, (root->xml.name) ? ">" : "<");
- root->pi[i][j + 1] = NULL; // null terminate pi list for this target
- root->pi[i][j] = s; // set instruction
-}
-
-// called when the parser finds an internal doctype subset
-static short ezxml_internal_dtd(ezxml_root_t root, char *s, size_t len)
-{
- char q, *c, *t, *n = NULL, *v, **ent, **pe;
- int i, j;
-
- pe = memcpy(malloc(sizeof(EZXML_NIL)), EZXML_NIL, sizeof(EZXML_NIL));
-
- for (s[len] = '\0'; s; ) {
- while (*s && *s != '<' && *s != '%') s++; // find next declaration
-
- if (! *s) break;
- else if (! strncmp(s, "<!ENTITY", 8)) { // parse entity definitions
- c = s += strspn(s + 8, EZXML_WS) + 8; // skip white space separator
- n = s + strspn(s, EZXML_WS "%"); // find name
- *(s = n + strcspn(n, EZXML_WS)) = ';'; // append ; to name
-
- v = s + strspn(s + 1, EZXML_WS) + 1; // find value
- if ((q = *(v++)) != '"' && q != '\'') { // skip externals
- s = strchr(s, '>');
- continue;
- }
-
- for (i = 0, ent = (*c == '%') ? pe : root->ent; ent[i]; i++);
- ent = realloc(ent, (i + 3) * sizeof(char *)); // space for next ent
- if (*c == '%') pe = ent;
- else root->ent = ent;
-
- *(++s) = '\0'; // null terminate name
- if ((s = strchr(v, q))) *(s++) = '\0'; // null terminate value
- ent[i + 1] = ezxml_decode(v, pe, '%'); // set value
- ent[i + 2] = NULL; // null terminate entity list
- if (! ezxml_ent_ok(n, ent[i + 1], ent)) { // circular reference
- if (ent[i + 1] != v) free(ent[i + 1]);
- ezxml_err(root, v, "circular entity declaration &%s", n);
- break;
- }
- else ent[i] = n; // set entity name
- }
- else if (! strncmp(s, "<!ATTLIST", 9)) { // parse default attributes
- t = s + strspn(s + 9, EZXML_WS) + 9; // skip whitespace separator
- if (! *t) { ezxml_err(root, t, "unclosed <!ATTLIST"); break; }
- if (*(s = t + strcspn(t, EZXML_WS ">")) == '>') continue;
- else *s = '\0'; // null terminate tag name
- for (i = 0; root->attr[i] && strcmp(n, root->attr[i][0]); i++);
-
- ++s; // ansi cpr
- while (*(n = s + strspn(s, EZXML_WS)) && *n != '>') {
- if (*(s = n + strcspn(n, EZXML_WS))) *s = '\0'; // attr name
- else { ezxml_err(root, t, "malformed <!ATTLIST"); break; }
-
- s += strspn(s + 1, EZXML_WS) + 1; // find next token
- c = (strncmp(s, "CDATA", 5)) ? "*" : " "; // is it cdata?
- if (! strncmp(s, "NOTATION", 8))
- s += strspn(s + 8, EZXML_WS) + 8;
- s = (*s == '(') ? strchr(s, ')') : s + strcspn(s, EZXML_WS);
- if (! s) { ezxml_err(root, t, "malformed <!ATTLIST"); break; }
-
- s += strspn(s, EZXML_WS ")"); // skip white space separator
- if (! strncmp(s, "#FIXED", 6))
- s += strspn(s + 6, EZXML_WS) + 6;
- if (*s == '#') { // no default value
- s += strcspn(s, EZXML_WS ">") - 1;
- if (*c == ' ') continue; // cdata is default, nothing to do
- v = NULL;
- }
- else if ((*s == '"' || *s == '\'') && // default value
- (s = strchr(v = s + 1, *s))) *s = '\0';
- else { ezxml_err(root, t, "malformed <!ATTLIST"); break; }
-
- if (! root->attr[i]) { // new tag name
- root->attr = (! i) ? malloc(2 * sizeof(char **))
- : realloc(root->attr,
- (i + 2) * sizeof(char **));
- root->attr[i] = malloc(2 * sizeof(char *));
- root->attr[i][0] = t; // set tag name
- root->attr[i][1] = (char *)(root->attr[i + 1] = NULL);
- }
-
- for (j = 1; root->attr[i][j]; j += 3); // find end of list
- root->attr[i] = realloc(root->attr[i],
- (j + 4) * sizeof(char *));
-
- root->attr[i][j + 3] = NULL; // null terminate list
- root->attr[i][j + 2] = c; // is it cdata?
- root->attr[i][j + 1] = (v) ? ezxml_decode(v, root->ent, *c)
- : NULL;
- root->attr[i][j] = n; // attribute name
- ++s;
- }
- }
- else if (! strncmp(s, "<!--", 4)) s = strstr(s + 4, "-->"); // comments
- else if (! strncmp(s, "<?", 2)) { // processing instructions
- if ((s = strstr(c = s + 2, "?>")))
- ezxml_proc_inst(root, c, s++ - c);
- }
- else if (*s == '<') s = strchr(s, '>'); // skip other declarations
- else if (*(s++) == '%' && ! root->standalone) break;
- }
-
- free(pe);
- return ! *root->err;
-}
-
-// Converts a UTF-16 string to UTF-8. Returns a new string that must be freed
-// or NULL if no conversion was needed.
-static char *ezxml_str2utf8(char **s, size_t *len)
-{
- char *u;
- size_t l = 0, sl, max = *len;
- long c, d;
- int b, be = (**s == '\xFE') ? 1 : (**s == '\xFF') ? 0 : -1;
-
- if (be == -1) return NULL; // not UTF-16
-
- u = malloc(max);
- for (sl = 2; sl < *len - 1; sl += 2) {
- c = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF) //UTF-16BE
- : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF); //UTF-16LE
- if (c >= 0xD800 && c <= 0xDFFF && (sl += 2) < *len - 1) { // high-half
- d = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF)
- : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF);
- c = (((c & 0x3FF) << 10) | (d & 0x3FF)) + 0x10000;
- }
-
- while (l + 6 > max) u = realloc(u, max += EZXML_BUFSIZE);
- if (c < 0x80) u[l++] = c; // US-ASCII subset
- else { // multi-byte UTF-8 sequence
- for (b = 0, d = c; d; d /= 2) b++; // bits in c
- b = (b - 2) / 5; // bytes in payload
- u[l++] = (0xFF << (7 - b)) | (c >> (6 * b)); // head
- while (b) u[l++] = 0x80 | ((c >> (6 * --b)) & 0x3F); // payload
- }
- }
- return *s = realloc(u, *len = l);
-}
-
-// frees a tag attribute list
-static void ezxml_free_attr(char **attr) {
- int i = 0;
- char *m;
-
- if (! attr || attr == EZXML_NIL) return; // nothing to free
- while (attr[i]) i += 2; // find end of attribute list
- m = attr[i + 1]; // list of which names and values are malloced
- for (i = 0; m[i]; i++) {
- if (m[i] & EZXML_NAMEM) free(attr[i * 2]);
- if (m[i] & EZXML_TXTM) free(attr[(i * 2) + 1]);
- }
- free(m);
- free(attr);
-}
-
-// parse the given xml string and return an ezxml structure
-ezxml_t ezxml_parse_str(char *s, size_t len)
-{
- ezxml_root_t root = (ezxml_root_t)ezxml_new(NULL);
- char q, e, *d, **attr, **a = NULL; // initialize a to avoid compile warning
- int l, i, j;
-
- root->m = s;
- if (! len) return ezxml_err(root, NULL, "root tag missing");
- root->u = ezxml_str2utf8(&s, &len); // convert utf-16 to utf-8
- root->e = (root->s = s) + len; // record start and end of work area
-
- e = s[len - 1]; // save end char
- s[len - 1] = '\0'; // turn end char into null terminator
-
- while (*s && *s != '<') s++; // find first tag
- if (! *s) return ezxml_err(root, s, "root tag missing");
-
- for (; ; ) {
- attr = (char **)EZXML_NIL;
- d = ++s;
-
- if (isalpha(*s) || *s == '_' || *s == ':' || *s < '\0') { // new tag
- if (! root->cur)
- return ezxml_err(root, d, "markup outside of root element");
-
- s += strcspn(s, EZXML_WS "/>");
- while (isspace(*s)) *(s++) = '\0'; // null terminate tag name
-
- if (*s && *s != '/' && *s != '>') // find tag in default attr list
- for (i = 0; (a = root->attr[i]) && strcmp(a[0], d); i++);
-
- for (l = 0; *s && *s != '/' && *s != '>'; l += 2) { // new attrib
- attr = (l) ? realloc(attr, (l + 4) * sizeof(char *))
- : malloc(4 * sizeof(char *)); // allocate space
- attr[l + 3] = (l) ? realloc(attr[l + 1], (l / 2) + 2)
- : malloc(2); // mem for list of maloced vals
- strcpy(attr[l + 3] + (l / 2), " "); // value is not malloced
- attr[l + 2] = NULL; // null terminate list
- attr[l + 1] = ""; // temporary attribute value
- attr[l] = s; // set attribute name
-
- s += strcspn(s, EZXML_WS "=/>");
- if (*s == '=' || isspace(*s)) {
- *(s++) = '\0'; // null terminate tag attribute name
- q = *(s += strspn(s, EZXML_WS "="));
- if (q == '"' || q == '\'') { // attribute value
- attr[l + 1] = ++s;
- while (*s && *s != q) s++;
- if (*s) *(s++) = '\0'; // null terminate attribute val
- else {
- ezxml_free_attr(attr);
- return ezxml_err(root, d, "missing %c", q);
- }
-
- for (j = 1; a && a[j] && strcmp(a[j], attr[l]); j +=3);
- attr[l + 1] = ezxml_decode(attr[l + 1], root->ent, (a
- && a[j]) ? *a[j + 2] : ' ');
- if (attr[l + 1] < d || attr[l + 1] > s)
- attr[l + 3][l / 2] = EZXML_TXTM; // value malloced
- }
- }
- while (isspace(*s)) s++;
- }
-
- if (*s == '/') { // self closing tag
- *(s++) = '\0';
- if ((*s && *s != '>') || (! *s && e != '>')) {
- if (l) ezxml_free_attr(attr);
- return ezxml_err(root, d, "missing >");
- }
- ezxml_open_tag(root, d, attr);
- ezxml_close_tag(root, d, s);
- }
- else if ((q = *s) == '>' || (! *s && e == '>')) { // open tag
- *s = '\0'; // temporarily null terminate tag name
- ezxml_open_tag(root, d, attr);
- *s = q;
- }
- else {
- if (l) ezxml_free_attr(attr);
- return ezxml_err(root, d, "missing >");
- }
- }
- else if (*s == '/') { // close tag
- s += strcspn(d = s + 1, EZXML_WS ">") + 1;
- if (! (q = *s) && e != '>') return ezxml_err(root, d, "missing >");
- *s = '\0'; // temporarily null terminate tag name
- if (ezxml_close_tag(root, d, s)) return &root->xml;
- if (isspace(*s = q)) s += strspn(s, EZXML_WS);
- }
- else if (! strncmp(s, "!--", 3)) { // xml comment
- if (! (s = strstr(s + 3, "--")) || (*(s += 2) != '>' && *s) ||
- (! *s && e != '>')) return ezxml_err(root, d, "unclosed <!--");
- }
- else if (! strncmp(s, "![CDATA[", 8)) { // cdata
- if ((s = strstr(s, "]]>")))
- ezxml_char_content(root, d + 8, (s += 2) - d - 10, 'c');
- else return ezxml_err(root, d, "unclosed <![CDATA[");
- }
- else if (! strncmp(s, "!DOCTYPE", 8)) { // dtd
- for (l = 0; *s && ((! l && *s != '>') || (l && (*s != ']' ||
- *(s + strspn(s + 1, EZXML_WS) + 1) != '>')));
- l = (*s == '[') ? 1 : l) s += strcspn(s + 1, "[]>") + 1;
- if (! *s && e != '>')
- return ezxml_err(root, d, "unclosed <!DOCTYPE");
- d = (l) ? strchr(d, '[') + 1 : d;
- if (l && ! ezxml_internal_dtd(root, d, s++ - d)) return &root->xml;
- }
- else if (*s == '?') { // <?...?> processing instructions
- do { s = strchr(s, '?'); } while (s && *(++s) && *s != '>');
- if (! s || (! *s && e != '>'))
- return ezxml_err(root, d, "unclosed <?");
- else ezxml_proc_inst(root, d + 1, s - d - 2);
- }
- else return ezxml_err(root, d, "unexpected <");
-
- if (! s || ! *s) break;
- *s = '\0';
- d = ++s;
- if (*s && *s != '<') { // tag character content
- while (*s && *s != '<') s++;
- if (*s) ezxml_char_content(root, d, s - d, '&');
- else break;
- }
- else if (! *s) break;
- }
-
- if (! root->cur) return &root->xml;
- else if (! root->cur->name) return ezxml_err(root, d, "root tag missing");
- else return ezxml_err(root, d, "unclosed tag <%s>", root->cur->name);
-}
-
-// free the memory allocated for the ezxml structure
-void ezxml_free(ezxml_t xml)
-{
- ezxml_root_t root = (ezxml_root_t)xml;
- int i, j;
- char **a, *s;
-
- if (! xml) return;
- ezxml_free(xml->child);
- ezxml_free(xml->ordered);
-
- if (! xml->parent) { // free root tag allocations
- for (i = 10; root->ent[i]; i += 2) // 0 - 9 are default entites (<>&"')
- if ((s = root->ent[i + 1]) < root->s || s > root->e) free(s);
- free(root->ent); // free list of general entities
-
- for (i = 0; (a = root->attr[i]); i++) {
- for (j = 1; a[j++]; j += 2) // free malloced attribute values
- if (a[j] && (a[j] < root->s || a[j] > root->e)) free(a[j]);
- free(a);
- }
- if (root->attr[0]) free(root->attr); // free default attribute list
-
- for (i = 0; root->pi[i]; i++) {
- for (j = 1; root->pi[i][j]; j++);
- free(root->pi[i][j + 1]);
- free(root->pi[i]);
- }
- if (root->pi[0]) free(root->pi); // free processing instructions
-
- if (root->len == -1) free(root->m); // malloced xml data
- if (root->u) free(root->u); // utf8 conversion
- }
-
- ezxml_free_attr(xml->attr); // tag attributes
- if ((xml->flags & EZXML_TXTM)) free(xml->txt); // character content
- if ((xml->flags & EZXML_NAMEM)) free(xml->name); // tag name
- free(xml);
-}
-
-// return parser error message or empty string if none
-const char *ezxml_error(ezxml_t xml)
-{
- while (xml && xml->parent) xml = xml->parent; // find root tag
- return (xml) ? ((ezxml_root_t)xml)->err : "";
-}
-
diff --git a/src/libezxml/ezxml.h b/src/libezxml/ezxml.h
deleted file mode 100644
index 0ee2513..0000000
--- a/src/libezxml/ezxml.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* ezxml.h
- *
- * Copyright 2004-2006 Aaron Voisine <aaron@voisine.org>
- *
- * 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.
- */
-
-#ifndef _EZXML_H
-#define _EZXML_H
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <fcntl.h>
-
-#define EZXML_BUFSIZE 1024 // size of internal memory buffers
-#define EZXML_NAMEM 0x80 // name is malloced
-#define EZXML_TXTM 0x40 // txt is malloced
-#define EZXML_DUP 0x20 // attribute name and value are strduped
-
-typedef struct ezxml *ezxml_t;
-struct ezxml {
- char *name; // tag name
- char **attr; // tag attributes { name, value, name, value, ... NULL }
- char *txt; // tag character content, empty string if none
- size_t off; // tag offset from start of parent tag character content
- ezxml_t next; // next tag with same name in this section at this depth
- ezxml_t sibling; // next tag with different name in same section and depth
- ezxml_t ordered; // next tag, same section and depth, in original order
- ezxml_t child; // head of sub tag list, NULL if none
- ezxml_t parent; // parent tag, NULL if current tag is root tag
- short flags; // additional information
-};
-
-// Given a string of xml data and its length, parses it and creates an ezxml
-// structure. For efficiency, modifies the data by adding null terminators
-// and decoding ampersand sequences. If you don't want this, copy the data and
-// pass in the copy. Returns NULL on failure.
-ezxml_t ezxml_parse_str(char *s, size_t len);
-
-// returns the first child tag (one level deeper) with the given name or NULL
-// if not found
-ezxml_t ezxml_child(ezxml_t xml, const char *name);
-
-// returns the next tag of the same name in the same section and depth or NULL
-// if not found
-#define ezxml_next(xml) ((xml) ? xml->next : NULL)
-
-// Returns the Nth tag with the same name in the same section at the same depth
-// or NULL if not found. An index of 0 returns the tag given.
-ezxml_t ezxml_idx(ezxml_t xml, int idx);
-
-// returns the name of the given tag
-#define ezxml_name(xml) ((xml) ? xml->name : NULL)
-
-// returns the given tag's character content or empty string if none
-#define ezxml_txt(xml) ((xml) ? xml->txt : "")
-
-// returns the value of the requested tag attribute, or NULL if not found
-const char *ezxml_attr(ezxml_t xml, const char *attr);
-
-// Traverses the ezxml sturcture to retrieve a specific subtag. Takes a
-// variable length list of tag names and indexes. The argument list must be
-// terminated by either an index of -1 or an empty string tag name. Example:
-// title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1);
-// This retrieves the title of the 3rd book on the 1st shelf of library.
-// Returns NULL if not found.
-ezxml_t ezxml_get(ezxml_t xml, ...);
-
-// frees the memory allocated for an ezxml structure
-void ezxml_free(ezxml_t xml);
-
-// returns parser error message or empty string if none
-const char *ezxml_error(ezxml_t xml);
-
-#endif // _EZXML_H
diff --git a/src/libpiano/crypt.c b/src/libpiano/crypt.c
index 3f98f42..6dafcca 100644
--- a/src/libpiano/crypt.c
+++ b/src/libpiano/crypt.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2008-2011
+Copyright (c) 2008-2012
Lars-Dominik Braun <lars@6xq.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,23 +22,13 @@ THE SOFTWARE.
*/
#include <string.h>
+#include <assert.h>
+#include <gcrypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
-#include <arpa/inet.h>
#include "crypt.h"
-#include "crypt_key_output.h"
-#include "crypt_key_input.h"
-#include "piano_private.h"
-
-#define byteswap32(x) ((((x) >> 24) & 0x000000ff) | \
- (((x) >> 8) & 0x0000ff00) | \
- (((x) << 8) & 0x00ff0000) | \
- (((x) << 24) & 0xff000000))
-
-#define hostToBigEndian32(x) htonl(x)
-#define bigToHostEndian32(x) ntohl(x)
/* decrypt hex-encoded, blowfish-crypted string: decode 2 hex-encoded blocks,
* decrypt, byteswap
@@ -46,162 +36,69 @@ THE SOFTWARE.
* @param decrypted string length (without trailing NUL)
* @return decrypted string or NULL
*/
-#define INITIAL_SHIFT 28
-#define SHIFT_DEC 4
-char *PianoDecryptString (const char * const s, size_t * const retSize) {
- const unsigned char *strInput = (const unsigned char *) s;
- /* hex-decode => strlen/2 + null-byte */
- uint32_t *iDecrypt;
- size_t decryptedSize;
- char *strDecrypted;
- unsigned char shift = INITIAL_SHIFT, intsDecoded = 0, j;
- /* blowfish blocks, 32-bit */
- uint32_t f, l, r, lrExchange;
-
- decryptedSize = strlen ((const char *) strInput)/2;
- if ((iDecrypt = calloc (decryptedSize/sizeof (*iDecrypt)+1,
- sizeof (*iDecrypt))) == NULL) {
- return NULL;
- }
- strDecrypted = (char *) iDecrypt;
-
- while (*strInput != '\0') {
- /* hex-decode string */
- if (*strInput >= '0' && *strInput <= '9') {
- *iDecrypt |= (*strInput & 0x0f) << shift;
- } else if (*strInput >= 'a' && *strInput <= 'f') {
- /* 0xa (hex) = 10 (decimal), 'a' & 0x0f == 1 => +9 */
- *iDecrypt |= ((*strInput+9) & 0x0f) << shift;
- }
- if (shift > 0) {
- shift -= SHIFT_DEC;
- } else {
- shift = INITIAL_SHIFT;
- /* initialize next dword */
- *(++iDecrypt) = 0;
- ++intsDecoded;
- }
-
- /* two 32-bit hex-decoded boxes available => blowfish decrypt */
- if (intsDecoded == 2) {
- l = *(iDecrypt-2);
- r = *(iDecrypt-1);
-
- for (j = in_key_n + 1; j > 1; --j) {
- l ^= in_key_p [j];
-
- f = in_key_s [0][(l >> 24) & 0xff] +
- in_key_s [1][(l >> 16) & 0xff];
- f ^= in_key_s [2][(l >> 8) & 0xff];
- f += in_key_s [3][l & 0xff];
- r ^= f;
- /* exchange l & r */
- lrExchange = l;
- l = r;
- r = lrExchange;
- }
- /* exchange l & r */
- lrExchange = l;
- l = r;
- r = lrExchange;
- r ^= in_key_p [1];
- l ^= in_key_p [0];
-
- *(iDecrypt-2) = bigToHostEndian32 (l);
- *(iDecrypt-1) = bigToHostEndian32 (r);
-
- intsDecoded = 0;
- }
- ++strInput;
+char *PianoDecryptString (const char * const input, size_t * const retSize) {
+ size_t inputLen = strlen (input);
+ gcry_error_t gret;
+ unsigned char *output;
+ size_t outputLen = inputLen/2;
+
+ assert (inputLen%2 == 0);
+
+ output = calloc (outputLen+1, sizeof (*output));
+ /* hex decode */
+ for (size_t i = 0; i < outputLen; i++) {
+ char hex[3];
+ memcpy (hex, &input[i*2], 2);
+ hex[2] = '\0';
+ output[i] = strtol (hex, NULL, 16);
}
- if (retSize != NULL) {
- *retSize = decryptedSize;
+ gcry_cipher_hd_t h;
+ gcry_cipher_open (&h, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 0);
+ gcry_cipher_setkey (h, (unsigned char *) "R=U!LH$O2B#", 11);
+ gret = gcry_cipher_decrypt (h, output, outputLen, NULL, 0);
+ if (gret) {
+ fprintf (stderr, "Failure: %s/%s\n", gcry_strsource (gret), gcry_strerror (gret));
+ return NULL;
}
- return strDecrypted;
+ gcry_cipher_close (h);
+ *retSize = outputLen;
+
+ return (char *) output;
}
-#undef INITIAL_SHIFT
-#undef SHIFT_DEC
/* blowfish-encrypt/hex-encode string
* @param encrypt this
* @return encrypted, hex-encoded string
*/
char *PianoEncryptString (const char *s) {
- const unsigned char *strInput = (const unsigned char *) s;
- const size_t strInputN = strlen ((const char *) strInput);
- /* num of 64-bit blocks, rounded to next block */
- size_t blockN = strInputN / 8 + 1;
- uint32_t *blockInput, *blockPtr;
- /* output string */
- unsigned char *strHex, *hexPtr;
- const char *hexmap = "0123456789abcdef";
-
- if ((blockInput = calloc (blockN*2, sizeof (*blockInput))) == NULL) {
+ unsigned char *paddedInput, *hexOutput;
+ size_t inputLen = strlen (s);
+ /* blowfish expects two 32 bit blocks */
+ size_t paddedInputLen = (inputLen % 8 == 0) ? inputLen : inputLen + (8-inputLen%8);
+ gcry_error_t gret;
+
+ paddedInput = calloc (paddedInputLen+1, sizeof (*paddedInput));
+ memcpy (paddedInput, s, inputLen);
+
+ gcry_cipher_hd_t h;
+ gcry_cipher_open (&h, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 0);
+ gcry_cipher_setkey (h, (unsigned char *) "6#26FRL$ZWD", 11);
+ gret = gcry_cipher_encrypt (h, paddedInput, paddedInputLen, NULL, 0);
+ if (gret) {
+ fprintf (stderr, "Failure: %s/%s\n", gcry_strsource (gret), gcry_strerror (gret));
return NULL;
}
- blockPtr = blockInput;
- if ((strHex = calloc (blockN*8*2 + 1, sizeof (*strHex))) == NULL) {
- return NULL;
- }
- hexPtr = strHex;
-
- memcpy (blockInput, strInput, strInputN);
-
- while (blockN > 0) {
- /* encryption blocks */
- uint32_t f, lrExchange;
- register uint32_t l, r;
- int i;
-
- l = hostToBigEndian32 (*blockPtr);
- r = hostToBigEndian32 (*(blockPtr+1));
-
- /* encrypt blocks */
- for (i = 0; i < out_key_n; i++) {
- l ^= out_key_p[i];
-
- f = out_key_s[0][(l >> 24) & 0xff] +
- out_key_s[1][(l >> 16) & 0xff];
- f ^= out_key_s[2][(l >> 8) & 0xff];
- f += out_key_s[3][l & 0xff];
- r ^= f;
- /* exchange l & r */
- lrExchange = l;
- l = r;
- r = lrExchange;
- }
- /* exchange l & r again */
- lrExchange = l;
- l = r;
- r = lrExchange;
- r ^= out_key_p [out_key_n];
- l ^= out_key_p [out_key_n+1];
-
- /* swap bytes again... */
- l = byteswap32 (l);
- r = byteswap32 (r);
-
- /* hex-encode encrypted blocks */
- for (i = 0; i < 4; i++) {
- *hexPtr++ = hexmap[(l & 0xf0) >> 4];
- *hexPtr++ = hexmap[l & 0x0f];
- l >>= 8;
- }
- for (i = 0; i < 4; i++) {
- *hexPtr++ = hexmap[(r & 0xf0) >> 4];
- *hexPtr++ = hexmap[r & 0x0f];
- r >>= 8;
- }
-
- /* two! 32-bit blocks encrypted (l & r) */
- blockPtr += 2;
- --blockN;
+ hexOutput = calloc (paddedInputLen*2+1, sizeof (*hexOutput));
+ for (size_t i = 0; i < paddedInputLen; i++) {
+ snprintf ((char * restrict) &hexOutput[i*2], 3, "%02x", paddedInput[i]);
}
- free (blockInput);
+ gcry_cipher_close (h);
+ free (paddedInput);
- return (char *) strHex;
+ return (char *) hexOutput;
}
+
diff --git a/src/libpiano/piano.c b/src/libpiano/piano.c
index 342e4ec..0ac96b6 100644
--- a/src/libpiano/piano.c
+++ b/src/libpiano/piano.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2008-2011
+Copyright (c) 2008-2012
Lars-Dominik Braun <lars@6xq.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -32,20 +32,17 @@ THE SOFTWARE.
#include <time.h>
#include <assert.h>
#include <stdint.h>
+#include <json.h>
/* needed for urlencode */
#include <waitress.h>
#include "piano_private.h"
#include "piano.h"
-#include "xml.h"
#include "crypt.h"
#include "config.h"
-#define PIANO_PROTOCOL_VERSION "33"
-#define PIANO_RPC_HOST "www.pandora.com"
-#define PIANO_RPC_PORT "80"
-#define PIANO_RPC_PATH "/radio/xmlrpc/v" PIANO_PROTOCOL_VERSION "?"
+#define PIANO_RPC_PATH "/services/json/?"
#define PIANO_SEND_BUFFER_SIZE 10000
/* initialize piano handle
@@ -54,10 +51,6 @@ THE SOFTWARE.
*/
void PianoInit (PianoHandle_t *ph) {
memset (ph, 0, sizeof (*ph));
-
- /* route-id seems to be random. we're using time anyway... */
- snprintf (ph->routeId, sizeof (ph->routeId), "%07luP",
- (unsigned long) time (NULL) % 10000000);
}
/* destroy artist linked list
@@ -125,10 +118,8 @@ void PianoDestroyPlaylist (PianoSong_t *playlist) {
free (curSong->artist);
free (curSong->musicId);
free (curSong->title);
- free (curSong->userSeed);
free (curSong->stationId);
free (curSong->album);
- free (curSong->artistMusicId);
free (curSong->feedbackId);
free (curSong->seedId);
free (curSong->detailUrl);
@@ -164,7 +155,6 @@ static void PianoDestroyGenres (PianoGenre_t *genres) {
/* destroy user information
*/
static void PianoDestroyUserInfo (PianoUserInfo_t *user) {
- free (user->webAuthToken);
free (user->authToken);
free (user->listenerId);
}
@@ -185,6 +175,7 @@ void PianoDestroy (PianoHandle_t *ph) {
curGenreCat = curGenreCat->next;
free (lastGenreCat);
}
+ free (ph->partnerAuthToken);
memset (ph, 0, sizeof (*ph));
}
@@ -228,9 +219,12 @@ static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
*/
PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoRequestType_t type) {
- char xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
+ const char *jsonSendBuf;
+ const char *method = NULL;
+ json_object *j = json_object_new_object ();
/* corrected timestamp */
time_t timestamp = time (NULL) - ph->timeOffset;
+ bool encrypted = true;
assert (ph != NULL);
assert (req != NULL);
@@ -248,66 +242,59 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
switch (logindata->step) {
case 0:
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>misc.sync</methodName>"
- "<params></params></methodCall>");
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=sync", ph->routeId);
+ encrypted = false;
+ req->secure = true;
+
+ json_object_object_add (j, "username",
+ json_object_new_string ("android"));
+ json_object_object_add (j, "password",
+ json_object_new_string ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"));
+ json_object_object_add (j, "deviceModel",
+ json_object_new_string ("android-generic"));
+ json_object_object_add (j, "version",
+ json_object_new_string ("5"));
+ json_object_object_add (j, "includeUrls",
+ json_object_new_boolean (true));
+ snprintf (req->urlPath, sizeof (req->urlPath),
+ PIANO_RPC_PATH "method=auth.partnerLogin");
break;
case 1: {
- char *xmlencodedPassword = NULL;
+ char *urlencAuthToken;
req->secure = true;
- /* username == email address does not contain &,<,>," */
- if ((xmlencodedPassword =
- PianoXmlEncodeString (logindata->password)) ==
- NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
+ json_object_object_add (j, "loginType",
+ json_object_new_string ("user"));
+ json_object_object_add (j, "username",
+ json_object_new_string (logindata->user));
+ json_object_object_add (j, "password",
+ json_object_new_string (logindata->password));
+ json_object_object_add (j, "partnerAuthToken",
+ json_object_new_string (ph->partnerAuthToken));
+ json_object_object_add (j, "syncTime",
+ json_object_new_int (timestamp));
+
+ urlencAuthToken = WaitressUrlEncode (ph->partnerAuthToken);
+ assert (urlencAuthToken != NULL);
+ snprintf (req->urlPath, sizeof (req->urlPath),
+ PIANO_RPC_PATH "method=auth.userLogin&"
+ "auth_token=%s&partner_id=%i", urlencAuthToken,
+ ph->partnerId);
+ free (urlencAuthToken);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>listener.authenticateListener</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* user */
- "<param><value><string>%s</string></value></param>"
- /* password */
- "<param><value><string>%s</string></value></param>"
- /* vendor */
- "<param><value><string>html5tuner</string></value></param>"
- "<param><value><string/></value></param>"
- "<param><value><string/></value></param>"
- "<param><value><string>HTML5</string></value></param>"
- "<param><value><boolean>1</boolean></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- logindata->user, xmlencodedPassword);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=authenticateListener", ph->routeId);
-
- free (xmlencodedPassword);
break;
}
}
break;
}
- case PIANO_REQUEST_GET_STATIONS:
+ case PIANO_REQUEST_GET_STATIONS: {
/* get stations, user must be authenticated */
assert (ph->user.listenerId != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.getStations</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getStations", ph->routeId,
- ph->user.listenerId);
+ method = "user.getStationList";
break;
+ }
case PIANO_REQUEST_GET_PLAYLIST: {
/* get playlist for specified station */
@@ -318,33 +305,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData->station->id != NULL);
assert (reqData->format != PIANO_AF_UNKNOWN);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.getFragment</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* total listening time */
- "<param><value><string>0</string></value></param>"
- /* time since last session */
- "<param><value><string></string></value></param>"
- /* tracking code */
- "<param><value><string></string></value></param>"
- /* audio format */
- "<param><value><string>%s</string></value></param>"
- /* delta listening time */
- "<param><value><string>0</string></value></param>"
- /* listening timestamp */
- "<param><value><string>0</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=0"
- "&arg3=&arg4=&arg5=%s&arg6=0&arg7=0", ph->routeId,
- ph->user.listenerId, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
+ req->secure = true;
+
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+
+ method = "station.getPlaylist";
break;
}
@@ -353,65 +319,31 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoRequestDataAddFeedback_t *reqData = req->data;
assert (reqData != NULL);
- assert (reqData->stationId != NULL);
+ assert (reqData->trackToken != NULL);
assert (reqData->rating != PIANO_RATE_NONE);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addFeedback</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* track token */
- "<param><value><string>%s</string></value></param>"
- /* positive */
- "<param><value><boolean>%i</boolean></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->stationId, reqData->trackToken,
- (reqData->rating == PIANO_RATE_LOVE) ? 1 : 0);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
- "&arg3=%s",
- ph->routeId, ph->user.listenerId, reqData->stationId,
- reqData->trackToken,
- (reqData->rating == PIANO_RATE_LOVE) ? "true" : "false");
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (reqData->trackToken));
+ json_object_object_add (j, "isPositive",
+ json_object_new_boolean (reqData->rating == PIANO_RATE_LOVE));
+
+ method = "station.addFeedback";
break;
}
case PIANO_REQUEST_RENAME_STATION: {
- /* rename stations */
PianoRequestDataRenameStation_t *reqData = req->data;
- char *urlencodedNewName, *xmlencodedNewName;
assert (reqData != NULL);
assert (reqData->station != NULL);
assert (reqData->newName != NULL);
- if ((xmlencodedNewName = PianoXmlEncodeString (reqData->newName)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedNewName = WaitressUrlEncode (reqData->newName);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setStationName</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* new name */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- xmlencodedNewName);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->station->id,
- urlencodedNewName);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+ json_object_object_add (j, "stationName",
+ json_object_new_string (reqData->newName));
- free (urlencodedNewName);
- free (xmlencodedNewName);
+ method = "station.renameStation";
break;
}
@@ -420,50 +352,26 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoStation_t *station = req->data;
assert (station != NULL);
+ assert (station->id != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.removeStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (station->id));
+
+ method = "station.deleteStation";
break;
}
case PIANO_REQUEST_SEARCH: {
/* search for artist/song title */
PianoRequestDataSearch_t *reqData = req->data;
- char *xmlencodedSearchStr, *urlencodedSearchStr;
assert (reqData != NULL);
assert (reqData->searchStr != NULL);
- if ((xmlencodedSearchStr = PianoXmlEncodeString (reqData->searchStr)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedSearchStr = WaitressUrlEncode (reqData->searchStr);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>music.search</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* search string */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, xmlencodedSearchStr);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=search&arg1=%s", ph->routeId,
- ph->user.listenerId, urlencodedSearchStr);
+ json_object_object_add (j, "searchText",
+ json_object_new_string (reqData->searchStr));
- free (urlencodedSearchStr);
- free (xmlencodedSearchStr);
+ method = "music.search";
break;
}
@@ -474,23 +382,11 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->id != NULL);
- assert (reqData->type != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s%s</string></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->type, reqData->id);
+ json_object_object_add (j, "musicToken",
+ json_object_new_string (reqData->id));
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createStation&arg1=%s%s&arg2=", ph->routeId,
- ph->user.listenerId, reqData->type, reqData->id);
+ method = "station.createStation";
break;
}
@@ -502,20 +398,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData->station != NULL);
assert (reqData->musicId != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addSeed</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id, reqData->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
- ph->user.listenerId, reqData->station->id, reqData->musicId);
+ json_object_object_add (j, "musicToken",
+ json_object_new_string (reqData->musicId));
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+
+ method = "station.addMusic";
break;
}
@@ -525,87 +413,40 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>listener.addTiredSong</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* key */
- "<param><value><string>%s</string></value></param>"
- /* user seed */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addTiredSong&arg1=%s&arg2=%s&arg3=%s",
- ph->routeId, ph->user.listenerId,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "user.sleepSong";
break;
}
case PIANO_REQUEST_SET_QUICKMIX: {
/* select stations included in quickmix (see useQuickMix flag of
* PianoStation_t) */
- char valueBuf[1000], urlArgBuf[1000];
PianoStation_t *curStation = ph->stations;
+ json_object *a = json_object_new_array ();
- memset (urlArgBuf, 0, sizeof (urlArgBuf));
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setQuickMix</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* quick mix type */
- "<param><value><string>RANDOM</string></value></param>"
- "<param><value><array><data>", (unsigned long) timestamp,
- ph->user.authToken);
while (curStation != NULL) {
/* quick mix can't contain itself */
- if (!curStation->useQuickMix || curStation->isQuickMix) {
- curStation = curStation->next;
- continue;
+ if (curStation->useQuickMix && !curStation->isQuickMix) {
+ json_object_array_add (a,
+ json_object_new_string (curStation->id));
}
- /* append to xml doc */
- snprintf (valueBuf, sizeof (valueBuf),
- "<value><string>%s</string></value>", curStation->id);
- strncat (xmlSendBuf, valueBuf, sizeof (xmlSendBuf) -
- strlen (xmlSendBuf) - 1);
- /* append to url arg */
- strncat (urlArgBuf, curStation->id, sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
+
curStation = curStation->next;
- /* if not last item: append "," */
- if (curStation != NULL) {
- strncat (urlArgBuf, "%2C", sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
- }
}
- strncat (xmlSendBuf,
- "</data></array></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- "</params></methodCall>",
- sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s&arg3=&arg4=",
- ph->routeId, ph->user.listenerId, urlArgBuf);
+ json_object_object_add (j, "quickMixStationIds", a);
+
+ method = "user.setQuickMix";
break;
}
- case PIANO_REQUEST_GET_GENRE_STATIONS:
+ case PIANO_REQUEST_GET_GENRE_STATIONS: {
/* receive list of pandora's genre stations */
- xmlSendBuf[0] = '\0';
- snprintf (req->urlPath, sizeof (req->urlPath), "/xml/genre?r=%lu",
- (unsigned long) timestamp);
+ method = "station.getGenreStations";
break;
+ }
case PIANO_REQUEST_TRANSFORM_STATION: {
/* transform shared station into private */
@@ -613,18 +454,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (station != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.transformShared</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (station->id));
+
+ method = "station.transformSharedStation";
break;
}
@@ -635,26 +468,15 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.narrative</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->song->stationId,
- reqData->song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=narrative&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->song->stationId,
- reqData->song->musicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (reqData->song->trackToken));
+
+ method = "track.explainTrack";
break;
}
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
+#if 0
/* find similar artists */
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
@@ -679,6 +501,7 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
"rid=%s&lid=%s&method=getSeedSuggestions&arg1=%s&arg2=%u",
ph->routeId, ph->user.listenerId, reqData->musicId, reqData->max);
+#endif
break;
}
@@ -688,21 +511,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->stationId, song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createBookmark&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, song->stationId,
- song->musicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "bookmark.addSongBookmark";
break;
}
@@ -712,18 +524,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createArtistBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->artistMusicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createArtistBookmark&arg1=%s",
- ph->routeId, ph->user.listenerId, song->artistMusicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "bookmark.addArtistBookmark";
break;
}
@@ -734,18 +538,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->station != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.getStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getStation&arg1=%s",
- ph->routeId, ph->user.listenerId, reqData->station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+ json_object_object_add (j, "includeExtendedAttributes",
+ json_object_new_boolean (true));
+
+ method = "station.getStation";
break;
}
@@ -754,18 +552,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.deleteFeedback</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* feedback id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->feedbackId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=deleteFeedback&arg1=%s",
- ph->routeId, ph->user.listenerId, song->feedbackId);
+ json_object_object_add (j, "feedbackId",
+ json_object_new_string (song->feedbackId));
+
+ method = "station.deleteFeedback";
break;
}
@@ -787,18 +577,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (seedId != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.deleteSeed</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* seed id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, seedId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=deleteSeed&arg1=%s",
- ph->routeId, ph->user.listenerId, seedId);
+ json_object_object_add (j, "seedId",
+ json_object_new_string (seedId));
+
+ method = "station.deleteMusic";
break;
}
@@ -866,24 +648,89 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
}
}
- if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
+ /* standard parameter */
+ if (method != NULL) {
+ char *urlencAuthToken;
+
+ assert (ph->user.authToken != NULL);
+
+ urlencAuthToken = WaitressUrlEncode (ph->user.authToken);
+ assert (urlencAuthToken != NULL);
+
+ snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
+ "method=%s&auth_token=%s&partner_id=%i&user_id=%s", method,
+ urlencAuthToken, ph->partnerId, ph->user.listenerId);
+
+ free (urlencAuthToken);
+
+ json_object_object_add (j, "userAuthToken",
+ json_object_new_string (ph->user.authToken));
+ json_object_object_add (j, "syncTime",
+ json_object_new_int (timestamp));
+ }
+
+ /* json to string */
+ jsonSendBuf = json_object_to_json_string (j);
+ if (encrypted) {
+ if ((req->postData = PianoEncryptString (jsonSendBuf)) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+ } else {
+ req->postData = strdup (jsonSendBuf);
}
+ json_object_put (j);
return PIANO_RET_OK;
}
+static char *PianoJsonStrdup (json_object *j, const char *key) {
+ return strdup (json_object_get_string (json_object_object_get (j, key)));
+}
+
+static void PianoJsonParseStation (json_object *j, PianoStation_t *s) {
+ s->name = PianoJsonStrdup (j, "stationName");
+ s->id = PianoJsonStrdup (j, "stationToken");
+ s->isCreator = !json_object_get_boolean (json_object_object_get (j,
+ "isShared"));
+ s->isQuickMix = json_object_get_boolean (json_object_object_get (j,
+ "isQuickMix"));
+}
+
/* parse xml response and update data structures/return new data structure
* @param piano handle
* @param initialized request (expects responseData to be a NUL-terminated
* string)
*/
PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
- PianoReturn_t ret = PIANO_RET_ERR;
+ PianoReturn_t ret = PIANO_RET_OK;
+ json_object *j, *result, *status;
assert (ph != NULL);
assert (req != NULL);
+ j = json_tokener_parse (req->responseData);
+
+ status = json_object_object_get (j, "stat");
+ if (status == NULL) {
+ json_object_put (j);
+ return PIANO_RET_INVALID_RESPONSE;
+ }
+
+ /* error handling */
+ if (strcmp (json_object_get_string (status), "ok") != 0) {
+ json_object *code = json_object_object_get (j, "code");
+ if (code == NULL) {
+ ret = PIANO_RET_INVALID_RESPONSE;
+ } else {
+ ret = json_object_get_int (code)+PIANO_RET_OFFSET;
+ }
+
+ json_object_put (j);
+ return ret;
+ }
+
+ result = json_object_object_get (j, "result");
+
switch (req->type) {
case PIANO_REQUEST_LOGIN: {
/* authenticate user */
@@ -894,29 +741,28 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
switch (reqData->step) {
case 0: {
- char *cryptedTimestamp = NULL;
-
- assert (req->responseData != NULL);
-
- /* abusing parseNarrative; has same xml structure */
- ret = PianoXmlParseNarrative (req->responseData, &cryptedTimestamp);
- if (ret == PIANO_RET_OK && cryptedTimestamp != NULL) {
- unsigned long timestamp = 0;
- const time_t realTimestamp = time (NULL);
- char *decryptedTimestamp = NULL;
- size_t decryptedSize;
-
- ret = PIANO_RET_ERR;
- if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp,
- &decryptedSize)) != NULL && decryptedSize > 4) {
- /* skip four bytes garbage(?) at beginning */
- timestamp = strtoul (decryptedTimestamp+4, NULL, 0);
- ph->timeOffset = realTimestamp - timestamp;
- ret = PIANO_RET_CONTINUE_REQUEST;
- }
- free (decryptedTimestamp);
+ /* decrypt timestamp */
+ const char *cryptedTimestamp = json_object_get_string (
+ json_object_object_get (result, "syncTime"));
+ unsigned long timestamp = 0;
+ const time_t realTimestamp = time (NULL);
+ char *decryptedTimestamp = NULL;
+ size_t decryptedSize;
+
+ ret = PIANO_RET_ERR;
+ if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp,
+ &decryptedSize)) != NULL && decryptedSize > 4) {
+ /* skip four bytes garbage(?) at beginning */
+ timestamp = strtoul (decryptedTimestamp+4, NULL, 0);
+ ph->timeOffset = realTimestamp - timestamp;
+ ret = PIANO_RET_CONTINUE_REQUEST;
}
- free (cryptedTimestamp);
+ free (decryptedTimestamp);
+ /* get auth token */
+ ph->partnerAuthToken = PianoJsonStrdup (result,
+ "partnerAuthToken");
+ ph->partnerId = json_object_get_int (
+ json_object_object_get (result, "partnerId"));
++reqData->step;
break;
}
@@ -927,48 +773,145 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
if (ph->user.listenerId != NULL) {
PianoDestroyUserInfo (&ph->user);
}
- ret = PianoXmlParseUserinfo (ph, req->responseData);
+ ph->user.listenerId = PianoJsonStrdup (result, "userId");
+ ph->user.authToken = PianoJsonStrdup (result,
+ "userAuthToken");
break;
}
break;
}
- case PIANO_REQUEST_GET_STATIONS:
+ case PIANO_REQUEST_GET_STATIONS: {
/* get stations */
assert (req->responseData != NULL);
-
- ret = PianoXmlParseStations (ph, req->responseData);
+
+ json_object *stations = json_object_object_get (result,
+ "stations"), *mix = NULL;
+
+ for (size_t i=0; i < json_object_array_length (stations); i++) {
+ PianoStation_t *tmpStation;
+ json_object *s = json_object_array_get_idx (stations, i);
+
+ if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ PianoJsonParseStation (s, tmpStation);
+
+ if (tmpStation->isQuickMix) {
+ /* fix flags on other stations later */
+ mix = json_object_object_get (s, "quickMixStationIds");
+ }
+
+ /* start new linked list or append */
+ if (ph->stations == NULL) {
+ ph->stations = tmpStation;
+ } else {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation->next != NULL) {
+ curStation = curStation->next;
+ }
+ curStation->next = tmpStation;
+ }
+ }
+
+ /* fix quickmix flags */
+ if (mix != NULL) {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation != NULL) {
+ for (size_t i = 0; i < json_object_array_length (mix); i++) {
+ json_object *id = json_object_array_get_idx (mix, i);
+ if (strcmp (json_object_get_string (id),
+ curStation->id) == 0) {
+ curStation->useQuickMix = true;
+ }
+ }
+ curStation = curStation->next;
+ }
+ }
break;
+ }
case PIANO_REQUEST_GET_PLAYLIST: {
/* get playlist, usually four songs */
PianoRequestDataGetPlaylist_t *reqData = req->data;
+ PianoSong_t *playlist = NULL;
assert (req->responseData != NULL);
assert (reqData != NULL);
- reqData->retPlaylist = NULL;
- ret = PianoXmlParsePlaylist (ph, req->responseData,
- &reqData->retPlaylist);
+ json_object *items = json_object_object_get (result, "items");
+ assert (items != NULL);
+
+ for (size_t i=0; i < json_object_array_length (items); i++) {
+ json_object *s = json_object_array_get_idx (items, i);
+ PianoSong_t *song;
+
+ if ((song = calloc (1, sizeof (*song))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ if (json_object_object_get (s, "artistName") == NULL) {
+ free (song);
+ continue;
+ }
+ song->audioUrl = strdup (json_object_get_string (json_object_object_get (json_object_object_get (json_object_object_get (s, "audioUrlMap"), "highQuality"), "audioUrl")));
+ song->artist = PianoJsonStrdup (s, "artistName");
+ song->album = PianoJsonStrdup (s, "albumName");
+ song->title = PianoJsonStrdup (s, "songName");
+ song->trackToken = PianoJsonStrdup (s, "trackToken");
+ song->stationId = PianoJsonStrdup (s, "stationId");
+ song->fileGain = json_object_get_double (
+ json_object_object_get (s, "trackGain"));
+ song->audioFormat = PIANO_AF_AACPLUS;
+ switch (json_object_get_int (json_object_object_get (s,
+ "songRating"))) {
+ case 1:
+ song->rating = PIANO_RATE_LOVE;
+ break;
+ }
+
+ /* begin linked list or append */
+ if (playlist == NULL) {
+ playlist = song;
+ } else {
+ PianoSong_t *curSong = playlist;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = song;
+ }
+ }
+
+ reqData->retPlaylist = playlist;
break;
}
- case PIANO_REQUEST_RATE_SONG:
+ case PIANO_REQUEST_RATE_SONG: {
/* love/ban song */
- assert (req->responseData != NULL);
-
- ret = PianoXmlParseSimple (req->responseData);
- if (ret == PIANO_RET_OK) {
- PianoRequestDataRateSong_t *reqData = req->data;
- reqData->song->rating = reqData->rating;
- }
+ PianoRequestDataRateSong_t *reqData = req->data;
+ reqData->song->rating = reqData->rating;
break;
+ }
case PIANO_REQUEST_ADD_FEEDBACK:
/* never ever use this directly, low-level call */
assert (0);
break;
+ case PIANO_REQUEST_RENAME_STATION: {
+ /* rename station and update PianoStation_t structure */
+ PianoRequestDataRenameStation_t *reqData = req->data;
+
+ assert (reqData != NULL);
+ assert (reqData->station != NULL);
+ assert (reqData->newName != NULL);
+
+ free (reqData->station->name);
+ reqData->station->name = strdup (reqData->newName);
+ break;
+ }
+
case PIANO_REQUEST_MOVE_SONG: {
/* move song to different station */
PianoRequestDataMoveSong_t *reqData = req->data;
@@ -977,107 +920,204 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (reqData != NULL);
assert (reqData->step < 2);
- ret = PianoXmlParseSimple (req->responseData);
- if (ret == PIANO_RET_OK && reqData->step == 0) {
+ if (reqData->step == 0) {
ret = PIANO_RET_CONTINUE_REQUEST;
++reqData->step;
}
break;
}
- case PIANO_REQUEST_RENAME_STATION:
- /* rename station and update PianoStation_t structure */
- assert (req->responseData != NULL);
-
- if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
- PianoRequestDataRenameStation_t *reqData = req->data;
+ case PIANO_REQUEST_DELETE_STATION: {
+ /* delete station from server and station list */
+ PianoStation_t *station = req->data;
- assert (reqData != NULL);
- assert (reqData->station != NULL);
- assert (reqData->newName != NULL);
+ assert (station != NULL);
- free (reqData->station->name);
- reqData->station->name = strdup (reqData->newName);
+ /* delete station from local station list */
+ PianoStation_t *curStation = ph->stations, *lastStation = NULL;
+ while (curStation != NULL) {
+ if (curStation == station) {
+ if (lastStation != NULL) {
+ lastStation->next = curStation->next;
+ } else {
+ /* first station in list */
+ ph->stations = curStation->next;
+ }
+ PianoDestroyStation (curStation);
+ free (curStation);
+ break;
+ }
+ lastStation = curStation;
+ curStation = curStation->next;
}
break;
+ }
+
+ case PIANO_REQUEST_SEARCH: {
+ /* search artist/song */
+ PianoRequestDataSearch_t *reqData = req->data;
+ PianoSearchResult_t *searchResult;
- case PIANO_REQUEST_DELETE_STATION:
- /* delete station from server and station list */
assert (req->responseData != NULL);
+ assert (reqData != NULL);
- if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
- PianoStation_t *station = req->data;
+ searchResult = &reqData->searchResult;
+ memset (searchResult, 0, sizeof (*searchResult));
- assert (station != NULL);
+ /* get artists */
+ json_object *artists = json_object_object_get (result, "artists");
+ if (artists != NULL) {
+ for (size_t i=0; i < json_object_array_length (artists); i++) {
+ json_object *a = json_object_array_get_idx (artists, i);
+ PianoArtist_t *artist;
- /* delete station from local station list */
- PianoStation_t *curStation = ph->stations, *lastStation = NULL;
- while (curStation != NULL) {
- if (curStation == station) {
- if (lastStation != NULL) {
- lastStation->next = curStation->next;
- } else {
- /* first station in list */
- ph->stations = curStation->next;
+ if ((artist = calloc (1, sizeof (*artist))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ artist->name = PianoJsonStrdup (a, "artistName");
+ artist->musicId = PianoJsonStrdup (a, "musicToken");
+
+ /* add result to linked list */
+ if (searchResult->artists == NULL) {
+ searchResult->artists = artist;
+ } else {
+ PianoArtist_t *curArtist = searchResult->artists;
+ while (curArtist->next != NULL) {
+ curArtist = curArtist->next;
}
- PianoDestroyStation (curStation);
- free (curStation);
- break;
+ curArtist->next = artist;
}
- lastStation = curStation;
- curStation = curStation->next;
}
}
- break;
- case PIANO_REQUEST_SEARCH: {
- /* search artist/song */
- PianoRequestDataSearch_t *reqData = req->data;
+ /* get songs */
+ json_object *songs = json_object_object_get (result, "songs");
+ if (songs != NULL) {
+ for (size_t i=0; i < json_object_array_length (songs); i++) {
+ json_object *s = json_object_array_get_idx (songs, i);
+ PianoSong_t *song;
- assert (req->responseData != NULL);
- assert (reqData != NULL);
+ if ((song = calloc (1, sizeof (*song))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- ret = PianoXmlParseSearch (req->responseData, &reqData->searchResult);
+ song->title = PianoJsonStrdup (s, "songName");
+ song->artist = PianoJsonStrdup (s, "artistName");
+ song->musicId = PianoJsonStrdup (s, "musicToken");
+
+ /* add result to linked list */
+ if (searchResult->songs == NULL) {
+ searchResult->songs = song;
+ } else {
+ PianoSong_t *curSong = searchResult->songs;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = song;
+ }
+ }
+ }
break;
}
case PIANO_REQUEST_CREATE_STATION: {
/* create station, insert new station into station list on success */
- assert (req->responseData != NULL);
+ PianoStation_t *tmpStation;
- ret = PianoXmlParseCreateStation (ph, req->responseData);
- break;
- }
-
- case PIANO_REQUEST_ADD_SEED: {
- /* add seed to station, updates station structure */
- PianoRequestDataAddSeed_t *reqData = req->data;
+ if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- assert (req->responseData != NULL);
- assert (reqData != NULL);
- assert (reqData->station != NULL);
+ PianoJsonParseStation (result, tmpStation);
- /* FIXME: update station data instead of replacing them */
- ret = PianoXmlParseAddSeed (ph, req->responseData, reqData->station);
+ /* start new linked list or append */
+ if (ph->stations == NULL) {
+ ph->stations = tmpStation;
+ } else {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation->next != NULL) {
+ curStation = curStation->next;
+ }
+ curStation->next = tmpStation;
+ }
break;
}
+ case PIANO_REQUEST_ADD_SEED:
case PIANO_REQUEST_ADD_TIRED_SONG:
case PIANO_REQUEST_SET_QUICKMIX:
case PIANO_REQUEST_BOOKMARK_SONG:
case PIANO_REQUEST_BOOKMARK_ARTIST:
case PIANO_REQUEST_DELETE_FEEDBACK:
- assert (req->responseData != NULL);
-
- ret = PianoXmlParseSimple (req->responseData);
+ case PIANO_REQUEST_DELETE_SEED:
+ /* response unused */
break;
- case PIANO_REQUEST_GET_GENRE_STATIONS:
+ case PIANO_REQUEST_GET_GENRE_STATIONS: {
/* get genre stations */
- assert (req->responseData != NULL);
+ json_object *categories = json_object_object_get (result, "categories");
+ if (categories != NULL) {
+ for (size_t i = 0; i < json_object_array_length (categories); i++) {
+ json_object *c = json_object_array_get_idx (categories, i);
+ PianoGenreCategory_t *tmpGenreCategory;
+
+ if ((tmpGenreCategory = calloc (1,
+ sizeof (*tmpGenreCategory))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- ret = PianoXmlParseGenreExplorer (ph, req->responseData);
+ tmpGenreCategory->name = PianoJsonStrdup (c,
+ "categoryName");
+
+ /* get genre subnodes */
+ json_object *stations = json_object_object_get (c,
+ "stations");
+ if (stations != NULL) {
+ for (size_t k = 0;
+ k < json_object_array_length (stations); k++) {
+ json_object *s =
+ json_object_array_get_idx (stations, k);
+ PianoGenre_t *tmpGenre;
+
+ if ((tmpGenre = calloc (1,
+ sizeof (*tmpGenre))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ /* get genre attributes */
+ tmpGenre->name = PianoJsonStrdup (s,
+ "stationName");
+ tmpGenre->musicId = PianoJsonStrdup (s,
+ "stationToken");
+
+ /* append station */
+ if (tmpGenreCategory->genres == NULL) {
+ tmpGenreCategory->genres = tmpGenre;
+ } else {
+ PianoGenre_t *curGenre =
+ tmpGenreCategory->genres;
+ while (curGenre->next != NULL) {
+ curGenre = curGenre->next;
+ }
+ curGenre->next = tmpGenre;
+ }
+ }
+ }
+ /* append category */
+ if (ph->genreStations == NULL) {
+ ph->genreStations = tmpGenreCategory;
+ } else {
+ PianoGenreCategory_t *curCat = ph->genreStations;
+ while (curCat->next != NULL) {
+ curCat = curCat->next;
+ }
+ curCat->next = tmpGenreCategory;
+ }
+ }
+ }
break;
+ }
case PIANO_REQUEST_TRANSFORM_STATION: {
/* transform shared station into private and update isCreator flag */
@@ -1086,27 +1126,51 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (req->responseData != NULL);
assert (station != NULL);
- /* though this call returns a bunch of "new" data only this one is
- * changed and important (at the moment) */
- if ((ret = PianoXmlParseTranformStation (req->responseData)) ==
- PIANO_RET_OK) {
- station->isCreator = 1;
- }
+ station->isCreator = 1;
break;
}
case PIANO_REQUEST_EXPLAIN: {
/* explain why song was selected */
PianoRequestDataExplain_t *reqData = req->data;
+ const size_t strSize = 1024;
+ size_t pos = 0;
- assert (req->responseData != NULL);
assert (reqData != NULL);
- ret = PianoXmlParseNarrative (req->responseData, &reqData->retExplain);
+ json_object *explanations = json_object_object_get (result,
+ "explanations");
+ if (explanations != NULL) {
+ reqData->retExplain = malloc (strSize *
+ sizeof (*reqData->retExplain));
+ strncpy (reqData->retExplain, "We're playing this track "
+ "because it features ", strSize);
+ pos = strlen (reqData->retExplain);
+ for (size_t i=0; i < json_object_array_length (explanations); i++) {
+ json_object *e = json_object_array_get_idx (explanations,
+ i);
+ const char *s = json_object_get_string (
+ json_object_object_get (e, "focusTraitName"));
+
+ strncpy (&reqData->retExplain[pos], s, strSize-pos-1);
+ pos += strlen (s);
+ if (i < json_object_array_length (explanations)-2) {
+ strncpy (&reqData->retExplain[pos], ", ", strSize-pos-1);
+ pos += 2;
+ } else if (i == json_object_array_length (explanations)-2) {
+ strncpy (&reqData->retExplain[pos], " and ", strSize-pos-1);
+ pos += 5;
+ } else {
+ strncpy (&reqData->retExplain[pos], ".", strSize-pos-1);
+ pos += 1;
+ }
+ }
+ }
break;
}
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
+#if 0
/* find similar artists */
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
@@ -1115,29 +1179,122 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
ret = PianoXmlParseSeedSuggestions (req->responseData,
&reqData->searchResult);
+#endif
break;
}
case PIANO_REQUEST_GET_STATION_INFO: {
/* get station information (seeds and feedback) */
PianoRequestDataGetStationInfo_t *reqData = req->data;
+ PianoStationInfo_t *info;
- assert (req->responseData != NULL);
assert (reqData != NULL);
- ret = PianoXmlParseGetStationInfo (req->responseData,
- &reqData->info);
- break;
- }
+ info = &reqData->info;
+ assert (info != NULL);
+
+ /* parse music seeds */
+ json_object *music = json_object_object_get (result, "music");
+ if (music != NULL) {
+ /* songs */
+ json_object *songs = json_object_object_get (music, "songs");
+ if (songs != NULL) {
+ for (size_t i = 0; i < json_object_array_length (songs); i++) {
+ json_object *s = json_object_array_get_idx (songs, i);
+ PianoSong_t *seedSong;
+
+ seedSong = calloc (1, sizeof (*seedSong));
+ if (seedSong == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- case PIANO_REQUEST_DELETE_SEED: {
- assert (req->responseData != NULL);
+ seedSong->title = PianoJsonStrdup (s, "songName");
+ seedSong->artist = PianoJsonStrdup (s, "artistName");
+ seedSong->seedId = PianoJsonStrdup (s, "seedId");
- /* dummy function, checks for errors only */
- ret = PianoXmlParseTranformStation (req->responseData);
+ if (info->songSeeds == NULL) {
+ info->songSeeds = seedSong;
+ } else {
+ PianoSong_t *curSong = info->songSeeds;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = seedSong;
+ }
+ }
+ }
+
+ /* artists */
+ json_object *artists = json_object_object_get (music,
+ "artists");
+ if (artists != NULL) {
+ for (size_t i = 0; i < json_object_array_length (artists); i++) {
+ json_object *a = json_object_array_get_idx (artists, i);
+ PianoArtist_t *seedArtist;
+
+ seedArtist = calloc (1, sizeof (*seedArtist));
+ if (seedArtist == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ seedArtist->name = PianoJsonStrdup (a, "artistName");
+ seedArtist->seedId = PianoJsonStrdup (a, "seedId");
+
+ if (info->artistSeeds == NULL) {
+ info->artistSeeds = seedArtist;
+ } else {
+ PianoArtist_t *curArtist = info->artistSeeds;
+ while (curArtist->next != NULL) {
+ curArtist = curArtist->next;
+ }
+ curArtist->next = seedArtist;
+ }
+ }
+ }
+ }
+
+ /* parse feedback */
+ json_object *feedback = json_object_object_get (result,
+ "feedback");
+ if (feedback != NULL) {
+ json_object_object_foreach (feedback, key, val) {
+ for (size_t i = 0; i < json_object_array_length (val); i++) {
+ json_object *s = json_object_array_get_idx (val, i);
+ PianoSong_t *feedbackSong;
+
+ feedbackSong = calloc (1, sizeof (*feedbackSong));
+ if (feedbackSong == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ feedbackSong->title = PianoJsonStrdup (s, "songName");
+ feedbackSong->artist = PianoJsonStrdup (s,
+ "artistName");
+ feedbackSong->feedbackId = PianoJsonStrdup (s,
+ "feedbackId");
+ feedbackSong->rating = json_object_get_boolean (
+ json_object_object_get (s, "isPositive")) ?
+ PIANO_RATE_LOVE : PIANO_RATE_BAN;
+
+
+ if (info->feedback == NULL) {
+ info->feedback = feedbackSong;
+ } else {
+ PianoSong_t *curSong = info->feedback;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = feedbackSong;
+ }
+ }
+ }
+ }
+ break;
}
}
+ json_object_put (j);
+
return ret;
}
@@ -1171,70 +1328,51 @@ const char *PianoErrorToStr (PianoReturn_t ret) {
return "Unknown.";
break;
- case PIANO_RET_XML_INVALID:
- return "Invalid XML.";
- break;
-
- case PIANO_RET_AUTH_TOKEN_INVALID:
- return "Invalid auth token.";
- break;
-
- case PIANO_RET_AUTH_USER_PASSWORD_INVALID:
- return "Username and/or password not correct.";
- break;
-
- case PIANO_RET_NOT_AUTHORIZED:
- return "Not authorized.";
- break;
-
- case PIANO_RET_PROTOCOL_INCOMPATIBLE:
- return "Protocol incompatible. Please upgrade " PACKAGE ".";
+ case PIANO_RET_INVALID_RESPONSE:
+ return "Invalid response.";
break;
- case PIANO_RET_READONLY_MODE:
- return "Request cannot be completed at this time, please try "
- "again later.";
- break;
-
- case PIANO_RET_STATION_CODE_INVALID:
- return "Station id is invalid.";
+ case PIANO_RET_CONTINUE_REQUEST:
+ /* never shown to the user */
+ assert (0);
+ return "Fix your program.";
break;
- case PIANO_RET_IP_REJECTED:
- return "Your ip address was rejected. Please setup a control "
- "proxy (see manpage).";
+ case PIANO_RET_OUT_OF_MEMORY:
+ return "Out of memory.";
break;
- case PIANO_RET_STATION_NONEXISTENT:
- return "Station does not exist.";
+ /* pandora error messages */
+ case PIANO_RET_P_INTERNAL:
+ return "Internal error.";
break;
- case PIANO_RET_OUT_OF_MEMORY:
- return "Out of memory.";
+ case PIANO_RET_P_CALL_NOT_ALLOWED:
+ return "Call not allowed.";
break;
- case PIANO_RET_OUT_OF_SYNC:
- return "Out of sync. Please correct your system's time.";
+ case PIANO_RET_P_INVALID_AUTH_TOKEN:
+ return "Invalid auth token.";
break;
- case PIANO_RET_PLAYLIST_END:
- return "Playlist end.";
+ case PIANO_RET_P_MAINTENANCE_MODE:
+ return "Maintenance mode.";
break;
- case PIANO_RET_QUICKMIX_NOT_PLAYABLE:
- return "Quickmix not playable.";
+ case PIANO_RET_P_MAX_STATIONS_REACHED:
+ return "Max number of stations reached.";
break;
- case PIANO_RET_REMOVING_TOO_MANY_SEEDS:
- return "Last seed cannot be removed.";
+ case PIANO_RET_P_READ_ONLY_MODE:
+ return "Read only mode. Try again later.";
break;
- case PIANO_RET_EXCESSIVE_ACTIVITY:
- return "Excessive activity.";
+ case PIANO_RET_P_STATION_DOES_NOT_EXIST:
+ return "Station does not exist.";
break;
- case PIANO_RET_DAILY_SKIP_LIMIT_REACHED:
- return "Daily skip limit reached.";
+ case PIANO_RET_P_INVALID_PARTNER_LOGIN:
+ return "Invalid partner login.";
break;
default:
diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h
index d582844..8a21d05 100644
--- a/src/libpiano/piano.h
+++ b/src/libpiano/piano.h
@@ -31,11 +31,13 @@ THE SOFTWARE.
* all strings _must_ be utf-8 encoded. i won't care, but pandora does. so
* be nice and check the encoding of your strings. thanks :) */
-#define PIANO_RPC_HOST "www.pandora.com"
-#define PIANO_RPC_PORT "80"
+/* Pandora API documentation is available at
+ * http://pan-do-ra-api.wikia.com
+ */
+
+#define PIANO_RPC_HOST "tuner.pandora.com"
typedef struct PianoUserInfo {
- char *webAuthToken;
char *listenerId;
char *authToken;
} PianoUserInfo_t;
@@ -66,10 +68,8 @@ typedef enum {
typedef struct PianoSong {
char *artist;
- char *artistMusicId;
char *stationId;
char *album;
- char *userSeed;
char *audioUrl;
char *coverArt;
char *musicId;
@@ -106,12 +106,13 @@ typedef struct PianoGenreCategory {
} PianoGenreCategory_t;
typedef struct PianoHandle {
- char routeId[9];
PianoUserInfo_t user;
/* linked lists */
PianoStation_t *stations;
PianoGenreCategory_t *genreStations;
int timeOffset;
+ char *partnerAuthToken;
+ unsigned int partnerId;
} PianoHandle_t;
typedef struct PianoSearchResult {
@@ -235,26 +236,58 @@ typedef struct {
PianoStation_t *station;
} PianoRequestDataDeleteSeed_t;
+/* pandora error code offset */
+#define PIANO_RET_OFFSET 1024
typedef enum {
PIANO_RET_ERR = 0,
PIANO_RET_OK = 1,
- PIANO_RET_XML_INVALID = 2,
- PIANO_RET_AUTH_TOKEN_INVALID = 3,
- PIANO_RET_AUTH_USER_PASSWORD_INVALID = 4,
- PIANO_RET_CONTINUE_REQUEST = 5,
- PIANO_RET_NOT_AUTHORIZED = 6,
- PIANO_RET_PROTOCOL_INCOMPATIBLE = 7,
- PIANO_RET_READONLY_MODE = 8,
- PIANO_RET_STATION_CODE_INVALID = 9,
- PIANO_RET_IP_REJECTED = 10,
- PIANO_RET_STATION_NONEXISTENT = 11,
- PIANO_RET_OUT_OF_MEMORY = 12,
- PIANO_RET_OUT_OF_SYNC = 13,
- PIANO_RET_PLAYLIST_END = 14,
- PIANO_RET_QUICKMIX_NOT_PLAYABLE = 15,
- PIANO_RET_REMOVING_TOO_MANY_SEEDS = 16,
- PIANO_RET_EXCESSIVE_ACTIVITY = 17,
- PIANO_RET_DAILY_SKIP_LIMIT_REACHED = 18,
+ PIANO_RET_INVALID_RESPONSE = 2,
+ PIANO_RET_CONTINUE_REQUEST = 3,
+ PIANO_RET_OUT_OF_MEMORY = 4,
+
+ PIANO_RET_P_INTERNAL = PIANO_RET_OFFSET+0,
+ PIANO_RET_P_API_VERSION_NOT_SUPPORTED = PIANO_RET_OFFSET+11,
+ PIANO_RET_P_BIRTH_YEAR_INVALID = PIANO_RET_OFFSET+1025,
+ PIANO_RET_P_BIRTH_YEAR_TOO_YOUNG = PIANO_RET_OFFSET+1026,
+ PIANO_RET_P_CALL_NOT_ALLOWED = PIANO_RET_OFFSET+1008,
+ PIANO_RET_P_CERTIFICATE_REQUIRED = PIANO_RET_OFFSET+7,
+ PIANO_RET_P_COMPLIMENTARY_PERIOD_ALREADY_IN_USE = PIANO_RET_OFFSET+1007,
+ PIANO_RET_P_DAILY_TRIAL_LIMIT_REACHED = PIANO_RET_OFFSET+1035,
+ PIANO_RET_P_DEVICE_ALREADY_ASSOCIATED_TO_ACCOUNT = PIANO_RET_OFFSET+1014,
+ PIANO_RET_P_DEVICE_DISABLED = PIANO_RET_OFFSET+1034,
+ PIANO_RET_P_DEVICE_MODEL_INVALID = PIANO_RET_OFFSET+1023,
+ PIANO_RET_P_DEVICE_NOT_FOUND = PIANO_RET_OFFSET+1009,
+ PIANO_RET_P_EXPLICIT_PIN_INCORRECT = PIANO_RET_OFFSET+1018,
+ PIANO_RET_P_EXPLICIT_PIN_MALFORMED = PIANO_RET_OFFSET+1020,
+ PIANO_RET_P_INSUFFICIENT_CONNECTIVITY = PIANO_RET_OFFSET+13,
+ PIANO_RET_P_INVALID_AUTH_TOKEN = PIANO_RET_OFFSET+1001,
+ PIANO_RET_P_INVALID_COUNTRY_CODE = PIANO_RET_OFFSET+1027,
+ PIANO_RET_P_INVALID_GENDER = PIANO_RET_OFFSET+1027,
+ PIANO_RET_P_INVALID_PARTNER_LOGIN = PIANO_RET_OFFSET+1002,
+ PIANO_RET_P_INVALID_PASSWORD = PIANO_RET_OFFSET+1012,
+ PIANO_RET_P_INVALID_SPONSOR = PIANO_RET_OFFSET+1036,
+ PIANO_RET_P_INVALID_USERNAME = PIANO_RET_OFFSET+1011,
+ PIANO_RET_P_LICENSING_RESTRICTIONS = PIANO_RET_OFFSET+12,
+ PIANO_RET_P_MAINTENANCE_MODE = PIANO_RET_OFFSET+1,
+ PIANO_RET_P_MAX_STATIONS_REACHED = PIANO_RET_OFFSET+1005,
+ PIANO_RET_P_PARAMETER_MISSING = PIANO_RET_OFFSET+9,
+ PIANO_RET_P_PARAMETER_TYPE_MISMATCH = PIANO_RET_OFFSET+8,
+ PIANO_RET_P_PARAMETER_VALUE_INVALID = PIANO_RET_OFFSET+10,
+ PIANO_RET_P_PARTNER_NOT_AUTHORIZED = PIANO_RET_OFFSET+1010,
+ PIANO_RET_P_READ_ONLY_MODE = PIANO_RET_OFFSET+1000,
+ PIANO_RET_P_SECURE_PROTOCOL_REQUIRED = PIANO_RET_OFFSET+6,
+ PIANO_RET_P_STATION_DOES_NOT_EXIST = PIANO_RET_OFFSET+1006,
+ PIANO_RET_P_UPGRADE_DEVICE_MODEL_INVALID = PIANO_RET_OFFSET+1015,
+ PIANO_RET_P_URL_PARAM_MISSING_AUTH_TOKEN = PIANO_RET_OFFSET+3,
+ PIANO_RET_P_URL_PARAM_MISSING_METHOD = PIANO_RET_OFFSET+2,
+ PIANO_RET_P_URL_PARAM_MISSING_PARTNER_ID = PIANO_RET_OFFSET+4,
+ PIANO_RET_P_URL_PARAM_MISSING_USER_ID = PIANO_RET_OFFSET+5,
+ PIANO_RET_P_USERNAME_ALREADY_EXISTS = PIANO_RET_OFFSET+1013,
+ PIANO_RET_P_USER_ALREADY_USED_TRIAL = PIANO_RET_OFFSET+1037,
+ PIANO_RET_P_USER_NOT_ACTIVE = PIANO_RET_OFFSET+1003,
+ PIANO_RET_P_USER_NOT_AUTHORIZED = PIANO_RET_OFFSET+1004,
+ PIANO_RET_P_ZIP_CODE_INVALID = PIANO_RET_OFFSET+1024,
+
} PianoReturn_t;
void PianoInit (PianoHandle_t *);
diff --git a/src/libpiano/xml.c b/src/libpiano/xml.c
deleted file mode 100644
index d194963..0000000
--- a/src/libpiano/xml.c
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
-Copyright (c) 2008-2011
- Lars-Dominik Braun <lars@6xq.net>
-
-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.
-*/
-
-#ifndef __FreeBSD__
-#define _BSD_SOURCE /* required by strdup() */
-#define _DARWIN_C_SOURCE /* strdup() on OS X */
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ezxml.h>
-#include <assert.h>
-
-#include "xml.h"
-#include "piano.h"
-#include "crypt.h"
-#include "config.h"
-#include "piano_private.h"
-
-static void PianoXmlStructParser (const ezxml_t,
- void (*callback) (const char *, const ezxml_t, void *), void *);
-static char *PianoXmlGetNodeText (const ezxml_t);
-
-/* parse fault and get fault type
- * @param xml <name> content
- * @param xml <value> node
- * @param return error string
- * @return nothing
- */
-static void PianoXmlIsFaultCb (const char *key, const ezxml_t value,
- void *data) {
- PianoReturn_t *ret = data;
- char *valueStr = PianoXmlGetNodeText (value);
- char *matchStart, *matchEnd;
-
- if (strcmp ("faultString", key) == 0) {
- *ret = PIANO_RET_ERR;
- /* find fault identifier in a string like this:
- * com.savagebeast.radio.api.protocol.xmlrpc.RadioXmlRpcException:
- * 192.168.160.78|1213101717317|AUTH_INVALID_TOKEN|
- * Invalid auth token */
- if ((matchStart = strchr (valueStr, '|')) != NULL) {
- if ((matchStart = strchr (matchStart+1, '|')) != NULL) {
- if ((matchEnd = strchr (matchStart+1, '|')) != NULL) {
- /* changes text in xml node, but we don't care... */
- *matchEnd = '\0';
- ++matchStart;
- /* translate to our error message system */
- if (strcmp ("AUTH_INVALID_TOKEN", matchStart) == 0) {
- *ret = PIANO_RET_AUTH_TOKEN_INVALID;
- } else if (strcmp ("AUTH_INVALID_USERNAME_PASSWORD",
- matchStart) == 0) {
- *ret = PIANO_RET_AUTH_USER_PASSWORD_INVALID;
- } else if (strcmp ("LISTENER_NOT_AUTHORIZED",
- matchStart) == 0) {
- *ret = PIANO_RET_NOT_AUTHORIZED;
- } else if (strcmp ("INCOMPATIBLE_VERSION",
- matchStart) == 0) {
- *ret = PIANO_RET_PROTOCOL_INCOMPATIBLE;
- } else if (strcmp ("READONLY_MODE", matchStart) == 0) {
- *ret = PIANO_RET_READONLY_MODE;
- } else if (strcmp ("STATION_CODE_INVALID",
- matchStart) == 0) {
- *ret = PIANO_RET_STATION_CODE_INVALID;
- } else if (strcmp ("STATION_DOES_NOT_EXIST",
- matchStart) == 0) {
- *ret = PIANO_RET_STATION_NONEXISTENT;
- } else if (strcmp ("OUT_OF_SYNC", matchStart) == 0) {
- *ret = PIANO_RET_OUT_OF_SYNC;
- } else if (strcmp ("PLAYLIST_END", matchStart) == 0) {
- *ret = PIANO_RET_PLAYLIST_END;
- } else if (strcmp ("QUICKMIX_NOT_PLAYABLE", matchStart) == 0) {
- *ret = PIANO_RET_QUICKMIX_NOT_PLAYABLE;
- } else if (strcmp ("REMOVING_TOO_MANY_SEEDS", matchStart) == 0) {
- *ret = PIANO_RET_REMOVING_TOO_MANY_SEEDS;
- } else if (strcmp ("EXCESSIVE_ACTIVITY", matchStart) == 0) {
- *ret = PIANO_RET_EXCESSIVE_ACTIVITY;
- } else if (strcmp ("DAILY_SKIP_LIMIT_REACHED", matchStart) == 0) {
- *ret = PIANO_RET_DAILY_SKIP_LIMIT_REACHED;
- } else {
- *ret = PIANO_RET_ERR;
- printf (PACKAGE ": Unknown error %s in %s\n",
- matchStart, valueStr);
- }
- }
- }
- }
- } else if (strcmp ("faultCode", key) == 0) {
- /* some errors can only be identified by looking at their id */
- /* detect pandora's ip restriction */
- if (strcmp ("12", valueStr) == 0) {
- *ret = PIANO_RET_IP_REJECTED;
- }
- }
-}
-
-/* check whether pandora returned an error or not
- * @param document root of xml doc
- * @return _RET_OK or fault code (_RET_*)
- */
-static PianoReturn_t PianoXmlIsFault (ezxml_t xmlDoc) {
- PianoReturn_t ret;
-
- if ((xmlDoc = ezxml_child (xmlDoc, "fault")) != NULL) {
- xmlDoc = ezxml_get (xmlDoc, "value", 0, "struct", -1);
- PianoXmlStructParser (xmlDoc, PianoXmlIsFaultCb, &ret);
- return ret;
- }
- return PIANO_RET_OK;
-}
-
-/* parses things like this:
- * <struct>
- * <member>
- * <name />
- * <value />
- * </member>
- * <!-- ... -->
- * </struct>
- * @param xml node named "struct" (or containing a similar structure)
- * @param who wants to use this data? callback: content of <name> as
- * string, content of <value> as xmlNode (may contain other nodes
- * or text), additional data used by callback(); don't forget
- * to *copy* data taken from <name> or <value> as they will be
- * freed soon
- * @param extra data for callback
- */
-static void PianoXmlStructParser (const ezxml_t structRoot,
- void (*callback) (const char *, const ezxml_t, void *), void *data) {
- ezxml_t curNode, keyNode, valueNode;
- char *key;
-
- /* get all <member> nodes */
- for (curNode = ezxml_child (structRoot, "member"); curNode; curNode = curNode->next) {
- /* reset variables */
- key = NULL;
- valueNode = keyNode = NULL;
-
- keyNode = ezxml_child (curNode, "name");
- if (keyNode != NULL) {
- key = ezxml_txt (keyNode);
- }
-
- valueNode = ezxml_child (curNode, "value");
- /* this will ignore empty <value /> nodes, but well... */
- if (*key != '\0' && valueNode != NULL) {
- (*callback) ((char *) key, valueNode, data);
- }
- }
-}
-
-/* create xml parser from string
- * @param xml document
- * @param returns document pointer (needed to free memory later)
- * @param returns document root
- * @return _OK or error
- */
-static PianoReturn_t PianoXmlInitDoc (char *xmlStr, ezxml_t *xmlDoc) {
- PianoReturn_t ret;
-
- if ((*xmlDoc = ezxml_parse_str (xmlStr, strlen (xmlStr))) == NULL) {
- return PIANO_RET_XML_INVALID;
- }
-
- if ((ret = PianoXmlIsFault (*xmlDoc)) != PIANO_RET_OK) {
- ezxml_free (*xmlDoc);
- return ret;
- }
-
- return PIANO_RET_OK;
-}
-
-/* get text from <value> nodes; some of them have <boolean>, <string>
- * or <int> subnodes, just ignore them
- * @param xml node <value>
- */
-static char *PianoXmlGetNodeText (const ezxml_t node) {
- char *retTxt = NULL;
-
- retTxt = ezxml_txt (node);
- /* no text => empty string */
- if (*retTxt == '\0') {
- retTxt = ezxml_txt (node->child);
- }
- return retTxt;
-}
-
-/* structParser callback; writes userinfo to PianoUserInfo structure
- * @param value identifier
- * @param value node
- * @param pointer to userinfo structure
- * @return nothing
- */
-static void PianoXmlParseUserinfoCb (const char *key, const ezxml_t value,
- void *data) {
- PianoUserInfo_t *user = data;
- char *valueStr = PianoXmlGetNodeText (value);
-
- if (strcmp ("webAuthToken", key) == 0) {
- user->webAuthToken = strdup (valueStr);
- } else if (strcmp ("authToken", key) == 0) {
- user->authToken = strdup (valueStr);
- } else if (strcmp ("listenerId", key) == 0) {
- user->listenerId = strdup (valueStr);
- }
-}
-
-static void PianoXmlParseStationsCb (const char *key, const ezxml_t value,
- void *data) {
- PianoStation_t *station = data;
- char *valueStr = PianoXmlGetNodeText (value);
-
- if (strcmp ("stationName", key) == 0) {
- station->name = strdup (valueStr);
- } else if (strcmp ("stationId", key) == 0) {
- station->id = strdup (valueStr);
- } else if (strcmp ("isQuickMix", key) == 0) {
- station->isQuickMix = (strcmp (valueStr, "1") == 0);
- } else if (strcmp ("isCreator", key) == 0) {
- station->isCreator = (strcmp (valueStr, "1") == 0);
- }
-}
-
-static void PianoXmlParsePlaylistCb (const char *key, const ezxml_t value,
- void *data) {
- PianoSong_t *song = data;
- char *valueStr = PianoXmlGetNodeText (value);
-
- if (strcmp ("audioURL", key) == 0) {
- /* last 48 chars of audioUrl are encrypted, but they put the key
- * into the door's lock... */
- const char urlTailN = 48;
- const size_t valueStrN = strlen (valueStr);
- char *urlTail = NULL,
- *urlTailCrypted = &valueStr[valueStrN - urlTailN];
-
- /* don't try to decrypt if string is too short (=> invalid memory
- * reads/writes) */
- if (valueStrN > urlTailN &&
- (urlTail = PianoDecryptString (urlTailCrypted, NULL)) != NULL) {
- if ((song->audioUrl = calloc (valueStrN + 1,
- sizeof (*song->audioUrl))) != NULL) {
- memcpy (song->audioUrl, valueStr, valueStrN - urlTailN);
- /* FIXME: the key seems to be broken... so ignore 8 x 0x08
- * postfix; urlTailN/2 because the encrypted hex string is now
- * decoded */
- memcpy (&song->audioUrl[valueStrN - urlTailN], urlTail,
- urlTailN/2 - 8);
- }
- free (urlTail);
- }
- } else if (strcmp ("artRadio", key) == 0) {
- song->coverArt = strdup (valueStr);
- } else if (strcmp ("artistSummary", key) == 0) {
- song->artist = strdup (valueStr);
- } else if (strcmp ("musicId", key) == 0) {
- song->musicId = strdup (valueStr);
- } else if (strcmp ("userSeed", key) == 0) {
- song->userSeed = strdup (valueStr);
- } else if (strcmp ("songTitle", key) == 0) {
- song->title = strdup (valueStr);
- } else if (strcmp ("rating", key) == 0) {
- if (strcmp (valueStr, "1") == 0) {
- song->rating = PIANO_RATE_LOVE;
- } else {
- song->rating = PIANO_RATE_NONE;
- }
- } else if (strcmp ("isPositive", key) == 0) {
- if (strcmp (valueStr, "1") == 0) {
- song->rating = PIANO_RATE_LOVE;
- } else {
- song->rating = PIANO_RATE_BAN;
- }
- } else if (strcmp ("stationId", key) == 0) {
- song->stationId = strdup (valueStr);
- } else if (strcmp ("albumTitle", key) == 0) {
- song->album = strdup (valueStr);
- } else if (strcmp ("fileGain", key) == 0) {
- song->fileGain = atof (valueStr);
- } else if (strcmp ("audioEncoding", key) == 0) {
- if (strcmp (valueStr, "aacplus") == 0) {
- song->audioFormat = PIANO_AF_AACPLUS;
- } else if (strcmp (valueStr, "mp3") == 0) {
- song->audioFormat = PIANO_AF_MP3;
- } else if (strcmp (valueStr, "mp3-hifi") == 0) {
- song->audioFormat = PIANO_AF_MP3_HI;
- }
- } else if (strcmp ("artistMusicId", key) == 0) {
- song->artistMusicId = strdup (valueStr);
- } else if (strcmp ("feedbackId", key) == 0) {
- song->feedbackId = strdup (valueStr);
- } else if (strcmp ("songDetailURL", key) == 0) {
- song->detailUrl = strdup (valueStr);
- } else if (strcmp ("trackToken", key) == 0) {
- song->trackToken = strdup (valueStr);
- }
-}
-
-/* parses userinfos sent by pandora as login response
- * @param piano handle
- * @param utf-8 string
- * @return _RET_OK or error
- */
-PianoReturn_t PianoXmlParseUserinfo (PianoHandle_t *ph, char *xml) {
- ezxml_t xmlDoc, structNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- /* <methodResponse> <params> <param> <value> <struct> */
- structNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
- PianoXmlStructParser (structNode, PianoXmlParseUserinfoCb, &ph->user);
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-static void PianoXmlParseQuickMixStationsCb (const char *key, const ezxml_t value,
- void *data) {
- char ***retIds = data;
- char **ids = NULL;
- size_t idsN = 0;
- ezxml_t curNode;
-
- if (strcmp ("quickMixStationIds", key) == 0) {
- for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
- curNode; curNode = curNode->next) {
- idsN++;
- if (ids == NULL) {
- if ((ids = calloc (idsN, sizeof (*ids))) == NULL) {
- *retIds = NULL;
- return;
- }
- } else {
- /* FIXME: memory leak (on failure) */
- if ((ids = realloc (ids, idsN * sizeof (*ids))) == NULL) {
- *retIds = NULL;
- return;
- }
- }
- ids[idsN-1] = strdup (PianoXmlGetNodeText (curNode));
- }
- /* append NULL: list ends here */
- idsN++;
- /* FIXME: copy&waste */
- if (ids == NULL) {
- if ((ids = calloc (idsN, sizeof (*ids))) == NULL) {
- *retIds = NULL;
- return;
- }
- } else {
- if ((ids = realloc (ids, idsN * sizeof (*ids))) == NULL) {
- *retIds = NULL;
- return;
- }
- }
- ids[idsN-1] = NULL;
-
- *retIds = ids;
- }
-}
-
-/* parse stations returned by pandora
- * @param piano handle
- * @param xml returned by pandora
- * @return _RET_OK or error
- */
-PianoReturn_t PianoXmlParseStations (PianoHandle_t *ph, char *xml) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
- char **quickMixIds = NULL, **curQuickMixId = NULL;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "array",
- 0, "data", -1);
-
- for (dataNode = ezxml_child (dataNode, "value"); dataNode;
- dataNode = dataNode->next) {
- PianoStation_t *tmpStation;
-
- if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
- ezxml_free (xmlDoc);
- return PIANO_RET_OUT_OF_MEMORY;
- }
-
- PianoXmlStructParser (ezxml_child (dataNode, "struct"),
- PianoXmlParseStationsCb, tmpStation);
-
- /* get stations selected for quickmix */
- if (tmpStation->isQuickMix) {
- PianoXmlStructParser (ezxml_child (dataNode, "struct"),
- PianoXmlParseQuickMixStationsCb, &quickMixIds);
- }
- /* start new linked list or append */
- if (ph->stations == NULL) {
- ph->stations = tmpStation;
- } else {
- PianoStation_t *curStation = ph->stations;
- while (curStation->next != NULL) {
- curStation = curStation->next;
- }
- curStation->next = tmpStation;
- }
- }
- /* set quickmix flags after all stations are read */
- if (quickMixIds != NULL) {
- curQuickMixId = quickMixIds;
- while (*curQuickMixId != NULL) {
- PianoStation_t *curStation = PianoFindStationById (ph->stations,
- *curQuickMixId);
- if (curStation != NULL) {
- curStation->useQuickMix = 1;
- }
- free (*curQuickMixId);
- curQuickMixId++;
- }
- free (quickMixIds);
- }
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* parse "create station" answer (it returns a new station structure)
- * @param piano handle
- * @param xml document
- * @return nothing yet
- */
-PianoReturn_t PianoXmlParseCreateStation (PianoHandle_t *ph, char *xml) {
- ezxml_t xmlDoc, dataNode;
- PianoStation_t *tmpStation;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
-
- if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
- ezxml_free (xmlDoc);
- return PIANO_RET_OUT_OF_MEMORY;
- }
- PianoXmlStructParser (dataNode, PianoXmlParseStationsCb, tmpStation);
- /* FIXME: copy & waste */
- /* start new linked list or append */
- if (ph->stations == NULL) {
- ph->stations = tmpStation;
- } else {
- PianoStation_t *curStation = ph->stations;
- while (curStation->next != NULL) {
- curStation = curStation->next;
- }
- curStation->next = tmpStation;
- }
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* parse "add seed" answer, nearly the same as ParseCreateStation
- * @param piano handle
- * @param xml document
- * @param update this station
- */
-PianoReturn_t PianoXmlParseAddSeed (PianoHandle_t *ph, char *xml,
- PianoStation_t *station) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
- PianoDestroyStation (station);
- PianoXmlStructParser (dataNode, PianoXmlParseStationsCb, station);
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-static PianoReturn_t PianoXmlParsePlaylistStruct (ezxml_t xml,
- PianoSong_t **retSong) {
- PianoSong_t *playlist = *retSong, *tmpSong;
-
- if ((tmpSong = calloc (1, sizeof (*tmpSong))) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
-
- PianoXmlStructParser (ezxml_child (xml, "struct"), PianoXmlParsePlaylistCb,
- tmpSong);
- /* begin linked list or append */
- if (playlist == NULL) {
- playlist = tmpSong;
- } else {
- PianoSong_t *curSong = playlist;
- while (curSong->next != NULL) {
- curSong = curSong->next;
- }
- curSong->next = tmpSong;
- }
-
- *retSong = playlist;
-
- return PIANO_RET_OK;
-}
-
-/* parses playlist; used when searching too
- * @param piano handle
- * @param xml document
- * @param return: playlist
- */
-PianoReturn_t PianoXmlParsePlaylist (PianoHandle_t *ph, char *xml,
- PianoSong_t **retPlaylist) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret = PIANO_RET_OK;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "array",
- 0, "data", -1);
-
- for (dataNode = ezxml_child (dataNode, "value"); dataNode;
- dataNode = dataNode->next) {
- if ((ret = PianoXmlParsePlaylistStruct (dataNode, retPlaylist)) !=
- PIANO_RET_OK) {
- break;
- }
- }
-
- ezxml_free (xmlDoc);
-
- return ret;
-}
-
-/* check for exception only
- * @param xml string
- * @return _OK or error
- */
-PianoReturn_t PianoXmlParseSimple (char *xml) {
- ezxml_t xmlDoc;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- ezxml_free (xmlDoc);
-
- return ret;
-}
-
-/* xml struct parser callback, used in PianoXmlParseSearchCb
- */
-static void PianoXmlParseSearchArtistCb (const char *key, const ezxml_t value,
- void *data) {
- PianoArtist_t *artist = data;
- char *valueStr = PianoXmlGetNodeText (value);
-
- if (strcmp ("artistName", key) == 0) {
- artist->name = strdup (valueStr);
- } else if (strcmp ("musicId", key) == 0) {
- artist->musicId = strdup (valueStr);
- }
-}
-
-/* callback for xml struct parser used in PianoXmlParseSearch, "switch" for
- * PianoXmlParseSearchArtistCb and PianoXmlParsePlaylistCb
- */
-static void PianoXmlParseSearchCb (const char *key, const ezxml_t value,
- void *data) {
- PianoSearchResult_t *searchResult = data;
- ezxml_t curNode;
-
- if (strcmp ("artists", key) == 0) {
- /* skip <value><array><data> */
- for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
- curNode; curNode = curNode->next) {
- PianoArtist_t *artist;
-
- if ((artist = calloc (1, sizeof (*artist))) == NULL) {
- /* fail silently */
- break;
- }
-
- memset (artist, 0, sizeof (*artist));
-
- PianoXmlStructParser (ezxml_child (curNode, "struct"),
- PianoXmlParseSearchArtistCb, artist);
-
- /* add result to linked list */
- if (searchResult->artists == NULL) {
- searchResult->artists = artist;
- } else {
- PianoArtist_t *curArtist = searchResult->artists;
- while (curArtist->next != NULL) {
- curArtist = curArtist->next;
- }
- curArtist->next = artist;
- }
- }
- } else if (strcmp ("songs", key) == 0) {
- for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value");
- curNode; curNode = curNode->next) {
- if (PianoXmlParsePlaylistStruct (curNode, &searchResult->songs) !=
- PIANO_RET_OK) {
- break;
- }
- }
- }
-}
-
-/* parse search result; searchResult is nulled before use
- * @param xml document
- * @param returns search result
- * @return nothing yet
- */
-PianoReturn_t PianoXmlParseSearch (char *xml,
- PianoSearchResult_t *searchResult) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
- /* we need a "clean" search result (with null pointers) */
- memset (searchResult, 0, sizeof (*searchResult));
- PianoXmlStructParser (dataNode, PianoXmlParseSearchCb, searchResult);
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* FIXME: copy&waste (PianoXmlParseSearch)
- */
-PianoReturn_t PianoXmlParseSeedSuggestions (char *xml,
- PianoSearchResult_t *searchResult) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", -1);
- /* we need a "clean" search result (with null pointers) */
- memset (searchResult, 0, sizeof (*searchResult));
- /* reuse seach result parser; structure is nearly the same */
- PianoXmlParseSearchCb ("artists", dataNode, searchResult);
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* encode reserved xml chars
- * TODO: remove and use ezxml_ampencode
- * @param encode this
- * @return encoded string or NULL
- */
-char *PianoXmlEncodeString (const char *s) {
- static const char *replacements[] = {"&&amp;", "'&apos;", "\"&quot;",
- "<&lt;", ">&gt;", NULL};
- const char **r;
- char *sOut, *sOutCurr, found;
-
- if ((sOut = calloc (strlen (s) * 5 + 1, sizeof (*sOut))) == NULL) {
- return NULL;
- }
-
- sOutCurr = sOut;
-
- while (*s != '\0') {
- r = replacements;
- found = 0;
- while (*r != NULL) {
- if (*s == *r[0]) {
- found = 1;
- strcat (sOutCurr, (*r) + 1);
- sOutCurr += strlen ((*r) + 1);
- break;
- }
- r++;
- }
- if (!found) {
- *sOutCurr = *s;
- sOutCurr++;
- }
- s++;
- }
- return sOut;
-}
-
-PianoReturn_t PianoXmlParseGenreExplorer (PianoHandle_t *ph, char *xml) {
- ezxml_t xmlDoc, catNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- /* get all <member> nodes */
- for (catNode = ezxml_child (xmlDoc, "category"); catNode;
- catNode = catNode->next) {
- PianoGenreCategory_t *tmpGenreCategory;
- ezxml_t genreNode;
-
- if ((tmpGenreCategory = calloc (1, sizeof (*tmpGenreCategory))) == NULL) {
- ezxml_free (xmlDoc);
- return PIANO_RET_OUT_OF_MEMORY;
- }
-
- tmpGenreCategory->name = strdup (ezxml_attr (catNode, "categoryName"));
-
- /* get genre subnodes */
- for (genreNode = ezxml_child (catNode, "genre"); genreNode;
- genreNode = genreNode->next) {
- PianoGenre_t *tmpGenre;
-
- if ((tmpGenre = calloc (1, sizeof (*tmpGenre))) == NULL) {
- ezxml_free (xmlDoc);
- return PIANO_RET_OUT_OF_MEMORY;
- }
-
- /* get genre attributes */
- tmpGenre->name = strdup (ezxml_attr (genreNode, "name"));
- tmpGenre->musicId = strdup (ezxml_attr (genreNode, "musicId"));
-
- /* append station */
- if (tmpGenreCategory->genres == NULL) {
- tmpGenreCategory->genres = tmpGenre;
- } else {
- PianoGenre_t *curGenre =
- tmpGenreCategory->genres;
- while (curGenre->next != NULL) {
- curGenre = curGenre->next;
- }
- curGenre->next = tmpGenre;
- }
- }
- /* append category */
- if (ph->genreStations == NULL) {
- ph->genreStations = tmpGenreCategory;
- } else {
- PianoGenreCategory_t *curCat = ph->genreStations;
- while (curCat->next != NULL) {
- curCat = curCat->next;
- }
- curCat->next = tmpGenreCategory;
- }
- }
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* dummy function, only checks for errors
- * @param xml doc
- * @return _OK or error
- */
-PianoReturn_t PianoXmlParseTranformStation (char *xml) {
- ezxml_t xmlDoc;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
-/* parses "why did you play ...?" answer
- * @param xml
- * @param returns the answer
- * @return _OK or error
- */
-PianoReturn_t PianoXmlParseNarrative (char *xml, char **retNarrative) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- /* <methodResponse> <params> <param> <value> $textnode */
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", -1);
- *retNarrative = strdup (ezxml_txt (dataNode));
-
- ezxml_free (xmlDoc);
-
- return ret;
-}
-
-/* seed bag, required because seedId is not part of artist/song struct in
- * pandora's xml response
- */
-struct PianoXmlParseSeedBag {
- char *seedId;
- PianoSong_t *song;
- PianoArtist_t *artist;
- PianoStation_t *station;
-};
-
-/* parse seed struct
- */
-static void PianoXmlParseSeedCb (const char *key, const ezxml_t value,
- void *data) {
- struct PianoXmlParseSeedBag *bag = data;
-
- assert (bag != NULL);
-
- if (strcmp ("song", key) == 0) {
- assert (bag->song == NULL);
-
- if ((bag->song = calloc (1, sizeof (*bag->song))) == NULL) {
- return;
- }
-
- PianoXmlStructParser (ezxml_child (value, "struct"),
- PianoXmlParsePlaylistCb, bag->song);
- } else if (strcmp ("artist", key) == 0) {
- assert (bag->artist == NULL);
-
- if ((bag->artist = calloc (1, sizeof (*bag->artist))) == NULL) {
- return;
- }
-
- PianoXmlStructParser (ezxml_child (value, "struct"),
- PianoXmlParseSearchArtistCb, bag->artist);
- } else if (strcmp ("nonGenomeStation", key) == 0) {
- /* genre stations are "non genome" station seeds */
- assert (bag->station == NULL);
-
- if ((bag->station = calloc (1, sizeof (*bag->station))) == NULL) {
- return;
- }
-
- PianoXmlStructParser (ezxml_child (value, "struct"),
- PianoXmlParseStationsCb, bag->station);
- } else if (strcmp ("seedId", key) == 0) {
- char *valueStr = PianoXmlGetNodeText (value);
- bag->seedId = strdup (valueStr);
- }
-}
-
-/* parse getStation xml struct
- */
-static void PianoXmlParseGetStationInfoCb (const char *key, const ezxml_t value,
- void *data) {
- PianoStationInfo_t *info = data;
-
- if (strcmp ("seeds", key) == 0) {
- const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1);
- for (ezxml_t seedNode = ezxml_child (dataNode, "value"); seedNode;
- seedNode = seedNode->next) {
- struct PianoXmlParseSeedBag bag;
- memset (&bag, 0, sizeof (bag));
-
- PianoXmlStructParser (ezxml_child (seedNode, "struct"),
- PianoXmlParseSeedCb, &bag);
-
- assert (bag.song != NULL || bag.artist != NULL ||
- bag.station != NULL);
-
- if (bag.seedId == NULL) {
- /* seeds without id are useless */
- continue;
- }
-
- /* FIXME: copy&waste */
- if (bag.song != NULL) {
- bag.song->seedId = bag.seedId;
-
- if (info->songSeeds == NULL) {
- info->songSeeds = bag.song;
- } else {
- PianoSong_t *curSong = info->songSeeds;
- while (curSong->next != NULL) {
- curSong = curSong->next;
- }
- curSong->next = bag.song;
- }
- } else if (bag.artist != NULL) {
- bag.artist->seedId = bag.seedId;
-
- if (info->artistSeeds == NULL) {
- info->artistSeeds = bag.artist;
- } else {
- PianoArtist_t *curSong = info->artistSeeds;
- while (curSong->next != NULL) {
- curSong = curSong->next;
- }
- curSong->next = bag.artist;
- }
- } else if (bag.station != NULL) {
- bag.station->seedId = bag.seedId;
-
- if (info->stationSeeds == NULL) {
- info->stationSeeds = bag.station;
- } else {
- PianoStation_t *curStation = info->stationSeeds;
- while (curStation->next != NULL) {
- curStation = curStation->next;
- }
- curStation->next = bag.station;
- }
- } else {
- free (bag.seedId);
- }
- }
- } else if (strcmp ("feedback", key) == 0) {
- const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1);
- for (ezxml_t feedbackNode = ezxml_child (dataNode, "value"); feedbackNode;
- feedbackNode = feedbackNode->next) {
- if (PianoXmlParsePlaylistStruct (feedbackNode, &info->feedback) !=
- PIANO_RET_OK) {
- break;
- }
- }
- }
-}
-
-/* parse getStation response
- */
-PianoReturn_t PianoXmlParseGetStationInfo (char *xml,
- PianoStationInfo_t *stationInfo) {
- ezxml_t xmlDoc, dataNode;
- PianoReturn_t ret;
-
- if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) {
- return ret;
- }
-
- dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1);
- PianoXmlStructParser (dataNode, PianoXmlParseGetStationInfoCb, stationInfo);
-
- ezxml_free (xmlDoc);
-
- return PIANO_RET_OK;
-}
-
diff --git a/src/libpiano/xml.h b/src/libpiano/xml.h
deleted file mode 100644
index 58ee28f..0000000
--- a/src/libpiano/xml.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-Copyright (c) 2008-2011
- Lars-Dominik Braun <lars@6xq.net>
-
-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.
-*/
-
-#ifndef _XML_H
-#define _XML_H
-
-#include "piano.h"
-
-PianoReturn_t PianoXmlParseUserinfo (PianoHandle_t *ph, char *xml);
-PianoReturn_t PianoXmlParseStations (PianoHandle_t *ph, char *xml);
-PianoReturn_t PianoXmlParsePlaylist (PianoHandle_t *ph, char *xml,
- PianoSong_t **);
-PianoReturn_t PianoXmlParseSearch (char *searchXml,
- PianoSearchResult_t *searchResult);
-PianoReturn_t PianoXmlParseSimple (char *xml);
-PianoReturn_t PianoXmlParseCreateStation (PianoHandle_t *ph,
- char *xml);
-PianoReturn_t PianoXmlParseAddSeed (PianoHandle_t *ph, char *xml,
- PianoStation_t *station);
-PianoReturn_t PianoXmlParseGenreExplorer (PianoHandle_t *ph,
- char *xmlContent);
-PianoReturn_t PianoXmlParseTranformStation (char *searchXml);
-PianoReturn_t PianoXmlParseNarrative (char *xml, char **retNarrative);
-PianoReturn_t PianoXmlParseSeedSuggestions (char *, PianoSearchResult_t *);
-PianoReturn_t PianoXmlParseGetStationInfo (char *, PianoStationInfo_t *);
-
-char *PianoXmlEncodeString (const char *s);
-
-#endif /* _XML_H */
diff --git a/src/settings.c b/src/settings.c
index 51c2400..d1dcc7f 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -133,8 +133,8 @@ void BarSettingsRead (BarSettings_t *settings) {
settings->listSongFormat = strdup ("%i) %a - %t%r");
settings->fifo = malloc (PATH_MAX * sizeof (*settings->fifo));
BarGetXdgConfigDir (PACKAGE "/ctl", settings->fifo, PATH_MAX);
- memcpy (settings->tlsFingerprint, "\xD9\x98\x0B\xA2\xCC\x0F\x97\xBB"
- "\x03\x82\x2C\x62\x11\xEA\xEA\x4A\x06\xEE\xF4\x27",
+ memcpy (settings->tlsFingerprint, "\xA2\xA0\xBE\x8A\x37\x92\x39\xAE"
+ "\x2B\x2E\x71\x4C\x56\xB3\x8B\xC1\x2A\x9B\x4B\x77",
sizeof (settings->tlsFingerprint));
settings->msgFormat[MSG_NONE].prefix = NULL;
diff --git a/src/ui.c b/src/ui.c
index 4162dce..f13bed5 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -138,7 +138,7 @@ void BarUiMsg (const BarSettings_t *settings, const BarUiMsg_t type,
*/
static WaitressReturn_t BarPianoHttpRequest (WaitressHandle_t *waith,
PianoRequest_t *req, bool forceTls) {
- waith->extraHeaders = "Content-Type: text/xml\r\n";
+ waith->extraHeaders = "Content-Type: text/plain\r\n";
waith->postData = req->postData;
waith->method = WAITRESS_METHOD_POST;
waith->url.path = req->urlPath;
@@ -186,7 +186,7 @@ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type,
*pRet = PianoResponse (&app->ph, &req);
if (*pRet != PIANO_RET_CONTINUE_REQUEST) {
/* checking for request type avoids infinite loops */
- if (*pRet == PIANO_RET_AUTH_TOKEN_INVALID &&
+ if (*pRet == PIANO_RET_P_INVALID_AUTH_TOKEN &&
type != PIANO_REQUEST_LOGIN) {
/* reauthenticate */
PianoReturn_t authpRet;
diff --git a/src/ui_act.c b/src/ui_act.c
index 76b9411..93ba623 100644
--- a/src/ui_act.c
+++ b/src/ui_act.c
@@ -261,8 +261,7 @@ BarUiActCallback(BarUiActDebug) {
"rating:\t%i\n"
"stationId:\t%s\n"
"title:\t%s\n"
- "trackToken:\t%s\n"
- "userSeed:\t%s\n",
+ "trackToken:\t%s\n",
selSong->album,
selSong->artist,
selSong->audioFormat,
@@ -274,8 +273,7 @@ BarUiActCallback(BarUiActDebug) {
selSong->rating,
selSong->stationId,
selSong->title,
- selSong->trackToken,
- selSong->userSeed);
+ selSong->trackToken);
}
/* rate current song