From: Gregor Beck Date: Wed, 29 Sep 2010 12:54:15 +0000 (+0200) Subject: s3-registry: add support for registration entries (.reg) files X-Git-Url: http://git.samba.org/?p=obnox%2Fsamba-ctdb.git;a=commitdiff_plain;h=8d71ca6371583b725bb7d97166592df94d7d6eea s3-registry: add support for registration entries (.reg) files --- diff --git a/source3/Makefile.in b/source3/Makefile.in index 42cf856120..521636c814 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 index 0000000000..9936662bf9 --- /dev/null +++ b/source3/registry/reg_format.c @@ -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 . + */ + +/** + * @brief Format dot.reg files + * @file reg_format.c + * @author Gregor Beck + * @date Sep 2010 + */ + +#include "reg_format.h" +#include "reg_parse.h" +#include "reg_parse_internal.h" +#include "cbuf.h" +#include "srprs.h" +#include + +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 : "")); + 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 : "")); + } + } 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; +} diff --git a/source3/registry/reg_format.h b/source3/registry/reg_format.h new file mode 100644 index 0000000000..5d0a4f2536 --- /dev/null +++ b/source3/registry/reg_format.h @@ -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 . + */ + +/** + * @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 + * @date Sep 2010 + */ +#ifndef __REG_FORMAT_H +#define __REG_FORMAT_H + +#include +#include +#include + +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 index 0000000000..77116c3756 --- /dev/null +++ b/source3/registry/reg_import.c @@ -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 . + */ + +#include "includes.h" +#include "reg_parse.h" +#include "reg_import.h" +#include + +/* 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%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; +} diff --git a/source3/registry/reg_import.h b/source3/registry/reg_import.h new file mode 100644 index 0000000000..5922f9d33b --- /dev/null +++ b/source3/registry/reg_import.h @@ -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 . + */ + +/** + * @brief Adapter to use reg_parse with the registry api + * @file reg_import.h + * @author Gregor Beck + * @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 index 0000000000..388eecdce8 --- /dev/null +++ b/source3/registry/reg_parse.c @@ -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 . + */ + +/** + * @brief Parser for dot.reg files + * @file reg_parse.c + * @author Gregor Beck + * @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 : "")); + 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")); + } + } 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 : "")); + } + } + } +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; */ +/* } */ diff --git a/source3/registry/reg_parse.h b/source3/registry/reg_parse.h new file mode 100644 index 0000000000..0694225ef5 --- /dev/null +++ b/source3/registry/reg_parse.h @@ -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 . + */ + +/** + * @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 + * @date Jun 2010 + */ + +#ifndef REG_PARSE_H +#define REG_PARSE_H + +#include +#include + +/** + * 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 index 0000000000..e257d67772 --- /dev/null +++ b/source3/registry/reg_parse_internal.c @@ -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 . + */ +/** + * @file reg_parse_internal.h + * @author Gregor Beck + * @date Sep 2010 + * @brief + */ + +#include "reg_parse_internal.h" +#include "cbuf.h" +#include "srprs.h" +#include + +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; sshort_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 index 0000000000..56e2127ca1 --- /dev/null +++ b/source3/registry/reg_parse_internal.h @@ -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 . + */ + +/** + * @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 + * @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 */