Merge branch 'refs/heads/t/afh-preserve' into next
authorAndre Noll <maan@tuebingen.mpg.de>
Thu, 6 Jun 2019 06:36:15 +0000 (08:36 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Thu, 6 Jun 2019 06:36:15 +0000 (08:36 +0200)
* refs/heads/t/afh-preserve:
  afh: Implement --preserve.

31 files changed:
Makefile.real
afs.c
afs.h
aft.c
ao_write.c
blob.c
check_wav.c
command.c
compress_filter.c
configure.ac
crypt_backend.h
crypt_common.c
daemon.c
error.h
gcrypt.c
interactive.c
m4/lls/filter_cmd.suite.m4
m4/lls/play.suite.m4
m4/lls/server_cmd.suite.m4
m4/lls/write_cmd.suite.m4
mixer.c
mp3_afh.c
ogg_afh_common.c
openssl.c
oss_write.c
play.c
spxdec_filter.c
string.c
vss.c
web/about.in.html
web/manual.md

index dae48f0..5df27ba 100644 (file)
@@ -18,7 +18,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 := 2018
+COPYRIGHT_YEAR := 2019
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -110,7 +110,6 @@ CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
 CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
 CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
-CPPFLAGS += -I/usr/local/include
 CPPFLAGS += -I$(lls_suite_dir)
 CPPFLAGS += -I$(yy_build_dir)
 CPPFLAGS += $(lopsub_cppflags)
@@ -191,7 +190,7 @@ $(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags)
 $(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
 
 $(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/openssl.o: CPPFLAGS += $(openssl_cppflags)
 $(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
 $(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
 $(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
diff --git a/afs.c b/afs.c
index 4fe2140..d9cddeb 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -589,8 +589,9 @@ static int com_select_callback(struct afs_callback_arg *aca)
        para_printf(&aca->pbout, "activating dummy mood\n");
        activate_mood_or_playlist(NULL, &num_admissible, NULL);
 out:
-       para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
-               current_mop? current_mop : "dummy mood", num_admissible);
+       para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n",
+               current_mop? current_mop : "dummy mood", num_admissible,
+                       num_admissible == 1? "" : "s");
 free_lpr:
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
diff --git a/afs.h b/afs.h
index b0d283f..cfa9cc6 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -285,10 +285,12 @@ int playlist_check_callback(struct afs_callback_arg *aca);
                struct para_buffer *pb, void *data); \
        extern struct osl_table *table_name ## _table;
 
+/** \cond blob_symbols */
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
 DECLARE_BLOB_SYMBOLS(images, img);
 DECLARE_BLOB_SYMBOLS(moods, mood);
 DECLARE_BLOB_SYMBOLS(playlists, pl);
+/** \endcond blob_symbols */
 
 /** The columns of an abstract blob table. */
 enum blob_table_columns {
diff --git a/aft.c b/aft.c
index 5d4e647..c04d4f9 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -50,46 +50,28 @@ static struct ls_data status_item_ls_data;
 
 /** The different sorting methods of the ls command. */
 enum ls_sorting_method {
-       /** -sp (default) */
-       LS_SORT_BY_PATH,
-       /** -ss */
-       LS_SORT_BY_SCORE,
-       /** -sl */
-       LS_SORT_BY_LAST_PLAYED,
-       /** -sn */
-       LS_SORT_BY_NUM_PLAYED,
-       /** -sf */
-       LS_SORT_BY_FREQUENCY,
-       /** -sc */
-       LS_SORT_BY_CHANNELS,
-       /** -si */
-       LS_SORT_BY_IMAGE_ID,
-       /** -sy */
-       LS_SORT_BY_LYRICS_ID,
-       /** -sb */
-       LS_SORT_BY_BITRATE,
-       /** -sd */
-       LS_SORT_BY_DURATION,
-       /** -sa */
-       LS_SORT_BY_AUDIO_FORMAT,
-       /** -sh */
-       LS_SORT_BY_HASH,
+       LS_SORT_BY_PATH, /**< -s=p (default) */
+       LS_SORT_BY_SCORE, /**< -s=s */
+       LS_SORT_BY_LAST_PLAYED, /**< -s=l */
+       LS_SORT_BY_NUM_PLAYED, /**< -s=n */
+       LS_SORT_BY_FREQUENCY, /**< -s=f */
+       LS_SORT_BY_CHANNELS, /**< -s=c */
+       LS_SORT_BY_IMAGE_ID, /**< -s=i */
+       LS_SORT_BY_LYRICS_ID, /**< -s=y */
+       LS_SORT_BY_BITRATE, /**< -s=b */
+       LS_SORT_BY_DURATION, /**< -s=d */
+       LS_SORT_BY_AUDIO_FORMAT, /**< -s=a */
+       LS_SORT_BY_HASH, /**< -s=h */
 };
 
 /** The different listing modes of the ls command. */
 enum ls_listing_mode {
-       /** Default listing mode. */
-       LS_MODE_SHORT,
-       /** -l or -ll */
-       LS_MODE_LONG,
-       /** -lv */
-       LS_MODE_VERBOSE,
-       /** -lm */
-       LS_MODE_MBOX,
-       /** -lc */
-       LS_MODE_CHUNKS,
-       /** -lp */
-       LS_MODE_PARSER,
+       LS_MODE_SHORT, /**< Default listing mode. */
+       LS_MODE_LONG, /**< -l or -l=l */
+       LS_MODE_VERBOSE, /** -l=v */
+       LS_MODE_MBOX, /** -l=m */
+       LS_MODE_CHUNKS, /** -l=c */
+       LS_MODE_PARSER, /** -l=p */
 };
 
 /**
@@ -414,7 +396,7 @@ static void save_chunk_table(struct afh_info *afhi, char *buf)
 {
        uint32_t n;
 
-       if (!afhi->chunk_table)
+       if (!afhi->chunk_table || afhi->chunks_total == 0)
                return;
        for (n = 0; n <= afhi->chunks_total; n++)
                write_u32(buf + 4 * n, afhi->chunk_table[n]);
@@ -983,13 +965,18 @@ out:
        WRITE_STATUS_ITEM(pb, SI_file_size, "%ld\n", statbuf.st_size / 1024);
 }
 
+/**
+ * Deallocate and invalidate the status item strings.
+ *
+ * This needs to be a public function so that afs.c can call it on shutdown.
+ */
 void free_status_items(void)
 {
        freep(&status_items);
        freep(&parser_friendly_status_items);
 }
 
-static int make_status_items(void)
+static void make_status_items(void)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
        char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
@@ -1000,7 +987,7 @@ static int make_status_items(void)
 
        free_status_items();
        if (!status_item_ls_data.path) /* no audio file open */
-               return 0;
+               return;
        ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
        assert(ret >= 0);
        time(&current_time);
@@ -1020,10 +1007,12 @@ static int make_status_items(void)
        parser_friendly_status_items = pb.buf;
        ret = 1;
 out:
-       if (ret < 0)
+       if (ret < 0) {
+               PARA_WARNING_LOG("could not create status items: %s\n",
+                       para_strerror(-ret));
                free_status_items();
+       }
        lls_free_parse_result(opts.lpr, cmd);
-       return ret;
 }
 
 /**
index e2858b9..447dea8 100644 (file)
@@ -87,7 +87,7 @@ static int aow_set_sample_format(unsigned sample_rate, unsigned channels,
                case SF_U8:
                case SF_U16_LE:
                case SF_U16_BE:
-                       return -E_AO_BAD_SAMPLE_FORMAT;
+                       return -E_BAD_SAMPLE_FORMAT;
                case SF_S8:
                        /* no need to set byte_format */
                        result->bits = 8;
@@ -159,8 +159,8 @@ static void aow_show_drivers(void)
 
                if (info->type == AO_TYPE_FILE)
                        continue;
-               PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
-               PARA_DEBUG_LOG("priority: %d", info->priority);
+               PARA_DEBUG_LOG("name: %s: %s\n", info->short_name, info->name);
+               PARA_DEBUG_LOG("priority: %d\n", info->priority);
                for (j = 0; j < info->option_count; j++) {
                        tmp = make_message("%s%s%s", keys? keys : "",
                                keys? ", " : "",
@@ -168,9 +168,9 @@ static void aow_show_drivers(void)
                        free(keys);
                        keys = tmp;
                }
-               PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+               PARA_DEBUG_LOG("keys: %s\n", keys? keys : "[none]");
                free(keys);
-               PARA_DEBUG_LOG("comment: %s", info->comment?
+               PARA_DEBUG_LOG("comment: %s\n", info->comment?
                        info->comment : "[none]");
        }
 }
diff --git a/blob.c b/blob.c
index 890b922..4ecbc45 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -72,7 +72,6 @@ static struct osl_column_description blob_cols[] = {
 /** Define a pointer to an osl blob table with a canonical name. */
 #define DEFINE_BLOB_TABLE_PTR(table_name) struct osl_table *table_name ## _table;
 
-
 /** Define a blob table. */
 #define INIT_BLOB_TABLE(table_name) \
        DEFINE_BLOB_TABLE_DESC(table_name); \
@@ -104,7 +103,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
                para_printf(&aca->pbout, "cannot list %s\n", name);
                return ret;
        }
-       id = *(uint32_t *)obj.data;
+       id = read_u32(obj.data);
        para_printf(&aca->pbout, "%u\t%s\n", id, name);
        return 1;
 }
@@ -121,6 +120,7 @@ static int com_lsblob_callback(const struct lls_command * const cmd,
                .action = print_blob,
        };
        int ret;
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        pmd.lpr = aca->lpr;
        assert(ret >= 0);
@@ -176,6 +176,7 @@ static int com_catblob_callback(const struct lls_command * const cmd,
                .data = &aca->fd,
                .action = cat_blob
        };
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        assert(ret >= 0);
        pmd.lpr = aca->lpr;
@@ -207,6 +208,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
 {
        struct afs_callback_arg *aca = data;
        int ret = osl(osl_del_row(table, row));
+
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot remove %s\n", name);
                return ret;
@@ -226,6 +228,7 @@ static int com_rmblob_callback(const struct lls_command * const cmd,
                .data = aca,
                .action = remove_blob
        };
+
        ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
        assert(ret >= 0);
        pmd.lpr = aca->lpr;
@@ -263,7 +266,8 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = aca->query.data;
        size_t name_len = strlen(name) + 1;
-       uint32_t id;
+       uint32_t id = (uint32_t)-1; /* STFU, gcc */
+       char id_buf[sizeof(id)];
        unsigned num_rows;
        int ret;
 
@@ -271,10 +275,15 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        if (ret < 0)
                goto out;
        if (!num_rows) { /* this is the first entry ever added */
-               /* insert dummy row containing the id */
-               id = 2; /* this entry will be entry #1, so 2 is the next */
-               objs[BLOBCOL_ID].data = &id;
-               objs[BLOBCOL_ID].size = sizeof(id);
+               /*
+                * Insert dummy row containing the next free ID. Since we are
+                * about to insert the first blob with ID 1, the next free ID
+                * will be 2.
+                */
+               id = 2U;
+               write_u32(id_buf, id);
+               objs[BLOBCOL_ID].data = id_buf;
+               objs[BLOBCOL_ID].size = sizeof(id_buf);
                objs[BLOBCOL_NAME].data = "";
                objs[BLOBCOL_NAME].size = 1;
                objs[BLOBCOL_DEF].data = "";
@@ -293,7 +302,7 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
                        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
                        if (ret < 0)
                                goto out;
-                       id = *(uint32_t *)obj.data;
+                       id = read_u32(obj.data);
                        obj.data = name + name_len;
                        obj.size = aca->query.size - name_len;
                        ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj));
@@ -308,15 +317,17 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
                ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
-               id = *(uint32_t *)obj.data + 1;
-               obj.data = &id;
+               id = read_u32(obj.data) + 1;
+               write_u32(id_buf, id);
+               obj.data = &id_buf;
                ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
        }
        id--;
-       objs[BLOBCOL_ID].data = &id;
-       objs[BLOBCOL_ID].size = sizeof(id);
+       write_u32(id_buf, id);
+       objs[BLOBCOL_ID].data = &id_buf;
+       objs[BLOBCOL_ID].size = sizeof(id_buf);
        objs[BLOBCOL_NAME].data = name;
        objs[BLOBCOL_NAME].size = name_len;
        objs[BLOBCOL_DEF].data = name + name_len;
@@ -374,7 +385,7 @@ again:
  *
  * This function is called from the addblob command handlers to instruct the
  * afs process to store the input in a blob table. Input is read and decrypted
- * from the file descriptor given by cc and appended to arg_obj, which contains
+ * from the file descriptor given by cc and appended to a buffer which also contains
  * the name of the blob to create. The combined buffer is made available to the
  * afs process via the callback method.
  */
@@ -509,7 +520,6 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
-
 static int blob_get_def_by_name(struct osl_table *table, char *name,
                struct osl_object *def)
 {
@@ -561,6 +571,7 @@ static int blob_get_name_and_def_by_row(struct osl_table *table,
 {
        struct osl_object obj;
        int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
+
        if (ret < 0)
                return ret;
        *name = obj.data;
@@ -596,6 +607,7 @@ static int blob_open(struct osl_table **table,
                const char *dir)
 {
        int ret;
+
        desc->dir = dir;
        ret = osl(osl_open_table(desc, table));
        if (ret >= 0)
index f0990bf..89ebdac 100644 (file)
@@ -11,6 +11,7 @@
 #include "buffer_tree.h"
 #include "error.h"
 #include "check_wav.h"
+#include "portable_io.h"
 
 /** Length of a standard wav header. */
 #define WAV_HEADER_LEN 44
@@ -155,9 +156,9 @@ int check_wav_post_select(struct check_wav_context *cwc)
        PARA_INFO_LOG("found wav header\n");
        cwc->state = CWS_HAVE_HEADER;
        /* Only set those values which have not already been set. */
-       cwc->channels = (unsigned)a[22];
-       cwc->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
-       bps = a[34] + ((unsigned)a[35] << 8);
+       cwc->channels = a[22];
+       cwc->sample_rate = read_u32(a + 24);
+       bps = read_u16(a + 34);
        if (bps != 8 && bps != 16) {
                PARA_WARNING_LOG("%u bps not supported, assuming 16\n",
                        bps);
index 6adb945..481fd0f 100644 (file)
--- a/command.c
+++ b/command.c
@@ -279,7 +279,7 @@ static int check_sender_args(struct command_context *cc,
 }
 
 /**
- * Send a sideband packet through a blocking file descriptor.
+ * Receive a sideband packet from a blocking file descriptor.
  *
  * \param scc fd and crypto keys.
  * \param expected_band The expected band designator.
@@ -663,8 +663,7 @@ EXPORT_SERVER_CMD_HANDLER(nomore);
 static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
 {
        long promille;
-       int ret, backwards = 0;
-       unsigned i;
+       int i, ret;
        char c, *errctx;
 
        ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
@@ -672,20 +671,33 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
                send_errctx(cc, errctx);
                return ret;
        }
-       if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c)))
-               return -E_COMMAND_SYNTAX;
-       if (ret > 1 && c == '-')
-               backwards = 1; /* jmp backwards */
+       ret = para_atoi32(lls_input(0, lpr), &i);
+       if (ret < 0) {
+               if (ret != -E_ATOI_JUNK_AT_END)
+                       return ret;
+               /*
+                * Compatibility code to keep the historic syntax (ff 30-)
+                * working. This can be removed after 0.7.0.
+                */
+               ret = sscanf(lls_input(0, lpr), "%i%c", &i, &c);
+               if (ret <= 0)
+                       return -E_COMMAND_SYNTAX;
+               if (ret > 1 && c == '-') {
+                       PARA_WARNING_LOG("use of obsolete syntax\n");
+                       i = -i;
+               }
+       }
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
                goto out;
        ret = 1;
        promille = (1000 * mmd->current_chunk) / mmd->afd.afhi.chunks_total;
-       if (backwards)
-               promille -= 1000 * i / mmd->afd.afhi.seconds_total;
-       else
-               promille += 1000 * i / mmd->afd.afhi.seconds_total;
+       /*
+        * We need this cast because without it the expression on the right
+        * hand side is of unsigned type.
+        */
+       promille += 1000 * i / (int)mmd->afd.afhi.seconds_total;
        if (promille < 0)
                promille = 0;
        if (promille > 1000) {
index 69a982e..15bed6d 100644 (file)
@@ -48,8 +48,7 @@ static int compress_post_select(__a_unused struct sched *s, void *context)
        size_t length, i;
        int16_t *ip, *op;
        uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
-       unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr),
-               mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U;
+       unsigned mask = (1U << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1U;
        //inplace = false;
 next_buffer:
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -78,19 +77,22 @@ next_buffer:
                        neg = true;
                }
                sample *= pcd->current_gain;
-               sample >>= gain_shift;
+               sample >>= inertia + 1;
                if (sample > 32767) { /* clip */
+                       PARA_WARNING_LOG("clip: %d\n", sample);
                        sample = 32767;
                        pcd->current_gain = (3 * pcd->current_gain +
                                (1 << inertia)) / 4;
                        pcd->peak = 0;
                } else if (sample > pcd->peak)
                        pcd->peak = sample;
+               sample >>= U32_OPTVAL(DAMP, fn->lpr);
                op[i] = neg? -sample : sample;
                if (++pcd->num_samples & mask)
                        continue;
 //             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
 //                     pcd->peak);
+
                if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) {
                        if (pcd->current_gain < pcd->max_gain)
                                pcd->current_gain++;
@@ -121,10 +123,43 @@ static void compress_open(struct filter_node *fn)
        fn->private_data = pcd;
        fn->min_iqs = 2; /* 16 bit audio */
        pcd->current_gain = 1U << inertia;
-       pcd->max_gain = 1U << (inertia + aggressiveness);
+       pcd->max_gain = (1U << inertia) * (1.0 + 3.0 * aggressiveness / 10.0);
+}
+
+static void *compress_setup(const struct lls_parse_result *lpr)
+{
+       uint32_t val;
+
+       val = U32_OPTVAL(BLOCKSIZE, lpr);
+       if (val == 0 || val > 31) {
+               PARA_EMERG_LOG("blocksize (%u) out of range\n", val);
+               exit(EXIT_FAILURE);
+       }
+       val = U32_OPTVAL(AGGRESSIVENESS, lpr);
+       if (val > 10) {
+               PARA_EMERG_LOG("aggressiveness (%u) out of range\n", val);
+               exit(EXIT_FAILURE);
+       }
+       val = U32_OPTVAL(INERTIA, lpr);
+       if (val == 0 || val > 14) {
+               PARA_EMERG_LOG("inertia (%u) out of range\n", val);
+               exit(EXIT_FAILURE);
+       }
+       val = U32_OPTVAL(TARGET_LEVEL, lpr);
+       if (val > 32767) {
+               PARA_EMERG_LOG("target-level (%u) out of range\n", val);
+               exit(EXIT_FAILURE);
+       }
+       val = U32_OPTVAL(DAMP, lpr);
+       if (val > 16) {
+               PARA_EMERG_LOG("damp (%u) out of range\n", val);
+               exit(EXIT_FAILURE);
+       }
+       return NULL; /* no need for a config structure */
 }
 
 const struct filter lsg_filter_cmd_com_compress_user_data = {
+       .setup = compress_setup,
        .open = compress_open,
        .close = compress_close,
        .pre_select = generic_filter_pre_select,
index 053996d..d993e52 100644 (file)
@@ -96,8 +96,24 @@ AC_CHECK_HEADER(openssl/ssl.h, [], [HAVE_OPENSSL=no])
 AC_CHECK_LIB([crypto], [RAND_bytes], [], [HAVE_OPENSSL=no])
 LIB_SUBST_FLAGS(openssl)
 if test $HAVE_OPENSSL = yes; then
-       AC_CHECK_LIB([crypto], [RSA_set0_key],
-               AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl-1.1]))
+       HAVE_RSA_SET0_KEY=yes
+       AC_CHECK_DECL([RSA_set0_key], [], [], [#include <openssl/rsa.h>])
+       AC_CHECK_LIB([crypto], [RSA_set0_key], [], [])
+       if test "$ac_cv_have_decl_RSA_set0_key" != "$ac_cv_lib_crypto_RSA_set0_key"; then
+               AC_MSG_ERROR([openssl header/library mismatch])
+       fi
+       test "$ac_cv_have_decl_RSA_set0_key" = yes &&
+               AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl >= 1.1])
+
+       HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes
+       AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [],
+               [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no],
+               [#include <openssl/rsa.h>])
+       AC_CHECK_LIB([crypto], [CRYPTO_cleanup_all_ex_data], [],
+               [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no])
+       test $HAVE_CRYPTO_CLEANUP_ALL_EX_DATA = yes &&
+               AC_DEFINE([HAVE_CRYPTO_CLEANUP_ALL_EX_DATA], [1],
+                       [not available on FreeBSD 12])
 fi
 UNSTASH_FLAGS
 ######################################################################### gcrypt
index 175a688..b0998d8 100644 (file)
@@ -7,6 +7,13 @@
 /** AES block size in bytes. */
 #define AES_CRT128_BLOCK_SIZE 16
 
-int decode_ssh_key(const char *filename, unsigned char **blob,
+int decode_public_key(const char *filename, unsigned char **blob,
                size_t *decoded_size);
+int decode_private_key(const char *key_file, unsigned char **result,
+               size_t *blob_size);
+/** Legacy PEM keys (openssh-7.7 and earlier, paraslash.0.6.2 and earlier) */
+#define PKT_PEM (0)
+/** OPENSSH keys (since openssh-7.8, paraslash.0.6.3) */
+#define PKT_OPENSSH (1)
 int check_private_key_file(const char *file);
+int find_openssh_bignum_offset(const unsigned char *data, int len);
index 235b8b8..c1e40d9 100644 (file)
@@ -85,7 +85,7 @@ static int check_ssh_key_header(const unsigned char *blob, int blen)
  *
  * \sa \ref uudecode().
  */
-int decode_ssh_key(const char *filename, unsigned char **blob,
+int decode_public_key(const char *filename, unsigned char **blob,
                size_t *decoded_size)
 {
        int ret, ret2;
@@ -159,3 +159,161 @@ int hash_compare(unsigned char *h1, unsigned char *h2)
        }
        return 0;
 }
+
+/**
+ * Check header of an openssh private key and compute bignum offset.
+ *
+ * \param data The base64-decoded key.
+ * \param len The size of the decoded key.
+ *
+ * Several assumptions are made about the key. Most notably, we only support
+ * single unencrypted keys without comments.
+ *
+ * \return The offset at which the first bignum of the private key (the public
+ * exponent n) starts. Negative error code on failure.
+ */
+int find_openssh_bignum_offset(const unsigned char *data, int len)
+{
+       /*
+        * Unencrypted keys without comments always start with the below byte
+        * sequence. See PROTOCOL.key of the openssh package.
+        */
+       static const unsigned char valid_openssh_header[] = {
+               /* string "openssh-key-v1" */
+               0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2d, 0x6b, 0x65,
+                       0x79, 0x2d, 0x76, 0x31,
+               /* length of the cipher name */
+               0x00, 0x00, 0x00, 0x00, 0x04,
+               /* cipher name: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of the kdfname (only used for encrypted keys) */
+               0x00, 0x00, 0x00, 0x04,
+               /* kdfname: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of kdfoptions */
+               0x00, 0x00, 0x00, 0x00,
+               /* number of keys */
+               0x00, 0x00, 0x00, 0x01,
+       };
+       uint32_t val;
+       const unsigned char *p, *end = data + len;
+
+       if (len <= sizeof(valid_openssh_header) + 4)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(data, valid_openssh_header, sizeof(valid_openssh_header)))
+               return -E_OPENSSH_PARSE;
+       p = data + sizeof(valid_openssh_header);
+       /* length of public key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += val + 4;
+       /* length of private key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += 4;
+       /* two equal random integers ("checkint") */
+       if (p + 8 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != read_u32_be(p + 4))
+               return -E_OPENSSH_PARSE;
+       p += 8;
+       /* length of name of key type "ssh-rsa" */
+       if (p + 11 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != 7)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(p + 4, "ssh-rsa", 7))
+               return -E_OPENSSH_PARSE;
+       p += 11;
+       return p - data;
+}
+
+/** Private PEM keys (legacy format) start with this header. */
+#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) start with this header. */
+#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
+/** Private PEM keys (legacy format) end with this footer. */
+#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) end with this footer. */
+#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----"
+
+/**
+ * Decode an openssh-v1 (aka RFC4716) or PEM (aka ASN.1) private key.
+ *
+ * \param key_file The private key file (usually id_rsa).
+ * \param result Pointer to base64-decoded blob is returned here.
+ * \param blob_size The size of the decoded blob.
+ *
+ * This only checks header and footer and base64-decodes the part in between.
+ * No attempt to read the decoded part is made.
+ *
+ * \return Negative on errors, PKT_PEM or PKT_OPENSSH on success, indicating
+ * the type of key.
+ */
+int decode_private_key(const char *key_file, unsigned char **result,
+               size_t *blob_size)
+{
+       int ret, ret2, i, j, key_type;
+       void *map;
+       size_t map_size, key_size;
+       unsigned char *blob = NULL;
+       char *begin, *footer, *key;
+
+       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+       if (ret < 0)
+               goto out;
+       ret = -E_KEY_MARKER;
+       if (strncmp(map, PRIVATE_PEM_KEY_HEADER,
+                       strlen(PRIVATE_PEM_KEY_HEADER)) == 0) {
+               key_type = PKT_PEM;
+               begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
+               footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
+               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
+       } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
+                       strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
+               key_type = PKT_OPENSSH;
+               begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER);
+               footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER);
+               PARA_INFO_LOG("detected openssh key %s\n", key_file);
+       } else
+               goto unmap;
+       if (!footer)
+               goto unmap;
+       /* skip whitespace at the beginning */
+       for (; begin < footer; begin++) {
+               if (para_isspace(*begin))
+                       continue;
+               break;
+       }
+       ret = -E_KEY_MARKER;
+       if (begin >= footer)
+               goto unmap;
+
+       key_size = footer - begin;
+       key = para_malloc(key_size + 1);
+       for (i = 0, j = 0; begin + i < footer; i++) {
+               if (para_isspace(begin[i]))
+                       continue;
+               key[j++] = begin[i];
+       }
+       key[j] = '\0';
+       ret = base64_decode(key, j, (char **)&blob, blob_size);
+       free(key);
+       if (ret < 0)
+               goto unmap;
+       ret = key_type;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       if (ret < 0) {
+               free(blob);
+               blob = NULL;
+       }
+out:
+       *result = blob;
+       return ret;
+}
+
index bfa8148..a4e2f31 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -146,6 +146,22 @@ void daemon_set_loglevel(const char *loglevel)
        me->loglevel = ret;
 }
 
