static void parse_browse_album(ezxml_t top, struct album_browse* a) { xmlstrncpy(a->name, sizeof a->name, top, "name", -1); xmlstrncpy(a->id, sizeof a->id, top, "id", -1); xmlstrncpy(a->cover_id, sizeof a->cover_id, top, "cover", -1); xmlatoi(&a->year, top, "year", -1); xmlatof(&a->popularity, top, "popularity", -1); xmlstrncpy(a->artist, sizeof a->artist, top, "artist", -1); xmlstrncpy(a->artist_id, sizeof a->artist_id, top, "artist-id", -1); ezxml_t x = ezxml_get(top, "review",-1); if (x) { int len = strlen(x->txt); a->review = malloc(len + 1); memcpy(a->review, x->txt, len+1); } /* TODO: support multiple discs per album */ a->tracks = calloc(1, sizeof(struct track)); ezxml_t disc = ezxml_get(top, "discs",0,"disc", -1); a->num_tracks = parse_tracks(disc, a->tracks, false); /* Copy missing metadata from album to tracks */ int count = 0; for (struct track *t = a->tracks; t; t = t->next) { DSFYstrncpy(t->album, a->name, sizeof t->album); DSFYstrncpy(t->album_id, a->id, sizeof t->album_id); DSFYstrncpy(t->cover_id, a->cover_id, sizeof t->cover_id); t->year = a->year; count++; } }
int session_connect (SESSION * session) { struct sockaddr_in sin; char host[1025 + 1], *service_list, *service; int port; /* Lookup service hosts in DNS */ service_list = dns_srv_list ("_spotify-client._tcp.spotify.com"); if (!service_list) { DSFYDEBUG("service lookup failed. falling back to ap.spotify.com\n"); service_list = malloc(200); strcpy(service_list, "ap.spotify.com:4070\n"); } for (service = service_list; *service;) { if (sscanf (service, "%[^:]:%d\n", host, &port) != 2) return -1; service += strlen (host) + 7; DSFYDEBUG ("session_connect(): Connecting to %s:%d\n", host, port); memset (&sin, 0, sizeof (sin)); sin.sin_family = PF_INET; sin.sin_port = htons (port); sin.sin_addr.s_addr = dns_resolve_name (host); if (sin.sin_addr.s_addr == INADDR_NONE) continue; session->ap_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (connect (session->ap_sock, (struct sockaddr *) &sin, sizeof (sin)) != -1) break; sock_close (session->ap_sock); session->ap_sock = -1; } free (service_list); if (sin.sin_addr.s_addr == INADDR_NONE) return -1; /* * Save for later use in ConnectionInfo message * (too lazy to do getpeername() later ;) */ DSFYstrncpy (session->server_host, host, sizeof session->server_host); session->server_port = port; DSFYstrncpy (session->user_info.server_host, host, sizeof session->user_info.server_host); session->user_info.server_port = port; return 0; }
void session_auth_set (SESSION * session, const char *username, const char *password) { DSFYstrncpy (session->user_info.username, username, sizeof session->user_info.username); DSFYstrncpy (session->username, username, sizeof session->username); session->username[sizeof (session->username) - 1] = 0; session->username_len = strlen (session->username); DSFYstrncpy (session->password, password, sizeof session->password); session->password[sizeof (session->password) - 1] = 0; }
static void parse_browse_album(ezxml_t top, struct album_browse* a, bool high_bitrate) { xmlstrncpy(a->name, sizeof a->name, top, "name", -1); xmlstrncpy(a->id, sizeof a->id, top, "id", -1); xmlstrncpy(a->artist, sizeof a->artist, top, "artist", -1); xmlstrncpy(a->artist_id, sizeof a->artist_id, top, "artist-id", -1); xmlstrncpy(a->cover_id, sizeof a->cover_id, top, "cover", -1); xmlatoi(&a->year, top, "year", -1); xmlatof(&a->popularity, top, "popularity", -1); a->tracks = calloc(1, sizeof(struct track)); ezxml_t disc = ezxml_get(top, "discs",0,"disc", -1); a->num_tracks = parse_tracks(disc, a->tracks, false, high_bitrate); /* Append extra discs to album */ struct track *last = a->tracks; while (last->next) last = last->next; while ((disc = disc->next)) { int offset = last->tracknumber; last->next = calloc(1, sizeof(struct track)); a->num_tracks += parse_tracks(disc, last->next, false, high_bitrate); do { last = last->next; last->tracknumber += offset; } while (last->next); } /* Copy missing metadata from album to tracks */ int count = 0; for (struct track *t = a->tracks; t; t = t->next) { DSFYstrncpy(t->album, a->name, sizeof t->album); DSFYstrncpy(t->album_id, a->id, sizeof t->album_id); DSFYstrncpy(t->cover_id, a->cover_id, sizeof t->cover_id); t->year = a->year; count++; } }
static void parse_browse_album(ezxml_t top, struct album_browse* a, bool high_bitrate) { xmlstrncpy(a->name, sizeof a->name, top, "name", -1); xmlstrncpy(a->id, sizeof a->id, top, "id", -1); xmlstrncpy(a->cover_id, sizeof a->cover_id, top, "cover", -1); xmlatoi(&a->year, top, "year", -1); xmlatof(&a->popularity, top, "popularity", -1); /* TODO: support multiple discs per album */ a->tracks = calloc(1, sizeof(struct track)); ezxml_t disc = ezxml_get(top, "discs",0,"disc", -1); a->num_tracks = parse_tracks(disc, a->tracks, false, high_bitrate); /* Copy missing metadata from album to tracks */ int count = 0; for (struct track *t = a->tracks; t; t = t->next) { DSFYstrncpy(t->album, a->name, sizeof t->album); DSFYstrncpy(t->album_id, a->id, sizeof t->album_id); DSFYstrncpy(t->cover_id, a->cover_id, sizeof t->cover_id); t->year = a->year; count++; } }
struct playlist* xml_parse_playlist(struct playlist* pl, unsigned char* xml, int len, bool list_of_lists) { ezxml_t top = ezxml_parse_str(xml, len); ezxml_t tmpx = ezxml_get(top, "next-change",0, "change", 0, "ops", 0, "add", 0, "items", -1); char* items = NULL; if (tmpx) items = tmpx->txt; while (items && *items && isspace(*items)) items++; if (list_of_lists) { /* create list of playlists */ struct playlist* prev = NULL; struct playlist* p = pl; for (char* id = strtok(items, ",\n"); id; id = strtok(NULL, ",\n")) { if (prev) { p = calloc(1, sizeof(struct playlist)); prev->next = p; } DSFYstrncpy(p->playlist_id, id, sizeof p->playlist_id); prev = p; } } else { /* create list of tracks */ struct track* prev = NULL; struct track* root = NULL; struct track* t = NULL; int track_count = 0; for (char* id = strtok(items, ",\n"); id; id = strtok(NULL, ",\n")) { t = calloc(1, sizeof(struct track)); if (prev) prev->next = t; else root = t; DSFYstrncpy(t->track_id, id, sizeof t->track_id); prev = t; track_count++; } pl->tracks = root; pl->num_tracks = track_count; // FIXME: <version> parsing overwrites track_count } xmlstrncpy(pl->author, sizeof pl->author, top, "next-change",0, "change", 0, "user", -1); xmlstrncpy(pl->name, sizeof pl->name, top, "next-change",0, "change", 0, "ops",0, "name", -1); xml_parse_version(pl, top, "next-change", 0, "version", -1); ezxml_free(top); return pl; }
static int parse_tracks(ezxml_t xml, struct track* t, bool ordered, bool high_bitrate) { int track_count = 0; struct track* prev = NULL; struct track* root = t; for (ezxml_t track = ezxml_get(xml, "track",-1); track; track = track->next) { /* is this an ordered list? in that case we have to find the right track struct for every track id */ if (ordered) { char tid[33]; xmlstrncpy(tid, sizeof tid, track, "id", -1); struct track* tt; for (tt = root; tt; tt = tt->next) if (!tt->has_meta_data && !strncmp(tt->track_id, tid, sizeof tt->track_id)) break; /* if we didn't find the id, check if an old, redirected id is used */ if (!tt) { char rid[33]; for (ezxml_t re = ezxml_child(track, "redirect"); re; re = re->next) { strncpy(rid, re->txt, sizeof rid); for (tt = root; tt; tt = tt->next) { /* update to new id */ /* FIXME: This invalidates the playlist checksum */ if (!tt->has_meta_data && !strncmp(tt->track_id, rid, sizeof tt->track_id)) { memcpy (tt->track_id, tid, sizeof tt->track_id); break; } } if (tt) break; } /* we've wasted enough cpu cycles on this track now */ if (!tt) { DSFYDEBUG("!!! error: track id not found: %s\n", tid); continue; } } t = tt; } else if (!t) { t = calloc(1, sizeof(struct track)); prev->next = t; } xmlstrncpy(t->title, sizeof t->title, track, "title", -1); xmlstrncpy(t->album, sizeof t->album, track, "album", -1); xmlstrncpy(t->track_id, sizeof t->track_id, track, "id", -1); xmlstrncpy(t->cover_id, sizeof t->cover_id, track, "cover", -1); xmlstrncpy(t->album_id, sizeof t->album_id, track, "album-id", -1); /* create list of artists */ struct artist* preva = NULL; struct artist* artist = calloc(1, sizeof(struct artist)); t->artist = artist; ezxml_t xid = ezxml_get(track, "artist-id", -1); for (ezxml_t xa = ezxml_get(track, "artist", -1); xa; xa = xa->next) { if (preva) { artist = calloc(1, sizeof(struct artist)); preva->next = artist; } DSFYstrncpy(artist->name, xa->txt, sizeof artist->name); if (xid) { DSFYstrncpy(artist->id, xid->txt, sizeof artist->id); xid = xid->next; } preva = artist; } for ( ezxml_t file = ezxml_get(track, "files",0, "file",-1); file; file = file->next) { char* fmt = (char*)ezxml_attr(file, "format"); if (fmt) { unsigned int bitrate; if (sscanf(fmt,"Ogg Vorbis,%u,", &bitrate)) { if (bitrate > t->file_bitrate) { if (high_bitrate || t->file_bitrate == 0) t->file_bitrate = bitrate; else continue; } } char* id = (char*)ezxml_attr(file, "id"); if (id) { DSFYstrncpy(t->file_id, id, sizeof t->file_id); t->playable = true; } } } for ( ezxml_t restriction = ezxml_get(track, "restrictions", 0, "restriction", -1); restriction; restriction = restriction->next) { char *catalogues = (char*)ezxml_attr(restriction, "catalogues"); if(catalogues && strstr(catalogues, "premium") != NULL) { char* allowed = (char*)ezxml_attr(restriction, "allowed"); if(allowed) { t->allowed = calloc(strlen(allowed)+1, sizeof(char)); DSFYstrncpy(t->allowed, allowed, strlen(allowed)+1); } else { t->allowed = NULL; } char* forbidden = (char*)ezxml_attr(restriction, "forbidden"); if(forbidden) { t->forbidden = calloc(strlen(forbidden)+1, sizeof(char)); DSFYstrncpy(t->forbidden, forbidden, strlen(forbidden)+1); } else { t->forbidden = NULL; } } } xmlatoi(&t->year, track, "year", -1); xmlatoi(&t->length, track, "length", -1); xmlatoi(&t->tracknumber, track, "track-number", -1); xmlatof(&t->popularity, track, "popularity", -1); t->has_meta_data = true; prev = t; t = t->next; track_count++; } return track_count; }
static int parse_tracks(ezxml_t xml, struct track* t, bool ordered) { int track_count = 0; struct track* prev = NULL; struct track* root = t; for (ezxml_t track = ezxml_get(xml, "track",-1); track; track = track->next) { /* is this an ordered list? in that case we have to find the right track struct for every track id */ if (ordered) { char tid[33]; xmlstrncpy(tid, sizeof tid, track, "id", -1); struct track* tt; for (tt = root; tt; tt = tt->next) if (!tt->has_meta_data && !strncmp(tt->track_id, tid, sizeof tt->track_id)) break; /* if we didn't find the id, check if an old, redirected id is used */ if (!tt) { char rid[33]; for (ezxml_t re = ezxml_child(track, "redirect"); re; re = re->next) { strncpy(rid, re->txt, sizeof rid); for (tt = root; tt; tt = tt->next) { /* update to new id */ /* FIXME: This invalidates the playlist checksum */ if (!tt->has_meta_data && !strncmp(tt->track_id, rid, sizeof tt->track_id)) { memcpy (tt->track_id, tid, sizeof tt->track_id); break; } } if (tt) break; } /* we've wasted enough cpu cycles on this track now */ if (!tt) { DSFYDEBUG("!!! error: track id not found: %s\n", tid); continue; } } t = tt; } else if (!t) { t = calloc(1, sizeof(struct track)); prev->next = t; } xmlstrncpy(t->title, sizeof t->title, track, "title", -1); xmlstrncpy(t->album, sizeof t->album, track, "album", -1); xmlstrncpy(t->track_id, sizeof t->track_id, track, "id", -1); xmlstrncpy(t->cover_id, sizeof t->cover_id, track, "cover", -1); xmlstrncpy(t->album_id, sizeof t->album_id, track, "album-id", -1); /* create list of artists */ struct artist* preva = NULL; struct artist* artist = calloc(1, sizeof(struct artist)); t->artist = artist; ezxml_t xid = ezxml_get(track, "artist-id", -1); for (ezxml_t xa = ezxml_get(track, "artist", -1); xa; xa = xa->next) { if (preva) { artist = calloc(1, sizeof(struct artist)); preva->next = artist; } DSFYstrncpy(artist->name, xa->txt, sizeof artist->name); if (xid) { DSFYstrncpy(artist->id, xid->txt, sizeof artist->id); xid = xid->next; } preva = artist; } ezxml_t file = ezxml_get(track, "files",0, "file",-1); if (file) { char* id = (char*)ezxml_attr(file, "id"); if (id) { DSFYstrncpy(t->file_id, id, sizeof t->file_id); t->playable = true; } } xmlatoi(&t->year, track, "year", -1); xmlatoi(&t->length, track, "length", -1); xmlatoi(&t->tracknumber, track, "track-number", -1); xmlatof(&t->popularity, track, "popularity", -1); t->has_meta_data = true; prev = t; t = t->next; track_count++; } return track_count; }
int session_connect (SESSION * session) { struct addrinfo h, *airoot, *ai; char host[1025 + 1], port[6], *service_list, *service; /* Lookup service hosts in DNS */ service_list = dns_srv_list ("_spotify-client._tcp.spotify.com"); if (!service_list) { DSFYDEBUG ("Service lookup failed. falling back to ap.spotify.com\n"); service_list = malloc(200); strcpy (service_list, "ap.spotify.com:4070\n"); } for (service = service_list; *service;) { if (sscanf (service, "%[^:]:%5s\n", host, port) != 2) return -1; service += strlen (host) + 7; DSFYDEBUG ("Connecting to %s:%s\n", host, port); memset(&h, 0, sizeof(h)); h.ai_family = PF_UNSPEC; h.ai_socktype = SOCK_STREAM; h.ai_protocol = IPPROTO_TCP; if (getaddrinfo (host, port, &h, &airoot)) { DSFYDEBUG ("getaddrinfo(%s,%s) failed with error %d\n", host, port, errno); continue; } for(ai = airoot; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; session->ap_sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (session->ap_sock < 0) continue; if (connect (session->ap_sock, (struct sockaddr *) ai->ai_addr, ai->ai_addrlen) != -1) break; sock_close (session->ap_sock); session->ap_sock = -1; } freeaddrinfo (airoot); if (session->ap_sock != -1) break; } free (service_list); if (session->ap_sock == -1) return -1; /* * Save for later use in ConnectionInfo message * (too lazy to do getpeername() later ;) */ DSFYstrncpy (session->server_host, host, sizeof session->server_host); session->server_port = atoi(port); DSFYstrncpy (session->user_info.server_host, host, sizeof session->user_info.server_host); session->user_info.server_port = atoi(port); return 0; }