]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'maint'
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 14 May 2022 15:21:42 +0000 (17:21 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sat, 14 May 2022 15:21:42 +0000 (17:21 +0200)
To get the single fix that was just merged to maint.

* maint:
  server: Fix memory leak at exit.

65 files changed:
Makefile.in
Makefile.real
NEWS.md
aac_afh.c
acl.c
afh.c
afh.h
afh_common.c
afh_recv.c
afs.c
aft.c
alsa_write.c
ao_write.c
attribute.c
audiod.c
buffer_tree.c
chunk_queue.c
client.c
client_common.c
close_on_fork.c
command.c
configure.ac
crypt.h
crypt_common.c
daemon.c
error.h
gcrypt.c
interactive.c
interactive.h
list.h
m4/lls/server.suite.m4
m4/lls/server_cmd.suite.m4
m4/lls/upgrade_db.suite.m4 [new file with mode: 0644]
mm.c [deleted file]
mm.h [deleted file]
mood.c
mp.c
mp.h
net.c
openssl.c
oss_write.c
play.c
sched.c
score.c
send_common.c
server.c
sync_filter.c
udp_send.c
upgrade_db.c [new file with mode: 0644]
vss.c
web/about.in.html
web/documentation.in.html
web/download.in.html
web/footer.html
web/header.html
web/header2.html
web/images/paraslash.ico
web/images/paraslash.png [deleted file]
web/images/paraslash.svg [new file with mode: 0644]
web/manual.md
web/para.css
write.h
write_common.c
yy/mp.lex
yy/mp.y

index d4a83a776eec0dfd602aa4329a72718cc15ea7f4..c618561d4dfeb18ebeb31f93a90ac69e32f84c63 100644 (file)
@@ -23,6 +23,7 @@ audiod_objs := @audiod_objs@
 audioc_objs := @audioc_objs@
 mixer_objs := @mixer_objs@
 server_objs := @server_objs@
+upgrade_db_objs := @upgrade_db_objs@
 write_objs := @write_objs@
 afh_objs := @afh_objs@
 play_objs := @play_objs@
@@ -67,4 +68,6 @@ curses_ldflags := @curses_ldflags@
 crypto_ldflags := @crypto_ldflags@
 iconv_ldflags := @iconv_ldflags@
 
+ENABLE_UBSAN := @ENABLE_UBSAN@
+
 include Makefile.real
index b88de22533048979bcfef8f3396b7608297afb3b..6e8084d542b8888e2a838671b6a9be845ac2cae2 100644 (file)
@@ -20,7 +20,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2021
+COPYRIGHT_YEAR := 2022
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -55,6 +55,7 @@ gui_objs += gui.lsg.o
 play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
 recv_objs += recv_cmd.lsg.o recv.lsg.o
 server_objs += server_cmd.lsg.o server.lsg.o
+upgrade_db_objs += upgrade_db.lsg.o
 write_objs += write_cmd.lsg.o write.lsg.o
 
 cmd_suites := $(addsuffix _cmd, audiod server play recv filter write)
@@ -77,6 +78,7 @@ audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs))
 audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs))
 mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs))
 server_objs := $(addprefix $(object_dir)/, $(server_objs))
+upgrade_db_objs := $(addprefix $(object_dir)/, $(upgrade_db_objs))
 write_objs := $(addprefix $(object_dir)/, $(write_objs))
 afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
 play_objs := $(addprefix $(object_dir)/, $(play_objs))
@@ -129,6 +131,11 @@ STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 
+ifeq ($(ENABLE_UBSAN), yes)
+       STRICT_CFLAGS += -fsanitize=undefined
+       LDFLAGS += -lubsan
+endif
+
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
        CPPFLAGS += -Wunused-macros
@@ -257,7 +264,7 @@ para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
 para_write para_play para_audiod \
 : LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
 para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
-para_server: LDFLAGS += $(osl_ldflags)
+para_server para_upgrade_db: LDFLAGS += $(osl_ldflags)
 para_gui: LDFLAGS += $(curses_ldflags)
 para_server \
 para_client \
@@ -294,6 +301,7 @@ para_gui \
 para_play \
 para_recv \
 para_server \
+para_upgrade_db \
 para_write \
 : LDFLAGS += $(lopsub_ldflags)
 
diff --git a/NEWS.md b/NEWS.md
index 7554cd6b9fac8ae061d8be75afa0f4f706007ef8..e9713d9826ff74e25aa01ceaad9a9db13f3113a5 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,52 @@
 NEWS
 ====
 
+-------------------------------------------
+0.7.1 (to be announced) "digital spindrift"
+-------------------------------------------
+
+[tarball](./releases/paraslash-git.tar.xz)
+
+----------------------------------
+0.7.0 (2022-03-12) "seismic orbit"
+----------------------------------
+
+The major incompatible change which requires to bump the major version
+is the switch from sha1 to sha256, see below for details. However,
+there are many other improvements, the usual amount of bug fixes and
+a couple of new features.
+
+- Starting with paraslash-0.7.0, the sha256 hash value of each known
+  audio file is stored in the database while older versions employed the
+  sha1 hash algorithm which has been considered insecure since 2005
+  and should no longer be used today. The switch from sha1 to sha256
+  requires users to upgrade their database using the new para_upgrade_db
+  program, followed by re-adding all files to recompute the hashes. With
+  this approach all metadata stored in the database (last played date,
+  num played value, moods, playlists, attributes etc.) are kept. An
+  simpler alternative is to start over from scratch by running the
+  "init" command but this will lose these metadata.
+- Server and client now hash the session keys with sha256 rather
+  than sha1 during the initial handshake. This feature is optional and
+  backwards compatible: old clients can still connect to a new server
+  (using sha1). Also new clients can connect to an old server (again
+  using sha1).
+- The new "duration" keyword of the mood grammar makes it possible to
+  impose a constraint on the duration of the admissible files.
+- The long deprecated version 1 mood syntax is no longer supported.
+- Paraslash writers handle early end-of-file more gracefully.
+- The alsa writer no longer warns about spurious underruns.
+- The score formula  of the audio file selector has been reworked.
+- Cleanups of the doubly linked lists code.
+- New option for configure: --enable-ubsan to detect and report undefined
+  behaviour.
+- The "tasks" server command has been removed.
+- The fancy new logo and a minor overhaul of the web pages.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.0.tar.xz),
+[signature](./releases/paraslash-0.7.0.tar.xz.asc)
+
 --------------------------------------
 0.6.4 (2021-11-04) "fuzzy calibration"
 --------------------------------------
index 2b3dd2cc538a57a233813b77adcb332bd77929ce..5c1225b62b2e179026dadf7638d4f44f041345cb 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
@@ -35,17 +35,13 @@ struct aac_afh_context {
 static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
 {
        struct aac_afh_context *c = user_data;
-       uint32_t have, rv;
+       size_t have, rv;
 
-       if (want == 0 || c->fpos >= c->mapsize) {
-               PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
-                       c->fpos);
-               errno = EAGAIN;
-               return -1;
-       }
+       if (want == 0 || c->fpos >= c->mapsize)
+               return 0;
        have = c->mapsize - c->fpos;
-       rv = PARA_MIN(have, want);
-       PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
+       rv = PARA_MIN(have, (size_t)want);
+       PARA_DEBUG_LOG("reading %zu bytes @%zu\n", rv, c->fpos);
        memcpy(dest, c->map + c->fpos, rv);
        c->fpos += rv;
        return rv;
@@ -130,8 +126,8 @@ static void aac_afh_close(void *afh_context)
  */
 int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
 
-static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
-               const char **buf, size_t *len)
+static int aac_afh_get_chunk(uint32_t chunk_num, void *afh_context,
+               const char **buf, uint32_t *len)
 {
        struct aac_afh_context *c = afh_context;
        int32_t ss;
@@ -170,8 +166,7 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        struct aac_afh_context *c;
        int64_t tmp;
        const char *buf;
-       size_t sz;
-       uint32_t n;
+       uint32_t n, len;
 
        ret = aac_afh_open(map, numbytes, (void **)&c);
        if (ret < 0)
@@ -196,16 +191,16 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        afhi->chunks_total = rv;
        afhi->max_chunk_size = 0;
        for (n = 0; n < afhi->chunks_total; n++) {
-               if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
+               if (aac_afh_get_chunk(n, c, &buf, &len) < 0)
                        break;
-               afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
+               afhi->max_chunk_size = PARA_MAX(afhi->max_chunk_size, len);
        }
 
        tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
        afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
        ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
 
-       if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
+       if (aac_afh_get_chunk(0, c, &buf, &len) >= 0)
                numbytes -= buf - map;
        afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
        _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
diff --git a/acl.c b/acl.c
index 2c90052658eda947ea586a55f292dc85f24e68f7..59ffab3e0a188a8ac921555b83a9276907ecfa46 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -101,7 +101,7 @@ static void acl_del_entry(struct list_head *acl, char *addr, unsigned netmask)
        struct access_info *ai, *tmp;
        struct in_addr to_delete;
 
-       PARA_NOTICE_LOG("removing entries matching %s/%u\n", addr, netmask);
+       PARA_INFO_LOG("removing entries matching %s/%u\n", addr, netmask);
        inet_pton(AF_INET, addr, &to_delete);
 
        list_for_each_entry_safe(ai, tmp, acl, node) {
@@ -111,7 +111,7 @@ static void acl_del_entry(struct list_head *acl, char *addr, unsigned netmask)
                        const char *p = inet_ntop(AF_INET, &ai->addr.s_addr,
                                dst, sizeof(dst));
                        if (p)
-                               PARA_INFO_LOG("removing %s/%u\n", p,
+                               PARA_DEBUG_LOG("removing %s/%u\n", p,
                                        ai->netmask);
                        list_del(&ai->node);
                        free(ai);
diff --git a/afh.c b/afh.c
index c896a7d1eff4a843e2e61a4ec8b6dac4982ed197..e419d2708d7224aaa5330515b11d45947c0d5fab 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -159,7 +159,7 @@ static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
                struct timeval tv;
                long unsigned from, to;
                const char *buf;
-               size_t len;
+               uint32_t len;
                tv_scale(i, &afhi->chunk_tv, &tv);
                from = tv2ms(&tv);
                tv_scale(i + 1, &afhi->chunk_tv, &tv);
@@ -177,7 +177,7 @@ static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
                printf("%td - %td", buf - (const char *)map,
                        buf + len - (const char *)map);
                if (!OPT_GIVEN(PARSER_FRIENDLY))
-                       printf(" (%zu)", len);
+                       printf(" (%u)", len);
                printf("\n");
        }
        afh_close(ctx, audio_format_id);
diff --git a/afh.h b/afh.h
index b3295f6e2fda111bd30ec5d20cd2431bbea325a6..ba72d80e5917b103e879ce221526d1a1a5e3fccc 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -111,8 +111,8 @@ struct audio_format_handler {
         * portion of the memory mapped audio file. The caller must not call
         * free() on it.
         */
-       int (*get_chunk)(long unsigned chunk_num, void *afh_context,
-               const char **buf, size_t *len);
+       int (*get_chunk)(uint32_t chunk_num, void *afh_context,
+               const char **buf, uint32_t *len);
        /** Deallocate the resources occupied by ->open(). */
        void (*close)(void *afh_context);
        /**
@@ -131,7 +131,7 @@ int compute_afhi(const char *path, char *data, size_t size,
 const char *audio_format_name(int);
 __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                uint8_t audio_format_id, const void *map, size_t mapsize,
-               const char **buf, size_t *len, void **afh_context);
+               const char **buf, uint32_t *len, void **afh_context);
 void afh_close(void *afh_context, uint8_t audio_format_id);
 int32_t afh_get_start_chunk(int32_t approx_chunk_num,
                const struct afh_info *afhi, uint8_t audio_format_id);
index a267f58b106b9f0df09d3a8dad99368c3c519401..7e8f63d22a2126494d53e94457278be06142d4f7 100644 (file)
@@ -219,7 +219,7 @@ void clear_afhi(struct afh_info *afhi)
        free(afhi->tags.comment);
 }
 
-static inline size_t get_chunk_len(long unsigned chunk_num,
+static inline uint32_t get_chunk_len(long unsigned chunk_num,
                const struct afh_info *afhi)
 {
        return afhi->chunk_table[chunk_num + 1] - afhi->chunk_table[chunk_num];
@@ -247,7 +247,7 @@ static inline size_t get_chunk_len(long unsigned chunk_num,
  */
 __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                uint8_t audio_format_id, const void *map, size_t mapsize,
-               const char **buf, size_t *len, void **afh_context)
+               const char **buf, uint32_t *len, void **afh_context)
 {
        struct audio_format_handler *afh = afl[audio_format_id];
 
index 4f8ff4974f018ef92e5055ac7a28bf337e3aa301..6a0ec239bbcbd120efd130b5f8f24c2b136d4516 100644 (file)
@@ -174,6 +174,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        char *buf;
        const char *start;
        size_t size;
+       uint32_t len;
        struct timeval chunk_time;
        unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
        unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
@@ -197,12 +198,12 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                long unsigned n;
                for (n = pard->first_chunk; n < pard->last_chunk; n++) {
                        ret = afh_get_chunk(n, afhi, pard->audio_format_num,
-                               pard->map, pard->map_size, &start, &size,
+                               pard->map, pard->map_size, &start, &len,
                                &pard->afh_context);
                        if (ret < 0)
                                goto out;
-                       PARA_DEBUG_LOG("adding %zu bytes\n", size);
-                       btr_add_output_dont_free(start, size, btrn);
+                       PARA_DEBUG_LOG("adding %u bytes\n", len);
+                       btr_add_output_dont_free(start, len, btrn);
                }
                ret = -E_RECV_EOF;
                goto out;
@@ -218,12 +219,12 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        }
        ret = afh_get_chunk(pard->current_chunk, afhi,
                pard->audio_format_num, pard->map,
-               pard->map_size, &start, &size,
+               pard->map_size, &start, &len,
                &pard->afh_context);
        if (ret < 0)
                goto out;
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
-       btr_add_output_dont_free(start, size, btrn);
+       btr_add_output_dont_free(start, len, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
                ret = -E_RECV_EOF;
                goto out;
diff --git a/afs.c b/afs.c
index 23ba2ad60cc9b133017ed006ce89ce2e91899808..710670255b2ec1cf67b9ab4e74823bfe19dd3a02 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -39,11 +39,10 @@ enum afs_table_num {
        TBLNUM_AUDIO_FILES,
        /** The table for the paraslash attributes. See \ref attribute.c. */
        TBLNUM_ATTRIBUTES,
-       /**
-        * Paraslash's scoring system is based on Gaussian normal
-        * distributions, and the relevant data is stored in the rbtrees of an
-        * osl table containing only volatile columns. See \ref score.c for
-        * details.
+       /*
+        * Moods and playlists organize the current set of admissible files in
+        * an osl table which contains only volatile columns. Each row consists
+        * of a pointer to an audio file and the score value of this file.
         */
        TBLNUM_SCORES,
        /**
@@ -91,26 +90,26 @@ static char *current_mop; /* mode or playlist specifier. NULL means dummy mood *
 extern uint32_t afs_socket_cookie;
 
 /**
- * Struct to let command handlers execute a callback in afs context.
- *
- * Commands that need to change the state of afs can't change the relevant data
- * structures directly because commands are executed in a child process, i.e.
- * they get their own virtual address space.
+ * Passed from command handlers to afs.
  *
- * This structure is used by \p send_callback_request() (executed from handler
- * context) in order to let the afs process call the specified function. An
- * instance of that structure is written to a shared memory area together with
- * the arguments to the callback function. The identifier of the shared memory
- * area is written to the command socket.
+ * Command handlers cannot change the afs database directly because they run in
+ * a separate process. The callback query structure circumvents this
+ * restriction as follows. To instruct the afs process to execute a particular
+ * function, the command hander writes an instance of this structure to a
+ * shared memory area, along with the arguments to the callback function. The
+ * identifier of the shared memory area is transferred to the afs process via
+ * the command socket.
  *
- * The afs process accepts connections on the command socket and reads the
- * shared memory id, attaches the corresponding area, calls the given handler to
- * perform the desired action and to optionally compute a result.
+ * The afs process reads the shared memory id from the command socket, attaches
+ * the corresponding area, and calls the callback function whose address is
+ * stored in the area.
  *
- * The result and a \p callback_result structure is then written to another
- * shared memory area. The identifier for that area is written to the handler's
- * command socket, so that the handler process can read the id, attach the
- * shared memory area and use the result.
+ * The command output, if any, is transferred back to the command handler in
+ * the same way: The afs process writes the output to a second shared memory
+ * area together with a fixed size metadata header whose format corresponds to
+ * the \ref callback_result structure. The identifier of this area is sent back
+ * to the command handler which attaches the area and forwards the output to
+ * the remote client.
  *
  * \sa \ref struct callback_result.
  */
@@ -482,6 +481,12 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible,
        if (num_admissible)
                *num_admissible = ret;
        current_play_mode = mode;
+       /*
+        * We get called with arg == current_mop from the signal dispatcher
+        * after SIGHUP and from the error path of the select command to
+        * re-select the current mood or playlist. In this case the assignment
+        * to current_mop below would result in a use-after-free condition.
+        */
        if (arg != current_mop) {
                free(current_mop);
                if (arg) {
@@ -648,7 +653,7 @@ static char *database_dir;
 static void close_afs_tables(void)
 {
        int i;
-       PARA_NOTICE_LOG("closing afs_tables\n");
+       PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].close();
        free(database_dir);
@@ -663,7 +668,7 @@ static void get_database_dir(void)
                else {
                        char *home = para_homedir();
                        database_dir = make_message(
-                               "%s/.paraslash/afs_database-0.4", home);
+                               "%s/.paraslash/afs_database-0.7", home);
                        free(home);
                }
        }
@@ -985,7 +990,7 @@ __noreturn void afs_init(int socket_fd)
        int i, ret;
 
        register_signal_task(&s);
-       INIT_LIST_HEAD(&afs_client_list);
+       init_list_head(&afs_client_list);
        for (i = 0; i < NUM_AFS_TABLES; i++)
                afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
diff --git a/aft.c b/aft.c
index c8c98e7ab679b5a5254eed9405afa3cca6cf870e..5f9098aa458955d1338bec6183c67493ce798cad 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -42,7 +42,7 @@ struct ls_data {
  */
 static struct osl_table *audio_file_table; /* NULL if table not open */
 static struct osl_row *current_aft_row; /* NULL if no audio file open */
-static unsigned char current_hash[HASH_SIZE]; /* only used on sighup */
+static unsigned char current_hash[HASH2_SIZE]; /* only used on sighup */
 
 static char *status_items;
 static char *parser_friendly_status_items;
@@ -226,7 +226,7 @@ static struct osl_column_description aft_cols[] = {
                .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
                .name = "hash",
                .compare_function = aft_hash_compare,
-               .data_size = HASH_SIZE
+               .data_size = HASH2_SIZE
        },
        [AFTCOL_PATH] = {
                .storage_type = OSL_MAPPED_STORAGE,
@@ -251,7 +251,7 @@ static struct osl_column_description aft_cols[] = {
 };
 
 static struct osl_table_description audio_file_table_desc = {
-       .name = "audio_files",
+       .name = "audio-files",
        .num_columns = NUM_AFT_COLUMNS,
        .flags = OSL_LARGE_TABLE,
        .column_descriptions = aft_cols
@@ -442,7 +442,7 @@ int aft_get_row_of_path(const char *path, struct osl_row **row)
  */
 static int aft_get_row_of_hash(unsigned char *hash, struct osl_row **row)
 {
-       const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
+       const struct osl_object obj = {.data = hash, .size = HASH2_SIZE};
        return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row));
 }
 
@@ -838,7 +838,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        char duration_buf[30]; /* nobody has an audio file long enough to overflow this */
        struct afs_info *afsi = &d->afsi;
        struct afh_info *afhi = &d->afhi;
-       char asc_hash[2 * HASH_SIZE + 1];
+       char asc_hash[2 * HASH2_SIZE + 1];
 
        if (opts->mode == LS_MODE_SHORT) {
                para_printf(b, "%s\n", d->path);
@@ -1041,7 +1041,7 @@ out:
  */
 int open_and_update_audio_file(int *fd)
 {
-       unsigned char file_hash[HASH_SIZE];
+       unsigned char file_hash[HASH2_SIZE];
        struct osl_object afsi_obj;
        struct afs_info new_afsi;
        int ret;
@@ -1101,7 +1101,7 @@ again:
        ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, fd);
        if (ret < 0)
                goto out;
-       hash_function(map.data, map.size, file_hash);
+       hash2_function(map.data, map.size, file_hash);
        ret = hash_compare(file_hash, d->hash);
        para_munmap(map.data, map.size);
        if (ret) {
@@ -1141,7 +1141,7 @@ out:
 static int ls_hash_compare(const void *a, const void *b)
 {
        struct ls_data *d1 = *(struct ls_data **)a, *d2 = *(struct ls_data **)b;
-       return memcmp(d1->hash, d2->hash, HASH_SIZE);
+       return memcmp(d1->hash, d2->hash, HASH2_SIZE);
 }
 
 static int ls_audio_format_compare(const void *a, const void *b)
@@ -1448,7 +1448,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
                        opts->mode = LS_MODE_PARSER;
                else {
-                       ret = -E_AFT_SYNTAX;
+                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
                        goto out;
                }
        }
@@ -1479,7 +1479,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                else if (!strcmp(val, "h") || !strcmp(val, "hash"))
                        opts->sorting = LS_SORT_BY_HASH;
                else {
-                       ret = -E_AFT_SYNTAX;
+                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
                        goto out;
                }
        }
@@ -1536,7 +1536,7 @@ enum com_add_buffer_offsets {
        /** The hash of the audio file being added. */
        CAB_HASH_OFFSET = 13,
        /** Start of the path of the audio file. */
-       CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH_SIZE),
+       CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH2_SIZE),
 };
 
 /*
@@ -1559,7 +1559,7 @@ static void save_add_callback_buffer(unsigned char *hash, const char *path,
 
        assert(size <= ~(uint32_t)0);
        write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
-       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+       memcpy(buf + CAB_HASH_OFFSET, hash, HASH2_SIZE);
        strcpy(buf + CAB_PATH_OFFSET, path);
        pos = CAB_PATH_OFFSET + path_len;
        write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
@@ -1635,7 +1635,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        struct osl_row *hs;
        struct osl_object objs[NUM_AFT_COLUMNS];
        unsigned char *hash;
-       char asc[2 * HASH_SIZE + 1];
+       char asc[2 * HASH2_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
        char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
@@ -1652,7 +1652,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
-       objs[AFTCOL_HASH].size = HASH_SIZE;
+       objs[AFTCOL_HASH].size = HASH2_SIZE;
 
        path = buf + CAB_PATH_OFFSET;
        objs[AFTCOL_PATH].data = path;
@@ -1715,7 +1715,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
        objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
        if (pb && !hs) { /* update pb's hash */
-               char old_asc[2 * HASH_SIZE + 1];
+               char old_asc[2 * HASH2_SIZE + 1];
                unsigned char *old_hash;
                ret = get_hash_of_row(pb, &old_hash);
                if (ret < 0)
@@ -1820,7 +1820,7 @@ static int add_one_audio_file(const char *path, void *private_data)
        struct afh_info afhi, *afhi_ptr = NULL;
        struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
        struct osl_object map, obj = {.data = NULL}, query;
-       unsigned char hash[HASH_SIZE];
+       unsigned char hash[HASH2_SIZE];
        bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
        bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
        bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
@@ -1848,11 +1848,11 @@ static int add_one_audio_file(const char *path, void *private_data)
        ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, &fd);
        if (ret < 0)
                goto out_free;
-       hash_function(map.data, map.size, hash);
+       hash2_function(map.data, map.size, hash);
 
        /* Check whether the database contains a file with the same hash. */
        query.data = hash;
-       query.size = HASH_SIZE;
+       query.size = HASH2_SIZE;
        ret = send_callback_request(hash_sister_callback, &query,
                get_row_pointer_from_result, &hs);
        if (ret < 0)
@@ -2517,7 +2517,7 @@ static void aft_close(void)
                        PARA_WARNING_LOG("hash lookup failure\n");
                        current_aft_row = NULL;
                } else
