[Security vulnerability] Access to a symbolic link
[platform/framework/web/download-provider.git] / agent / download-agent-file.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 <dirent.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <sys/vfs.h>
23 #include <math.h>
24 #include <errno.h>
25
26 #include "download-agent-debug.h"
27 #include "download-agent-file.h"
28 #include "download-agent-mime-util.h"
29 /* FIXME Later */
30 #include "download-agent-http-msg-handler.h"
31 #include "download-agent-plugin-drm.h"
32 #include "download-agent-plugin-conf.h"
33 #include "download-provider-client-manager.h"
34 #include <storage.h>
35
36
37 #define NO_NAME_TEMP_STR "No name"
38 #define MAX_SUFFIX_COUNT        1000000000
39
40 typedef enum {
41         NOT_EXIST_FILE = 0,
42         REGULAR_FILE,
43         OTHER_FILE
44 } file_state_e;
45
46 da_ret_t __saved_file_open(file_info_t *file_info)
47 {
48         da_ret_t ret = DA_RESULT_OK;
49         char *actual_file_path = DA_NULL;
50         char *resolved_path = DA_NULL;
51         void *fd = DA_NULL;
52
53         DA_LOGV("");
54
55         actual_file_path = file_info->file_path;
56         if (!actual_file_path)
57                 return DA_ERR_INVALID_ARGUMENT;
58
59         resolved_path = realpath(actual_file_path, NULL);
60         if (resolved_path) {
61                 /* Check if actual_file_path is symbolic file or not */
62                 if (strcmp(resolved_path, actual_file_path) != 0) {
63                         free(resolved_path);
64                         return DA_ERR_INVALID_ARGUMENT;
65                 }
66         } else if (errno != ENOENT) {
67                 return DA_ERR_INVALID_ARGUMENT;
68         }
69
70         fd = fopen(actual_file_path, "a+"); // for resume
71         if (fd == DA_NULL) {
72                 DA_LOGE("File open failed");
73                 if (errno == ENOSPC)
74                         ret = DA_ERR_DISK_FULL;
75                 else
76                         ret = DA_ERR_FAIL_TO_ACCESS_FILE;
77                 goto ERR;
78         }
79
80         file_info->file_handle = fd;
81 ERR:
82         if (DA_RESULT_OK != ret)
83                 file_info->file_handle = DA_NULL;
84         free(resolved_path);
85         return ret;
86 }
87
88 da_ret_t __divide_file_name_into_pure_name_N_extesion(const char *in_file_name, char *url, char **out_pure_file_name, char **out_extension)
89 {
90         char *file_name = DA_NULL;
91         char *tmp_ptr = DA_NULL;
92         char temp_file[DA_MAX_FILE_NAME_LEN + 1] = {0,};
93         char tmp_ext[DA_MAX_STR_LEN] = {0,};
94         int len = 0;
95         da_ret_t ret = DA_RESULT_OK;
96
97         DA_LOGD("");
98
99         if (!in_file_name)
100                 return DA_ERR_INVALID_ARGUMENT;
101
102         file_name = (char *)in_file_name;
103
104         tmp_ptr = strrchr(file_name, '.');
105
106         if (tmp_ptr == file_name) {
107                 // a leading dot means the file is hidden.
108                 tmp_ptr = DA_NULL;
109         } else {
110                 if (tmp_ptr)
111                         tmp_ptr++;
112                 if (tmp_ptr && out_extension) {
113                         strncpy((char*) tmp_ext, tmp_ptr, sizeof(tmp_ext) - 1);
114                         *out_extension = strdup((const char*) tmp_ext);
115                         DA_SECURE_LOGD("extension [%s]", *out_extension);
116                 } else if (!tmp_ptr && url) {
117                         char *extension = DA_NULL;
118                         da_bool_t b_ret = da_get_extension_name_from_url(url, &extension);
119                         if (b_ret && !extension)
120                                 *out_extension = NULL;
121                 }
122         }
123
124         if (!out_pure_file_name)
125                 return ret;
126
127         if (tmp_ptr)
128                 len = tmp_ptr - file_name - 1;
129         else
130                 len = strlen(file_name);
131
132         if (len >= DA_MAX_FILE_NAME_LEN) {
133                 strncpy((char*) temp_file, file_name, DA_MAX_FILE_NAME_LEN);
134                 temp_file[DA_MAX_FILE_NAME_LEN] = '\0';
135         } else {
136                 strncpy((char*) temp_file, file_name, len);
137                 temp_file[len] = '\0';
138         }
139
140         delete_prohibited_char((char*) temp_file,
141                         strlen((char*) temp_file));
142         if (strlen(temp_file) < 1) {
143                 *out_pure_file_name = strdup(NO_NAME_TEMP_STR);
144         } else {
145                 *out_pure_file_name = strdup(
146                                 (const char*) temp_file);
147         }
148
149         DA_LOGD("pure file name [%s]", *out_pure_file_name);
150         return ret;
151 }
152
153 da_ret_t __file_write_buf_make_buf(file_info_t *file_info)
154 {
155         da_ret_t ret = DA_RESULT_OK;
156         char *buffer = DA_NULL;
157
158         DA_LOGV("");
159
160         buffer = (char*) calloc(1, DA_FILE_BUF_SIZE);
161         if (DA_NULL == buffer) {
162                 DA_LOGE("Calloc failure ");
163                 ret = DA_ERR_FAIL_TO_MEMALLOC;
164         } else {
165                 file_info->buffer_len = 0;
166                 file_info->buffer = buffer;
167         }
168
169         return ret;
170 }
171
172 da_ret_t __file_write_buf_destroy_buf(file_info_t *file_info)
173 {
174         da_ret_t ret = DA_RESULT_OK;
175
176         DA_LOGV("");
177         NULL_CHECK_RET(file_info);
178
179         free(file_info->buffer);
180         file_info->buffer = DA_NULL;
181         file_info->buffer_len = 0;
182
183         return ret;
184 }
185
186 da_ret_t __file_write_buf_flush_buf(file_info_t *file_info)
187 {
188         da_ret_t ret = DA_RESULT_OK;
189         char *buffer = DA_NULL;
190         size_t buffer_size = 0;
191         size_t write_success_len = 0;
192         void *fd = DA_NULL;
193
194         //      DA_LOGV("");
195
196         buffer = file_info->buffer;
197         buffer_size = file_info->buffer_len;
198
199         if (buffer_size == 0) {
200                 DA_LOGE("no data on buffer..");
201                 return ret;
202         }
203
204         fd = file_info->file_handle;
205         if (DA_NULL == fd) {
206                 DA_LOGE("There is no file handle.");
207
208                 ret = DA_ERR_FAIL_TO_ACCESS_FILE;
209                 goto ERR;
210         }
211
212         write_success_len = fwrite(buffer, sizeof(char), buffer_size,
213                         (FILE *) fd);
214         /* FIXME : This can be necessary later due to progressive download.
215          * The solution for reducing fflush is needed */
216         //fflush((FILE *) fd);
217         if (write_success_len != buffer_size) {
218                 DA_LOGE("write  fails ");
219                 if (errno == ENOSPC)
220                         ret = DA_ERR_DISK_FULL;
221                 else
222                         ret = DA_ERR_FAIL_TO_ACCESS_FILE;
223                 goto ERR;
224         }
225         file_info->bytes_written_to_file += write_success_len;
226         file_info->is_updated = DA_TRUE;
227         file_info->buffer_len = 0;
228 ERR:
229         return ret;
230 }
231
232 da_ret_t __file_write_buf_copy_to_buf(file_info_t *file_info, char *body,
233                 int body_len)
234 {
235         da_ret_t ret = DA_RESULT_OK;
236         char *buffer = DA_NULL;
237         size_t buffer_size = 0;
238
239         DA_LOGV("");
240
241         NULL_CHECK_RET(file_info->buffer);
242         buffer = file_info->buffer;
243         buffer_size = file_info->buffer_len;
244
245         memcpy(buffer + buffer_size, body, body_len);
246         file_info->buffer_len += body_len;
247
248         return ret;
249 }
250
251 da_ret_t __file_write_buf_directly_write(file_info_t *file_info,
252                 char *body, int body_len)
253 {
254         da_ret_t ret = DA_RESULT_OK;
255         size_t write_success_len = 0;
256         void *fd = DA_NULL;
257
258         //      DA_LOGV("");
259
260         fd = file_info->file_handle;
261         if (DA_NULL == fd) {
262                 DA_LOGE("There is no file handle.");
263                 ret = DA_ERR_FAIL_TO_ACCESS_FILE;
264                 goto ERR;
265         }
266
267         write_success_len = fwrite(body, sizeof(char), (size_t)body_len,
268                         (FILE *) fd);
269         /* FIXME : This can be necessary later due to progressive download.
270          * The solution for reducing fflush is needed */
271         //fflush((FILE *) fd);
272         if (write_success_len != (size_t)body_len) {
273                 DA_LOGE("write  fails ");
274                 if (errno == ENOSPC)
275                         ret = DA_ERR_DISK_FULL;
276                 else
277                         ret = DA_ERR_FAIL_TO_ACCESS_FILE;
278                 goto ERR;
279         }
280         file_info->bytes_written_to_file += write_success_len;
281         DA_LOGV("write %llu bytes", write_success_len);
282         file_info->is_updated = DA_TRUE;
283
284 ERR:
285         return ret;
286 }
287
288 /* Priority to derive extension
289  * 1. extension name which client set
290  * 2. according to MIME-Type
291  * 3. if MIME-Type is ambiguous or blank,
292  *    3-1. derived from <Content-Disposition> field's "filename" attribute
293  *    3-2. derived from url
294  * 4. if url does not have extension, leave blank for extension
295  */
296 char *__get_extension_name(char *mime_type,
297                 char *file_name_from_header, char *url)
298 {
299         char *extension = DA_NULL;
300
301         /* Priority 1 */
302         if (mime_type && !is_ambiguous_MIME_Type(mime_type)) {
303                 if (url) {
304                         DA_LOGV("If the file extension exists and mime type exists, choose the file extension over mime type");
305                         da_bool_t b_ret = da_get_extension_name_from_url(url, &extension);
306                         if (b_ret && extension)
307                                 return extension;
308                 }
309                 char *extension = DA_NULL;
310                 da_ret_t ret = get_extension_from_mime_type(mime_type, &extension);
311                 if (ret == DA_RESULT_OK && extension)
312                         return extension;
313         }
314         /* Priority 2-1 */
315         if (file_name_from_header) {
316                 char *extension = DA_NULL;
317                 DA_SECURE_LOGI("Content-Disposition :[%s]", file_name_from_header);
318                 __divide_file_name_into_pure_name_N_extesion(file_name_from_header, DA_NULL,
319                                 DA_NULL, &extension);
320                 if (extension)
321                         return extension;
322         }
323         /* Priority 2-2 */
324         if (url) {
325                 DA_LOGV("Get extension from url");
326                 da_bool_t b_ret = da_get_extension_name_from_url(url, &extension);
327                 if (b_ret && extension)
328                         return extension;
329         }
330         return DA_NULL;
331 }
332
333 /** Priority for deciding file name
334  * 1. file name which client set
335  * 2. 'filename' option on HTTP response header's Content-Disposition field
336  * 3. requesting URL
337  * 4. Otherwise, define it as "No name"
338  */
339 da_ret_t __get_candidate_file_name(const char *user_file_name, char *url,
340                 char *file_name_from_header,
341                 char **out_pure_file_name, char **out_extension)
342 {
343         da_ret_t ret = DA_RESULT_OK;
344
345         DA_LOGD("");
346
347         /* Priority 1 */
348         if (user_file_name) {
349                 __divide_file_name_into_pure_name_N_extesion(
350                                 user_file_name, url, out_pure_file_name, out_extension);
351         }
352         if (*out_pure_file_name)
353                 return ret;
354         /* Priority 2 */
355         if (file_name_from_header) {
356                 DA_SECURE_LOGI("Content-Disposition:[%s]", file_name_from_header);
357                 __divide_file_name_into_pure_name_N_extesion(file_name_from_header, DA_NULL,
358                                 out_pure_file_name, out_extension);
359         }
360         if (*out_pure_file_name)
361                 return ret ;
362         /* Priority 3 */
363         if (url) {
364                 DA_LOGD("Get file name from url");
365                 da_get_file_name_from_url(url, out_pure_file_name, out_extension);
366         }
367         if (*out_pure_file_name)
368                 return ret ;
369         /* Priority 4 */
370         *out_pure_file_name = strdup(NO_NAME_TEMP_STR);
371         if (*out_pure_file_name == DA_NULL)
372                 ret = DA_ERR_FAIL_TO_MEMALLOC;
373         return ret;
374 }
375
376 da_ret_t __decide_file_path(da_info_t *da_info)
377 {
378         da_ret_t ret = DA_RESULT_OK;
379         char *extension = DA_NULL;
380         char *file_name = DA_NULL;
381         char *tmp_file_path = DA_NULL;
382         char *file_name_from_header = DA_NULL;
383         char *url = DA_NULL;
384         const char *install_dir = DA_NULL;
385         const char *user_file_name = DA_NULL;
386         file_info_t *file_info = DA_NULL;
387         req_info_t *req_info = DA_NULL;
388         http_info_t *http_info = DA_NULL;
389         dp_client_slots_fmt *slot = DA_NULL;
390
391         DA_LOGV("");
392
393         NULL_CHECK_RET(da_info);
394         file_info = da_info->file_info;
395         NULL_CHECK_RET(file_info);
396         req_info = da_info->req_info;
397         NULL_CHECK_RET(req_info);
398         http_info = da_info->http_info;
399         NULL_CHECK_RET(http_info);
400         slot  =  req_info->user_client_data;
401         NULL_CHECK_RET(slot);
402
403         if (req_info->install_path)
404                 install_dir = (const char *)req_info->install_path;
405         else {
406                 tzplatform_set_user(slot->credential.uid);
407                 install_dir = DA_DEFAULT_INSTALL_PATH_FOR_PHONE;
408                 tzplatform_reset_user();
409         }
410         user_file_name = (const char *)req_info->file_name;
411         /* If there is location url from response header in case of redirection,
412          * it try to parse the file name from the location url */
413         if (http_info->location_url) {
414                 url = http_info->location_url;
415                 DA_LOGI("[TEST] location_url[%s][%p]", http_info->location_url, http_info->location_url);
416         } else
417                 url = req_info->url;
418
419         file_name_from_header = http_info->file_name_from_header;
420
421         /* extension is extracted only if User set specific name */
422         ret = __get_candidate_file_name(user_file_name, url, file_name_from_header,
423                         &file_name, &extension);
424         if (ret != DA_RESULT_OK)
425                 goto ERR;
426
427         if (file_name && strpbrk(file_name, DA_INVALID_PATH_STRING) != NULL) {
428                 DA_LOGI("Invalid string at file name");
429                 free(file_name);
430                 file_name = strdup(NO_NAME_TEMP_STR);
431                 if (!file_name) {
432                         ret = DA_ERR_FAIL_TO_MEMALLOC;
433                         goto ERR;
434                 }
435
436         }
437
438         DA_SECURE_LOGI("candidate file name [%s]", file_name);
439
440         if (!extension) {
441                 if (file_info->mime_type
442                                 && strncmp(file_info->mime_type, "application/x-xz", strlen("application/x-xz"))
443                                 && strncmp(file_info->mime_type, "application/rss+xml", strlen("application/rss+xml")))
444                         extension = __get_extension_name(file_info->mime_type, file_name_from_header, url);
445                 else
446                         extension = __get_extension_name(NULL, file_name_from_header, url);
447         }
448         if (file_name && !file_info->pure_file_name) {
449                 file_info->pure_file_name = file_name;
450                 file_name = DA_NULL;
451         }
452         if (extension && !file_info->extension) {
453                 DA_LOGV("candidate extension [%s]", extension);
454                 file_info->extension = extension;
455                 extension = DA_NULL;
456         }
457
458         // for resume
459         tmp_file_path = get_full_path_avoided_duplication(install_dir,
460                         file_info->pure_file_name, file_info->extension);
461         if (tmp_file_path) {
462                 file_info->file_path = tmp_file_path;
463                 tmp_file_path = DA_NULL;
464         } else {
465                 ret = DA_ERR_FAIL_TO_ACCESS_FILE;
466                 goto ERR;
467         }
468
469 ERR:
470         DA_SECURE_LOGI("decided file path [%s]", file_info->file_path);
471         if (file_name)
472                 free(file_name);
473         if (extension)
474                 free(extension);
475         return ret;
476 }
477
478 // for resume with new download request
479 da_ret_t __decide_file_path_for_resume(file_info_t *file_info)
480 {
481         da_ret_t ret = DA_RESULT_OK;
482         char *extension = DA_NULL;
483         char *file_name = DA_NULL;
484         char *file_path = DA_NULL;
485         char *ptr = DA_NULL;
486         char *ptr2 = DA_NULL;
487
488         DA_LOGV("");
489
490         NULL_CHECK_RET(file_info);
491
492         file_path = file_info->file_path;
493
494         NULL_CHECK_RET(file_path);
495
496         ptr = strrchr(file_path, '/');
497         if (ptr) {
498                 ptr++;
499                 ptr2 = strrchr(ptr, '.');
500                 if (ptr2) {
501                         int len = 0;
502                         len = ptr2 - ptr;
503                         ptr2++;
504                         extension = strdup(ptr2);
505                         file_name = calloc(1, len + 1);
506                         if (file_name)
507                                 snprintf(file_name, len + 1, "%s", ptr);
508                 } else {
509                         file_name = strdup(ptr);
510                 }
511         }
512
513         if (file_name && !file_info->pure_file_name) {
514                 file_info->pure_file_name = file_name;
515                 file_name = DA_NULL;
516         } else {
517                 free(file_name);
518         }
519         if (extension && !file_info->extension) {
520                 DA_LOGV("candidate extension [%s]", extension);
521                 file_info->extension = extension;
522                 extension = DA_NULL;
523         } else {
524                 free(extension);
525         }
526         return ret;
527 }
528
529 da_ret_t start_file_writing(da_info_t *da_info)
530 {
531         da_ret_t ret = DA_RESULT_OK;
532         file_info_t *file_info = DA_NULL;
533         req_info_t *req_info = DA_NULL;
534
535         DA_LOGV("");
536
537         NULL_CHECK_RET(da_info);
538         file_info = da_info->file_info;
539         NULL_CHECK_RET(file_info);
540         req_info = da_info->req_info;
541         NULL_CHECK_RET(req_info);
542
543         /* resume case */
544         if (req_info->etag || req_info->temp_file_path) {
545                 char *file_path = DA_NULL;
546                 char *origin_path = DA_NULL;
547                 file_path = req_info->temp_file_path;
548                 if (!file_path)
549                         return DA_ERR_INVALID_ARGUMENT;
550                 origin_path = file_info->file_path;
551                 file_info->file_path = strdup(file_path);
552                 free(origin_path);
553                 if (file_info)
554                         ret = __decide_file_path_for_resume(file_info);
555         } else {
556                 ret = __decide_file_path(da_info);
557         }
558
559         if (ret != DA_RESULT_OK)
560                 goto ERR;
561
562         if (req_info->etag || req_info->temp_file_path) {
563                 da_size_t file_size = 0;
564                 get_file_size(req_info->temp_file_path, &file_size);
565                 if (file_size < 1)
566                         goto ERR;
567 #ifdef _RAF_SUPPORT
568                 file_info->file_size_of_temp_file = file_size;
569 #endif
570                 file_info->bytes_written_to_file = file_size;
571         } else {
572                 file_info->bytes_written_to_file = 0;
573         }
574         ret = __saved_file_open(file_info);
575 ERR:
576         return ret;
577 }
578
579 da_ret_t start_file_append(file_info_t *file_info)
580 {
581         da_ret_t ret = DA_RESULT_OK;
582
583         DA_LOGV("");
584
585         NULL_CHECK_RET(file_info);
586
587         ret = __saved_file_open(file_info);
588         return ret;
589 }
590
591 da_ret_t file_write_ongoing(file_info_t *file_info, char *body, int body_len)
592 {
593         da_ret_t ret = DA_RESULT_OK;
594         size_t buffer_size = 0;
595         char *buffer = DA_NULL;
596
597         DA_LOGV("");
598
599         buffer = file_info->buffer;
600         buffer_size = file_info->buffer_len;
601
602         if (DA_NULL == buffer) {
603                 if (body_len < DA_FILE_BUF_SIZE) {
604                         ret = __file_write_buf_make_buf(file_info);
605                         if (ret != DA_RESULT_OK)
606                                 goto ERR;
607                         __file_write_buf_copy_to_buf(file_info, body, body_len);
608                 } else {
609                         ret = __file_write_buf_directly_write(file_info,
610                                         body, body_len);
611                         if (ret != DA_RESULT_OK)
612                                 goto ERR;
613                 }
614         } else {
615                 if (DA_FILE_BUF_SIZE <= body_len) {
616                         ret = __file_write_buf_flush_buf(file_info);
617                         if (ret != DA_RESULT_OK)
618                                 goto ERR;
619                         ret = __file_write_buf_directly_write(file_info,
620                                         body, body_len);
621                         if (ret != DA_RESULT_OK)
622                                 goto ERR;
623                 } else if ((DA_FILE_BUF_SIZE - buffer_size) <= body_len) {
624                         ret = __file_write_buf_flush_buf(file_info);
625                         if (ret != DA_RESULT_OK)
626                                 goto ERR;
627                         __file_write_buf_copy_to_buf(file_info, body, body_len);
628                 } else {
629                         __file_write_buf_copy_to_buf(file_info, body, body_len);
630                 }
631         }
632 ERR:
633         if (ret != DA_RESULT_OK) {
634                 file_info->buffer_len = 0;
635                 free(file_info->buffer);
636                 file_info->buffer = DA_NULL;
637         }
638         return ret;
639 }
640
641 #ifdef _RAF_SUPPORT
642 da_ret_t file_write_complete_for_raf(file_info_t *file_info)
643 {
644         da_ret_t ret = DA_RESULT_OK;
645         char *buffer = DA_NULL;
646         da_size_t wrriten_size = 0;
647         da_size_t file_size = 0;
648         void *fd = DA_NULL;
649
650         DA_LOGV("");
651         fd = file_info->file_handle;
652
653         wrriten_size = file_info->bytes_written_to_file;
654         // test code
655         get_file_size(file_info->file_path, &file_size);
656         DA_LOGI("wrriten_size:%llu file_size:%llu file[%s]",
657                         wrriten_size, file_size, file_info->file_path);
658         if (fd) {
659                 fclose(fd);
660                 fd = DA_NULL;
661         }
662         file_info->file_handle = DA_NULL;
663         if (wrriten_size < file_size) {
664                 DA_LOGD("Try truncate");
665                 if (truncate(file_info->file_path, wrriten_size) < 0)
666                         DA_LOGE("Fail to ftruncate: errno[%d]", errno);
667                 DA_LOGD("Try truncate done");
668         }
669
670 ERR:
671         return ret;
672 }
673 #endif
674
675 da_ret_t file_write_complete(file_info_t *file_info)
676 {
677         da_ret_t ret = DA_RESULT_OK;
678         char *buffer = DA_NULL;
679         unsigned int buffer_size = 0;
680         void *fd = DA_NULL;
681
682         DA_LOGV("");
683
684         buffer = file_info->buffer;
685         buffer_size = file_info->buffer_len;
686
687         if (DA_NULL == buffer) {
688                 DA_LOGE("file buffer is NULL");
689         } else {
690                 if (buffer_size != 0) {
691                         ret = __file_write_buf_flush_buf(file_info);
692                         if (ret != DA_RESULT_OK)
693                                 goto ERR;
694                 }
695                 __file_write_buf_destroy_buf(file_info);
696         }
697         fd = file_info->file_handle;
698
699         if (fd) {
700                 fclose(fd);
701                 fd = DA_NULL;
702         }
703         file_info->file_handle = DA_NULL;
704 ERR:
705         return ret;
706 }
707
708 da_ret_t discard_download(file_info_t *file_info)
709 {
710         da_ret_t ret = DA_RESULT_OK;
711         FILE *f_handle = DA_NULL;
712
713         DA_LOGV("");
714
715         f_handle = file_info->file_handle;
716         if (f_handle) {
717                 fclose(f_handle);
718                 file_info->file_handle = DA_NULL;
719         }
720         return ret;
721 }
722
723 void clean_paused_file(file_info_t *file_info)
724 {
725         char *paused_file_path = DA_NULL;
726         FILE *fd = DA_NULL;
727
728         DA_LOGV("");
729
730         fd = file_info->file_handle;
731         if (fd) {
732                 fclose(fd);
733                 file_info->file_handle = DA_NULL;
734         }
735
736         paused_file_path = file_info->file_path;
737         file_info->bytes_written_to_file = 0; // Ignore resume flow after failed or cancled.
738         remove_file((const char*) paused_file_path);
739
740         return;
741 }
742
743 static file_state_e get_file_state(const char *file_path)
744 {
745         struct stat dir_state;
746         int stat_ret;
747
748         if (file_path == DA_NULL) {
749                 DA_LOGE("file path is DA_NULL");
750                 return NOT_EXIST_FILE;
751         }
752         stat_ret = stat(file_path, &dir_state);
753         if (stat_ret == 0) {
754                 if (dir_state.st_mode & S_IFREG) {
755                         //DA_SECURE_LOGD("Exist! %s is a regular file & its size = %lu", file_path, dir_state.st_size);
756                         return REGULAR_FILE;
757                 }
758
759                 return OTHER_FILE;
760         }
761         return NOT_EXIST_FILE;
762 }
763
764 void get_file_size(char *file_path, da_size_t *out_file_size)
765 {
766         struct stat dir_state;
767         int stat_ret;
768
769         *out_file_size = -1;
770
771         if (file_path == DA_NULL) {
772                 DA_LOGE("file path is DA_NULL");
773                 return;
774         }
775         /* Please do not use ftell() to obtain file size, use stat instead.
776          *  This is a guide from www.securecoding.cert.org
777          *    : FIO19-C. Do not use fseek() and ftell() to compute the size of a file
778          */
779         stat_ret = stat(file_path, &dir_state);
780         if (stat_ret == 0) {
781                 if (dir_state.st_mode & S_IFREG) {
782                         DA_LOGV("size = %lu", dir_state.st_size);
783                         *out_file_size = dir_state.st_size;
784                 }
785         }
786         return;
787 }
788
789 char *get_full_path_avoided_duplication(const char *in_dir,
790                 char *in_candidate_file_name, char *in_extension)
791 {
792         char *dir = NULL;
793         char *file_name = in_candidate_file_name;
794         char *extension = in_extension;
795         char *final_path = DA_NULL;
796
797         int dir_path_len = 0;
798         int final_path_len = 0;
799         int extension_len = 0;
800
801         int suffix_count = 0;   /* means suffix on file name. up to "_99" */
802         int suffix_len = (int)log10(MAX_SUFFIX_COUNT + 1) + 1;  /* 1 means "_" */
803
804         if (!in_dir || !in_candidate_file_name)
805                 return DA_NULL;
806
807         dir = strdup(in_dir);
808         if (!dir)
809                 return DA_NULL;
810
811         //DA_SECURE_LOGI("in_candidate_file_name=[%s],in_extension=[%s]",
812         //in_candidate_file_name, in_extension);
813
814         if (extension)
815                 extension_len = strlen(extension);
816
817         // to remove trailing slash from dir path
818         dir_path_len = strlen(dir);
819         if (dir[dir_path_len - 1] == '/') {
820                 dir[dir_path_len - 1] = '\0';
821                 --dir_path_len;
822         }
823
824         /* first 1 for "/", second 1 for ".", last 1 for DA_NULL */
825         final_path_len = dir_path_len + 1 + strlen(file_name) + 1
826                 + suffix_len + extension_len + 1;
827
828         final_path = (char*)calloc(1, final_path_len);
829         if (!final_path) {
830                 DA_LOGE("DA_ERR_FAIL_TO_MEMALLOC");
831                 free(dir);
832                 return DA_NULL;
833         }
834
835         do {
836                 /* e.g) /tmp/abc.jpg
837                  * if there is no extension name, just make a file name without extension */
838                 if (0 == extension_len) {
839                         if (suffix_count == 0) {
840                                 snprintf(final_path, final_path_len,
841                                                 "%s/%s", dir, file_name);
842                         } else {
843                                 snprintf(final_path, final_path_len,
844                                                 "%s/%s_%d", dir, file_name, suffix_count);
845                         }
846                 } else {
847                         if (suffix_count == 0) {
848                                 snprintf(final_path, final_path_len,
849                                                 "%s/%s.%s", dir, file_name, extension);
850                         } else {
851                                 snprintf(final_path, final_path_len,
852                                                 "%s/%s_%d.%s",
853                                                 dir, file_name, suffix_count, extension);
854                         }
855                 }
856                 if (get_file_state(final_path) != NOT_EXIST_FILE) {
857                         suffix_count++;
858                         if (suffix_count > MAX_SUFFIX_COUNT) {
859                                 free(final_path);
860                                 final_path = DA_NULL;
861                                 break;
862                         } else {
863                                 memset(final_path, 0x00, final_path_len);
864                                 continue;
865                         }
866                 }
867                 break;
868         } while (1);
869
870         //DA_SECURE_LOGD("decided path = [%s]", final_path);
871         free(dir);
872
873         return final_path;
874 }
875
876
877 da_ret_t check_drm_convert(file_info_t *file_info)
878 {
879         da_ret_t ret = DA_RESULT_OK;
880 #ifdef _ENABLE_OMA_DRM
881         da_bool_t ret_b = DA_TRUE;
882 #endif
883
884         DA_LOGD("");
885
886         NULL_CHECK_RET(file_info);
887
888 #ifdef _ENABLE_OMA_DRM
889
890         /* In case of OMA DRM 1.0 SD, it is not necessary to call DRM convert API.
891          * Because it is already converted itself.
892          * And, the case will return fail because The SD is not supported now.
893          */
894         if (is_content_drm_dcf(file_info->mime_type)) {
895                 DA_LOGI("DRM SD case");
896                 unlink(file_info->file_path);
897                 free(file_info->file_path);
898                 file_info->file_path = DA_NULL;
899                 return DA_ERR_DRM_FAIL;
900         }
901         if (is_content_drm_dm(file_info->mime_type)) {
902                 char *actual_file_path = DA_NULL;
903                 char *out_file_path = DA_NULL;
904
905                 actual_file_path = file_info->file_path;
906                 DA_SECURE_LOGD("actual_file_path = %s", actual_file_path);
907                 if (!actual_file_path)
908                         return DA_ERR_INVALID_ARGUMENT;
909                 ret_b = EDRM_convert(actual_file_path, &out_file_path);
910                 unlink(actual_file_path);
911                 free(actual_file_path);
912                 if (!ret_b)
913                         ret = DA_ERR_DRM_FAIL;
914                 file_info->file_path = out_file_path;
915         } else {
916                 return ret;
917         }
918 #endif
919
920         return ret;
921 }
922
923 void remove_file(const char *file_path)
924 {
925         DA_LOGV("");
926
927         if (file_path && get_file_state(file_path) == REGULAR_FILE) {
928                 DA_SECURE_LOGD("remove file [%s]", file_path);
929                 if (unlink(file_path) < 0)
930                         DA_LOGE("file removing failed.");
931         }
932 }
933
934 da_ret_t get_available_memory(const char *dir_path, da_size_t len)
935 {
936         da_ret_t ret = DA_RESULT_OK;
937         int fs_ret = 0;
938         struct statfs filesys_info = {0, };
939         //struct statvfs filesys_info;
940
941         DA_LOGV("");
942
943         if (!dir_path)
944                 return DA_ERR_INVALID_INSTALL_PATH;
945         DA_LOGD("Dir_path : %s", dir_path);
946
947         fs_ret = statfs(dir_path, &filesys_info);
948         // Using this as it considers FOTA memory while returning available memory
949         //fs_ret = storage_get_internal_memory_size(&filesys_info);
950
951         if (fs_ret != 0) {
952                 //      DA_LOGE("statfs error[%s]", strerror(errno));
953                 return DA_ERR_INVALID_ARGUMENT;
954                 //      return DA_ERR_INVALID_INSTALL_PATH;
955         }
956
957         double available_size =  (double)filesys_info.f_frsize * filesys_info.f_bavail;
958         double total_size = (double)filesys_info.f_frsize * filesys_info.f_blocks;
959         DA_SECURE_LOGI(" total = %lf ", total_size);
960         DA_SECURE_LOGI(" available = %lf ", available_size);
961
962         DA_LOGV("Available Memory(f_bavail) : %lu", filesys_info.f_bavail);
963         DA_LOGV("Available Memory(f_bsize) : %d", filesys_info.f_bsize);
964         DA_LOGD("Available Memory(kbytes) : %lu", (filesys_info.f_bavail/1024)*filesys_info.f_bsize);
965         DA_LOGV("Content: %llu", len);
966         if (available_size < (len
967                                 + SAVE_FILE_BUFFERING_SIZE_50KB)) /* 50KB buffering */
968                 ret = DA_ERR_DISK_FULL;
969
970         return ret;
971 }