+/**
+ * Register functions to be called before and after a message is logged.
+ *
+ * \param pre_log_hook Called before the message is logged.
+ * \param post_log_hook Called after the message is logged.
+ *
+ * The purpose of this function is to provide a primitive for multi-threaded
+ * applications to serialize the access to the log facility, preventing
+ * interleaving log messages. This can be achieved by having the pre-log hook
+ * acquire a lock which blocks the other threads on the attempt to log a
+ * message at the same time.  The post-log hook is responsible for releasing
+ * the lock.
+ *
+ * If these hooks are unnecessary, for example because the application is
+ * single-threaded, this function does not need to be called.
+ */
 void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void))
 {
        me->pre_log_hook = pre_log_hook;
diff --git a/error.h b/error.h
index 02f4224..3e34ada 100644 (file)
--- a/error.h
+++ b/error.h
@@ -24,7 +24,6 @@
        PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
-       PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
        PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
        PARA_ERROR(AO_EOF, "ao: end of file"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
@@ -84,6 +83,7 @@
        PARA_ERROR(EMPTY, "file is empty"), \
        PARA_ERROR(ENCRYPT, "encrypt error"), \
        PARA_ERROR(EOF, "end of file"), \
+       PARA_ERROR(EOP, "end of playlist"), \
        PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
        PARA_ERROR(FECDEC_EOF, "received eof packet"), \
        PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
        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"), \
        PARA_ERROR(OPUS_DECODE, "opus decode error"), \
        PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
 enum para_error_codes {PARA_ERRORS};
 #undef PARA_ERROR
 #define PARA_ERROR(err, msg) msg
-/** Array of error strings. */
+/** All .c files need the declararation of the array of error strings. */
 extern const char * const para_errlist[];
+/** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
 
 /**
index 694c0ad..dbe4900 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -106,66 +106,6 @@ static const char *gcrypt_strerror(gcry_error_t gret)
        return gcry_strerror(gcry_err_code(gret));
 }
 
-static int decode_key(const char *key_file, const char *header_str,
-               const char *footer_str, unsigned char **result)
-{
-       int ret, ret2, i, j;
-       void *map;
-       size_t map_size, key_size, blob_size;
-       unsigned char *blob = NULL;
-       char *begin, *footer, *key;
-
-       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
-       if (ret < 0)
-               goto out;
-       ret = -E_KEY_MARKER;
-       if (strncmp(map, header_str, strlen(header_str)))
-               goto unmap;
-       footer = strstr(map, footer_str);
-       ret = -E_KEY_MARKER;
-       if (!footer)
-               goto unmap;
-       begin = map + strlen(header_str);
-       /* skip whitespace at the beginning */
-       for (; begin < footer; begin++) {
-               if (para_isspace(*begin))
-                       continue;
-               break;
-       }
-       ret = -E_KEY_MARKER;
-       if (begin >= footer)
-               goto unmap;
-
-       key_size = footer - begin;
-       key = para_malloc(key_size + 1);
-       for (i = 0, j = 0; begin + i < footer; i++) {
-               if (para_isspace(begin[i]))
-                       continue;
-               key[j++] = begin[i];
-       }
-       key[j] = '\0';
-       ret = base64_decode(key, j, (char **)&blob, &blob_size);
-       free(key);
-       if (ret < 0)
-               goto free_unmap;
-       ret = blob_size;
-       goto unmap;
-free_unmap:
-       free(blob);
-       blob = NULL;
-unmap:
-       ret2 = para_munmap(map, map_size);
-       if (ret >= 0 && ret2 < 0)
-               ret = ret2;
-       if (ret < 0) {
-               free(blob);
-               blob = NULL;
-       }
-out:
-       *result = blob;
-       return ret;
-}
-
 /** ASN Types and their code. */
 enum asn1_types {
        /** The next object is an integer. */
@@ -207,11 +147,11 @@ static inline int get_long_form_num_length_bytes(unsigned char c)
 
 /*
  * Returns: Number of bytes scanned. This may differ from the value returned via
- * bn_bytes because the latter does not include the ASN.1 prefix and a leading
- * zero is not considered as an additional byte for bn_bytes.
+ * bitsp because the latter does not include the ASN.1 prefix and a leading
+ * zero is not considered as an additional byte for the number of bits.
  */
-static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
-               int *bn_bytes)
+static int read_pem_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
+               unsigned *bitsp)
 {
        int i, bn_size;
        gcry_error_t gret;
@@ -249,8 +189,8 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
                cp++;
                bn_size--;
        }