-                       memcpy(current_hash, p, HASH_SIZE);
+                       memcpy(current_hash, p, HASH2_SIZE);
        }
        osl_close_table(audio_file_table, OSL_MARK_CLEAN);
        audio_file_table = NULL;
index bc06fc315bc51fb3fc1fa9729ae429b47a70d4b4..bbbf8b650ac86c4952ac46f22c166583018bfb88 100644 (file)
@@ -240,6 +240,8 @@ static void alsa_close(struct writer_node *wn)
 
        if (!pad)
                return;
+       if (!pad->handle)
+               goto free_pad;
        /*
         * It's OK to have a blocking operation here because we already made
         * sure that the PCM output buffer is (nearly) empty.
@@ -248,6 +250,7 @@ static void alsa_close(struct writer_node *wn)
        snd_pcm_drain(pad->handle);
        snd_pcm_close(pad->handle);
        snd_config_update_free_global();
+free_pad:
        free(pad);
 }
 
@@ -294,21 +297,23 @@ again:
                if (bytes == 0) /* no data available */
                        return 0;
                pad = wn->private_data = para_calloc(sizeof(*pad));
-               get_btr_sample_rate(btrn, &val);
+               ret = get_btr_sample_rate(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_rate = val;
-               get_btr_channels(btrn, &val);
+               ret = get_btr_channels(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->channels = val;
-               get_btr_sample_format(btrn, &val);
+               ret = get_btr_sample_format(btrn, &val);
+               if (ret < 0)
+                       goto err;
                pad->sample_format = get_alsa_pcm_format(val);
-
                PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
                        pad->sample_rate);
                ret = alsa_init(wn);
-               if (ret < 0) {
-                       free(wn->private_data);
-                       wn->private_data = NULL;
+               if (ret < 0)
                        goto err;
-               }
                wn->min_iqs = pad->bytes_per_frame;
                goto again;
        }
@@ -326,7 +331,11 @@ again:
                goto again;
        }
        if (frames == -EPIPE) {
-               PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
+               snd_pcm_status_t *status;
+               snd_pcm_status_malloc(&status);
+               if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN)
+                       PARA_WARNING_LOG("underrun\n");
+               snd_pcm_status_free(status);
                snd_pcm_prepare(pad->handle);
                return 0;
        }
index 447dea84c00a468330fe8229bfd70b74292264bd..037b92993325539552ddd625b881a843aa57c5a9 100644 (file)
@@ -357,9 +357,15 @@ static int aow_post_select(__a_unused struct sched *s, void *context)
                        goto remove_btrn;
                if (ret == 0)
                        return 0;
-               get_btr_sample_rate(wn->btrn, &rate);
-               get_btr_channels(wn->btrn, &ch);
-               get_btr_sample_format(wn->btrn, &format);
+               ret = get_btr_sample_rate(wn->btrn, &rate);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_channels(wn->btrn, &ch);
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = get_btr_sample_format(wn->btrn, &format);
+               if (ret < 0)
+                       goto remove_btrn;
                ret = aow_init(wn, rate, ch, format);
                if (ret < 0)
                        goto remove_btrn;
index 414a65e6526fabc08ccabaec07694423665b4442..fb1b3eac3482f1c0725e0e79a4b80166be511350 100644 (file)
@@ -104,7 +104,7 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum)
        return 1;
 }
 
-/** Data passed to the action function of lsatt */
+/* Data passed to the action function of lsatt */
 static int print_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
@@ -470,26 +470,12 @@ int attribute_check_callback(struct afs_callback_arg *aca)
        return aft_check_attributes(att_mask, &aca->pbout);
 }
 
-/**
- * Close the attribute table.
- *
- * \sa \ref osl_close_table().
- */
 static void attribute_close(void)
 {
        osl_close_table(attribute_table, OSL_MARK_CLEAN);
        attribute_table = NULL;
 }
 
-/**
- * Open the attribute table.
- *
- * \param dir The database directory.
- *
- * \return Positive on success, negative on errors.
- *
- * \sa \ref osl_open_table().
- */
 static int attribute_open(const char *dir)
 {
        int ret;
index 88599c3fa297337b6a3f598b31f92d6e6b4b0993..838f375fe6df2c2d96c4bfa2aa41d28e64c29f72 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -445,7 +445,8 @@ static void close_receiver(int slot_num)
        task_reap(&s->receiver_node->task);
        free(s->receiver_node);
        s->receiver_node = NULL;
-       stat_task->current_audio_format_num = -1;
+       if (audiod_status == AUDIOD_ON)
+               stat_task->current_audio_format_num = -1;
        tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
                &a->restart_barrier);
 }
@@ -479,6 +480,23 @@ static void close_writers(struct slot_info *s)
        s->wns = NULL;
 }
 
