Split multirange parsing into multipart.c
authorJonathan Dieter <jdieter@gmail.com>
Mon, 19 Mar 2018 19:11:51 +0000 (21:11 +0200)
committerJonathan Dieter <jdieter@gmail.com>
Mon, 19 Mar 2018 19:11:51 +0000 (21:11 +0200)
Signed-off-by: Jonathan Dieter <jdieter@gmail.com>
include/zck.h
src/lib/dl/dl.c
src/lib/dl/meson.build
src/lib/dl/multipart.c [new file with mode: 0644]
src/lib/zck_private.h
src/zck_dl.c

index 20e3bc5067e3182b65bb8ddb8ba857cbe8f7bdde..b14195593489667f991c33e8fabc397ed82d114d 100644 (file)
@@ -111,12 +111,12 @@ void zck_set_log_level(log_type ll);
 void zck_dl_global_init();
 void zck_dl_global_cleanup();
 zckDL *zck_dl_init();
-void zck_dl_free(zckDL *dl);
+void zck_dl_free(zckDL **dl);
 void zck_dl_free_regex(zckDL *dl);
 int zck_dl_get_header(zckCtx *zck, zckDL *dl, char *url);
 size_t zck_dl_get_bytes_downloaded(zckDL *dl);
 size_t zck_dl_get_bytes_uploaded(zckDL *dl);
-int zck_dl_range(zckDL *dl, char *url);
+int zck_dl_range(zckDL *dl, char *url, int is_chunk);
 char *zck_dl_get_range(unsigned int start, unsigned int end);
 int zck_hash_check_full_file(zckCtx *zck, int dst_fd);
 #endif
index 879b1ddb368dd4bfb23df265220f8b75d5183c89..6c59ac885b4e96046887703691124439e1bb5c2b 100644 (file)
@@ -30,7 +30,6 @@
 #include <curl/curl.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include <regex.h>
 #include <errno.h>
 #include <zck.h>
 
                             return False; \
                         }
 