-       if (bn_bytes)
-               *bn_bytes = bn_size;
+       if (bitsp)
+               *bitsp = bn_size * 8;
        cp += bn_size;
 //     unsigned char *buf;
 //     gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn);
@@ -258,7 +198,55 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
        return cp - start;
 }
 
-static int find_privkey_bignum_offset(const unsigned char *data, int len)
+struct rsa_params {
+       gcry_mpi_t n, e, d, p, q, u;
+};
+
+static int read_pem_rsa_params(unsigned char *start, unsigned char *end,
+               struct rsa_params *p)
+{
+       unsigned char *cp = start;
+       unsigned bits;
+       int ret;
+
+       ret = read_pem_bignum(cp, end, &p->n, &bits);
+       if (ret < 0)
+               return ret;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->e, NULL);
+       if (ret < 0)
+               goto release_n;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->d, NULL);
+       if (ret < 0)
+               goto release_e;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->p, NULL);
+       if (ret < 0)
+               goto release_d;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->q, NULL);
+       if (ret < 0)
+               goto release_p;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->u, NULL);
+       if (ret < 0)
+               goto release_q;
+       return bits;
+release_q:
+       gcry_mpi_release(p->q);
+release_p:
+       gcry_mpi_release(p->p);
+release_d:
+       gcry_mpi_release(p->d);
+release_e:
+       gcry_mpi_release(p->e);
+release_n:
+       gcry_mpi_release(p->n);
+       return ret;
+}
+
+static int find_pem_bignum_offset(const unsigned char *data, int len)
 {
        const unsigned char *p = data, *end = data + len;
 
@@ -290,96 +278,131 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len)
        return p - data;
 }
 