+static void notify_writers(int error)
+{
+       int i;
+
+       FOR_EACH_SLOT(i) {
+               struct slot_info *s = slot + i;
+               struct audio_format_info *a;
+               int j;
+
+               if (s->format < 0)
+                       continue;
+               a = afi + s->format;
+               for (j = 0; j < a->num_writers; j++)
+                       task_notify(s->wns[j].task, error);
+       }
+}
+
 static void close_filters(struct slot_info *s)
 {
        int i;
@@ -1217,8 +1235,9 @@ static void start_stop_decoders(void)
        struct slot_info *sl;
 
        close_unused_slots();
-       if (audiod_status != AUDIOD_ON ||
-                       !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
+       if (audiod_status != AUDIOD_ON)
+               return notify_writers(E_NOT_PLAYING);
+       if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
                return notify_receivers(E_NOT_PLAYING);
        if (!must_start_decoder())
                return;
index 8a3175133e69aa57eff7da5155341cbf05532d03..f0d2002d13c28b31c17f7130a4f3c34fc858bd58 100644 (file)
@@ -270,8 +270,8 @@ struct btr_node *btr_new_node(struct btr_node_description *bnd)
        btrn->context = bnd->context;
        btrn->start.tv_sec = 0;
        btrn->start.tv_usec = 0;
-       INIT_LIST_HEAD(&btrn->children);
-       INIT_LIST_HEAD(&btrn->input_queue);
+       init_list_head(&btrn->children);
+       init_list_head(&btrn->input_queue);
        if (!bnd->child) {
                if (bnd->parent) {
                        list_add_tail(&btrn->node, &bnd->parent->children);
index 08f57e9d1ecf9bae1cadead0757fe29418c446c3..cf74cc336de25caa593b198bcfd79a3166996381 100644 (file)
@@ -131,7 +131,7 @@ int cq_get(struct queued_chunk *qc, const char **buf, size_t *num_bytes)
 struct chunk_queue *cq_new(size_t max_pending)
 {
        struct chunk_queue *cq = para_malloc(sizeof(*cq));
-       INIT_LIST_HEAD(&cq->q);
+       init_list_head(&cq->q);
        cq->max_pending = max_pending;
        cq->num_pending = 0;
        return cq;
index 3edaab5dbbf2ec6f086a9483500e431069770b28..8caf44839919fb33c1368715c528f507ebc7a5c9 100644 (file)
--- a/client.c
+++ b/client.c
@@ -243,7 +243,6 @@ I9E_DUMMY_COMPLETER(term);
 I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(addatt);
 I9E_DUMMY_COMPLETER(init);
-I9E_DUMMY_COMPLETER(tasks);
 
 static struct i9e_completer completers[];
 
index c25da96b169126ab36f5b6e7f95a2161d19a3aa9..94a6e86544e63824664b114cc400161d4e223684 100644 (file)
@@ -258,6 +258,11 @@ static int send_sb_command(struct client_task *ct)
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
 }
 
+static bool has_feature(const char *feature, struct client_task *ct)
+{
+       return find_arg(feature, ct->features) >= 0? true : false;
+}
+
 /*
  * This function reads or writes to the socket file descriptor which
  * corresponds to an established connection between the client and the server.
@@ -290,16 +295,25 @@ static int client_post_select(struct sched *s, void *context)
                ct->status = CL_RECEIVED_WELCOME;
                return 0;
        case CL_RECEIVED_WELCOME: /* send auth command */
+               {
+               /*
+                * Use sha256 iff the server announced the feature. After 0.7.0
+                * we may request and use the feature unconditionally. After
+                * 0.8.0 we no longer need to request the feature.
+                */
+               bool has_sha256;
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
-                       ct->user);
+               has_sha256 = has_feature("sha256", ct);
+               sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
+                       " sha256" : "");
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                        goto out;
                ct->status = CL_SENT_AUTH;
                return 0;
+               }
        case CL_SENT_AUTH:
                /*
                 * Receive challenge and session keys, decrypt the challenge and
@@ -325,19 +339,29 @@ static int client_post_select(struct sched *s, void *context)
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
-               ct->challenge_hash = para_malloc(HASH_SIZE);
-               hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+               ct->challenge_hash = para_malloc(HASH2_SIZE);
+
+               if (has_feature("sha256", ct)) {
+                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash2_to_asc(ct->challenge_hash, buf);
+               } else {
+                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash_to_asc(ct->challenge_hash, buf);
+               }
                ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
                ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
                        SESSION_KEY_LEN);
-               hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
                }
        case CL_RECEIVED_CHALLENGE:
-               ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
-                       SBD_CHALLENGE_RESPONSE, false);
+               if (has_feature("sha256", ct))
+                       ret = send_sb(ct, 0, ct->challenge_hash, HASH2_SIZE,
+                               SBD_CHALLENGE_RESPONSE, false);
+               else
+                       ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
+                               SBD_CHALLENGE_RESPONSE, false);
                if (ret != 0)
                        ct->challenge_hash = NULL;
                if (ret <= 0)
index 28c5eabb1ae9a4821b60b638be559e837dc54f36..7b464d09cab0ed446aac327fbba6d32f9aadd7b3 100644 (file)
@@ -34,7 +34,7 @@ void add_close_on_fork_list(int fd)
        struct close_on_fork *cof = para_malloc(sizeof(struct close_on_fork));
 
        if (!initialized) {
-               INIT_LIST_HEAD(&close_on_fork_list);
+               init_list_head(&close_on_fork_list);
                initialized = 1;
        }
        cof->fd = fd;
index 7b3d6faf90849a92ecbafb98538f277cfef5b02a..5b17f116d53c93956faf60f88d903b233b7e9b69 100644 (file)
--- a/command.c
+++ b/command.c
@@ -6,10 +6,7 @@
 #include <sys/socket.h>
 #include <regex.h>
 #include <signal.h>
-#include <sys/types.h>
-#include <osl.h>
 #include <arpa/inet.h>
-#include <sys/un.h>
 #include <netdb.h>
 #include <lopsub.h>
 
@@ -22,7 +19,6 @@
 #include "command.h"
 #include "string.h"
 #include "afh.h"
-#include "afs.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
@@ -745,14 +741,6 @@ out:
 }
 EXPORT_SERVER_CMD_HANDLER(jmp);
 
-/* deprecated, does nothing */
-static int com_tasks(__a_unused struct command_context *cc,
-               __a_unused struct lls_parse_result *lpr)
-{
-       return 1;
-}
-EXPORT_SERVER_CMD_HANDLER(tasks);
-
 static void reset_signals(void)
 {
        para_sigaction(SIGCHLD, SIG_IGN);
@@ -762,7 +750,7 @@ static void reset_signals(void)
 }
 
 struct connection_features {
-       int dummy; /* none at the moment */
+       bool sha256_requested; /* can be removed after 0.7.0 */
 };
 
 static int parse_auth_request(char *buf, int len, const struct user **u,
@@ -787,11 +775,22 @@ static int parse_auth_request(char *buf, int len, const struct user **u,
                *p = '\0';
                p++;
                create_argv(p, ",", &features);
+               /*
+                * Still accept sideband and AES feature requests (as a no-op)
+                * because some 0.6.x clients request them. The two checks
+                * below may be removed after 0.7.1.
+                */
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
                                continue;
                        if (strcmp(features[i], "aes_ctr128") == 0)
                                continue;
+                       /*
+                        * ->sha256_requested can go away after 0.7.0 but the
+                        * check has to stay until 0.9.0.
+                        */
+                       if (strcmp(features[i], "sha256") == 0)
+                               cf->sha256_requested = true;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
@@ -889,7 +888,7 @@ int handle_connect(int fd)
 {
        int ret;
        unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
-       unsigned char challenge_hash[HASH_SIZE];
+       unsigned char challenge_hash[HASH2_SIZE];
        char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct;
@@ -905,7 +904,7 @@ int handle_connect(int fd)
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
                PACKAGE_VERSION ".\n"
-               "Features: sideband,aes_ctr128\n"
+               "Features: sha256\n" /* no longer announce this after 0.8.0 */
        );
        if (ret < 0)
                goto net_err;
@@ -953,11 +952,19 @@ int handle_connect(int fd)
         * of the random data.
         */
        ret = -E_BAD_AUTH;
-       if (numbytes != HASH_SIZE)
-               goto net_err;
-       hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
-       if (memcmp(challenge_hash, buf, HASH_SIZE))
-               goto net_err;
+       if (cf.sha256_requested) {
+               if (numbytes != HASH2_SIZE)
+                       goto net_err;
+               hash2_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
+               if (memcmp(challenge_hash, buf, HASH2_SIZE))
+                       goto net_err;
+       } else { /* old client. This can be removed after 0.7.0 */
+               if (numbytes != HASH_SIZE)
+                       goto net_err;
+               hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
+               if (memcmp(challenge_hash, buf, HASH_SIZE))
+                       goto net_err;
+       }
        /* auth successful */
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
index f518f89af154933ffaa64401ba1086b7e3f369c9..b817979f39ec79c141cd67eaaef6f49f559b5d21 100644 (file)
@@ -373,11 +373,16 @@ AC_CHECK_HEADER(samplerate.h, [], HAVE_SAMPLERATE=no)
 AC_CHECK_LIB([samplerate], [src_process], [], HAVE_SAMPLERATE=no)
 LIB_SUBST_FLAGS(samplerate)
 UNSTASH_FLAGS
+######################################################################### ubsan
+AC_ARG_ENABLE([ubsan], [AS_HELP_STRING(--enable-ubsan,
+       [Detect and report undefined behaviour.])],
+       [ENABLE_UBSAN=yes], [ENABLE_UBSAN=no])
+AC_SUBST(ENABLE_UBSAN)
 ######################################################################### server
 if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                test -n "$FLEX"; then
        build_server="yes"
-       executables="$executables server"
+       executables="$executables server upgrade_db"
        server_errlist_objs="
                server
                afh_common
@@ -391,7 +396,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                daemon
                http_send
                close_on_fork
-               mm
                crypt_common
                base64
                ipc
@@ -437,6 +441,17 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
 else
        build_server="no"
 fi
+############################################################# upgrade_db
+upgrade_db_objs='
+       crypt_common
+       exec
+       fd
+       string
+       upgrade_db
+       version
+       base64
+'
+AC_SUBST(upgrade_db_objs, add_dot_o($upgrade_db_objs))
 ############################################################# client
 if test -n "$CRYPTOLIB"; then
        build_client="yes"
@@ -855,6 +870,7 @@ id3 version 2 support: $HAVE_ID3TAG
 faad: $HAVE_FAAD
 audio format handlers: $audio_format_handlers
 
+exe: $executables
 para_server: $build_server
 para_gui: $build_gui
 para_mixer: $build_mixer
diff --git a/crypt.h b/crypt.h
index 9623a0035a3b1a89818bd99c3c09c1dac041519b..5ca6a54112b60f6d61b16485248fb28ff708df6b 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -200,3 +200,42 @@ void hash_to_asc(const unsigned char *hash, char *asc);
  * less than or equal to h2, respectively.
  */
 int hash_compare(const unsigned char *h1, const unsigned char *h2);
+
+/** Size of the hash value in bytes. */
+#define HASH2_SIZE 32
+
+/**
+ * Compute the hash2 of the given input data.
+ *
+ * \param data Pointer to the data to compute the hash value from.
+ * \param len The length of \a data in bytes.
+ * \param hash Result pointer.
+ *
+ * \a hash must point to an area at least \p HASH2_SIZE bytes large.
+ *
+ * \sa sha(3), openssl(1).
+ * */
+void hash2_function(const char *data, unsigned long len, unsigned char *hash);
+
+/**
+ * Convert a hash2 value to ascii format.
+ *
+ * \param hash the hash value.
+ * \param asc Result pointer.
+ *
+ * \a asc must point to an area of at least 2 * \p HASH2_SIZE + 1 bytes which
+ * will be filled by the function with the ascii representation of the hash
+ * value given by \a hash, and a terminating \p NULL byte.
+ */
+void hash2_to_asc(const unsigned char *hash, char *asc);
+
+/**
+ * Compare two version 2 hashes.
+ *
+ * \param h1 Pointer to the first hash value.
+ * \param h2 Pointer to the second hash value.
+ *
+ * \return 1, -1, or zero, depending on whether \a h1 is greater than,
+ * less than or equal to h2, respectively.
+ */
+int hash2_compare(const unsigned char *h1, const unsigned char *h2);
index ff24e356ab8d837f8d59d67a3c983d5264376db1..3a44dbdddcd1cf59108ba110c8d83c4fac11d609 100644 (file)
@@ -160,6 +160,31 @@ int hash_compare(const unsigned char *h1, const unsigned char *h2)
        return 0;
 }
 
+void hash2_to_asc(const unsigned char *hash, char *asc)
+{
+       int i;
+       const char hexchar[] = "0123456789abcdef";
+
+       for (i = 0; i < HASH2_SIZE; i++) {
+               asc[2 * i] = hexchar[hash[i] >> 4];
+               asc[2 * i + 1] = hexchar[hash[i] & 0xf];
+       }
+       asc[2 * HASH2_SIZE] = '\0';
+}
+
+int hash2_compare(const unsigned char *h1, const unsigned char *h2)
+{
+       int i;
+
+       for (i = 0; i < HASH2_SIZE; i++) {
+               if (h1[i] < h2[i])
+                       return -1;
+               if (h1[i] > h2[i])
+                       return 1;
+       }
+       return 0;
+}
+
 /**
  * Check header of an openssh private key and compute bignum offset.
  *
index a4e2f3193730d456ce9908eb55888cdb759154e0..dd5420a6eab92de518e3af60ea49d094ae5db6bb 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -58,9 +58,9 @@ static void daemon_set_default_log_colors(void)
 }
 
 /**
- * Set the color for one loglevel.
+ * Set the color for log messages of the given severity level.
  *
- * \param arg Must be of the form "ll:[fg [bg]] [attr]".
+ * \param arg Must be of the form "severity:[fg [bg]] [attr]".
  */
 void daemon_set_log_color_or_die(const char *arg)
 {
diff --git a/error.h b/error.h
index fe44ff5c50487744881ea1d21d1007528a2ecd70..cd5c20ef8f44501429ea8a9ab16b4bfe25d62b54 100644 (file)
--- a/error.h
+++ b/error.h
@@ -13,7 +13,6 @@
        PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
        PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \
        PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \
-       PARA_ERROR(AFT_SYNTAX, "audio file table syntax error"), \
        PARA_ERROR(ALSA, "alsa error"), \
        PARA_ERROR(ALSA_MIX_GET_VAL, "could not read control element state"), \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
        PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
        PARA_ERROR(OGG_EMPTY, "no ogg pages found"), \
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
-       PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
        PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \
        PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
  * 'E_') and gets later redefined to expand to the error text only
  */
 #define PARA_ERROR(err, msg) E_ ## err
+/**
+ * Numeric error codes.
+ *
+ * Public functions which can fail return the negated value of one of the
+ * constants defined here to indicate the cause of the error.
+ *
+ * \sa \ref para_strerror().
+ */
 enum para_error_codes {PARA_ERRORS};
 #undef PARA_ERROR
 #define PARA_ERROR(err, msg) msg
index dbe4900862fef83a552d9c60d9ac21d4c8253d5e..506f0bb84e6c199d065942553935dc27a8833d93 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -46,6 +46,22 @@ void hash_function(const char *data, unsigned long len, unsigned char *hash)
        gcry_md_close(handle);
 }
 
+void hash2_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       gcry_error_t gret;
+       gcry_md_hd_t handle;
+       unsigned char *md;
+
+       gret = gcry_md_open(&handle, GCRY_MD_SHA256, 0);
+       assert(gret == 0);
+       gcry_md_write(handle, data, (size_t)len);
+       gcry_md_final(handle);
+       md = gcry_md_read(handle, GCRY_MD_SHA256);
+       assert(md);
+       memcpy(hash, md, HASH2_SIZE);
+       gcry_md_close(handle);
+}
+
 void get_random_bytes_or_die(unsigned char *buf, int num)
 {
        gcry_randomize(buf, (size_t)num, GCRY_STRONG_RANDOM);
index a8197308e8323c8fe78546117cc2e9479df0fbfd..8c4545b476e8f792ea7bba6f88b4e83fb952e540 100644 (file)
@@ -45,7 +45,7 @@ static struct i9e_private i9e_private, *i9ep = &i9e_private;
  * running.
  *
  * \return A negative return value of zero means the i9e task terminated. Only
- * in this case it is safe to call ie9_close().
+ * in this case it is safe to call i9e_close().
  */
 int i9e_get_error(void)
 {
@@ -556,7 +556,7 @@ __printf_2_3 void i9e_log(int ll, const char* fmt,...)
  * the given text. If the length of this text exceeds the width of the
  * terminal, the text is shortened by leaving out a part in the middle.
  */
-void ie9_print_status_bar(char *buf, unsigned len)
+void i9e_print_status_bar(char *buf, unsigned len)
 {
        size_t x = i9ep->num_columns, y = (x - 4) / 2;
 
index 40ff294018b221708dd0d1db9cac23c80fb70533..ddf02d76d2bc44a71133844fa0dddf4d9db03778 100644 (file)
@@ -80,7 +80,7 @@ struct i9e_client_info {
 
 int i9e_open(struct i9e_client_info *ici, struct sched *s);
 void i9e_attach_to_stdout(struct btr_node *producer);
-void ie9_print_status_bar(char *buf, unsigned len);
+void i9e_print_status_bar(char *buf, unsigned len);
 void i9e_close(void);
 void i9e_signal_dispatch(int sig_num);
 __printf_2_3 void i9e_log(int ll, const char* fmt,...);
diff --git a/list.h b/list.h
index 66c6d91525031261ff94cd3028e672cb340c06cd..78c302fa322fe6bc2dae2926e95e58189c10c944 100644 (file)
--- a/list.h
+++ b/list.h
  * Copied from the Linux kernel source tree, version 2.6.13.
  *
  * Licensed under the GPL v2 as per the whole kernel source tree.
- *
  */
 
-/** \file list.h doubly linked list implementation */
+/** \file list.h Doubly linked list implementation. */
 
 #include <stddef.h> /* offsetof */
 
-/** get the struct this entry is embedded in */
+/** Get the struct this entry is embedded in. */
 #define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
 
-/**
- * Non-NULL pointers that will result in page faults under normal
- * circumstances, used to verify that nobody uses non-initialized list entries.
- * Used for poisoning the \a next pointer of struct list_head.
- */
-#define LIST_POISON1  ((void *) 0x00100100)
-/** Non-null pointer, used for poisoning the \a prev pointer of struct
- * list_head
- */
-#define LIST_POISON2  ((void *) 0x00200200)
-
-/** Simple doubly linked list implementation. */
+/** A list head is just a pair of pointers. */
 struct list_head {
-       /** pointer to the next list entry */
+       /** Pointer to the next list entry. */
        struct list_head *next;
-       /** pointer to the previous list entry */
+       /** Pointer to the previous list entry. */
        struct list_head *prev;
 };
 
 /** Define an initialized list head. */
-#define INITIALIZED_LIST_HEAD(name) struct list_head name = { &(name), &(name) }
-
-
-/** must be called before using any other list functions */
-#define INIT_LIST_HEAD(ptr) do { \
-       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
+#define INITIALIZED_LIST_HEAD(name) struct list_head name = {&(name), &(name)}
 
-
-/*
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
-                             struct list_head *prev,
-                             struct list_head *next)
+/** This must be called before using any other list functions. */
+static inline void init_list_head(struct list_head *head)
 {
-       next->prev = new;
-       new->next = next;
-       new->prev = prev;
-       prev->next = new;
+       head->next = head;
+       head->prev = head;
 }
 
 /**
- * add a new entry
+ * Insert a new entry after the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it after
+ * \param entry The new entry to add.
+ * \param head The list head to add it after.
  *
- * Insert a new entry after the specified head.
  * This is good for implementing stacks.
  */
-static inline void para_list_add(struct list_head *new, struct list_head *head)
+static inline void para_list_add(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head, head->next);
+       entry->prev = head;
+       entry->next = head->next;
+       head->next->prev = entry;
+       head->next = entry;
 }
 
 /**
- * add a new entry
+ * Insert a new entry before the specified head.
  *
- * \param new new entry to be added
- * \param head list head to add it before
+ * \param entry The new entry to add.
+ * \param head list head to add it before.
  *
- * Insert a new entry before the specified head.
  * This is useful for implementing queues.
  */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void list_add_tail(struct list_head *entry, struct list_head *head)
 {
-       __list_add(new, head->prev, head);
+       entry->prev = head->prev;
+       entry->next = head;
+       head->prev->next = entry;
+       head->prev = entry;
 }
 
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
+/**
+ * Delete an entry from a list.
+ *
+ * \param entry The element to delete.
  *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
+ * The list entry is in an undefined state after this and \ref list_empty()
+ * does not return true.
  */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
+static inline void list_del(struct list_head *entry)
 {
-       next->prev = prev;
-       prev->next = next;
+       entry->prev->next = entry->next;
+       entry->next->prev = entry->prev;
+       /*
+        * These non-NULL pointers result in page faults when dereferenced.
+        * This helps to catch bugs resulting from using deleted list heads.
+        */
+       entry->next = (void *)0x00100100;
+       entry->prev = (void *)0x00200200;
 }
 
 /**
- * Delete entry from list.
- *
- * \param entry the element to delete from the list.
+ * Delete an entry from one list and add it as another list's head.
  *
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
+ * \param entry The entry to move.
+ * \param head The head that will precede our entry.
  */
-static inline void list_del(struct list_head *entry)
+static inline void list_move(struct list_head *entry, struct list_head *head)
 {
-       __list_del(entry->prev, entry->next);
-       entry->next = LIST_POISON1;
-       entry->prev = LIST_POISON2;
+        list_del(entry);
+        para_list_add(entry, head);
 }
 
 /**
- * delete from one list and add as another's head
+ * Test whether a list contains no entries.
  *
- * \param list: the entry to move
- * \param head: the head that will precede our entry
+ * \param head The list to test.
  */
-static inline void list_move(struct list_head *list, struct list_head *head)
+static inline int list_empty(const struct list_head *head)
 {
-        __list_del(list->prev, list->next);
-        para_list_add(list, head);
+       return head->next == head;
 }
 
 /**
- * test whether a list is empty
+ * Test whether a list has just one entry.
  *
- * \param head the list to test.
+ * \param head The list to test.
  */
-static inline int list_empty(const struct list_head *head)
+static inline int list_is_singular(const struct list_head *head)
 {
-       return head->next == head;
+       return !list_empty(head) && (head->next == head->prev);
 }
 
 /**
- * get the struct for this entry
+ * Get the struct in which this entry is embedded in.
  *
- * \param ptr the &struct list_head pointer.
- * \param type the type of the struct this is embedded in.
- * \param member the name of the list_struct within the struct.
+ * \param ptr The list head pointer.
+ * \param type The type of containing structure.
+ * \param member The name of the list head member within the structure.
  */
-#define list_entry(ptr, type, member) \
-       container_of(ptr, type, member)
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
 
 /**
- * iterate over list of given type
+ * Iterate over a list.
  *
- * \param pos the type * to use as a loop counter.
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos A struct pointer which serves as the iterator.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry(pos, head, member)                         \
        for (pos = list_entry((head)->next, typeof(*pos), member);      \
@@ -169,49 +137,27 @@ static inline int list_empty(const struct list_head *head)
             pos = list_entry(pos->member.next, typeof(*pos), member))
 
 /**
- * iterate over list of given type safe against removal of list entry
+ * Iterate over list, safe against removal of list entry.
  *
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
+ * \param pos The iterator struct pointer.
+ * \param n A second struct pointer which is used as temporary storage.
+ * \param head The head of the list.
+ * \param member The name of the list head member within the structure.
  */
 #define list_for_each_entry_safe(pos, n, head, member)                 \
        for (pos = list_entry((head)->next, typeof(*pos), member),      \
                n = list_entry(pos->member.next, typeof(*pos), member); \
             &pos->member != (head);                                    \
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
-/**
- * iterate backwards over list of given type safe against removal of list entry
- * \param pos the type * to use as a loop counter.
- * \param n another type * to use as temporary storage
- * \param head the head for your list.
- * \param member the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member)         \
-       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
-               n = list_entry(pos->member.prev, typeof(*pos), member); \
-            &pos->member != (head);                                    \
-            pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 
 /**
- * Get the first element from a list
- * \param ptr the list head to take the element from.
+ * Get the first element of a list.
+ *
+ * \param ptr The list head to take the element from.
  * \param type The type of the struct this is embedded in.
  * \param member The name of the list_struct within the struct.
  *
- * Note that list is expected to be not empty.
+ * Note that the list is expected to be non-empty.
  */
 #define list_first_entry(ptr, type, member) \
-        list_entry((ptr)->next, type, member)
-
-/**
- * Test whether a list has just one entry.
- *
- * \param head The list to test.
- */
-static inline int list_is_singular(const struct list_head *head)
-{
-       return !list_empty(head) && (head->next == head->prev);
-}
-
+       list_entry((ptr)->next, type, member)
index 93e4b573e5ae694581b5f111f1a898d45b6c4f7b..1c941c433b8fcd753beb09f1c619e1976e8c5817 100644 (file)
@@ -133,7 +133,7 @@ version-string = GIT_VERSION()
                typestr = directory
                [help]
                        The directory which contains the database for the audio file
-                       selector. The default is ~/.paraslash/afs_database-0.4.
+                       selector. The default is ~/.paraslash/afs_database-0.7.
 
                        If no database was found, the "init" command must be executed to
                        initialize the database. Once initialized, audio files may added with
index 2a39907cee624219d1fb166b07dbf84288f67bb4..f73d66b26a713ce70ed144e38362a73f5ddcf8c1 100644 (file)
@@ -500,15 +500,6 @@ aux_info_prefix = Permissions:
                the vss status flags, effectively stopping playback.
        [/description]
 
-[subcommand tasks]
-       purpose = list active server tasks (deprecated)
-       aux_info = NO_PERMISSION_REQUIRED
-       [description]
-               This used to print the ID, the status and the name of each task,
-               mainly for debugging purposes. As of version 0.6.2, the subcommand
-               prints nothing. It will be removed in 0.7.0. Don't use.
-       [/description]
-
 [subcommand term]
        purpose = ask the server to terminate
        aux_info = VSS_READ | VSS_WRITE
diff --git a/m4/lls/upgrade_db.suite.m4 b/m4/lls/upgrade_db.suite.m4
new file mode 100644 (file)
index 0000000..7f46b74
--- /dev/null
@@ -0,0 +1,33 @@
+m4_define(PROGRAM, para_upgrade_db)
+[suite upgrade_db]
+version-string = GIT_VERSION()
+[supercommand para_upgrade_db]
+       purpose = upgrade the paraslash database to version 0.7
+       [description]
+               The database format changes with paraslash-0.7.0. This program converts
+               the database from the older 0.4 format that was used in paraslash 0.4.x
+               through 0.6.x. In has to be executed only once.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       [option src-database-dir]
+               summary = location of the old afs database
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the database to be converted. The default
+                       is ~/.paraslash/afs_database-0.4.
+               [/help]
+       [option dst-database-dir]
+               summary = location of the new afs database
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the converted database after the program
+                       has terminated. The default is ~/.paraslash/afs_database-0.7.
+               [/help]
diff --git a/mm.c b/mm.c
deleted file mode 100644 (file)
index 358783a..0000000
--- a/mm.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.c Paraslash's mood methods. */
-
-#include <regex.h>
-#include <fnmatch.h>
-#include <osl.h>
-#include <lopsub.h>
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "afh.h"
-#include "afs.h"
-#include "mm.h"
-
-/** The comparators for numeric mood methods (year, bitrate, ...). */
-#define MOOD_COMPARATORS \
-       MC(LESS, <) \
-       MC(LESS_OR_EQUAL, <=) \
-       MC(EQUAL, =) \
-       MC(EQUAL2, ==) \
-       MC(NOT_EQUAL, !=) \
-       MC(NOT_EQUAL2, <>) \
-       MC(GREATER, >) \
-       MC(GREATER_OR_EQUAL, >=) \
-
-/** Prefix mood comparator name with "_MC", example: MC_LESS. */
-#define MC(a, b) MC_ ## a,
-/** Each mood comparator is identified by an integer of this type. */
-enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
-#undef MC
-/** Stringfied mood comparator, example: "<". */
-#define MC(a, b) # b,
-/** Array of mood comparators represented as C strings ("<", "<=", ...). */
-static const char *mood_comparators[] = {MOOD_COMPARATORS};
-#undef MC
-
-static int parse_mood_comparator(const char *word)
-{
-       int i;
-
-       for (i = 0; i < NUM_MOOD_COMPARATORS; i++)
-               if (!strcmp(word, mood_comparators[i]))
-                       return i;
-       return -E_MOOD_SYNTAX;
-}
-
-struct mm_compare_num_data {
-       /** <, <=, =, !=, >=, or >. */
-       enum mood_comparator_id id;
-       /** The value given at the mood line. */
-       int32_t arg;
-};
-
-static int mm_compare_num_score_function(int32_t val,
-               const struct mm_compare_num_data *cnd)
-{
-       int res;
-       int32_t arg = cnd->arg;
-
-       switch (cnd->id) {
-       case MC_LESS:
-               res = val < arg; break;
-       case MC_LESS_OR_EQUAL:
-               res = val <= arg; break;
-       case MC_EQUAL:
-       case MC_EQUAL2:
-               res = val == arg; break;
-       case MC_NOT_EQUAL:
-       case MC_NOT_EQUAL2:
-               res = val != arg; break;
-       case MC_GREATER:
-               res = val > arg; break;
-       case MC_GREATER_OR_EQUAL:
-               res = val >= arg; break;
-       default:
-               PARA_EMERG_LOG("BUG: invalid mood comparator\n");
-               exit(EXIT_FAILURE);
-       }
-       return res? 100 : -100;
-}
-
-static int mm_compare_num_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       enum mood_comparator_id id;
-       int32_t arg;
-       struct mm_compare_num_data *cnd;
-       if (argc != 2)
-               return -E_MOOD_SYNTAX;
-       ret = parse_mood_comparator(argv[1]);
-       if (ret < 0)
-               return ret;
-       id = ret;
-       ret = para_atoi32(argv[2], &arg);
-       if (ret < 0)
-               return ret;
-       cnd = para_malloc(sizeof(struct mm_compare_num_data));
-       cnd->id = id;
-       cnd->arg = arg;
-       *private = cnd;
-       return 1;
-}
-
-static int mm_regex_parser(int argc, char **argv, void **private)
-{
-       regex_t *preg;
-       int ret;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       preg = para_malloc(sizeof(*preg));
-       ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB);
-       if (ret < 0) {
-               free(preg);
-               return ret;
-       }
-       *private = preg;
-       return 1;
-}
-
-static int mm_regex_score_function(const regex_t *preg, const char *pattern)
-{
-       return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100;
-}
-
-static void mm_regex_cleanup(void *private)
-{
-       regex_t *preg = private;
-       regfree(preg);
-       free(preg);
-}
-
-static int mm_artist_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.artist);
-}
-
-static int mm_title_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.title);
-}
-
-static int mm_album_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.album);
-}
-
-static int mm_comment_matches_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_regex_score_function(private, afhi->tags.comment);
-}
-
-static int mm_bitrate_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->bitrate, private);
-}
-
-static int mm_frequency_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->frequency, private);
-}
-
-static int mm_channels_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afhi->channels, private);
-}
-
-static int mm_image_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->image_id, private);
-}
-
-static int mm_lyrics_id_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->lyrics_id, private);
-}
-
-static int mm_num_played_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *private)
-{
-       return mm_compare_num_score_function(afsi->num_played, private);
-}
-
-struct mm_year_data {
-       /** Comparator and year given at the mood line. */
-       struct mm_compare_num_data *cnd;
-       /** Used to detect Y2K issues. */
-       int32_t current_year;
-};
-
-static int mm_year_parser(int argc, char **argv, void **private)
-{
-       int ret;
-       struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd));
-       time_t current_time;
-       struct tm *gmt;
-
-       ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd);
-       if (ret < 0)
-               goto err;
-       current_time = time(NULL);
-       gmt = gmtime(&current_time);
-       /* tm_year is the number of years since 1900 */
-       mmyd->current_year = gmt->tm_year + 1900;
-       *private = mmyd;
-       return 1;
-err:
-       free(mmyd);
-       return ret;
-}
-
-static int mm_year_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               const struct afh_info *afhi,
-               const void *private)
-{
-       const struct mm_year_data *mmyd = private;
-       int32_t tag_year;
-       int ret = para_atoi32(afhi->tags.year, &tag_year);
-
-       if (ret < 0) /* year tag not present or not a number */
-               return -100;
-       if (tag_year < 0)
-               return -100;
-       /* try to work around Y2K issues */
-       if (tag_year < 100) {
-               tag_year += 1900;
-               if (tag_year + 100 <= mmyd->current_year)
-                       tag_year += 100; /* assume tag_year >= 2000 */
-       }
-       return mm_compare_num_score_function(tag_year, mmyd->cnd);
-}
-
-static void mm_year_cleanup(void *private)
-{
-       struct mm_year_data *mmyd = private;
-
-       free(mmyd->cnd);
-       free(mmyd);
-}
-
-static int mm_no_attributes_set_parser(int argc, __a_unused char **argv,
-               __a_unused void **ignored)
-{
-       return argc? -E_MOOD_SYNTAX : 1;
-}
-
-static int mm_no_attributes_set_score_function(__a_unused const char *path,
-               const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               __a_unused const void *data)
-{
-       if (!afsi->attributes)
-               return 100;
-       return -100;
-}
-
-static int mm_path_matches_score_function(const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       if (fnmatch(data, path, 0))
-               return -100;
-       return 100;
-}
-
-static int mm_path_matches_parser(int argc, char **argv, void **data)
-{
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       *data = para_strdup(argv[1]);
-       return 1;
-}
-
-static void mm_path_matches_cleanup(void *data)
-{
-       free(data);
-}
-
-static int mm_is_set_parser(int argc, char **argv, void **bitnum)
-{
-       int ret;
-       unsigned char c, *res;
-
-       if (argc != 1)
-               return -E_MOOD_SYNTAX;
-       ret = get_attribute_bitnum_by_name(argv[1], &c);
-       if (ret < 0)
-               return ret;
-       res = para_malloc(1);
-       *res = c;
-       *bitnum = res;
-       return 1;
-}
-
-static int mm_is_set_score_function(__a_unused const char *path,
-               __a_unused const struct afs_info *afsi,
-               __a_unused const struct afh_info *afhi,
-               const void *data)
-{
-       const unsigned char *bn = data;
-       if (afsi->attributes & (1ULL << *bn))
-               return 100;
-       return -100;
-}
-
-#define DEFINE_MOOD_METHOD(_name) \
-.parser = mm_ ## _name ## _parser, \
-.score_function = mm_ ## _name ## _score_function, \
-.name = #_name
-
-#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \
-       DEFINE_MOOD_METHOD(_name), \
-       .cleanup = mm_ ## _name ## _cleanup
-
-#define DEFINE_REGEX_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_regex_parser, \
-       .score_function = mm_ ## _name ## _score_function, \
-       .cleanup = mm_regex_cleanup
-
-#define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \
-       .name = #_name, \
-       .parser = mm_compare_num_parser, \
-       .score_function = mm_ ## _name ## _score_function
-
-const struct mood_method mood_methods[] = {
-       {DEFINE_MOOD_METHOD(no_attributes_set)},
-       {DEFINE_MOOD_METHOD(is_set)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)},
-       {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)},
-       {DEFINE_REGEX_MOOD_METHOD(artist_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(title_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(album_matches)},
-       {DEFINE_REGEX_MOOD_METHOD(comment_matches)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
-       {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},
-       {.parser = NULL}
-};
diff --git a/mm.h b/mm.h
deleted file mode 100644 (file)
index 28038e7..0000000
--- a/mm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mm.h Symbols and declarations for mood methods. */
-
-/**
- * Assign scores according to a mood_method.
- *
- * Each mood_method has its own mood_score_function. The first three parameters
- * passed to that function are informations about the audio file whose score is
- * to be computed. The data argument depends on the mood method this function
- * is used for. It usually is the argument given at the end of a mood line.
- *
- * Mood score functions must return values between -100 and +100 inclusively.
- * Boolean score functions should always return either -100 or +100.
- *
- * \sa struct \ref mood_method, \ref mood_parser.
- */
-typedef int mood_score_function(const char *path, const struct afs_info *afsi,
-               const struct afh_info *afhi, const void *data);
-
-/**
- * Pre-process a mood line.
- *
- * The mood_parser of a mood_method is called once at mood open time for each
- * line of the current mood definition that contains the mood_method's name as
- * a keyword. The line is passed to the mood_parser as the first argument. The
- * mood_parser must determine whether the line is syntactically correct and
- * return a positive value if so and a negative value otherwise.
- *
- * Some mood parsers pre-process the data given in the mood line to compute a
- * structure which depends of the particular mood_method and which is used
- * later in the mood_score_function of the mood_method. The mood_parser may
- * store a pointer to its structure via the void** pointer.
- *
- * \sa \ref mood_cleanup_function, \ref mood_score_function.
- */
-typedef int mood_parser(int, char **, void **);
-
-/**
- * Deallocate resources which were allocated by the mood_parser.
- *
- * Function to free the resources allocated in \ref mood_method::parser. The
- * argument is a pointer to mood method specific data returned by ->parser().
- */
-typedef void mood_cleanup_function(void *);
-
-/**
- * Used for scoring and to determine whether a file is admissible.
- */
-struct mood_method {
-       /** The name of the method. */
-       const char *name;
-       /** Pointer to the mood parser. */
-       mood_parser *parser;
-       /** Pointer to the score function */
-       mood_score_function *score_function;
-       /** Optional cleanup function. */
-       mood_cleanup_function *cleanup;
-};
-
-/** The array of available mood methods. */
-extern const struct mood_method mood_methods[];
diff --git a/mood.c b/mood.c
index 5268e77fe03c7e56d1b146935f683e30dea9a2f6..a228b2432f9b56d3bc51535c13d74094314e7da9 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mm.h"
 #include "mood.h"
 
 /*
@@ -39,47 +38,21 @@ struct afs_statistics {
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
+       /** Correction factor for the num played score. */
+       int64_t num_played_correction;
+       /** Correction factor for the last played score. */
+       int64_t last_played_correction;
+       /** Common divisor of the correction factors. */
+       int64_t normalization_divisor;
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics;
+static struct afs_statistics statistics = {.normalization_divisor = 1};
 
-/**
- * Each line of the current mood corresponds to a mood_item.
- */
-struct mood_item {
-       /** The method this line is referring to. */
-       const struct mood_method *method;
-       /** The data structure computed by the mood parser. */
-       void *parser_data;
-       /** The given score value, or zero if none was given. */
-       int32_t score_arg;
-       /** Non-zero if random scoring was requested. */
-       int random_score;
-       /** Whether the "not" keyword was given in the mood line. */
-       int logical_not;
-       /** The position in the list of items. */
-       struct list_head mood_item_node;
-};
-
-/*
- * Created from the mood definition by \ref change_current_mood().
- *
- * When a mood is opened, each line of its definition is investigated, and a
- * corresponding mood item is produced. Each mood line starts with accept,
- * deny, or score which determines the type of the mood line. For each such
- * type a linked list is maintained whose entries are the mood items.
- */
 struct mood {
        /** The name of this mood. */
        char *name;
-       /** The list of mood items of type \p accept. */
-       struct list_head accept_list;
-       /** The list of mood items of type \p deny. */
-       struct list_head deny_list;
-       /** The list of mood items of type \p score. */
-       struct list_head score_list;
-       /* Only used for version 2 moods. */
+       /** Info for the bison parser. */
        struct mp_context *parser_context;
 };
 
@@ -146,115 +119,20 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/*
- * Returns true if row matches, false if it does not match. In any case score
- * and score_arg_sum are set/increased accordingly.
- */
-static bool get_item_score(struct mood_item *item, const struct afs_info *afsi,
-               const struct afh_info *afhi, const char *path, long *score,
-               long *score_arg_sum)
-{
-       int ret;
-       bool match = true;
-
-       *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg);
-       ret = 100;
-       if (item->method) {
-               ret = item->method->score_function(path, afsi, afhi,
-                       item->parser_data);
-               if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not))
-                       match = false;
-       }
-       if (item->random_score)
-               *score = PARA_ABS(ret) * para_random(100);
-       else
-               *score = PARA_ABS(ret) * item->score_arg;
-       return match;
-}
-
 /* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m,
-               long *scorep)
+static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
 {
-       struct mood_item *item;
-       int ret;
-       bool match;
-       long score_arg_sum = 0, score = 0, item_score;
-       struct afs_info afsi;
-       struct afh_info afhi;
-       char *path;
-
        if (!m)
                return -E_NO_MOOD;
-       if (m->parser_context) {
-               *scorep = 0;
-               return mp_eval_row(aft_row, m->parser_context);
-       }
-       ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret < 0)
-               return ret;
-       ret = get_afhi_of_row(aft_row, &afhi);
-       if (ret < 0)
-               return ret;
-       ret = get_audio_file_path_of_row(aft_row, &path);
-       if (ret < 0)
-               return ret;
-       /* reject audio file if it matches any entry in the deny list */
-       list_for_each_entry(item, &m->deny_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match) /* not admissible */
-                       return 0;
-               score += item_score;
-       }
-       match = false;
-       list_for_each_entry(item, &m->accept_list, mood_item_node) {
-               ret = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (ret == 0)
-                       continue;
-               match = true;
-               score += item_score;
-       }
-       /* reject if there is no matching entry in the accept list */
-       if (!match && !list_empty(&m->accept_list))
-               return 0;
-       list_for_each_entry(item, &m->score_list, mood_item_node) {
-               match = get_item_score(item, &afsi, &afhi, path, &item_score,
-                       &score_arg_sum);
-               if (match)
-                       score += item_score;
-       }
-       if (score_arg_sum)
-               score /= score_arg_sum;
-       *scorep = score;
-       return 1;
-}
-
-static void cleanup_list_entry(struct mood_item *item)
-{
-       if (item->method && item->method->cleanup)
-               item->method->cleanup(item->parser_data);
-       else
-               free(item->parser_data);
-       list_del(&item->mood_item_node);
-       free(item);
+       return mp_eval_row(aft_row, m->parser_context);
 }
 
 static void destroy_mood(struct mood *m)
 {
-       struct mood_item *tmp, *item;
-
        if (!m)
                return;
-       list_for_each_entry_safe(item, tmp, &m->accept_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->deny_list, mood_item_node)
-               cleanup_list_entry(item);
-       list_for_each_entry_safe(item, tmp, &m->score_list, mood_item_node)
-               cleanup_list_entry(item);
-       free(m->name);
        mp_shutdown(m->parser_context);
+       free(m->name);
        free(m);
 }
 
