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