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