@@ -263,160 +141,16 @@ static struct mood *alloc_new_mood(const char *name)
        struct mood *m = para_calloc(sizeof(struct mood));
        if (name)
                m->name = para_strdup(name);
-       INIT_LIST_HEAD(&m->accept_list);
-       INIT_LIST_HEAD(&m->deny_list);
-       INIT_LIST_HEAD(&m->score_list);
        return m;
 }
 
-/** The different types of a mood line. */
-enum mood_line_type {
-       /** Invalid. */
-       ML_INVALID,
-       /** Accept line. */
-       ML_ACCEPT,
-       /** Deny line. */
-       ML_DENY,
-       /** Score line. */
-       ML_SCORE
-};
-
-/** Data passed to the parser of a mood line. */
-struct mood_line_parser_data {
-       /** The mood this mood line belongs to. */
-       struct mood *m;
-       /** The line number in the mood definition. */
-       unsigned line_num;
-};
-
-/*
- * <accept [with score <score>] | deny [with score <score>]  | score <score>>
- *     [if] [not] <mood_method> [options]
- * <score> is either an integer or "random" which assigns a random score to
- * all matching files
- */
-static int parse_mood_line(char *mood_line, void *data)
-{
-       struct mood_line_parser_data *mlpd = data;
-       char **argv;
-       unsigned num_words;
-       char **w;
-       int i, ret;
-       enum mood_line_type mlt = ML_INVALID;
-       struct mood_item *mi = NULL;
-
-       mlpd->line_num++;
-       ret = create_argv(mood_line, " \t", &argv);
-       if (ret < 0)
-               return ret;
-       num_words = ret;
-       if (!num_words) /* empty line */
-               goto out;
-       w = argv;
-       if (**w == '#') /* comment */
-               goto out;
-       if (!strcmp(*w, "accept"))
-               mlt = ML_ACCEPT;
-       else if (!strcmp(*w, "deny"))
-               mlt = ML_DENY;
-       else if (!strcmp(*w, "score"))
-               mlt = ML_SCORE;
-       ret = -E_MOOD_SYNTAX;
-       if (mlt == ML_INVALID)
-               goto out;
-       mi = para_calloc(sizeof(struct mood_item));
-       if (mlt != ML_SCORE) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "with"))
-                       goto check_for_if;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "score"))
-                       goto out;
-       }
-       if (mlt == ML_SCORE || !strcmp(*w, "score")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               if (strcmp(*w, "random")) {
-                       mi->random_score = 0;
-                       ret = para_atoi32(*w, &mi->score_arg);
-                       if (ret < 0)
-                               goto out;
-               } else {
-                       mi->random_score = 1;
-                       if (!*(w + 1))
-                       goto success; /* the line "score random" is valid */
-               }
-       } else
-               mi->score_arg = 0;
-       ret = -E_MOOD_SYNTAX;
-       w++;
-       if (!*w)
-               goto out;
-check_for_if:
-       if (!strcmp(*w, "if")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-       }
-       if (!strcmp(*w, "not")) {
-               ret = -E_MOOD_SYNTAX;
-               w++;
-               if (!*w)
-                       goto out;
-               mi->logical_not = 1;
-       } else
-               mi->logical_not = 0;
-       for (i = 0; mood_methods[i].parser; i++) {
-               if (strcmp(*w, mood_methods[i].name))
-                       continue;
-               break;
-       }
-       ret = -E_MOOD_SYNTAX;
-       if (!mood_methods[i].parser)
-               goto out;
-       ret = mood_methods[i].parser(num_words - 1 - (w - argv), w,
-               &mi->parser_data);
-       if (ret < 0)
-               goto out;
-       mi->method = &mood_methods[i];
-success:
-       if (mlpd->m) {
-               if (mlt == ML_ACCEPT)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->accept_list);
-               else if (mlt == ML_DENY)
-                       para_list_add(&mi->mood_item_node, &mlpd->m->deny_list);
-               else
-                       para_list_add(&mi->mood_item_node, &mlpd->m->score_list);
-       }
-       PARA_DEBUG_LOG("%s entry added, method: %p\n", mlt == ML_ACCEPT? "accept" :
-               (mlt == ML_DENY? "deny" : "score"), mi->method);
-       ret = 1;
-out:
-       free_argv(argv);
-       if (mi && (ret < 0 || !mlpd->m)) { /* mi was not added to any list */
-               free(mi->parser_data);
-               free(mi);
-       }
-       return ret;
-}
-
 static int load_mood(const struct osl_row *mood_row, struct mood **m,
                char **errmsg)
 {
        char *mood_name;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
        int ret;
 
-       *m = NULL;
        ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
        if (ret < 0) {
                if (errmsg)
@@ -425,33 +159,21 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
                return ret;
        }
        assert(*mood_name);
-       mlpd.m = alloc_new_mood(mood_name);
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
-       if (ret < 0) {
-               PARA_INFO_LOG("opening version 2 mood %s\n", mlpd.m->name);
-               ret = mp_init(mood_def.data, mood_def.size, &mlpd.m->parser_context,
-                       errmsg);
-               if (ret < 0)
-                       destroy_mood(mlpd.m);
-       } else {
-               PARA_WARNING_LOG("loaded version 1 mood %s\n", mlpd.m->name);
-               PARA_WARNING_LOG("please convert to version 2\n");
-               ret = 1;
-       }
+       *m = alloc_new_mood(mood_name);
+       PARA_INFO_LOG("opening mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
        osl_close_disk_object(&mood_def);
-       if (ret >= 0)
-               *m = mlpd.m;
+       if (ret < 0)
+               destroy_mood(*m);
        return ret;
 }
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
        struct para_buffer *pb = data;
-       char *mood_name;
+       char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood_line_parser_data mlpd = {.line_num = 0};
-
+       struct mood *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
@@ -460,24 +182,15 @@ static int check_mood(struct osl_row *mood_row, void *data)
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
-               parse_mood_line, &mlpd);
+       m = alloc_new_mood("check");
+       ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
+               &errmsg);
        if (ret < 0) {
-               char *errmsg;
-               struct mood *m = alloc_new_mood("check");
-               ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
-                       &errmsg);
-               if (ret < 0) {
-                       para_printf(pb, "%s: %s\n", mood_name, errmsg);
-                       free(errmsg);
-                       para_printf(pb, "%s\n", para_strerror(-ret));
-               } else
-                       destroy_mood(m);
-       } else {
-               para_printf(pb, "%s: v1 mood, please convert to v2\n",
-                       mood_name);
-
-       }
+               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               free(errmsg);
+               para_printf(pb, "%s\n", para_strerror(-ret));
+       } else
+               destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
@@ -499,6 +212,43 @@ int mood_check_callback(struct afs_callback_arg *aca)
                check_mood));
 }
 
