/* This is part of the audio CD player library Copyright (C)1998-99 Tony Arcieri This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* XXX This file is NOT strtok() clean */ #include #include #include #include #include #include #include #include #include #include #include int cdindex_read_line(int sock, char *inbuffer, int len); /* Connect to a cover art server */ int coverart_connect_server(struct cddb_host host, struct cddb_server *proxy, char *http_string, int len) { int sock; if(proxy != NULL) { if((sock = cddb_connect(proxy)) < 0) return -1; } else { if((sock = cddb_connect(&host.host_server)) < 0) return -1; } snprintf(http_string, len, "GET http://%s:%d/%s", host.host_server.server_name, host.host_server.server_port, host.host_addressing); return sock; } /* Internal function to make strings HTTP/1.0 compliant */ static void coverart_httpize(char *out, char *in, int length) { int index, outdex = 0; char hexval[3]; for(index = 0; index < length; index++) { if(outdex >= length - 1) { out[outdex] = '\0'; return; } if(isalpha(in[index]) || isdigit(in[index]) || in[index] == '.' || in[index] == '-' || in[index] == '_') out[outdex++] = in[index]; else if(in[index] == ' ') out[outdex++] = '+'; else if(in[index] == '\n') { out[outdex++] = '\0'; return; } else if(in[index] =='\0') { out[outdex++] = '\0'; return; } else { if(outdex >= length - 3) { out[outdex] = '\0'; return; } snprintf(hexval, 3, "%02x", in[index]); out[outdex++] = '%'; out[outdex++] = hexval[0]; out[outdex++] = hexval[1]; } } out[outdex] = '\0'; return; } /* Internal function to process a line of cover art data */ static void coverart_process_line(char *line, struct art_query *query) { int index = 0, outdex = 0; char procbuffer[128]; if(strchr(line, ':') == NULL) return; while(line[index++] != ':'); line[index - 1] = '\0'; index++; while(line[index] != '\0') procbuffer[outdex++] = line[index++]; procbuffer[outdex] = '\0'; if(strcmp(line, "NumMatches") == 0) { query->query_matches = strtol(procbuffer, NULL, 10); if(query->query_matches < 1) { query->query_match = QUERY_NOMATCH; query->query_matches = 0; return; } else if(query->query_matches == 1) { query->query_match = QUERY_EXACT; return; } else { query->query_match = QUERY_INEXACT; return; } } else if(strncmp(line, "Album", 5) == 0) { long n = strtol((char *)line + 5, NULL, 10); if(parse_disc_artist && strchr(procbuffer, '/') != NULL) { strtok(procbuffer, "/"); strncpy(query->query_list[n].list_artist, procbuffer, (strlen(procbuffer) < 64) ? (strlen(procbuffer) - 1) : 64); strncpy(query->query_list[n].list_album, (char *)strtok(NULL, "/") + 1, 64); } else { strncpy(query->query_list[n].list_album, procbuffer, 64); query->query_list[n].list_artist[0] = '\0'; } } else if(strncmp(line, "Url", 3) == 0) { long n = strtol((char *)line + 3, NULL, 10); cddb_process_url(&query->query_list[n].list_host, procbuffer); } return; } /* Internal function to read the results of a cover art request */ static int coverart_read_results(int sock, struct art_query *query) { char inbuffer[512]; cdindex_read_line(sock, inbuffer, 512); if(strcmp("NumMatches: 0", inbuffer) == 0 || strncmp("NumMatches: ", inbuffer, 12) != 0) { query->query_match = QUERY_NOMATCH; query->query_matches = 0; return 0; } do coverart_process_line(inbuffer, query); while(cdindex_read_line(sock, inbuffer, 512) > -1); return 0; } /* Cover art query which uses a CD Index ID */ int coverart_query(int cd_desc, int sock, struct art_query *query, char *http_string) { char cdindex_id[CDINDEX_ID_SIZE], outbuffer[512]; cdindex_discid(cd_desc, cdindex_id, CDINDEX_ID_SIZE); snprintf(outbuffer, 512, "%s?id=%s\n", http_string, cdindex_id); write(sock, outbuffer, strlen(outbuffer)); return coverart_read_results(sock, query); } /* Cover art query using a CDDB ID */ int coverart_name_query(int sock, struct art_query *query, char *http_string, char *album, char *artist) { char artistbuffer[64], albumbuffer[64], outbuffer[512]; if(artist != NULL) coverart_httpize(artistbuffer, artist, 64); coverart_httpize(albumbuffer, album, 64); if(artist != NULL) snprintf(outbuffer, 512, "%s?artist=%s&album=%s\n", http_string, artistbuffer, albumbuffer); else snprintf(outbuffer, 512, "%s?album=%s\n", http_string, albumbuffer); write(sock, outbuffer, strlen(outbuffer)); return coverart_read_results(sock, query); } /* Read cover art from a specified host into an art_data structure */ int coverart_read(struct art_data *art, struct cddb_server *proxy, struct cddb_host host) { int sock, bytes_read; char outbuffer[512], inbuffer[512], *artptr; art->art_present = 0; if(proxy == NULL) { if((sock = cddb_connect(&host.host_server)) < 0) return -1; snprintf(outbuffer, 512, "GET /%s HTTP/1.0\n\n", host.host_addressing); } else { if((sock = cddb_connect(proxy)) < 0) return -1; snprintf(outbuffer, 512, "GET http://%s:%d/%s HTTP/1.0\n\n", host.host_server.server_name, host.host_server.server_port, host.host_addressing); } write(sock, outbuffer, strlen(outbuffer)); while(cdindex_read_line(sock, inbuffer, 512) >= 0) { if(strlen(inbuffer) <= 1) { break; } if(strchr(inbuffer, ' ') != NULL) { strtok(inbuffer, " "); if(strcmp(inbuffer, "Content-Type:") == 0) { strncpy(art->art_mime_type, strtok(NULL, " "), 16); if(art->art_mime_type[strlen(art->art_mime_type) - 1] == 0xD) art->art_mime_type[strlen(art->art_mime_type) - 1] = '\0'; } } } artptr = art->art_image; art->art_length = 0; while((bytes_read = read(sock, inbuffer, 512)) > 0) { if(art->art_length < DISC_ART_SIZE - bytes_read) { memcpy(artptr, inbuffer, bytes_read); art->art_length += bytes_read; artptr += bytes_read; } else { return -1; } } if(art->art_length > 0) art->art_present = 1; else return -1; return 0; } /* Read cover art from local cache */ int coverart_read_data(int cd_desc, struct art_data *art) { DIR *coverart_dir; int index, coverart_fd; char root_dir[256], procbuffer[256], id[CDINDEX_ID_SIZE]; struct stat st; struct dirent *d; if(getenv("HOME") == NULL) { strncpy(cddb_message, "$HOME is not set!", 256); return -1; } snprintf(root_dir, 256, "%s/.coverart", getenv("HOME")); cdindex_discid(cd_desc, id, CDINDEX_ID_SIZE); if(stat(root_dir, &st) < 0) { if(errno != ENOENT) return -1; else { art->art_present = 0; return 0; } } else if(!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return -1; } if((coverart_dir = opendir(root_dir)) == NULL) return -1; while((d = readdir(coverart_dir)) != NULL) { if(strncmp(id, d->d_name, strlen(id) - 1) == 0) { strncpy(procbuffer, d->d_name, 256); index = strlen(d->d_name); if(strchr(d->d_name, '.') != NULL) { while(d->d_name[index--] != '.'); index += 2; snprintf(art->art_mime_type, 16, "image/%s", d->d_name + index); art->art_mime_type[6 + (strlen(d->d_name) - index)] = '\0'; } else art->art_mime_type[0] = '\0'; snprintf(procbuffer, 256, "%s/%s", root_dir, d->d_name); if(stat(procbuffer, &st) < 0) return -1; art->art_length = st.st_size; if((coverart_fd = open(procbuffer, O_RDONLY)) < 0) return -1; if(read(coverart_fd, art->art_image, art->art_length) < 0) return -1; art->art_present = 1; return 0; } } art->art_present = 0; return 0; } /* Write cover art to local cache */ int coverart_write_data(int cd_desc, struct art_data art) { int coverart_fd; char root_dir[256], file[256], id[CDINDEX_ID_SIZE], extension[16], procbuffer[16]; struct stat st; if(!art.art_present) return 0; if(getenv("HOME") == NULL) { strncpy(cddb_message, "$HOME is not set!", 256); return -1; } memset(extension, '\0', 16); memset(file, '\0', 256); cdindex_discid(cd_desc, id, CDINDEX_ID_SIZE); strncpy(procbuffer, art.art_mime_type, 16); if(strchr(procbuffer, '/') != NULL) { strtok(procbuffer, "/"); strncpy(extension, strtok(NULL, "/"), 16); } else strncpy(extension, procbuffer, 16); snprintf(root_dir, 256, "%s/.coverart", getenv("HOME")); snprintf(file, 256, "%s/%s.%s", root_dir, id, extension); if(stat(root_dir, &st) < 0) { if(errno != ENOENT) return -1; else mkdir(root_dir, 0755); } else if(!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return -1; } if((coverart_fd = creat(file, 0644)) < 0) return -1; if(write(coverart_fd, art.art_image, art.art_length) < 0) return -1; return 0; } /* Erase a cover art entry directly */ int coverart_direct_erase_data(char *cdindex_id, struct art_data *art) { char *filename; if(getenv("HOME") == NULL) { strncpy(cddb_message, "$HOME is not set!", 256); return -1; } if((filename = malloc(108)) == NULL) return -1; if(strchr(art->art_mime_type, '/') == NULL) return -1; snprintf(filename, 108, "%s/.coverart/%s.%s", getenv("HOME"), cdindex_id, strchr(art->art_mime_type, '/') + 1); if(unlink(filename) < 0) { free(filename); return -1; } free(filename); return 0; } int coverart_erase_data(int cd_desc) { struct art_data art; char cdindex_id[CDINDEX_ID_SIZE]; if(cdindex_discid(cd_desc, cdindex_id, CDINDEX_ID_SIZE) < 0) return -1; if(coverart_read_data(cd_desc, &art) < 0) return -1; if(!art.art_present) return 0; if(coverart_direct_erase_data(cdindex_id, &art) < 0) return -1; return 0; }