-/** Private keys start with this header. */
-#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
-/** Private keys end with this footer. */
-#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
-
-static int get_private_key(const char *key_file, struct asymmetric_key **result)
+static int read_openssh_bignum(unsigned char *start, unsigned char *end,
+               gcry_mpi_t *bn, unsigned *bitsp)
 {
-       gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL,
-               u = NULL;
-       unsigned char *blob, *cp, *end;
-       int blob_size, ret, n_size;
        gcry_error_t gret;
-       size_t erroff;
-       gcry_sexp_t sexp;
-       struct asymmetric_key *key;
+       size_t nscanned;
+       unsigned bits;
 
-       *result = NULL;
-       ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER,
-               &blob);
-       if (ret < 0)
-               return ret;
-       blob_size = ret;
-       end = blob + blob_size;
-       ret = find_privkey_bignum_offset(blob, blob_size);
-       if (ret < 0)
-               goto free_blob;
-       PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
-       cp = blob + ret;
+       gret = gcry_mpi_scan(bn, GCRYMPI_FMT_SSH, start, end - start, &nscanned);
+       if (gret) {
+               PARA_ERROR_LOG("gcry_mpi_scan: %s\n",
+                       gcry_strerror(gcry_err_code(gret)));
+               return -E_MPI_SCAN;
+       }
+       bits = (nscanned - 4 - (start[4] == '\0')) * 8;
+       if (bitsp)
+               *bitsp = bits;
+       PARA_DEBUG_LOG("scanned %u-bit bignum\n", bits);
+       return nscanned;
+}
 
