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