source: trunk/src/common.c @ 139

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

extended error message logging to allow for message type filtering

fine tuned message verbosity to more reasonable default levels for reglookup and reglookup-recover

updated related documentation

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