Tizen 2.1 base
[platform/framework/web/download-provider.git] / agent / download-agent-basic.c
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <stdlib.h>
18
19 #include "download-agent-basic.h"
20 #include "download-agent-debug.h"
21 #include "download-agent-client-mgr.h"
22 #include "download-agent-utils.h"
23 #include "download-agent-http-mgr.h"
24 #include "download-agent-http-misc.h"
25 #include "download-agent-dl-mgr.h"
26 #include "download-agent-pthread.h"
27 #include "download-agent-file.h"
28
29 static void* __thread_start_download(void* data);
30 void __thread_clean_up_handler_for_start_download(void *arg);
31
32 static da_result_t __make_source_info_basic_download(
33                 stage_info *stage,
34                 client_input_t *client_input);
35 static da_result_t __download_content(stage_info *stage);
36
37 da_result_t start_download(const char *url , int *dl_id)
38 {
39         DA_LOG_FUNC_START(Default);
40         return start_download_with_extension(url, dl_id, NULL);
41 }
42
43 da_result_t start_download_with_extension(
44                 const char *url,
45                 int *dl_id,
46                 extension_data_t *extension_data)
47 {
48         da_result_t  ret = DA_RESULT_OK;
49         int slot_id = 0;
50         const char **request_header = DA_NULL;
51         const char *install_path = DA_NULL;
52         const char *file_name = DA_NULL;
53         const char *etag = DA_NULL;
54         const char *temp_file_path = DA_NULL;
55         int request_header_count = 0;
56         void *user_data = DA_NULL;
57         client_input_t *client_input = DA_NULL;
58         client_input_basic_t *client_input_basic = DA_NULL;
59         download_thread_input *thread_info = DA_NULL;
60         pthread_attr_t thread_attr;
61
62         DA_LOG_FUNC_START(Default);
63
64         if (extension_data) {
65                 request_header = extension_data->request_header;
66                 if (extension_data->request_header_count)
67                         request_header_count = extension_data->request_header_count;
68                 install_path = extension_data->install_path;
69                 file_name = extension_data->file_name;
70                 user_data = extension_data->user_data;
71                 etag = extension_data->etag;
72                 temp_file_path = extension_data->temp_file_path;
73         }
74
75         ret = get_available_slot_id(&slot_id);
76         if (DA_RESULT_OK != ret)
77                 return ret;
78
79         *dl_id = GET_DL_ID(slot_id);
80
81         client_input = (client_input_t *)calloc(1, sizeof(client_input_t));
82         if (!client_input) {
83                 DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC");
84                 ret = DA_ERR_FAIL_TO_MEMALLOC;
85                 goto ERR;
86         } else {
87                 client_input->user_data = user_data;
88                 if (install_path) {
89                         int install_path_len = strlen(install_path);
90                         if (install_path[install_path_len-1] == '/')
91                                 install_path_len--;
92
93                         client_input->install_path = (char *)calloc(install_path_len+1, sizeof(char));
94                         if (client_input->install_path)
95                                 strncpy(client_input->install_path, install_path, install_path_len);
96                 }
97
98                 if (file_name) {
99                         client_input->file_name = (char *)calloc(strlen(file_name)+1, sizeof(char));
100                         if (client_input->file_name)
101                                 strncpy(client_input->file_name, file_name, strlen(file_name));
102                 }
103
104                 if (etag) {
105                         client_input->etag = (char *)calloc(strlen(etag)+1, sizeof(char));
106                         if (client_input->etag)
107                                 strncpy(client_input->etag, etag, strlen(etag));
108
109                 }
110
111                 if (temp_file_path) {
112                         client_input->temp_file_path = (char *)calloc(strlen(temp_file_path)+1, sizeof(char));
113                         if (client_input->temp_file_path)
114                                 strncpy(client_input->temp_file_path, temp_file_path, strlen(temp_file_path));
115                 }
116
117                 client_input_basic = &(client_input->client_input_basic);
118                 client_input_basic->req_url = (char *)calloc(strlen(url)+1, sizeof(char));
119                 if(DA_NULL == client_input_basic->req_url) {
120                         DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC");
121                         ret = DA_ERR_FAIL_TO_MEMALLOC;
122                         goto ERR;
123                 }
124                 strncpy(client_input_basic->req_url ,url,strlen(url));
125
126                 if (request_header_count > 0) {
127                         int i = 0;
128                         client_input_basic->user_request_header =
129                                 (char **)calloc(1, sizeof(char *)*request_header_count);
130                         if(DA_NULL == client_input_basic->user_request_header) {
131                                 DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC");
132                                 ret = DA_ERR_FAIL_TO_MEMALLOC;
133                                 goto ERR;
134                         }
135                         for (i = 0; i < request_header_count; i++)
136                         {
137                                 client_input_basic->user_request_header[i] = strdup(request_header[i]);
138                         }
139                         client_input_basic->user_request_header_count = request_header_count;
140                 }
141         }
142
143         thread_info = (download_thread_input *)calloc(1, sizeof(download_thread_input));
144         if (!thread_info) {
145                 DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC");
146                 ret = DA_ERR_FAIL_TO_MEMALLOC;
147                 goto ERR;
148         } else {
149                 thread_info->slot_id = slot_id;
150                 thread_info->client_input = client_input;
151         }
152         if (pthread_attr_init(&thread_attr) != 0) {
153                 ret = DA_ERR_FAIL_TO_CREATE_THREAD;
154                 goto ERR;
155         }
156
157         if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0) {
158                 ret = DA_ERR_FAIL_TO_CREATE_THREAD;
159                 goto ERR;
160         }
161
162         if (pthread_create(&GET_DL_THREAD_ID(slot_id), &thread_attr,
163                         __thread_start_download, thread_info) < 0) {
164                 DA_LOG_ERR(Thread, "making thread failed..");
165                 ret = DA_ERR_FAIL_TO_CREATE_THREAD;
166         } else {
167                 if (GET_DL_THREAD_ID(slot_id) < 1) {
168                         DA_LOG_ERR(Thread, "The thread start is failed before calling this");
169 // When http resource is leaked, the thread ID is initialized at error handling section of thread_start_download()
170 // Because the thread ID is initialized, the ptrhead_detach should not be called. This is something like timing issue between threads.
171 // thread info and basic_dl_input is freed at thread_start_download(). And it should not returns error code in this case.
172                         goto ERR;
173                 }
174         }
175         DA_LOG_CRITICAL(Thread, "download thread create slot_id[%d] thread id[%lu]",
176                         slot_id,GET_DL_THREAD_ID(slot_id));
177
178 ERR:
179         if (DA_RESULT_OK != ret) {
180                 if (client_input) {
181                         clean_up_client_input_info(client_input);
182                         free(client_input);
183                         client_input = DA_NULL;
184                 }
185                 if (thread_info) {
186                         free(thread_info);
187                         thread_info = DA_NULL;
188                 }
189                 destroy_download_info(slot_id);
190         }
191         return ret;
192 }
193
194 da_result_t __make_source_info_basic_download(
195                 stage_info *stage,
196                 client_input_t *client_input)
197 {
198         da_result_t ret = DA_RESULT_OK;
199         client_input_basic_t *client_input_basic = DA_NULL;
200         source_info_t *source_info = DA_NULL;
201         source_info_basic_t *source_info_basic = DA_NULL;
202
203         DA_LOG_FUNC_START(Default);
204
205         if (!stage) {
206                 DA_LOG_ERR(Default, "no stage; DA_ERR_INVALID_ARGUMENT");
207                 ret = DA_ERR_INVALID_ARGUMENT;
208                 goto ERR;
209         }
210
211         client_input_basic = &(client_input->client_input_basic);
212         if (DA_NULL == client_input_basic->req_url) {
213                 DA_LOG_ERR(Default, "DA_ERR_INVALID_URL");
214                 ret = DA_ERR_INVALID_URL;
215                 goto ERR;
216         }
217
218         source_info_basic = (source_info_basic_t*)calloc(1, sizeof(source_info_basic_t));
219         if (DA_NULL == source_info_basic) {
220                 DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC");
221                 ret = DA_ERR_FAIL_TO_MEMALLOC;
222                 goto ERR;
223         }
224
225         source_info_basic->url = client_input_basic->req_url;
226         client_input_basic->req_url = DA_NULL;
227
228         if (client_input_basic->user_request_header) {
229                 source_info_basic->user_request_header =
230                         client_input_basic->user_request_header;
231                 source_info_basic->user_request_header_count =
232                         client_input_basic->user_request_header_count;
233                 client_input_basic->user_request_header = DA_NULL;
234                 client_input_basic->user_request_header_count = 0;
235         }
236
237         source_info = GET_STAGE_SOURCE_INFO(stage);
238         memset(source_info, 0, sizeof(source_info_t));
239
240         source_info->source_info_type.source_info_basic = source_info_basic;
241
242         DA_LOG(Default, "BASIC HTTP STARTED: URL=%s",
243                         source_info->source_info_type.source_info_basic->url);
244 ERR:
245         return ret;
246 }
247
248 void __thread_clean_up_handler_for_start_download(void *arg)
249 {
250        DA_LOG_CRITICAL(Default, "cleanup for thread id = %d", pthread_self());
251 }
252
253 static void *__thread_start_download(void *data)
254 {
255         da_result_t ret = DA_RESULT_OK;
256         download_thread_input *thread_info = DA_NULL;
257         client_input_t *client_input = DA_NULL;
258         stage_info *stage = DA_NULL;
259         download_state_t download_state = 0;
260
261         int slot_id = DA_INVALID_ID;
262
263         DA_LOG_FUNC_START(Thread);
264
265         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL);
266
267         thread_info = (download_thread_input*)data;
268         if (DA_NULL == thread_info) {
269                 DA_LOG_ERR(Thread, "thread_info is NULL..");
270                 ret = DA_ERR_INVALID_ARGUMENT;
271                 return DA_NULL;
272         } else {
273                 slot_id = thread_info->slot_id;
274                 client_input = thread_info->client_input;
275
276                 if(thread_info) {
277                         free(thread_info);
278                         thread_info = DA_NULL;
279                 }
280         }
281
282         pthread_cleanup_push(__thread_clean_up_handler_for_start_download, (void *)NULL);
283
284         if (DA_FALSE == is_valid_slot_id(slot_id)) {
285                 ret = DA_ERR_INVALID_ARGUMENT;
286                 DA_LOG_ERR(Default, "Invalid Download ID");
287                 goto ERR;
288         }
289
290         if (!client_input) {
291                 ret = DA_ERR_INVALID_ARGUMENT;
292                 DA_LOG_ERR(Default, "Invalid client_input");
293                 goto ERR;
294         }
295
296         stage = Add_new_download_stage(slot_id);
297         if (!stage) {
298                 ret = DA_ERR_FAIL_TO_MEMALLOC;
299                 DA_LOG_ERR(Default, "STAGE ADDITION FAIL!");
300                 goto ERR;
301         }
302         DA_LOG(Default, "new added Stage : %p", stage);
303
304         GET_DL_USER_DATA(slot_id) = client_input->user_data;
305         client_input->user_data = DA_NULL;
306         GET_DL_USER_INSTALL_PATH(slot_id) = client_input->install_path;
307         client_input->install_path = DA_NULL;
308         GET_DL_USER_FILE_NAME(slot_id) = client_input->file_name;
309         client_input->file_name = DA_NULL;
310         GET_DL_USER_ETAG(slot_id) = client_input->etag;
311         client_input->etag = DA_NULL;
312         GET_DL_USER_TEMP_FILE_PATH(slot_id) = client_input->temp_file_path;
313         client_input->temp_file_path = DA_NULL;
314         ret = __make_source_info_basic_download(stage, client_input);
315         /* to save memory */
316         if (client_input) {
317                 clean_up_client_input_info(client_input);
318                 free(client_input);
319                 client_input = DA_NULL;
320         }
321
322         if (ret == DA_RESULT_OK)
323                 ret = __download_content(stage);
324
325 ERR:
326         if (client_input) {
327                 clean_up_client_input_info(client_input);
328                 free(client_input);
329                 client_input = DA_NULL;
330         }
331
332         if (DA_RESULT_OK == ret) {
333                 char *installed_path = NULL;
334                 char *etag = DA_NULL;
335                 req_dl_info *request_info = NULL;
336                 file_info *file_storage = NULL;
337                 DA_LOG_CRITICAL(Default, "Whole download flow is finished.");
338                 _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
339                 download_state = GET_DL_STATE_ON_STAGE(stage);
340                 _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
341                 if (download_state == DOWNLOAD_STATE_ABORTED) {
342                         DA_LOG(Default, "Abort case. Do not call client callback");
343 #ifdef PAUSE_EXIT
344                 } else if (download_state == DOWNLOAD_STATE_PAUSED) {
345                         DA_LOG(Default, "Finish case from paused state");
346                         destroy_download_info(slot_id);
347 #endif
348                 } else {
349                         request_info = GET_STAGE_TRANSACTION_INFO(stage);
350                         etag = GET_REQUEST_HTTP_HDR_ETAG(request_info);
351                         file_storage = GET_STAGE_CONTENT_STORE_INFO(stage);
352                         installed_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage);
353                         send_user_noti_and_finish_download_flow(slot_id, installed_path,
354                                         etag);
355                 }
356         } else {
357                 char *etag = DA_NULL;
358                 req_dl_info *request_info = NULL;
359                 request_info = GET_STAGE_TRANSACTION_INFO(stage);
360                 DA_LOG_CRITICAL(Default, "Download Failed -Return = %d", ret);
361                 if (request_info) {
362                         etag = GET_REQUEST_HTTP_HDR_ETAG(request_info);
363                         send_client_finished_info(slot_id, GET_DL_ID(slot_id),
364                                         DA_NULL, etag, ret, get_http_status(slot_id));
365                 }
366                 destroy_download_info(slot_id);
367         }
368
369         pthread_cleanup_pop(0);
370         DA_LOG_CRITICAL(Thread, "=====thread_start_download - EXIT=====");
371         pthread_exit((void *)NULL);
372         return DA_NULL;
373 }
374
375 da_result_t __download_content(stage_info *stage)
376 {
377         da_result_t ret = DA_RESULT_OK;
378         download_state_t download_state = 0;
379         da_bool_t isDownloadComplete = DA_FALSE;
380         int slot_id = DA_INVALID_ID;
381
382         DA_LOG_FUNC_START(Default);
383
384         slot_id = GET_STAGE_DL_ID(stage);
385         CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD, stage);
386
387         do {
388                 stage = GET_DL_CURRENT_STAGE(slot_id);
389                 _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
390                 download_state = GET_DL_STATE_ON_STAGE(stage);
391                 DA_LOG(Default, "download_state to - [%d] ", download_state);
392                 _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
393
394                 switch(download_state) {
395                 case DOWNLOAD_STATE_NEW_DOWNLOAD:
396                         ret = requesting_download(stage);
397
398                         _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
399                         download_state = GET_DL_STATE_ON_STAGE(stage);
400                         _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]);
401                         if (download_state == DOWNLOAD_STATE_CANCELED ||
402                                         download_state == DOWNLOAD_STATE_ABORTED ||
403                                         download_state == DOWNLOAD_STATE_PAUSED) {
404                                 break;
405                         } else {
406                                 if (DA_RESULT_OK == ret) {
407                                         ret = handle_after_download(stage);
408                                 }
409                         }
410                         break;
411                 default:
412                         isDownloadComplete = DA_TRUE;
413                         break;
414                 }
415         }while ((DA_RESULT_OK == ret) && (DA_FALSE == isDownloadComplete));
416
417         return ret;
418 }