diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/libezxml/COPYING | 20 | ||||
| -rw-r--r-- | src/libezxml/ezxml.c | 718 | ||||
| -rw-r--r-- | src/libezxml/ezxml.h | 93 | ||||
| -rw-r--r-- | src/libpiano/crypt.c | 205 | ||||
| -rw-r--r-- | src/libpiano/piano.c | 1146 | ||||
| -rw-r--r-- | src/libpiano/piano.h | 79 | ||||
| -rw-r--r-- | src/libpiano/xml.c | 980 | ||||
| -rw-r--r-- | src/libpiano/xml.h | 49 | ||||
| -rw-r--r-- | src/settings.c | 4 | ||||
| -rw-r--r-- | src/ui.c | 4 | ||||
| -rw-r--r-- | src/ui_act.c | 6 | 
11 files changed, 755 insertions, 2549 deletions
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;", "<", "gt;", ">", "quot;", """, -                           "apos;", "'", "amp;", "&", 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[] = {"&&", "''", "\""", -			"<<", ">>", 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; @@ -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  | 
