s3-registry: add support for registration entries (.reg) files
authorGregor Beck <gbeck@sernet.de>
Wed, 29 Sep 2010 12:54:15 +0000 (14:54 +0200)
committerGregor Beck <gbeck@sernet.de>
Wed, 29 Sep 2010 13:18:27 +0000 (15:18 +0200)
source3/Makefile.in
source3/registry/reg_format.c [new file with mode: 0644]
source3/registry/reg_format.h [new file with mode: 0644]
source3/registry/reg_import.c [new file with mode: 0644]
source3/registry/reg_import.h [new file with mode: 0644]
source3/registry/reg_parse.c [new file with mode: 0644]
source3/registry/reg_parse.h [new file with mode: 0644]
source3/registry/reg_parse_internal.c [new file with mode: 0644]
source3/registry/reg_parse_internal.h [new file with mode: 0644]

index 42cf85612040f2cdb32fd96de2c6ba5ca9ee9201..521636c8140012548d6b30cafb175f4977265198 100644 (file)
@@ -942,6 +942,8 @@ NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_help.o \
           utils/net_g_lock.o \
           utils/net_serverid.o \
           utils/net_eventlog.o \
+          registry/reg_parse.o registry/reg_format.o \
+          registry/reg_parse_internal.o registry/reg_import.o \
           lib/cbuf.o lib/srprs.o
 
 # these are not processed by make proto