+/* Free zckDL regex's used for downloading ranges */
+void zck_dl_free_dl_regex(zckDL *dl) {
+    if(dl == NULL || dl->priv == NULL)
+        return;
+
+    if(dl->priv->dl_regex) {
+        regfree(dl->priv->dl_regex);
+        free(dl->priv->dl_regex);
+        dl->priv->dl_regex = NULL;
+    }
+    if(dl->priv->end_regex) {
+        regfree(dl->priv->end_regex);
+        free(dl->priv->end_regex);
+        dl->priv->end_regex = NULL;
+    }
+}
+
+/* Write zeros to tgt->fd in location of tgt_idx */
 int zck_dl_write_zero(zckCtx *tgt, zckIndex *tgt_idx) {
     char buf[BUF_SIZE] = {0};
     size_t tgt_data_offset = tgt->preindex_size + tgt->comp_index_size;
@@ -59,6 +76,7 @@ int zck_dl_write_zero(zckCtx *tgt, zckIndex *tgt_idx) {
 }
 
 int zck_dl_write(zckDL *dl, const char *at, size_t length) {
+    VALIDATE(dl);
     if(dl->write_in_chunk < length)
         length = dl->write_in_chunk;
     if(!zck_write(dl->dst_fd, at, length))
@@ -68,6 +86,7 @@ int zck_dl_write(zckDL *dl, const char *at, size_t length) {
 }
 
 int zck_dl_md_write(zckDL *dl, const char *at, size_t length) {
+    VALIDATE(dl);
     int wb = 0;
     if(dl->write_in_chunk > 0) {
         wb = zck_dl_write(dl, at, length);
@@ -82,6 +101,7 @@ int zck_dl_md_write(zckDL *dl, const char *at, size_t length) {
 }
 
 int zck_dl_write_chunk(zckDL *dl) {
+    VALIDATE(dl);
     if(dl->chunk_hash == NULL) {
         zck_log(ZCK_LOG_ERROR, "Chunk hash not initialized\n");
         return False;
@@ -102,8 +122,9 @@ int zck_dl_write_chunk(zckDL *dl) {
     return True;
 }
 
-int zck_dl_multidata_cb(zckDL *dl, const char *at, size_t length) {
-    if(dl == NULL || dl->info.index.first == NULL) {
+int zck_dl_write_range(zckDL *dl, const char *at, size_t length) {
+    VALIDATE(dl);
+    if(dl->info.index.first == NULL) {
         zck_log(ZCK_LOG_ERROR, "zckDL index not initialized\n");
         return 0;
     }
@@ -150,78 +171,13 @@ int zck_dl_multidata_cb(zckDL *dl, const char *at, size_t length) {
     }
     int wb2 = 0;
     if(dl->write_in_chunk > 0 && wb < length) {
-        wb2 = zck_dl_multidata_cb(dl, at+wb, length-wb);
+        wb2 = zck_dl_write_range(dl, at+wb, length-wb);
         if(wb2 == 0)
             return 0;
     }
     return wb + wb2;
 }
 
-zckDL *zck_dl_init() {
-    zckDL *dl = zmalloc(sizeof(zckDL));
-    if(!dl) {
-        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for zckDL\n",
-                sizeof(zckDL));
-        return NULL;
-    }
-    dl->priv = zmalloc(sizeof(zckDLPriv));
-    if(!dl->priv) {
-        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for dl->priv\n",
-                sizeof(zckDL));
-        return NULL;
-    }
-    dl->priv->mp = zmalloc(sizeof(zckMP));
-    if(!dl->priv->mp) {
-        zck_log(ZCK_LOG_ERROR,
-                "Unable to allocate %lu bytes for dl->priv->mp\n",
-                sizeof(zckMP));
-        return NULL;
-    }
-    dl->priv->curl_ctx = curl_easy_init();
-    if(!dl->priv->curl_ctx) {
-        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for dl->curl_ctx\n",
-                sizeof(CURL));
-        return NULL;
-    }
-    return dl;
-}
-
-void zck_dl_free_regex(zckDL *dl) {
-    if(dl == NULL || dl->priv == NULL)
-        return;
-
-    if(dl->priv->dl_regex) {
-        regfree(dl->priv->dl_regex);
-        free(dl->priv->dl_regex);
-        dl->priv->dl_regex = NULL;
-    }
-    if(dl->priv->hdr_regex) {
-        regfree(dl->priv->hdr_regex);
-        free(dl->priv->hdr_regex);
-        dl->priv->hdr_regex = NULL;
-    }
-}
-
-void zck_dl_free(zckDL *dl) {
-    if(!dl)
-        return;
-    if(dl->priv) {
-        if(dl->priv->mp) {
-            if(dl->priv->mp->buffer)
-                free(dl->priv->mp->buffer);
-            free(dl->priv->mp);
-        }
-        zck_dl_free_regex(dl);
-        curl_easy_cleanup(dl->priv->curl_ctx);
-        free(dl->priv);
-    }
-    if(dl->info.first)
-        zck_range_close(&(dl->info));
-    if(dl->boundary)
-        free(dl->boundary);
-    free(dl);
-}
-
 char *zck_dl_get_range_char(unsigned int start, unsigned int end) {
     zckRange r = {0};
     zckRange *range = &r;
@@ -232,191 +188,6 @@ char *zck_dl_get_range_char(unsigned int start, unsigned int end) {
     return range_header;
 }
 
-static char *add_boundary_to_regex(const char *regex, const char *boundary) {
-    char *regex_b = zmalloc(strlen(regex) + strlen(boundary) + 1);
-    if(regex_b == NULL) {
-        zck_log(ZCK_LOG_ERROR,
-                "Unable to reallocate %lu bytes for regular expression\n",
-                strlen(regex) + strlen(boundary) - 2);
-        return 0;
-    }
-    if(snprintf(regex_b, strlen(regex) + strlen(boundary), regex,
-                boundary) != strlen(regex) + strlen(boundary) - 2) {
-        zck_log(ZCK_LOG_ERROR, "Unable to build regular expression\n");
-        return 0;
-    }
-    return regex_b;
-}
-
-
-static int create_regex(regex_t *reg, const char *regex) {
-    if(regcomp(reg, regex, REG_ICASE | REG_EXTENDED) != 0) {
-        zck_log(ZCK_LOG_ERROR, "Unable to compile regular expression\n");
-        return False;
-    }
-    return True;
-}
-
-static size_t extract_multipart(char *b, size_t l, void *dl_v) {
-    if(dl_v == NULL)
-        return 0;
-    zckDL *dl = (zckDL*)dl_v;
-    if(dl->priv == NULL || dl->priv->mp == NULL)
-        return 0;
-    zckMP *mp = dl->priv->mp;
-    char *buf = b;
-    int alloc_buf = False;
-
-    /* Add new data to stored buffer */
-    if(mp->buffer) {
-        buf = realloc(mp->buffer, mp->buffer_len + l);
-        if(buf == NULL) {
-            zck_log(ZCK_LOG_ERROR, "Unable to reallocate %lu bytes for zckDL\n",
-                    mp->buffer_len + l);
-            return 0;
-        }
-        memcpy(buf + mp->buffer_len, b, l);
-        l = mp->buffer_len + l;
-        mp->buffer = NULL;  // No need to free, buf holds realloc'd buffer
-        mp->buffer_len = 0;
-        alloc_buf = True;
-    }
-
-    /* If regex hasn't been created, create it */
-    if(dl->priv->dl_regex == NULL) {
-        char *regex = "\r\n--%s\r\ncontent-type:.*\r\n" \
-                      "content-range: *bytes *([0-9]+) *- *([0-9]+) */.*\r\n\r";
-        char *regex_b = add_boundary_to_regex(regex, dl->boundary);
-        if(regex_b == NULL)
-            return 0;
-        dl->priv->dl_regex = zmalloc(sizeof(regex_t));
-        if(!create_regex(dl->priv->dl_regex, regex_b)) {
-            free(regex_b);
-            return 0;
-        }
-        free(regex_b);
-    }
-
-    char *header_start = buf;
-    char *i = buf;
-    while(i) {
-        char *end = buf + l;
-        /* If we're in data writing state, then write data until end of buffer
-         * or end of range, whichever comes first */
-        if(mp->state != 0) {
-            if(i >= end)
-                break;
-            size_t size = end - i;
-            if(mp->length <= size) {
-                size = mp->length;
-                mp->length = 0;
-                mp->state = 0;
-                header_start = i + size;
-            } else {
-                mp->length -= size;
-            }
-            if(zck_dl_multidata_cb(dl, i, size) != size)
-                return 0;
-            i += size;
-            continue;
-        }
-
-        /* If we've reached the end of the buffer without finishing, save it
-         * and leave loop */
-        if(i >= end) {
-            size_t size = buf + l - header_start;
-            if(size > 0) {
-                mp->buffer = malloc(size);
-                memcpy(mp->buffer, header_start, size);
-                mp->buffer_len = size;
-            }
-            break;
-        }
-
-        /* Find double newline and replace final \n with \0, so it's a zero-
-         * terminated string */
-        for(char *j=i; j<end; j++) {
-            if(j + 4 >= end) {
-                i = j+4;
-                break;
-            }
-            if(memcmp(j, "\r\n\r\n", 4) == 0) {
-                j[3] = '\0';
-                break;
-            }
-        }
-        if(i >= end)
-            continue;
-
-        /* Run regex against download range string */
-        regmatch_t match[4] = {0};
-        if(regexec(dl->priv->dl_regex, i, 3, match, 0) != 0) {
-            zck_log(ZCK_LOG_ERROR, "Unable to find multipart download range\n");
-            goto end;
-        }
-
-        /* Get range start from regex */
-        size_t rstart = 0;
-        for(char *c=i + match[1].rm_so; c < i + match[1].rm_eo; c++)
-            rstart = rstart*10 + (size_t)(c[0] - 48);
-
-        /* Get range end from regex */
-        size_t rend = 0;
-        for(char *c=i + match[2].rm_so; c < i + match[2].rm_eo; c++)
-            rend = rend*10 + (size_t)(c[0] - 48);
-
-        i += match[0].rm_eo + 1;
-        zck_log(ZCK_LOG_DEBUG, "Download range: %lu-%lu\n", rstart, rend);
-        mp->length = rend-rstart+1;
-        mp->state = 1;
-    }
-end:
-    if(alloc_buf)
-        free(buf);
-    return l;
-}
-
-static size_t get_header(char *b, size_t l, size_t c, void *dl_v) {
-    if(dl_v == NULL)
-        return 0;
-    zckDL *dl = (zckDL*)dl_v;
-
-    if(dl->priv == NULL)
-        return 0;
-
-    /* Create regex to find boundary */
-    if(dl->priv->hdr_regex == NULL) {
-        char *regex = "boundary *= *([0-9a-fA-F]+)";
-        dl->priv->hdr_regex = zmalloc(sizeof(regex_t));
-        if(!create_regex(dl->priv->hdr_regex, regex))
-            return 0;
-    }
-
-    /* Copy buffer to null-terminated string because POSIX regex requires null-
-     * terminated string */
-    size_t size = l*c;
-    char *buf = zmalloc(size+1);
-    if(buf == NULL) {
-        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for header\n",
-                size+1);
-        return 0;
-    }
-    buf[size] = '\0';
-    memcpy(buf, b, size);
-
-    /* Check whether this header contains the boundary and set it if it does */
-    regmatch_t match[2] = {0};
-    if(regexec(dl->priv->hdr_regex, buf, 2, match, 0) == 0) {
-        char *boundary = zmalloc(match[1].rm_eo - match[1].rm_so + 1);
-        memcpy(boundary, buf + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
-        zck_log(ZCK_LOG_DEBUG, "Multipart boundary: %s\n", boundary);
-        dl->boundary = boundary;
-    }
-    if(buf)
-        free(buf);
-    return l*c;
-}
-
 static size_t write_data(void *ptr, size_t l, size_t c, void *dl_v) {
     if(dl_v == NULL)
         return 0;
@@ -424,7 +195,13 @@ static size_t write_data(void *ptr, size_t l, size_t c, void *dl_v) {
     size_t wb = 0;
     dl->dl += l*c;
     if(dl->boundary != NULL) {
-        int retval = extract_multipart(ptr, l*c, dl_v);
+        int retval = zck_multipart_extract(dl, ptr, l*c);
+        if(retval == 0)
+            wb = 0;
+        else
+            wb = l*c;
+    } else if(dl->priv->is_chunk) {
+        int retval = zck_dl_write_range(dl, ptr, l*c);
         if(retval == 0)
             wb = 0;
         else
@@ -510,7 +287,15 @@ int zck_dl_copy_src_chunks(zckRangeInfo *info, zckCtx *src, zckCtx *tgt) {
     return True;
 }
 
-int zck_dl_range(zckDL *dl, char *url) {
+static size_t get_header(char *b, size_t l, size_t c, void *dl_v) {
+    if(dl_v == NULL)
+        return 0;
+    zckDL *dl = (zckDL*)dl_v;
+
+    return zck_multipart_get_boundary(dl, b, c*l);
+}
+
+int zck_dl_range(zckDL *dl, char *url, int is_chunk) {
     if(dl == NULL || dl->priv == NULL || dl->info.first == NULL) {
         zck_log(ZCK_LOG_ERROR, "Struct not defined\n");
         return False;
@@ -521,6 +306,7 @@ int zck_dl_range(zckDL *dl, char *url) {
     }
     if(dl->info.segments == 0)
         dl->info.segments = 1;
+    dl->priv->is_chunk = is_chunk;
 
     char **ra = calloc(sizeof(char*), dl->info.segments);
     if(!zck_range_get_array(&(dl->info), ra)) {
@@ -531,6 +317,13 @@ int zck_dl_range(zckDL *dl, char *url) {
 
     for(int i=0; i<dl->info.segments; i++) {
         struct curl_slist *header = NULL;
+
+        if(dl->priv->dl_regex != NULL)
+            zck_dl_free_dl_regex(dl);
+        if(dl->boundary != NULL)
+            free(dl->boundary);
+
+        zck_log(ZCK_LOG_DEBUG, "%s\n", ra[i]);
         header = curl_slist_append(header, ra[i]);
         curl_easy_setopt(dl->priv->curl_ctx, CURLOPT_URL, url);
         curl_easy_setopt(dl->priv->curl_ctx, CURLOPT_FOLLOWLOCATION, 1L);
@@ -555,6 +348,7 @@ int zck_dl_range(zckDL *dl, char *url) {
                     url);
             return False;
         }
+        zck_dl_free_regex(dl);
     }
     free(ra);
     return True;
@@ -580,7 +374,7 @@ int zck_dl_bytes(zckDL *dl, char *url, size_t bytes, size_t start,
         idx.length = start+bytes-*buffer_len;
         zck_range_close(&(dl->info));
         zck_range_add(&(dl->info), &idx, NULL);
-        if(!zck_dl_range(dl, url))
+        if(!zck_dl_range(dl, url, 0))
             return False;
         zck_range_close(&(dl->info));
         *buffer_len = start+bytes;
@@ -626,6 +420,7 @@ int zck_zero_bytes(zckDL *dl, size_t bytes, size_t start, size_t *buffer_len) {
     return True;
 }
 
+/* Download header */
 int zck_dl_get_header(zckCtx *zck, zckDL *dl, char *url) {
     size_t buffer_len = 0;
     size_t start = 0;
@@ -692,3 +487,68 @@ void zck_dl_global_init() {
 void zck_dl_global_cleanup() {
     curl_global_cleanup();
 }
+
+/* Free zckDL header regex used for downloading ranges */
+void zck_dl_free_regex(zckDL *dl) {
+    if(dl == NULL || dl->priv == NULL)
+        return;
+
+    zck_dl_free_dl_regex(dl);
+    if(dl->priv->hdr_regex) {
+        regfree(dl->priv->hdr_regex);
+        free(dl->priv->hdr_regex);
+        dl->priv->hdr_regex = NULL;
+    }
+}
+
+/* Initialize zckDL.  When finished, zckDL *must* be freed by zck_dl_free() */
+zckDL *zck_dl_init() {
+    zckDL *dl = zmalloc(sizeof(zckDL));
+    if(!dl) {
+        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for zckDL\n",
+                sizeof(zckDL));
+        return NULL;
+    }
+    dl->priv = zmalloc(sizeof(zckDLPriv));
+    if(!dl->priv) {
+        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for dl->priv\n",
+                sizeof(zckDL));
+        return NULL;
+    }
+    dl->priv->mp = zmalloc(sizeof(zckMP));
+    if(!dl->priv->mp) {
+        zck_log(ZCK_LOG_ERROR,
+                "Unable to allocate %lu bytes for dl->priv->mp\n",
+                sizeof(zckMP));
+        return NULL;
+    }
+    dl->priv->curl_ctx = curl_easy_init();
+    if(!dl->priv->curl_ctx) {
+        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for dl->curl_ctx\n",
+                sizeof(CURL));
+        return NULL;
+    }
+    return dl;
+}
+
+/* Free zckDL and set pointer to NULL */
+void zck_dl_free(zckDL **dl) {
+    if(!*dl)
+        return;
+    if((*dl)->priv) {
+        if((*dl)->priv->mp) {
+            if((*dl)->priv->mp->buffer)
+                free((*dl)->priv->mp->buffer);
+            free((*dl)->priv->mp);
+        }
+        zck_dl_free_regex(*dl);
+        curl_easy_cleanup((*dl)->priv->curl_ctx);
+        free((*dl)->priv);
+    }
+    if((*dl)->info.first)
+        zck_range_close(&((*dl)->info));
+    if((*dl)->boundary)
+        free((*dl)->boundary);
+    free(*dl);
+    *dl = NULL;
+}
index 07a43e704511508be36503533d88d7560194ebb4..3e302bee9469e279c912ee78c95527168c8976e9 100644 (file)
@@ -1 +1 @@
-sources += ['dl/range.c', 'dl/dl.c']
+sources += ['dl/range.c', 'dl/dl.c', 'dl/multipart.c']
diff --git a/src/lib/dl/multipart.c b/src/lib/dl/multipart.c
new file mode 100644 (file)
index 0000000..09fd4de
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2018 Jonathan Dieter <jdieter@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <regex.h>
+#include <zck.h>
+
+#include "zck_private.h"
+
+#define VALIDATE(f)     if(!f) { \
+                            zck_log(ZCK_LOG_ERROR, "zckDL not allocated\n"); \
+                            return False; \
+                        }
+
+static char *add_boundary_to_regex(const char *regex, const char *boundary) {
+    if(regex == NULL || boundary == NULL)
+        return NULL;
+    char *regex_b = zmalloc(strlen(regex) + strlen(boundary) + 1);
+    if(regex_b == NULL) {
+        zck_log(ZCK_LOG_ERROR,
+                "Unable to reallocate %lu bytes for regular expression\n",
+                strlen(regex) + strlen(boundary) - 2);
+        return NULL;
+    }
+    if(snprintf(regex_b, strlen(regex) + strlen(boundary), regex,
+                boundary) != strlen(regex) + strlen(boundary) - 2) {
+        free(regex_b);
+        zck_log(ZCK_LOG_ERROR, "Unable to build regular expression\n");
+        return NULL;
+    }
+    return regex_b;
+}
+
+static int create_regex(regex_t *reg, const char *regex) {
+    if(reg == NULL || regex == NULL) {
+        zck_log(ZCK_LOG_ERROR, "Regular expression not initialized\n");
+        return False;
+    }
+    if(regcomp(reg, regex, REG_ICASE | REG_EXTENDED) != 0) {
+        zck_log(ZCK_LOG_ERROR, "Unable to compile regular expression\n");
+        return False;
+    }
+    return True;
+}
+
+size_t zck_multipart_extract(zckDL *dl, char *b, size_t l) {
+    VALIDATE(dl);
+    if(dl->priv == NULL || dl->priv->mp == NULL)
+        return 0;
+    zckMP *mp = dl->priv->mp;
+    char *buf = b;
+    int alloc_buf = False;
+
+    /* Add new data to stored buffer */
+    if(mp->buffer) {
+        buf = realloc(mp->buffer, mp->buffer_len + l);
+        if(buf == NULL) {
+            zck_log(ZCK_LOG_ERROR, "Unable to reallocate %lu bytes for zckDL\n",
+                    mp->buffer_len + l);
+            return 0;
+        }
+        memcpy(buf + mp->buffer_len, b, l);
+        l = mp->buffer_len + l;
+        mp->buffer = NULL;  // No need to free, buf holds realloc'd buffer
+        mp->buffer_len = 0;
+        alloc_buf = True;
+    }
+
+    /* If regex hasn't been created, create it */
+    if(dl->priv->dl_regex == NULL) {
+        char *next = "\r\n--%s\r\ncontent-type:.*\r\n" \
+                     "content-range: *bytes *([0-9]+) *- *([0-9]+) */.*\r\n\r";
+        char *end =  "\r\n--%s--\r\n\r";
+        char *regex_n = add_boundary_to_regex(next, dl->boundary);
+        if(regex_n == NULL)
+            return 0;
+        char *regex_e = add_boundary_to_regex(end, dl->boundary);
+        if(regex_n == NULL)
+            return 0;
+        dl->priv->dl_regex = zmalloc(sizeof(regex_t));
+        if(!create_regex(dl->priv->dl_regex, regex_n)) {
+            free(regex_n);
+            return 0;
+        }
+        free(regex_n);
+        dl->priv->end_regex = zmalloc(sizeof(regex_t));
+        if(!create_regex(dl->priv->end_regex, regex_e)) {
+            free(regex_e);
+            return 0;
+        }
+        free(regex_e);
+    }
+
+    char *header_start = buf;
+    char *i = buf;
+    while(i) {
+        char *end = buf + l;
+        /* If we're in data writing state, then write data until end of buffer
+         * or end of range, whichever comes first */
+        if(mp->state != 0) {
+            if(i >= end)
+                break;
+            size_t size = end - i;
+            if(mp->length <= size) {
+                size = mp->length;
+                mp->length = 0;
+                mp->state = 0;
+                header_start = i + size;
+            } else {
+                mp->length -= size;
+            }
+            if(zck_dl_write_range(dl, i, size) != size)
+                return 0;
+            i += size;
+            continue;
+        }
+
+        /* If we've reached the end of the buffer without finishing, save it
+         * and leave loop */
+        if(i >= end) {
+            size_t size = buf + l - header_start;
+            if(size > 0) {
+                mp->buffer = malloc(size);
+                memcpy(mp->buffer, header_start, size);
+                mp->buffer_len = size;
+            }
+            break;
+        }
+
+        /* Find double newline and replace final \n with \0, so it's a zero-
+         * terminated string */
+        for(char *j=i; j<end; j++) {
+            if(j + 4 >= end) {
+                i = j+4;
+                break;
+            }
+            if(memcmp(j, "\r\n\r\n", 4) == 0) {
+                j[3] = '\0';
+                break;
+            }
+        }
+        if(i >= end)
+            continue;
+
+        /* Run regex against download range string */
+        regmatch_t match[4] = {0};
+        if(regexec(dl->priv->dl_regex, i, 3, match, 0) != 0) {
+            if(regexec(dl->priv->end_regex, i, 3, match, 0) != 0)
+                zck_log(ZCK_LOG_ERROR, "Unable to find multipart download range\n");
+            goto end;
+        }
+
+        /* Get range start from regex */
+        size_t rstart = 0;
+        for(char *c=i + match[1].rm_so; c < i + match[1].rm_eo; c++)
+            rstart = rstart*10 + (size_t)(c[0] - 48);
+
+        /* Get range end from regex */
+        size_t rend = 0;
+        for(char *c=i + match[2].rm_so; c < i + match[2].rm_eo; c++)
+            rend = rend*10 + (size_t)(c[0] - 48);
+
+        i += match[0].rm_eo + 1;
+        zck_log(ZCK_LOG_DEBUG, "Download range: %lu-%lu\n", rstart, rend);
+        mp->length = rend-rstart+1;
+        mp->state = 1;
+    }
+end:
+    if(alloc_buf)
+        free(buf);
+    return l;
+}
+
+static void reset_mp(zckMP *mp) {
+    if(mp->buffer)
+        free(mp->buffer);
+    memset(mp, 0, sizeof(zckMP));
+}
+
+size_t zck_multipart_get_boundary(zckDL *dl, char *b, size_t size) {
+    VALIDATE(dl);
+    if(dl->priv == NULL)
+        return 0;
+
+    /* Create regex to find boundary */
+    if(dl->priv->hdr_regex == NULL) {
+        char *regex = "boundary *= *([0-9a-fA-F]+)";
+        dl->priv->hdr_regex = zmalloc(sizeof(regex_t));
+        if(!create_regex(dl->priv->hdr_regex, regex))
+            return 0;
+    }
+
+    /* Copy buffer to null-terminated string because POSIX regex requires null-
+     * terminated string */
+    char *buf = zmalloc(size+1);
+    if(buf == NULL) {
+        zck_log(ZCK_LOG_ERROR, "Unable to allocate %lu bytes for header\n",
+                size+1);
+        return 0;
+    }
+    buf[size] = '\0';
+    memcpy(buf, b, size);
+
+    /* Check whether this header contains the boundary and set it if it does */
+    regmatch_t match[2] = {0};
+    if(regexec(dl->priv->hdr_regex, buf, 2, match, 0) == 0) {
+        reset_mp(dl->priv->mp);
+        char *boundary = zmalloc(match[1].rm_eo - match[1].rm_so + 1);
+        memcpy(boundary, buf + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
+        zck_log(ZCK_LOG_DEBUG, "Multipart boundary: %s\n", boundary);
+        dl->boundary = boundary;
+    }
+    if(buf)
+        free(buf);
+    return size;
+}
index 7246bb39300ebef03faa2d8a5ae96bae77334c54..0b736752fb3576030c760ee555ec1ef887f5684a 100644 (file)
@@ -47,7 +47,9 @@ typedef struct zckDLPriv {
     CURL *curl_ctx;
     zckMP *mp;
     int parser_started;
+    int is_chunk;
     regex_t *dl_regex;
+    regex_t *end_regex;
     regex_t *hdr_regex;
 } zckDLPriv;
 
@@ -136,6 +138,13 @@ int zck_write_header(zckCtx *zck);
 char *zck_range_get_char(zckRange **range, int max_ranges);
 int zck_range_add(zckRangeInfo *info, zckIndex *idx, zckCtx *zck);
 
+/* dl/multipart.c */
+size_t zck_multipart_extract(zckDL *dl, char *b, size_t l);
+size_t zck_multipart_get_boundary(zckDL *dl, char *b, size_t size);
+
+/* dl/dl.c */
+int zck_dl_write_range(zckDL *dl, const char *at, size_t length);
+
 /* log.c */
 void zck_log(log_type lt, const char *format, ...);
 #endif
index 614b13cef71d14cb8368d52c2492149ad999ed5e..c0bd70317429b5cedb1f4e06b703d76e9a210a91 100644 (file)
@@ -81,7 +81,7 @@ int main (int argc, char *argv[]) {
         exit(1);
 
     lseek(dl->dst_fd, 0, SEEK_SET);
-    if(!zck_dl_range(dl, argv[2]))
+    if(!zck_dl_range(dl, argv[2], 1))
         exit(1);
 
     /*
@@ -107,7 +107,7 @@ int main (int argc, char *argv[]) {
         default:
             break;
     }
-    zck_dl_free(dl);
+    zck_dl_free(&dl);
     zck_free(zck_tgt);
     zck_free(zck_src);
     zck_dl_global_cleanup();