-       ret = read_bignum(cp, end, &n, &n_size);
+static int read_openssh_rsa_params(unsigned char *start, unsigned char *end,
+               struct rsa_params *p)
+{
+       unsigned char *cp = start;
+       unsigned bits;
+       int ret;
+
+       ret = read_openssh_bignum(cp, end, &p->n, &bits);
        if (ret < 0)
-               goto free_blob;
+               return ret;
        cp += ret;
-
-       ret = read_bignum(cp, end, &e, NULL);
+       ret = read_openssh_bignum(cp, end, &p->e, NULL);
        if (ret < 0)
                goto release_n;
        cp += ret;
-
-       ret = read_bignum(cp, end, &d, NULL);
+       ret = read_openssh_bignum(cp, end, &p->d, NULL);
        if (ret < 0)
                goto release_e;
        cp += ret;
-
-       ret = read_bignum(cp, end, &p, NULL);
+       ret = read_openssh_bignum(cp, end, &p->u, NULL);
        if (ret < 0)
                goto release_d;
        cp += ret;
-
-       ret = read_bignum(cp, end, &q, NULL);
+       ret = read_openssh_bignum(cp, end, &p->p, NULL);
        if (ret < 0)
-               goto release_p;
+               goto release_u;
        cp += ret;
-       ret = read_bignum(cp, end, &u, NULL);
+       ret = read_openssh_bignum(cp, end, &p->q, NULL);
        if (ret < 0)
-               goto release_q;
+               goto release_p;
+       return bits;
+release_p:
+       gcry_mpi_release(p->p);
+release_u:
+       gcry_mpi_release(p->u);
+release_d:
+       gcry_mpi_release(p->d);
+release_e:
+       gcry_mpi_release(p->e);
+release_n:
+       gcry_mpi_release(p->n);
+       return ret;
+}
+
+static int get_private_key(const char *key_file, struct asymmetric_key **result)
+{
+       struct rsa_params params;
+       unsigned char *blob, *end;
+       unsigned bits;
+       int ret, key_type;
+       gcry_error_t gret;
+       size_t erroff, blob_size;
+       gcry_sexp_t sexp;
+       struct asymmetric_key *key;
+
+       *result = NULL;
+       ret = decode_private_key(key_file, &blob, &blob_size);
+       if (ret < 0)
+               return ret;
+       key_type = ret;
+       end = blob + blob_size;
+       if (key_type == PKT_PEM)
+               ret = find_pem_bignum_offset(blob, blob_size);
+       else
+               ret = find_openssh_bignum_offset(blob, blob_size);
+       if (ret < 0)
+               goto free_blob;
+       PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
+       if (key_type == PKT_PEM)
+               ret = read_pem_rsa_params(blob + ret, end, &params);
+       else
+               ret = read_openssh_rsa_params(blob + ret, end, &params);
+       if (ret < 0)
+               goto free_blob;
+       bits = ret;
        /*
         * OpenSSL uses slightly different parameters than gcrypt. To use these
         * parameters we need to swap the values of p and q and recompute u.
         */
-       if (gcry_mpi_cmp(pq) > 0) {
-               gcry_mpi_swap(pq);
-               gcry_mpi_invm(u, p, q);
+       if (gcry_mpi_cmp(params.p, params.q) > 0) {
+               gcry_mpi_swap(params.p, params.q);
+               gcry_mpi_invm(params.u, params.p, params.q);
        }
-       gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP,
-               n, e, d, p, q, u);
+       gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, params.n,
+               params.e, params.d, params.p, params.q, params.u);
 
        if (gret) {
                PARA_ERROR_LOG("offset %zu: %s\n", erroff,
                        gcry_strerror(gcry_err_code(gret)));
                ret = -E_SEXP_BUILD;
-               goto release_u;
+               goto free_params;
        }
        key = para_malloc(sizeof(*key));
        key->sexp = sexp;
        *result = key;
-       ret = n_size * 8;
+       ret = bits;
        PARA_INFO_LOG("succesfully read %d bit private key\n", ret);
-release_u:
-       gcry_mpi_release(u);
-release_q:
-       gcry_mpi_release(q);
-release_p:
-       gcry_mpi_release(p);
-release_d:
-       gcry_mpi_release(d);
-release_e:
-       gcry_mpi_release(e);
-release_n:
-       gcry_mpi_release(n);
+free_params:
+       gcry_mpi_release(params.n);
+       gcry_mpi_release(params.e);
+       gcry_mpi_release(params.d);
+       gcry_mpi_release(params.u);
+       gcry_mpi_release(params.p);
+       gcry_mpi_release(params.q);
+
 free_blob:
        free(blob);
        return ret;
@@ -390,34 +413,25 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        unsigned char *blob, *p, *end;
        int ret;
        gcry_error_t gret;
-       size_t nr_scanned, erroff, decoded_size;
+       size_t erroff, decoded_size;
        gcry_mpi_t e, n;
        gcry_sexp_t sexp;
        struct asymmetric_key *key;
+       unsigned bits;
 
-       ret = decode_ssh_key(key_file, &blob, &decoded_size);
+       ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                return ret;
        p = blob + ret;
        end = blob + decoded_size;
        PARA_DEBUG_LOG("scanning modulus and public exponent\n");