diff --git a/source3/registry/reg_format.c b/source3/registry/reg_format.c
new file mode 100644 (file)
index 0000000..9936662
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Format dot.reg files
+ * @file   reg_format.c
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Sep 2010
+ */
+
+#include "reg_format.h"
+#include "reg_parse.h"
+#include "reg_parse_internal.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include <assert.h>
+
+static void cstr_unescape(char* val)
+{
+       all_string_sub(val, "\\r", "\r", 0);
+       all_string_sub(val, "\\n", "\n", 0);
+       all_string_sub(val, "\\t", "\t", 0);
+       all_string_sub(val, "\\\\", "\\", 0);
+}
+
+/******************************************************************************/
+
+/**
+ * Print value assign to stream.
+ *
+ * @param[out] ost outstream
+ * @param[in]  name string
+ *
+ * @return numner of bytes written, -1 on error
+ * @see srprs_val_name
+ */
+static int cbuf_print_value_assign(cbuf* ost, const char* name) {
+       int ret, n;
+       if (*name == '\0') {
+               ret = cbuf_putc(ost, '@');
+       } else {
+               ret = cbuf_print_quoted_string(ost, name);
+       }
+
+       if (ret<0) {
+               return ret;
+       }
+
+       n = cbuf_putc(ost, '=');
+       if (n < 0) {
+               return n;
+       }
+       ret += n;
+
+       return ret;
+}
+
+enum fmt_hive {
+       FMT_HIVE_PRESERVE=0,
+       FMT_HIVE_SHORT,
+       FMT_HIVE_LONG
+};
+
+
+struct fmt_key {
+       enum fmt_hive hive_fmt;
+       enum fmt_case hive_case;
+       enum fmt_case key_case;
+       const char*   sep;
+};
+
+
+static int
+cbuf_print_hive(cbuf* ost, const char* hive, int len, const struct fmt_key* fmt)
+{
+       if (fmt->hive_fmt != FMT_HIVE_PRESERVE) {
+               const struct hive_info* hinfo = hive_info(hive, len);
+               if (hinfo == NULL) {
+                       DEBUG(0, ("Unknown hive %*s", len, hive));
+               } else {
+                       switch(fmt->hive_fmt) {
+                       case FMT_HIVE_SHORT:
+                               hive = hinfo->short_name;
+                               len  = hinfo->short_name_len;
+                               break;
+                       case FMT_HIVE_LONG:
+                               hive = hinfo->long_name;
+                               len  = hinfo->long_name_len;
+                               break;
+                       default:
+                               DEBUG(0, ("Unsupported hive format %d",
+                                         (int)fmt->hive_fmt));
+                               return -1;
+                       }
+               }
+       }
+
+       return cbuf_puts_case(ost, hive, len, fmt->hive_case);
+}
+
+static int
+cbuf_print_keyname(cbuf* ost, const char* key[], int n, const struct fmt_key* fmt)
+{
+       int r, ret=0;
+       size_t pos = cbuf_getpos(ost);
+       bool hive = true;
+
+       for (; n>0; key++, n--) {
+               const char* start = *key;
+               while(*start != '\0') {
+                       const char* end = start;
+                       while(*end != '\\' && *end != '\0') {
+                               end++;
+                       }
+
+                       if (hive) {
+                               r = cbuf_print_hive(ost, start, end-start, fmt);
+                               if (r < 0) {
+                                       goto fail;
+                               }
+
+                               ret += r;
+                               hive = false;
+                       } else {
+                               r = cbuf_puts(ost, fmt->sep, -1);
+                               if (r < 0) {
+                                       goto fail;
+                               }
+                               ret += r;
+
+                               r = cbuf_puts_case(ost, start, end-start, fmt->key_case);
+                               if (r < 0) {
+                                       goto fail;
+                               }
+                               ret += r;
+                       }
+
+                       while(*end == '\\') {
+                               end++;
+                       }
+                       start = end;
+               }
+       }
+       return ret;
+fail:
+       cbuf_setpos(ost, pos);
+       return r;
+}
+/**@}*/
+
+/**
+ * @defgroup reg_format Format dot.reg file.
+ * @{
+ */
+
+struct reg_format
+{
+       struct reg_parse_callback reg_parse_callback;
+       struct reg_format_callback call;
+       unsigned flags;
+       smb_iconv_t fromUTF16;
+       const char* sep;
+};
+
+int reg_format_value_delete(struct reg_format* f, const char* name)
+{
+       int ret;
+       cbuf* line = cbuf_new(f);
+
+       ret = cbuf_print_value_assign(line, name);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = cbuf_putc(line, '-');
+       if (ret < 0 ) {
+               goto done;
+       }
+
+       ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+       talloc_free(line);
+       return ret;
+}
+
+/* Todo: write hex if str contains CR or LF */
+static int
+reg_format_value_sz(struct reg_format* f, const char* name, const char* str)
+{
+       int ret;
+       cbuf* line = cbuf_new(f);
+
+       ret = cbuf_print_value_assign(line, name);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = cbuf_print_quoted_string(line, str);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+
+done:
+       talloc_free(line);
+       return ret;
+}
+
+static int reg_format_value_dw(struct reg_format* f, const char* name, uint32_t dw)
+{
+       int ret;
+       cbuf* line = cbuf_new(f);
+
+       ret = cbuf_print_value_assign(line, name);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = cbuf_printf(line, "dword:%08x", dw);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+       talloc_free(line);
+       return ret;
+}
+
+static int reg_format_value_hex(struct reg_format* f, const char* name, uint32_t type,
+                               const void* data, size_t len)
+{
+       int n;
+       int cpl=0;
+       int ret=0;
+       const unsigned char* ptr;
+
+       cbuf* line = cbuf_new(f);
+
+       n = cbuf_print_value_assign(line, name);
+       if (n < 0) {
+               ret = n;
+               goto done;
+       }
+
+       cpl += n;
+
+       if (type==REG_BINARY && !(f->flags & REG_FMT_HEX_BIN)) {
+               n=cbuf_puts(line, "hex:", -1);
+       } else {
+               n=cbuf_printf(line, "hex(%x):", type);
+       }
+       if (n < 0) {
+               ret = n;
+               goto done;
+       }
+
+       cpl += n;
+
+       for (ptr=data; len>1; len--,ptr++) {
+               n = cbuf_printf(line, "%02x,", (unsigned)(*ptr));
+               if (n < 0) {
+                       return n;
+               }
+               cpl += n;
+
+               if ( cpl > 76 ) {
+                       n = cbuf_putc(line, '\\');
+                       if (n< 0) {
+                               return n;
+                       }
+
+                       n = f->call.writeline(f->call.data, cbuf_gets(line,0));
+                       if (n < 0) {
+                               ret = n;
+                               goto done;
+                       }
+                       ret += n;
+
+                       cbuf_clear(line);
+                       cpl = cbuf_puts(line, "  ", -1);
+                       if (cpl < 0) {
+                               ret = cpl;
+                               goto done;
+                       }
+               }
+       }
+
+       if ( len > 0 ) {
+               n = cbuf_printf(line, "%02x", (unsigned)(*ptr));
+               if (n < 0) {
+                       ret = n;
+                       goto done;
+               }
+               cpl += n;
+       }
+
+       n = f->call.writeline(f->call.data, cbuf_gets(line,0));
+       if (n < 0) {
+               ret = n;
+               goto done;
+       }
+       ret += n;
+done:
+       talloc_free(line);
+       return ret;
+}
+
+int reg_format_value(struct reg_format* f, const char* name, uint32_t type,
+                    const uint8_t* data, size_t len)
+{
+       int ret = 0;
+       void* mem_ctx = talloc_new(f);
+
+       switch (type) {
+       case REG_SZ:
+               if (!(f->flags & REG_FMT_HEX_SZ)) {
+                       char* str = NULL;
+                       size_t dlen;
+                       if (pull_ucs2_talloc(mem_ctx, &str, (const smb_ucs2_t*)data, &dlen)) {
+                               ret = reg_format_value_sz(f, name, str);
+                               goto done;
+                       } else {
+                               DEBUG(0, ("reg_format_value %s: "
+                                         "pull_ucs2_talloc failed"
+                                         ", try to write hex\n", name));
+                       }
+               }
+               break;
+
+       case REG_DWORD:
+               if (!(f->flags & REG_FMT_HEX_SZ) && (len == sizeof(uint32_t))) {
+                       uint32_t dw = IVAL(data,0);
+                       ret = reg_format_value_dw(f, name, dw);
+                       goto done;
+               }
+               break;
+
+       case REG_MULTI_SZ:
+       case REG_EXPAND_SZ:
+               if (f->fromUTF16 && (f->fromUTF16 != ((smb_iconv_t)-1))) {
+                       char* str = NULL;
+                       size_t dlen = iconvert_talloc(mem_ctx, f->fromUTF16,
+                                                     (const char*)data, len, &str);
+                       if (dlen != -1) {
+                               ret = reg_format_value_hex(f, name, type, str, dlen);
+                               goto done;
+                       } else {
+                               DEBUG(0, ("reg_format_value %s: "
+                                         "iconvert_talloc failed"
+                                         ", try to write hex\n", name));
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       ret = reg_format_value_hex(f, name, type, data, len);
+done:
+       talloc_free(mem_ctx);
+       return ret;
+}
+
+
+int reg_format_comment(struct reg_format* f, const char* txt)
+{
+       int ret;
+       cbuf* line = cbuf_new(f);
+
+       ret = cbuf_putc(line,';');
+       if (ret<0) {
+               goto done;
+       }
+
+       ret = cbuf_puts(line, txt, -1);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+       talloc_free(line);
+       return ret;
+}
+
+
+/******************************************************************************/
+
+
+
+struct reg_format* reg_format_new(const void* talloc_ctx,
+                                 struct reg_format_callback cb,
+                                 const char* str_enc, unsigned flags,
+                                 const char* sep)
+{
+       static const struct reg_parse_callback reg_parse_callback_default = {
+               .key = (reg_parse_callback_key_t)&reg_format_key,
+               .val = (reg_parse_callback_val_t)&reg_format_value,
+               .val_del = (reg_parse_callback_val_del_t)&reg_format_value_delete,
+               .comment = (reg_parse_callback_comment_t)&reg_format_comment,
+       };
+
+       struct reg_format* f = talloc_zero(talloc_ctx, struct reg_format);
+       if (f == NULL) {
+               return NULL;
+       }
+
+       f->reg_parse_callback = reg_parse_callback_default;
+       f->reg_parse_callback.data = f;
+
+       f->call = cb;
+       f->flags = flags;
+       f->sep   = sep;
+
+       if (str_enc && !set_iconv(&f->fromUTF16, str_enc, "UTF-16LE")) {
+               DEBUG(0, ("reg_format_new: failed to set encoding: %s\n",
+                         str_enc));
+               goto fail;
+       }
+
+       assert(&f->reg_parse_callback == (struct reg_parse_callback*)f);
+       return f;
+fail:
+       talloc_free(f);
+       return NULL;
+}
+
+int reg_format_set_options(struct reg_format* fmt, const char* options)
+{
+       static const char* DEFAULT ="enc=unix,flags=0,sep=\\";
+
+       int ret = 0;
+       char *key, *val;
+       void* ctx = talloc_new(fmt);
+
+       if (options == NULL) {
+               options = DEFAULT;
+       }
+
+       while (srprs_option(&options, ctx, &key, &val)) {
+               if ((strcmp(key, "enc") == 0) || (strcmp(key, "strenc") == 0)) {
+                       if (!set_iconv(&fmt->fromUTF16, val, "UTF-16LE")) {
+                               DEBUG(0, ("Failed to set encoding: %s\n", val));
+                               ret = -1;
+                       }
+               } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+                       char* end = NULL;
+                       if (val != NULL) {
+                               fmt->flags = strtol(val, &end, 0);
+                       }
+                       if ((end==NULL) || (*end != '\0')) {
+                               DEBUG(0, ("Invalid flags format: %s\n",
+                                         val ? val : "<NULL>"));
+                               ret = -1;
+                       }
+               } else if ((strcmp(key, "sep") == 0) && (val != NULL)) {
+                       cstr_unescape(val);
+                       fmt->sep = talloc_steal(fmt, val);
+               }
+
+               /* else if (strcmp(key, "hive") == 0) { */
+               /*      if (strcmp(val, "short") == 0) { */
+               /*              f->hive_fmt = REG_FMT_SHORT_HIVES; */
+               /*      } else if (strcmp(val, "long") == 0) { */
+               /*              f->hive_fmt = REG_FMT_LONG_HIVES; */
+               /*      } else if (strcmp(val, "preserve") == 0) { */
+               /*              f->hive_fmt = REG_FMT_PRESERVE_HIVES; */
+               /*      } else { */
+               /*              DEBUG(0, ("Invalid hive format: %s\n", val)); */
+               /*              ret = -1; */
+               /*      } */
+               /* } */
+       }
+       talloc_free(ctx);
+       return ret;
+}
+
+int reg_format_key(struct reg_format* f, const char* key[], size_t n, bool del)
+{
+       int ret, r;
+       cbuf* line = cbuf_new(f);
+       struct fmt_key key_fmt = {
+               .key_case  = (f->flags >>  4) & 0x0F,
+               .hive_case = (f->flags >>  8) & 0x0F,
+               .hive_fmt  = (f->flags >> 12) & 0x0F,
+               .sep       = f->sep,
+       };
+
+       ret = cbuf_putc(line, '[');
+       if (ret < 0) {
+               goto done;
+       }
+
+       if (del) {
+               ret = cbuf_putc(line, '-');
+               if (ret < 0) {
+                       goto done;
+               }
+       }
+
+       ret = cbuf_print_keyname(line, key, n, &key_fmt);
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = cbuf_putc(line, ']');
+       if (ret < 0) {
+               goto done;
+       }
+
+       ret = f->call.writeline(f->call.data, "");
+       if (ret < 0) {
+               goto done;
+       }
+
+       r = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+       if (r < 0) {
+               ret = r;
+               goto done;
+       }
+       ret += r;
+
+done:
+       talloc_free(line);
+       return ret;
+}
+
+
+int reg_format_registry_key(struct reg_format* f, struct registry_key* key,
+                           bool del)
+{
+       return reg_format_key(f, (const char**)&key->key->name, 1, del);
+}
+
+int reg_format_registry_value(struct reg_format* f, const char* name,
+                             struct registry_value* val)
+{
+       DATA_BLOB blob;
+       WERROR werr;
+       int ret;
+
+       switch(val->type) {
+       case REG_DWORD:
+               return reg_format_value_dw(f, name, val->v.dword);
+       case REG_SZ:
+               return reg_format_value_sz(f, name, val->v.sz.str);
+       default:
+               break;
+       }
+
+       werr = registry_push_value(f, val, &blob);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("reg_format_create_value: registry_push_value: "
+                         "%s\n", win_errstr(werr)));
+               return -1;
+       }
+
+       ret = reg_format_value_hex(f, name, val->type, blob.data, blob.length);
+
+       talloc_free(blob.data);
+       return ret;
+}
+
+int reg_format_regval_blob(struct reg_format* f, const char* name,
+                          struct regval_blob* val)
+{
+
+       return reg_format_value(f,
+                               name ? name : regval_name(val),
+                               regval_type(val),
+                               regval_data_p(val),
+                               regval_size(val));
+}
+
+/**@}*/
+
+
+struct reg_format_file
+{
+       FILE* file;
+       const char* encoding;
+       smb_iconv_t fromUnix;
+       char* nl;
+       size_t nl_len;
+};
+
+
+static int reg_format_file_close(struct reg_format* fmt)
+{
+       struct reg_format_file* fmt_ctx
+               = (struct reg_format_file*) fmt->call.data;
+       int ret = 0;
+       FILE* file = fmt_ctx->file;
+
+       if (fmt_ctx->encoding) {
+               char buf[32];
+               snprintf(buf, sizeof(buf), "coding: %s", fmt_ctx->encoding);
+               reg_format_comment(fmt, "Local Variables:");
+               reg_format_comment(fmt, buf);
+               reg_format_comment(fmt, "End:");
+       }
+
+       if (file != NULL) {
+               ret = fclose(file);
+       }
+
+       return ret;
+}
+
+static int reg_format_file_writeline(void* ptr, const char* line)
+{
+       size_t size;
+       char* dst=NULL;
+       struct reg_format_file* fmt_ctx = (struct reg_format_file*)ptr;
+       int ret, r;
+
+       size = iconvert_talloc(ptr, fmt_ctx->fromUnix, line, strlen(line), &dst);
+       if (size == -1 ) {
+               DEBUG(0, ("reg_format_file_writeline: iconvert_talloc failed >%s<\n",  line));
+               return -1;
+       }
+
+       ret = fwrite(dst, 1, size, fmt_ctx->file);
+       if (ret < 0) {
+               goto done;
+       }
+
+       r = fwrite(fmt_ctx->nl, 1, fmt_ctx->nl_len, fmt_ctx->file);
+       ret = (r < 0) ? r : ret + r;
+
+done:
+       talloc_free(dst);
+       return ret;
+}
+
+struct reg_format_file_opt {
+       const char* head;
+       const char* nl;
+       const char* enc;
+       bool bom;
+       const char* str_enc;
+       unsigned flags;
+       const char* sep;
+};
+
+struct reg_format_file_opt reg_format_file_opt(void* mem_ctx, const char* opt)
+{
+       static const struct reg_format_file_opt REG4 = {
+               .head = "REGEDIT4",
+               .nl   = "\r\n",
+               .enc  = "dos",
+               .str_enc = "dos",
+               .bom  = false,
+               .flags = (FMT_HIVE_LONG << 12),
+               .sep   = "\\",
+       };
+
+       static const struct reg_format_file_opt REG5 = {
+               .head = "Windows Registry Editor Version 5.00",
+               .nl   = "\r\n",
+               .enc  = "UTF-16LE",
+               .str_enc = "UTF-16LE",
+               .bom  = true,
+               .flags = (FMT_HIVE_LONG << 12),
+               .sep   = "\\",
+       };
+
+       struct reg_format_file_opt ret = {
+               .head = REG5.head,
+               .nl   = "\n",
+               .enc  = "unix",
+               .bom  = false,
+               .str_enc = "UTF-16LE",
+               .flags = 0,
+               .sep   = "\\",
+       };
+
+       void* tmp_ctx = talloc_new(mem_ctx);
+
+       char *key, *val;
+
+       if (opt == NULL) {
+               goto done;
+       }
+
+       while(srprs_option(&opt, tmp_ctx, &key, &val)) {
+               if (strcmp(key, "enc") == 0) {
+                       ret.enc     = talloc_steal(mem_ctx, val);
+                       ret.str_enc = ret.enc;
+               } else if (strcmp(key, "strenc") == 0) {
+                       ret.str_enc = talloc_steal(mem_ctx, val);
+               } else if (strcmp(key, "fileenc") == 0) {
+                       ret.enc = talloc_steal(mem_ctx, val);
+               } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+                       char* end = NULL;
+                       if (val != NULL) {
+                               ret.flags = strtol(val, &end, 0);
+                       }
+                       if ((end==NULL) || (*end != '\0')) {
+                               DEBUG(0, ("Invalid flags format: %s\n",
+                                         val ? val : "<NULL>"));
+                       }
+               } else if ((strcmp(key, "sep") == 0) && (val != NULL)) {
+                       cstr_unescape(val);
+                       ret.sep = talloc_steal(mem_ctx, val);
+               } else if (strcmp(key, "head") == 0) {
+                       cstr_unescape(val);
+                       ret.head = talloc_steal(mem_ctx, val);
+               } else if (strcmp(key, "nl") == 0) {
+                       cstr_unescape(val);
+                       ret.nl = talloc_steal(mem_ctx, val);
+               } else if (strcmp(key, "bom") == 0) {
+                       if (val == NULL) {
+                               ret.bom = true;
+                       } else {
+                               ret.bom = atoi(val);
+                       }
+               } else if (strcmp(key, "regedit4") == 0) {
+                       ret = REG4;
+               } else if (strcmp(key, "regedit5") == 0) {
+                       ret = REG5;
+               }
+       }
+done:
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
+
+struct reg_format* reg_format_file(const void* talloc_ctx,
+                                  const char* filename,
+                                  const char* options)
+{
+       struct reg_format_file* fmt_ctx;
+       struct reg_format* fmt;
+       int ret;
+       struct reg_format_file_opt opt;
+
+       struct reg_format_callback reg_format_cb = {
+               .writeline = &reg_format_file_writeline
+       };
+
+       fmt_ctx = talloc_zero(talloc_ctx, struct reg_format_file);
+       if (fmt_ctx == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       opt = reg_format_file_opt(fmt_ctx, options);
+
+       reg_format_cb.data = fmt_ctx;
+
+       fmt = reg_format_new(talloc_ctx, reg_format_cb,
+                            opt.str_enc, opt.flags, opt.sep);
+       if (fmt == NULL) {
+               errno = ENOMEM;
+               talloc_free(fmt_ctx);
+               return NULL;
+       }
+
+       talloc_steal(fmt, fmt_ctx);
+
+       if (!set_iconv(&fmt->fromUTF16, opt.str_enc, "UTF-16LE")) { /* HACK */
+               DEBUG(0, ("reg_format_file: failed to set string encoding %s",
+                             opt.str_enc));
+               goto fail;
+       }
+
+       if (!set_iconv(&fmt_ctx->fromUnix, opt.enc, "unix")) {
+               DEBUG(0, ("reg_format_file: failed to set file encoding %s",
+                         opt.enc));
+               goto fail;
+       }
+       fmt_ctx->encoding = talloc_strdup(fmt_ctx, get_charset(opt.enc));
+
+       fmt_ctx->file = fopen(filename, "w");
+       if (fmt_ctx->file == NULL) {
+               DEBUG(0, ("reg_format_file: fopen failed: %s\n", strerror(errno)));
+               goto fail;
+       }
+
+       if (setvbuf(fmt_ctx->file, NULL, _IOFBF, 64000) < 0) {
+               DEBUG(0, ("reg_format_file: setvbuf failed: %s\n", strerror(errno)));
+       }
+
+       talloc_set_destructor(fmt, reg_format_file_close);
+
+       fmt_ctx->nl_len = iconvert_talloc(fmt, fmt_ctx->fromUnix, opt.nl, strlen(opt.nl), &fmt_ctx->nl);
+       if (fmt_ctx->nl_len == -1) {
+               DEBUG(0, ("iconvert_talloc failed\n"));
+               goto fail;
+       }
+
+       if (opt.bom) {
+               ret = write_bom(fmt_ctx->file, opt.enc, -1);
+               if (ret < 0) {
+                       goto fail;
+               }
+       }
+
+       ret = fmt->call.writeline(fmt->call.data, opt.head);
+       if (ret < 0) {
+               goto fail;
+       }
+
+       return fmt;
+fail:
+       talloc_free(fmt);
+       return NULL;
+}
diff --git a/source3/registry/reg_format.h b/source3/registry/reg_format.h
new file mode 100644 (file)
index 0000000..5d0a4f2
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Format registration entries (.reg) files.
+ * A formater is a talloced incarnation of an opaque struct reg_format.
+ * It is fed with registry key's and value's and emits output by calling
+ * writeline from its reg_format_callback.
+ * @file   reg_format.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Sep 2010
+ */
+#ifndef __REG_FORMAT_H
+#define __REG_FORMAT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct registry_key;
+struct registry_value;
+struct regval_blob;
+
+
+/**
+ * A Formater for registration entries (.reg) files.
+ *
+ * It may be used as a reg_parse_callback, so the following is valid:
+ * @code
+ * reg_parse* p = reg_parse_new(mem_ctx,
+ *                             (reg_parse_callback)reg_format_new(mem_ctx, cb, NULL, 0, "\\"),
+ *                                NULL, 0);
+ * @endcode
+ * @see reg_parse
+ */
+typedef struct reg_format reg_format;
+
+/**
+ * Protoype for function called to output a line.
+ *
+ * @param private_data
+ * @param line line to write in UNIX charset
+ *
+ * @return number of characters written, < 0 on error
+ *
+ * @see reg_parse
+ */
+typedef int (*reg_format_callback_writeline_t)(void* private_data,
+                                              const char* line);
+/**
+ * Type handling the output of a reg_format object.
+ * It containes the functions to call and an opaque data pointer.
+ */
+typedef struct reg_format_callback {
+       /**< Function called to write a line */
+       reg_format_callback_writeline_t writeline;
+       void* data; /**< Private data passed to callback function */
+} reg_format_callback;
+
+/**
+ * Create a new reg_format object.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb         the output handler
+ * @param str_enc    the charset of hex encoded strings (REG_MULTI_SZ, REG_EXAND_SZ) if not UTF-16
+ * @param flags
+ * @param sep        the separator for subkeys
+ *
+ * @return a talloc'ed reg_format object, NULL on error
+ */
+reg_format* reg_format_new(const void* talloc_ctx,
+                          reg_format_callback cb,
+                          const char* str_enc,
+                          unsigned flags,
+                          const char* sep);
+
+/**
+ * Create a new reg_format object, writing to a file.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param filename   the file to write to
+ * @param options
+ *
+ * @return a talloc'ed reg_format object, NULL on error
+ */
+reg_format* reg_format_file(const void* talloc_ctx,
+                           const char* filename,
+                           const char* options);
+
+/**
+ * Format a registry key given as struct registry_key.
+ * Create/Open or Delete
+ *
+ * @param f   the formater.
+ * @param key the key to output.
+ * @param del wheter to format the deletion of the key
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_registry_key(reg_format* f,
+                           struct registry_key* key,
+                           bool del);
+
+/**
+ * Format a registry value given as struct registry_value.
+ *
+ * @param f    the formater.
+ * @param name the values name
+ * @param val  the values value.
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_registry_value(reg_format* f,
+                             const char* name,
+                             struct registry_value* val);
+
+/**
+ * Format a registry value given as struct regval_blob.
+ *
+ * @param f    the formater.
+ * @param name the values name, if NULL use val->valname which is limited in size;
+ * @param val  the values value.
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_regval_blob(reg_format* f,
+                          const char* name,
+                          struct regval_blob* val);
+
+
+/**
+ * Format deletion of a registry value.
+ *
+ * @param f    the formater.
+ * @param name the values name
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_val_del_t
+ */
+int reg_format_value_delete(reg_format* f, const char* name);
+
+/**
+ * Format a comment.
+ *
+ * @param f   the formater.
+ * @param txt the comment in UNIX charset, may not contain newlines.
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_comment_t
+ */
+int reg_format_comment(reg_format* f, const char* txt);
+
+
+int reg_format_set_options(reg_format* f, const char* options);
+
+
+/* reg_format flags */
+#define REG_FMT_HEX_SZ  1
+#define REG_FMT_HEX_DW  2
+#define REG_FMT_HEX_BIN 4
+#define REG_FMT_HEX_ALL (REG_FMT_HEX_SZ | REG_FMT_HEX_DW | REG_FMT_HEX_BIN);
+#define REG_FMT_LONG_HIVES 16
+#define REG_FMT_SHORT_HIVES 32
+
+/* lowlevel */
+
+/**
+ * Format a registry key.
+ * Create/Open or Delete
+ *
+ * @param f   the formater
+ * @param key the key to output
+ * @param klen number of elements in key
+ * @param del wheter to format the deletion of the key
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_key_t
+ */
+int reg_format_key(reg_format* f,
+                  const char* key[], size_t klen,
+                  bool del);
+
+/**
+ * Format a registry value.
+ *
+ * @param f    the formater
+ * @param name the values name
+ * @param type the values type
+ * @param data the values value
+ * @param len  the number of bytes of data
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_val_hex_t
+ */
+int reg_format_value(reg_format* f,
+                    const char* name, uint32_t type,
+                    const uint8_t* data, size_t len);
+
+#endif /* __REG_FORMAT_H */
diff --git a/source3/registry/reg_import.c b/source3/registry/reg_import.c
new file mode 100644 (file)
index 0000000..77116c3
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Adapter to use reg_parse with the registry api
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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 "includes.h"
+#include "reg_parse.h"
+#include "reg_import.h"
+#include <assert.h>
+
+/* Debuglevel for tracing */
+static const int TL = 2;
+
+struct reg_import
+{
+       struct reg_parse_callback reg_parse_callback;
+       struct reg_import_callback call;
+       void* open_key;
+};
+
+static int
+reg_parse_callback_key(struct reg_import* cb_private,
+                      const char* key[], size_t n,
+                      bool del);
+
+static int
+reg_parse_callback_val(struct reg_import* cb_private,
+                      const char* name, uint32_t type,
+                      const uint8_t* data, uint32_t len);
+
+static int
+reg_parse_callback_val_registry_value(struct reg_import* cb_private,
+                                     const char* name, uint32_t type,
+                                     const uint8_t* data, uint32_t len);
+
+static int
+reg_parse_callback_val_regval_blob(struct reg_import* cb_private,
+                                  const char* name, uint32_t type,
+                                  const uint8_t* data, uint32_t len);
+
+static int
+reg_parse_callback_val_del(struct reg_import* cb_private,
+                          const char* name);
+
+static int
+reg_parse_callback_comment(struct reg_import* cb_private,
+                          const char* txt);
+
+
+/*******************************************************************************/
+
+int reg_parse_callback_key(struct reg_import* p,
+                          const char* key[], size_t n, bool del)
+{
+       WERROR werr = WERR_OK;
+
+       DEBUG(TL, ("%s: %s\n", __FUNCTION__, key[0]));
+
+       if (p->open_key != NULL ) {
+               werr = p->call.closekey(p->call.data, p->open_key);
+               if (!W_ERROR_IS_OK(werr)) {
+                       DEBUG(0, ("closekey failed: %s\n", win_errstr(werr)));
+               }
+       }
+
+       if (del) {
+               werr = p->call.deletekey(p->call.data, NULL, key[0]);
+               if (W_ERROR_EQUAL(werr, WERR_BADFILE)) {
+                       /* the key didn't exist, treat as success */
+                       werr = WERR_OK;
+               }
+               if (!W_ERROR_IS_OK(werr)) {
+                       DEBUG(0, ("deletekey %s failed: %s\n",
+                                 key[0], win_errstr(werr)));
+               }
+       }
+       else {
+               bool existing;
+               werr = p->call.createkey(p->call.data, NULL, key[0],
+                                        &p->open_key, &existing);
+               if (W_ERROR_IS_OK(werr)) {
+                       DEBUG(TL, ("createkey %s %s\n",
+                                 existing ? "opened" : "created", key[0]));
+               } else {
+                       DEBUG(0, ("createkey %s failed: %s\n",
+                                 key[0], win_errstr(werr)));
+               }
+       }
+
+       return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+#define DEBUG_ADD_HEX(LEV, PTR, LEN)                                   \
+       do {                                                            \
+               int i;                                                  \
+               const unsigned char* ptr = (const unsigned char*)PTR;   \
+               for (i=0; i<LEN; i++) {                                 \
+                       DEBUGADD(LEV, ("'%c'(%02x)%s",                  \
+                                      isprint(ptr[i]) ? ptr[i] : '.',  \
+                                      (unsigned)ptr[i],                \
+                                      ((i+1 < LEN) && (i+1)%8)         \
+                                      ? ",  " : "\n"));                \
+               }                                                       \
+       } while(0)
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val(struct reg_import* p,
+                          const char* name, uint32_t type,
+                          const uint8_t* data, uint32_t len)
+{
+       WERROR werr = WERR_OK;
+
+       DEBUG(TL, ("%s(%x): >%s< = [%x]\n",  __FUNCTION__, type, name, len));
+       DEBUG_ADD_HEX(TL, data, len);
+
+       werr = p->call.setval.blob(p->call.data, p->open_key, name, type,
+                                  data, len);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("setval %s failed: %s\n",
+                         name, win_errstr(werr)));
+       }
+
+       return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val_registry_value(struct reg_import* p,
+                                         const char* name, uint32_t type,
+                                         const uint8_t* val, uint32_t len)
+{
+       WERROR werr = WERR_OK;
+       void* mem_ctx = talloc_new(p);
+       struct registry_value* v = NULL;
+
+       DEBUG(TL, ("%s(%x): >%s< = [%x]\n", __FUNCTION__, type, name, len));
+       DEBUG_ADD_HEX(TL, val, len);
+
+       werr = registry_pull_value(mem_ctx, &v, type,
+                                  discard_const(val), len, len);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("registry_pull_value %s failed: %s\n",
+                         name, win_errstr(werr)));
+               goto done;
+       }
+
+       werr = p->call.setval.registry_value(p->call.data, p->open_key,
+                                            name, v);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("setval.registry_value %s failed: %s\n",
+                         name,
+                         win_errstr(werr)));
+       }
+
+done:
+       talloc_free(mem_ctx);
+       return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val_regval_blob(struct reg_import* p,
+                                      const char* name, uint32_t type,
+                                      const uint8_t* data, uint32_t len)
+{
+       WERROR werr = WERR_OK;
+       void* mem_ctx = talloc_new(p);
+       struct regval_blob* v = NULL;
+
+       DEBUG(TL, ("%s(%x): >%s< = [%x]\n", __FUNCTION__, type, name, len));
+       DEBUG_ADD_HEX(TL, data, len);
+
+       v = regval_compose(mem_ctx, name, type, (const char*)data, len);
+       if (v == NULL) {
+               DEBUG(0, ("regval_compose %s failed\n", name));
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       werr = p->call.setval.regval_blob(p->call.data, p->open_key, v);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("setval %s failed: %s\n",
+                         name, win_errstr(werr)));
+       }
+
+done:
+       talloc_free(mem_ctx);
+
+       return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+int reg_parse_callback_val_del(struct reg_import* p,
+                              const char* name)
+{
+       WERROR werr = WERR_OK;
+
+       DEBUG(TL, ("%s: %s\n", __FUNCTION__, name));
+
+       werr = p->call.deleteval(p->call.data, p->open_key, name);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0, ("deleteval %s failed: %s\n",
+                         name, win_errstr(werr)));
+       }
+
+       return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+
+int reg_parse_callback_comment(struct reg_import* cb_private,
+                              const char* txt)
+{
+       DEBUG(TL, ("%s: %s\n", __FUNCTION__, txt));
+       return 0;
+}
+
+/******************************************************************************/
+static int nop(void* data)
+{
+       return 0;
+}
+
+
+struct reg_parse_callback* reg_import_adapter(const void* talloc_ctx,
+                                             struct reg_import_callback cb)
+{
+       struct reg_parse_callback* ret;
+       struct reg_import* p = talloc_zero(talloc_ctx, struct reg_import);
+       if (p == NULL) {
+               goto fail;
+       }
+       if (cb.openkey == NULL ) {
+               cb.openkey = (reg_import_callback_openkey_t)&nop;
+       }
+       if (cb.closekey == NULL ) {
+               cb.closekey = (reg_import_callback_closekey_t)&nop;
+       }
+       if (cb.createkey == NULL ) {
+               cb.createkey = (reg_import_callback_createkey_t)&nop;
+       }
+       if (cb.deletekey == NULL ) {
+               cb.deletekey = (reg_import_callback_deletekey_t)&nop;
+       }
+       if (cb.deleteval == NULL ) {
+               cb.deleteval = (reg_import_callback_deleteval_t)&nop;
+       }
+
+       p->call = cb;
+
+       ret = &p->reg_parse_callback;
+       ret->key     = (reg_parse_callback_key_t)     &reg_parse_callback_key;
+       ret->val_del = (reg_parse_callback_val_del_t) &reg_parse_callback_val_del;
+       ret->comment = (reg_parse_callback_comment_t) &reg_parse_callback_comment;
+       ret->data = p;
+
+       switch (cb.setval_type) {
+       case BLOB:
+               assert(cb.setval.blob != NULL);
+               ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val;
+               break;
+       case REGISTRY_VALUE:
+               assert(cb.setval.registry_value != NULL);
+               ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val_registry_value;
+               break;
+       case REGVAL_BLOB:
+               assert(cb.setval.regval_blob != NULL);
+               ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val_regval_blob;
+               break;
+       case NONE:
+               ret->val = NULL;
+               break;
+       default:
+               assert(false);
+       }
+
+       assert((struct reg_parse_callback*)p == ret);
+       return ret;
+fail:
+       talloc_free(p);
+       return NULL;
+}
diff --git a/source3/registry/reg_import.h b/source3/registry/reg_import.h
new file mode 100644 (file)
index 0000000..5922f9d
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Adapter to use reg_parse with the registry api
+ * @file   reg_import.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Jun 2010
+ */
+
+
+#ifndef REG_IMPORT_H
+#define REG_IMPORT_H
+
+#include "reg_parse.h"
+
+struct registry_value;
+struct regval_blob;
+
+/**
+ * Protoype for function called to open a key.
+ *
+ * @param private_data
+ * @param[in] parent    the parent of the key to open, may be NULL
+ * @param[in] name      the name of the key relative to parent.
+ * @param[out] key      the opened key
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_openkey_t) (void* private_data,
+                                               void* parent,
+                                               const char* name,
+                                               void** key);
+
+/**
+ * Protoype for function called to close a key.
+ *
+ * @param private_data
+ * @param key the key to close
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_closekey_t) (void* private_data,
+                                                 void* key);
+
+/**
+ * Protoype for function called to create (or open an existing) key.
+ *
+ * @param private_data
+ * @param[in] parent    the parent of the key to create, may be NULL
+ * @param[in] name      the name of the key relative to parent.
+ * @param[out] key      the opened key
+ * @param[out] existing whether we opened an existing key
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_createkey_t)(void* private_data,
+                                               void* parent,
+                                               const char* name,
+                                               void** key,
+                                               bool* existing);
+
+/**
+ * Protoype for function called to delete a key.
+ *
+ * @param private_data
+ * @param parent    the parent of the key to delete, may be NULL
+ * @param[in] name  the name of the key relative to parent.
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_deletekey_t)(void* private_data,
+                                                 void* parent,
+                                                 const char* name);
+
+/**
+ * Protoype for function called to delete a value.
+ *
+ * @param private_data
+ * @param parent    the key of the value to delete
+ * @param[in] name  the name of the value to delete.
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_deleteval_t)(void* private_data,
+                                               void* parent,
+                                               const char* name);
+
+/**
+ * Protoype for function called to set a value.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param name   the name of the value
+ * @param type   the type of the value
+ * @param data   the value of the value
+ * @param size   the number of bytes of data
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_setval_blob_t)(void* private_data,
+                                                 void* parent,
+                                                 const char* name,
+                                                 uint32_t type,
+                                                 const uint8_t* data,
+                                                 uint32_t size);
+
+/**
+ * Protoype for function called to set a value given as struct registry_value.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param name   the name of the value
+ * @param val    the value of the value
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_setval_registry_value_t) (
+       void* private_data,
+       void* parent,
+       const char* name,
+       const struct registry_value* val);
+
+/**
+ * Protoype for function called to set a value given as struct struct regval_blob.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param val    the value
+ *
+ * @return WERR_OK on success
+ */
+typedef        WERROR (*reg_import_callback_setval_regval_blob_t)(
+       void* private_data,
+       void* parent,
+       const struct regval_blob* val);
+
+/**
+ * Type handling the output of a reg_import object.
+ * It containes the functions to call and an opaque data pointer.
+ */
+struct reg_import_callback {
+       /** Function called to open key */
+       reg_import_callback_openkey_t   openkey;
+       /** Function called to close key */
+       reg_import_callback_closekey_t  closekey;
+       /** Function called to create or open key */
+       reg_import_callback_createkey_t createkey;
+       /** Function called to delete key */
+       reg_import_callback_deletekey_t deletekey;
+       /** Function called to delete value */
+       reg_import_callback_deleteval_t deleteval;
+
+       /** Function called to set value */
+       union {
+               reg_import_callback_setval_blob_t           blob;
+               reg_import_callback_setval_registry_value_t registry_value;
+               reg_import_callback_setval_regval_blob_t    regval_blob;
+       } setval;
+       /** Which function is called to set a value */
+       enum {
+               NONE=0,         /**< no setval function used */
+               BLOB,           /**< @ref reg_import_callback_setval_blob_t blob */
+               REGISTRY_VALUE, /**< @ref reg_import_callback_setval_registry_value_t registry_value */
+               REGVAL_BLOB,    /**< @ref reg_import_callback_setval_regval_blob_t regval_blob */
+       } setval_type;
+       void* data; /**< Private data passed to callback function */
+};
+
+
+/**
+ * Create a new reg_import object.
+ * Because its only purpose is to act as an reg_parse_callback the return type
+ * is accordingly.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb         the output handler
+ *
+ * @return a talloc'ed reg_import object, NULL on error
+ */
+struct reg_parse_callback* reg_import_adapter(const void* talloc_ctx,
+                                             struct reg_import_callback cb);
+#endif
diff --git a/source3/registry/reg_parse.c b/source3/registry/reg_parse.c
new file mode 100644 (file)
index 0000000..388eecd
--- /dev/null
@@ -0,0 +1,958 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Parser for dot.reg files
+ * @file   reg_parse.c
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Jun 2010
+ *
+ */
+
+#include "includes.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include "reg_parse_internal.h"
+#include "reg_parse.h"
+#include "reg_format.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <assert.h>
+#include <stdint.h>
+
+enum reg_parse_state {
+       STATE_DEFAULT,
+       STATE_KEY_OPEN,
+       STATE_VAL_HEX_CONT,
+       STATE_VAL_SZ_CONT
+};
+
+struct reg_parse {
+       struct reg_format_callback reg_format_callback;
+       cbuf* key;
+       cbuf* valname;
+       uint32_t   valtype;
+       cbuf* valblob;
+       cbuf* tmp;
+       struct reg_parse_callback call;
+       int ret;
+       int linenum;
+       enum reg_parse_state state;
+       struct reg_parse_options* opt;
+       smb_iconv_t str2UTF16;
+       unsigned flags;
+};
+
+/**
+ * @defgroup action Action
+ * @{
+ */
+static bool act_key(struct reg_parse* p, cbuf* keyname, bool del)
+{
+       const char* name = cbuf_gets(keyname, 0);
+       cbuf_swap(p->key, keyname);
+
+       assert(p->state == STATE_DEFAULT || p->state == STATE_KEY_OPEN);
+       p->state = del ? STATE_DEFAULT : STATE_KEY_OPEN;
+
+       assert(p->call.key);
+       p->ret = p->call.key(p->call.data, &name, 1, del);
+       return p->ret >= 0;
+}
+
+static bool value_callback(struct reg_parse* p)
+{
+       const char* name = cbuf_gets(p->valname,0);
+       const uint8_t* val = (const uint8_t*)cbuf_gets(p->valblob,0);
+       size_t len = cbuf_getpos(p->valblob);
+
+       assert(p->call.val);
+       p->ret = p->call.val(p->call.data, name, p->valtype, val, len);
+       return p->ret >= 0;
+}
+
+static bool act_val_hex(struct reg_parse* p, cbuf* value, bool cont)
+{
+       cbuf_swap(p->valblob, value);
+       assert((p->state == STATE_KEY_OPEN) || (p->state == STATE_VAL_HEX_CONT));
+
+       if (cont) {
+               p->state = STATE_VAL_HEX_CONT;
+       } else {
+               p->state = STATE_KEY_OPEN;
+
+               switch (p->valtype) {
+               case REG_EXPAND_SZ:
+               case REG_MULTI_SZ:
+                       if (p->str2UTF16 != NULL) {
+                               char* dst = NULL;
+                               const char* src = cbuf_gets(p->valblob, 0);
+                               const size_t slen = cbuf_getpos(p->valblob);
+                               size_t dlen = iconvert_talloc(p,
+                                                             p->str2UTF16,
+                                                             src, slen,
+                                                             &dst);
+                               if (dlen != -1) {
+                                       cbuf_swapptr(p->valblob, &dst, dlen);
+                               } else {
+                                       DEBUG(0, ("iconvert_talloc failed\n"));
+                               }
+                               talloc_free(dst);
+                       }
+               default:
+                       break;
+               }
+               return value_callback(p);
+       }
+       return true;
+}
+
+static bool act_val_dw(struct reg_parse* p, uint32_t val)
+{
+       assert(p->valtype == REG_DWORD);
+       assert(p->state == STATE_KEY_OPEN);
+
+       cbuf_clear(p->valblob);
+
+       if (cbuf_putdw(p->valblob, val) < 0) {
+               return false;
+       }
+       return value_callback(p);
+}
+
+static bool act_val_sz(struct reg_parse* p, cbuf* value, bool cont)
+{
+       cbuf_swap(p->valblob, value);
+
+       assert(p->valtype == REG_SZ);
+       assert((p->state == STATE_KEY_OPEN) || (p->state == STATE_VAL_SZ_CONT));
+
+       if (cont) {
+               p->state = STATE_VAL_SZ_CONT;
+       } else {
+               char* dst = NULL;
+               size_t dlen;
+               const char* src = cbuf_gets(p->valblob, 0);
+
+               p->state = STATE_KEY_OPEN;
+
+
+               if (convert_string_talloc(p->valblob, CH_UNIX, CH_UTF16LE,
+                                         src, strlen(src)+1,
+                                         &dst, &dlen, true))
+               {
+                       cbuf_swapptr(p->valblob, &dst, dlen);
+               } else {
+                       DEBUG(0, ("convert_string_talloc failed: >%s<\n"
+                                 "use it as is\t", src));
+               }
+               talloc_free(dst);
+
+               return value_callback(p);
+       }
+       return true;
+}
+
+static bool act_val_del(struct reg_parse* p)
+{
+       const char* name = cbuf_gets(p->valname, 0);
+
+       assert(p->call.val_del);
+       p->ret = p->call.val_del(p->call.data, name);
+       return p->ret >= 0;
+}
+
+static bool act_comment (struct reg_parse* p, const char* txt)
+{
+       assert(p->call.comment);
+       p->ret = p->call.comment(p->call.data, txt);
+       return p->ret >= 0;
+}
+/**@}*/
+
+
+static int nop(void* data)
+{
+       return 0;
+}
+
+
+struct reg_parse* reg_parse_new(const void* ctx,
+                               struct reg_parse_callback cb,
+                               const char* str_enc, unsigned flags)
+{
+       struct reg_parse* s = talloc_zero(ctx, struct reg_parse);
+       if (s == NULL)
+               return NULL;
+       s->key     = cbuf_new(s);
+       s->valname = cbuf_new(s);
+       s->valblob = cbuf_new(s);
+       s->tmp     = cbuf_new(s);
+       if ( (s->tmp == NULL) || (s->valblob == NULL)
+            || (s->valname == NULL) || (s->key == NULL) )
+       {
+               goto fail;
+       }
+
+       s->reg_format_callback.writeline = (reg_format_callback_writeline_t)&reg_parse_line;
+       s->reg_format_callback.data      = s;
+
+       s->valtype = 0;
+       if (cb.key == NULL) {
+               cb.key = (reg_parse_callback_key_t)&nop;
+       }
+       if (cb.val == NULL) {
+               cb.val = (reg_parse_callback_val_t)&nop;
+       }
+       if (cb.val_del == NULL) {
+               cb.val_del = (reg_parse_callback_val_del_t)&nop;
+       }
+       if (cb.comment == NULL) {
+               cb.comment = (reg_parse_callback_comment_t)&nop;
+       }
+
+       s->call = cb;
+       s->linenum = 0;
+       s->state = STATE_DEFAULT;
+       s->flags = flags;
+
+       if (str_enc && !set_iconv(&s->str2UTF16, "UTF-16LE", str_enc)) {
+               DEBUG(0, ("reg_parse_new: failed to set encoding: %s",
+                         str_enc));
+               goto fail;
+       }
+
+       assert(&s->reg_format_callback == (struct reg_format_callback*)s);
+       return s;
+fail:
+       talloc_free(s);
+       return NULL;
+}
+
+/**
+ * @defgroup parse Parser Primitive
+ * @ingroup internal
+ * @{
+ */
+
+
+static bool srprs_key(const char** ptr, cbuf* key, bool* del)
+{
+       const char* pos = *ptr;
+       const char* closing_bracket_pos = NULL;
+       size_t      closing_bracket_idx;
+
+       if (!srprs_skipws(&pos) || !srprs_char(&pos, '[')) {
+               return false;
+       }
+
+       *del = srprs_char(&pos, '-');
+
+       cbuf_clear(key);
+
+       while (true) {
+               while (srprs_charsetinv(&pos, "]\\", key))
+                       ;
+
+               switch (*pos) {
+
+               case ']':
+                       closing_bracket_idx = cbuf_getpos(key);
+                       closing_bracket_pos = pos;
+                       cbuf_putc(key, ']');
+                       pos++;
+                       break;
+
+               case '\\':
+                       cbuf_putc(key, '\\');
+                       /* n++; */
+                       /* cbuf_puts(subkeyidx, cbuf_getpos(key), sizeof(size_t)) */
+                       while (srprs_char(&pos,'\\'))
+                               ;
+                       break;
+
+               case '\0':
+                       if (closing_bracket_pos == NULL) {
+                               return false;
+                       }
+
+                       /* remove trailing backslash (if any) */
+                       if (*(closing_bracket_pos-1)=='\\') {
+                               closing_bracket_idx--;
+                       }
+
+                       cbuf_setpos(key, closing_bracket_idx);
+                       *ptr = closing_bracket_pos+1;
+                       return true;
+
+               default:
+                       assert(false);
+               }
+       }
+}
+
+static bool srprs_val_name(const char** ptr, cbuf* name)
+{
+       const char* pos = *ptr;
+       const size_t spos = cbuf_getpos(name);
+
+       if ( !srprs_skipws(&pos) ) {
+               goto fail;
+       }
+
+       if ( srprs_char(&pos, '@') ) {
+               cbuf_puts(name, "", -1);
+       }
+       else if (!srprs_quoted_string(&pos, name, NULL)) {
+               goto fail;
+       }
+
+       if (!srprs_skipws(&pos) || !srprs_char(&pos, '=')) {
+               goto fail;
+       }
+
+       *ptr = pos;
+       return true;
+
+fail:
+       cbuf_setpos(name, spos);
+       return false;
+}
+
+static bool srprs_val_dword(const char** ptr, uint32_t* type, uint32_t* val)
+{
+       const char* pos = *ptr;
+
+       if (!srprs_str(&pos, "dword:", -1)) {
+               return false;
+       }
+
+       if (!srprs_hex(&pos, 8, val)) {
+               return false;
+       }
+
+       *type = REG_DWORD;
+       *ptr  = pos;
+       return true;
+}
+
+static bool srprs_val_sz(const char** ptr, uint32_t* type, cbuf* val, bool* cont)
+{
+       if (!srprs_quoted_string(ptr, val, cont)) {
+               return false;
+       }
+
+       *type = REG_SZ;
+       return true;
+}
+
+
+static bool srprs_nl_no_eos(const char** ptr, cbuf* str, bool eof)
+{
+       const char* pos = *ptr;
+       const size_t spos = cbuf_getpos(str);
+
+       if( srprs_nl(&pos, str) && (eof || *pos != '\0')) {
+               *ptr = pos;
+               return true;
+       }
+       cbuf_setpos(str, spos);
+       return false;
+}
+
+
+static bool srprs_eol_cont(const char** ptr, bool* cont)
+{
+       const char* pos = *ptr;
+       bool bs = srprs_char(&pos, '\\');
+
+       if (!srprs_eol(&pos, NULL)) {
+               return false;
+       }
+
+       *cont = bs;
+       *ptr = pos;
+       return true;
+}
+
+/* matches the empty string, for zero length lists */
+static bool srprs_val_hex_values(const char** ptr, cbuf* val, bool* cont)
+{
+       const char* pos = *ptr;
+       unsigned u;
+
+       do {
+               if (!srprs_skipws(&pos) || !srprs_hex(&pos, 2, &u) || !srprs_skipws(&pos)) {
+                       break;
+               }
+               cbuf_putc(val, (char)u);
+       } while(srprs_char(&pos, ','));
+
+       *ptr = pos;
+
+       if (srprs_skipws(&pos) && srprs_eol_cont(&pos, cont)) {
+               *ptr = pos;
+       }
+
+       return true;
+}
+
+static bool srprs_val_hex(const char** ptr, uint32_t* ptype, cbuf* val,
+                      bool* cont)
+{
+       const char* pos = *ptr;
+       uint32_t type;
+
+       if (!srprs_str(&pos, "hex", -1)) {
+               return false;
+       }
+
+       if (srprs_char(&pos, ':')) {
+               type = REG_BINARY;
+       }
+       else if (!srprs_char(&pos, '(') ||
+                !srprs_hex(&pos, 8, &type) ||
+                !srprs_char(&pos,')') ||
+                !srprs_char(&pos, ':'))
+       {
+               return false;
+       }
+
+       if (!srprs_val_hex_values(&pos, val, cont)) {
+               return false;
+       }
+
+       *ptype = type;
+       *ptr = pos;
+       return true;
+}
+
+
+static bool srprs_comment(const char** ptr, cbuf* str)
+{
+       return srprs_char(ptr, ';') && srprs_line(ptr, str);
+}
+
+/**@}*/
+
+int reg_parse_set_options(struct reg_parse* parser, const char* options)
+{
+       static const char* DEFAULT ="enc=unix,flags=0";
+
+       int ret = 0;
+       char *key, *val;
+       void* ctx = talloc_new(parser);
+
+       if (options == NULL) {
+               options = DEFAULT;
+       }
+
+       while (srprs_option(&options, ctx, &key, &val)) {
+               if ((strcmp(key, "enc") == 0) || (strcmp(key, "strenc") == 0)) {
+               } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+                       char* end = NULL;
+                       if (val != NULL) {
+                               parser->flags = strtol(val, &end, 0);
+                       }
+                       if ((end==NULL) || (*end != '\0')) {
+                               DEBUG(0, ("Invalid flags format: %s\n",
+                                         val ? val : "<NULL>"));
+                               ret = -1;
+                       }
+               }
+               /* else if (strcmp(key, "hive") == 0) { */
+               /*      if (strcmp(val, "short") == 0) { */
+               /*              f->hive_fmt = REG_FMT_SHORT_HIVES; */
+               /*      } else if (strcmp(val, "long") == 0) { */
+               /*              f->hive_fmt = REG_FMT_LONG_HIVES; */
+               /*      } else if (strcmp(val, "preserve") == 0) { */
+               /*              f->hive_fmt = REG_FMT_PRESERVE_HIVES; */
+               /*      } else { */
+               /*              DEBUG(0, ("Invalid hive format: %s\n", val)); */
+               /*              ret = -1; */
+               /*      } */
+               /* } */
+       }
+       talloc_free(ctx);
+       return ret;
+}
+
+
+int reg_parse_line(struct reg_parse* parser, const char* line)
+{
+       const char* pos;
+       bool del=false;
+       cbuf* tmp=cbuf_clear(parser->tmp);
+       bool cb_ok = true;
+       bool cont = true;
+
+       if (!line) {
+               return -4;
+       }
+
+       parser->linenum++;
+       pos = line;
+
+       switch (parser->state) {
+       case STATE_VAL_HEX_CONT:
+               if (srprs_val_hex_values(&pos, parser->valblob, &cont)) {
+                       cb_ok = act_val_hex(parser, parser->valblob, cont);
+               }
+               goto done;
+       case STATE_VAL_SZ_CONT:
+               if (srprs_quoted_string(&pos, parser->valblob, &cont)) {
+                       cb_ok = act_val_sz(parser, parser->valblob, cont);
+               }
+               goto done;
+       default:
+               cont = false;
+       }
+
+       if ( !srprs_skipws(&pos) ) {
+               return -4;
+       }
+
+       /* empty line ?*/
+       if ( srprs_eol(&pos, NULL) ) {
+               return 0;
+       }
+
+       /* key line ?*/
+       else if (srprs_key(&pos, tmp, &del)) {
+               cb_ok = act_key(parser, tmp, del);
+       }
+
+       /* comment line ? */
+       else if (srprs_comment(&pos, tmp)) {
+               cb_ok = act_comment(parser, cbuf_gets(tmp, 0));
+       }
+
+       /* head line */
+       else if ((parser->linenum == 1) && srprs_line(&pos, tmp) ) {
+               /* cb_ok = act_head(parser, cbuf_gets(tmp, 0)); */
+       }
+
+       /* value line ?*/
+       else if (srprs_val_name(&pos, tmp)) {
+               uint32_t dw;
+               cbuf_swap(parser->valname, tmp);
+               cbuf_clear(tmp);
+
+               if (parser->state != STATE_KEY_OPEN) {
+                       DEBUG(0, ("value \"%s\" without a key at line: %i",
+                                 cbuf_gets(parser->valname, 0), parser->linenum));
+                       return -3;
+               }
+
+               if (!srprs_skipws(&pos)) {
+                       return -4;
+               }
+
+               if (srprs_char(&pos, '-')) {
+                       cb_ok = act_val_del(parser);
+               }
+               else if (srprs_val_dword(&pos, &parser->valtype, &dw)) {
+                       cb_ok = act_val_dw(parser, dw);
+               }
+               else if (srprs_val_sz(&pos, &parser->valtype, tmp, &cont)) {
+                       cb_ok = act_val_sz(parser, tmp, cont);
+               }
+               else if (srprs_val_hex(&pos, &parser->valtype, tmp, &cont)){
+                       cb_ok = act_val_hex(parser, tmp, cont);
+               }
+               else {
+                       DEBUG(0, ("value \"%s\" parse error"
+                                 "at line: %i pos: %li : %s",
+                                 cbuf_gets(parser->valname, 0), parser->linenum,
+                                 pos-line, pos));
+                       return -3;
+               }
+       }
+       else {
+               DEBUG(0, ("unrecognized line %i : %s\n", parser->linenum, line));
+               return -3;
+       }
+
+done:
+       if (!cb_ok)
+               return -2;
+
+       if (!srprs_skipws(&pos) || !srprs_eol(&pos, NULL)) {
+               DEBUG(0, ("trailing garbage at line: %i pos: %li : %s\n",
+                         parser->linenum, pos-line, pos));
+               return -1;
+       }
+       return 0;
+}
+
+/******************************************************************************/
+/**
+ * @addtogroup misc
+ * @{
+ */
+static bool lookslike_utf16(const char* line, size_t len, bool* little_endian)
+{
+       static const uint16_t M_LE = 0xFF80;
+       static const uint16_t M_BE = 0x80FF;
+       uint16_t mask;
+       bool le;
+
+       size_t l = MIN(len/2, 64);
+       uint16_t* u = (uint16_t*)line;
+       int i;
+
+       assert(len >= 2);
+
+       if ( u[0] & M_LE ) {
+               le = true;
+               mask = M_LE;
+       } else  if ( u[0] & M_BE ) {
+               le = false;
+               mask = M_BE;
+       } else {
+               return false;
+       }
+
+       for (i=1; i<l; i++) {
+               if ( u[i] & mask ) {
+                       return false;
+               }
+       }
+
+       *little_endian = le;
+       return true;
+}
+
+static bool lookslike_dos(const char* line, size_t len)
+{
+       int i;
+       for (i=0; i<len; i++) {
+               if ( (line[i] == '\0') || (line[i] & 0x80) ) {
+                       return false;
+               }
+               if ( (line[i] == '\r') && (i+1 < len) && (line[i+1] == '\n') ) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static bool guess_charset(const char** ptr,
+                         size_t* len,
+                         const char** file_enc,
+                         const char** str_enc)
+{
+       const char* charset = NULL;
+       const char* pos = *ptr;
+
+       if (*len < 4) {
+               return false;
+       }
+
+       if (srprs_bom(&pos, &charset, NULL)) {
+               *len -= (pos - *ptr);
+               *ptr = pos;
+               if (*file_enc == NULL) {
+                       *file_enc = charset;
+               }
+               else if( strcmp(*file_enc, charset) != 0 ) {
+                       DEBUG(0, ("file encoding forced to %s\n",
+                                 *file_enc));
+               }
+       }
+       else if (*file_enc == NULL) {
+               bool le;
+               if (lookslike_utf16(*ptr, *len, &le)) {
+                       *file_enc = le ? "UTF-16LE" : "UTF-16BE";
+               }
+               else if (lookslike_dos(*ptr, *len)) {
+                       *file_enc = "dos";
+               }
+               else {
+                       *file_enc = "unix";
+               }
+       }
+
+       if ((str_enc != NULL) && (*str_enc == NULL)) {
+               *str_enc = ( strncmp(*ptr, "REGEDIT4", 8) == 0)
+                       ? *file_enc
+                       : "UTF-16LE";
+       }
+
+       return true;
+}
+/**@}*/
+
+struct reg_parse_fd_opt {
+       const char* file_enc;
+       const char* str_enc;
+       unsigned flags;
+       int fail_level;
+};
+
+static struct reg_parse_fd_opt
+reg_parse_fd_opt(void* mem_ctx, const char* options)
+{
+       struct reg_parse_fd_opt ret = {
+               .file_enc = NULL,
+               .str_enc  = NULL,
+               .flags    = 0,
+       };
+
+       void* ctx = talloc_new(mem_ctx);
+       char *key, *val;
+
+       if (options == NULL) {
+               goto done;
+       }
+
+       while (srprs_option(&options, ctx, &key, &val)) {
+               if (strcmp(key, "enc") == 0) {
+                       ret.file_enc = talloc_steal(mem_ctx, val);
+                       ret.str_enc  = ret.file_enc;
+               } else if (strcmp(key, "strenc") == 0) {
+                       ret.str_enc = talloc_steal(mem_ctx, val);
+               } else if (strcmp(key, "fileenc") == 0) {
+                       ret.file_enc = talloc_steal(mem_ctx, val);
+               } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+                       char* end = NULL;
+                       if (val != NULL) {
+                               ret.flags = strtol(val, &end, 0);
+                       }
+                       if ((end==NULL) || (*end != '\0')) {
+                               DEBUG(0, ("Invalid format \"%s\": %s\n",
+                                         key, val ? val : "<NULL>"));
+                       }
+               } else if ((strcmp(key, "fail") == 0) && (val != NULL)) {
+                       char* end = NULL;
+                       if (val != NULL) {
+                               ret.fail_level = -strtol(val, &end, 0);
+                       }
+                       if ((end==NULL) || (*end != '\0')) {
+                               DEBUG(0, ("Invalid format \"%s\": %s\n",
+                                         key, val ? val : "<NULL>"));
+                       }
+               }
+       }
+done:
+       talloc_free(ctx);
+       return ret;
+}
+
+
+static void
+handle_iconv_errno(int err, const char* obuf, size_t linenum,
+                  smb_iconv_t cd, const char** iptr, size_t* ilen,
+                  char** optr, size_t *olen)
+{
+       const char *pos = obuf;
+       const char *ptr = obuf;
+       switch(err) {
+       case EINVAL:
+               /* DEBUG(0, ("Incomplete multibyte sequence\n")); */
+       case E2BIG:
+               return;
+       case EILSEQ:
+               break;
+       default:
+               assert(false);
+       }
+
+       **optr = '\0';
+       while (srprs_line(&ptr, NULL) && srprs_nl(&ptr, NULL)) {
+               pos = ptr;
+               linenum++;
+       }
+       if (pos == *optr) {
+               pos = MAX(obuf, *optr-60);
+       }
+       DEBUG(0, ("Illegal multibyte sequence at line %lu: %s", linenum+1, pos));
+
+       assert(ilen > 0);
+       do {
+               size_t il = 1;
+               DEBUGADD(0, ("<%02x>", (unsigned char)**iptr));
+
+               if (olen > 0) {
+                       *(*optr)++ = '\?';
+                       (*iptr)++;
+                       /* Todo: parametrize, e.g. skip: *optr++ = *iptr++; */
+                       (*ilen)--;
+               }
+
+               if (smb_iconv(cd, iptr, &il, optr, olen) != (size_t)-1 || (errno != EILSEQ)) {
+                       if(il == 0)
+                               (*ilen)-- ;
+                       break;
+               }
+
+       }
+       while ((*ilen > 0) && (*olen > 0));
+
+       DEBUGADD(0, ("\n"));
+
+}
+
+int reg_parse_fd(int fd, const struct reg_parse_callback* cb, const char* opts)
+{
+       void* mem_ctx            = talloc_stackframe();
+       cbuf* line               = cbuf_new(mem_ctx);
+       smb_iconv_t cd           = (smb_iconv_t)-1;
+       struct reg_parse* parser = NULL;
+       char buf_raw[1024];
+       char buf_unix[1025];
+
+       ssize_t nread;
+       size_t  nconv;
+       const char* pos;
+       const char* iptr;
+       char* optr;
+        size_t ilen;
+       size_t olen;
+       int ret = -1;
+       bool eof = false;
+       size_t linenum = 0;
+
+       struct reg_parse_fd_opt opt = reg_parse_fd_opt(mem_ctx, opts);
+
+       if (cb == NULL) {
+               DEBUG(0,("reg_parse_fd: NULL callback\n"));
+               goto done;
+       }
+
+       nread = read(fd, buf_raw, sizeof(buf_raw));
+       if (nread < 0) {
+               DEBUG(0, ("reg_parse_fd: read failed: %s\n", strerror(errno)));
+               ret = nread;
+               goto done;
+       }
+
+       iptr = &buf_raw[0];
+       ilen = nread;
+
+       if (!guess_charset(&iptr, &ilen,
+                          &opt.file_enc, &opt.str_enc))
+       {
+               DEBUG(0, ("reg_parse_fd: failed to guess encoding\n"));
+               goto done;
+       }
+
+       DEBUG(0, ("reg_parse_fd: encoding file: %s str: %s\n",
+                 opt.file_enc, opt.str_enc));
+
+
+       if (!set_iconv(&cd, "unix", opt.file_enc)) {
+               DEBUG(0, ("reg_parse_fd: failed to set file encoding %s\n",
+                         opt.file_enc));
+               goto done;
+       }
+
+       parser = reg_parse_new(mem_ctx, *cb, opt.str_enc, opt.flags);
+
+       optr = &buf_unix[0];
+       while (!eof) {
+               olen = sizeof(buf_unix) - (optr - buf_unix) - 1 ;
+               while ( olen > 0 ) {
+                       memmove(buf_raw, iptr, ilen);
+
+                       nread = read(fd, buf_raw + ilen, sizeof(buf_raw) - ilen);
+                       if (nread < 0) {
+                               DEBUG(0, ("reg_parse_fd: read failed: %s\n", strerror(errno)));
+                               ret = nread;
+                               goto done;
+                       }
+
+                       iptr =  buf_raw;
+                       ilen += nread;
+
+                       if (ilen == 0) {
+                               smb_iconv(cd, NULL, NULL, &optr, &olen);
+                               eof = true;
+                               break;
+                       }
+
+                       nconv = smb_iconv(cd, &iptr, &ilen, &optr, &olen);
+
+                       if (nconv == (size_t)-1) {
+                               handle_iconv_errno(errno, buf_unix, linenum,
+                                                  cd, &iptr, &ilen,
+                                                  &optr, &olen);
+                               break;
+                       }
+               }
+       /* process_lines: */
+               *optr = '\0';
+               pos = &buf_unix[0];
+
+               while ( srprs_line(&pos, line) && srprs_nl_no_eos(&pos, line, eof)) {
+                       linenum ++;
+                       ret = reg_parse_line(parser, cbuf_gets(line, 0));
+                       if (ret < opt.fail_level) {
+                               goto done;
+                       }
+                       cbuf_clear(line);
+               }
+               memmove(buf_unix, pos, optr - pos);
+               optr -= (pos - buf_unix);
+       }
+
+       ret = 0;
+done:
+       set_iconv(&cd, NULL, NULL);
+       talloc_free(mem_ctx);
+       return ret;
+}
+
+int reg_parse_file(const char* fname, const struct reg_parse_callback* cb,
+                  const char* opt)
+{
+       int ret = -1;
+       int fd;
+
+       fd = open(fname, O_RDONLY);
+       if (fd < 0) {
+               DEBUG(0, ("reg_parse_file: open failed: %s\n", strerror(errno)));
+               return -1;
+       }
+
+       ret = reg_parse_fd(fd, cb, opt);
+
+       close(fd);
+       return ret;
+}
+
+/* static struct registry_key *find_regkey_by_hnd(pipes_struct *p, */
+/*                                            struct policy_handle *hnd) */
+/* { */
+/*     struct registry_key *regkey = NULL; */
+
+/*     if(!find_policy_by_hnd(p,hnd,(void **)(void *)&regkey)) { */
+/*             DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: ")); */
+/*             return NULL; */
+/*     } */
+
+/*     return regkey; */
+/* } */
diff --git a/source3/registry/reg_parse.h b/source3/registry/reg_parse.h
new file mode 100644 (file)
index 0000000..0694225
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Parser for registration entries (.reg) files.
+ * A parser is a talloced incarnation of an opaque struct reg_parse.
+ * It is fed with the (.reg) file line by line calling @ref reg_parse_line
+ * and emits output by calling functions from its reg_parse_callback.
+ * @file   reg_parse.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Jun 2010
+ */
+
+#ifndef REG_PARSE_H
+#define REG_PARSE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * Protoype for function called on key found.
+ * The usual action to take is delete the key if del==true, open it if
+ * already existing or create a new one.
+ *
+ * @param private_data
+ * @param key
+ * @param klen number of elements in key
+ * @param del whether to delete the key
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_key
+ */
+typedef int (*reg_parse_callback_key_t) (void* private_data,
+                                        const char* key[],
+                                        size_t klen,
+                                        bool del);
+
+/**
+ * Protoype for function called on value found.
+ * The usual action to take is set the value of the last opened key.
+ *
+ * @param private_data
+ * @param name the values name
+ * @param type the values type
+ * @param data the values value
+ * @param len  the number of bytes of data
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_value
+ */
+typedef int (*reg_parse_callback_val_t) (void*  private_data,
+                                        const char* name,
+                                        uint32_t type,
+                                        const uint8_t* data,
+                                        uint32_t len);
+
+/**
+ * Protoype for function called on value delete found.
+ * Delete value from the last opened key. It is usually no error if
+ * no such value exist.
+ *
+ * @param private_data
+ * @param name
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_value_delete
+ */
+typedef int (*reg_parse_callback_val_del_t) (void* private_data,
+                                            const char* name);
+
+
+/**
+ * Protoype for function called on comment found.
+ *
+ * @param private_data
+ * @param line comment with marker removed.
+ *
+ * @retval  >=0 on success
+ *
+ * @see reg_format_comment
+ */
+typedef int (*reg_parse_callback_comment_t) (void* private_data,
+                                            const char* line);
+
+/**
+ * Type handling the output of a reg_parse object.
+ * It containes the functions to call and an opaque data pointer.
+ */
+typedef struct reg_parse_callback {
+       reg_parse_callback_key_t key; /**< Function called on key found */
+       reg_parse_callback_val_t val; /**< Function called on value found */
+        /** Function called on value delete found */
+       reg_parse_callback_val_del_t val_del;
+       /** Function called on comment found */
+       reg_parse_callback_comment_t comment;
+       void* data; /**< Private data passed to callback function */
+} reg_parse_callback;
+
+/**
+ * A Parser for a registration entries (.reg) file.
+ *
+ * It may be used as a reg_format_callback, so the following is valid:
+ * @code
+ * reg_format* f = reg_format_new(mem_ctx,
+ *                                (reg_format_callback)reg_parse_new(mem_ctx, cb, NULL, 0),
+ *                                NULL, 0, "\\");
+ * @endcode
+ * @see reg_format
+ */
+typedef struct reg_parse reg_parse;
+
+/**
+ * Create a new reg_parse object.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb         the output handler
+ * @param str_enc    the charset of hex encoded strings (REG_MULTI_SZ, REG_EXAND_SZ) if not UTF-16
+ * @param flags
+ *
+ * @return a talloc'ed reg_parse object, NULL on error
+ */
+reg_parse* reg_parse_new(const void* talloc_ctx,
+                        reg_parse_callback cb,
+                        const char* str_enc,
+                        unsigned flags);
+
+/**
+ * Feed one line to the parser.
+ *
+ * @param parser
+ * @param line one line from a (.reg) file, in UNIX charset
+ *
+ * @return 0 on success
+ *
+ * @see reg_format_callback_writeline_t
+ */
+int reg_parse_line(struct reg_parse* parser, const char* line);
+
+
+/**
+ * Parse a (.reg) file, read from a file descriptor.
+ *
+ * @param fd the file descriptor
+ * @param cb the output handler
+ * @param opts
+ *
+ * @return 0 on success
+ */
+int reg_parse_fd(int fd,
+                const reg_parse_callback* cb,
+                const char* opts);
+
+/**
+ * Parse a (.reg) file
+ *
+ * @param filename the file to open
+ * @param cb the output handler
+ * @param opts
+ *
+ * @return 0 on success
+ */
+int reg_parse_file(const char* filename,
+                  const reg_parse_callback* cb,
+                  const char* opts);
+
+int reg_parse_set_options(reg_parse* parser, const char* opt);
+
+/******************************************************************************/
+
+
+#endif /* REG_PARSE_H */
diff --git a/source3/registry/reg_parse_internal.c b/source3/registry/reg_parse_internal.c
new file mode 100644 (file)
index 0000000..e257d67
--- /dev/null
@@ -0,0 +1,347 @@
+/* * Samba Unix/Linux SMB client library
+ *
+ * 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/>.
+ */
+/**
+ * @file   reg_parse_internal.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Sep 2010
+ * @brief
+ */
+
+#include "reg_parse_internal.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include <assert.h>
+
+size_t iconvert_talloc(const void* ctx,
+                      smb_iconv_t cd,
+                      const char* src, size_t srclen,
+                      char** pdst)
+{
+       size_t dstlen, ret;
+       size_t obytes, ibytes;
+       char *optr, *dst, *tmp;
+       const char* iptr;
+
+       if (cd == NULL || cd == ((smb_iconv_t)-1)) {
+               return -1;
+       }
+
+       dst = *pdst;
+
+       if (dst == NULL) {
+               /* dstlen = 2*srclen + 2; */
+               dstlen = srclen + 2;
+               dst = talloc_size(ctx, dstlen);
+               if (dst == NULL) {
+                       DEBUG(0,("iconver_talloc no mem\n"));
+                       return -1;
+               }
+       } else {
+               dstlen = talloc_get_size(dst);
+       }
+convert:
+       iptr   = src;
+       ibytes = srclen;
+       optr   = dst;
+       obytes = dstlen-2;
+
+       ret = smb_iconv(cd, &iptr, &ibytes, &optr, &obytes);
+
+       if(ret == -1) {
+               const char *reason="unknown error";
+               switch(errno) {
+               case EINVAL:
+                       reason="Incomplete multibyte sequence";
+                       break;
+               case E2BIG:
+                       dstlen = 2*dstlen + 2;
+                       tmp    = talloc_realloc(ctx, dst, char, dstlen);
+                       if (tmp == NULL) {
+                               reason="talloc_realloc failed";
+                               break;
+                       }
+                       dst = tmp;
+                       goto convert;
+               case EILSEQ:
+                       reason="Illegal multibyte sequence";
+                       break;
+               }
+               DEBUG(0,("Conversion error: %s(%.80s) %li\n", reason, iptr, iptr-src));
+               talloc_free(dst);
+               return -1;
+       }
+
+       assert(dstlen > dstlen - 2 - obytes);
+       dstlen = (dstlen-2) - obytes;
+
+       SSVAL(dst, dstlen, 0);
+
+       *pdst = dst;
+       return dstlen;
+}
+
+#ifndef HKEY_CURRENT_CONFIG
+#define HKEY_CURRENT_CONFIG            0x80000005
+#endif
+#ifndef HKEY_DYN_DATA
+#define HKEY_DYN_DATA                  0x80000006
+#endif
+#ifndef HKEY_PERFORMANCE_TEXT
+#define HKEY_PERFORMANCE_TEXT          0x80000050
+#endif
+#ifndef HKEY_PERFORMANCE_NLSTEXT
+#define HKEY_PERFORMANCE_NLSTEXT       0x80000060
+#endif
+
+#define HIVE_INFO_ENTRY(SHORT,LONG)                    \
+static const struct hive_info HIVE_INFO_##SHORT = {    \
+       .handle = LONG,                                 \
+       .short_name = #SHORT,                           \
+       .short_name_len = sizeof(#SHORT)-1,             \
+       .long_name = #LONG,                             \
+       .long_name_len = sizeof(#LONG)-1,               \
+}
+
+HIVE_INFO_ENTRY(HKLM, HKEY_LOCAL_MACHINE);
+HIVE_INFO_ENTRY(HKCU, HKEY_CURRENT_USER);
+HIVE_INFO_ENTRY(HKCR, HKEY_CLASSES_ROOT);
+HIVE_INFO_ENTRY(HKU , HKEY_USERS);
+HIVE_INFO_ENTRY(HKCC, HKEY_CURRENT_CONFIG);
+HIVE_INFO_ENTRY(HKDD, HKEY_DYN_DATA);
+HIVE_INFO_ENTRY(HKPD, HKEY_PERFORMANCE_DATA);
+HIVE_INFO_ENTRY(HKPT, HKEY_PERFORMANCE_TEXT);
+HIVE_INFO_ENTRY(HKPN, HKEY_PERFORMANCE_NLSTEXT);
+#undef HIVE_INFO_ENTRY
+
+static const struct hive_info* HIVE_INFO[] = {
+       &HIVE_INFO_HKLM, &HIVE_INFO_HKCU, &HIVE_INFO_HKCR, &HIVE_INFO_HKU,
+       &HIVE_INFO_HKCC, &HIVE_INFO_HKDD, &HIVE_INFO_HKPD, &HIVE_INFO_HKPT,
+       &HIVE_INFO_HKPN, NULL
+};
+
+const struct hive_info* hive_info(const char* name, int nlen)
+{
+       const struct hive_info** info;
+       char buf[32];
+       int s;
+
+       if (nlen >= sizeof(buf)) {
+               return NULL;
+       }
+       for (s=0; s<nlen; s++) {
+               buf[s] = toupper(name[s]);
+       }
+       buf[s] = '\0';
+
+       if ((s < 3) || (strncmp(buf, "HK", 2) != 0)) {
+               return NULL;
+       }
+
+       if (s <= 4) {
+               for(info = HIVE_INFO; *info; info++) {
+                       if (strcmp(buf+2, (*info)->short_name+2) == 0) {
+                               return *info;
+                       }
+               }
+               return NULL;
+       }
+
+       if ((s < 10) || (strncmp(buf, "HKEY_", 5)) != 0) {
+               return NULL;
+       }
+
+       for(info = HIVE_INFO; *info; info++) {
+               if (strcmp(buf+5, (*info)->long_name+5) == 0) {
+                       return *info;
+               }
+       }
+       return NULL;
+}
+
+const char* get_charset(const char* c)
+{
+       if (strcmp(c, "dos") == 0) {
+               return lp_dos_charset();
+       } else if (strcmp(c, "unix") == 0) {
+               return lp_unix_charset();
+       } else {
+               return c;
+       }
+}
+
+bool set_iconv(smb_iconv_t* t, const char* to, const char* from)
+{
+       smb_iconv_t cd = (smb_iconv_t)-1;
+
+       if (to && from) {
+               to   = get_charset(to);
+               from = get_charset(from);
+               cd   = smb_iconv_open(to, from);
+               if (cd == ((smb_iconv_t)-1)) {
+                       return false;
+               }
+       }
+       if ((*t != (smb_iconv_t)NULL) && (*t != (smb_iconv_t)-1)) {
+               smb_iconv_close(*t);
+       }
+       *t = cd;
+       return true;
+}
+
+/**
+ * Parse option string
+ * @param[in,out] ptr parse position
+ * @param[in] mem_ctx talloc context
+ * @param[out] name ptr 2 value
+ * @param[out] value ptr 2 value
+ * @return true on success
+ */
+bool srprs_option(const char** ptr, const void* mem_ctx, char** name, char** value)
+{
+       const char* pos = *ptr;
+       void* ctx = talloc_new(mem_ctx);
+
+       cbuf* key = cbuf_new(ctx);
+       cbuf* val = NULL;
+
+       while(srprs_charsetinv(&pos, ",= \t\n\r", key))
+               ;
+       if (pos == *ptr) {
+               talloc_free(ctx);
+               return false;
+       }
+
+       if (name != NULL) {
+               *name = talloc_steal(mem_ctx, cbuf_gets(key, 0));
+       }
+
+       if (*pos == '=') {
+               val = cbuf_new(ctx);
+               pos++;
+               if (!srprs_quoted_string(ptr, val, NULL)) {
+                       while(srprs_charsetinv(&pos, ", \t\n\r", val))
+                               ;
+               }
+               if (value != NULL) {
+                       *value = talloc_steal(mem_ctx, cbuf_gets(val, 0));
+               }
+       } else {
+               if (value != NULL) {
+                       *value = NULL;
+               }
+       }
+
+       while(srprs_char(&pos, ','))
+               ;
+
+       *ptr = pos;
+       return true;
+}
+
+#define CH_INVALID ((charset_t)-1)
+static const struct {
+       const char* const name;
+       charset_t ctype;
+       int  len;
+       char seq[4];
+} BOM[] = {
+       {"UTF-8",    CH_UTF8,    3, {0xEF, 0xBB, 0xBF}},
+       {"UTF-32LE", CH_INVALID, 4, {0xFF, 0xFE, 0x00, 0x00}},
+       {"UTF-16LE", CH_UTF16LE, 2, {0xFF, 0xFE}},
+       {"UTF-16BE", CH_UTF16BE, 2, {0xFE, 0xFF}},
+       {"UTF-32BE", CH_INVALID, 4, {0x00, 0x00, 0xFE, 0xFF}},
+       {NULL,       CH_INVALID, 0, {}}
+};
+
+bool srprs_bom(const char** ptr, const char** name, charset_t* ctype)
+{
+       int i;
+       for (i=0; BOM[i].name; i++) {
+               if (memcmp(*ptr, BOM[i].seq, BOM[i].len) == 0) {
+                       break;
+               }
+       }
+
+       if (BOM[i].name != NULL) {
+               DEBUG(0, ("Found Byte Order Mark for : %s\n", BOM[i].name));
+
+               if (name != NULL) {
+                       *name  = BOM[i].name;
+               }
+
+               if (ctype != NULL) {
+                       *ctype = BOM[i].ctype;
+               }
+
+               *ptr  += BOM[i].len;
+
+               return true;
+       }
+       return false;
+}
+
+int write_bom(FILE* file, const char* charset, charset_t ctype)
+{
+       int i;
+       if ( charset == NULL ) {
+               for (i=0; BOM[i].name; i++) {
+                       if (BOM[i].ctype == ctype) {
+                               return fwrite(BOM[i].seq, 1, BOM[i].len, file);
+                       }
+               }
+               DEBUG(0, ("No Byte Order Mark for charset_t: %u\n", (unsigned)ctype));
+       } else {
+               for (i=0; BOM[i].name; i++) {
+                       if (StrCaseCmp(BOM[i].name, charset) == 0) {
+                               return fwrite(BOM[i].seq, 1, BOM[i].len, file);
+                       }
+               }
+               DEBUG(0, ("No Byte Order Mark for charset_t: %s\n", charset));
+       }
+       return 0;
+}
+
+
+int cbuf_puts_case(cbuf* s, const char* str, size_t len, enum fmt_case fmt)
+{
+       size_t pos = cbuf_getpos(s);
+       int ret = cbuf_puts(s, str, len);
+       char* ptr = cbuf_gets(s,pos);
+
+       if (ret <= 0) {
+               return ret;
+       }
+
+       switch (fmt) {
+       case FMT_CASE_PRESERVE:
+               break;
+       case FMT_CASE_UPPER:
+               while(*ptr != '\0') {
+                       *ptr = toupper(*ptr);
+                       ptr++;
+               }
+               break;
+       case FMT_CASE_TITLE:
+               *ptr = toupper(*ptr);
+               ptr++;
+       case FMT_CASE_LOWER:
+               while(*ptr != '\0') {
+                       *ptr = tolower(*ptr);
+                       ptr++;
+               }
+       }
+       return ret;
+}
diff --git a/source3/registry/reg_parse_internal.h b/source3/registry/reg_parse_internal.h
new file mode 100644 (file)
index 0000000..56e2127
--- /dev/null
@@ -0,0 +1,102 @@
+/* Samba Unix/Linux SMB client library
+ *
+ * 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/>.
+ */
+
+/**
+ * @brief  Some stuff used by reg_parse and reg_format.
+ * It might be usefull elsewehre but need some review of the interfaces.
+ * @file   reg_parse_internal.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Sep 2010
+ */
+#ifndef __REG_PARSE_INTERNAL_H
+#define __REG_PARSE_INTERNAL_H
+
+#include "includes.h"
+
+struct cbuf;
+
+#define USE_NATIVE_ICONV
+#ifdef USE_NATIVE_ICONV
+#  define smb_iconv_t     iconv_t
+#  define smb_iconv(CD, IPTR, ILEN, OPTR, OLEN) \
+       iconv(CD, (char**)(IPTR), ILEN, OPTR, OLEN)
+#  define smb_iconv_open  iconv_open
+#  define smb_iconv_close iconv_close
+#endif
+
+size_t iconvert_talloc(const void* ctx,
+                      smb_iconv_t cd,
+                      const char* src, size_t srclen,
+                      char** pdst);
+
+struct hive_info {
+       uint32_t handle;
+       const char* short_name;
+       size_t short_name_len;
+       const char* long_name;
+       size_t long_name_len;
+};
+
+const struct hive_info* hive_info(const char* name, int nlen);
+
+const char* get_charset(const char* c);
+
+bool set_iconv(smb_iconv_t* t, const char* to, const char* from);
+
+/**
+ * Parse option string
+ * @param[in,out] ptr parse position
+ * @param[in] mem_ctx talloc context
+ * @param[out] name ptr 2 value
+ * @param[out] value ptr 2 value
+ * @return true on success
+ */
+bool srprs_option(const char** ptr, const void* mem_ctx, char** name, char** value);
+
+/**
+ * Write Byte Order Mark for \p charset to file.
+ * If \c charset==NULL write BOM for \p ctype.
+ *
+ * @param[in] file file to write to
+ * @param[in] charset
+ * @param[in] ctype
+ *
+ * @return number of bytes written, -1 on error
+ * @todo write to cbuf
+ */
+int write_bom(FILE* file, const char* charset, charset_t ctype);
+
+/**
+ * Parse Byte Order Mark.
+ *
+ * @param[in,out] ptr parse position
+ * @param[out] name name of characterset
+ * @param[out] ctype charset_t
+ *
+ * @return true if found
+ * @ingroup parse bom
+ */
+bool srprs_bom(const char** ptr, const char** name, charset_t* ctype);
+
+enum fmt_case {
+       FMT_CASE_PRESERVE=0,
+       FMT_CASE_UPPER,
+       FMT_CASE_LOWER,
+       FMT_CASE_TITLE
+};
+int cbuf_puts_case(struct cbuf* s, const char* str, size_t len, enum fmt_case fmt);
+
+#endif /* __REG_PARSE_INTERNAL_H */