+/*
+ * The normalized num_played and last_played values are defined as
+ *
+ *     nn := -(np - mean_n) / sigma_n and nl := -(lp - mean_l) / sigma_l
+ *
+ *  For a (hypothetical) file with np = 0 and lp = now we thus have
+ *
+ *     nn =  mean_n / sigma_n =: hn > 0
+ *     nl = -(now - mean_l) / sigma_l =: hl < 0
+ *
+ * We design the score function so that both contributions get the same
+ * weight. Define the np and lp score of an arbitrary file as
+ *
+ *     sn := nn * -hl and sl := nl * hn
+ *
+ * Example:
+ *     num_played mean/sigma: 87/14
+ *     last_played mean/sigma: 45/32 days
+ *
+ *     We have hn = 87 / 14 = 6.21 and hl = -45 / 32 = -1.41. Multiplying
+ *     nn of every file with the correction factor 1.41 and nl with
+ *     6.21 makes the weight of the two contributions equal.
+ *
+ * The total score s := sn + sl has the representation
+ *
+ *     s = -cn * (np - mean_n) - cl * (lp - mean_l)
+ *
+ * with positive correction factors
+ *
+ *     cn = (now - mean_l) / (sqrt(ql) * sqrt(qn) / n)
+ *     cl = mean_n / (sqrt(ql) * sqrt(qn) / n)
+ *
+ * where ql and qn are the quadratic deviations stored in the statistics
+ * structure and n is the number of admissible files. To avoid integer
+ * overflows and rounding errors we store the common divisor of the
+ * correction factors separately.
+ */
 static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
 {
        if (!n || !qd)
@@ -506,13 +256,13 @@ static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
        return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
 }
 
-static long compute_score(struct afs_info *afsi, long mood_score)
+static long compute_score(struct afs_info *afsi)
 {
-       mood_score -= normalized_value(afsi->num_played, statistics.num,
+       long score = -normalized_value(afsi->num_played, statistics.num,
                statistics.num_played_sum, statistics.num_played_qd);
-       mood_score -= normalized_value(afsi->last_played, statistics.num,
+       score -= normalized_value(afsi->last_played, statistics.num,
                statistics.last_played_sum, statistics.last_played_qd);
-       return mood_score / 3;
+       return score / 2;
 }
 
 static int add_afs_statistics(const struct osl_row *row)
@@ -556,6 +306,7 @@ static int del_afs_statistics(const struct osl_row *row)
        assert(n);
        if (n == 1) {
                memset(&statistics, 0, sizeof(statistics));
+               statistics.normalization_divisor = 1;
                return 1;
        }
 
@@ -581,63 +332,42 @@ static int del_afs_statistics(const struct osl_row *row)
 
 /*
  * At mood open time we determine the set of admissible files for the given
- * mood. The mood score of each admissible file is computed by adding up all
- * mood item scores. Next, we update the afs statistics and append a struct
- * admissible_file_info to a temporary array.
- *
- * When all files have been processed in this way, the final score of each
- * admissible file is computed by adding the dynamic score (which depends on
- * the afs_statistics and the current time) to the mood score. Finally, all
- * audio files in the temporary array are added to the score table and the
- * array is freed.
+ * mood where each file is identified by a pointer to a row of the audio file
+ * table. In the first pass the pointers are added to a temporary array and
+ * statistics are computed. When all admissible files have been processed in
+ * this way, the score of each admissible file is computed and the (row, score)
+ * pair is added to the score table. This has to be done in a second pass
+ * since the score depends on the statistics. Finally, the array is freed.
  */
-struct admissible_file_info
-{
-       /** The admissible audio file. */
-       struct osl_row *aft_row;
-       /** Its score. */
-       long score;
-};
-
-/** The temporary array of admissible files. */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
        struct mood *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
-       struct admissible_file_info *array;
+       struct osl_row **array;
 };
 
-/**
- * Add an entry to the array of admissible files.
- *
- * \param aft_row The audio file to be added.
- * \param private_data Pointer to a struct admissible_file_info.
- *
- * \return 1 if row admissible, 0 if not, negative on errors.
+/*
+ * Check whether the given audio file is admissible. If it is, add it to array
+ * of admissible files.
  */
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
        int ret;
-       long score = 0;
 
-       ret = row_is_admissible(aft_row, aa->m, &score);
+       ret = row_is_admissible(aft_row, aa->m);
        if (ret <= 0)
                return ret;
        if (statistics.num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
                aa->array = para_realloc(aa->array,
-                       aa->size * sizeof(struct admissible_file_info));
+                       aa->size * sizeof(struct osl_row *));
        }
