Blame src/cache.c

Packit 3ae693
/*-*- Mode: C; c-basic-offset: 8 -*-*/
Packit 3ae693
Packit 3ae693
/***
Packit 3ae693
  This file is part of libcanberra.
Packit 3ae693
Packit 3ae693
  Copyright 2008 Lennart Poettering
Packit 3ae693
Packit 3ae693
  libcanberra is free software; you can redistribute it and/or modify
Packit 3ae693
  it under the terms of the GNU Lesser General Public License as
Packit 3ae693
  published by the Free Software Foundation, either version 2.1 of the
Packit 3ae693
  License, or (at your option) any later version.
Packit 3ae693
Packit 3ae693
  libcanberra is distributed in the hope that it will be useful, but
Packit 3ae693
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ae693
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit 3ae693
  Lesser General Public License for more details.
Packit 3ae693
Packit 3ae693
  You should have received a copy of the GNU Lesser General Public
Packit 3ae693
  License along with libcanberra. If not, see
Packit 3ae693
  <http://www.gnu.org/licenses/>.
Packit 3ae693
***/
Packit 3ae693
Packit 3ae693
#ifdef HAVE_CONFIG_H
Packit 3ae693
#include <config.h>
Packit 3ae693
#endif
Packit 3ae693
Packit 3ae693
#include <sys/stat.h>
Packit 3ae693
#include <sys/types.h>
Packit 3ae693
#include <signal.h>
Packit 3ae693
#include <fcntl.h>
Packit 3ae693
#include <unistd.h>
Packit 3ae693
#include <pthread.h>
Packit 3ae693
#include <errno.h>
Packit 3ae693
Packit 3ae693
#include <tdb.h>
Packit 3ae693
Packit 3ae693
#include "malloc.h"
Packit 3ae693
#include "macro.h"
Packit 3ae693
#include "mutex.h"
Packit 3ae693
#include "canberra.h"
Packit 3ae693
#include "sound-theme-spec.h"
Packit 3ae693
#include "cache.h"
Packit 3ae693
Packit 3ae693
#define FILENAME "event-sound-cache.tdb"
Packit 3ae693
#define UPDATE_INTERVAL 10
Packit 3ae693
Packit 3ae693
/* This part is not portable due to pthread_once usage, should be abstracted
Packit 3ae693
 * when we port this to platforms that do not have POSIX threading */
