ruby: Add unit test for new RLenValue type
[hivex.git] / xml / hivexml.c
1 /* hivexml - Convert Windows Registry "hive" to XML file.
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <locale.h>
30
31 #ifdef HAVE_LIBINTL_H
32 #include <libintl.h>
33 #endif
34
35 #include <getopt.h>
36
37 #include <libxml/xmlwriter.h>
38
39 #include "hivex.h"
40
41 #ifdef HAVE_GETTEXT
42 #include "gettext.h"
43 #define _(str) dgettext(PACKAGE, (str))
44 //#define N_(str) dgettext(PACKAGE, (str))
45 #else
46 #define _(str) str
47 //#define N_(str) str
48 #endif
49
50 static char *filetime_to_8601 (int64_t windows_ticks);
51
52 /* Callback functions. */
53 static int node_start (hive_h *, void *, hive_node_h, const char *name);
54 static int node_end (hive_h *, void *, hive_node_h, const char *name);
55 static int value_string (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
56 static int value_multiple_strings (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv);
57 static int value_string_invalid_utf16 (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
58 static int value_dword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t);
59 static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t);
60 static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
61 static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
62 static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
63
64 static struct hivex_visitor visitor = {
65   .node_start = node_start,
66   .node_end = node_end,
67   .value_string = value_string,
68   .value_multiple_strings = value_multiple_strings,
69   .value_string_invalid_utf16 = value_string_invalid_utf16,
70   .value_dword = value_dword,
71   .value_qword = value_qword,
72   .value_binary = value_binary,
73   .value_none = value_none,
74   .value_other = value_other
75 };
76
77 #define XML_CHECK(proc, args)                                           \
78   do {                                                                  \
79     if ((proc args) == -1) {                                            \
80       fprintf (stderr, _("%s: failed to write XML document\n"), #proc); \
81       exit (EXIT_FAILURE);                                              \
82     }                                                                   \
83   } while (0)
84
85 int
86 main (int argc, char *argv[])
87 {
88   setlocale (LC_ALL, "");
89 #ifdef HAVE_BINDTEXTDOMAIN
90   bindtextdomain (PACKAGE, LOCALEBASEDIR);
91   textdomain (PACKAGE);
92 #endif
93
94   int c;
95   int open_flags = 0;
96   int visit_flags = 0;
97
98   while ((c = getopt (argc, argv, "dk")) != EOF) {
99     switch (c) {
100     case 'd':
101       open_flags |= HIVEX_OPEN_DEBUG;
102       break;
103     case 'k':
104       visit_flags |= HIVEX_VISIT_SKIP_BAD;
105       break;
106     default:
107       fprintf (stderr, "hivexml [-dk] regfile > output.xml\n");
108       exit (EXIT_FAILURE);
109     }
110   }
111
112   if (optind + 1 != argc) {
113     fprintf (stderr, _("hivexml: missing name of input file\n"));
114     exit (EXIT_FAILURE);
115   }
116
117   hive_h *h = hivex_open (argv[optind], open_flags);
118   if (h == NULL) {
119     perror (argv[optind]);
120     exit (EXIT_FAILURE);
121   }
122
123   /* Note both this macro, and xmlTextWriterStartDocument leak memory.  There
124    * doesn't seem to be any way to recover that memory, but it's not a
125    * large amount.
126    */
127   LIBXML_TEST_VERSION;
128
129   xmlTextWriterPtr writer;
130   writer = xmlNewTextWriterFilename ("/dev/stdout", 0);
131   if (writer == NULL) {
132     fprintf (stderr, _("xmlNewTextWriterFilename: failed to create XML writer\n"));
133     exit (EXIT_FAILURE);
134   }
135
136   XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
137   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
138
139   int64_t hive_mtime = hivex_last_modified (h);
140   if (hive_mtime >= 0) {
141     char *timebuf = filetime_to_8601 (hive_mtime);
142     if (timebuf) {
143       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
144       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
145       XML_CHECK (xmlTextWriterEndElement, (writer));
146       free (timebuf);
147     }
148   }
149
150   if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
151     perror (argv[optind]);
152     exit (EXIT_FAILURE);
153   }
154
155   if (hivex_close (h) == -1) {
156     perror (argv[optind]);
157     exit (EXIT_FAILURE);
158   }
159
160   XML_CHECK (xmlTextWriterEndElement, (writer));
161   XML_CHECK (xmlTextWriterEndDocument, (writer));
162   xmlFreeTextWriter (writer);
163
164   exit (EXIT_SUCCESS);
165 }
166
167 /* Convert Windows filetime to ISO 8601 format.
168  * http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux/6161842#6161842
169  *
170  * Source for time_t->char* conversion: Fiwalk version 0.6.14's
171  * fiwalk.cpp.
172  *
173  * The caller should free the returned buffer.
174  *
175  * This function returns NULL on a 0 input.  In the context of
176  * hives, which only have mtimes, 0 will always be a complete
177  * absence of data.
178  */
179
180 #define WINDOWS_TICK 10000000LL
181 #define SEC_TO_UNIX_EPOCH 11644473600LL
182 #define TIMESTAMP_BUF_LEN 32
183
184 static char *
185 filetime_to_8601 (int64_t windows_ticks)
186 {
187   char *ret;
188   time_t t;
189   struct tm *tm;
190
191   if (windows_ticks == 0LL)
192     return NULL;
193
194   t = windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH;
195   tm = gmtime (&t);
196   if (tm == NULL)
197     return NULL;
198
199   ret = malloc (TIMESTAMP_BUF_LEN);
200   if (ret == NULL) {
201     perror ("malloc");
202     exit (EXIT_FAILURE);
203   }
204
205   if (strftime (ret, TIMESTAMP_BUF_LEN, "%FT%TZ", tm) == 0) {
206     perror ("strftime");
207     exit (EXIT_FAILURE);
208   }
209
210   return ret;
211 }
212
213 static int
214 node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
215 {
216   int64_t last_modified;
217   char *timebuf;
218
219   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
220   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
221   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
222
223   if (node == hivex_root (h)) {
224     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "root", BAD_CAST "1"));
225   }
226
227   last_modified = hivex_node_timestamp (h, node);
228   if (last_modified >= 0) {
229     timebuf = filetime_to_8601 (last_modified);
230     if (timebuf) {
231       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
232       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
233       XML_CHECK (xmlTextWriterEndElement, (writer));
234       free (timebuf);
235     }
236   }
237
238   return 0;
239 }
240
241 static int
242 node_end (hive_h *h, void *writer_v, hive_node_h node, const char *name)
243 {
244   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
245   XML_CHECK (xmlTextWriterEndElement, (writer));
246   return 0;
247 }
248
249 static void
250 start_value (xmlTextWriterPtr writer,
251              const char *key, const char *type, const char *encoding)
252 {
253   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "value"));
254   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "type", BAD_CAST type));
255   if (encoding)
256     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "encoding", BAD_CAST encoding));
257   if (*key)
258     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "key", BAD_CAST key));
259   else                          /* default key */
260     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "default", BAD_CAST "1"));
261 }
262
263 static void
264 end_value (xmlTextWriterPtr writer)
265 {
266   XML_CHECK (xmlTextWriterEndElement, (writer));
267 }
268
269 static int
270 value_string (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
271               hive_type t, size_t len, const char *key, const char *str)
272 {
273   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
274   const char *type;
275
276   switch (t) {
277   case hive_t_string: type = "string"; break;
278   case hive_t_expand_string: type = "expand"; break;
279   case hive_t_link: type = "link"; break;
280
281   case hive_t_none:
282   case hive_t_binary:
283   case hive_t_dword:
284   case hive_t_dword_be:
285   case hive_t_multiple_strings:
286   case hive_t_resource_list:
287   case hive_t_full_resource_description:
288   case hive_t_resource_requirements_list:
289   case hive_t_qword:
290     abort ();                   /* internal error - should not happen */
291
292   default:
293     type = "unknown";
294   }
295
296   start_value (writer, key, type, NULL);
297   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
298   XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST str));
299   XML_CHECK (xmlTextWriterEndAttribute, (writer));
300   end_value (writer);
301   return 0;
302 }
303
304 static int
305 value_multiple_strings (hive_h *h, void *writer_v, hive_node_h node,
306                         hive_value_h value, hive_type t, size_t len,
307                         const char *key, char **argv)
308 {
309   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
310   start_value (writer, key, "string-list", NULL);
311
312   size_t i;
313   for (i = 0; argv[i] != NULL; ++i) {
314     XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "string"));
315     XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST argv[i]));
316     XML_CHECK (xmlTextWriterEndElement, (writer));
317   }
318
319   end_value (writer);
320   return 0;
321 }
322
323 static int
324 value_string_invalid_utf16 (hive_h *h, void *writer_v, hive_node_h node,
325                             hive_value_h value, hive_type t, size_t len,
326                             const char *key,
327                             const char *str /* original data */)
328 {
329   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
330   const char *type;
331
332   switch (t) {
333   case hive_t_string: type = "bad-string"; break;
334   case hive_t_expand_string: type = "bad-expand"; break;
335   case hive_t_link: type = "bad-link"; break;
336   case hive_t_multiple_strings: type = "bad-string-list"; break;
337
338   case hive_t_none:
339   case hive_t_binary:
340   case hive_t_dword:
341   case hive_t_dword_be:
342   case hive_t_resource_list:
343   case hive_t_full_resource_description:
344   case hive_t_resource_requirements_list:
345   case hive_t_qword:
346     abort ();                   /* internal error - should not happen */
347
348   default:
349     type = "unknown";
350   }
351
352   start_value (writer, key, type, "base64");
353   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
354   XML_CHECK (xmlTextWriterWriteBase64, (writer, str, 0, len));
355   XML_CHECK (xmlTextWriterEndAttribute, (writer));
356   end_value (writer);
357
358   return 0;
359 }
360
361 static int
362 value_dword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
363              hive_type t, size_t len, const char *key, int32_t v)
364 {
365   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
366   start_value (writer, key, "int32", NULL);
367   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi32, v));
368   end_value (writer);
369   return 0;
370 }
371
372 static int
373 value_qword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
374              hive_type t, size_t len, const char *key, int64_t v)
375 {
376   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
377   start_value (writer, key, "int64", NULL);
378   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi64, v));
379   end_value (writer);
380   return 0;
381 }
382
383 static int
384 value_binary (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
385               hive_type t, size_t len, const char *key, const char *v)
386 {
387   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
388   start_value (writer, key, "binary", "base64");
389   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
390   XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
391   XML_CHECK (xmlTextWriterEndAttribute, (writer));
392   end_value (writer);
393   return 0;
394 }
395
396 static int
397 value_none (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
398             hive_type t, size_t len, const char *key, const char *v)
399 {
400   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
401   start_value (writer, key, "none", "base64");
402   if (len > 0) {
403     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
404     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
405     XML_CHECK (xmlTextWriterEndAttribute, (writer));
406   }
407   end_value (writer);
408   return 0;
409 }
410
411 static int
412 value_other (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
413              hive_type t, size_t len, const char *key, const char *v)
414 {
415   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
416   const char *type;
417
418   switch (t) {
419   case hive_t_none:
420   case hive_t_binary:
421   case hive_t_dword:
422   case hive_t_dword_be:
423   case hive_t_qword:
424   case hive_t_string:
425   case hive_t_expand_string:
426   case hive_t_link:
427   case hive_t_multiple_strings:
428     abort ();                   /* internal error - should not happen */
429
430   case hive_t_resource_list: type = "resource-list"; break;
431   case hive_t_full_resource_description: type = "resource-description"; break;
432   case hive_t_resource_requirements_list: type = "resource-requirements"; break;
433
434   default:
435     type = "unknown";
436   }
437
438   start_value (writer, key, type, "base64");
439   if (len > 0) {
440     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
441     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
442     XML_CHECK (xmlTextWriterEndAttribute, (writer));
443   }
444   end_value (writer);
445
446   return 0;
447 }