1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-18 00:23:50 +03:00
Files
samba-mirror/source/lib/registry/tools/regpatch.c
2007-10-10 12:51:44 -05:00

808 lines
17 KiB
C

/*
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-1) {
/*
* 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[i] = '\0';
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[0]) return NULL;
p1 = strdup(cl->line);
/* 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: (%d) %s\n", cur_ofs, 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[i] = '\0';
cl->line_len = i;
return;
}
}
}
/*
* 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_string(cl->line, " \t", " \t");
if (!cl->line[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(const 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 */
DEBUG(0,("Stat on %s failed\n", 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) {
DEBUG(0,("Error opening %s\n", file));
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);
DEBUG(0,("Unknown type\n"));
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_KEY *root, const char *cmd_file_name)
{
CMD *cmd;
BOOL modified = False;
CMD_FILE *cmd_file = NULL;
REG_KEY *tmp = NULL;
WERROR error;
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:
error = reg_open_key(root, cmd->key, &tmp);
/* If we found it, apply the other bits, else create such a key */
if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) {
if(W_ERROR_IS_OK(reg_key_add_name_recursive(root, cmd->key))) {
error = reg_open_key(root, cmd->key, &tmp);
if(!W_ERROR_IS_OK(error)) {
DEBUG(0, ("Error finding new key '%s' after it has been added\n", cmd->key));
continue;
}
} else {
DEBUG(0, ("Error adding new key '%s'\n", cmd->key));
continue;
}
modified = True;
}
while (cmd->val_count) {
VAL_SPEC_LIST *val = cmd->val_spec_list;
REG_VAL *reg_val = NULL;
if (val->type == REG_DELETE) {
error = reg_key_get_value_by_name( tmp, val->name, &reg_val);
if(W_ERROR_IS_OK(error)) {
error = reg_val_del(reg_val);
}
if(!W_ERROR_IS_OK(error)) {
DEBUG(0, ("Error removing value '%s'\n", val->name));
}
modified = True;
}
else {
if(!W_ERROR_IS_OK(reg_key_add_value(tmp, val->name, val->type, val->val, strlen(val->val)))) {
DEBUG(0, ("Error adding new value '%s'\n", val->name));
continue;
}
modified = True;
}
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 ...
*/
error = reg_open_key(root, cmd->key, &tmp);
if(!W_ERROR_IS_OK(error)) {
DEBUG(0, ("Unable to open key '%s'\n", cmd->key));
continue;
}
error = reg_key_del_recursive(tmp);
if(!W_ERROR_IS_OK(error)) {
DEBUG(0, ("Unable to delete key '%s'\n", cmd->key));
continue;
}
modified = True;
break;
}
}
free_cmd(cmd);
return modified;
}
int main(int argc, char **argv)
{
int opt;
poptContext pc;
REG_KEY *root;
const char *location;
const char *credentials = NULL;
const char *patch;
const char *backend = "dir";
REG_HANDLE *h;
WERROR error;
struct poptOption long_options[] = {
POPT_AUTOHELP
{"backend", 'b', POPT_ARG_STRING, &backend, 'b', "backend to use", NULL},
{"credentials", 'c', POPT_ARG_STRING, &credentials, 'c', "credentials (user%password", 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;
}
error = reg_open(backend, location, credentials, &h);
if(!h) {
fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location, backend);
return 1;
}
patch = poptGetArg(pc);
if(!patch) patch = "/dev/stdin";
poptFreeContext(pc);
error = reg_get_root(h, &root);
if(!W_ERROR_IS_OK(error)) {
DEBUG(0, ("Error opening root!\n"));
return 1;
}
nt_apply_reg_command_file(root, patch);
reg_free(h);
return 0;
}