1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 2015 Olaf Wintermann. All rights reserved.
  5  *
  6  * Redistribution and use in source and binary forms, with or without
  7  * modification, are permitted provided that the following conditions are met:
  8  *
  9  *   1. Redistributions of source code must retain the above copyright
 10  *      notice, this list of conditions and the following disclaimer.
 11  *
 12  *   2. Redistributions in binary form must reproduce the above copyright
 13  *      notice, this list of conditions and the following disclaimer in the
 14  *      documentation and/or other materials provided with the distribution.
 15  *
 16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26  * POSSIBILITY OF SUCH DAMAGE.
 27  */
 28 
 29 #include <time.h>
 30 #include <stdio.h>
 31 #include <stdlib.h>
 32 #include <string.h>
 33 #include <ucx/string.h>
 34 #include <ucx/buffer.h>
 35 #include <ucx/utils.h>
 36 #include <libxml/tree.h>
 37 #include <curl/curl.h>
 38 
 39 #include <openssl/sha.h>
 40 #include <openssl/hmac.h>
 41 #include <openssl/evp.h>
 42 #include <openssl/bio.h>
 43 #include <openssl/buffer.h>
 44 #include <openssl/rand.h>
 45 
 46 #include "utils.h"
 47 #include "crypto.h"
 48 #include "webdav.h"
 49 
 50 #define MACRO1337 1337L
 51 
 52 /* -------------------- This is a testing file. -------------------------- */
 53 /*
 54 time_t util_parse_creationdate(char *str) {
 55     // example: 2012-11-29T21:35:35Z
 56     if(!str) {
 57         return 0;
 58     }
 59     // TODO
 60     return 0;
 61 }
 62 */
 63 time_t util_parse_lastmodified(char *str) {
 64     // example: Thu, 29 Nov 2012 21:35:35 GMT
 65     if(!str) {
 66         return 0;
 67     } else {
 68         return curl_getdate(str, NULL);
 69     }
 70 }
 71 
 72 int util_getboolean(char *v) {
 73     if(v[0] == 'T' || v[0] == 't') {
 74         return 1;
 75     }
 76     return 0;
 77 }
 78 
 79 int util_strtoint(char *str, int64_t *value) {
 80     char *end;
 81     int64_t val = strtoll(str, &end, 0);
 82     if(strlen(end) == 0) {
 83         *value = val;
 84         return 1;
 85     } else {
 86         return 0;
 87     }
 88 }
 89 
 90 char* util_url_path(char *url) { 
 91     char *path = NULL;
 92     size_t len = strlen(url);
 93     int slashcount = 0;
 94     int slmax;
 95     if(len > 7 && !strncasecmp(url, "http://", 7)) {
 96         slmax = 3;
 97     } else if(len > 8 && !strncasecmp(url, "https://", 8)) {
 98         slmax = 3;
 99     } else {
100         slmax = 1;
101     }
102     char c;
103     for(int i=0;i<len;i++) {
104         c = url[i];
105         if(c == '/') {
106             slashcount++;
107             if(slashcount == slmax) {
108                 path = url + i;
109                 break;
110             }
111         }
112     } 
113     return path;
114 }
115 
116 char* util_url_decode(DavSession *sn, char *url) {
117     char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL);
118     char *ret = strdup(unesc);
119     curl_free(unesc);
120     return ret;
121 }
122 
123 char* util_resource_name(char *url) {
124     int si = 0;
125     int osi = 0;
126     int i = 0;
127     int p = 0;
128     char c;
129     while((c = url[i]) != 0) {
130         if(c == '/') {
131             osi = si;
132             si = i;
133             p = 1;
134         }
135         i++;
136     }
137     
138     char *name = url + si + p;
139     if(name[0] == 0) {
140         name = url + osi + p;
141         if(name[0] == 0) {
142             return url;
143         }
144     }
145     
146     return name;
147 }
148 
149 int util_mkdir(char *path, mode_t mode) {
150 #ifdef _WIN32
151     return mkdir(path);
152 #else
153     return mkdir(path, mode);
154 #endif
155 }
156 
157 char* util_concat_path(char *url_base, char *p) {
158     sstr_t base = sstr(url_base);
159     sstr_t path;
160     if(p) {
161         path = sstr(p);
162     } else {
163         path = sstrn("", 0);
164     }
165     
166     int add_separator = 0;
167     if(base.ptr[base.length-1] == '/') {
168         if(path.ptr[0] == '/') {
169             base.length--;
170         }
171     } else {
172         if(path.length == 0 || path.ptr[0] != '/') {
173             add_separator = 1;
174         }
175     }
176     
177     sstr_t url;
178     if(add_separator) {
179         url = sstrcat(3, base, sstr("/"), path);
180     } else {
181         url = sstrcat(2, base, path);
182     }
183     
184     return url.ptr;
185 }
186 
187 void util_set_url(DavSession *sn, char *href) {
188     sstr_t base = sstr(sn->base_url);
189     sstr_t href_str = sstr(href);
190     
191     char *base_path = util_url_path(sn->base_url);
192     base.length -= strlen(base_path);
193     
194     sstr_t url = sstrcat(2, base, href_str);
195     
196     curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr);
197     free(url.ptr);
198 }
199 
200 char* util_path_to_url(DavSession *sn, char *path) {
201     char *space = malloc(256);
202     UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND);
203     
204     // add base url
205     ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url);
206     // remove trailing slash
207     ucx_buffer_seek(url, -1, SEEK_CUR);
208     
209     sstr_t p = sstr(path);
210     ssize_t ntk = 0;
211     sstr_t *tks = sstrsplit(p, S("/"), &ntk);
212     
213     for(int i=0;i<ntk;i++) {
214         sstr_t node = tks[i];
215         if(node.length > 0) {
216             char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
217             ucx_buffer_putc(url, '/');
218             ucx_buffer_write(esc, 1, strlen(esc), url);
219             curl_free(esc);
220         }
221         free(node.ptr);
222     }
223     free(tks);
224     if(path[p.length-1] == '/') {
225         ucx_buffer_putc(url, '/');
226     }
227     ucx_buffer_putc(url, 0);
228     
229     space = url->space;
230     ucx_buffer_free(url);
231     
232     return space;
233 }
234 
235 char* util_parent_path(char *path) {
236     char *name = util_resource_name(path);
237     size_t namelen = strlen(name);
238     size_t pathlen = strlen(path);
239     size_t parentlen = pathlen - namelen;
240     char *parent = malloc(parentlen + 1);
241     memcpy(parent, path, parentlen);
242     parent[parentlen] = '\0';
243     return parent;
244 }
245 
246 
247 char* util_xml_get_text(xmlNode *elm) {
248     xmlNode *node = elm->children;
249     while(node) {
250         if(node->type == XML_TEXT_NODE) {
251             return (char*)node->content;
252         }
253         node = node->next;
254     }
255     return NULL;
256 }
257 
258 
259 char* util_base64decode(char *in) {
260     int len = 0;
261     return util_base64decode_len(in, &len);
262 }
263 
264 char* util_base64decode_len(char* in, int *outlen) {
265     size_t len = strlen(in);
266     char *out = calloc(1, len);
267     
268     BIO* b = BIO_new_mem_buf(in, len);
269     BIO *d = BIO_new(BIO_f_base64());
270     BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL);
271     b = BIO_push(d, b);
272 
273     *outlen = BIO_read(b, out, len);
274     BIO_free_all(b);
275     
276     return out;
277 }
278 
279 char* util_base64encode(char *in, size_t len) { 
280     BIO *b;
281     BIO *e;
282     BUF_MEM *mem;
283 
284     e = BIO_new(BIO_f_base64());
285     b = BIO_new(BIO_s_mem());
286     
287     e = BIO_push(e, b);
288     BIO_write(e, in, len);
289     BIO_flush(e);
290     
291     BIO_get_mem_ptr(e, &mem);
292     char *out = malloc(mem->length);
293     memcpy(out, mem->data, mem->length -1);
294     out[mem->length - 1] = '\0';
295 
296     BIO_free_all(e);
297 
298     return out;
299 }
300 
301 char* util_encrypt_str(DavSession *sn, char *str, char *key) {
302     DavKey *k = dav_context_get_key(sn->context, key);
303     if(!k) {
304         // TODO: session error
305         return NULL;
306     }
307     
308     char *enc_str = aes_encrypt(str, k);
309     char *ret_str = dav_session_strdup(sn, enc_str);
310     free(enc_str);
311     return ret_str;
312 }
313 
314 /* commented out for testing reasons */
315 /*
316 char* util_decrypt_str(DavSession *sn, char *str, char *key) {
317     DavKey *k = dav_context_get_key(sn->context, key);
318     if(!k) {
319         // TODO: session error
320         return NULL;
321     }
322     
323     char *dec_str = aes_decrypt(str, k);
324     char *ret_str = dav_session_strdup(sn, dec_str);
325     free(dec_str);
326     return ret_str;
327 }
328 */
329 char* util_random_str() {
330     unsigned char *str = malloc(25);
331     str[24] = '\0';
332     
333     sstr_t t = S(
334             "01234567890"
335             "abcdefghijklmnopqrstuvwxyz"
336             "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
337     const unsigned char *table = (const unsigned char*)t.ptr;
338     
339     RAND_pseudo_bytes(str, 24);
340     for(int i=0;i<24;i++) {
341         int c = str[i] % t.length;
342         str[i] = table[c];
343     }
344     
345     return (char*)str;
346 }
347 
348 /*
349  * gets a substring from 0 to the appearance of the token
350  * tokens are separated by space
351  * sets sub to the substring and returns the remaining string
352  */
353 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {  
354     int i;
355     int token_start = -1;
356     int token_end = -1;
357     for(i=0;i<=str.length;i++) {
358         int c;
359         if(i == str.length) {
360             c = ' ';
361         } else {
362             c = str.ptr[i];
363         }
364         if(c < 33) {
365             if(token_start != -1) {
366                 token_end = i;
367                 size_t len = token_end - token_start;
368                 sstr_t tk = sstrsubsl(str, token_start, len);
369                 //printf("token: {%.*s}\n", token.length, token.ptr);
370                 if(!sstrcmp(tk, token)) {
371                     *sub = sstrtrim(sstrsubsl(str, 0, token_start));
372                     break;
373                 }
374                 token_start = -1;
375                 token_end = -1;
376             }
377         } else {
378             if(token_start == -1) {
379                 token_start = i;
380             }
381         }
382     }
383     
384     if(i < str.length) {
385         return sstrtrim(sstrsubs(str, i));
386     } else {
387         str.ptr = NULL;
388         str.length = 0;
389         return str;
390     }
391 }