Prevent warning about unused variable in test.
[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
30 #include <libxml/xmlwriter.h>
31
32 #include "hivex.h"
33
34 #ifdef HAVE_GETTEXT
35 #include "gettext.h"
36 #define _(str) dgettext(PACKAGE, (str))
37 //#define N_(str) dgettext(PACKAGE, (str))
38 #else
39 #define _(str) str
40 //#define N_(str) str
41 #endif
42
43 static char *filetime_to_8601 (int64_t windows_ticks);
44
45 /* Callback functions. */
46 static int node_start (hive_h *, void *, hive_node_h, const char *name);
47 static int node_end (hive_h *, void *, hive_node_h, const char *name);
48 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);
49 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);
50 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);
51 static int value_dword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t);
52 static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t);
53 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);
54 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);
55 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);
56
57 static struct hivex_visitor visitor = {
58   .node_start = node_start,
59   .node_end = node_end,
60   .value_string = value_string,
61   .value_multiple_strings = value_multiple_strings,
62   .value_string_invalid_utf16 = value_string_invalid_utf16,
63   .value_dword = value_dword,
64   .value_qword = value_qword,
65   .value_binary = value_binary,
66   .value_none = value_none,
67   .value_other = value_other
68 };
69
70 #define XML_CHECK(proc, args)                                           \
71   do {                                                                  \
72     if ((proc args) == -1) {                                            \
73       fprintf (stderr, _("%s: failed to write XML document\n"), #proc); \
74       exit (EXIT_FAILURE);                                              \
75     }                                                                   \
76   } while (0)
77
78 int
79 main (int argc, char *argv[])
80 {
81   setlocale (LC_ALL, "");
82   bindtextdomain (PACKAGE, LOCALEBASEDIR);
83   textdomain (PACKAGE);
84
85   int c;
86   int open_flags = 0;
87   int visit_flags = 0;
88
89   while ((c = getopt (argc, argv, "dk")) != EOF) {
90     switch (c) {
91     case 'd':
92       open_flags |= HIVEX_OPEN_DEBUG;
93       break;
94     case 'k':
95       visit_flags |= HIVEX_VISIT_SKIP_BAD;
96       break;
97     default:
98       fprintf (stderr, "hivexml [-dk] regfile > output.xml\n");
99       exit (EXIT_FAILURE);
100     }
101   }
102
103   if (optind + 1 != argc) {
104     fprintf (stderr, _("hivexml: missing name of input file\n"));
105     exit (EXIT_FAILURE);
106   }
107
108   hive_h *h = hivex_open (argv[optind], open_flags);
109   if (h == NULL) {
110     perror (argv[optind]);
111     exit (EXIT_FAILURE);
112   }
113
114   /* Note both this macro, and xmlTextWriterStartDocument leak memory.  There
115    * doesn't seem to be any way to recover that memory, but it's not a
116    * large amount.
117    */
118   LIBXML_TEST_VERSION;
119
120   xmlTextWriterPtr writer;
121   writer = xmlNewTextWriterFilename ("/dev/stdout", 0);
122   if (writer == NULL) {
123     fprintf (stderr, _("xmlNewTextWriterFilename: failed to create XML writer\n"));
124     exit (EXIT_FAILURE);
125   }
126
127   XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
128   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
129
130   int64_t hive_mtime = hivex_last_modified (h);
131   if (hive_mtime >= 0) {
132     char *timebuf = filetime_to_8601 (hive_mtime);
133     if (timebuf) {
134       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
135       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
136       XML_CHECK (xmlTextWriterEndElement, (writer));
137       free (timebuf);
138     }
139   }
140
141   if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
142     perror (argv[optind]);
143     exit (EXIT_FAILURE);
144   }
145
146   if (hivex_close (h) == -1) {
147     perror (argv[optind]);
148     exit (EXIT_FAILURE);
149   }
150
151   XML_CHECK (xmlTextWriterEndElement, (writer));
152   XML_CHECK (xmlTextWriterEndDocument, (writer));
153   xmlFreeTextWriter (writer);
154
155   exit (EXIT_SUCCESS);
156 }
157
158 /* Convert Windows filetime to ISO 8601 format.
159  * http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux/6161842#6161842
160  *
161  * Source for time_t->char* conversion: Fiwalk version 0.6.14's
162  * fiwalk.cpp.
163  *
164  * The caller should free the returned buffer.
165  */
166
167 #define WINDOWS_TICK 10000000LL
168 #define SEC_TO_UNIX_EPOCH 11644473600LL
169 #define TIMESTAMP_BUF_LEN 32
170
171 static char *
172 filetime_to_8601 (int64_t windows_ticks)
173 {
174   char *ret;
175   time_t t;
176   struct tm *tm;
177
178   t = windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH;
179   tm = gmtime (&t);
180   if (tm == NULL)
181     return NULL;
182
183   ret = malloc (TIMESTAMP_BUF_LEN);
184   if (ret == NULL) {
185     perror ("malloc");
186     exit (EXIT_FAILURE);
187   }
188
189   if (strftime (ret, TIMESTAMP_BUF_LEN, "%FT%TZ", tm) == 0) {
190     perror ("strftime");
191     exit (EXIT_FAILURE);
192   }
193
194   return ret;
195 }
196
197 static int
198 node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
199 {
200   int64_t last_modified;
201   char *timebuf;
202
203   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
204   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
205   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
206
207   last_modified = hivex_node_timestamp (h, node);
208   if (last_modified >= 0) {
209     timebuf = filetime_to_8601 (last_modified);
210     if (timebuf) {
211       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
212       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
213       XML_CHECK (xmlTextWriterEndElement, (writer));
214       free (timebuf);
215     }
216   }
217
218   return 0;
219 }
220
221 static int
222 node_end (hive_h *h, void *writer_v, hive_node_h node, const char *name)
223 {
224   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
225   XML_CHECK (xmlTextWriterEndElement, (writer));
226   return 0;
227 }
228
229 static void
230 start_value (xmlTextWriterPtr writer,
231              const char *key, const char *type, const char *encoding)
232 {
233   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "value"));
234   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "type", BAD_CAST type));
235   if (encoding)
236     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "encoding", BAD_CAST encoding));
237   if (*key)
238     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "key", BAD_CAST key));
239   else                          /* default key */
240     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "default", BAD_CAST "1"));
241 }
242
243 static void
244 end_value (xmlTextWriterPtr writer)
245 {
246   XML_CHECK (xmlTextWriterEndElement, (writer));
247 }
248
249 static int
250 value_string (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
251               hive_type t, size_t len, const char *key, const char *str)
252 {
253   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
254   const char *type;
255
256   switch (t) {
257   case hive_t_string: type = "string"; break;
258   case hive_t_expand_string: type = "expand"; break;
259   case hive_t_link: type = "link"; break;
260
261   case hive_t_none:
262   case hive_t_binary:
263   case hive_t_dword:
264   case hive_t_dword_be:
265   case hive_t_multiple_strings:
266   case hive_t_resource_list:
267   case hive_t_full_resource_description:
268   case hive_t_resource_requirements_list:
269   case hive_t_qword:
270     abort ();                   /* internal error - should not happen */
271
272   default:
273     type = "unknown";
274   }
275
276   start_value (writer, key, type, NULL);
277   XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST str));
278   end_value (writer);
279   return 0;
280 }
281
282 static int
283 value_multiple_strings (hive_h *h, void *writer_v, hive_node_h node,
284                         hive_value_h value, hive_type t, size_t len,
285                         const char *key, char **argv)
286 {
287   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
288   start_value (writer, key, "string-list", NULL);
289
290   size_t i;
291   for (i = 0; argv[i] != NULL; ++i) {
292     XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "string"));
293     XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST argv[i]));
294     XML_CHECK (xmlTextWriterEndElement, (writer));
295   }
296
297   end_value (writer);
298   return 0;
299 }
300
301 static int
302 value_string_invalid_utf16 (hive_h *h, void *writer_v, hive_node_h node,
303                             hive_value_h value, hive_type t, size_t len,
304                             const char *key,
305                             const char *str /* original data */)
306 {
307   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
308   const char *type;
309
310   switch (t) {
311   case hive_t_string: type = "bad-string"; break;
312   case hive_t_expand_string: type = "bad-expand"; break;
313   case hive_t_link: type = "bad-link"; break;
314   case hive_t_multiple_strings: type = "bad-string-list"; break;
315
316   case hive_t_none:
317   case hive_t_binary:
318   case hive_t_dword:
319   case hive_t_dword_be:
320   case hive_t_resource_list:
321   case hive_t_full_resource_description:
322   case hive_t_resource_requirements_list:
323   case hive_t_qword:
324     abort ();                   /* internal error - should not happen */
325
326   default:
327     type = "unknown";
328   }
329
330   start_value (writer, key, type, "base64");
331   XML_CHECK (xmlTextWriterWriteBase64, (writer, str, 0, len));
332   end_value (writer);
333
334   return 0;
335 }
336
337 static int
338 value_dword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
339              hive_type t, size_t len, const char *key, int32_t v)
340 {
341   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
342   start_value (writer, key, "int32", NULL);
343   XML_CHECK (xmlTextWriterWriteFormatString, (writer, "%" PRIi32, v));
344   end_value (writer);
345   return 0;
346 }
347
348 static int
349 value_qword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
350              hive_type t, size_t len, const char *key, int64_t v)
351 {
352   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
353   start_value (writer, key, "int64", NULL);
354   XML_CHECK (xmlTextWriterWriteFormatString, (writer, "%" PRIi64, v));
355   end_value (writer);
356   return 0;
357 }
358
359 static int
360 value_binary (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
361               hive_type t, size_t len, const char *key, const char *v)
362 {
363   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
364   start_value (writer, key, "binary", "base64");
365   XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
366   end_value (writer);
367   return 0;
368 }
369
370 static int
371 value_none (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
372             hive_type t, size_t len, const char *key, const char *v)
373 {
374   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
375   start_value (writer, key, "none", "base64");
376   if (len > 0) XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
377   end_value (writer);
378   return 0;
379 }
380
381 static int
382 value_other (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
383              hive_type t, size_t len, const char *key, const char *v)
384 {
385   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
386   const char *type;
387
388   switch (t) {
389   case hive_t_none:
390   case hive_t_binary:
391   case hive_t_dword:
392   case hive_t_dword_be:
393   case hive_t_qword:
394   case hive_t_string:
395   case hive_t_expand_string:
396   case hive_t_link:
397   case hive_t_multiple_strings:
398     abort ();                   /* internal error - should not happen */
399
400   case hive_t_resource_list: type = "resource-list"; break;
401   case hive_t_full_resource_description: type = "resource-description"; break;
402   case hive_t_resource_requirements_list: type = "resource-requirements"; break;
403
404   default:
405     type = "unknown";
406   }
407
408   start_value (writer, key, type, "base64");
409   if (len > 0) XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
410   end_value (writer);
411
412   return 0;
413 }