5c9851b71b72cf3fb9319e1884084a0bbf4cd1c0
[bbaumbach/samba-autobuild/.git] / source4 / lib / registry / tools / regpatch.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple registry frontend
4    
5    Copyright (C) 2002, Richard Sharpe, rsharpe@richardsharpe.com
6    Copyright (C) 2004, Jelmer Vernooij, jelmer@samba.org
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "dynconfig.h"
25 #include "registry.h"
26 #include "lib/cmdline/popt_common.h"
27 #include "system/filesys.h"
28
29 /*
30  * Routines to parse a REGEDIT4 file
31  * 
32  * The file consists of:
33  * 
34  * REGEDIT4
35  * \[[-]key-path\]\n
36  * <value-spec>*
37  *
38  * Format:
39  * [cmd:]name=type:value
40  *
41  * cmd = a|d|c|add|delete|change|as|ds|cs
42  *
43  * There can be more than one key-path and value-spec.
44  *
45  * Since we want to support more than one type of file format, we
46  * construct a command-file structure that keeps info about the command file
47  */
48
49 #define FMT_UNREC -1
50 #define FMT_REGEDIT4 0
51 #define FMT_EDITREG1_1 1
52
53 #define FMT_STRING_REGEDIT4 "REGEDIT4"
54 #define FMT_STRING_EDITREG1_0 "EDITREG1.0"
55
56 #define CMD_NONE     0
57 #define CMD_ADD_KEY  1
58 #define CMD_DEL_KEY  2
59
60 #define CMD_KEY 1
61 #define CMD_VAL 2
62
63 typedef struct val_spec_list {
64   struct val_spec_list *next;
65   char *name;
66   int type;
67   char *val;    /* Kept as a char string, really? */
68 } VAL_SPEC_LIST;
69
70 typedef struct command_s {
71   int cmd;
72   char *key;
73   int val_count;
74   VAL_SPEC_LIST *val_spec_list, *val_spec_last;
75 } CMD;
76
77 typedef struct cmd_line {
78   int len, line_len;
79   char *line;
80 } CMD_LINE;
81
82 static void free_val_spec_list(VAL_SPEC_LIST *vl)
83 {
84   if (!vl) return;
85   if (vl->name) free(vl->name);
86   if (vl->val) free(vl->val);
87   free(vl);
88
89 }
90
91 /* 
92  * Some routines to handle lines of info in the command files
93  */
94 static void skip_to_eol(int fd)
95 {
96   int rc;
97   char ch = 0;
98
99   while ((rc = read(fd, &ch, 1)) == 1) {
100     if (ch == 0x0A) return;
101   }
102   if (rc < 0) {
103     DEBUG(0, ("Could not read file descriptor: %d, %s\n",
104             fd, strerror(errno)));
105     exit(1);
106   }
107 }
108
109 static void free_cmd(CMD *cmd)
110 {
111   if (!cmd) return;
112
113   while (cmd->val_spec_list) {
114     VAL_SPEC_LIST *tmp;
115
116     tmp = cmd->val_spec_list;
117     cmd->val_spec_list = tmp->next;
118     free(tmp);
119   }
120
121   free(cmd);
122
123 }
124
125 static void free_cmd_line(CMD_LINE *cmd_line)
126 {
127   if (cmd_line) {
128     if (cmd_line->line) free(cmd_line->line);
129     free(cmd_line);
130   }
131 }
132
133 static void print_line(struct cmd_line *cl)
134 {
135   char *pl;
136
137   if (!cl) return;
138
139   pl = smb_xmalloc(cl->line_len + 1);
140
141   strncpy(pl, cl->line, cl->line_len);
142   pl[cl->line_len] = 0;
143
144   fprintf(stdout, "%s\n", pl);
145   free(pl);
146 }
147
148 #define INIT_ALLOC 10 
149
150 /*
151  * Read a line from the input file.
152  * NULL returned when EOF and no chars read
153  * Otherwise we return a cmd_line *
154  * Exit if other errors
155  */
156 static struct cmd_line *get_cmd_line(int fd)
157 {
158   CMD_LINE *cl = smb_xmalloc_p(CMD_LINE);
159   int i = 0, rc;
160   uint8_t ch;
161
162   cl->len = INIT_ALLOC;
163
164   /*
165    * Allocate some space for the line. We extend later if needed.
166    */
167
168   cl->line = (char *)smb_xmalloc(INIT_ALLOC);
169
170   /*
171    * Now read in the chars to EOL. Don't store the EOL in the 
172    * line. What about CR?
173    */
174
175   while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
176     if (ch == '\r') continue; /* skip CR */
177     if (i == cl->len-1) {
178       /*
179        * Allocate some more memory
180        */
181       if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
182         DEBUG(0, ("Unable to realloc space for line: %s\n",
183                 strerror(errno)));
184         exit(1);
185       }
186       cl->len += INIT_ALLOC;
187     }
188     cl->line[i] = ch;
189     i++;
190   }
191
192   /* read 0 and we were at loc'n 0, return NULL */
193   if (rc == 0 && i == 0) {
194     free_cmd_line(cl);
195     return NULL;
196   }
197
198   cl->line[i] = '\0';
199   cl->line_len = i;
200
201   return cl;
202
203 }
204
205 /*
206  * parse_value: parse out a value. We pull it apart as:
207  *
208  * <value> ::= <value-name>=<type>:<value-string>
209  *
210  * <value-name> ::= char-string-without-spaces | '"' char-string '"'
211  *
212  * If it parsed OK, return the <value-name> as a string, and the
213  * value type and value-string in parameters.
214  *
215  * The value name can be empty. There can only be one empty name in 
216  * a list of values. A value of - removes the value entirely.  
217  */
218
219 static char *parse_name(char *nstr)
220 {
221   int len = 0, start = 0;
222   if (!nstr) return NULL;
223
224   len = strlen(nstr);
225
226   while (len && nstr[len - 1] == ' ') len--;
227
228   nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
229
230   /*
231    * Beginning and end should be '"' or neither should be so
232    */
233   if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
234       (nstr[0] != '"' && nstr[len - 1] == '"'))
235     return NULL;
236
237   if (nstr[0] == '"') {
238     start = 1;
239     len -= 2;
240   }
241
242   return strndup(&nstr[start], len);
243 }
244
245 static int parse_value_type(char *tstr)
246 {
247   int len = strlen(tstr);
248   
249   while (len && tstr[len - 1] == ' ') len--;
250   tstr[len] = 0;
251
252   if (strcmp(tstr, "REG_DWORD") == 0)
253     return REG_DWORD;
254   else if (strcmp(tstr, "dword") == 0)
255     return REG_DWORD;
256   else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
257     return REG_EXPAND_SZ;
258   else if (strcmp(tstr, "REG_BIN") == 0)
259     return REG_BINARY;
260   else if (strcmp(tstr, "REG_SZ") == 0)
261     return REG_SZ;
262   else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
263     return REG_MULTI_SZ;
264   else if (strcmp(tstr, "-") == 0)
265     return REG_DELETE;
266
267   return 0;
268 }
269
270 static char *parse_val_str(char *vstr)
271 {
272   
273   return strndup(vstr, strlen(vstr));
274
275 }
276
277 static char *parse_value(struct cmd_line *cl, int *vtype, char **val)
278 {
279   char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
280   
281   if (!cl || !vtype || !val) return NULL;
282   if (!cl->line[0]) return NULL;
283
284   p1 = strdup(cl->line);
285   /* FIXME: Better return codes etc ... */
286   if (!p1) return NULL;
287   p2 = strchr(p1, '=');
288   if (!p2) return NULL;
289
290   *p2 = 0; p2++; /* Split into two strings at p2 */
291
292   /* Now, parse the name ... */
293
294   nstr = parse_name(p1);
295   if (!nstr) goto error;
296
297   /* Now, split the remainder and parse on type and val ... */
298
299   tstr = p2;
300   while (*tstr == ' ') tstr++; /* Skip leading white space */
301   p2 = strchr(p2, ':');
302
303   if (p2) {
304     *p2 = 0; p2++; /* split on the : */
305   }
306
307   *vtype = parse_value_type(tstr);
308
309   if (!vtype) goto error;
310
311   if (!p2 || !*p2) return nstr;
312
313   /* Now, parse the value string. It should return a newly malloc'd string */
314   
315   while (*p2 == ' ') p2++; /* Skip leading space */
316   vstr = parse_val_str(p2);
317
318   if (!vstr) goto error;
319
320   *val = vstr;
321
322   return nstr;
323
324  error:
325   if (p1) free(p1);
326   if (nstr) free(nstr);
327   if (vstr) free(vstr);
328   return NULL;
329 }
330
331 /*
332  * Parse out a key. Look for a correctly formatted key [...] 
333  * and whether it is a delete or add? A delete is signalled 
334  * by a - in front of the key.
335  * Assumes that there are no leading and trailing spaces
336  */
337
338 static char *parse_key(struct cmd_line *cl, int *cmd)
339 {
340   int start = 1;
341   char *tmp;
342
343   if (cl->line[0] != '[' ||
344       cl->line[cl->line_len - 1] != ']') return NULL;
345   if (cl->line_len == 2) return NULL;
346   *cmd = CMD_ADD_KEY;
347   if (cl->line[1] == '-') {
348     if (cl->line_len == 3) return NULL;
349     start = 2;
350     *cmd = CMD_DEL_KEY;
351   }
352   tmp = smb_xmalloc(cl->line_len - 1 - start + 1);
353   strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
354   tmp[cl->line_len - 1 - start] = 0;
355   return tmp;
356 }
357
358 /*
359  * Parse a line to determine if we have a key or a value
360  * We only check for key or val ...
361  */
362
363 static int parse_line(struct cmd_line *cl)
364 {
365
366   if (!cl || cl->len == 0) return 0;
367
368   if (cl->line[0] == '[')  /* No further checking for now */
369     return CMD_KEY;
370   else 
371     return CMD_VAL;
372 }
373
374 /*
375  * We seek to offset 0, read in the required number of bytes, 
376  * and compare to the correct value.
377  * We then seek back to the original location
378  */
379 static int regedit4_file_type(int fd)
380 {
381   int cur_ofs = 0;
382   char desc[9];
383
384   cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
385   if (cur_ofs < 0) {
386     DEBUG(0, ("Unable to get current offset: (%d) %s\n", cur_ofs, strerror(errno)));
387     exit(1);  /* FIXME */
388   }
389
390   if (cur_ofs) {
391     lseek(fd, 0, SEEK_SET);
392   }
393
394   if (read(fd, desc, 8) < 8) {
395     DEBUG(0, ("Unable to read command file format\n")); 
396     exit(2);  /* FIXME */
397   }
398
399   desc[8] = 0;
400
401   if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
402     if (cur_ofs) {
403       lseek(fd, cur_ofs, SEEK_SET);
404     } else {
405       skip_to_eol(fd);
406     }
407     return FMT_REGEDIT4;
408   }
409
410   return FMT_UNREC;
411 }
412
413 /*
414  * Run though the data in the line and strip anything after a comment
415  * char.
416  */
417 static void strip_comment(struct cmd_line *cl)
418 {
419   int i;
420
421   if (!cl) return;
422
423   for (i = 0; i < cl->line_len; i++) {
424     if (cl->line[i] == ';') {
425                 cl->line[i] = '\0';
426       cl->line_len = i;
427       return;
428     }
429   }
430 }
431
432 /* 
433  * Get a command ... This consists of possibly multiple lines:
434  * [key]
435  * values*
436  * possibly Empty line
437  *
438  * value ::= <value-name>=<value-type>':'<value-string>
439  * <value-name> is some path, possibly enclosed in quotes ...
440  * We alctually look for the next key to terminate a previous key
441  * if <value-type> == '-', then it is a delete type.
442  */
443 static CMD *regedit4_get_cmd(int fd)
444 {
445   struct command_s *cmd = NULL;
446   struct cmd_line *cl = NULL;
447   struct val_spec_list *vl = NULL;
448
449   cmd = smb_xmalloc_p(struct command_s);
450
451   cmd->cmd = CMD_NONE;
452   cmd->key = NULL;
453   cmd->val_count = 0;
454   cmd->val_spec_list = cmd->val_spec_last = NULL;
455   while ((cl = get_cmd_line(fd))) {
456
457     /*
458      * If it is an empty command line, and we already have a key
459      * then exit from here ... FIXME: Clean up the parser
460      */
461
462     if (cl->line_len == 0 && cmd->key) {
463       free_cmd_line(cl);
464       break;
465     } 
466
467     strip_comment(cl);     /* remove anything beyond a comment char */
468         trim_string(cl->line, " \t", " \t");
469
470     if (!cl->line[0]) {    /* An empty line */
471       free_cmd_line(cl);
472     }
473     else {                 /* Else, non-empty ... */
474       /* 
475        * Parse out the bits ... 
476        */
477       switch (parse_line(cl)) {
478       case CMD_KEY:
479         if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
480           DEBUG(0, ("Error parsing key from line: "));
481           print_line(cl);
482           DEBUG(0, ("\n"));
483         }
484         break;
485
486       case CMD_VAL:
487         /*
488          * We need to add the value stuff to the list
489          * There could be a \ on the end which we need to 
490          * handle at some time
491          */
492         vl = smb_xmalloc_p(struct val_spec_list);
493         vl->next = NULL;
494         vl->val = NULL;
495         vl->name = parse_value(cl, &vl->type, &vl->val);
496         if (!vl->name) goto error;
497         if (cmd->val_spec_list == NULL) {
498           cmd->val_spec_list = cmd->val_spec_last = vl;
499         }
500         else {
501           cmd->val_spec_last->next = vl;
502           cmd->val_spec_last = vl;
503         }
504         cmd->val_count++;
505         break;
506
507       default:
508         DEBUG(0, ("Unrecognized line in command file: \n"));
509         print_line(cl);
510         break;
511       }
512     }
513
514   }
515   if (!cmd->cmd) goto error; /* End of file ... */
516
517   return cmd;
518
519  error:
520   if (vl) free(vl);
521   if (cmd) free_cmd(cmd);
522   return NULL;
523 }
524
525 static int regedit4_exec_cmd(CMD *cmd)
526 {
527
528   return 0;
529 }
530
531 static int editreg_1_0_file_type(int fd)
532 {
533   int cur_ofs = 0;
534   char desc[11];
535
536   cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
537   if (cur_ofs < 0) {
538     DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno)));
539     exit(1);  /* FIXME */
540   }
541
542   if (cur_ofs) {
543     lseek(fd, 0, SEEK_SET);
544   }
545
546   if (read(fd, desc, 10) < 10) {
547     DEBUG(0, ("Unable to read command file format\n")); 
548     exit(2);  /* FIXME */
549   }
550
551   desc[10] = 0;
552
553   if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
554     lseek(fd, cur_ofs, SEEK_SET);
555     return FMT_REGEDIT4;
556   }
557
558   return FMT_UNREC;
559 }
560
561 static CMD *editreg_1_0_get_cmd(int fd)
562 {
563   return NULL;
564 }
565
566 static int editreg_1_0_exec_cmd(CMD *cmd)
567 {
568
569   return -1;
570 }
571
572 typedef struct command_ops_s {
573   int type;
574   int (*file_type)(int fd);
575   CMD *(*get_cmd)(int fd);
576   int (*exec_cmd)(CMD *cmd);
577 } CMD_OPS;
578
579 static CMD_OPS default_cmd_ops[] = {
580   {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
581   {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
582   {-1,  NULL, NULL, NULL}
583 }; 
584
585 typedef struct command_file_s {
586   char *name;
587   int type, fd;
588   CMD_OPS cmd_ops;
589 } CMD_FILE;
590
591 /*
592  * Create a new command file structure
593  */
594
595 static CMD_FILE *cmd_file_create(const char *file)
596 {
597   CMD_FILE *tmp;
598   struct stat sbuf;
599   int i = 0;
600
601   /*
602    * Let's check if the file exists ...
603    * No use creating the cmd_file structure if the file does not exist
604    */
605
606   if (stat(file, &sbuf) < 0) { /* Not able to access file */
607         DEBUG(0,("Stat on %s failed\n", file));
608     return NULL;
609   }
610
611   tmp = smb_xmalloc_p(CMD_FILE); 
612
613   /*
614    * Let's fill in some of the fields;
615    */
616
617   tmp->name = strdup(file);
618
619   if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
620         DEBUG(0,("Error opening %s\n", file));
621     free(tmp);
622     return NULL;
623   }
624
625   /*
626    * Now, try to find the format by indexing through the table
627    */
628   while (default_cmd_ops[i].type != -1) {
629     if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
630       tmp->cmd_ops = default_cmd_ops[i];
631       return tmp;
632     }
633     i++;
634   }
635
636   /* 
637    * If we got here, return NULL, as we could not figure out the type
638    * of command file.
639    *
640    * What about errors? 
641    */
642
643   free(tmp);
644   DEBUG(0,("Unknown type\n"));
645   return NULL;
646 }
647
648 /*
649  * Extract commands from the command file, and execute them.
650  * We pass a table of command callbacks for that 
651  */
652
653 /* FIXME */
654
655 /*
656  * Main code from here on ...
657  */
658
659 /*
660  * key print function here ...
661  */
662
663 /*
664  * Sec Desc print functions 
665  */
666
667 char *str_type(uint8_t type);
668
669 static int nt_apply_reg_command_file(struct registry_context *r, const char *cmd_file_name)
670 {
671         CMD *cmd;
672         BOOL modified = False;
673         CMD_FILE *cmd_file = NULL;
674         TALLOC_CTX *mem_ctx = talloc_init("apply_cmd_file");
675         struct registry_key *tmp = NULL;
676         WERROR error;
677         cmd_file = cmd_file_create(cmd_file_name);
678
679         while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
680
681                 /*
682                  * Now, apply the requests to the tree ...
683                  */
684                 switch (cmd->cmd) {
685                 case CMD_ADD_KEY: 
686                   error = reg_open_key_abs(mem_ctx, r, cmd->key, &tmp);
687
688                   /* If we found it, apply the other bits, else create such a key */
689                   if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) {
690                           if(!W_ERROR_IS_OK(reg_key_add_abs(mem_ctx, r, cmd->key, 0, NULL, &tmp))) {
691                                         DEBUG(0, ("Error adding new key '%s'\n", cmd->key));
692                                         continue;
693                           }
694                           modified = True;
695                   }
696
697                   while (cmd->val_count) {
698                           VAL_SPEC_LIST *val = cmd->val_spec_list;
699
700                           if (val->type == REG_DELETE) {
701                                   error = reg_del_value(tmp, val->name);
702                                   if(!W_ERROR_IS_OK(error)) {
703                                         DEBUG(0, ("Error removing value '%s'\n", val->name));
704                                   }
705                                   modified = True;
706                           }
707                           else {
708                                   if(!W_ERROR_IS_OK(reg_val_set(tmp, val->name, val->type, val->val, strlen(val->val)))) {
709                                           DEBUG(0, ("Error adding new value '%s'\n", val->name));
710                                           continue;
711                                   }
712                                   modified = True;
713                           }
714
715                           cmd->val_spec_list = val->next;
716                           free_val_spec_list(val);
717                           cmd->val_count--;
718                   }
719
720                   break;
721
722                 case CMD_DEL_KEY:
723                   /* 
724                    * Any value does not matter ...
725                    * Find the key if it exists, and delete it ...
726                    */
727
728                   error = reg_key_del_abs(r, cmd->key); 
729                   if(!W_ERROR_IS_OK(error)) {
730                           DEBUG(0, ("Unable to delete key '%s'\n", cmd->key));
731                           continue;
732                   }
733                   modified = True;
734                   break;
735                 }
736         }
737         free_cmd(cmd);
738
739         return modified;
740 }
741
742  int main(int argc, char **argv)
743 {
744         int opt;
745         poptContext pc;
746         const char *patch;
747         struct registry_context *h;
748         const char *remote = NULL;
749         WERROR error;
750         struct poptOption long_options[] = {
751                 POPT_AUTOHELP
752                 POPT_COMMON_CREDENTIALS
753                 {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
754                 POPT_TABLEEND
755         };
756
757         regpatch_init_subsystems;
758
759         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
760                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
761         }
762
763
764         pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
765
766         while((opt = poptGetNextOpt(pc)) != -1) {
767         }
768
769         setup_logging(argv[0], DEBUG_STDOUT);
770
771         if (remote) {
772                 error = reg_open_remote (&h, cmdline_credentials, remote);
773         } else {
774                 error = reg_open_local (&h);
775         }
776
777         if (W_ERROR_IS_OK(error)) {
778                 fprintf(stderr, "Error: %s\n", win_errstr(error));
779                 return 1;
780         }
781                 
782         patch = poptGetArg(pc);
783         if(!patch) patch = "/dev/stdin";
784         poptFreeContext(pc);
785
786         nt_apply_reg_command_file(h, patch);
787
788         return 0;
789 }