summaryrefslogtreecommitdiff
path: root/source4/lib/registry/tools/regpatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/registry/tools/regpatch.c')
-rw-r--r--source4/lib/registry/tools/regpatch.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c
new file mode 100644
index 0000000000..f76da7ebf9
--- /dev/null
+++ b/source4/lib/registry/tools/regpatch.c
@@ -0,0 +1,808 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) 2002, Richard Sharpe, rsharpe@richardsharpe.com
+ Copyright (C) 2004, Jelmer Vernooij, jelmer@samba.org
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * Routines to parse a REGEDIT4 file
+ *
+ * The file consists of:
+ *
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE 0
+#define CMD_ADD_KEY 1
+#define CMD_DEL_KEY 2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+#include <include/includes.h>
+
+typedef struct val_spec_list {
+ struct val_spec_list *next;
+ char *name;
+ int type;
+ char *val; /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+ int cmd;
+ char *key;
+ int val_count;
+ VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+ int len, line_len;
+ char *line;
+} CMD_LINE;
+
+static void free_val_spec_list(VAL_SPEC_LIST *vl)
+{
+ if (!vl) return;
+ if (vl->name) free(vl->name);
+ if (vl->val) free(vl->val);
+ free(vl);
+
+}
+
+/*
+ * Some routines to handle lines of info in the command files
+ */
+static void skip_to_eol(int fd)
+{
+ int rc;
+ char ch = 0;
+
+ while ((rc = read(fd, &ch, 1)) == 1) {
+ if (ch == 0x0A) return;
+ }
+ if (rc < 0) {
+ DEBUG(0, ("Could not read file descriptor: %d, %s\n",
+ fd, strerror(errno)));
+ exit(1);
+ }
+}
+
+static void free_cmd(CMD *cmd)
+{
+ if (!cmd) return;
+
+ while (cmd->val_spec_list) {
+ VAL_SPEC_LIST *tmp;
+
+ tmp = cmd->val_spec_list;
+ cmd->val_spec_list = tmp->next;
+ free(tmp);
+ }
+
+ free(cmd);
+
+}
+
+static void free_cmd_line(CMD_LINE *cmd_line)
+{
+ if (cmd_line) {
+ if (cmd_line->line) free(cmd_line->line);
+ free(cmd_line);
+ }
+}
+
+static void print_line(struct cmd_line *cl)
+{
+ char *pl;
+
+ if (!cl) return;
+
+ pl = smb_xmalloc(cl->line_len + 1);
+
+ strncpy(pl, cl->line, cl->line_len);
+ pl[cl->line_len] = 0;
+
+ fprintf(stdout, "%s\n", pl);
+ free(pl);
+}
+
+#define INIT_ALLOC 10
+
+/*
+ * Read a line from the input file.
+ * NULL returned when EOF and no chars read
+ * Otherwise we return a cmd_line *
+ * Exit if other errors
+ */
+static struct cmd_line *get_cmd_line(int fd)
+{
+ struct cmd_line *cl = (CMD_LINE *)smb_xmalloc(sizeof(CMD_LINE));
+ int i = 0, rc;
+ unsigned char ch;
+
+ cl->len = INIT_ALLOC;
+
+ /*
+ * Allocate some space for the line. We extend later if needed.
+ */
+
+ cl->line = (char *)smb_xmalloc(INIT_ALLOC);
+
+ /*
+ * Now read in the chars to EOL. Don't store the EOL in the
+ * line. What about CR?
+ */
+
+ while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
+ if (ch == '\r') continue; /* skip CR */
+ if (i == cl->len) {
+ /*
+ * Allocate some more memory
+ */
+ if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
+ DEBUG(0, ("Unable to realloc space for line: %s\n",
+ strerror(errno)));
+ exit(1);
+ }
+ cl->len += INIT_ALLOC;
+ }
+ cl->line[i] = ch;
+ i++;
+ }
+
+ /* read 0 and we were at loc'n 0, return NULL */
+ if (rc == 0 && i == 0) {
+ free_cmd_line(cl);
+ return NULL;
+ }
+
+ cl->line_len = i;
+
+ return cl;
+
+}
+
+/*
+ * parse_value: parse out a value. We pull it apart as:
+ *
+ * <value> ::= <value-name>=<type>:<value-string>
+ *
+ * <value-name> ::= char-string-without-spaces | '"' char-string '"'
+ *
+ * If it parsed OK, return the <value-name> as a string, and the
+ * value type and value-string in parameters.
+ *
+ * The value name can be empty. There can only be one empty name in
+ * a list of values. A value of - removes the value entirely.
+ */
+
+static char *parse_name(char *nstr)
+{
+ int len = 0, start = 0;
+ if (!nstr) return NULL;
+
+ len = strlen(nstr);
+
+ while (len && nstr[len - 1] == ' ') len--;
+
+ nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
+
+ /*
+ * Beginning and end should be '"' or neither should be so
+ */
+ if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
+ (nstr[0] != '"' && nstr[len - 1] == '"'))
+ return NULL;
+
+ if (nstr[0] == '"') {
+ start = 1;
+ len -= 2;
+ }
+
+ return strndup(&nstr[start], len);
+}
+
+static int parse_value_type(char *tstr)
+{
+ int len = strlen(tstr);
+
+ while (len && tstr[len - 1] == ' ') len--;
+ tstr[len] = 0;
+
+ if (strcmp(tstr, "REG_DWORD") == 0)
+ return REG_DWORD;
+ else if (strcmp(tstr, "dword") == 0)
+ return REG_DWORD;
+ else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
+ return REG_EXPAND_SZ;
+ else if (strcmp(tstr, "REG_BIN") == 0)
+ return REG_BINARY;
+ else if (strcmp(tstr, "REG_SZ") == 0)
+ return REG_SZ;
+ else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
+ return REG_MULTI_SZ;
+ else if (strcmp(tstr, "-") == 0)
+ return REG_DELETE;
+
+ return 0;
+}
+
+static char *parse_val_str(char *vstr)
+{
+
+ return strndup(vstr, strlen(vstr));
+
+}
+
+static char *parse_value(struct cmd_line *cl, int *vtype, char **val)
+{
+ char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
+
+ if (!cl || !vtype || !val) return NULL;
+ if (!cl->line_len) return NULL;
+
+ p1 = strndup(cl->line, cl->line_len);
+ /* FIXME: Better return codes etc ... */
+ if (!p1) return NULL;
+ p2 = strchr(p1, '=');
+ if (!p2) return NULL;
+
+ *p2 = 0; p2++; /* Split into two strings at p2 */
+
+ /* Now, parse the name ... */
+
+ nstr = parse_name(p1);
+ if (!nstr) goto error;
+
+ /* Now, split the remainder and parse on type and val ... */
+
+ tstr = p2;
+ while (*tstr == ' ') tstr++; /* Skip leading white space */
+ p2 = strchr(p2, ':');
+
+ if (p2) {
+ *p2 = 0; p2++; /* split on the : */
+ }
+
+ *vtype = parse_value_type(tstr);
+
+ if (!vtype) goto error;
+
+ if (!p2 || !*p2) return nstr;
+
+ /* Now, parse the value string. It should return a newly malloc'd string */
+
+ while (*p2 == ' ') p2++; /* Skip leading space */
+ vstr = parse_val_str(p2);
+
+ if (!vstr) goto error;
+
+ *val = vstr;
+
+ return nstr;
+
+ error:
+ if (p1) free(p1);
+ if (nstr) free(nstr);
+ if (vstr) free(vstr);
+ return NULL;
+}
+
+/*
+ * Parse out a key. Look for a correctly formatted key [...]
+ * and whether it is a delete or add? A delete is signalled
+ * by a - in front of the key.
+ * Assumes that there are no leading and trailing spaces
+ */
+
+static char *parse_key(struct cmd_line *cl, int *cmd)
+{
+ int start = 1;
+ char *tmp;
+
+ if (cl->line[0] != '[' ||
+ cl->line[cl->line_len - 1] != ']') return NULL;
+ if (cl->line_len == 2) return NULL;
+ *cmd = CMD_ADD_KEY;
+ if (cl->line[1] == '-') {
+ if (cl->line_len == 3) return NULL;
+ start = 2;
+ *cmd = CMD_DEL_KEY;
+ }
+ tmp = smb_xmalloc(cl->line_len - 1 - start + 1);
+ strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
+ tmp[cl->line_len - 1 - start] = 0;
+ return tmp;
+}
+
+/*
+ * Parse a line to determine if we have a key or a value
+ * We only check for key or val ...
+ */
+
+static int parse_line(struct cmd_line *cl)
+{
+
+ if (!cl || cl->len == 0) return 0;
+
+ if (cl->line[0] == '[') /* No further checking for now */
+ return CMD_KEY;
+ else
+ return CMD_VAL;
+}
+
+/*
+ * We seek to offset 0, read in the required number of bytes,
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+static int regedit4_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[9];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno)));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 8) < 8) {
+ DEBUG(0, ("Unable to read command file format\n"));
+ exit(2); /* FIXME */
+ }
+
+ desc[8] = 0;
+
+ if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
+ if (cur_ofs) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ }
+ else {
+ skip_to_eol(fd);
+ }
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+/*
+ * Run though the data in the line and strip anything after a comment
+ * char.
+ */
+static void strip_comment(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] == ';') {
+ cl->line_len = i;
+ return;
+ }
+ }
+}
+
+/*
+ * trim leading space
+ */
+
+static void trim_leading_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] != ' '){
+ if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
+ return;
+ }
+ }
+}
+
+/*
+ * trim trailing spaces
+ */
+static void trim_trailing_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = cl->line_len; i == 0; i--) {
+ if (cl->line[i-1] != ' ' &&
+ cl->line[i-1] != '\t') {
+ cl->line_len = i;
+ }
+ }
+}
+
+/*
+ * Get a command ... This consists of possibly multiple lines:
+ * [key]
+ * values*
+ * possibly Empty line
+ *
+ * value ::= <value-name>=<value-type>':'<value-string>
+ * <value-name> is some path, possibly enclosed in quotes ...
+ * We alctually look for the next key to terminate a previous key
+ * if <value-type> == '-', then it is a delete type.
+ */
+static CMD *regedit4_get_cmd(int fd)
+{
+ struct command_s *cmd = NULL;
+ struct cmd_line *cl = NULL;
+ struct val_spec_list *vl = NULL;
+
+ cmd = (struct command_s *)smb_xmalloc(sizeof(struct command_s));
+
+ cmd->cmd = CMD_NONE;
+ cmd->key = NULL;
+ cmd->val_count = 0;
+ cmd->val_spec_list = cmd->val_spec_last = NULL;
+ while ((cl = get_cmd_line(fd))) {
+
+ /*
+ * If it is an empty command line, and we already have a key
+ * then exit from here ... FIXME: Clean up the parser
+ */
+
+ if (cl->line_len == 0 && cmd->key) {
+ free_cmd_line(cl);
+ break;
+ }
+
+ strip_comment(cl); /* remove anything beyond a comment char */
+ trim_trailing_spaces(cl);
+ trim_leading_spaces(cl);
+
+ if (cl->line_len == 0) { /* An empty line */
+ free_cmd_line(cl);
+ }
+ else { /* Else, non-empty ... */
+ /*
+ * Parse out the bits ...
+ */
+ switch (parse_line(cl)) {
+ case CMD_KEY:
+ if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
+ DEBUG(0, ("Error parsing key from line: "));
+ print_line(cl);
+ DEBUG(0, ("\n"));
+ }
+ break;
+
+ case CMD_VAL:
+ /*
+ * We need to add the value stuff to the list
+ * There could be a \ on the end which we need to
+ * handle at some time
+ */
+ vl = (struct val_spec_list *)smb_xmalloc(sizeof(struct val_spec_list));
+ vl->next = NULL;
+ vl->val = NULL;
+ vl->name = parse_value(cl, &vl->type, &vl->val);
+ if (!vl->name) goto error;
+ if (cmd->val_spec_list == NULL) {
+ cmd->val_spec_list = cmd->val_spec_last = vl;
+ }
+ else {
+ cmd->val_spec_last->next = vl;
+ cmd->val_spec_last = vl;
+ }
+ cmd->val_count++;
+ break;
+
+ default:
+ DEBUG(0, ("Unrecognized line in command file: \n"));
+ print_line(cl);
+ break;
+ }
+ }
+
+ }
+ if (!cmd->cmd) goto error; /* End of file ... */
+
+ return cmd;
+
+ error:
+ if (vl) free(vl);
+ if (cmd) free_cmd(cmd);
+ return NULL;
+}
+
+static int regedit4_exec_cmd(CMD *cmd)
+{
+
+ return 0;
+}
+
+static int editreg_1_0_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[11];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno)));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 10) < 10) {
+ DEBUG(0, ("Unable to read command file format\n"));
+ exit(2); /* FIXME */
+ }
+
+ desc[10] = 0;
+
+ if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+static CMD *editreg_1_0_get_cmd(int fd)
+{
+ return NULL;
+}
+
+static int editreg_1_0_exec_cmd(CMD *cmd)
+{
+
+ return -1;
+}
+
+typedef struct command_ops_s {
+ int type;
+ int (*file_type)(int fd);
+ CMD *(*get_cmd)(int fd);
+ int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+ {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+ {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
+ {-1, NULL, NULL, NULL}
+};
+
+typedef struct command_file_s {
+ char *name;
+ int type, fd;
+ CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+static CMD_FILE *cmd_file_create(char *file)
+{
+ CMD_FILE *tmp;
+ struct stat sbuf;
+ int i = 0;
+
+ /*
+ * Let's check if the file exists ...
+ * No use creating the cmd_file structure if the file does not exist
+ */
+
+ if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+ return NULL;
+ }
+
+ tmp = (CMD_FILE *)smb_xmalloc(sizeof(CMD_FILE));
+
+ /*
+ * Let's fill in some of the fields;
+ */
+
+ tmp->name = strdup(file);
+
+ if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+ free(tmp);
+ return NULL;
+ }
+
+ /*
+ * Now, try to find the format by indexing through the table
+ */
+ while (default_cmd_ops[i].type != -1) {
+ if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+ tmp->cmd_ops = default_cmd_ops[i];
+ return tmp;
+ }
+ i++;
+ }
+
+ /*
+ * If we got here, return NULL, as we could not figure out the type
+ * of command file.
+ *
+ * What about errors?
+ */
+
+ free(tmp);
+ return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that
+ */
+
+//FIXME
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+/*
+ * Sec Desc print functions
+ */
+
+char *str_type(unsigned char type);
+
+int nt_apply_reg_command_file(REG_HANDLE *regf, const char *cmd_file_name)
+{
+ CMD *cmd;
+ int modified = 0;
+ CMD_FILE *cmd_file = NULL;
+ cmd_file = cmd_file_create(cmd_file_name);
+
+ while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
+
+ /*
+ * Now, apply the requests to the tree ...
+ */
+ switch (cmd->cmd) {
+ case CMD_ADD_KEY: {
+ REG_KEY *tmp = NULL;
+ tmp = reg_open_key(reg_get_root(regf), cmd->key);
+ /* If we found it, apply the other bits, else create such a key */
+ if (!tmp) {
+ if(reg_key_add_name(reg_get_root(regf), cmd->key)) {
+ tmp = reg_open_key(reg_get_root(regf), cmd->key);
+ }
+ modified = 1;
+ }
+
+ while (cmd->val_count) {
+ VAL_SPEC_LIST *val = cmd->val_spec_list;
+ REG_VAL *reg_val = NULL;
+
+ if (val->type == REG_DELETE) {
+ reg_val = reg_key_get_value_by_name( tmp, val->name);
+ reg_val_del(reg_val);
+ modified = 1;
+ }
+ else {
+ /* FIXME
+ reg_val = nt_add_reg_value(tmp, val->name, val->type,
+ val->val); */
+ modified = 1;
+ }
+
+ cmd->val_spec_list = val->next;
+ free_val_spec_list(val);
+ cmd->val_count--;
+ }
+
+ break;
+ }
+
+ case CMD_DEL_KEY:
+ /*
+ * Any value does not matter ...
+ * Find the key if it exists, and delete it ...
+ */
+
+ reg_key_del_recursive(reg_open_key(reg_get_root(regf), cmd->key));
+ modified = 1;
+ break;
+ }
+ }
+ free_cmd(cmd);
+
+ return modified;
+}
+
+int main (int argc, char **argv)
+{
+ uint32 setparms, checkparms;
+ int opt;
+ poptContext pc;
+ REG_KEY *root;
+ const char *location;
+ const char *patch;
+ char *backend = "dir";
+ REG_HANDLE *h;
+ int fullpath = 0, no_values = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"backend", 'b', POPT_ARG_STRING, &backend, 'b', "backend to use", NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ setup_logging(argv[0], True);
+
+ location = poptGetArg(pc);
+ if(!location) {
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ h = reg_open(backend, location, True);
+ if(!h) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location, backend);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ patch = poptGetArg(pc);
+ if(!patch) patch = "/dev/stdin";
+
+ nt_apply_reg_command_file(h, patch);
+
+ return 0;
+}