-       gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
-       if (gret) {
-               ret = -E_MPI_SCAN;
-               PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+       ret = read_openssh_bignum(p, end, &e, NULL);
+       if (ret < 0)
                goto free_blob;
-       }
-       PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned);
-       p += nr_scanned;
-       if (p >= end)
-               goto release_e;
-       gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
-       if (gret) {
-               ret = -E_MPI_SCAN;
-               PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+       p += ret;
+       ret = read_openssh_bignum(p, end, &n, &bits);
+       if (ret < 0)
                goto release_e;
-       }
-       PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned);
        gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
        if (gret) {
                PARA_ERROR_LOG("offset %zu: %s\n", erroff,
@@ -425,12 +439,12 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
                ret = -E_SEXP_BUILD;
                goto release_n;
        }
-       ret = ROUND_DOWN(nr_scanned, 32);
-       PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
+       PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
        key = para_malloc(sizeof(*key));
        key->num_bytes = ret;
        key->sexp = sexp;
        *result = key;
+       ret = bits;
 release_n:
        gcry_mpi_release(n);
 release_e:
index 190cdf2..a819730 100644 (file)
@@ -762,17 +762,25 @@ int i9e_print_completions(struct i9e_completer *completers)
        ci.argc = create_argv(ci.buffer, " ", &ci.argv);
        ci.word_num = compute_word_num(ci.buffer, " ", ci.point);
 
+       /* determine the current word to complete */
        end = ci.buffer + ci.point;
+
+       if (*end == ' ') {
+               if (ci.point == 0 || ci.buffer[ci.point - 1] == ' ') {
+                       ci.word = para_strdup(NULL);
+                       goto create_matches;
+               } else /* The cursor is positioned right after a word */
+                       end--;
+       }
        for (p = end; p > ci.buffer && *p != ' '; p--)
                ; /* nothing */
        if (*p == ' ')
                p++;
-
        n = end - p + 1;
        ci.word = para_malloc(n + 1);
        strncpy(ci.word, p, n);
        ci.word[n] = '\0';
-
+create_matches:
        PARA_DEBUG_LOG("line: %s, point: %d (%c), wordnum: %d, word: %s\n",
                ci.buffer, ci.point, ci.buffer[ci.point], ci.word_num, ci.word);
        if (ci.word_num == 0)
index d269d23..c026a62 100644 (file)
@@ -25,7 +25,7 @@ caption = filters
        purpose = dynamically adjust the volume of an audio stream
        [option blocksize]
                short_opt = b
-               summary = use blocks of size 2**bits
+               summary = adjust volume after each block of size 2**bits (1-31)
                typestr = bits
                arg_info = required_arg
                arg_type = uint32
@@ -35,32 +35,52 @@ caption = filters
                [/help]
        [option aggressiveness]
                short_opt = a
-               summary = controls the maximum amount to amplify by
+               summary = controls the maximum amount to amplify by (0-10)
                typestr = bits
                arg_info = required_arg
                arg_type = uint32
                default_val = 4
+               [help]
+                       This controls the maximal gain factor. Zero means to not amplify
+                       at all while the value 10 corresponds to maximal gain factor which
+                       results in a 4-fold increase in volume.
+               [/help]
        [option inertia]
                short_opt = i
-               summary = how much inertia ramping has
+               summary = how much inertia ramping has (1-14)
                typestr = bits
                arg_info = required_arg
                arg_type = uint32
                default_val = 6
+               [help]
+                       Larger values cause smaller volume adjustments.
+               [/help]
        [option target-level]
                short_opt = t
-               summary = target signal level (0-32768)
+               summary = target signal level (0-32767)
                typestr = level
                arg_info = required_arg
                arg_type = uint32
-               default_val = 20000
+               default_val = 16384
+               [help]
+                       If the peak of the previous block is less than the target level,
+                       volume is increased slightly for the next block. Otherwise it is
+                       decreased. The default value is chosen to minimize clipping. There
+                       is usually no reason to change it.
+               [/help]
        [option damp]
                short_opt = d
-               summary = if non-zero, scale down after normalizing
+               summary = if non-zero, scale down after normalizing (0-16)
                typestr = bits
                arg_info = required_arg
                arg_type = uint32
                default_val = 0
+               [help]
+                       This scales down the volume of the audio stream by factor 2**bits.
+                       This is mostly useful if another audio application (e.g., a video
+                       game) is running in parallel and the relative volume of the audio
+                       stream is too high.
+               [/help]
 [subcommand fecdec]
        purpose = decode a (lossy) input stream using forward error correction
 [subcommand flacdec]
index 4af2a05..f2220f1 100644 (file)
@@ -9,10 +9,11 @@ version-string = GIT_VERSION()
        [description]
                para_play operates either in command mode or in insert mode. In insert
                mode it presents a prompt and allows the user to enter commands like
-               stop, play, pause etc. In command mode the current audio file and the
-               playback position are shown and the program reads single key strokes
-               from stdin. Keys may be mapped to commands so that the configured
-               command is executed when a mapped key is pressed.
+               play, pause, quit, etc. In command mode the current audio file and the
+               playback position are shown instead of the prompt/command line, and
+               the program reads single key strokes from stdin. Keys may be mapped
+               to commands so that the configured command is executed whenever a
+               mapped key is pressed.
        [/description]
        m4_include(common-option-section.m4)
        m4_include(help.m4)
@@ -25,6 +26,16 @@ version-string = GIT_VERSION()
        [option randomize]
                short_opt = z
                summary = randomize playlist at startup
+       [option end-of-playlist]
+               summary = what to do after the last file has been played
+               arg_info = required_arg
+               arg_type = string
+               typestr = behaviour
+               values = {
+                       EOP_LOOP = "loop",
+                       EOP_STOP = "stop",
+                       EOP_QUIT = "quit"
+                }
        [option key-map]
                short_opt = k
                summary = map a key to a command
index 079589d..2a39907 100644 (file)
@@ -124,18 +124,18 @@ aux_info_prefix = Permissions:
                summary = enable verbose mode
 
 [subcommand ff]
-       purpose = jump N seconds forward or backward
-       synopsis = n[-]
+       purpose = jump forward or backward in the current audio file
+       synopsis = seconds
        aux_info = VSS_READ | VSS_WRITE
        [description]
-               This sets the 'R' (reposition request) bit of the vss status flags
-               which enqueues a request to jump n seconds forwards or backwards.
-
-               Example:
-
-                    para_client ff 30-
-
-               jumps 30 seconds backwards.
+               This enqueues a request to reposition the audio stream according to
+               the argument, which may be a signed or an unsigned integer. Negative
+               values correspond to backward jumps.
+
+               If a negative number is given whose absolute value exceeds the current
+               postition of the stream, a jump to the beginning of the audio file
+               is performed. If a positive amount of seconds is given which exceeds
+               the remaining time of the audio file, the next audio file is loaded.
 
        [/description]
 
index 9d8ee75..102afed 100644 (file)
@@ -38,8 +38,9 @@ caption = writers
                arg_info = required_arg
                arg_type = string
                [help]
-                       If this is not given, the driver with the highest priority (see below)
-                       will be used.
+                       If this is not given, the driver with the highest priority will be
+                       used. The list of available drivers and their priorities is shown in
+                       debug mode.
                [/help]
        [option ao-option]
                short_opt = o
@@ -51,7 +52,7 @@ caption = writers
                [help]
                        For each time this option is given, the supplied key-value pair is
                        appended to the list of options for the driver. Invalid keys are
-                       silently ignored.
+                       silently ignored. The list of available keys is shown in debug mode.
                [/help]
 [subcommand oss]
        purpose = output plugin for the Open Sound System
@@ -62,18 +63,6 @@ caption = writers
                arg_info = required_arg
                arg_type = string
                default_val = /dev/dsp
-[subcommand osx]
-       purpose = output plugin for Mac OS coreaudio
-       [option numbuffers]
-               short_opt = n
-               summary = number of audio buffers to allocate
-               typestr = num
-               arg_info = required_arg
-               arg_type = uint32
-               default_val = 20
-               [help]
-                       Increase if you get buffer underruns.
-               [/help]
 [subcommand file]
        purpose = output plugin that writes to a local file
        [option filename]
diff --git a/mixer.c b/mixer.c
index ad674bf..55282e7 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
-/** \file mixer.c A volume fader and alarm clock for OSS. */
+/** \file mixer.c A volume fader and alarm clock. */
 
 #include <regex.h>
 #include <lopsub.h>
@@ -40,7 +40,7 @@ static struct lls_parse_result *lpr, *sub_lpr;
 #define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
 #define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
 
-typedef int (*mixer_subcommand_handler_t)(const struct mixer *, struct mixer_handle *);
+typedef int (*mixer_subcommand_handler_t)(const struct mixer *);
 
 #define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
        lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
@@ -153,11 +153,37 @@ sleep:
        return ret;
 }
 
-static int com_fade(const struct mixer *m, struct mixer_handle *h)
+static int open_mixer_and_set_channel(const struct mixer *m, struct mixer_handle **h)
+{
+       int ret;
+
+       ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), h);
+       if (ret < 0)
+               return ret;
+       ret = set_channel(m, *h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret == -E_BAD_CHANNEL) {
+               char *channels = m->get_channels(*h);
+               printf("Available channels: %s\n", channels);
+               free(channels);
+       }
+       if (ret < 0)
+               m->close(h);
+       return ret;
+}
+
+static int com_fade(const struct mixer *m)
 {
        uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL);
        uint32_t fade_time = OPT_UINT32_VAL(FADE, FADE_TIME);
-       return fade(m, h, new_vol, fade_time);
+       struct mixer_handle *h;
+       int ret;
+
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               return ret;
+       ret = fade(m, h, new_vol, fade_time);
+       m->close(&h);
+       return ret;
 }
 EXPORT_CMD(fade);
 
@@ -237,7 +263,7 @@ static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
        return 1;
 }
 
-static int com_sleep(const struct mixer *m, struct mixer_handle *h)
+static int com_sleep(const struct mixer *m)
 {
        time_t t1, wake_time_epoch;
        unsigned int delay;
@@ -252,9 +278,13 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
        int fiv = OPT_UINT32_VAL(SLEEP, FI_VOL);
        int fov = OPT_UINT32_VAL(SLEEP, FO_VOL);
        int32_t hour, min = 0;
-       char *tmp;
-       char *wt = para_strdup(wake_time + (wake_time[0] == '+'));
+       char *tmp, *wt;
+       struct mixer_handle *h;
 
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               return ret;
+       wt = para_strdup(wake_time + (wake_time[0] == '+'));
        /* calculate wake time */
        time(&t1);
        tmp = strchr(wt, ':');
@@ -264,13 +294,13 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
                ret = para_atoi32(tmp, &min);
                if (ret < 0) {
                        free(wt);
-                       return ret;
+                       goto close_mixer;
                }
        }
        ret = para_atoi32(wt, &hour);
        free(wt);
        if (ret < 0)
-               return ret;
+               goto close_mixer;
        if (wake_time[0] == '+') { /* relative */
                t1 += hour * 60 * 60 + min * 60;
                tm = localtime(&t1);
@@ -291,19 +321,19 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
        if (fot && fo_mood) {
                ret = set_initial_volume(m, h);
                if (ret < 0)
-                       return ret;
+                       goto close_mixer;
                change_afs_mode(fo_mood);
                client_cmd("play");
                ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
                if (ret < 0)
-                       return ret;
+                       goto close_mixer;
                ret = fade(m, h, fov, fot);
                if (ret < 0)
-                       return ret;
+                       goto close_mixer;
        } else {
                ret = m->set(h, fov);
                if (ret < 0)
-                       return ret;
+                       goto close_mixer;
        }
        if (sleep_mood) {
                change_afs_mode(sleep_mood);
@@ -311,9 +341,9 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
                        client_cmd("play");
        } else if (fot && fo_mood) /* currently playing */
                client_cmd("stop");
+       m->close(&h);
        if (!fit || !fi_mood) /* nothing to do */
                return 1;
-       change_afs_mode(fi_mood);
        for (;;) {
                time(&t1);
                if (wake_time_epoch <= t1 + fit)
@@ -324,22 +354,35 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
                        (delay % 3600) / 60);
                sleep(delay);
        }
