#include <time.h>
#include <unistd.h>
+#include <sys/stat.h>
+
#include "common.h"
#include <assert.h>
return buf;
}
+// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample
+// code at
+// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
+
+int do_mkdir(const char *path, mode_t mode) {
+ struct stat st;
+ int status = 0;
+
+ if (stat(path, &st) != 0) {
+ /* Directory does not exist. EEXIST for race condition */
+ if (mkdir(path, mode) != 0 && errno != EEXIST)
+ status = -1;
+ } else if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ status = -1;
+ }
+
+ return (status);
+}
+
+// mkpath - ensure all directories in path exist
+// Algorithm takes the pessimistic view and works top-down to ensure
+// each directory in path exists, rather than optimistically creating
+// the last element and working backwards.
+
+int mkpath(const char *path, mode_t mode) {
+ char *pp;
+ char *sp;
+ int status;
+ char *copypath = strdup(path);
+
+ status = 0;
+ pp = copypath;
+ while (status == 0 && (sp = strchr(pp, '/')) != 0) {
+ if (sp != pp) {
+ /* Neither root nor double slash in path */
+ *sp = '\0';
+ status = do_mkdir(copypath, mode);
+ *sp = '/';
+ }
+ pp = sp + 1;
+ }
+ if (status == 0)
+ status = do_mkdir(path, mode);
+ free(copypath);
+ return (status);
+}
+
uint8_t *base64_dec(char *input, int *outlen) {
// slight problem here is that Apple cut the padding off their challenges. We must restore it
// before passing it in to the decoder, it seems
* then you need a metadata hub,
* where everything is stored
* This file is part of Shairport Sync.
- * Copyright (c) Mike Brady 2017
+ * Copyright (c) Mike Brady 2017--2018
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "config.h"
+
+#include "common.h"
#include "dacp.h"
#include "metadata_hub.h"
+#ifdef HAVE_LIBMBEDTLS
+#include <mbedtls/md5.h>
+#endif
+
+#ifdef HAVE_LIBPOLARSSL
+#include <polarssl/md5.h>
+#endif
+
+#ifdef HAVE_LIBSSL
+#include <openssl/md5.h>
+#endif
+
void metadata_hub_init(void) {
debug(1, "Metadata bundle initialisation.");
memset(&metadata_store, 0, sizeof(metadata_store));
}
}
+void metadata_write_image_file(const char *buf, int len) {
+
+ uint8_t img_md5[16];
+// uint8_t ap_md5[16];
+
+#ifdef HAVE_LIBSSL
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, buf, len);
+ MD5_Final(img_md5, &ctx);
+#endif
+
+#ifdef HAVE_LIBMBEDTLS
+ mbedtls_md5_context tctx;
+ mbedtls_md5_starts(&tctx);
+ mbedtls_md5_update(&tctx, buf, len);
+ mbedtls_md5_finish(&tctx, img_md5);
+#endif
+
+#ifdef HAVE_LIBPOLARSSL
+ md5_context tctx;
+ md5_starts(&tctx);
+ md5_update(&tctx, buf, len);
+ md5_finish(&tctx, img_md5);
+#endif
+
+ char img_md5_str[33];
+ memset(img_md5_str, 0, sizeof(img_md5_str));
+ char *ext;
+ char png[] = "png";
+ char jpg[] = "jpg";
+ int i;
+ for (i = 0; i < 16; i++)
+ sprintf(&img_md5_str[i * 2], "%02x", (uint8_t)img_md5[i]);
+ // see if the file is a jpeg or a png
+ if (strncmp(buf, "\xFF\xD8\xFF", 3) == 0)
+ ext = jpg;
+ else if (strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0)
+ ext = png;
+ else {
+ debug(1, "Unidentified image type of cover art -- jpg extension used.");
+ ext = jpg;
+ }
+
+ int result = mkpath(config.cover_art_cache_dir, 0700);
+ if ((result == 0) || (result == -EEXIST)) {
+ debug(1, "Cover art cache directory okay");
+ // see if the file exists by opening it.
+ // if it exists, we're done
+ char *prefix = "cover-";
+
+ size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) + 1 +
+ strlen(ext);
+
+ char *path = malloc(pl + 1);
+ snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext);
+ int cover_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ if (cover_fd > 0) {
+ // write the contents
+ if (write(cover_fd, buf, len) < len) {
+ warn("Writing cover art file \"%s\" failed!", path);
+ }
+ close(cover_fd);
+ free(path);
+ } else {
+ if (errno == EEXIST)
+ debug(1, "Cover art file \"%s\" already exists!", path);
+ else
+ warn("Could not open file \"%s\" for writing cover art", path);
+ free(path);
+ }
+ } else {
+ debug(1, "Couldn't access or create the cover art cache directory \"%s\".",
+ config.cover_art_cache_dir);
+ }
+}
+
void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t length) {
// metadata coming in from the audio source or from Shairport Sync itself passes through here
// this has more information about tags, which might be relevant:
}
break;
case 'ascm':
- if ((metadata_store.comment == NULL) ||
- (strncmp(metadata_store.comment, data, length) != 0)) {
+ if ((metadata_store.comment == NULL) || (strncmp(metadata_store.comment, data, length) != 0)) {
if (metadata_store.comment)
free(metadata_store.comment);
metadata_store.comment = strndup(data, length);
}
break;
case 'asgn':
- if ((metadata_store.genre == NULL) ||
- (strncmp(metadata_store.genre, data, length) != 0)) {
+ if ((metadata_store.genre == NULL) || (strncmp(metadata_store.genre, data, length) != 0)) {
if (metadata_store.genre)
free(metadata_store.genre);
metadata_store.genre = strndup(data, length);
}
break;
case 'assn':
- if ((metadata_store.sort_as == NULL) ||
- (strncmp(metadata_store.sort_as, data, length) != 0)) {
+ if ((metadata_store.sort_as == NULL) || (strncmp(metadata_store.sort_as, data, length) != 0)) {
if (metadata_store.sort_as)
free(metadata_store.sort_as);
metadata_store.sort_as = strndup(data, length);
break;
case 'PICT':
debug(1, "MH Picture received, length %u bytes.", length);
+ if (length > 16)
+ metadata_write_image_file(data, length);
break;
case 'clip':
if ((metadata_store.client_ip == NULL) ||
metadata_store.changed = 1;
}
break;
-
+
default:
- if (type == 'ssnc')
- {
+ if (type == 'ssnc') {
char typestring[5];
*(uint32_t *)typestring = htonl(type);
typestring[4] = 0;
set_requested_connection_state_to_output(1);
}
-// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample
-// code at
-// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
-
-int do_mkdir(const char *path, mode_t mode) {
- struct stat st;
- int status = 0;
-
- if (stat(path, &st) != 0) {
- /* Directory does not exist. EEXIST for race condition */
- if (mkdir(path, mode) != 0 && errno != EEXIST)
- status = -1;
- } else if (!S_ISDIR(st.st_mode)) {
- errno = ENOTDIR;
- status = -1;
- }
-
- return (status);
-}
-
-// mkpath - ensure all directories in path exist
-// Algorithm takes the pessimistic view and works top-down to ensure
-// each directory in path exists, rather than optimistically creating
-// the last element and working backwards.
-
-int mkpath(const char *path, mode_t mode) {
- char *pp;
- char *sp;
- int status;
- char *copypath = strdup(path);
-
- status = 0;
- pp = copypath;
- while (status == 0 && (sp = strchr(pp, '/')) != 0) {
- if (sp != pp) {
- /* Neither root nor double slash in path */
- *sp = '\0';
- status = do_mkdir(copypath, mode);
- *sp = '/';
- }
- pp = sp + 1;
- }
- if (status == 0)
- status = do_mkdir(path, mode);
- free(copypath);
- return (status);
-}
-
char *get_version_string() {
char *version_string = malloc(200);
if (version_string) {
config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if
// nothing else comes in first.
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
+ config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
config_setting_t *setting;
const char *str = 0;