12847eedd83f3628cd3b8c21fe155e3ca093bc2e
[ira/wip.git] / source / lib / registry / patchfile.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Reading .REG files
4    
5    Copyright (C) Jelmer Vernooij 2004
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/registry/registry.h"
23 #include "system/filesys.h"
24
25 /**
26  * @file
27  * @brief Registry patch files
28  */
29
30 #define DEFAULT_IDENT_STRING "SAMBA4 REGISTRY"
31
32 static struct reg_diff_key *diff_find_add_key(struct reg_diff *diff, const char *path)
33 {
34         int i;
35         
36         for (i = 0; diff->numkeys; i++) {
37                 if (!strcasecmp(diff->keys[i].name, path))
38                         return &diff->keys[i];
39         }
40
41         diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2);
42         diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, path);
43         diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY;
44         diff->keys[diff->numkeys].numvalues = 0;
45         diff->keys[diff->numkeys].values = NULL;
46
47         diff->numkeys++;
48         return NULL;
49 }
50
51 /*
52  * Generate difference between two keys
53  */
54 static WERROR reg_generate_diff_key(struct reg_diff *diff, struct registry_key *oldkey, struct registry_key *newkey)
55 {
56         int i;
57         struct registry_key *t1, *t2;
58         struct registry_value *v1, *v2;
59         WERROR error1, error2;
60         TALLOC_CTX *mem_ctx = talloc_init("writediff");
61
62         /* Subkeys that were deleted */
63         for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i, &t1)); i++) {
64                 error2 = reg_key_get_subkey_by_name(mem_ctx, newkey, t1->name, &t2);
65
66                 if (W_ERROR_IS_OK(error2))
67                         continue;
68
69                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
70                         DEBUG(0, ("Error occured while getting subkey by name: %d\n", W_ERROR_V(error2)));
71                         return error2;
72                 }
73
74                 /* newkey didn't have such a subkey, add del diff */
75                 diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2);
76                 diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, t1->path);
77                 diff->keys[diff->numkeys].changetype = REG_DIFF_DEL_KEY;
78                 diff->numkeys++;
79         }
80
81         if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) {
82                 DEBUG(0, ("Error occured while getting subkey by index: %d\n", W_ERROR_V(error1)));
83                 talloc_free(mem_ctx);
84                 return error1;
85         }
86
87         /* Subkeys that were added */
88         for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i, &t1)); i++) {
89                 error2 = reg_key_get_subkey_by_name(mem_ctx, oldkey, t1->name, &t2);
90                         
91                 if (W_ERROR_IS_OK(error2))
92                         continue;
93                 
94                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
95                         DEBUG(0, ("Error occured while getting subkey by name: %d\n", W_ERROR_V(error2)));
96                         return error2;
97                 }
98
99                 /* oldkey didn't have such a subkey, add add diff */
100                 diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2);
101                 diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, t1->path);
102                 diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY;
103                 diff->keys[diff->numkeys].numvalues = 0;
104                 diff->keys[diff->numkeys].values = NULL;
105                 diff->numkeys++;
106
107                 reg_generate_diff_key(diff, t1, t2);
108         }
109
110         if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) {
111                 DEBUG(0, ("Error occured while getting subkey by index: %d\n", W_ERROR_V(error1)));
112                 talloc_free(mem_ctx);
113                 return error1;
114         }
115
116         /* Values that were changed */
117         for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_value_by_index(mem_ctx, newkey, i, &v1)); i++) {
118                 struct reg_diff_key *thiskey = NULL;
119                 error2 = reg_key_get_value_by_name(mem_ctx, oldkey, v1->name, &v2);
120         
121                 if(!W_ERROR_IS_OK(error2) && 
122                    !W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
123                         DEBUG(0, ("Error occured while getting value by name: %d\n", W_ERROR_V(error2)));
124                         return error2;
125                 }
126
127                 if (W_ERROR_IS_OK(error2) && data_blob_cmp(&v1->data, &v2->data) == 0)
128                         continue;
129
130                 thiskey = diff_find_add_key(diff, oldkey->path);
131                 thiskey->values = talloc_realloc(diff, thiskey->values, struct reg_diff_value, thiskey->numvalues+2);
132                 thiskey->values[thiskey->numvalues].name = talloc_strdup(thiskey->values, v1->name);
133                 thiskey->values[thiskey->numvalues].type = v2->data_type;
134                 thiskey->values[thiskey->numvalues].changetype = REG_DIFF_SET_VAL;
135                 thiskey->values[thiskey->numvalues].data = data_blob_dup_talloc(thiskey->values, &v2->data);
136                 thiskey->numvalues++;
137         }
138
139         if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) {
140                 DEBUG(0, ("Error occured while getting value by index: %d\n", W_ERROR_V(error1)));
141                 talloc_free(mem_ctx);
142                 return error1;
143         }
144
145         /* Values that were deleted */
146         for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &v1)); i++) {
147                 struct reg_diff_key *thiskey = NULL;
148                 error2 = reg_key_get_value_by_name(mem_ctx, newkey, v1->name, &v2);
149
150                 if (W_ERROR_IS_OK(error2))
151                         continue;
152
153                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
154                         DEBUG(0, ("Error occured while getting value by name: %d\n", W_ERROR_V(error2)));
155                         return error2;
156                 }
157
158                 thiskey = diff_find_add_key(diff, oldkey->path);
159                 thiskey->values = talloc_realloc(diff, thiskey->values, struct reg_diff_value, thiskey->numvalues+2);
160                 thiskey->values[thiskey->numvalues].name = talloc_strdup(thiskey->values, v1->name);
161                 thiskey->values[thiskey->numvalues].changetype = REG_DIFF_DEL_VAL;
162                 thiskey->numvalues++;
163         }
164
165         if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) {
166                 DEBUG(0, ("Error occured while getting value by index: %d\n", W_ERROR_V(error1)));
167                 talloc_free(mem_ctx);
168                 return error1;
169         }
170
171         talloc_free(mem_ctx);
172         return WERR_OK;
173 }
174
175 /**
176  * Generate diff between two registry contexts 
177  */
178 _PUBLIC_ struct reg_diff *reg_generate_diff(TALLOC_CTX *mem_ctx, struct registry_context *ctx1, struct registry_context *ctx2)
179 {
180         struct reg_diff *diff = talloc_zero(mem_ctx, struct reg_diff);
181         int i;
182         WERROR error;
183
184         for(i = HKEY_CLASSES_ROOT; i <= HKEY_PERFORMANCE_NLSTEXT; i++) {
185                 struct registry_key *r1, *r2;
186                 error = reg_get_predefined_key(ctx1, i, &r1);
187                 if (!W_ERROR_IS_OK(error)) {
188                         DEBUG(0, ("Unable to open hive %s for backend 1\n", reg_get_predef_name(i)));
189                         continue;
190                 }
191                 
192                 error = reg_get_predefined_key(ctx2, i, &r2);
193                 if (!W_ERROR_IS_OK(error)) {
194                         DEBUG(0, ("Unable to open hive %s for backend 2\n", reg_get_predef_name(i)));
195                         continue;
196                 }
197
198                 reg_generate_diff_key(diff, r1, r2);
199         }
200
201         return diff;
202 }
203
204 /**
205  * Save registry diff
206  */
207 _PUBLIC_ WERROR reg_diff_save(const struct reg_diff *diff, const char *filename)
208 {
209         int xf, i, j;
210
211         if (filename) {
212                 xf = open(filename, O_CREAT, 0755);
213                 if (xf == -1) {
214                         DEBUG(0, ("Unable to open %s\n", filename));
215                         return WERR_BADFILE;
216                 }
217         } else 
218                 xf = STDIN_FILENO;
219
220         fdprintf(xf, "%s\n\n", diff->format?diff->format:DEFAULT_IDENT_STRING);
221
222         for (i = 0; i < diff->numkeys; i++) {
223                 if (diff->keys[i].changetype == REG_DIFF_DEL_KEY) {
224                         fdprintf(xf, "-%s\n\n",  diff->keys[i].name);
225                         continue;
226                 }
227
228                 fdprintf(xf, "[%s]\n", diff->keys[i].name);
229
230                 for (j = 0; j < diff->keys[i].numvalues; j++) {
231                         fdprintf(xf, "\"%s\"=", diff->keys[i].values[j].name);
232                         switch (diff->keys[i].values[j].changetype) {
233                                 case REG_DIFF_DEL_VAL:
234                                         fdprintf(xf, "-\n");
235                                         break;
236                                 case REG_DIFF_SET_VAL:
237                                         fdprintf(xf, "%s:%s\n", 
238                                                          str_regtype(diff->keys[i].values[j].type), 
239                                                          reg_val_data_string(NULL, 
240                                                                 diff->keys[i].values[j].type, 
241                                                                 &diff->keys[i].values[j].data));
242                                         break;
243                         }
244                 }
245
246                 fdprintf(xf, "\n");
247         }
248
249         close(xf);
250
251         return WERR_OK;
252 }
253
254 /**
255  * Load diff file
256  */
257 _PUBLIC_ struct reg_diff *reg_diff_load(TALLOC_CTX *ctx, const char *fn)
258 {
259         struct reg_diff *diff;
260         int fd;
261         char *line, *p, *q;
262         struct reg_diff_key *curkey = NULL;
263         struct reg_diff_value *curval;
264
265         fd = open(fn, O_RDONLY, 0);
266         if (fd == -1) {
267                 DEBUG(0, ("Error opening registry patch file `%s'\n", fn));
268                 return NULL;
269         }
270
271         diff = talloc_zero(ctx, struct reg_diff);
272         if (diff == NULL) {
273                 close(fd);
274                 return NULL;
275         }
276         
277         diff->format = afdgets(fd, diff, 0);
278         if (!diff->format) {
279                 talloc_free(diff);
280                 close(fd);
281                 return NULL;
282         }
283
284         while ((line = afdgets(fd, diff, 0))) {
285                 /* Ignore comments and empty lines */
286                 if (strlen(line) == 0 || line[0] == ';') {
287                         curkey = NULL;
288                         talloc_free(line);
289                         continue;
290                 }
291
292                 /* Start of key */
293                 if (line[0] == '[') {
294                         p = strchr_m(line, ']');
295                         if (p[strlen(p)-2] != ']') {
296                                 DEBUG(0, ("Malformed line\n"));
297                                 return NULL;
298                         }
299                         diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2);
300                         diff->keys[diff->numkeys].name = talloc_strndup(diff->keys, line+1, strlen(line)-2);
301                         diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY;
302                         diff->keys[diff->numkeys].numvalues = 0;
303                         diff->keys[diff->numkeys].values = NULL;
304                         curkey = &diff->keys[diff->numkeys];
305                         diff->numkeys++;
306                         talloc_free(line);
307                         continue;
308                 }
309
310                 /* Deleting key */
311                 if (line[0] == '-') {
312                         diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2);
313                         diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, line+1);
314                         diff->keys[diff->numkeys].changetype = REG_DIFF_DEL_KEY;
315                         diff->numkeys++;
316                         talloc_free(line);
317                         continue;
318                 }
319
320                 /* Deleting/Changing value */
321                 p = strchr_m(line, '=');
322                 if (p == NULL) {
323                         DEBUG(0, ("Malformed line\n"));
324                         talloc_free(line);
325                         continue;
326                 }
327
328                 *p = '\0'; p++;
329
330                 if (curkey == NULL) {
331                         DEBUG(0, ("Value change without key\n"));
332                         talloc_free(line);
333                         continue;
334                 }
335
336                 curkey->values = talloc_realloc(diff->keys, curkey->values, struct reg_diff_value, curkey->numvalues+2);
337                 curval = &curkey->values[curkey->numvalues];
338                 curkey->numvalues++;
339                 curval->name = talloc_strdup(curkey->values, line);
340
341                 /* Delete value */
342                 if (strcmp(p, "-")) {
343                         curval->changetype = REG_DIFF_DEL_VAL;
344                         talloc_free(line);
345                         continue;
346                 }
347                 
348                 q = strchr_m(p, ':');
349                 if (q) {
350                         *q = '\0'; 
351                         q++;
352                 }
353
354                 curval->changetype = REG_DIFF_SET_VAL;
355                 reg_string_to_val(curkey->values, q?p:"REG_SZ", q?q:p, &curval->type, &curval->data);
356
357                 talloc_free(line);
358         }
359
360         close(fd);
361
362         return diff;
363 }
364
365 /**
366  * Apply diff to a registry context 
367  */
368 _PUBLIC_ BOOL reg_diff_apply (const struct reg_diff *diff, struct registry_context *ctx)
369 {
370         TALLOC_CTX *mem_ctx = talloc_init("apply_cmd_file");
371         struct registry_key *tmp = NULL;
372         WERROR error;
373         int i, j;
374
375         for (i = 0; i < diff->numkeys; i++) {
376                 if (diff->keys[i].changetype == REG_DIFF_DEL_KEY) {
377                         error = reg_key_del_abs(ctx, diff->keys[i].name);
378
379                         if(!W_ERROR_IS_OK(error)) {
380                                 DEBUG(0, ("Unable to delete key '%s'\n", diff->keys[i].name));
381                                 return False;
382                         }
383
384                         continue;
385                 }
386
387                 /* Add / change key */
388                 error = reg_open_key_abs(mem_ctx, ctx, diff->keys[i].name, &tmp);
389
390                 /* If we found it, apply the other bits, else create such a key */
391                 if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) {
392                         if(!W_ERROR_IS_OK(reg_key_add_abs(mem_ctx, ctx, diff->keys[i].name, 0, NULL, &tmp))) {
393                                 DEBUG(0, ("Error adding new key '%s'\n", diff->keys[i].name));
394                                 return False;
395                         }
396                 }
397
398                 for (j = 0; j < diff->keys[i].numvalues; j++) {
399                         if (diff->keys[i].values[j].changetype == REG_DIFF_DEL_VAL) {
400                                 error = reg_del_value(tmp, diff->keys[i].values[j].name);
401                                 if (!W_ERROR_IS_OK(error)) {
402                                         DEBUG(0, ("Error deleting value '%s'\n", diff->keys[i].values[j].name));
403                                         return False;
404                                 }
405                         
406                                 error = reg_val_set(tmp, diff->keys[i].values[j].name, 
407                                                          diff->keys[i].values[j].type,
408                                                          diff->keys[i].values[j].data);
409                                 if (!W_ERROR_IS_OK(error)) {
410                                         DEBUG(0, ("Error setting value '%s'\n", diff->keys[i].values[j].name));
411                                         return False;
412                                 }       
413                         }
414                 }
415         }
416
417         return True;
418 }