int32_t S3_HLS_Client_Upload_Buffer()

in S3_HLS_S3_Put_Client.c [624:861]


int32_t S3_HLS_Client_Upload_Buffer(S3_HLS_CLIENT_CTX* ctx, char* object_key, uint8_t* first_data, uint32_t first_length, uint8_t* second_data, uint32_t second_length) {
    uint8_t retry_flag = 0;

    PUT_DEBUG("Upload start!\n");
    PUT_DEBUG("Validate parameters\n");
    if(NULL == ctx || NULL == object_key || NULL == first_data)
        return S3_HLS_INVALID_PARAMETER;
        
    if(0 == strlen(object_key))
        return S3_HLS_INVALID_PARAMETER;
        
    if('/' != object_key[0])
        return S3_HLS_INVALID_PARAMETER;
        
    if(NULL == second_data && 0 != second_length)
        return S3_HLS_INVALID_PARAMETER;
        
    PUT_DEBUG("Format date and timestamp!\n");

    time_t current_time;
    time(&current_time);
    struct tm* time_tm = gmtime(&current_time);
    
    int32_t length = sprintf(ctx->date_buffer, S3_HLS_DATE_FORMAT, time_tm->tm_year + 1900, time_tm->tm_mon + 1, time_tm->tm_mday);
    if(0 >= length)
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;
        
    length = sprintf(ctx->timestamp_buffer, S3_HLS_TIMESTAMP_HEADER_FORMAT, time_tm->tm_year + 1900, time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
    if(0 >= length)
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;
        
    PUT_DEBUG("Get Content Hash\n");

    S3_SHA256_CTX sha256_ctx;
    S3_SHA256_Init(&sha256_ctx);
    S3_SHA256_Update(&sha256_ctx, first_data, first_length);
    if(NULL != second_data)
        S3_SHA256_Update(&sha256_ctx, second_data, second_length);

    S3_SHA256_HASH payload_hash;
    S3_SHA256_Final(&sha256_ctx, payload_hash);
    
    char payload_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH + 1];
    for(uint8_t i = 0; i < sizeof(payload_hash); i++) {
        if(0 >= sprintf(payload_hash_string + (i * 2), "%02x", payload_hash[i]))
            return S3_HLS_UNKNOWN_INTERNAL_ERROR;
    }
    
    payload_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH] = '\0';

    PUT_DEBUG("Generate content_hash header!\n");
    if(0 >= sprintf(ctx->content_hash, S3_HLS_CONTENT_SHA256_HEADER_FORMAT, payload_hash_string))
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;

    S3_SHA256_HASH canonical_hash;
    S3_HLS_Hash_Put_Canonical_Request(ctx, object_key, canonical_hash);

    char canonical_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH + 1];
    for(uint8_t i = 0; i < S3_SHA256_DIGEST_LENGTH; i++) {
        if(0 >= sprintf(canonical_hash_string + (i * 2), "%02x", canonical_hash[i])) {
            return S3_HLS_UNKNOWN_INTERNAL_ERROR;
        }
    }
    
    canonical_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH] = '\0'; // add null terminator

    if(0 >= sprintf(    ctx->string_to_sign,
                        S3_HLS_STRING_TO_SIGN_FORMAT,
                        ctx->timestamp_buffer + S3_HLS_TIMESTAMP_VALUE_OFFSET,
                        ctx->date_buffer,
                        ctx->region,
                        canonical_hash_string
                    )) {
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;
    }
    
    if(0 != pthread_mutex_lock(&ctx->credential_lock))
        return S3_HLS_LOCK_FAILED;

    S3_SHA256_HASH date_key;
    S3_HMAC_SHA256(
                    ctx->secret_access_key, 
                    strlen(ctx->secret_access_key),
                    ctx->date_buffer,
                    strlen(ctx->date_buffer),
                    date_key
                    );

    S3_SHA256_HASH region_key;
    S3_HMAC_SHA256(date_key, SHA256_DIGEST_LENGTH, ctx->region, strlen(ctx->region), region_key);

    S3_SHA256_HASH service_key;
    S3_HMAC_SHA256(region_key, SHA256_DIGEST_LENGTH, S3_SERVICE_KEY, strlen(S3_SERVICE_KEY), service_key);

    S3_SHA256_HASH signing_key;
    S3_HMAC_SHA256(service_key, SHA256_DIGEST_LENGTH, AWS_SIGV4_REQUEST, strlen(AWS_SIGV4_REQUEST), signing_key);

    PUT_DEBUG("String To Sign: \n%s\n", ctx->string_to_sign);
    S3_SHA256_HASH signature;
    S3_HMAC_SHA256(signing_key, SHA256_DIGEST_LENGTH, ctx->string_to_sign, strlen(ctx->string_to_sign), signature);

    char signature_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH + 1];
    for(uint8_t i = 0; i < S3_SHA256_DIGEST_LENGTH; i++) {
        if(0 >= sprintf(signature_hash_string + (i * 2), "%02x", signature[i])) {
            pthread_mutex_unlock(&ctx->credential_lock);
            return S3_HLS_UNKNOWN_INTERNAL_ERROR;
        }
    }
    
    signature_hash_string[S3_HLS_HEX_HASH_STIRNG_LENGTH] = '\0';
    
    PUT_DEBUG("String signed!\n");
    PUT_DEBUG("Auth header address: %ld!\n", ctx->auth_header);
    length = sprintf(
                        ctx->auth_header,
                        S3_HLS_AUTHENTICATION_HEADER_FORMAT,
                        ctx->access_key,
                        ctx->date_buffer,
                        ctx->region,
                        NULL != ctx->token_header ? S3_HLS_TOKEN_HEADER_IN_CANONICAL_REQUEST : S3_HLS_EMPTY_STRING,
                        NULL == ctx->tag_header ? S3_HLS_EMPTY_STRING : S3_HLS_TAG_HEADER_IN_CANONICAL_REQUEST,
                        signature_hash_string
                    );

    pthread_mutex_unlock(&ctx->credential_lock);
    
    if(0 >= length)
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;

    PUT_DEBUG("Auth header: \n%s\n", ctx->auth_header);

    length = sprintf(ctx->uri, S3_HLS_HTTPS_URI_FORMAT, ctx->endpoint, object_key);
    if(0 >= length)
        return S3_HLS_UNKNOWN_INTERNAL_ERROR;

    /* get a curl handle */
    printf("Start Upload!\n");
    if(NULL == ctx->curl) {
l_retry_entry:
        ctx->curl = curl_easy_init();
        if(NULL == ctx->curl) {
            fprintf(stderr, "curl_easy_init() failed!\n");
            
            return S3_HLS_HTTP_CLIENT_INIT_ERROR;
        }
        
        curl_easy_setopt(ctx->curl, CURLOPT_READFUNCTION, S3_HLS_Upload_Data);
    }

    S3_HLS_UPLOAD_CTX upload_ctx;
    upload_ctx.first_part_start = first_data;
    upload_ctx.first_part_length = first_length;
    
    upload_ctx.second_part_start = second_data;
    upload_ctx.second_part_length = second_length;
    
    upload_ctx.pos = 0;

    // adding headers
    struct curl_slist *headers = NULL;

    // set upload methods
    curl_easy_setopt(ctx->curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(ctx->curl, CURLOPT_PUT, 1L);
    
    /* First set the URL that is about to receive our POST. */ 
    curl_easy_setopt(ctx->curl, CURLOPT_URL, ctx->uri);

    PUT_DEBUG("Upload CTX: %ld\n", &upload_ctx);
    curl_easy_setopt(ctx->curl, CURLOPT_READDATA, &upload_ctx);

    uint32_t payload_length = first_length + second_length;
    curl_easy_setopt(ctx->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)payload_length);

    /* enable TCP keep-alive for this transfer */
    curl_easy_setopt(ctx->curl, CURLOPT_TCP_KEEPALIVE, 1L);
    /* keep-alive idle time to 120 seconds */
    curl_easy_setopt(ctx->curl, CURLOPT_TCP_KEEPIDLE, 180L);
    /* interval time between keep-alive probes: 60 seconds */
    curl_easy_setopt(ctx->curl, CURLOPT_TCP_KEEPINTVL, 60L);
    
    curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT, S3_HLS_CURL_TRANSFER_TIMEOUT);

    curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT, S3_HLS_CURL_CONNECTION_TIMEOUT);

    curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYSTATUS, 0);     
    curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYPEER, 0);
    
    headers = curl_slist_append(headers, ctx->content_hash);
    headers = curl_slist_append(headers, ctx->timestamp_buffer);
    headers = curl_slist_append(headers, "Expect:");
    headers = curl_slist_append(headers, "Accept:");

    if(NULL != ctx->token_header) {
        headers = curl_slist_append(headers, ctx->token_header);
    }
    
    if(NULL != ctx->tag_header) {
        headers = curl_slist_append(headers, ctx->tag_header);
    }

    PUT_DEBUG("Auth Header: %s\n", ctx->auth_header);
    headers = curl_slist_append(headers, ctx->auth_header);

    /* Now specify we want to POST data */ 
    curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, headers);
    
    /* get verbose debug output please */ 
    curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L);
    
    PUT_DEBUG("Start Put!\n");
    /* Perform the request, res will get the return code */ 
    CURLcode res = curl_easy_perform(ctx->curl);
    PUT_DEBUG("Put Done!\n");
    
    curl_slist_free_all(headers);

    /* Check for errors */ 
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
    
        printf("Error, clean up curl!\n");
        curl_easy_cleanup(ctx->curl);
        
        ctx->curl = NULL;
        printf("Curl cleaned!\n");
        
        if(!retry_flag) {
            retry_flag = 1;
            goto l_retry_entry;
        }
        
        return S3_HLS_UPLOAD_FAILED;
    }

    return S3_HLS_OK;
}