-       client_cmd("play");
+       change_afs_mode(fi_mood);
+       if (sleep_mood) /* currently playing */
+               client_cmd("next");
+       else /* currently stopped */
+               client_cmd("play");
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               return ret;
        ret = fade(m, h, fiv, fit);
-       PARA_INFO_LOG("fade complete, returning\n");
+close_mixer:
+       m->close(&h);
        return ret;
 }
 EXPORT_CMD(sleep);
 
-static int com_snooze(const struct mixer *m, struct mixer_handle *h)
+static int com_snooze(const struct mixer *m)
 {
        int ret, val;
+       struct mixer_handle *h;
 
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               return ret;
+       ret = 1;
        if (OPT_UINT32_VAL(SNOOZE, SO_TIME) == 0)
-               return 1;
+               goto close_mixer;
        ret = m->get(h);
        if (ret < 0)
-               return ret;
+               goto close_mixer;
        val = ret;
        if (val < OPT_UINT32_VAL(SNOOZE, SO_VOL))
                ret = m->set(h, OPT_UINT32_VAL(SNOOZE, SO_VOL));
@@ -347,20 +390,35 @@ static int com_snooze(const struct mixer *m, struct mixer_handle *h)
                ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SO_VOL),
                        OPT_UINT32_VAL(SNOOZE, SO_TIME));
        if (ret < 0)
-               return ret;
+               goto close_mixer;
        client_cmd("pause");
        PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n",
                OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+       m->close(&h);
        sleep(OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
        client_cmd("play");
-       return fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL),
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               goto close_mixer;
+       ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL),
                OPT_UINT32_VAL(SNOOZE, SI_TIME));
+close_mixer:
+       m->close(&h);
+       return ret;
 }
 EXPORT_CMD(snooze);
 
-static int com_set(const struct mixer *m, struct mixer_handle *h)
+static int com_set(const struct mixer *m)
 {
-       return m->set(h, OPT_UINT32_VAL(SET, VAL));
+       struct mixer_handle *h;
+       int ret;
+
+       ret = open_mixer_and_set_channel(m, &h);
+       if (ret < 0)
+               return ret;
+       ret = m->set(h, OPT_UINT32_VAL(SET, VAL));
+       m->close(&h);
+       return ret;
 }
 EXPORT_CMD(set);
 
@@ -399,9 +457,7 @@ static void show_subcommands(void)
        }
 }
 
-
-static int com_help(__a_unused const struct mixer *m,
-               __a_unused struct mixer_handle *h)
+static int com_help(__a_unused const struct mixer *m)
 {
        const struct lls_command *cmd;
        const struct lls_opt_result *r_l = OPT_RESULT(HELP, LONG);
@@ -484,7 +540,6 @@ int main(int argc, char *argv[])
        char *errctx;
        const char *subcmd;
        const struct mixer *m;
-       struct mixer_handle *h;
        unsigned n;
 
        ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
@@ -512,23 +567,10 @@ int main(int argc, char *argv[])
        if (ret < 0)
                goto free_lpr;
        ret = parse_and_merge_config_file(cmd);
-       if (ret < 0)
-               goto free_lpr;
-       m = get_mixer_or_die();
-       ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), &h);
        if (ret < 0)
                goto free_sub_lpr;
-       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
-       if (ret == -E_BAD_CHANNEL) {
-               char *channels = m->get_channels(h);
-               printf("Available channels: %s\n", channels);
-               free(channels);
-       }
-       if (ret < 0)
-               goto close_mixer;
-       ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h);
-close_mixer:
-       m->close(&h);
+       m = get_mixer_or_die();
+       ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m);
 free_sub_lpr:
        lls_free_parse_result(sub_lpr, cmd);
 free_lpr:
index 42dd753..471efd9 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -63,8 +63,6 @@ static const int mp3info_bitrate[2][3][14] = {
 };
 
 static const int frame_size_index[] = {24000, 72000, 72000};
-static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"};
-
 #ifdef HAVE_ID3TAG
 
 #include <id3tag.h>
@@ -433,10 +431,13 @@ static int header_frequency(struct mp3header *h)
        return frequencies[h->version][h->freq];
 }
 
-static const char *header_mode(struct mp3header *h)
+static const char *header_mode(const struct mp3header *h)
 {
-       if (h->mode > 4)
-               h->mode = 4; /* invalid */
+       const char * const mode_text[] = {"stereo", "joint stereo",
+               "dual channel", "mono"};
+
+       if (h->mode >= ARRAY_SIZE(mode_text))
+               return "invalid";
        return mode_text[h->mode];
 }
 
index 62cde3d..12a1520 100644 (file)
@@ -124,8 +124,9 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        ogg_sync_state oss;
        ogg_page op;
        char *buf;
-       int ret, i, j, frames_per_chunk, ct_size;
-       long long unsigned num_frames = 0;
+       int ret, i, j, frames_per_chunk, ct_size, prev_pageno = 0;
+       long long unsigned granule_skip = 0, num_frames = 0;
+       int64_t granule = 0, prev_granule = 0;
 
        ogg_sync_init(&oss);
        ret = -E_OGG_SYNC;
@@ -145,8 +146,16 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        oss.returned = 0;
        oss.fill = numbytes;
        /* count ogg pages and get duration of the file */
-       for (i = 0; ogg_sync_pageseek(&oss, &op) > 0; i++)
-               num_frames = ogg_page_granulepos(&op);
+       for (i = 0; ogg_sync_pageseek(&oss, &op) > 0; i++) {
+               int this_pageno = ogg_page_pageno(&op);
+
+               granule = ogg_page_granulepos(&op);
+               if (i > 0 && this_pageno != prev_pageno + 1) /* hole */
+                       granule_skip += granule - prev_granule;
+               prev_pageno = this_pageno;
+               prev_granule = granule;
+       }
+       num_frames = granule - granule_skip;
        PARA_INFO_LOG("%d pages, %llu frames\n", i, num_frames);
        ret = -E_OGG_EMPTY;
        if (i == 0)
