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