Tizen 2.1 base
[platform/framework/web/download-provider.git] / provider / download-provider-da-interface.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 <stdio.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <time.h>
23 #include <sys/time.h>
24
25 #include "download-provider.h"
26 #include "download-provider-log.h"
27 #include "download-provider-pthread.h"
28 #include "download-provider-socket.h"
29 #include "download-provider-db.h"
30 #include "download-provider-queue.h"
31 #include "download-provider-notification.h"
32 #include "download-provider-request.h"
33
34 #include "download-agent-defs.h"
35 #include "download-agent-interface.h"
36
37 int dp_is_file_exist(const char *file_path)
38 {
39         struct stat file_state;
40         int stat_ret;
41
42         if (file_path == NULL) {
43                 TRACE_ERROR("[NULL-CHECK] file path is NULL");
44                 return -1;
45         }
46
47         stat_ret = stat(file_path, &file_state);
48
49         if (stat_ret == 0)
50                 if (file_state.st_mode & S_IFREG)
51                         return 0;
52
53         return -1;
54 }
55
56 static int __change_error(int err)
57 {
58         int ret = DP_ERROR_NONE;
59         switch (err) {
60         case DA_RESULT_OK:
61                 ret = DP_ERROR_NONE;
62                 break;
63         case DA_ERR_INVALID_ARGUMENT:
64                 ret = DP_ERROR_INVALID_PARAMETER;
65                 break;
66         case DA_ERR_FAIL_TO_MEMALLOC:
67                 ret = DP_ERROR_OUT_OF_MEMORY;
68                 break;
69         case DA_ERR_UNREACHABLE_SERVER:
70                 ret = DP_ERROR_NETWORK_UNREACHABLE;
71                 break;
72         case DA_ERR_HTTP_TIMEOUT:
73                 ret = DP_ERROR_CONNECTION_TIMED_OUT;
74                 break;
75         case DA_ERR_DISK_FULL:
76                 ret = DP_ERROR_NO_SPACE;
77                 break;
78         case DA_ERR_INVALID_STATE:
79                 ret = DP_ERROR_INVALID_STATE;
80                 break;
81         case DA_ERR_NETWORK_FAIL:
82                 ret = DP_ERROR_CONNECTION_FAILED;
83                 break;
84         case DA_ERR_INVALID_URL:
85                 ret = DP_ERROR_INVALID_URL;
86                 break;
87         case DA_ERR_INVALID_INSTALL_PATH:
88                 ret = DP_ERROR_INVALID_DESTINATION;
89                 break;
90         case DA_ERR_ALREADY_MAX_DOWNLOAD:
91                 ret = DP_ERROR_TOO_MANY_DOWNLOADS;
92                 break;
93         case DA_ERR_FAIL_TO_CREATE_THREAD:
94         case DA_ERR_FAIL_TO_OBTAIN_MUTEX:
95         case DA_ERR_FAIL_TO_ACCESS_FILE:
96         case DA_ERR_FAIL_TO_GET_CONF_VALUE:
97         case DA_ERR_FAIL_TO_ACCESS_STORAGE:
98         default:
99                 ret = DP_ERROR_IO_ERROR;
100                 break;
101         }
102         return ret;
103 }
104
105 static void __download_info_cb(user_download_info_t *info, void *user_data)
106 {
107         if (!info) {
108                 TRACE_ERROR("[NULL-CHECK] Agent info");
109                 return ;
110         }
111         if (!user_data) {
112                 TRACE_ERROR("[NULL-CHECK] user_data");
113                 return ;
114         }
115         dp_request *request = (dp_request *) user_data;
116         if (request->id < 0 || (request->agent_id != info->download_id)) {
117                 TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d",
118                         request->agent_id, info->download_id);
119                 return ;
120         }
121
122         int request_id = request->id;
123
124         // update info before sending event
125         if (info->file_type) {
126                 TRACE_INFO("[STARTED][%d] [%s]", request_id, info->file_type);
127                 if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO,
128                                 DP_DB_COL_MIMETYPE,
129                                 DP_DB_COL_TYPE_TEXT, info->file_type) == 0) {
130
131                         if (info->tmp_saved_path) {
132                                 TRACE_INFO("[PATH][%d] being written to [%s]",
133                                         request_id, info->tmp_saved_path);
134                                 if (dp_db_set_column
135                                                 (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
136                                                 DP_DB_COL_TMP_SAVED_PATH, DP_DB_COL_TYPE_TEXT,
137                                                 info->tmp_saved_path) < 0)
138                                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
139                         }
140
141                         if (info->file_size > 0) {
142                                 TRACE_INFO
143                                         ("[FILE-SIZE][%d] [%lld]", request_id, info->file_size);
144                                 CLIENT_MUTEX_LOCK(&(request->mutex));
145                                 request->file_size = info->file_size;
146                                 CLIENT_MUTEX_UNLOCK(&(request->mutex));
147                                 if (dp_db_set_column
148                                                 (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
149                                                 DP_DB_COL_CONTENT_SIZE,
150                                                 DP_DB_COL_TYPE_INT64, &info->file_size) < 0)
151                                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
152                         }
153
154                         if (info->content_name) {
155                                 TRACE_INFO
156                                         ("[CONTENTNAME][%d] [%s]", request_id, info->content_name);
157                                 if (dp_db_set_column
158                                                 (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
159                                                 DP_DB_COL_CONTENT_NAME,
160                                                 DP_DB_COL_TYPE_TEXT, info->content_name) < 0)
161                                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
162                         }
163                         if (info->etag) {
164                                 TRACE_INFO("[ETAG][%d] [%s]", request_id, info->etag);
165                                 if (dp_db_replace_column
166                                                 (request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ETAG,
167                                                 DP_DB_COL_TYPE_TEXT, info->etag) < 0)
168                                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
169                         }
170                 } else {
171                         TRACE_ERROR
172                                 ("[ERROR][%d][SQL] failed to insert downloadinfo",
173                                 request_id);
174                 }
175         }
176
177         CLIENT_MUTEX_LOCK(&(request->mutex));
178
179         request->state = DP_STATE_DOWNLOADING;
180         if (dp_db_set_column(request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE,
181                         DP_DB_COL_TYPE_INT, &request->state) < 0)
182                 TRACE_ERROR("[ERROR][%d][SQL]", request->id);
183
184         if (request->group && request->group->event_socket >= 0 &&
185                 request->state_cb)
186                 dp_ipc_send_event(request->group->event_socket,
187                         request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, 0);
188
189         if (request->auto_notification)
190                 request->noti_priv_id =
191                         dp_set_downloadinginfo_notification
192                                 (request->id, request->packagename);
193
194         CLIENT_MUTEX_UNLOCK(&(request->mutex));
195 }
196
197 static void __progress_cb(user_progress_info_t *info, void *user_data)
198 {
199         if (!info) {
200                 TRACE_ERROR("[NULL-CHECK] Agent info");
201                 return ;
202         }
203         if (!user_data) {
204                 TRACE_ERROR("[NULL-CHECK] user_data");
205                 return ;
206         }
207         dp_request *request = (dp_request *) user_data;
208         if (request->id < 0 || (request->agent_id != info->download_id)) {
209                 TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d",
210                         request->id, request->agent_id, info->download_id);
211                 return ;
212         }
213
214         CLIENT_MUTEX_LOCK(&(request->mutex));
215         if (request->state == DP_STATE_DOWNLOADING) {
216                 request->received_size = info->received_size;
217                 time_t tt = time(NULL);
218                 struct tm *localTime = localtime(&tt);
219                 // send event every 1 second.
220                 if (request->progress_lasttime != localTime->tm_sec) {
221                         request->progress_lasttime = localTime->tm_sec;
222                         if (request->progress_cb && request->group &&
223                                 request->group->event_socket >= 0 &&
224                                 request->received_size > 0)
225                                 dp_ipc_send_event(request->group->event_socket,
226                                         request->id, request->state, request->error,
227                                         request->received_size);
228                         if (request->auto_notification)
229                                 dp_update_downloadinginfo_notification
230                                         (request->noti_priv_id,
231                                         (double)request->received_size,
232                                         (double)request->file_size);
233                 }
234         }
235         CLIENT_MUTEX_UNLOCK(&(request->mutex));
236 }
237
238 static void __finished_cb(user_finished_info_t *info, void *user_data)
239 {
240         if (!info) {
241                 TRACE_ERROR("[NULL-CHECK] Agent info");
242                 return ;
243         }
244         TRACE_INFO("Agent ID[%d] err[%d] http_status[%d]",
245                 info->download_id, info->err, info->http_status);
246         if (!user_data) {
247                 TRACE_ERROR("[NULL-CHECK] user_data");
248                 return ;
249         }
250         dp_request *request = (dp_request *) user_data;
251         if (request->id < 0 || (request->agent_id != info->download_id)) {
252                 TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d",
253                         request->id, request->agent_id, info->download_id);
254                 return ;
255         }
256
257         CLIENT_MUTEX_LOCK(&(request->mutex));
258         int request_id = request->id;
259         dp_credential cred = request->credential;
260         CLIENT_MUTEX_UNLOCK(&(request->mutex));
261         dp_state_type state = DP_STATE_NONE;
262         dp_error_type errorcode = DP_ERROR_NONE;
263
264         // update info before sending event
265         if (dp_db_update_date
266                         (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0)
267                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
268
269         if (info->http_status > 0)
270                 if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO,
271                                 DP_DB_COL_HTTP_STATUS,
272                                 DP_DB_COL_TYPE_INT, &info->http_status) < 0)
273                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
274
275         if (info->err == DA_RESULT_OK) {
276                 if (info->saved_path) {
277                         char *str = NULL;
278                         char *content_name = NULL;
279
280                         str = strrchr(info->saved_path, '/');
281                         if (str) {
282                                 str++;
283                                 content_name = dp_strdup(str);
284                                 TRACE_INFO("[PARSE][%d] content_name [%s]",
285                                         request_id, content_name);
286                         }
287                         TRACE_INFO
288                                 ("[chown][%d] [%d][%d]", request_id, cred.uid, cred.gid);
289                         if (chown(info->saved_path, cred.uid, cred.gid) < 0)
290                                 TRACE_STRERROR("[ERROR][%d] chown", request_id);
291                         if (dp_db_replace_column
292                                         (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
293                                         DP_DB_COL_SAVED_PATH,
294                                         DP_DB_COL_TYPE_TEXT, info->saved_path) == 0) {
295                                 if (content_name != NULL) {
296                                         if (dp_db_set_column
297                                                         (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
298                                                         DP_DB_COL_CONTENT_NAME,
299                                                         DP_DB_COL_TYPE_TEXT, content_name) < 0)
300                                                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
301                                 }
302                         } else {
303                                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
304                         }
305                         if (content_name != NULL)
306                                 free(content_name);
307
308                         errorcode = DP_ERROR_NONE;
309                         state = DP_STATE_COMPLETED;
310
311                         TRACE_INFO("[COMPLETED][%d] saved to [%s]",
312                                         request_id, info->saved_path);
313                 } else {
314                         TRACE_ERROR("Cannot enter here");
315                         TRACE_ERROR("[ERROR][%d] No SavedPath", request_id);
316                         errorcode = DP_ERROR_INVALID_DESTINATION;
317                         state = DP_STATE_FAILED;
318                 }
319                 CLIENT_MUTEX_LOCK(&(request->mutex));
320                 if (request->file_size == 0) {
321                         request->file_size = request->received_size;
322                         if (dp_db_replace_column
323                                         (request_id, DP_DB_TABLE_DOWNLOAD_INFO,
324                                         DP_DB_COL_CONTENT_SIZE,
325                                         DP_DB_COL_TYPE_INT64, &request->file_size ) < 0)
326                                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
327                 }
328                 CLIENT_MUTEX_UNLOCK(&(request->mutex));
329         } else if (info->err == DA_RESULT_USER_CANCELED) {
330                 state = DP_STATE_CANCELED;
331                 errorcode = DP_ERROR_NONE;
332                 TRACE_INFO("[CANCELED][%d]", request_id);
333         } else {
334                 state = DP_STATE_FAILED;
335                 errorcode = __change_error(info->err);
336                 TRACE_INFO("[FAILED][%d][%s]", request_id,
337                                 dp_print_errorcode(errorcode));
338         }
339
340         if (dp_db_set_column
341                         (request_id, DP_DB_TABLE_LOG, DP_DB_COL_STATE,
342                         DP_DB_COL_TYPE_INT, &state) < 0)
343                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
344
345         if (errorcode != DP_ERROR_NONE) {
346                 if (dp_db_set_column(request_id, DP_DB_TABLE_LOG,
347                                 DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT,
348                                 &errorcode) < 0)
349                         TRACE_ERROR("[ERROR][%d][SQL]", request_id);
350         }
351
352         // need MUTEX LOCK
353         CLIENT_MUTEX_LOCK(&(request->mutex));
354
355         request->state = state;
356         request->error = errorcode;
357
358         // stay on memory till called destroy by client or timeout
359         if (request->group != NULL && request->group->event_socket >= 0) {
360                 /* update the received file size.
361                 * The last received file size cannot update
362                 * because of reducing update algorithm*/
363                 if (request->received_size > 0)
364                         dp_ipc_send_event(request->group->event_socket,
365                                 request->id, DP_STATE_DOWNLOADING, request->error,
366                                 request->received_size);
367                 if (request->state_cb)
368                         dp_ipc_send_event(request->group->event_socket,
369                                 request->id, request->state, request->error, 0);
370                 request->group->queued_count--;
371         }
372
373         // to prevent the crash . check packagename of request
374         if (request->auto_notification && request->packagename != NULL)
375                 request->noti_priv_id =
376                         dp_set_downloadedinfo_notification(request->noti_priv_id,
377                                 request->id, request->packagename, request->state);
378
379         request->stop_time = (int)time(NULL);
380
381         CLIENT_MUTEX_UNLOCK(&(request->mutex));
382
383         dp_thread_queue_manager_wake_up();
384 }
385
386 static void __paused_cb(user_paused_info_t *info, void *user_data)
387 {
388         TRACE_INFO("");
389         dp_request *request = (dp_request *) user_data;
390         if (!request) {
391                 TRACE_ERROR("[NULL-CHECK] request");
392                 return ;
393         }
394         if (request->id < 0 || (request->agent_id != info->download_id)) {
395                 TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d",
396                         request->id, request->agent_id, info->download_id);
397                 return ;
398         }
399
400         CLIENT_MUTEX_LOCK(&(request->mutex));
401         int request_id = request->id;
402         CLIENT_MUTEX_UNLOCK(&(request->mutex));
403
404         if (dp_db_update_date
405                         (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0)
406                 TRACE_ERROR("[ERROR][%d][SQL]", request_id);
407
408         // need MUTEX LOCK
409         CLIENT_MUTEX_LOCK(&(request->mutex));
410
411         request->state = DP_STATE_PAUSED;
412
413         if (dp_db_set_column
414                         (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE,
415                         DP_DB_COL_TYPE_INT, &request->state) < 0) {
416                 TRACE_ERROR("[ERROR][%d][SQL]", request->id);
417         }
418
419         if (request->group &&
420                 request->group->event_socket >= 0 && request->state_cb)
421                 dp_ipc_send_event(request->group->event_socket,
422                         request->id, request->state, request->error, 0);
423
424         if (request->group)
425                 request->group->queued_count--;
426
427         CLIENT_MUTEX_UNLOCK(&(request->mutex));
428
429         dp_thread_queue_manager_wake_up();
430 }
431
432 int dp_init_agent()
433 {
434         int da_ret = 0;
435         da_client_cb_t da_cb = {
436                 __download_info_cb,
437                 __progress_cb,
438                 __finished_cb,
439                 __paused_cb
440         };
441         da_ret = da_init(&da_cb);
442         if (da_ret != DA_RESULT_OK) {
443                 return DP_ERROR_OUT_OF_MEMORY;
444         }
445         return DP_ERROR_NONE;
446 }
447
448 void dp_deinit_agent()
449 {
450         da_deinit();
451 }
452
453 // 0 : success
454 // -1 : failed
455 dp_error_type dp_cancel_agent_download(int req_id)
456 {
457         if (req_id < 0) {
458                 TRACE_ERROR("[NULL-CHECK] req_id");
459                 return -1;
460         }
461         if (da_cancel_download(req_id) == DA_RESULT_OK)
462                 return 0;
463         return -1;
464 }
465
466 // 0 : success
467 // -1 : failed
468 dp_error_type dp_pause_agent_download(int req_id)
469 {
470         if (req_id < 0) {
471                 TRACE_ERROR("[NULL-CHECK] req_id");
472                 return -1;
473         }
474         if (da_suspend_download(req_id) == DA_RESULT_OK)
475                 return 0;
476         return -1;
477 }
478
479
480 // 0 : success
481 // -1 : failed
482 // -2 : pended
483 dp_error_type dp_start_agent_download(dp_request *request)
484 {
485         int da_ret = -1;
486         int req_dl_id = -1;
487         dp_error_type errorcode = DP_ERROR_NONE;
488         extension_data_t ext_data = {0,};
489
490         TRACE_INFO("");
491         if (!request) {
492                 TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot");
493                 return DP_ERROR_INVALID_PARAMETER;
494         }
495
496         char *url = dp_request_get_url(request->id, request, &errorcode);
497         if (url == NULL) {
498                 TRACE_ERROR("[ERROR][%d] URL is NULL", request->id);
499                 return DP_ERROR_INVALID_URL;
500         }
501         char *destination =
502                 dp_request_get_destination(request->id, request, &errorcode);
503         if (destination != NULL)
504                 ext_data.install_path = destination;
505
506         char *filename =
507                 dp_request_get_filename(request->id, request, &errorcode);
508         if (filename != NULL)
509                 ext_data.file_name = filename;
510
511         // call start_download() of download-agent
512
513         char *tmp_saved_path =
514                 dp_request_get_tmpsavedpath(request->id, request, &errorcode);
515         if (tmp_saved_path) {
516                 char *etag = dp_request_get_etag(request->id, request, &errorcode);
517                 if (etag) {
518                         TRACE_INFO("[RESUME][%d]", request->id);
519                         ext_data.etag = etag;
520                         ext_data.temp_file_path = tmp_saved_path;
521                 } else {
522                         /* FIXME later : It is better to handle the unlink function in download agaent module
523                          * or in upload the request data to memory after the download provider process is restarted */
524                         TRACE_INFO("[RESTART][%d] try to remove tmp file [%s]",
525                                 request->id, tmp_saved_path);
526                         if (dp_is_file_exist(tmp_saved_path) == 0)
527                                 if (unlink(tmp_saved_path) != 0)
528                                         TRACE_STRERROR
529                                                 ("[ERROR][%d] remove file", request->id);
530                 }
531         }
532
533         // get headers list from httpheaders table(DB)
534         int headers_count = dp_db_get_cond_rows_count
535                         (request->id, DP_DB_TABLE_HTTP_HEADERS, NULL, 0, NULL);
536         if (headers_count > 0) {
537                 ext_data.request_header = calloc(headers_count, sizeof(char*));
538                 if (ext_data.request_header != NULL) {
539                         ext_data.request_header_count = dp_db_get_http_headers_list
540                                 (request->id, (char**)ext_data.request_header);
541                 }
542         }
543
544         ext_data.user_data = (void *)request;
545
546         // call start API of agent lib
547         da_ret =
548                 da_start_download_with_extension(url, &ext_data, &req_dl_id);
549         if (ext_data.request_header_count > 0) {
550                 int len = 0;
551                 int i = 0;
552                 len = ext_data.request_header_count;
553                 for (i = 0; i < len; i++) {
554                         if (ext_data.request_header[i])
555                                 free((void *)(ext_data.request_header[i]));
556                 }
557                 free(ext_data.request_header);
558         }
559         free(url);
560         free(destination);
561         free(filename);
562         free(tmp_saved_path);
563
564         // if start_download() return error cause of maximun download limitation,
565         // set state to DOWNLOAD_STATE_PENDED.
566         if (da_ret == DA_ERR_ALREADY_MAX_DOWNLOAD) {
567                 TRACE_INFO("[PENDING][%d] DA_ERR_ALREADY_MAX_DOWNLOAD [%d]",
568                         request->id, da_ret);
569                 return DP_ERROR_TOO_MANY_DOWNLOADS;
570         } else if (da_ret != DA_RESULT_OK) {
571                 TRACE_ERROR("[ERROR][%d] DP_ERROR_CONNECTION_FAILED [%d]",
572                         request->id, da_ret);
573                 return __change_error(da_ret);
574         }
575         TRACE_INFO("[SUCCESS][%d] agent_id [%d]", request->id, req_dl_id);
576         request->agent_id = req_dl_id;
577         return DP_ERROR_NONE;
578 }
579
580 dp_error_type dp_resume_agent_download(int req_id)
581 {
582         int da_ret = -1;
583         if (req_id < 0) {
584                 TRACE_ERROR("[NULL-CHECK] req_id");
585                 return DP_ERROR_INVALID_PARAMETER;
586         }
587         da_ret = da_resume_download(req_id);
588         if (da_ret == DA_RESULT_OK)
589                 return DP_ERROR_NONE;
590         else if (da_ret == DA_ERR_INVALID_STATE)
591                 return DP_ERROR_INVALID_STATE;
592         return __change_error(da_ret);
593 }
594
595 // 1 : alive
596 // 0 : not alive
597 int dp_is_alive_download(int req_id)
598 {
599         int da_ret = 0;
600         if (req_id < 0) {
601                 TRACE_ERROR("[NULL-CHECK] req_id");
602                 return 0;
603         }
604         da_ret = da_is_valid_download_id(req_id);
605         return da_ret;
606 }
607