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