-       aa->array[statistics.num].aft_row = aft_row;
-       aa->array[statistics.num].score = score;
-       ret = add_afs_statistics(aft_row);
-       if (ret < 0)
-               return ret;
-       return 1;
+       aa->array[statistics.num] = aft_row;
+       return add_afs_statistics(aft_row);
 }
 
 /**
@@ -704,7 +434,7 @@ static int update_afs_statistics(struct afs_info *old_afsi,
        return 1;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
+static int add_to_score_table(const struct osl_row *aft_row)
 {
        long score;
        struct afs_info afsi;
@@ -712,7 +442,7 @@ static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi, mood_score);
+       score = compute_score(&afsi);
        return score_add(aft_row, score);
 }
 
@@ -770,7 +500,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        if (ret < 0)
                return ret;
        was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood, &score);
+       ret = row_is_admissible(aft_row, current_mood);
        if (ret < 0)
                return ret;
        is_admissible = (ret > 0);
@@ -782,7 +512,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                ret = add_afs_statistics(aft_row);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row, score);
+               return add_to_score_table(aft_row);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
@@ -793,7 +523,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                if (ret < 0)
                        return ret;
        }
-       score = compute_score(&afsi, score);
+       score = compute_score(&afsi);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -804,15 +534,11 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        return score_update(aft_row, percent);
 }
 
-static void log_statistics(void)
+/* sse: seconds since epoch. */
+static void log_statistics(int64_t sse)
 {
        unsigned n = statistics.num;
        int mean_days, sigma_days;
-       /*
-        * We can not use the "now" pointer from sched.c here because we are
-        * called before schedule(), which initializes "now".
-        */
-       struct timeval rnow;
 
        assert(current_mood);
        PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
@@ -822,13 +548,18 @@ static void log_statistics(void)
                return;
        }
        PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       clock_get_realtime(&rnow);
-       mean_days = (rnow.tv_sec - statistics.last_played_sum / n) / 3600 / 24;
+       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
        sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
        PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %llu/%llu\n",
-               (long long unsigned)statistics.num_played_sum / n,
-               (long long unsigned)int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
+               statistics.num_played_sum / n,
+               int_sqrt(statistics.num_played_qd / n));
+       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
+               statistics.num_played_correction);
+       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
+               statistics.last_played_correction);
+       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
+               statistics.normalization_divisor);
 }
 
 /**
@@ -841,6 +572,25 @@ void close_current_mood(void)
        destroy_mood(current_mood);
        current_mood = NULL;
        memset(&statistics, 0, sizeof(statistics));
+       statistics.normalization_divisor = 1;
+}
+
+static void compute_correction_factors(int64_t sse)
+{
+       struct afs_statistics *s = &statistics;
+
+       if (s->num > 0) {
+               s->normalization_divisor = int_sqrt(s->last_played_qd)
+                       * int_sqrt(s->num_played_qd) / s->num / 100;
+               s->num_played_correction = sse - s->last_played_sum / s->num;
+               s->last_played_correction = s->num_played_sum / s->num;
+       }
+       if (s->num_played_correction == 0)
+               s->num_played_correction = 1;
+       if (s->normalization_divisor == 0)
+               s->normalization_divisor = 1;
+       if (s->last_played_correction == 0)
+               s->last_played_correction = 1;
 }
 
 /**
@@ -868,6 +618,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                .size = 0,
                .array = NULL
        };
+       /*
+        * We can not use the "now" pointer from sched.c here because we are
+        * called before schedule(), which initializes "now".
+        */
+       struct timeval rnow;
 
        if (mood_name) {
                struct mood *m;
@@ -904,9 +659,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        *errmsg = make_message("audio file loop failed");
                goto out;
        }
+       clock_get_realtime(&rnow);
+       compute_correction_factors(rnow.tv_sec);
+       log_statistics(rnow.tv_sec);
        for (i = 0; i < statistics.num; i++) {
-               struct admissible_file_info *a = aa.array + i;
-               ret = add_to_score_table(a->aft_row, a->score);
+               ret = add_to_score_table(aa.array[i]);
                if (ret < 0) {
                        if (errmsg)
                                *errmsg = make_message(
@@ -914,7 +671,6 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        goto out;
                }
        }
-       log_statistics();
        ret = statistics.num;
 out:
        free(aa.array);
diff --git a/mp.c b/mp.c
index aea767d99fea4b7bf0954504a03602766831a3b9..c537e72998d2ae46e0e9dbaa55175eaebf14f07f 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -389,6 +389,27 @@ MP_AFHI(frequency)
 MP_AFHI(channels)
 /** \endcond */
 
+/**
+ * Return the duration of the audio file from the afh info structure.
+ *
+ * \param ctx See \ref mp_path().
+ *
+ * The duration is computed by multiplying the number of chunks and the
+ * duration of one chunk.
+ *
+ * \return The approximate number of milliseconds.
+ */
+int64_t mp_duration(struct mp_context *ctx)
+{
+       struct timeval tmp;
+       int ret = get_afhi(ctx);
+
+       if (ret < 0)
+               return 0;
+       tv_scale(ctx->afhi.chunks_total, &ctx->afhi.chunk_tv, &tmp);
+       return tv2ms(&tmp);
+}
+
 /**
  * Define a function which extracts and returns the value of a meta tag.
  *
diff --git a/mp.h b/mp.h
index febbe3240f49ba81b08ca2168b10dd1189fc3f54..891bfb054e2f4cf1a922e7434a8467808f2e474b 100644 (file)
--- a/mp.h
+++ b/mp.h
@@ -139,6 +139,7 @@ bool mp_is_set(const char *attr, struct mp_context *ctx);
 char *mp_path(struct mp_context *ctx);
 int64_t mp_year(struct mp_context *ctx);
 int64_t mp_num_attributes_set(struct mp_context *ctx);
+int64_t mp_duration(struct mp_context *ctx);
 
 /* Generated with MP_AFSI() */
 /** \cond MP_AFSI */
diff --git a/net.c b/net.c
index 91200fc040bcfebafccf9e737fb65514af9ff8f2..e1951e5e8d87501b1ef9096d3f3df64117e904f0 100644 (file)
--- a/net.c
+++ b/net.c
@@ -288,7 +288,7 @@ struct flowopts *flowopt_new(void)
 {
        struct flowopts *new = para_malloc(sizeof(*new));
 
-       INIT_LIST_HEAD(&new->sockopts);
+       init_list_head(&new->sockopts);
        return new;
 }
 
index 0ad9d7db4e7f035dce140365b2b38abc144b6923..32891cbb012b66c0eaf5b8ca56f2c4288cd3bb1b 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -414,3 +414,11 @@ void hash_function(const char *data, unsigned long len, unsigned char *hash)
        SHA1_Update(&c, data, len);
        SHA1_Final(hash, &c);
 }
+
+void hash2_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       SHA256_CTX c;
+       SHA256_Init(&c);
+       SHA256_Update(&c, data, len);
+       SHA256_Final(hash, &c);
+}
index 311a514dc86ff6bfb01abf53d16a75672827af12..0565167c256a43bfe86a6442f195a320e1458db8 100644 (file)
@@ -199,9 +199,15 @@ static int oss_post_select(__a_unused struct sched *s, void *context)
 
                if (sound_device_is_busy())
                        return 0;
-               get_btr_sample_rate(btrn, &rate);
-               get_btr_channels(btrn, &ch);
-               get_btr_sample_format(btrn, &format);
+               ret = get_btr_sample_rate(btrn, &rate);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_channels(btrn, &ch);
+               if (ret < 0)
+                       goto out;
+               ret = get_btr_sample_format(btrn, &format);
+               if (ret < 0)
+                       goto out;
                ret = oss_init(wn, rate, ch, format);
                if (ret < 0)
                        goto out;
diff --git a/play.c b/play.c
index ffdc8555ce6686833b846f9610a3d81f6adf03cb..14fac42fd7b6e92566815d4c56b1f4b6b23c88e2 100644 (file)
--- a/play.c
+++ b/play.c
@@ -1102,7 +1102,7 @@ static void session_update_time_string(char *str, unsigned len)
                if (btr_get_input_queue_size(pt->btrn) > 0)
                        return;
        }
-       ie9_print_status_bar(str, len);
+       i9e_print_status_bar(str, len);
 }
 
 /*
diff --git a/sched.c b/sched.c
index a2903940fdaea1b24d6a49cfc2f54766c136070f..aac8efed1bcf8d897e6aef4d07973f2babc8d4f4 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -244,7 +244,7 @@ struct task *task_register(struct task_info *info, struct sched *s)
        assert(info->post_select);
 
        if (!s->task_list.next)
-               INIT_LIST_HEAD(&s->task_list);
+               init_list_head(&s->task_list);
 
        t->info = *info;
        t->name = para_strdup(info->name);
diff --git a/score.c b/score.c
index 983589333f088b600d85d9c0271044d7a21f8eb7..894e8ca3deae39f21552a6cd331237551e9c7a1b 100644 (file)
--- a/score.c
+++ b/score.c
@@ -21,15 +21,11 @@ static int ptr_compare(const struct osl_object *obj1, const struct osl_object *o
        return NUM_COMPARE(d1, d2);
 }
 
-/**
- * Compare the score of two audio files
- *
- * \param obj1 Pointer to the first score object.
- * \param obj2 Pointer to the second score object.
- *
- * This function first compares the score values as usual integers. If they compare as
- * equal, the address of \a obj1 and \a obj2 are compared. So this compare function
- * returns zero if and only if \a obj1 and \a obj2 point to the same memory area.
+/*
+ * This function first compares the score values. If they are equal, the
+ * addresses of the two objects are compared. Thus, the function returns
+ * "equal" only if the two objects alias each other, i.e., point to the same
+ * memory address.
  */
 static int score_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 {
@@ -92,16 +88,7 @@ int get_num_admissible_files(unsigned *num)
        return osl(osl_get_num_rows(score_table, num));
 }
 