@@ -163,7 +172,7 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        oss.returned = afhi->header_len;
        oss.fill = numbytes;
        for (j = 1; ogg_sync_pageseek(&oss, &op) > 0; /* nothing */) {
-               int granule = ogg_page_granulepos(&op);
+               granule = ogg_page_granulepos(&op);
 
                while (granule >= (j + 1) * frames_per_chunk) {
                        j++;
index 4895e17..5f04c84 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -57,27 +57,9 @@ void crypt_init(void)
 
 void crypt_shutdown(void)
 {
+#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
        CRYPTO_cleanup_all_ex_data();
-}
-
-static int get_private_key(const char *path, RSA **rsa)
-{
-       EVP_PKEY *pkey;
-       BIO *bio = BIO_new(BIO_s_file());
-
-       *rsa = NULL;
-       if (!bio)
-               return -E_PRIVATE_KEY;
-       if (BIO_read_filename(bio, path) <= 0)
-               goto bio_free;
-       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-       if (!pkey)
-               goto bio_free;
-       *rsa = EVP_PKEY_get1_RSA(pkey);
-       EVP_PKEY_free(pkey);
-bio_free:
-       BIO_free(bio);
-       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
+#endif
 }
 
 /*
@@ -144,6 +126,152 @@ free_rsa:
        return ret;
 }
 
+static int read_pem_private_key(const char *path, RSA **rsa)
+{
+       EVP_PKEY *pkey;
+       BIO *bio = BIO_new(BIO_s_file());
+
+       *rsa = NULL;
+       if (!bio)
+               return -E_PRIVATE_KEY;
+       if (BIO_read_filename(bio, path) <= 0)
+               goto bio_free;
+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+       if (!pkey)
+               goto bio_free;
+       *rsa = EVP_PKEY_get1_RSA(pkey);
+       EVP_PKEY_free(pkey);
+bio_free:
+       BIO_free(bio);
+       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
+}
+
+static int read_private_rsa_params(const unsigned char *blob,
+               const unsigned char *end, RSA **result)
+{
+       int ret;
+       RSA *rsa;
+       BN_CTX *ctx;
+       BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */
+       BIGNUM *dmp1, *dmq1; /* these will be computed */
+       BIGNUM *tmp;
+       const unsigned char *cp = blob;
+
+       rsa = RSA_new();
+       if (!rsa)
+               return -E_BIGNUM;
+       ret = -E_BIGNUM;
+       tmp = BN_new();
+       if (!tmp)
+               goto free_rsa;
+       ctx = BN_CTX_new();
+       if (!ctx)
+               goto free_tmp;
+       dmp1 = BN_new();
+       if (!dmp1)
+               goto free_ctx;
+       dmq1 = BN_new();
+       if (!dmq1)
+               goto free_dmp1;
+       ret = read_bignum(cp, end - cp, &n);
+       if (ret < 0)
+               goto free_dmq1;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &e);
+       if (ret < 0)
+               goto free_n;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &d);
+       if (ret < 0)
+               goto free_e;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &iqmp);
+       if (ret < 0)
+               goto free_d;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &p);
+       if (ret < 0)
+               goto free_iqmp;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &q);
+       if (ret < 0)
+               goto free_p;
+       ret = -E_BIGNUM;
+       if (!BN_sub(tmp, q, BN_value_one()))
+               goto free_q;
+       if (!BN_mod(dmp1, d, tmp, ctx))
+               goto free_q;
+       if (!BN_sub(tmp, q, BN_value_one()))
+               goto free_q;
+       if (!BN_mod(dmq1, d, tmp, ctx))
+               goto free_q;
+#ifdef HAVE_RSA_SET0_KEY
+       RSA_set0_key(rsa, n, e, d);
+       RSA_set0_factors(rsa, p, q);
+       RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
+#else
+       rsa->n = n;
+       rsa->e = e;
+       rsa->d = d;
+       rsa->p = p;
+       rsa->q = q;
+       rsa->dmp1 = dmp1;
+       rsa->dmq1 = dmq1;
+       rsa->iqmp = iqmp;
+#endif
+       *result = rsa;
+       ret = 1;
+       goto free_ctx;
+free_q:
+       BN_clear_free(q);
+free_p:
+       BN_clear_free(p);
+free_iqmp:
+       BN_clear_free(iqmp);
+free_d:
+       BN_clear_free(d);
+free_e:
+       BN_free(e);
+free_n:
+       BN_free(n);
+free_dmq1:
+       BN_clear_free(dmq1);
+free_dmp1:
+       BN_clear_free(dmp1);
+free_ctx:
+       BN_CTX_free(ctx);
+free_tmp:
+       BN_clear_free(tmp);
+free_rsa:
+       if (ret < 0)
+               RSA_free(rsa);
+       return ret;
+}
+
+static int get_private_key(const char *path, RSA **rsa)
+{
+       int ret;
+       unsigned char *blob, *end;
+       size_t blob_size;
+
+       *rsa = NULL;
+       ret = decode_private_key(path, &blob, &blob_size);
+       if (ret < 0)
+               return ret;
+       end = blob + blob_size;
+       if (ret == PKT_OPENSSH) {
+               ret = find_openssh_bignum_offset(blob, blob_size);
+               if (ret < 0)
+                       goto free_blob;
+               PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
+               ret = read_private_rsa_params(blob + ret, end, rsa);
+       } else
+               ret = read_pem_private_key(path, rsa);
+free_blob:
+       free(blob);
+       return ret;
+}
+
 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
 {
        unsigned char *blob;
@@ -151,7 +279,7 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        int ret;
        struct asymmetric_key *key = para_malloc(sizeof(*key));
 
-       ret = decode_ssh_key(key_file, &blob, &decoded_size);
+       ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                goto out;
        ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
index d4b7658..311a514 100644 (file)
@@ -57,7 +57,7 @@ static int get_oss_format(enum sample_format sf)
        case SF_S16_BE: return AFMT_S16_BE;
        case SF_U16_LE: return AFMT_U16_LE;
        case SF_U16_BE: return AFMT_U16_BE;
-       default: return AFMT_S16_LE;
+       default: return -E_BAD_SAMPLE_FORMAT;
        }
 }
 
@@ -113,10 +113,14 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
        if (ret < 0)
                goto err;
        /* set PCM format */
-       sample_format = format = get_oss_format(sample_format);
+       ret = get_oss_format(sample_format);
+       if (ret < 0)
+               return ret;
+       sample_format = format = ret;
        ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
        if (ret < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
+               PARA_ERROR_LOG("could not set sample format\n");
                goto err;
        }
        ret = -E_BAD_SAMPLE_FORMAT;
diff --git a/play.c b/play.c
index 647367a..e8dfbde 100644 (file)
--- a/play.c
+++ b/play.c
@@ -219,7 +219,6 @@ static long unsigned get_play_time(void)
        return result;
 }
 
-
 static void wipe_receiver_node(void)
 {
        PARA_NOTICE_LOG("cleaning up receiver node\n");
@@ -434,6 +433,15 @@ static int next_valid_file(void)
        int i, j = pt->current_file;
        unsigned num_inputs = lls_num_inputs(play_lpr);
 
+       if (j == num_inputs - 1) {
+               switch (OPT_UINT32_VAL(END_OF_PLAYLIST)) {
+               case EOP_LOOP: break;
+               case EOP_STOP:
+                       pt->playing = false;
+                       return 0;
+               case EOP_QUIT: return -E_EOP;
+               }
+       }
        for (i = 0; i < num_inputs; i++) {
                j = (j + 1) % num_inputs;
                if (!pt->invalid[j])
@@ -1249,7 +1257,6 @@ int main(int argc, char *argv[])
        init_shuffle_map();
        pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
        pt->rq = CRT_FILE_CHANGE;
-       pt->current_file = num_inputs - 1;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
index 5aa78b4..7be817d 100644 (file)
@@ -48,6 +48,7 @@
 #include <speex/speex_callbacks.h>
 
 #include "para.h"
+#include "portable_io.h"
 #include "list.h"
 #include "sched.h"
 #include "buffer_tree.h"
@@ -121,12 +122,6 @@ static int speexdec_init(struct filter_node *fn)
        return 1;
 }
 
-#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
-#define le_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))
-#else
-#define le_short(s) ((short) (s))
-#endif
-
 /**
  * Size of the output buffer.
  *
@@ -178,7 +173,7 @@ static int speexdec_write_frames(int packet_no,
                samples = new_frame_size * psd->shi.channels;
                btr_output = para_malloc(2 * samples);
                for (i = 0; i < samples; i++)
-                       btr_output[i] = le_short(output[i + skip_idx]);
+                       btr_output[i] = read_u16(output + i + skip_idx);
                btr_add_output((char *)btr_output, samples * 2, btrn);
        }
        return 1;
index 742eafd..65d2240 100644 (file)
--- a/string.c
+++ b/string.c
@@ -113,9 +113,9 @@ __must_check __malloc char *para_strdup(const char *s)
 }
 
 /**
- * Print a formated message to a dynamically allocated string.
+ * Print a formatted message to a dynamically allocated string.
  *
- * \param result The formated string is returned here.
+ * \param result The formatted string is returned here.
  * \param fmt The format string.
  * \param ap Initialized list of arguments.
  *
diff --git a/vss.c b/vss.c
index 2cd0a16..73c7231 100644 (file)
--- a/vss.c
+++ b/vss.c
 #include "fd.h"
 
 extern struct misc_meta_data *mmd;
-
-extern void dccp_send_init(struct sender *);
-extern void http_send_init(struct sender *);
-extern void udp_send_init(struct sender *);
-
 extern const struct sender udp_sender, dccp_sender, http_sender;
 const struct sender * const senders[] = {
        &http_sender, &dccp_sender, &udp_sender, NULL};
@@ -946,6 +941,7 @@ static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
 }
 
 #ifndef MAP_POPULATE
+/** As of 2018, neither FreeBSD-11.2 nor NetBSD-8.0 have MAP_POPULATE. */
 #define MAP_POPULATE 0
 #endif
 
index d91a2ad..8395864 100644 (file)
@@ -17,7 +17,8 @@ systems. It is written in C and released under the GPLv2.
 </ul>
 
 <b> Author: </b> Andr&eacute; Noll,
-<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>
+<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.
index e0f5ccf..44799e9 100644 (file)
@@ -446,7 +446,7 @@ following commands:
 Next, change to the "bar" account on client_host and generate the
 key pair with the commands
 
-       ssh-keygen -q -t rsa -b 2048 -N '' -m PEM -f $key
+       ssh-keygen -q -t rsa -b 2048 -N '' -m RFC4716
 
 This generates the two files id_rsa and id_rsa.pub in ~/.ssh.  Note
 that para_server won't accept keys shorter than 2048 bits. Moreover,