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
--- /dev/null
+/*
+ * 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)®_format_key,
+ .val = (reg_parse_callback_val_t)®_format_value,
+ .val_del = (reg_parse_callback_val_del_t)®_format_value_delete,
+ .comment = (reg_parse_callback_comment_t)®_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 = ®_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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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) ®_parse_callback_key;
+ ret->val_del = (reg_parse_callback_val_del_t) ®_parse_callback_val_del;
+ ret->comment = (reg_parse_callback_comment_t) ®_parse_callback_comment;
+ ret->data = p;
+
+ switch (cb.setval_type) {
+ case BLOB:
+ assert(cb.setval.blob != NULL);
+ ret->val = (reg_parse_callback_val_t) ®_parse_callback_val;
+ break;
+ case REGISTRY_VALUE:
+ assert(cb.setval.registry_value != NULL);
+ ret->val = (reg_parse_callback_val_t) ®_parse_callback_val_registry_value;
+ break;
+ case REGVAL_BLOB:
+ assert(cb.setval.regval_blob != NULL);
+ ret->val = (reg_parse_callback_val_t) ®_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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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)®_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 *)®key)) { */
+/* DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: ")); */
+/* return NULL; */
+/* } */
+
+/* return regkey; */
+/* } */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/* * 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;
+}
--- /dev/null
+/* 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 */