-/**
- * Get the score of the audio file associated with given row of the score table.
- *
- * \param score_row Pointer to the row in the score table.
- * \param score Result is returned here on success.
- *
- * On errors (negative return value) the content of \a score is undefined.
- *
- * \return The return value of the underlying call to osl_get_object().
- */
+/* On errors (negative return value) the content of score is undefined. */
 static int get_score_of_row(void *score_row, long *score)
 {
        struct osl_object obj;
@@ -202,8 +189,7 @@ int score_update(const struct osl_row *aft_row, long percent)
  * \param score Result pointer.
  * \param aft_row Result pointer.
  *
- * \return Negative on errors, positive on success. Possible errors: Errors
- * returned by osl_get_object().
+ * \return Standard.
  */
 int get_score_and_aft_row(struct osl_row *score_row, long *score,
                struct osl_row **aft_row)
@@ -250,8 +236,7 @@ int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
  * \param aft_row Points to the row in the aft of the "best" audio file.
  * \param score Highest score value in the score table.
  *
- * \return Positive on success, negative on errors. Possible errors: Errors
- * returned by osl_rbtree_last_row(), osl_get_object().
+ * \return Standard.
  */
 int score_get_best(struct osl_row **aft_row, long *score)
 {
@@ -273,8 +258,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
  *
  * \param aft_row The file which is no longer admissible.
  *
- * \return Positive on success, negative on errors. Possible errors:
- * Errors returned by osl_get_row() and osl_del_row().
+ * \return Standard.
  *
  * \sa \ref score_add().
  */
@@ -315,20 +299,12 @@ int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
        return 1;
 }
 
-/* Close the score table. */
 static void score_close(void)
 {
        osl_close_table(score_table, OSL_FREE_VOLATILE);
        score_table = NULL;
 }
 
-/**
- * Open the score table.
- *
- * \param dir Unused.
- *
- * \return The return value of the underlying call to osl_open_table().
- */
 static int score_open(__a_unused const char *dir)
 {
        score_table_desc.dir = NULL; /* this table has only volatile columns */
index ea494d9a7b23f5ee87186917d11e883631ec9cbc..90242d5c9b5ccb125617004e16fbf9b64d5484f6 100644 (file)
  */
 void shutdown_client(struct sender_client *sc, struct sender_status *ss)
 {
-       PARA_INFO_LOG("shutting down %s on fd %d\n", sc->name, sc->fd);
-       free(sc->name);
        if (!process_is_command_handler()) {
+               PARA_INFO_LOG("shutting down %s on fd %d\n", sc->name, sc->fd);
                close(sc->fd);
                del_close_on_fork_list(sc->fd);
        }
+       free(sc->name);
        cq_destroy(sc->cq);
        list_del(&sc->node);
        free(sc->private_data);
@@ -136,9 +136,9 @@ void init_sender_status(struct sender_status *ss,
        }
        ss->default_port = default_port;
 
-       INIT_LIST_HEAD(&ss->client_list);
+       init_list_head(&ss->client_list);
        /* Initialize an access control list */
-       INIT_LIST_HEAD(&ss->acl);
+       init_list_head(&ss->acl);
        for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
                const char *arg = lls_string_val(i, acl_opt_result);
                char addr[16];
index d51e4e0bfe5b33c341ff744eda4f9380bd666844..1020e4fed98ac03d0981d80d517e68abf6e3a76f 100644 (file)
--- a/server.c
+++ b/server.c
@@ -2,31 +2,6 @@
 
 /** \file server.c Paraslash's main server. */
 
-/**
- * \mainpage Main data structures and selected APIs:
- *
- *     - Senders: \ref sender,
- *     - Audio file selector: \ref afs_info, \ref afs_table,
- *     - Audio format handler: \ref audio_format_handler, \ref afh_info
- *     - Receivers/filters/writers: \ref receiver, \ref receiver_node,
- *       \ref filter, \ref filter_node, \ref writer_node, \ref writer.
- *     - Scheduling: \ref sched.h,
- *     - Buffer trees: \ref buffer_tree.h,
- *     - Sideband API: \ref sideband.h,
- *     - Crypto: \ref crypt.h, \ref crypt_backend.h,
- *     - Error subsystem: \ref error.h,
- *     - Inter process communication: \ref ipc.h,
- *     - Forward error correction: \ref fec.h,
- *     - Daemons: \ref daemon.h,
- *     - Mixer API: \ref mix.h,
- *     - Interactive sessions: \ref interactive.h,
- *     - File descriptors: \ref fd.h,
- *     - Signals: \ref signal.h,
- *     - Networking: \ref net.h,
- *     - Time: \ref time.c,
- *     - Doubly linked lists: \ref list.h.
- */
-
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <signal.h>
index f23545adb68342322fbeaef4c45c73c663a969be..8e9ff2c5de79a6385b6472453f3dec417b9ac5cc 100644 (file)
@@ -103,7 +103,7 @@ static void sync_open(struct filter_node *fn)
        const struct lls_opt_result *r_b;
 
        ctx = fn->private_data = para_calloc(sizeof(*ctx));
-       INIT_LIST_HEAD(&ctx->buddies);
+       init_list_head(&ctx->buddies);
 
        /* create socket to listen for incoming packets */
        ret = makesock(
index 96a25d370a99d3fdcb6782ef9451ae86b1cfc3a4..68d75e3c3ef87dc089a3a1eec4d59fb647e73e44 100644 (file)
@@ -60,7 +60,7 @@ static void udp_close_target(struct sender_client *sc)
                return;
        if (ut->sent_fec_eof)
                return;
-       PARA_NOTICE_LOG("sending FEC EOF\n");
+       PARA_INFO_LOG("sending FEC EOF\n");
        len = vss_get_fec_eof_packet(&buf);
        /* Ignore write() errors since we are closing the target anyway. */
        if (write(sc->fd, buf, len))
@@ -72,7 +72,8 @@ static void udp_delete_target(struct sender_client *sc, const char *msg)
 {
        struct udp_target *ut = sc->private_data;
 
-       PARA_NOTICE_LOG("deleting %s (%s) from list\n", sc->name, msg);
+       if (!process_is_command_handler())
+               PARA_NOTICE_LOG("deleting %s (%s) from list\n", sc->name, msg);
        udp_close_target(sc);
        /* command handlers already called close_listed_fds() */
        if (!process_is_command_handler()) {
@@ -248,7 +249,7 @@ static int udp_init_fec(struct sender_client *sc)
        struct udp_target *ut = sc->private_data;
        int mps;
 
-       PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
+       PARA_INFO_LOG("sending to udp %s\n", sc->name);
        ut->sent_fec_eof = false;
        mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
@@ -394,7 +395,7 @@ static void udp_init_target_list(void)
        struct sender_command_data scd;
        int i;
 
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
                if (udp_resolve_target(arg, &scd) < 0)
@@ -427,7 +428,7 @@ static char *udp_help(void)
 /* Initialize the list of udp targets. */
 static void udp_send_init(void)
 {
-       INIT_LIST_HEAD(&targets);
+       init_list_head(&targets);
        sender_status = SENDER_off;
        udp_init_target_list();
        if (!OPT_GIVEN(UDP_NO_AUTOSTART))
diff --git a/upgrade_db.c b/upgrade_db.c
new file mode 100644 (file)
index 0000000..487d46c
--- /dev/null
@@ -0,0 +1,386 @@
+/* Copyright (C) 2020 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file upgrade_db.c Prepare the paraslash database for paraslash-0.7. */
+
+#include <osl.h>
+#include <lopsub.h>
+#include <regex.h>
+
+#include "upgrade_db.lsg.h"
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "fd.h"
+#include "crypt.h"
+#include "version.h"
+
+#define CMD_PTR (lls_cmd(0, upgrade_db_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_UPGRADE_DB_PARA_UPGRADE_DB_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
+#define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
+
+static int loglevel;
+INIT_STDERR_LOGGING(loglevel);
+
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+static void handle_help_flag(struct lls_parse_result *lpr)
+{
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
+}
+
+static struct stat *path_exists(const char *path)
+{
+       static struct stat sb;
+
+       if (stat(path, &sb) < 0)
+               return NULL;
+       return &sb;
+}
+
+static bool is_dir(const char *path)
+{
+       struct stat *sb = path_exists(path);
+       if (!sb)
+               return false;
+       return (sb->st_mode & S_IFMT) == S_IFDIR;
+}
+
+__noreturn static void die(const char *msg)
+{
+       PARA_EMERG_LOG("%s\n", msg);
+       exit(EXIT_FAILURE);
+}
+
+static int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+{
+       const char *str1 = obj1->data;
+       const char *str2 = obj2->data;
+       return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
+}
+
+static char *src_db_dir, *dst_db_dir, *src_aft_dir, *dst_aft_dir;
+
+static void set_paths(const struct lls_parse_result *lpr)
+{
+       char *home = para_homedir();
+
+       if (OPT_GIVEN(SRC_DATABASE_DIR, lpr))
+               src_db_dir = para_strdup(OPT_STRING_VAL(SRC_DATABASE_DIR,
+                       lpr));
+       else
+               src_db_dir = make_message(
+                       "%s/.paraslash/afs_database-0.4", home);
+       if (OPT_GIVEN(DST_DATABASE_DIR, lpr))
+               dst_db_dir = para_strdup(OPT_STRING_VAL(DST_DATABASE_DIR,
+                       lpr));
+       else
+               dst_db_dir = make_message(
+                       "%s/.paraslash/afs_database-0.7", home);
+       free(home);
+       src_aft_dir = make_message("%s/audio_files", src_db_dir);
+       dst_aft_dir = make_message("%s/audio-files", src_db_dir);
+       PARA_NOTICE_LOG("source aft dir: %s\n", src_aft_dir);
+       PARA_NOTICE_LOG("destination aft dir: %s\n", dst_aft_dir);
+}
+
+static void check_sanity(void)
+{
+       PARA_INFO_LOG("checking source and destination directories\n");
+       if (!is_dir(src_db_dir))
+               die("source db directory does not exist");
+       if (path_exists(dst_db_dir))
+               die("destination db already exists");
+       if (!is_dir(src_aft_dir))
+               die("source audio file table does not exist");
+       if (path_exists(dst_aft_dir))
+               die("destination audio file table already exists");
+}
+
+/** The columns of the audio file table (both old and new). */
+enum audio_file_table_columns {
+       /** The hash on the content of the audio file. */
+       AFTCOL_HASH,
+       /** The full path in the filesystem. */
+       AFTCOL_PATH,
+       /** The audio file selector info. */
+       AFTCOL_AFSI,
+       /** The audio format handler info. */
+       AFTCOL_AFHI,
+       /** The chunk table info and the chunk table of the audio file. */
+       AFTCOL_CHUNKS,
+       /** The number of columns of this table. */
+       NUM_AFT_COLUMNS
+};
+
+#define AFSI_SIZE 32
+
+static int src_aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
+{
+       return hash_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
+}
+
+static struct osl_column_description src_aft_cols[] = {
+       [AFTCOL_HASH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
+               .name = "hash",
+               .compare_function = src_aft_hash_compare,
+               .data_size = HASH_SIZE
+       },
+       [AFTCOL_PATH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_UNIQUE,
+               .name = "path",
+               .compare_function = string_compare,
+       },
+       [AFTCOL_AFSI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_FIXED_SIZE,
+               .name = "afs_info",
+               .data_size = AFSI_SIZE
+       },
+       [AFTCOL_AFHI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .name = "afh_info",
+       },
+       [AFTCOL_CHUNKS] = {
+               .storage_type = OSL_DISK_STORAGE,
+               .name = "chunks",
+       }
+};
+
+static struct osl_table_description src_aft_desc = {
+       .name = "audio_files",
+       .num_columns = NUM_AFT_COLUMNS,
+       .flags = OSL_LARGE_TABLE,
+       .column_descriptions = src_aft_cols
+};
+
+static struct osl_table *src_aft, *dst_aft;
+
+static void open_src_aft(void)
+{
+       int ret;
+
+       PARA_NOTICE_LOG("opening: %s\n", src_aft_dir);
+       src_aft_desc.dir = src_db_dir;
+       ret = osl(osl_open_table(&src_aft_desc, &src_aft));
+       if (ret < 0) {
+               PARA_EMERG_LOG("can not open source audio file table: %s\n",
+                        para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       PARA_INFO_LOG("successfully opened source audio file table\n");
+}
+
+static int dst_aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
+{
+       return hash2_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
+}
+
+/* identical to src_aft_cols except the comparator and the hash size. */
+static struct osl_column_description dst_aft_cols[] = {
+       [AFTCOL_HASH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
+               .name = "hash",
+               .compare_function = dst_aft_hash_compare,
+               .data_size = HASH2_SIZE
+       },
+       [AFTCOL_PATH] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_RBTREE | OSL_UNIQUE,
+               .name = "path",
+               .compare_function = string_compare,
+       },
+       [AFTCOL_AFSI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .storage_flags = OSL_FIXED_SIZE,
+               .name = "afs_info",
+               .data_size = AFSI_SIZE
+       },
+       [AFTCOL_AFHI] = {
+               .storage_type = OSL_MAPPED_STORAGE,
+               .name = "afh_info",
+       },
+       [AFTCOL_CHUNKS] = {
+               .storage_type = OSL_DISK_STORAGE,
+               .name = "chunks",
+       }
+};
+
+static struct osl_table_description dst_aft_desc = {
+       .name = "audio-files",
+       .num_columns = NUM_AFT_COLUMNS,
+       .flags = OSL_LARGE_TABLE,
+       .column_descriptions = dst_aft_cols
+};
+
+static int create_and_open_dst_aft(void)
+{
+       int ret;
+
+       PARA_NOTICE_LOG("creating %s\n", dst_aft_dir);
+       dst_aft_desc.dir = src_db_dir;
+       ret = osl(osl_create_table(&dst_aft_desc));
+       if (ret < 0) {
+               PARA_EMERG_LOG("could not create destination audio file table\n");
+               return ret;
+       }
+       ret = osl(osl_open_table(&dst_aft_desc, &dst_aft));
+       if (ret < 0) {
+               PARA_EMERG_LOG("could not open destination audio file table: %s\n",
+                        para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       PARA_INFO_LOG("successfully opened destination audio file table\n");
+       return 0;
+}
+
+static int copy_aft_row(struct osl_row *row, void *data)
+{
+       unsigned *n = data;
+       int i, ret;
+       unsigned char hash2[HASH2_SIZE] = "\0";
+       struct osl_object objs[NUM_AFT_COLUMNS] = {
+               [AFTCOL_HASH] = {.data = hash2, .size = HASH2_SIZE}
+       };
+
+       ret = osl(osl_open_disk_object(src_aft, row, AFTCOL_CHUNKS,
+               objs + AFTCOL_CHUNKS));
+       if (ret < 0) {
+               PARA_ERROR_LOG("can not open disk object: %s\n",
+                       para_strerror(-ret));
+               return ret;
+       }
+       for (i = 0; i < NUM_AFT_COLUMNS; i++) {
+               if (i == AFTCOL_HASH) /* never assign to this index */
+                       continue;
+               if (i == AFTCOL_CHUNKS) /* disk storage object handled above */
+                       continue;
+               /* mapped storage */
+               ret = osl(osl_get_object(src_aft, row, i, objs + i));
+               if (ret < 0) {
+                       PARA_ERROR_LOG("get_object (col = %d): %s\n",
+                               i, para_strerror(-ret));
+                       return ret;
+               }
+               if (i == AFTCOL_PATH)
+                       PARA_DEBUG_LOG("copying %s\n", (char *)objs[i].data);
+       }
+       (*n)++;
+       memcpy(hash2, n, sizeof(*n));
+       ret = osl(osl_add_row(dst_aft, objs));
+       if (ret < 0)
+               PARA_ERROR_LOG("failed to add row: %s\n", para_strerror(-ret));
+       osl_close_disk_object(objs + AFTCOL_CHUNKS);
+       return ret;
+}
+
+static int convert_aft(void)
+{
+       unsigned n;
+       int ret;
+
+       osl_get_num_rows(src_aft, &n);
+       PARA_NOTICE_LOG("converting hash of %u rows to sha256\n", n);
+       n = 0;
+       ret = osl(osl_rbtree_loop(src_aft, AFTCOL_HASH, &n, copy_aft_row));
+       if (ret < 0)
+               PARA_ERROR_LOG("osl_rbtree_loop failed\n");
+       return ret;
+}
+
+static int remove_source_aft(void)
+{
+       pid_t pid;
+       int fds[3] = {-1, -1, -1}; /* no redirection of stdin/stdout/stderr */
+       int ret, wstatus;
+       char *cmdline = make_message("rm -rf %s", src_aft_dir);
+
+       PARA_NOTICE_LOG("removing %s\n", src_aft_dir);
+       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
+       if (ret < 0) {
+               PARA_ERROR_LOG("exec failure\n");
+               goto out;
+       }
+       do {
+               ret = waitpid(pid, &wstatus, 0);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0)
+               PARA_ERROR_LOG("waitpid failure\n");
+out:
+       return ret;
+}
+
+static int rename_db(void)
+{
+       PARA_NOTICE_LOG("renaming %s -> %s\n", src_db_dir, dst_db_dir);
+       if (rename(src_db_dir, dst_db_dir) < 0) {
+               int ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("rename failed\n");
+               return ret;
+       }
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       struct lls_parse_result *lpr; /* command line */
+       char *errctx;
+       int ret;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+       set_paths(lpr);
+       check_sanity();
+       open_src_aft();
+       ret = create_and_open_dst_aft();
+       if (ret < 0)
+               goto close_src_aft;
+       ret = convert_aft();
+       if (ret < 0)
+               goto close_dst_aft;
+       ret = remove_source_aft();
+       if (ret < 0)
+               goto close_dst_aft;
+       ret = rename_db();
+close_dst_aft:
+       osl_close_table(dst_aft, OSL_MARK_CLEAN);
+close_src_aft:
+       PARA_INFO_LOG("closing audio file tables\n");
+       osl_close_table(src_aft, OSL_MARK_CLEAN);
+out:
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       } else {
+               PARA_WARNING_LOG("success. Now start para_server and force-add"
+                       " all audio files.\n");
+       }
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/vss.c b/vss.c
index 235219c1148aac2412b1b41879bf045d0b571af8..f9bf57b5575a7348fa5b5bb8a2db3c446d9dd92c 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -348,7 +348,7 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
 }
 
 static int vss_get_chunk(int chunk_num, struct vss_task *vsst,
-               char **buf, size_t *sz)
+               char **buf, uint32_t *len)
 {
        int ret;
 
@@ -363,15 +363,15 @@ static int vss_get_chunk(int chunk_num, struct vss_task *vsst,
        if (chunk_num == 0 && vsst->header_len > 0) {
                assert(vsst->header_buf);
                *buf = vsst->header_buf; /* stripped header */
-               *sz = vsst->header_len;
+               *len = vsst->header_len;
                return 0;
        }
        ret = afh_get_chunk(chunk_num, &mmd->afd.afhi,
                mmd->afd.audio_format_id, vsst->map, vsst->mapsize,
-               (const char **)buf, sz, &vsst->afh_context);
+               (const char **)buf, len, &vsst->afh_context);
        if (ret < 0) {
                *buf = NULL;
-               *sz = 0;
+               *len = 0;
        }
        return ret;
 }
@@ -380,7 +380,7 @@ static int compute_group_size(struct vss_task *vsst, struct fec_group *g,
                int max_bytes)
 {
        char *buf;
-       size_t len;
+       uint32_t len;
        int ret, i, max_chunks = PARA_MAX(1LU, 150 / tv2ms(vss_chunk_time()));
 
        if (g->first_chunk == 0) {
@@ -587,7 +587,7 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
        slice_copied = 0;
        for (c = g->first_chunk; c < g->first_chunk + g->num_chunks; c++) {
                char *buf;
-               size_t src_len;
+               uint32_t src_len;
                ret = vss_get_chunk(c, vsst, &buf, &src_len);
                if (ret < 0)
                        return ret;
@@ -1029,7 +1029,7 @@ static void vss_send(struct vss_task *vsst)
        struct timeval due;
        struct fec_client *fc, *tmp_fc;
        char *buf;
-       size_t len;
+       uint32_t len;
 
        if (!vsst->map || !vss_playing())
                return;
@@ -1139,7 +1139,7 @@ static int vss_post_select(struct sched *s, void *context)
        if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)
                recv_afs_result(vsst, &s->rfds);
        else if (FD_ISSET(vsst->afs_socket, &s->wfds)) {
-               PARA_NOTICE_LOG("requesting new fd from afs\n");
+               PARA_INFO_LOG("requesting new fd from afs\n");
                ret = write_buffer(vsst->afs_socket, "new");
                if (ret < 0)
                        PARA_CRIT_LOG("%s\n", para_strerror(-ret));
@@ -1177,7 +1177,7 @@ void vss_init(int afs_socket, struct sched *s)
        vsst->afs_socket = afs_socket;
        ms2tv(announce_time, &vsst->announce_tv);
        PARA_INFO_LOG("announce timeval: %lums\n", tv2ms(&vsst->announce_tv));
-       INIT_LIST_HEAD(&fec_client_list);
+       init_list_head(&fec_client_list);
        FOR_EACH_SENDER(i) {
                PARA_NOTICE_LOG("initializing %s sender\n", senders[i]->name);
                senders[i]->init();
@@ -1208,11 +1208,14 @@ void vss_init(int afs_socket, struct sched *s)
 void vss_shutdown(void)
 {
        int i;
+       bool is_command_handler = process_is_command_handler();
 
        FOR_EACH_SENDER(i) {
                if (!senders[i]->shutdown)
                        continue;
-               PARA_NOTICE_LOG("shutting down %s sender\n", senders[i]->name);
+               if (!is_command_handler)
+                       PARA_NOTICE_LOG("shutting down %s sender\n",
+                               senders[i]->name);
                senders[i]->shutdown();
        }
 }
index 8395864a00a6e376634272954708cd96739afc2f..f188ff2566ad30645365e73aa62e399439dfbf5e 100644 (file)
@@ -1,8 +1,8 @@
 <h1>About</h1>
 <hr>
 
-Paraslash is a collection of network audio streaming tools for Unix
-systems. It is written in C and released under the GPLv2.
+<p> Paraslash is a collection of network audio streaming tools for
+Unix systems. It is written in C and released under the GPLv2. </p>
 
 <ul>
        <li> Runs on Linux, FreeBSD, NetBSD </li>
@@ -10,15 +10,17 @@ systems. It is written in C and released under the GPLv2.
        <li> http, dccp and udp network streaming </li>
        <li> Stand-alone decoder, player, tagger </li>
        <li> Curses-based gui (<a href="gui.png">screenshot</a>) </li>
-       <li> Integrated volume normalizer, fader, alarm clock </li>
+       <li> Volume normalizer, fader, alarm clock </li>
        <li> Sophisticated audio file selector </li>
        <li> Command line interface with tab-completion </li>
        <li> Open source and well documented </li>
 </ul>
 
-<b> Author: </b> Andr&eacute; Noll,
-<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
-Homepage: <a href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
-<br>
-Comments and bug reports are welcome. Please provide the version of
-paraslash you are using and relevant parts of the logs.
+<p> Author: Andre Noll, <a
+href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
+Homepage: <a
+href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
+</p>
+
+<p> Comments and bug reports are welcome. Please provide the version
+of paraslash you are using and relevant parts of the logs. </p>
index d6d690a16bd5299a6fdb8d304cfe48fc13dbc6c3..32efe9c3b191ff2bfa6df658480e63a884e3952f 100644 (file)
        [<a href="para_filter.man.html">para_filter</a>]
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
-       [<a href="para_mixer.man.html">para_mixere</a>]
+       [<a href="para_mixer.man.html">para_mixer</a>]
        [<a href="para_play.man.html">para_play</a>]
+       [<a href="para_upgrade_db.man.html">para_upgrade_db</a>]
 </p>
 
 <h2> Source code documentation </h2>
 
 <ul>
-       <li> <a href="doxygen/html/index.html">API Reference</a> </li>
+       <li> <a href="doxygen/html/files.html">API Reference</a> </li>
 </ul>
index 9ef92b7af654ec06f193ad5dd3941648b82a83b8..2b31e5d76c7f9f7c348ba08f852dc6c5a5780f73 100644 (file)
@@ -1,8 +1,9 @@
 <h1>Download</h1>
 <hr>
 
-Paraslash is only available as source code, no binary packages are
-provided at this point. There are several ways to download the source:
+<p> Paraslash is only available as source code, no binary packages
+are provided at this point. There are several ways to download the
+source: </p>
 
 <ul>
        <li> <em> git</em>.
@@ -19,7 +20,7 @@ provided at this point. There are several ways to download the source:
                checkout of any of the four integration branches maint,
                master, next, pu (see the
 
-               <a href="manual.html#Git.branches">Git branches</a>
+               <a href="manual.html#Git-branches">Git branches</a>
 
                section of the manual). All previous releases
                correspond to tagged commits and may be checked out
index 38572c940dac47cafcd170a64c04a21953dc74bf..cceeff1a3097597c261254b020c1ffbe0dd3ef19 100644 (file)
@@ -1,5 +1,3 @@
 <hr>
-       </td>
-       </table>
 </body>
 </html>
index a5d01204939d789cf4841499ce31119bfd5e1c35..1e102a6b3e7732c43d3c8eaa56797540022cb134 100644 (file)
 <body>
        <table>
        <tr>
-               <td>
-                       <a title="paraslash homepage" href=".">
-                               <img src="paraslash.png" alt="paraslash">
+               <td rowspan="2">
+                       <a title="paraslash homepage" href="./index.html">
+                               <img src="paraslash.svg" alt="paraslash">
                        </a>
                </td>
                <td>
-                       <h3>Paraslash network audio streaming tools</h3>
+                       <span class="slogan">Paraslash Audio Streaming</span>
                </td>
        </tr>
        <tr>
-               <td valign="top">
-                       <br>
-                       <a href=".">About</a><br>
-                       <a href="news.html">News</a><br>
-                       <a href="download.html">Download</a><br>
-                       <a href="documentation.html">Documentation</a><br>
-                       <a href="devel.html">Development</a><br>
-               </td>
                <td>
+                       <a href="./index.html">About</a>&nbsp;
+                       &nbsp;<a href="news.html">News</a>&nbsp;
+                       &nbsp;<a href="download.html">Download</a>&nbsp;
+                       &nbsp;<a href="documentation.html">Documentation</a>&nbsp;
+                       &nbsp;<a href="devel.html">Development</a>
+               </td>
+       </tr>
+       </table>
+<hr>
index 98235b8621324bc9176d2f9803ea4cf097afd83a..29ec02d40eb266c3f1eaa2617b6c609cd1d90b40 100644 (file)
 <body>
        <table>
        <tr>
-               <td>
-                       <a title="paraslash homepage" href="../..//">
-                               <img src="../../paraslash.png" alt="paraslash">
+               <td rowspan="2">
+                       <a title="paraslash homepage" href="../../index.html">
+                               <img src="../../paraslash.svg" alt="paraslash">
                        </a>
                </td>
                <td>
-                       <h3>Paraslash network audio streaming tools</h3>
+                       <span class="slogan">Paraslash Audio Streaming</span>
                </td>
        </tr>
        <tr>
-               <td valign="top">
-                       <br>
-                       <a href="../..">About</a><br>
-                       <a href="../../news.html">News</a><br>
-                       <a href="../../download.html">Download</a><br>
-                       <a href="../../documentation.html">Documentation</a><br>
-                       <a href="../../devel.html">Development</a><br>
-               </td>
                <td>
-                       <h1>API Reference</h1>
-                       <hr />
-
+                       <a href="../../index.html">About</a>&nbsp;
+                       &nbsp;<a href="../../news.html">News</a>&nbsp;
+                       &nbsp;<a href="../../download.html">Download</a>&nbsp;
+                       &nbsp;<a href="../../documentation.html">Documentation</a>&nbsp;
+                       &nbsp;<a href="../../devel.html">Development</a>
+               </td>
+       </tr>
+       </table>
+<hr>
index 06ff03362e8a33dfe2b02bd2c214892dd3ba59c4..65a0dd69fd3e52d529e63b8ea039a3b32a7cbce4 100644 (file)
Binary files a/web/images/paraslash.ico and b/web/images/paraslash.ico differ
diff --git a/web/images/paraslash.png b/web/images/paraslash.png
deleted file mode 100644 (file)
index 804752c..0000000
Binary files a/web/images/paraslash.png and /dev/null differ
diff --git a/web/images/paraslash.svg b/web/images/paraslash.svg
new file mode 100644 (file)
index 0000000..b0616a7
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" standalone="no"?>
+<svg
+       xmlns="http://www.w3.org/2000/svg"
+       width="80" height="100"
+       xmlns:xlink="http://www.w3.org/1999/xlink"
+>
+       <defs>
+               <radialGradient id="gradient" r="70%">
+                       <stop offset="0%" stop-color="#d40"/>
+                       <stop offset="100%" stop-color="#010"/>
+               </radialGradient>
+
+               <g
+                       id="bow"
+                       stroke="#111" stroke-width="0"
+                       fill="url(#gradient)"
+               >
+                       <path d="M 40,50 c 0,-21 30,-21 30,0"/>
+               </g>
+       </defs>
+       <g transform="scale(1.0, 1.3) translate(0, -11)">
+               <circle
+                       cx="40" cy="50" r="38"
+                       stroke-width="0"
+                       fill="url(#gradient)"
+               />
+               <use
+                       xlink:href="#bow"
+                       transform="
+                               scale(1.5)
+                               rotate(320, 40,50)
+                               translate(-14, -24)
+                       "
+               />
+               <use
+                       xlink:href="#bow"
+                       transform="
+                               translate(0, -36)
+                               scale(1.5)
+                               rotate(140, 40,50)
+                       "
+               />
+       </g>
+</svg>
index db28a699bdd05516d17de4bf837cc110f737df7e..78834a4d48be70f178dba5ffed88f5db8761745c 100644 (file)
@@ -488,10 +488,10 @@ An empty database is created with
        para_client init
 
 This initializes a couple of empty tables under
-~/.paraslash/afs_database-0.4. You normally don't need to look at these
+~/.paraslash/afs_database-0.7. You normally don't need to look at these
 tables, but it's good to know that you can start from scratch with
 
-       rm -rf ~/.paraslash/afs_database-0.4
+       rm -rf ~/.paraslash/afs_database-0.7
 
 in case something went wrong.
 
@@ -613,10 +613,11 @@ while the second part is the session key.
 
 - para_client receives the encrypted buffer and decrypts it with the
 user's private key, thereby obtaining the challenge buffer and the
-session key. It sends the SHA1 hash value of the challenge back to
-para_server and stores the session key for further use.
+session key. It hashes the challenge buffer with a crytographic hash
+function, sends the hash value back to para_server and stores the
+session key for further use.
 
-- para_server also computes the SHA1 hash of the challenge and compares
+- para_server also computes the hash value of the challenge and compares
 it against what was sent back by the client.
 
 - If the two hashes do not match, the authentication has failed and
@@ -630,7 +631,7 @@ the session key known to both peers.
 paraslash relies on the quality of the pseudo-random bytes provided
 by the crypto library (openssl or libgcrypt), on the security of
 the implementation of the RSA and AES crypto routines and on the
-infeasibility to invert the SHA1 function.
+infeasibility to invert the hash function.
 
 Neither para_server or para_client create RSA keys on their
 own. This has to be done once for each user as sketched in
@@ -804,10 +805,11 @@ This is the most important and usually also the largest table of the
 AFS database. It contains the information needed to stream each audio
 file. In particular the following data is stored for each audio file.
 
-- SHA1 hash value of the audio file contents. This is computed once
-when the file is added to the database. Whenever AFS selects this
-audio file for streaming the hash value is recomputed and checked
-against the value stored in the database to detect content changes.
+- The cryptographic hash value of the audio file contents. This is
+computed once when the file is added to the database. Whenever AFS
+selects this audio file for streaming the hash value is recomputed
+and checked against the value stored in the database to detect
+content changes.
 
 - The time when this audio file was last played.
 
@@ -1023,6 +1025,7 @@ Keyword              |    Type | Semantic value
 `bitrate`            | integer | The average bitrate
 `frequency`          | integer | The output sample rate
 `channels`           | integer | The number of channels
+`duration`           | integer | The number of milliseconds
 `is_set("foo")`      | boolean | True if attribute "foo" is set.
 
 [\*] For most audio formats, the year tag is stored as a string. It
@@ -1146,7 +1149,7 @@ if the "-a" switch is given:
 File renames and content changes
 --------------------------------
 
-Since the audio file selector knows the SHA1 of each audio file that
+Since the audio file selector knows the hash of each audio file that
 has been added to the afs database, it recognizes if the content of
 a file has changed, e.g. because an ID3 tag was added or modified.
 Also, if a file has been renamed or moved to a different location,
@@ -1175,14 +1178,14 @@ may refuse to start again because of "dirty osl tables". In this
 case you'll have to run the oslfsck program of libosl to fix your
 database:
 
-       oslfsck -fd ~/.paraslash/afs_database-0.4
+       oslfsck -fd ~/.paraslash/afs_database-0.7
 
 However, make sure para_server isn't running before executing oslfsck.
 
 If you don't mind to recreate your database you can start
 from scratch by removing the entire database directory, i.e.
 
-       rm -rf ~/.paraslash/afs_database-0.4
+       rm -rf ~/.paraslash/afs_database-0.7
 
 Be aware that this removes all attribute definitions, all playlists
 and all mood definitions and requires to re-initialize the tables.
@@ -2074,11 +2077,11 @@ here are the most important points.
 - Don't leave whitespace at the end of lines.
 - The limit on the length of lines is 80 columns.
 - Use K&R style for placing braces and spaces:
-
+<pre>
                if (x is true) {
                        we do y
                }
-
+</pre>
 - Use a space after (most) keywords.
 - Do not add spaces around (inside) parenthesized expressions.
 - Use one space around (on each side of) most binary and ternary operators.
index e8c43489f4681bcbd8bb859db80582b5fa83c068..2043fbb70b2afdf9ef243200920910bf564f2a1e 100644 (file)
@@ -1,46 +1,29 @@
-body,h1,h2,h3,h4,h5,h6,p,center,td,th,ul,dl,div {
-       font-family: sans-serif;
-}
-
 body {
+       font-family: sans-serif;
        background-color: black;
        color: #bbbbbb;
-       margin: 0px;
-}
-
-table {
-       padding: 8px 4px;
-}
-
-th {
-       padding: 2px 5px;
-       font-size: 100%;
-       text-align: left;
+       margin: 20px;
 }
 
 td {
-       padding: 2px 5px;
-       font-size: 100%;
+       padding: 2px 10px 2px 10px;
        vertical-align: top;
 }
 
+span.slogan {
+       font-size: 200%;
+       font-weight: bold;
+       color: #ddd;
+}
+
 a {
        color: #cc3322;
 }
 
 hr {
-       height: 1px;
-       border: none;
        border-top: 1px solid yellow;
 }
 
-img {
-       float: right;
-       border-width: 0px;
-}
-
-caption { font-weight: bold }
-
 /* doxgen */
 
 /* Data structure index. Box with clickable letters */
diff --git a/write.h b/write.h
index cb0beff812121b338e3749e80f63ad2cb99999db..833cb69a5cb6373bae819d5b7c323e1d567c6104 100644 (file)
--- a/write.h
+++ b/write.h
@@ -66,7 +66,7 @@ const struct writer *writer_get(int wid);
 const char *writer_name(int wid);
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+int get_btr_channels(struct btr_node *btrn, int32_t *result);
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result);
 void print_writer_helps(bool detailed);
index 14cc98a4189a5f74907f8bfcf416f62bcc2515c5..41c3eb23728ab11cde71f837d4c58a1ef6908d24 100644 (file)
@@ -174,24 +174,24 @@ void print_writer_helps(bool detailed)
        }
 }
 
-static void get_btr_value(struct btr_node *btrn, const char *cmd,
+static int get_btr_value(struct btr_node *btrn, const char *cmd,
                int32_t *result)
 {
        char *buf = NULL;
        int ret = btr_exec_up(btrn, cmd, &buf);
 
-       if (ret < 0) {
-               /*
-                * This really should not happen. It means one of our parent
-                * nodes died unexpectedly. Proceed with fingers crossed.
-                */
-               PARA_CRIT_LOG("cmd %s: %s\n", cmd, para_strerror(-ret));
-               *result = 0;
-               return;
-       }
+       *result = 0;
+       /*
+        * Errors may happen when the decoder returns EOF before the writer had
+        * a chance to query the buffer tree for the channel count, sample rate
+        * etc.
+        */
+       if (ret < 0)
+               return ret;
        ret = para_atoi32(buf, result);
        assert(ret >= 0);
        free(buf);
+       return ret;
 }
 
 /**
@@ -200,11 +200,11 @@ static void get_btr_value(struct btr_node *btrn, const char *cmd,
  * \param btrn Where to start the search.
  * \param result Filled in by this function.
  *
- * This function is assumed to succeed and terminates on errors.
+ * \return Standard.
  */
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_rate", result);
+       return get_btr_value(btrn, "sample_rate", result);
 }
 
 /**
@@ -212,10 +212,12 @@ void get_btr_sample_rate(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result See \ref get_btr_sample_rate.
+ *
+ * \return Standard.
  */
-void get_btr_channels(struct btr_node *btrn, int32_t *result)
+int get_btr_channels(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "channels", result);
+       return get_btr_value(btrn, "channels", result);
 }
 
 /**
@@ -223,8 +225,10 @@ void get_btr_channels(struct btr_node *btrn, int32_t *result)
  *
  * \param btrn See \ref get_btr_sample_rate.
  * \param result Contains the sample format as an enum sample_format type.
+ *
+ * \return Standard.
  */
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result)
+int get_btr_sample_format(struct btr_node *btrn, int32_t *result)
 {
-       get_btr_value(btrn, "sample_format", result);
+       return get_btr_value(btrn, "sample_format", result);
 }
index 2dbe21b779a82eebd79403fa5f60bc56925489dd..4a53db2741754bf6359733af8b42ca60ca255e92 100644 (file)
--- a/yy/mp.lex
+++ b/yy/mp.lex
@@ -74,6 +74,7 @@ lyrics_id {return LYRICS_ID;}
 bitrate {return BITRATE;}
 frequency {return FREQUENCY;}
 channels {return CHANNELS;}
+duration {return DURATION;}
 true {return TRUE;}
 false {return FALSE;}
 
diff --git a/yy/mp.y b/yy/mp.y
index 0f2c9cb8b256a82eeb2fac78114f6026902a24f2..06d76101daf42550ea192d1c963c32d5da6635d2 100644 (file)
--- a/yy/mp.y
+++ b/yy/mp.y
@@ -210,6 +210,9 @@ static int eval_node(struct mp_ast_node *node, struct mp_context *ctx,
        case CHANNELS:
                result->intval= mp_channels(ctx);
                return ST_INTVAL;
+       case DURATION:
+               result->intval= mp_duration(ctx);
+               return ST_INTVAL;
        /* bools */
        case IS_SET:
                arg = node->children[0]->sv.strval;
@@ -327,6 +330,7 @@ bool mp_eval_ast(struct mp_ast_node *root, struct mp_context *ctx)
 %token <node> BITRATE
 %token <node> FREQUENCY
 %token <node> CHANNELS
+%token <node> DURATION
 %token <node> FALSE TRUE
 
 /* keywords without semantic value */
@@ -377,6 +381,7 @@ exp: NUM {$$ = $1;}
        | BITRATE {$$ = mp_new_ast_leaf_node(BITRATE);}
        | FREQUENCY {$$ = mp_new_ast_leaf_node(FREQUENCY);}
        | CHANNELS {$$ = mp_new_ast_leaf_node(CHANNELS);}
+       | DURATION {$$ = mp_new_ast_leaf_node(DURATION);}
 ;
 
 boolexp: IS_SET '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(IS_SET, $3);}