source: trunk/src/common.c @ 126

Last change on this file since 126 was 126, checked in by tim, 16 years ago

improved validation and output of key class names, MULTI_SZ and other unicode strings, and improved warnings and other error messages.

  • Property svn:keywords set to Id
File size: 9.5 KB
Line 
1/*
2 * This file stores code common to the command line tools.
3 * XXX: This should be converted to a proper library.
4 *
5 * Copyright (C) 2005-2008 Timothy D. Morgan
6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3 of the License.
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 * $Id: common.c 126 2008-08-19 23:31:33Z tim $
22 */
23
24#include <iconv.h>
25iconv_t conv_desc;
26
27const char* key_special_chars = ",\"\\/";
28const char* subfield_special_chars = ",\"\\|";
29const char* common_special_chars = ",\"\\";
30
31#define REGLOOKUP_VERSION "0.9.0"
32
33
34void bailOut(int code, char* message)
35{
36  fprintf(stderr, message);
37  exit(code);
38}
39
40
41/* Returns a newly malloc()ed string which contains original buffer,
42 * except for non-printable or special characters are quoted in hex
43 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
44 * character.  A null terminator is added, since only ascii, not binary,
45 * is returned.
46 */
47static char* quote_buffer(const unsigned char* str, 
48                          unsigned int len, const char* special)
49{
50  unsigned int i, added_len;
51  unsigned int num_written = 0;
52
53  unsigned int buf_len = sizeof(char)*(len+1);
54  char* ret_val = malloc(buf_len);
55  char* tmp_buf;
56
57  if(ret_val == NULL)
58    return NULL;
59
60  for(i=0; i<len; i++)
61  {
62    if(buf_len <= (num_written+5))
63    {
64      /* Expand the buffer by the memory consumption rate seen so far
65       * times the amount of input left to process.  The expansion is bounded
66       * below by a minimum safety increase, and above by the maximum possible
67       * output string length.  This should minimize both the number of
68       * reallocs() and the amount of wasted memory.
69       */
70      added_len = (len-i)*num_written/(i+1);
71      if((buf_len+added_len) > (len*4+1))
72        buf_len = len*4+1;
73      else
74      {
75        if (added_len < 5)
76          buf_len += 5;
77        else
78          buf_len += added_len;
79      }
80
81      tmp_buf = realloc(ret_val, buf_len);
82      if(tmp_buf == NULL)
83      {
84        free(ret_val);
85        return NULL;
86      }
87      ret_val = tmp_buf;
88    }
89   
90    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
91    {
92      num_written += snprintf(ret_val + num_written, buf_len - num_written,
93                              "\\x%.2X", str[i]);
94    }
95    else
96      ret_val[num_written++] = str[i];
97  }
98  ret_val[num_written] = '\0';
99
100  return ret_val;
101}
102
103
104/* Returns a newly malloc()ed string which contains original string,
105 * except for non-printable or special characters are quoted in hex
106 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
107 * character.
108 */
109static char* quote_string(const char* str, const char* special)
110{
111  unsigned int len;
112
113  if(str == NULL)
114    return NULL;
115
116  len = strlen(str);
117  return quote_buffer((const unsigned char*)str, len, special);
118}
119
120
121/*
122 * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
123 * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
124 * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
125 * terminates the string.  Returns the length of the data written to
126 * ascii.  On error, returns a negative errno code.
127 */
128static int uni_to_ascii(unsigned char* uni, char* ascii, 
129                        uint32 uni_max, uint32 ascii_max)
130{
131  char* inbuf = (char*)uni;
132  char* outbuf = ascii;
133  size_t in_len = (size_t)uni_max;
134  size_t out_len = (size_t)(ascii_max-1);
135  int ret;
136
137  /* Set up conversion descriptor. */
138  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
139
140  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
141  if(ret == -1)
142  {
143    iconv_close(conv_desc);
144    return -errno;
145  }
146  *outbuf = '\0';
147
148  iconv_close(conv_desc); 
149  return ascii_max-out_len-1;
150}
151
152
153static char* quote_unicode(unsigned char* uni, uint32 length, 
154                           const char* special, char** error_msg)
155{
156  char* ret_val;
157  char* ascii;
158  char* tmp_err;
159  int ret_err;
160  *error_msg = NULL;
161
162  ascii = malloc(length+1);
163  if(ascii == NULL)
164  {
165    *error_msg = (char*)malloc(27);
166    if(*error_msg == NULL)
167      return NULL;
168    strcpy(*error_msg, "Memory allocation failure.");
169    return NULL;
170  }
171 
172  ret_err = uni_to_ascii(uni, ascii, length, length+1);
173  if(ret_err < 0)
174  {
175    free(ascii);
176    tmp_err = strerror(-ret_err);
177    *error_msg = (char*)malloc(54+strlen(tmp_err));
178    if(*error_msg == NULL)
179    {
180      free(ascii);
181      return NULL;
182    }
183
184    sprintf(*error_msg, 
185            "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
186    ret_val = quote_buffer(uni, length, special);
187  }
188  else
189  {
190    ret_val = quote_string(ascii, special);
191    free(ascii);
192  }
193 
194  return ret_val;
195}
196
197
198/*
199 * Convert a data value to a string for display.  Returns NULL on error,
200 * and the string to display if there is no error, or a non-fatal
201 * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
202 * be set to a newly allocated string, containing an error message.  If
203 * a memory allocation failure occurs while generating the error
204 * message, both the return value and (*error_msg) will be NULL.  It
205 * is the responsibility of the caller to free both a non-NULL return
206 * value, and a non-NULL (*error_msg).
207 */
208static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type, 
209                           char** error_msg)
210{
211  char* asciip;
212  char* ascii;
213  char* ascii_tmp;
214  char* cur_quoted;
215  char* tmp_err = NULL;
216  const char* delim;
217  uint32 i;
218  uint32 cur_str_len;
219  uint32 ascii_max;
220  uint32 str_rem, alen;
221  int ret_err;
222
223  if(datap == NULL)
224  {
225    *error_msg = (char*)malloc(24);
226    if(*error_msg == NULL)
227      return NULL;
228    strcpy(*error_msg, "Data pointer was NULL.");
229    return NULL;
230  }
231  *error_msg = NULL;
232
233  switch (type) 
234  {
235  case REG_SZ:
236  case REG_EXPAND_SZ:
237    /* REG_LINK is a symbolic link, stored as a unicode string. */
238  case REG_LINK:
239    /* Sometimes values have binary stored in them.  If the unicode
240     * conversion fails, just quote it raw.
241     */
242    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
243    if(cur_quoted == NULL)
244    {
245      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
246        strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
247      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
248      {
249        sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
250                tmp_err);
251        free(tmp_err);
252      }
253    }
254    else if (tmp_err != NULL)
255      *error_msg = tmp_err;
256    return cur_quoted;
257    break;
258
259  case REG_DWORD:
260    ascii_max = sizeof(char)*(8+2+1);
261    ascii = malloc(ascii_max);
262    if(ascii == NULL)
263      return NULL;
264
265    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
266             datap[3], datap[2], datap[1], datap[0]);
267    return ascii;
268    break;
269
270  case REG_DWORD_BE:
271    ascii_max = sizeof(char)*(8+2+1);
272    ascii = malloc(ascii_max);
273    if(ascii == NULL)
274      return NULL;
275
276    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
277             datap[0], datap[1], datap[2], datap[3]);
278    return ascii;
279    break;
280
281  case REG_QWORD:
282    ascii_max = sizeof(char)*(16+2+1);
283    ascii = malloc(ascii_max);
284    if(ascii == NULL)
285      return NULL;
286
287    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
288             datap[7], datap[6], datap[5], datap[4],
289             datap[3], datap[2], datap[1], datap[0]);
290    return ascii;
291    break;
292   
293  case REG_MULTI_SZ:
294    ascii_max = sizeof(char)*(len*4+1);
295    ascii_tmp = malloc(ascii_max);
296    if(ascii_tmp == NULL)
297      return NULL;
298
299    /* Attempt to convert entire string from UTF-16LE to ASCII,
300     * then parse and quote fields individually.
301     * If this fails, simply quote entire buffer as binary.
302     */
303    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
304    if(ret_err < 0)
305    {
306      tmp_err = strerror(-ret_err);
307      *error_msg = (char*)malloc(54+strlen(tmp_err));
308      if(*error_msg == NULL)
309        return NULL;
310      sprintf(*error_msg, "MULTI_SZ unicode conversion"
311              " failed with '%s'. Quoting as binary.", tmp_err);
312      ascii = quote_buffer(datap, len, subfield_special_chars);
313    }
314    else
315    {
316      ascii = malloc(ascii_max);
317      if(ascii == NULL)
318      {
319        free(ascii_tmp);
320        return NULL;
321      }
322      asciip = ascii;
323      asciip[0] = '\0';
324      str_rem = ascii_max;
325      delim = "";
326      for(i=0; i<ret_err; i+=cur_str_len+1)
327      {
328        cur_str_len = strlen(ascii_tmp+i);
329        if(ascii_tmp[i] != '\0')
330        {
331          cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
332          if(cur_quoted != NULL)
333          {
334            alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
335            asciip += alen;
336            str_rem -= alen;
337            free(cur_quoted);
338          }
339        }
340        delim = "|";
341      }
342    }
343
344    free(ascii_tmp);
345    return ascii;
346    break;
347
348  /* XXX: Dont know what to do with these yet, just print as binary... */
349  default:
350    *error_msg = (char*)malloc(65);
351    if(*error_msg == NULL)
352      return NULL;
353    sprintf(*error_msg,
354            "Unrecognized registry data type (0x%.8X); quoting as binary.",
355            type);
356   
357  case REG_NONE:
358  case REG_RESOURCE_LIST:
359  case REG_FULL_RESOURCE_DESCRIPTOR:
360  case REG_RESOURCE_REQUIREMENTS_LIST:
361
362  case REG_BINARY:
363    return quote_buffer(datap, len, common_special_chars);
364    break;
365  }
366
367  return NULL;
368}
Note: See TracBrowser for help on using the repository browser.