test: guess_raw_type: plain signed incoming message
[libisds.git] / src / utils.c
blob731fb7acd9faa0d0cc0db5c06816dd399cf89ed2
1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #define _POSIX_C_SOURCE 200112L /* For setenv() */
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <iconv.h>
7 #include <langinfo.h>
8 #include <time.h>
9 #include <errno.h>
10 #include "utils.h"
11 #include "cencode.h"
12 #include "cdecode.h"
14 char *tz_orig; /* Copy of original TZ variable */
16 /* Concatenate two strings into newly allocated buffer.
17 * You must free() them, when you don't need it anymore.
18 * Any of the arguments can be NULL meaning empty string.
19 * In case of error returns NULL.
20 * Empty string is always returned as allocated empty string. */
21 _hidden char *astrcat(const char *first, const char *second) {
22 size_t first_len, second_len;
23 char *buf;
25 first_len = (first) ? strlen(first) : 0;
26 second_len = (second) ? strlen(second) : 0;
27 buf = malloc(1 + first_len + second_len);
28 if (buf) {
29 buf[0] = '\0';
30 if (first) strcpy(buf, first);
31 if (second) strcpy(buf + first_len, second);
33 return buf;
37 /* Concatenate three strings into newly allocated buffer.
38 * You must free() them, when you don't need it anymore.
39 * Any of the arguments can be NULL meaning empty string.
40 * In case of error returns NULL.
41 * Empty string is always returned as allocated empty string. */
42 _hidden char *astrcat3(const char *first, const char *second,
43 const char *third) {
44 size_t first_len, second_len, third_len;
45 char *buf, *next;
47 first_len = (first) ? strlen(first) : 0;
48 second_len = (second) ? strlen(second) : 0;
49 third_len = (third) ? strlen(third) : 0;
50 buf = malloc(1 + first_len + second_len + third_len);
51 if (buf) {
52 buf[0] = '\0';
53 next = buf;
54 if (first) {
55 strcpy(next, first);
56 next += first_len;
58 if (second) {
59 strcpy(next, second);
60 next += second_len;
62 if (third) {
63 strcpy(next, third);
66 return buf;
70 /* Print formated string into automtically reallocated @uffer.
71 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
72 * memory.
73 * @format format string as for printf(3)
74 * @ap list of variadic arguments, after call will be in udefined state
75 * @Returns number of bytes printed. In case of errror, -1 and NULL @buffer*/
76 _hidden int isds_vasprintf(char **buffer, const char *format, va_list ap) {
77 va_list aq;
78 int length, new_length;
79 char *new_buffer;
81 if (!buffer || !format) {
82 if (buffer) {
83 free(*buffer);
84 *buffer = NULL;
86 return -1;
89 va_copy(aq, ap);
90 length = vsnprintf(NULL, 0, format, aq) + 1;
91 va_end(aq);
92 if (length <= 0) {
93 free(*buffer);
94 *buffer = NULL;
95 return -1;
98 new_buffer = realloc(*buffer, length);
99 if (!new_buffer) {
100 free(*buffer);
101 *buffer = NULL;
102 return -1;
104 *buffer = new_buffer;
106 new_length = vsnprintf(*buffer, length, format, ap);
107 if (new_length >= length) {
108 free(*buffer);
109 *buffer = NULL;
110 return -1;
113 return new_length;
117 /* Print formated string into automtically reallocated @uffer.
118 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
119 * memory.
120 * @format format string as for printf(3)
121 * @... variadic arguments
122 * @Returns number of bytes printed. In case of errror, -1 and NULL @buffer*/
123 _hidden int isds_asprintf(char **buffer, const char *format, ...) {
124 int ret;
125 va_list ap;
126 va_start(ap, format);
127 ret = isds_vasprintf(buffer, format, ap);
128 va_end(ap);
129 return ret;
133 /* Converts UTF8 string into locale encoded string.
134 * @utf string int UTF-8 terminated by zero byte
135 * @return allocated string encoded in locale specific encoding. You must free
136 * it. In case of error or NULL @utf returns NULL. */
137 _hidden char *utf82locale(const char *utf) {
138 iconv_t state;
139 size_t utf_length;
140 char *buffer = NULL, *new_buffer;
141 size_t buffer_length = 0, buffer_used = 0;
142 char *inbuf, *outbuf;
143 size_t inleft, outleft;
145 if (!utf) return NULL;
147 /* nl_langinfo() is not thread-safe */
148 state = iconv_open(nl_langinfo(CODESET), "UTF-8");
149 if (state == (iconv_t) -1) return NULL;
151 /* Get the initial ouput buffer length */
152 utf_length = strlen(utf);
153 buffer_length = utf_length + 1;
155 inbuf = (char *) utf;
156 inleft = utf_length + 1;
158 while (inleft > 0) {
159 /* Extend buffer */
160 new_buffer = realloc(buffer, buffer_length);
161 if (!new_buffer) {
162 free(buffer);
163 buffer = NULL;
164 goto leave;
166 buffer = new_buffer;
168 /* FIXME */
169 outbuf = buffer + buffer_used;
170 outleft = buffer_length - buffer_used;
172 /* Convert chunk of data */
173 if ((size_t) -1 == iconv(state, &inbuf, &inleft, &outbuf, &outleft) &&
174 errno != E2BIG) {
175 free(buffer);
176 buffer = NULL;
177 goto leave;
180 /* Update positions */
181 buffer_length += 1024;
182 buffer_used = outbuf - buffer;
185 leave:
186 iconv_close(state);
187 return buffer;
191 /* Encode given data into MIME Base64 encoded zero terminated string.
192 * @plain are input data (binary stream)
193 * @length is length of @plain data in bytes
194 * @return allocated string of base64 encoded plain data or NULL in case of
195 * error. You must free it. */
196 _hidden char *b64encode(const void *plain, const size_t length) {
198 base64_encodestate state;
199 size_t code_length;
200 char *buffer, *new_buffer;
202 if (!plain) return NULL;
204 base64_init_encodestate(&state);
206 /* TODO: This function assumes sizeof(char) == 1 byte.
207 * To fix it, one must fix underlying functions too. */
208 if (sizeof(char) != 1) PANIC("sizeof(char) != 1 byte");
210 /* Allocate buffer
211 * (4 is padding, 1 is final new line, and 1 is string terminator) */
212 buffer = malloc(length * 2 + 4 + 1 + 1);
213 if (!buffer) return NULL;
215 /* Encode plain data */
216 code_length = base64_encode_block(plain, length, buffer, &state);
217 code_length += base64_encode_blockend(buffer + code_length, &state);
219 /* Terminate string */
220 buffer[code_length++] = '\0';
222 /* Shrink the buffer */
223 new_buffer = realloc(buffer, code_length);
224 if (new_buffer) buffer = new_buffer;
226 return buffer;
230 /* Decode given data from MIME Base64 encoded zero terminated string to binary
231 * stream.
232 * @encoded are input data (Base64 zero terminated string)
233 * @plain are automatically realocated output data (binary stream). You must
234 * free it. Will be freed in case of error.
235 * @return length of @plain data in bytes or (size_t) -1 in case of decoding
236 * failure. */
237 _hidden size_t b64decode(const char *encoded, void **plain) {
239 base64_decodestate state;
240 size_t encoded_length;
241 int plain_length;
242 char *buffer;
244 if (!encoded || !plain) {
245 if (plain && *plain) zfree(*plain);
246 return ((size_t) -1);
249 encoded_length = strlen(encoded);
250 base64_init_decodestate(&state);
252 /* TODO: This function assumes sizeof(char) == 1 byte.
253 * To fix it, one must fix underlying functions too. */
254 if (sizeof(char) != 1) PANIC("sizeof(char) != 1 byte");
256 /* Allocate buffer */
257 buffer = realloc(*plain, encoded_length);
258 if (!buffer) {
259 zfree(*plain);
260 return ((size_t) -1);
262 *plain = buffer;
264 /* Decode encoded data */
265 plain_length = base64_decode_block(encoded, encoded_length,
266 *plain, &state);
267 if (plain_length < 0) {
268 zfree(*plain);
269 return((size_t) -1);
272 /* Shrink the buffer */
273 buffer = realloc(*plain, plain_length);
274 if (!buffer) *plain = buffer;
276 return plain_length;
280 /* Switches time zone to UTC.
281 * XXX: This is not reentrant and not thread-safe */
282 _hidden void switch_tz_to_utc(void) {
283 char *tz;
285 tz = getenv("TZ");
286 if (tz) {
287 tz_orig = strdup(tz);
288 if (!tz_orig)
289 PANIC("Can not back original time zone up");
290 } else {
291 tz_orig = NULL;
294 if (setenv("TZ", "", 1))
295 PANIC("Can not change time zone to UTC temporarily");
297 tzset();
301 /* Switches time zone to original value.
302 * XXX: This is not reentrant and not thread-safe */
303 _hidden void switch_tz_to_native(void) {
304 if (tz_orig) {
305 if (setenv("TZ", tz_orig, 1))
306 PANIC("Can not restore time zone by setting TZ variable");
307 free(tz_orig);
308 tz_orig = NULL;
309 } else {
310 if(unsetenv("TZ"))
311 PANIC("Can not restore time zone by unsetting TZ variable");
313 tzset();