>From c3020025f930fc2a2ced2a2886f0ac79186bce72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 19 Aug 2015 12:34:08 +0200
Subject: [PATCH 3/5] TOOLS: add sss_colondb API

To simplify import/export users and groups.
---
 src/tools/common/sss_colondb.c | 305 +++++++++++++++++++++++++++++++++++++++++
 src/tools/common/sss_colondb.h |  73 ++++++++++
 2 files changed, 378 insertions(+)
 create mode 100644 src/tools/common/sss_colondb.c
 create mode 100644 src/tools/common/sss_colondb.h

diff --git a/src/tools/common/sss_colondb.c b/src/tools/common/sss_colondb.c
new file mode 100644
index 0000000000000000000000000000000000000000..6b340c80e703342defb8582537db2e4ef3926155
--- /dev/null
+++ b/src/tools/common/sss_colondb.c
@@ -0,0 +1,305 @@
+/*
+    Authors:
+        Pavel Březina <pbrezina@redhat.com>
+
+    Copyright (C) 2015 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "tools/common/sss_colondb.h"
+
+#define IS_STD_FILE(db) ((db)->file == stdin || (db)->file == stdout)
+
+static char *read_field_as_string(char *line,
+                                  const char **_value)
+{
+    char *rest;
+    char *value;
+
+    if (line == NULL || *line == '\n' || *line == '\0') {
+        /* There is nothing else to read. */
+        rest = NULL;
+        value = NULL;
+        goto done;
+    }
+
+    if (*line == ':') {
+        /* Special case for empty value. */
+        *line = '\0';
+        rest = line + 1;
+        value = NULL;
+        goto done;
+    }
+
+    /* Value starts at current position. */
+    value = line;
+
+    /* Find next field delimiter. */
+    rest = strchr(line, ':');
+    if (rest == NULL) {
+        /* There is no more field. Remove \n from the end. */
+        rest = strchr(line, '\n');
+        if (rest != NULL) {
+            *rest = '\0';
+            rest = NULL;
+        }
+        goto done;
+    }
+
+    /* Remove it and step one character further. */
+    *rest = '\0';
+    rest++;
+
+done:
+    *_value = value;
+
+    return rest;
+}
+
+static char *read_field_as_uint32(char *line,
+                                  uint32_t *_value)
+{
+    const char *str;
+    char *rest;
+    errno_t ret;
+
+    rest = read_field_as_string(line, &str);
+    if (str == NULL) {
+        *_value = 0;
+        return rest;
+    }
+
+    *_value = strtouint32(str, NULL, 10);
+    if (errno != 0) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse number [%d]: %s\n",
+              ret, sss_strerror(ret));
+
+        *_value = 0;
+    }
+
+    return rest;
+}
+
+struct sss_colondb {
+    FILE *file;
+    enum sss_colondb_mode mode;
+};
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+                             struct sss_colondb *db,
+                             struct sss_colondb_read_field *table)
+{
+    int readchars;
+    size_t linelen = 0;
+    char *line = NULL;
+    char *tcline;
+    char *rest;
+    errno_t ret;
+    int i;
+
+    if (db->mode != SSS_COLONDB_READ) {
+        return ERR_INTERNAL;
+    }
+
+    readchars = getline(&line, &linelen, db->file);
+    if (readchars == -1) {
+        /* Nothing was read. */
+        if (errno != 0) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read line [%d]: %s",
+                  ret, sss_strerror(ret));
+            return ret;
+        }
+
+        return EOF;
+    }
+
+    /* Copy line to mem_ctx. */
+    tcline = talloc_strdup(mem_ctx, line);
+
+    free(line);
+    line = NULL;
+
+    if (tcline == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        return ENOMEM;
+    }
+
+    rest = tcline;
+    for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+        switch (table[i].type) {
+        case SSS_COLONDB_UINT32:
+            rest = read_field_as_uint32(rest, table[i].data.uint32);
+            break;
+        case SSS_COLONDB_STRING:
+            rest = read_field_as_string(rest, table[i].data.str);
+            break;
+        case SSS_COLONDB_SENTINEL:
+            DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        if (rest == NULL && table[i + 1].type != SSS_COLONDB_SENTINEL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Line contains less values than expected!\n");
+            ret = EINVAL;
+            goto done;
+        } else if (rest != NULL && table[i + 1].type == SSS_COLONDB_SENTINEL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Line contains more values than expected!\n");
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(tcline);
+    }
+
+    return ret;
+}
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+                              struct sss_colondb_write_field *table)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *line = NULL;
+    errno_t ret;
+    int i;
+
+    if (db->mode != SSS_COLONDB_WRITE) {
+        return ERR_INTERNAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+        return ENOMEM;
+    }
+
+    for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+        switch (table[i].type) {
+        case SSS_COLONDB_UINT32:
+            if (table[i].data.uint32 == 0) {
+                line = talloc_asprintf_append(line, ":");
+            } else {
+                line = talloc_asprintf_append(line, ":%u", table[i].data.uint32);
+            }
+            break;
+        case SSS_COLONDB_STRING:
+            if (table[i].data.str == NULL) {
+                line = talloc_asprintf_append(line, ":");
+            } else {
+                line = talloc_asprintf_append(line, ":%s", table[i].data.str);
+            }
+            break;
+        case SSS_COLONDB_SENTINEL:
+            DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        if (line == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    /* Remove starting : */
+    line++;
+
+    fprintf(db->file, "%s\n", line);
+    fflush(db->file);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static int sss_colondb_close(void *pvt)
+{
+    struct sss_colondb *db = talloc_get_type(pvt, struct sss_colondb);
+
+    if (db->file == NULL || IS_STD_FILE(db)) {
+        return 0;
+    }
+
+    fclose(db->file);
+    db->file = NULL;
+
+    return 0;
+}
+
+static FILE *open_db(const char *filename, enum sss_colondb_mode mode)
+{
+    FILE *fp = NULL;
+    errno_t ret;
+
+    errno = 0;
+
+    switch (mode) {
+    case SSS_COLONDB_READ:
+        fp = filename == NULL ? stdin : fopen(filename, "r");
+        break;
+    case SSS_COLONDB_WRITE:
+        fp = filename == NULL ? stdout : fopen(filename, "w");
+        break;
+    }
+
+    if (fp == NULL && filename != NULL) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s [%d]: %s\n",
+              filename, ret, sss_strerror(ret));
+    }
+
+    return fp;
+}
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+                                     enum sss_colondb_mode mode,
+                                     const char *filename)
+{
+    struct sss_colondb *db;
+
+    db = talloc_zero(mem_ctx, struct sss_colondb);
+    if (db == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+        return NULL;
+    }
+
+    db->file = open_db(filename, mode);
+    db->mode = mode;
+
+    if (db->file == NULL) {
+        talloc_free(db);
+        return NULL;
+    }
+
+    talloc_set_destructor((TALLOC_CTX *)db, sss_colondb_close);
+
+    return db;
+}
diff --git a/src/tools/common/sss_colondb.h b/src/tools/common/sss_colondb.h
new file mode 100644
index 0000000000000000000000000000000000000000..6edd99cbe3b9ef5c86a48632ac3fc71e8a3e55fe
--- /dev/null
+++ b/src/tools/common/sss_colondb.h
@@ -0,0 +1,73 @@
+/*
+    Authors:
+        Pavel Březina <pbrezina@redhat.com>
+
+    Copyright (C) 2015 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_COLONDB_H_
+#define _SSS_COLONDB_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+
+struct sss_colondb;
+
+enum sss_colondb_mode {
+    SSS_COLONDB_READ,
+    SSS_COLONDB_WRITE
+};
+
+enum sss_colondb_type {
+    SSS_COLONDB_UINT32,
+    SSS_COLONDB_STRING,
+    SSS_COLONDB_SENTINEL
+};
+
+union sss_colondb_write_data {
+    uint32_t uint32;
+    const char *str;
+};
+
+union sss_colondb_read_data {
+    uint32_t *uint32;
+    const char **str;
+};
+
+struct sss_colondb_write_field {
+    enum sss_colondb_type type;
+    union sss_colondb_write_data data;
+};
+
+struct sss_colondb_read_field {
+    enum sss_colondb_type type;
+    union sss_colondb_read_data data;
+};
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+                                     enum sss_colondb_mode mode,
+                                     const char *filename);
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+                             struct sss_colondb *db,
+                             struct sss_colondb_read_field *table);
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+                              struct sss_colondb_write_field *table);
+
+#endif /* _SSS_COLONDB_H_ */
-- 
1.9.3

