test: server: Report server crash
[libisds.git] / src / utils.c
blobc1ee1152f5924765804526573e3f1dbe42d4418a
1 #include "isds_priv.h"
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <iconv.h>
6 #include <langinfo.h>
7 #include <time.h>
8 #include <errno.h>
9 #include "utils.h"
10 #include "cencode.h"
11 #include "cdecode.h"
13 char *tz_orig; /* Copy of original TZ variable */
15 /* Concatenate two strings into newly allocated buffer.
16 * You must free() them, when you don't need it anymore.
17 * Any of the arguments can be NULL meaning empty string.
18 * In case of error returns NULL.
19 * Empty string is always returned as allocated empty string. */
20 _hidden char *_isds_astrcat(const char *first, const char *second) {
21 size_t first_len, second_len;
22 char *buf;
24 first_len = (first) ? strlen(first) : 0;
25 second_len = (second) ? strlen(second) : 0;
26 buf = malloc(1 + first_len + second_len);
27 if (buf) {
28 buf[0] = '\0';
29 if (first) strcpy(buf, first);
30 if (second) strcpy(buf + first_len, second);
32 return buf;
36 /* Concatenate three strings into newly allocated buffer.
37 * You must free() them, when you don't need it anymore.
38 * Any of the arguments can be NULL meaning empty string.
39 * In case of error returns NULL.
40 * Empty string is always returned as allocated empty string. */
41 _hidden char *_isds_astrcat3(const char *first, const char *second,
42 const char *third) {
43 size_t first_len, second_len, third_len;
44 char *buf, *next;
46 first_len = (first) ? strlen(first) : 0;
47 second_len = (second) ? strlen(second) : 0;
48 third_len = (third) ? strlen(third) : 0;
49 buf = malloc(1 + first_len + second_len + third_len);
50 if (buf) {
51 buf[0] = '\0';
52 next = buf;
53 if (first) {
54 strcpy(next, first);
55 next += first_len;
57 if (second) {
58 strcpy(next, second);
59 next += second_len;
61 if (third) {
62 strcpy(next, third);
65 return buf;
69 /* Print formatted string into automatically reallocated @buffer.
70 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
71 * memory.
72 * @format format string as for printf(3)
73 * @ap list of variadic arguments, after call will be in undefined state
74 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer*/
75 _hidden int isds_vasprintf(char **buffer, const char *format, va_list ap) {
76 va_list aq;
77 int length, new_length;
78 char *new_buffer;
80 if (!buffer || !format) {
81 if (buffer) {
82 free(*buffer);
83 *buffer = NULL;
85 return -1;
88 va_copy(aq, ap);
89 length = vsnprintf(NULL, 0, format, aq) + 1;
90 va_end(aq);
91 if (length <= 0) {
92 free(*buffer);
93 *buffer = NULL;
94 return -1;
97 new_buffer = realloc(*buffer, length);
98 if (!new_buffer) {
99 free(*buffer);
100 *buffer = NULL;
101 return -1;
103 *buffer = new_buffer;
105 new_length = vsnprintf(*buffer, length, format, ap);
106 if (new_length >= length) {
107 free(*buffer);
108 *buffer = NULL;
109 return -1;
112 return new_length;
116 /* Print formatted string into automatically reallocated @buffer.
117 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
118 * memory.
119 * @format format string as for printf(3)
120 * @... variadic arguments
121 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer */
122 _hidden int isds_asprintf(char **buffer, const char *format, ...) {
123 int ret;
124 va_list ap;
125 va_start(ap, format);
126 ret = isds_vasprintf(buffer, format, ap);
127 va_end(ap);
128 return ret;
131 /* Converts a block from charset to charset.
132 * @from is input charset of @input block as known to iconv
133 * @to is output charset @input will be converted to @output
134 * @input is block in @from charset/encoding of length @input_length
135 * @input_length is size of @input block in bytes
136 * @output is automatically allocated block of data converted from @input. No
137 * NUL is apended. Can be NULL, if resulting size is 0. You must free it.
138 * @return size of @output in bytes. In case of error returns (size_t) -1 and
139 * deallocates @output if this function allocated it in this call. */
140 _hidden size_t _isds_any2any(const char *from, const char *to,
141 const void *input, size_t input_length, void **output) {
142 iconv_t state;
143 char *buffer = NULL, *new_buffer;
144 size_t buffer_length = 0, buffer_used = 0;
145 char *inbuf, *outbuf;
146 size_t inleft, outleft;
148 if (output != NULL) *output = NULL;
149 if (from == NULL || to == NULL || input == NULL || output == NULL)
150 return (size_t) -1;
152 state = iconv_open(to, from);
153 if (state == (iconv_t) -1) return (size_t) -1;
155 /* Get the initial output buffer length */
156 buffer_length = input_length;
158 inbuf = (char *) input;
159 inleft = input_length;
161 while (inleft > 0) {
162 /* Extend buffer */
163 new_buffer = realloc(buffer, buffer_length);
164 if (!new_buffer) {
165 zfree(buffer);
166 buffer_used = (size_t) -1;
167 goto leave;
169 buffer = new_buffer;
171 /* FIXME */
172 outbuf = buffer + buffer_used;
173 outleft = buffer_length - buffer_used;
175 /* Convert chunk of data */
176 if ((size_t) -1 == iconv(state, &inbuf, &inleft, &outbuf, &outleft) &&
177 errno != E2BIG) {
178 zfree(buffer);
179 buffer_used = (size_t) -1;
180 goto leave;
183 /* Update positions */
184 buffer_length += 1024;
185 buffer_used = outbuf - buffer;
188 leave:
189 iconv_close(state);
190 if (buffer_used == 0) zfree(buffer);
191 *output = buffer;
192 return buffer_used;
195 /* Converts UTF8 string into locale encoded string.
196 * @utf string int UTF-8 terminated by zero byte
197 * @return allocated string encoded in locale specific encoding. You must free
198 * it. In case of error or NULL @utf returns NULL. */
199 _hidden char *_isds_utf82locale(const char *utf) {
200 char *output, *bigger_output;
201 size_t length;
203 if (utf == NULL) return NULL;
205 length = _isds_any2any("UTF-8", nl_langinfo(CODESET), utf, strlen(utf),
206 (void **) &output);
207 if (length == (size_t) -1) return NULL;
209 bigger_output = realloc(output, length + 1);
210 if (bigger_output == NULL) {
211 zfree(output);
212 } else {
213 output = bigger_output;
214 output[length] = '\0';
217 return output;
221 /* Encode given data into MIME Base64 encoded zero terminated string.
222 * @plain are input data (binary stream)
223 * @length is length of @plain data in bytes
224 * @return allocated string of base64 encoded plain data or NULL in case of
225 * error. You must free it. */
226 _hidden char *_isds_b64encode(const void *plain, const size_t length) {
228 base64_encodestate state;
229 size_t code_length;
230 char *buffer, *new_buffer;
232 if (!plain) {
233 if (length) return NULL;
234 /* Empty input is valid input */
235 plain = "";
238 _isds_base64_init_encodestate(&state);
240 /* Allocate buffer
241 * (4 is padding, 1 is final new line, and 1 is string terminator) */
242 buffer = malloc(length * 2 + 4 + 1 + 1);
243 if (!buffer) return NULL;
245 /* Encode plain data */
246 code_length = _isds_base64_encode_block(plain, length, (int8_t *)buffer,
247 &state);
248 code_length += _isds_base64_encode_blockend(((int8_t*)buffer) + code_length,
249 &state);
251 /* Terminate string */
252 buffer[code_length++] = '\0';
254 /* Shrink the buffer */
255 new_buffer = realloc(buffer, code_length);
256 if (new_buffer) buffer = new_buffer;
258 return buffer;
262 /* Decode given data from MIME Base64 encoded zero terminated string to binary
263 * stream. Invalid Base64 symbols are skipped.
264 * @encoded are input data (Base64 zero terminated string)
265 * @plain are automatically reallocated output data (binary stream). You must
266 * free it. Will be freed in case of error.
267 * @return length of @plain data in bytes or (size_t) -1 in case of memory
268 * allocation failure. */
269 _hidden size_t _isds_b64decode(const char *encoded, void **plain) {
271 base64_decodestate state;
272 size_t encoded_length;
273 int plain_length;
274 char *buffer;
276 if (!encoded || !plain) {
277 if (plain && *plain) zfree(*plain);
278 return ((size_t) -1);
281 encoded_length = strlen(encoded);
282 _isds_base64_init_decodestate(&state);
284 /* Divert empty input */
285 if (encoded_length == 0) {
286 zfree(*plain);
287 return 0;
290 /* Allocate buffer */
291 buffer = realloc(*plain, encoded_length);
292 if (!buffer) {
293 zfree(*plain);
294 return ((size_t) -1);
296 *plain = buffer;
298 /* Decode encoded data */
299 plain_length = _isds_base64_decode_block((const int8_t *)encoded,
300 encoded_length, *plain, &state);
301 if (plain_length < 0 || plain_length >= (size_t) -1) {
302 zfree(*plain);
303 return((size_t) -1);
306 /* Shrink the buffer */
307 buffer = realloc(*plain, plain_length);
308 if (!buffer) *plain = buffer;
309 /* realloc(, 0) can return NULL or pointer designed to free() */
310 if (plain_length == 0) zfree(*plain);
312 return plain_length;
316 /* Convert hexadecimal digit to integer. Return negative value if character is
317 * not valid hexadecimal digit. */
318 _hidden int _isds_hex2i(char digit) {
319 if (digit >= '0' && digit <= '9')
320 return digit - '0';
321 if (digit >= 'a' && digit <= 'f')
322 return digit - 'a' + 10;
323 if (digit >= 'A' && digit <= 'F')
324 return digit - 'A' + 10;
325 return -1;
329 /* Switches time zone to UTC.
330 * XXX: This is not reentrant and not thread-safe */
331 static void _isds_switch_tz_to_utc(void) {
332 char *tz;
334 tz = getenv("TZ");
335 if (tz) {
336 tz_orig = strdup(tz);
337 if (!tz_orig)
338 PANIC("Can not back original time zone up");
339 } else {
340 tz_orig = NULL;
343 if (setenv("TZ", "", 1))
344 PANIC("Can not change time zone to UTC temporarily");
346 tzset();
350 /* Switches time zone to original value.
351 * XXX: This is not reentrant and not thread-safe */
352 static void _isds_switch_tz_to_native(void) {
353 if (tz_orig) {
354 if (setenv("TZ", tz_orig, 1))
355 PANIC("Can not restore time zone by setting TZ variable");
356 free(tz_orig);
357 tz_orig = NULL;
358 } else {
359 if(unsetenv("TZ"))
360 PANIC("Can not restore time zone by unsetting TZ variable");
362 tzset();
366 /* Convert UTC broken time to time_t.
367 * @broken_utc it time in UTC in broken format. Despite its content is not
368 * touched, it'sw not-const because underlying POSIX function has non-const
369 * signature.
370 * @return (time_t) -1 in case of error */
371 _hidden time_t _isds_timegm(struct tm *broken_utc) {
372 time_t time;
374 _isds_switch_tz_to_utc();
375 time = mktime(broken_utc);
376 _isds_switch_tz_to_native();
378 return time;