Packit 3ae693
Packit 3ae693
static ca_mutex *mutex = NULL;
Packit 3ae693
static struct tdb_context *database = NULL;
Packit 3ae693
Packit 3ae693
static void allocate_mutex_once(void) {
Packit 3ae693
        mutex = ca_mutex_new();
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int allocate_mutex(void) {
Packit 3ae693
        static pthread_once_t once = PTHREAD_ONCE_INIT;
Packit 3ae693
Packit 3ae693
        if (pthread_once(&once, allocate_mutex_once) != 0)
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        if (!mutex)
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        return 0;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int get_cache_home(char **e) {
Packit 3ae693
        const char *env, *subdir;
Packit 3ae693
        char *r;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(e, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((env = getenv("XDG_CACHE_HOME")) && *env == '/')
Packit 3ae693
                subdir = "";
Packit 3ae693
        else if ((env = getenv("HOME")) && *env == '/')
Packit 3ae693
                subdir = "/.cache";
Packit 3ae693
        else {
Packit 3ae693
                *e = NULL;
Packit 3ae693
                return CA_SUCCESS;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (!(r = ca_new(char, strlen(env) + strlen(subdir) + 1)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        sprintf(r, "%s%s", env, subdir);
Packit 3ae693
        *e = r;
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int sensible_gethostbyname(char *n, size_t l) {
Packit 3ae693
Packit 3ae693
        if (gethostname(n, l) < 0)
Packit 3ae693
                return -1;
Packit 3ae693
Packit 3ae693
        n[l-1] = 0;
Packit 3ae693
Packit 3ae693
        if (strlen(n) >= l-1) {
Packit 3ae693
                errno = ENAMETOOLONG;
Packit 3ae693
                return -1;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (!n[0]) {
Packit 3ae693
                errno = ENOENT;
Packit 3ae693
                return -1;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return 0;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int get_machine_id(char **id) {
Packit 3ae693
        FILE *f;
Packit 3ae693
        size_t l;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(id, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        /* First we try the D-Bus machine id */
Packit 3ae693
Packit 3ae693
        if ((f = fopen(CA_MACHINE_ID, "r"))) {
Packit 3ae693
                char ln[34] = "", *r;
Packit 3ae693
Packit 3ae693
                r = fgets(ln, sizeof(ln)-1, f);
Packit 3ae693
                fclose(f);
Packit 3ae693
Packit 3ae693
                if (r) {
Packit 3ae693
                        ln[strcspn(ln, " \n\r\t")] = 0;
Packit 3ae693
Packit 3ae693
                        if (!(*id = ca_strdup(ln)))
Packit 3ae693
                                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
                        return CA_SUCCESS;
Packit 3ae693
                }
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* Then we try the host name */
Packit 3ae693
Packit 3ae693
        l = 100;
Packit 3ae693
Packit 3ae693
        for (;;) {
Packit 3ae693
                if (!(*id = ca_new(char, l)))
Packit 3ae693
                        return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
                if (sensible_gethostbyname(*id, l) >= 0)
Packit 3ae693
                        return CA_SUCCESS;
Packit 3ae693
Packit 3ae693
                ca_free(*id);
Packit 3ae693
Packit 3ae693
                if (errno != EINVAL && errno != ENAMETOOLONG)
Packit 3ae693
                        break;
Packit 3ae693
Packit 3ae693
                l *= 2;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* Then we use the POSIX host id */
Packit 3ae693
Packit 3ae693
        *id = ca_sprintf_malloc("%08lx", (unsigned long) gethostid());
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int db_open(void) {
Packit 3ae693
        int ret;
Packit 3ae693
        char *c, *id, *pn;
Packit 3ae693
Packit 3ae693
        if ((ret = allocate_mutex()) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(mutex);
Packit 3ae693
Packit 3ae693
        if (database) {
Packit 3ae693
                ret = CA_SUCCESS;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((ret = get_cache_home(&c)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        if (!c) {
Packit 3ae693
                ret = CA_ERROR_NOTFOUND;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* Try to create, just in case it doesn't exist yet. We don't do
Packit 3ae693
         * this recursively however. */
Packit 3ae693
        mkdir(c, 0755);
Packit 3ae693
Packit 3ae693
        if ((ret = get_machine_id(&id)) < 0) {
Packit 3ae693
                ca_free(c);
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* This data is machine specific, hence we include some kind of
Packit 3ae693
         * stable machine id here in the name. Also, we don't want to care
Packit 3ae693
         * abouth endianess/packing issues, hence we include the compiler
Packit 3ae693
         * target in the name, too. */
Packit 3ae693
Packit 3ae693
        pn = ca_sprintf_malloc("%s/" FILENAME ".%s." CANONICAL_HOST, c, id);
Packit 3ae693
        ca_free(c);
Packit 3ae693
        ca_free(id);
Packit 3ae693
Packit 3ae693
        if (!pn) {
Packit 3ae693
                ret = CA_ERROR_OOM;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* We pass TDB_NOMMAP here as long as rhbz 460851 is not fixed in
Packit 3ae693
         * tdb. */
Packit 3ae693
        database = tdb_open(pn, 0, TDB_NOMMAP, O_RDWR|O_CREAT|O_NOCTTY
Packit 3ae693
#ifdef O_CLOEXEC
Packit 3ae693
                            | O_CLOEXEC
Packit 3ae693
#endif
Packit 3ae693
                            , 0644);
Packit 3ae693
        ca_free(pn);
Packit 3ae693
Packit 3ae693
        if (!database) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ret = CA_SUCCESS;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
        ca_mutex_unlock(mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
#ifdef CA_GCC_DESTRUCTOR
Packit 3ae693
Packit 3ae693
static void db_close(void) CA_GCC_DESTRUCTOR;
Packit 3ae693
Packit 3ae693
static void db_close(void) {
Packit 3ae693
        /* Only here to make this valgrind clean */
Packit 3ae693
Packit 3ae693
        if (!getenv("VALGRIND"))
Packit 3ae693
                return;
Packit 3ae693
Packit 3ae693
        if (mutex) {
Packit 3ae693
                ca_mutex_free(mutex);
Packit 3ae693
                mutex = NULL;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (database) {
Packit 3ae693
                tdb_close(database);
Packit 3ae693
                database = NULL;
Packit 3ae693
        }
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
#endif
Packit 3ae693
Packit 3ae693
static int db_lookup(const void *key, size_t klen, void **data, size_t *dlen) {
Packit 3ae693
        int ret;
Packit 3ae693
        TDB_DATA k, d;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(data, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(dlen, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = db_open()) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        k.dptr = (void*) key;
Packit 3ae693
        k.dsize = klen;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(mutex);
Packit 3ae693
Packit 3ae693
        ca_assert(database);
Packit 3ae693
        d = tdb_fetch(database, k);
Packit 3ae693
        if (!d.dptr) {
Packit 3ae693
                ret = CA_ERROR_NOTFOUND;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        *data = d.dptr;
Packit 3ae693
        *dlen = d.dsize;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
        ca_mutex_unlock(mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int db_store(const void *key, size_t klen, const void *data, size_t dlen) {
Packit 3ae693
        int ret;
Packit 3ae693
        TDB_DATA k, d;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(data || dlen == 0, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = db_open()) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        k.dptr = (void*) key;
Packit 3ae693
        k.dsize = klen;
Packit 3ae693
Packit 3ae693
        d.dptr = (void*) data;
Packit 3ae693
        d.dsize = dlen;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(mutex);
Packit 3ae693
Packit 3ae693
        ca_assert(database);
Packit 3ae693
        if (tdb_store(database, k, d, TDB_REPLACE) < 0) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ret = CA_SUCCESS;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
        ca_mutex_unlock(mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int db_remove(const void *key, size_t klen) {
Packit 3ae693
        int ret;
Packit 3ae693
        TDB_DATA k;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(key, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(klen > 0, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = db_open()) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        k.dptr = (void*) key;
Packit 3ae693
        k.dsize = klen;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(mutex);
Packit 3ae693
Packit 3ae693
        ca_assert(database);
Packit 3ae693
        if (tdb_delete(database, k) < 0) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ret = CA_SUCCESS;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
        ca_mutex_unlock(mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static char *build_key(
Packit 3ae693
                const char *theme,
Packit 3ae693
                const char *name,
Packit 3ae693
                const char *locale,
Packit 3ae693
                const char *profile,
Packit 3ae693
                size_t *klen) {
Packit 3ae693
Packit 3ae693
        char *key, *k;
Packit 3ae693
        size_t tl, nl, ll, pl;
Packit 3ae693
Packit 3ae693
        tl = strlen(theme);
Packit 3ae693
        nl = strlen(name);
Packit 3ae693
        ll = strlen(locale);
Packit 3ae693
        pl = strlen(profile);
Packit 3ae693
        *klen = tl+1+nl+1+ll+1+pl+1;
Packit 3ae693
Packit 3ae693
        if (!(key = ca_new(char, *klen)))
Packit 3ae693
                return NULL;
Packit 3ae693
Packit 3ae693
        k = key;
Packit 3ae693
        strcpy(k, theme);
Packit 3ae693
        k += tl+1;
Packit 3ae693
        strcpy(k, name);
Packit 3ae693
        k += nl+1;
Packit 3ae693
        strcpy(k, locale);
Packit 3ae693
        k += ll+1;
Packit 3ae693
        strcpy(k, profile);
Packit 3ae693
Packit 3ae693
        return key;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
static int get_last_change(time_t *t) {
Packit 3ae693
        int ret;
Packit 3ae693
        char *e, *k;
Packit 3ae693
        struct stat st;
Packit 3ae693
        static time_t last_check = 0, last_change = 0;
Packit 3ae693
        time_t now;
Packit 3ae693
        const char *g;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(t, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if ((ret = allocate_mutex()) < 0)
Packit 3ae693
                return ret;
Packit 3ae693
Packit 3ae693
        ca_mutex_lock(mutex);
Packit 3ae693
Packit 3ae693
        ca_assert_se(time(&now) != (time_t) -1);
Packit 3ae693
Packit 3ae693
        if (now < last_check + UPDATE_INTERVAL) {
Packit 3ae693
                *t = last_change;
Packit 3ae693
                ret = CA_SUCCESS;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((ret = ca_get_data_home(&e)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        *t = 0;
Packit 3ae693
Packit 3ae693
        if (e) {
Packit 3ae693
                if (!(k = ca_new(char, strlen(e) + sizeof("/sounds")))) {
Packit 3ae693
                        ca_free(e);
Packit 3ae693
                        ret = CA_ERROR_OOM;
Packit 3ae693
                        goto finish;
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
                sprintf(k, "%s/sounds", e);
Packit 3ae693
                ca_free(e);
Packit 3ae693
Packit 3ae693
                if (stat(k, &st) >= 0)
Packit 3ae693
                        *t = st.st_mtime;
Packit 3ae693
Packit 3ae693
                ca_free(k);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        g = ca_get_data_dirs();
Packit 3ae693
Packit 3ae693
        for (;;) {
Packit 3ae693
                size_t j = strcspn(g, ":");
Packit 3ae693
Packit 3ae693
                if (g[0] == '/' && j > 0) {
Packit 3ae693
Packit 3ae693
                        if (!(k = ca_new(char, j + sizeof("/sounds")))) {
Packit 3ae693
                                ret = CA_ERROR_OOM;
Packit 3ae693
                                goto finish;
Packit 3ae693
                        }
Packit 3ae693
Packit 3ae693
                        memcpy(k, g, j);
Packit 3ae693
                        strcpy(k+j, "/sounds");
Packit 3ae693
Packit 3ae693
                        if (stat(k, &st) >= 0)
Packit 3ae693
                                if (st.st_mtime >= *t)
Packit 3ae693
                                        *t = st.st_mtime;
Packit 3ae693
Packit 3ae693
                        ca_free(k);
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
                if (g[j] == 0)
Packit 3ae693
                        break;
Packit 3ae693
Packit 3ae693
                g += j+1;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        last_change = *t;
Packit 3ae693
        last_check = now;
Packit 3ae693
Packit 3ae693
        ret = 0;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        ca_mutex_unlock(mutex);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_cache_lookup_sound(
Packit 3ae693
                ca_sound_file **f,
Packit 3ae693
                ca_sound_file_open_callback_t sfopen,
Packit 3ae693
                char **sound_path,
Packit 3ae693
                const char *theme,
Packit 3ae693
                const char *name,
Packit 3ae693
                const char *locale,
Packit 3ae693
                const char *profile) {
Packit 3ae693
Packit 3ae693
        char *key = NULL;
Packit 3ae693
        void *data = NULL;
Packit 3ae693
        size_t klen, dlen;
Packit 3ae693
        int ret;
Packit 3ae693
        uint32_t timestamp;
Packit 3ae693
        time_t last_change, now;
Packit 3ae693
        ca_bool_t remove_entry = FALSE;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(f, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(sfopen, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(theme, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(name && *name, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(locale, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(profile, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (sound_path)
Packit 3ae693
                *sound_path = NULL;
Packit 3ae693
Packit 3ae693
        if (!(key = build_key(theme, name, locale, profile, &klen)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        ret = db_lookup(key, klen, &data, &dlen);
Packit 3ae693
Packit 3ae693
        if (ret < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        ca_assert(data);
Packit 3ae693
Packit 3ae693
        if (dlen < sizeof(uint32_t) ||
Packit 3ae693
            (dlen > sizeof(uint32_t) && ((char*) data)[dlen-1] != 0)) {
Packit 3ae693
Packit 3ae693
                /* Corrupt entry */
Packit 3ae693
                ret = CA_ERROR_NOTFOUND;
Packit 3ae693
                remove_entry = TRUE;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        memcpy(&timestamp, data, sizeof(timestamp));
Packit 3ae693
Packit 3ae693
        if ((ret = get_last_change(&last_change)) < 0)
Packit 3ae693
                goto finish;
Packit 3ae693
Packit 3ae693
        ca_assert_se(time(&now) != (time_t) -1);
Packit 3ae693
Packit 3ae693
        /* Hmm, is the entry older than the last change to our sound theme
Packit 3ae693
         * dirs? Also, check for clock skews */
Packit 3ae693
        if ((time_t) timestamp < last_change || ((time_t) timestamp > now)) {
Packit 3ae693
                remove_entry = TRUE;
Packit 3ae693
                ret = CA_ERROR_NOTFOUND;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (dlen <= sizeof(uint32_t)) {
Packit 3ae693
                /* Negative caching entry. */
Packit 3ae693
                *f = NULL;
Packit 3ae693
                ret = CA_SUCCESS;
Packit 3ae693
                goto finish;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (sound_path) {
Packit 3ae693
                if (!(*sound_path = ca_strdup((const char*) data + sizeof(uint32_t)))) {
Packit 3ae693
                        ret = CA_ERROR_OOM;
Packit 3ae693
                        goto finish;
Packit 3ae693
                }
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if ((ret = sfopen(f, (const char*) data + sizeof(uint32_t))) < 0)
Packit 3ae693
                remove_entry = TRUE;
Packit 3ae693
Packit 3ae693
finish:
Packit 3ae693
Packit 3ae693
        if (remove_entry)
Packit 3ae693
                db_remove(key, klen);
Packit 3ae693
Packit 3ae693
        if (sound_path && ret < 0)
Packit 3ae693
                ca_free(*sound_path);
Packit 3ae693
Packit 3ae693
        ca_free(key);
Packit 3ae693
        ca_free(data);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_cache_store_sound(
Packit 3ae693
                const char *theme,
Packit 3ae693
                const char *name,
Packit 3ae693
                const char *locale,
Packit 3ae693
                const char *profile,
Packit 3ae693
                const char *fname) {
Packit 3ae693
Packit 3ae693
        char *key;
Packit 3ae693
        void *data;
Packit 3ae693
        size_t klen, dlen;
Packit 3ae693
        int ret;
Packit 3ae693
        time_t now;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(theme, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(name && *name, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(locale, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(profile, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(key = build_key(theme, name, locale, profile, &klen)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        dlen = sizeof(uint32_t) + (fname ? strlen(fname) + 1 : 0);
Packit 3ae693
Packit 3ae693
        if (!(data = ca_malloc(dlen))) {
Packit 3ae693
                ca_free(key);
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        ca_assert_se(time(&now) != (time_t) -1);
Packit 3ae693
        *(uint32_t*) data = (uint32_t) now;
Packit 3ae693
Packit 3ae693
        if (fname)
Packit 3ae693
                strcpy((char*) data + sizeof(uint32_t), fname);
Packit 3ae693
Packit 3ae693
        ret = db_store(key, klen, data, dlen);
Packit 3ae693
Packit 3ae693
        ca_free(key);
Packit 3ae693
        ca_free(data);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}