2 * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 #include <tpkp_curl.h>
25 #include "download-agent-dl-info.h"
26 #include "download-agent-http-msg-handler.h"
27 #include "download-agent-plugin-libcurl.h"
29 da_bool_t using_content_sniffing = DA_FALSE;
31 int __translate_error_code(int curl_error)
34 case CURLE_OPERATION_TIMEDOUT:
35 return DA_ERR_HTTP_TIMEOUT;
36 case CURLE_SSL_CONNECT_ERROR:
37 case CURLE_SSL_ENGINE_NOTFOUND:
38 case CURLE_SSL_ENGINE_SETFAILED:
39 case CURLE_SSL_CERTPROBLEM:
40 case CURLE_SSL_CIPHER:
41 case CURLE_SSL_CACERT:
42 case CURLE_SSL_ENGINE_INITFAILED:
43 case CURLE_SSL_CACERT_BADFILE:
45 case CURLE_SSL_SHUTDOWN_FAILED:
46 case CURLE_SSL_CRL_BADFILE:
47 case CURLE_SSL_ISSUER_ERROR:
48 return DA_ERR_SSL_FAIL;
49 case CURLE_TOO_MANY_REDIRECTS:
50 return DA_ERR_TOO_MANY_REDIRECTS;
51 case CURLE_OUT_OF_MEMORY:
52 return DA_ERR_FAIL_TO_MEMALLOC;
53 case CURLE_UNSUPPORTED_PROTOCOL:
54 case CURLE_URL_MALFORMAT:
55 case CURLE_COULDNT_RESOLVE_PROXY:
56 case CURLE_COULDNT_RESOLVE_HOST:
57 case CURLE_COULDNT_CONNECT:
58 case CURLE_REMOTE_ACCESS_DENIED:
59 case CURLE_HTTP_POST_ERROR:
60 case CURLE_BAD_DOWNLOAD_RESUME:
61 return DA_ERR_CONNECTION_FAIL;
62 case CURLE_ABORTED_BY_CALLBACK:
63 return DA_RESULT_USER_CANCELED;
65 return DA_ERR_NETWORK_FAIL;
69 int my_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *user)
74 DA_SECURE_LOGI("[curl] Info:%s", data);
76 case CURLINFO_HEADER_OUT:
77 DA_LOGD("[curl] Send header");
79 DA_SECURE_LOGI("[curl] %s", data);
81 case CURLINFO_DATA_OUT:
82 DA_LOGD("[curl] Send data");
84 DA_SECURE_LOGI("[curl] %s", data);
86 case CURLINFO_SSL_DATA_OUT:
87 DA_LOGD("[curl] Send SSL data");
89 case CURLINFO_HEADER_IN:
90 DA_LOGD("[curl] Recv header");
92 DA_SECURE_LOGI("[curl] %s", data);
95 case CURLINFO_DATA_IN:
96 DA_LOGD("[curl] Recv data");
98 DA_SECURE_LOGI("[curl] %d", strlen(data));
101 case CURLINFO_SSL_DATA_IN:
102 DA_SECURE_LOGI("[curl] Recv SSL data");
110 void __parse_raw_header(const char *raw_data, http_info_t *http_info)
113 char *ptr2 = DA_NULL;
115 char *field = DA_NULL;
116 char *value = DA_NULL;
117 http_msg_response_t *http_msg_response = NULL;
119 if (!raw_data || !http_info) {
120 DA_LOGE("NULL Check!: raw_data or http_info");
124 if (!http_info->http_msg_response) {
125 http_info->http_msg_response = (http_msg_response_t *)calloc(1,
126 sizeof(http_msg_response_t));
127 if (!http_info->http_msg_response) {
128 DA_LOGE("Fail to calloc");
131 http_info->http_msg_response->head = DA_NULL;
133 http_msg_response = http_info->http_msg_response;
135 ptr = strchr(raw_data, ':');
138 len = ptr - (char *)raw_data;
139 field = (char *)calloc(len + 1, sizeof(char));
141 DA_LOGE("Fail to calloc");
144 memcpy(field, raw_data, len);
153 ptr2 = strchr(raw_data, '\n');
159 value = (char *)calloc(len + 1, sizeof(char));
161 DA_LOGE("Fail to calloc");
165 memcpy(value, ptr, len);
167 http_msg_response_add_field(http_msg_response, field, value);
172 void __store_header(void *msg, da_info_t *da_info, size_t header_size,
173 const char *sniffed_type)
175 http_info_t *http_info = DA_NULL;
177 if (!da_info || !msg) {
178 DA_LOGE("NULL Check!: da_info or msg");
181 http_info = da_info->http_info;
183 DA_LOGE("NULL Check!: http_info");
187 // FIXME later : check status code and redirection case check.
189 if (strncmp(msg, HTTP_FIELD_END_OF_FIELD,
190 strlen(HTTP_FIELD_END_OF_FIELD)) == 0) {
194 http_raw_data_t *raw_data = DA_NULL;
195 curl = http_info->http_msg->curl;
196 res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
197 if (res != CURLE_OK) {
198 DA_LOGE("Fail to get response status code");
201 DA_LOGV("status code[%d]", (int)status);
202 if (http_info->http_msg_response) {
203 http_info->http_msg_response->status_code = (int)status;
205 raw_data = (http_raw_data_t *)calloc(1, sizeof(http_raw_data_t));
207 DA_LOGE("Fail to calloc");
211 raw_data->status_code = (int)status;
212 raw_data->type = HTTP_EVENT_GOT_HEADER;
214 if (http_info->update_cb) {
215 http_info->update_cb(raw_data, da_info);
221 DA_LOGI("%s",(char *)msg);
222 __parse_raw_header((const char *)msg, http_info);
225 size_t __http_gotheaders_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
227 da_info_t *da_info = DA_NULL;
228 if (!ptr || !userdata) {
229 DA_LOGE("Check NULL!: ptr, userdata");
232 da_info = (da_info_t *)userdata;
233 if (da_info->http_info && da_info->http_info->http_msg
234 && da_info->http_info->http_msg->is_cancel_reqeusted) {
235 DA_LOGI("Cancel requested");
238 if (!using_content_sniffing)
239 __store_header(ptr, da_info, (size * nmemb), DA_NULL);
241 DA_LOGV("ignore because content sniffing is turned on");
244 DA_LOGI("[RAF] __http_gotheaders_cb done");
247 return (size * nmemb);
251 da_ret_t PI_http_set_file_name_to_curl(http_msg_t *http_msg, char *file_path)
253 NULL_CHECK_RET(http_msg);
254 NULL_CHECK_RET(file_path);
255 DA_LOGI("[RAF]set file_path[%s]", file_path);
256 curl_easy_setopt(http_msg->curl, CURLOPT_BOOSTER_RAF_FILE, file_path);
261 size_t __http_gotchunk_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
263 http_info_t *http_info = DA_NULL;
264 da_info_t *da_info = DA_NULL;
265 http_raw_data_t *raw_data = DA_NULL;
266 if (!ptr || !userdata) {
267 DA_LOGE("Check NULL!: ptr, stream");
270 da_info = (da_info_t *)userdata;
271 NULL_CHECK_RET_OPT(da_info, 0);
272 http_info = da_info->http_info;
273 NULL_CHECK_RET_OPT(http_info, 0);
274 NULL_CHECK_RET_OPT(http_info->http_msg, 0);
275 if (da_info->http_info->http_msg->is_cancel_reqeusted) {
276 DA_LOGI("Cancel requested");
279 //DA_LOGV("size=%ld, nmemb=%ld, datalen=%ld", size, nmemb, strlen((const char *)ptr));
281 //DA_LOGI("size=%ld, nmemb=%ld, datalen=%ld", size, nmemb, strlen((const char *)ptr));
282 if (http_info->is_raf_mode_confirmed) {
283 DA_LOGI("[RAF] return chunked callback");
284 return (size * nmemb);
288 if (ptr && size * nmemb > 0) {
289 if (http_info->update_cb) {
290 raw_data = (http_raw_data_t *)calloc(1, sizeof(http_raw_data_t));
292 DA_LOGE("Fail to calloc");
295 raw_data->body = (char *)calloc(size, nmemb);
296 if (!(raw_data->body)) {
297 DA_LOGE("Fail to calloc");
301 memcpy(raw_data->body, ptr, size * nmemb);
302 raw_data->body_len = size*nmemb;
303 raw_data->type = HTTP_EVENT_GOT_PACKET;
304 http_info->update_cb(raw_data, da_info);
307 return (size * nmemb);
310 long __http_finished_cb(void *ptr)
313 DA_LOGE("Check NULL!: ptr");
314 return CURL_CHUNK_END_FUNC_FAIL;
317 return CURL_CHUNK_END_FUNC_OK;
321 da_ret_t __set_proxy_on_soup_session(char *proxy_addr, CURL *curl)
323 da_ret_t ret = DA_RESULT_OK;
325 if (proxy_addr && strlen(proxy_addr) > 0) {
326 DA_SECURE_LOGI("received proxy[%s]", proxy_addr);
327 if (!strstr(proxy_addr, "0.0.0.0")) {
328 if (strstr((const char *)proxy_addr, "http") == DA_NULL) {
329 char *tmp_str = DA_NULL;
332 needed_len = strlen(proxy_addr) + strlen(
334 tmp_str = (char *) calloc(1, needed_len);
336 DA_LOGE("DA_ERR_FAIL_TO_MEMALLOC");
337 ret = DA_ERR_FAIL_TO_MEMALLOC;
340 snprintf(tmp_str, needed_len, "%s%s",
341 SCHEME_HTTP, proxy_addr);
343 curl_easy_setopt(curl, CURLOPT_PROXY, proxy_addr);
347 DA_LOGV("There is \"http\" on uri, so, push this address to soup directly.");
348 curl_easy_setopt(curl, CURLOPT_PROXY, proxy_addr);
356 struct curl_slist *__fill_soup_msg_header(CURL *curl, http_info_t *info)
358 http_msg_request_t *input_http_msg_request;
359 struct curl_slist *headers = DA_NULL;
362 DA_LOGE("NULL Check!: curl");
365 input_http_msg_request = info->http_msg_request;
367 if (input_http_msg_request) {
368 char *field = DA_NULL;
369 char *value = DA_NULL;
370 char *buff = DA_NULL;
372 http_header_t *cur = DA_NULL;
373 cur = input_http_msg_request->head;
377 if (field && value) {
378 len = strlen(field) + strlen(value) + 1;
379 buff = (char *)calloc(len + 1, sizeof(char));
381 DA_LOGE("Fail to memalloc");
384 // DA_SECURE_LOGI("[%s] %s", field, value);
385 snprintf(buff, len + 1, "%s:%s", field, value);
386 headers = curl_slist_append(headers, (const char *)buff);
393 DA_LOGE("NULL Check!: input_http_msg_request");
396 if (input_http_msg_request->http_body) {
397 char buff[256] = {0,};
398 int body_len = strlen(input_http_msg_request->http_body);
399 snprintf(buff, sizeof(buff), "%s:%d", HTTP_FIELD_CONTENT_LENGTH,
401 headers = curl_slist_append(headers, buff);
402 memset(buff, 0x00, 256);
403 snprintf(buff, sizeof(buff), "%s:text/plain", HTTP_FIELD_CONTENT_TYPE);
404 headers = curl_slist_append(headers, buff);
405 headers = curl_slist_append(headers, input_http_msg_request->http_body);
407 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
412 int __http_progress_cb(void *clientp, double dltotal, double dlnow,
413 double ultotal, double ulnow)
415 da_info_t *da_info = DA_NULL;
416 http_info_t *http_info = DA_NULL;
417 http_raw_data_t *raw_data = DA_NULL;
419 if (dlnow > 0 || ulnow > 0)
420 DA_LOGI("[RAF]dlnow/ulnow[%llu/%llu][%llu,%llu]", (da_size_t)dlnow, (da_size_t)ulnow, (da_size_t)dltotal, (da_size_t)ultotal);
425 DA_LOGI("[RAF]dlnow is zero. Why is this callback called although there is zero size?");
428 NULL_CHECK_RET_OPT(clientp, -1);
429 da_info = (da_info_t *)clientp;
430 http_info = da_info->http_info;
431 NULL_CHECK_RET_OPT(http_info, -1);
432 NULL_CHECK_RET_OPT(http_info->http_msg, -1);
434 if (http_info->http_msg->is_cancel_reqeusted) {
435 DA_LOGI("Cancel requested");
440 if (http_info->update_cb) {
441 raw_data = (http_raw_data_t *)calloc(1, sizeof(http_raw_data_t));
443 DA_LOGE("Fail to calloc");
446 raw_data->received_len = (da_size_t)dlnow;
447 raw_data->type = HTTP_EVENT_GOT_PACKET;
448 http_info->update_cb(raw_data, da_info);
455 da_ret_t PI_http_start(da_info_t *da_info)
457 da_ret_t ret = DA_RESULT_OK;
458 http_method_t http_method;
459 CURL *curl = DA_NULL;
461 http_msg_t *http_msg = DA_NULL;
463 http_info_t *http_info = DA_NULL;
464 long http_status = 0;
465 struct curl_httppost* post = NULL;
466 struct curl_slist *headers = DA_NULL;
467 char err_buffer[CURL_ERROR_SIZE] = {0,};
472 get_smart_bonding_vconf();
474 NULL_CHECK_GOTO(da_info);
475 NULL_CHECK_GOTO(da_info->req_info);
476 url = da_info->req_info->url;
477 NULL_CHECK_GOTO(url);
478 http_info = da_info->http_info;
479 NULL_CHECK_GOTO(http_info);
481 http_method = http_info->http_method;
482 ret = init_http_msg_t(&http_msg);
483 if (ret != DA_RESULT_OK)
485 http_info->http_msg = http_msg;
487 curl_global_init(CURL_GLOBAL_ALL);
488 curl = curl_easy_init();
491 DA_LOGE("Fail to create curl");
492 return DA_ERR_FAIL_TO_MEMALLOC;
494 DA_LOGI("curl[%p]", curl);
496 curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, MAX_SESSION_COUNT);
497 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, MAX_TIMEOUT);
499 __set_proxy_on_soup_session(http_info->proxy_addr, curl);
501 curl_easy_setopt(curl, CURLOPT_URL, url);
502 switch (http_method) {
503 case HTTP_METHOD_GET:
504 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
506 case HTTP_METHOD_POST:
507 // FIXME later : If the post method is supprot, the post data should be set with curl_fromadd
508 curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
509 DA_LOGI("Need more information for post filed");
511 case HTTP_METHOD_HEAD:
512 DA_LOGI("Donnot implement yet");
515 DA_LOGE("Cannot enter here");
519 if (using_content_sniffing) {
524 headers = __fill_soup_msg_header(curl, http_info);
526 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, __http_gotheaders_cb); // can replace to started_cb
527 curl_easy_setopt(curl, CURLOPT_HEADERDATA, da_info); // param .. same with CURLOPT_WRITEHEADER
528 curl_easy_setopt(curl, CURLOPT_HEADER, 0L); // does not include header to body
529 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __http_gotchunk_cb); // can replace to progress_
530 curl_easy_setopt(curl, CURLOPT_WRITEDATA, da_info); // param .. same with CURLOPT_WRITEHEADERcb
531 curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, __http_finished_cb);
532 curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, da_info);
533 #if _ENABLE_LIBCURL_LOG_VERBOSE
534 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
536 curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
538 // curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
539 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buffer);
540 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
541 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
543 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, __http_progress_cb);
544 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, da_info);
545 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
548 if (da_info->req_info->network_bonding) {
549 #ifdef _DOWNLOAD_BOOSTER_SUPPORT
550 DA_LOGI("network bonding enable");
551 curl_easy_setopt(curl, CURLOPT_MULTIRAT_NEEDED, 1L);
554 curl_easy_setopt(curl, CURLOPT_BOOSTER_RAF_MODE, 1L);
557 http_msg->curl = curl;
558 curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, tpkp_curl_ssl_ctx_callback);
559 res = curl_easy_perform(curl);
560 DA_LOGD("perform done! res[%d]",res);
561 if (res != CURLE_OK) {
562 //DA_LOGE("Fail to send data :%d[%s]", res, curl_easy_strerror(res));
563 DA_LOGE("Fail to send data :%d[%s]", res, curl_easy_strerror(res));
564 if (strlen(err_buffer) > 1)
565 DA_LOGE("Fail to error buffer[%s]", err_buffer);
567 res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
568 if (res != CURLE_OK) {
569 //DA_LOGE("Fail to get response code:%d[%s]", res, curl_easy_strerror(res));
570 DA_LOGE("Fail to get response code:%d[%s]", res, curl_easy_strerror(res));
571 ret = DA_ERR_FAIL_TO_MEMALLOC;;
574 DA_LOGD("Response Http Status code[%d]", (int)http_status);
577 if (http_info->update_cb) {
578 http_raw_data_t *raw_data = DA_NULL;
579 raw_data = (http_raw_data_t *)calloc(1, sizeof(http_raw_data_t));
581 DA_LOGE("Fail to calloc");
582 ret = DA_ERR_FAIL_TO_MEMALLOC;
585 if (http_msg->is_cancel_reqeusted ||
586 res == CURLE_ABORTED_BY_CALLBACK) {
587 DA_LOGI("canceled exit. Err[%d]", http_info->error_code);
588 if (http_info->error_code < 0)
589 ret = http_info->error_code;
591 ret = DA_RESULT_USER_CANCELED;
592 } else if ((http_status > 0 && http_status < 100)) {
593 raw_data->error = __translate_error_code(res);
594 ret = DA_ERR_NETWORK_FAIL;
595 } else if (res != CURLE_OK) {
596 raw_data->error = __translate_error_code(res);
597 ret = DA_ERR_NETWORK_FAIL;
599 raw_data->status_code = (int)http_status;
601 raw_data->type = HTTP_EVENT_FINAL;
602 http_info->update_cb(raw_data, da_info);
604 if (DA_NULL != headers)
605 curl_slist_free_all(headers);
606 curl_easy_cleanup(curl);
608 http_msg->curl = DA_NULL;
609 DA_MUTEX_INIT(&(http_msg->mutex), DA_NULL);
616 da_ret_t PI_http_disconnect(http_info_t *info)
618 da_ret_t ret = DA_RESULT_OK;
619 http_msg_t *http_msg = DA_NULL;
622 NULL_CHECK_RET(info);
623 http_msg = info->http_msg;
624 NULL_CHECK_RET(http_msg);
625 DA_LOGV("session [%p]", http_msg->curl);
626 DA_MUTEX_LOCK(&(http_msg->mutex));
627 if (http_msg->is_paused)
628 PI_http_unpause(info);
630 curl_easy_cleanup(http_msg->curl);
632 http_msg->curl = DA_NULL;
633 http_msg->is_paused = DA_FALSE;
634 http_msg->is_cancel_reqeusted = DA_FALSE;
635 DA_MUTEX_UNLOCK(&(http_msg->mutex));
636 DA_MUTEX_DESTROY(&(http_msg->mutex));
637 destroy_http_msg_t(http_msg);
638 info->http_msg = DA_NULL;
642 da_ret_t PI_http_cancel(http_info_t *info)
644 da_ret_t ret = DA_RESULT_OK;
645 http_msg_t *http_msg = DA_NULL;
649 NULL_CHECK_RET(info);
650 http_msg = info->http_msg;
651 NULL_CHECK_RET(http_msg);
652 NULL_CHECK_RET(http_msg->curl);
653 DA_MUTEX_LOCK(&(http_msg->mutex));
654 DA_LOGI("curl[%p]", http_msg->curl);
655 http_msg->is_cancel_reqeusted = DA_TRUE;
656 DA_MUTEX_UNLOCK(&(http_msg->mutex));
657 DA_LOGD("Done - soup cancel");
661 da_ret_t PI_http_pause(http_info_t *info)
663 da_ret_t ret = DA_RESULT_OK;
664 http_msg_t *http_msg = DA_NULL;
665 CURLcode res = CURLE_OK;
668 NULL_CHECK_RET(info);
669 http_msg = info->http_msg;
670 NULL_CHECK_RET(http_msg);
671 DA_LOGD("curl [%p]", http_msg->curl);
672 NULL_CHECK_RET(http_msg->curl);
673 DA_MUTEX_LOCK(&(http_msg->mutex));
674 DA_LOGE("curl_easy_pause call");
675 res = curl_easy_pause(http_msg->curl, CURLPAUSE_ALL);
676 DA_LOGE("curl_easy_pause:%d", res);
677 if (res == CURLE_OK) {
678 http_msg->is_paused = DA_TRUE;
680 ret = DA_ERR_CANNOT_SUSPEND;
682 DA_MUTEX_UNLOCK(&(http_msg->mutex));
686 da_ret_t PI_http_unpause(http_info_t *info)
688 da_ret_t ret = DA_RESULT_OK;
689 http_msg_t *http_msg = DA_NULL;
690 CURLcode res = CURLE_OK;
693 NULL_CHECK_RET(info);
694 http_msg = info->http_msg;
695 DA_LOGV("curl [%p]", http_msg->curl);
696 NULL_CHECK_RET(http_msg->curl);
697 DA_MUTEX_LOCK(&(http_msg->mutex));
698 res = curl_easy_pause(http_msg->curl, CURLPAUSE_CONT);
700 http_msg->is_paused = DA_FALSE;
702 ret = DA_ERR_CANNOT_RESUME;
703 DA_MUTEX_UNLOCK(&(http_msg->mutex));