fb2b44b4b22243eab48584c6ad18f3ee3e986e57
[platform/core/connectivity/mtp-responder.git] / src / util / mtp_support.c
1 /*
2  * Copyright (c) 2012, 2013 Samsung Electronics Co., Ltd.
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 <glib.h>
18 #include <glib/gprintf.h>
19 #include <unistd.h>
20 #include <sys/wait.h>
21 #include <stdint.h>
22 #include "mtp_support.h"
23 #include "ptp_datacodes.h"
24 #include "mtp_util.h"
25
26 /*
27  * STATIC FUNCTIONS
28  */
29 static mtp_char *__util_conv_int_to_hex_str(mtp_int32 int_val, mtp_char *str);
30
31 /*
32  * FUNCTIONS
33  */
34 /* LCOV_EXCL_START */
35 void _util_conv_byte_order(void *data, mtp_int32 size)
36 {
37         mtp_int32 idx;
38         mtp_uchar temp;
39         mtp_int32 h_size;
40         mtp_uchar *l_data = (mtp_uchar *)data;
41
42         ret_if(data == NULL);
43         retm_if(size <= 1, "size(%d) is invalid", size);
44
45         h_size = size / 2;
46         for (idx = 0; idx < h_size; idx++) {
47                 temp = l_data[idx];
48                 l_data[idx] = l_data[size - idx - 1];
49                 l_data[size - idx - 1] = temp;
50         }
51
52         return;
53 }
54
55 void _util_conv_byte_order_wstring(mtp_uint16 *wstr, mtp_int32 size)
56 {
57         _util_conv_byte_order_gen_str(wstr, size, sizeof(mtp_uint16));
58         return;
59 }
60
61 void _util_conv_byte_order_gen_str(void *str, mtp_int32 size, mtp_int32 elem_sz)
62 {
63         mtp_int32 idx;
64         mtp_int32 f_size = size * elem_sz;
65         mtp_uchar *l_str = (mtp_uchar *)str;
66
67         ret_if(str == NULL);
68         retm_if(elem_sz <= 1, "elem_sz(%d) is invalid", elem_sz);
69
70         for (idx = 0; idx < f_size; idx += elem_sz)
71                 _util_conv_byte_order(&(l_str[idx]), elem_sz);
72
73         return;
74 }
75 /* LCOV_EXCL_STOP */
76
77 /*
78  * If items_written is greater than or equal to dest_size,
79  * src is truncated when it is copied to dest.
80  */
81 mtp_int32 _util_utf16_to_utf8(char *dest, mtp_int32 dest_size,
82                 const mtp_wchar *src)
83 {
84         gchar *utf8 = NULL;
85         GError *error = NULL;
86         glong items_read = 0;
87         glong items_written = 0;
88         const gunichar2 *utf16 = (const gunichar2 *)src;
89
90         retv_if(src == NULL, 0);
91         retv_if(dest == NULL, 0);
92
93         utf8 = g_utf16_to_utf8(utf16, -1, &items_read, &items_written, &error);
94         if (utf8 == NULL) {
95                 /* LCOV_EXCL_START */
96                 ERR("%s\n", error->message);
97                 g_error_free(error);
98
99                 dest[0] = '\0';
100                 items_written = 0;
101                 /* LCOV_EXCL_STOP */
102         } else {
103                 g_strlcpy(dest, (char *)utf8, dest_size);
104                 g_free(utf8);
105         }
106
107         return (mtp_int32)items_written;
108 }
109
110 /*
111  * If items_written is greater than or equal to dest_items,
112  * src is truncated when it is copied to dest.
113  */
114 mtp_int32 _util_utf8_to_utf16(mtp_wchar *dest, mtp_int32 dest_items,
115                 const char *src)
116 {
117         GError *error = NULL;
118         glong items_read = 0;
119         gunichar2 *utf16 = NULL;
120         glong items_written = 0;
121
122         retv_if(src == NULL, 0);
123         retv_if(dest == NULL, 0);
124
125         utf16 = g_utf8_to_utf16(src, -1, &items_read, &items_written, &error);
126         if (utf16 == NULL) {
127                 /* LCOV_EXCL_START */
128                 ERR("%s\n", error->message);
129                 g_error_free(error);
130                 error = NULL;
131
132                 dest[0] = (mtp_wchar)'\0';
133                 items_written = 0;
134                 /* LCOV_EXCL_STOP */
135         } else {
136                 _util_wchar_ncpy(dest, utf16, dest_items);
137                 g_free(utf16);
138         }
139
140         return (mtp_int32)items_written;
141 }
142
143
144 /*
145  * Copies a unicode string.
146  * @param[in]   src     Null-terminated source string
147  * @param[out]  dest    Destination buffer
148  * @return      None
149  */
150 /* LCOV_EXCL_START */
151 void _util_wchar_cpy(mtp_wchar *dest, const mtp_wchar *src)
152 {
153         ret_if(src == NULL);
154         ret_if(dest == NULL);
155
156         if (!((uintptr_t)dest & 0x1) && !((uintptr_t)src & 0x1)) {
157                 /* 2-byte aligned */
158                 mtp_wchar *temp = dest;
159
160                 while ((*temp++ = *src++) != '\0')
161                         ;       /* DO NOTHING  */
162         } else {
163                 /* not-aligned, byte to byte approach - slow */
164                 mtp_char *pc1 = (mtp_char *)dest;
165                 mtp_char *pc2 = (mtp_char *)src;
166
167                 while (*pc2 || *(pc2 + 1)) {
168                         *pc1 = *pc2;
169                         *(pc1 + 1) = *(pc2 + 1);
170
171                         pc1 += 2;
172                         pc2 += 2;
173                 }
174         }
175
176         return;
177 }
178 /* LCOV_EXCL_STOP */
179
180 /*
181  * Copies a unicode string a given numbers of character.
182  * @param[in]   src     Null-terminated source string
183  * @param[out]  dest    Destination buffer
184  * @return      None
185  */
186 void _util_wchar_ncpy(mtp_wchar *dest, const mtp_wchar *src, unsigned long n)
187 {
188         char *pc1 = NULL;
189         char *pc2 = NULL;
190         mtp_wchar *temp = NULL;
191
192         ret_if(src == NULL);
193         ret_if(dest == NULL);
194
195         if (!((uintptr_t)dest & 0x1) && !((uintptr_t)src & 0x1)) {      /* 2-byte aligned */
196                 temp = dest;
197
198                 while (n && (*temp++ = *src++))
199                         n--;
200
201                 if (n) {
202                         while (--n)
203                                 *temp++ = 0;
204                 }
205         } else {                /* not-aligned, byte to byte approach - slow */
206
207                 pc1 = (char *)dest;
208                 pc2 = (char *)src;
209
210                 /* LCOV_EXCL_START */
211                 while (n && (*pc2 || *(pc2 + 1))) {
212                         --n;
213                         *pc1++ = *pc2++;
214                         *pc1++ = *pc2++;
215                 }
216
217                 if (n) {
218                         while (--n) {
219                                 *pc1++ = 0;
220                                 *pc1++ = 0;
221                         }
222                 }
223         }
224
225         return;
226 }
227
228 /*
229  * Returns the length of wide character string
230  * @param[in]   src     Wide char string
231  * @return      length
232  */
233 size_t _util_wchar_len(const mtp_wchar *s)
234 {
235         if (!((uintptr_t)s & 0x1)) {    /* 2-byte aligned */
236                 mtp_wchar *temp = (mtp_wchar *)s;
237
238                 while (*temp++)
239                         /* DO NOTHING */ ;
240
241                 DBG("Length : %zd\n", temp - s - 1);
242                 return ((size_t)(temp - s - 1));
243         } else {                /* not-aligned, byte to byte approach - slow */
244
245                 unsigned char *temp = (unsigned char *)s;
246
247                 while (*temp || *(temp + 1))
248                         temp += 2;
249
250                 DBG("Length : %zd\n", (temp - (unsigned char *)s) / 2);
251                 return ((size_t) (temp - (unsigned char *)s) / 2);
252         }
253 }
254
255 static mtp_char* __util_conv_int_to_hex_str(mtp_int32 int_val, mtp_char *str)
256 {
257         mtp_char *nstr = str;
258         mtp_int32 val = int_val;
259         mtp_char hex[] = { "0123456789ABCDEF" };
260
261         retv_if(str == NULL, NULL);
262
263         *nstr++ = '0';
264         *nstr++ = 'x';
265
266         for (val = int_val; val; val <<= 4)
267                 *nstr++ = hex[(val >> (sizeof(int) * 8 - 4)) & 0xF];
268
269         *nstr = '\0';
270         return str;
271 }
272
273 /*
274  * This is very minimal implementation.
275  * Be cautious using this function.
276  */
277 mtp_err_t _util_wchar_swprintf(mtp_wchar *mtp_wstr, mtp_int32 size,
278                 mtp_char *format, ...)
279 {
280         mtp_char *ptr;
281         mtp_wchar wsHex[24];
282         mtp_int32 count = 0;
283         mtp_wchar *wbuf = mtp_wstr;
284         mtp_char *bptr_val = NULL;
285         mtp_wchar *wstr_val = NULL;
286         mtp_int32 int_val = 0, len = 0;
287         mtp_char buf[MTP_MAX_PATHNAME_SIZE + 1];
288
289         va_list arg_list;
290         va_start(arg_list, format);
291         for (ptr = format; *ptr && count < (size - 1); ptr++) {
292                 if (*ptr == '%') {
293                         switch (*(ptr + 1)) {
294                         case 'd':
295                                 int_val = va_arg(arg_list, int);
296                                 wbuf[count++] = (mtp_wchar) (int_val + '0');
297                                 ptr++;
298                                 break;
299                         case 's':
300                                 bptr_val = va_arg(arg_list, char *);
301                                 wstr_val = (mtp_wchar *) bptr_val;
302                                 len = _util_wchar_len(wstr_val);
303                                 if (len + count > size - 1) {
304                                         len = size - 1 - count;
305                                         _util_wchar_ncpy(&wbuf[count], wstr_val,
306                                                         len);
307                                 } else {
308                                         _util_wchar_cpy(&wbuf[count], wstr_val);
309                                 }
310                                 count += len;
311                                 ptr++;
312                                 break;
313                         case 'x':
314                                 int_val = va_arg(arg_list, int);
315                                 __util_conv_int_to_hex_str(int_val, buf);
316                                 _util_utf8_to_utf16(wsHex,
317                                                 sizeof(wsHex) / WCHAR_SIZ, buf);
318                                 len = strlen(buf);
319                                 if (len + count > size - 1) {
320                                         len = size - 1 - count;
321                                         _util_wchar_ncpy(&wbuf[count], wsHex,
322                                                         len);
323                                 } else {
324                                         _util_wchar_cpy(&wbuf[count], wsHex);
325                                 }
326                                 count += len;
327                                 ptr++;
328                                 break;
329
330                         default:
331                                 DBG("swprintf not handling: %c", *(ptr + 1));
332                                 break;
333                         }
334                 } else {
335                         wbuf[count++] = (mtp_wchar)(*ptr);
336                 }
337         }
338
339         va_end(arg_list);
340         wbuf[count] = (mtp_wchar)'\0';
341         _util_utf16_to_utf8(buf, sizeof(buf), wbuf);
342
343         return MTP_ERROR_NONE;
344 }
345 /* LCOV_EXCL_STOP */
346
347 mtp_uint16 _util_get_fmtcode(const mtp_char *extn)
348 {
349         static fmt_code_t fmt_code_table[] = {
350                 {"ALB", MTP_FMT_ABSTRACT_AUDIO_ALBUM},
351                 {"MP3", PTP_FMT_MP3},
352                 {"WMA", MTP_FMT_WMA},
353                 {"WMV", MTP_FMT_WMV},
354                 {"JPG", PTP_FMT_IMG_EXIF},
355                 {"GIF", PTP_FMT_IMG_GIF},
356                 {"BMP", PTP_FMT_IMG_BMP},
357                 {"PNG", PTP_FMT_IMG_PNG},
358                 {"ASF", PTP_FMT_ASF},
359                 {"WAV", PTP_FMT_WAVE},
360                 {"AVI", PTP_FMT_AVI},
361                 {"MPG", PTP_FMT_MPEG},
362                 {"TXT", PTP_FMT_TEXT},
363                 {"3GP", MTP_FMT_3GP},
364                 {"ODF", MTP_FMT_3GP},
365                 {"O4A", MTP_FMT_3GP},
366                 {"O4V", MTP_FMT_3GP},
367                 {"MP4", MTP_FMT_MP4},
368                 {"FLAC", MTP_FMT_FLAC},
369                 {"", PTP_FMT_UNDEF}
370         };
371
372         fmt_code_t *p = NULL;
373         p = fmt_code_table;
374
375         while (p->fmt_code != PTP_FMT_UNDEF) {
376                 if (!strncasecmp(extn, p->extn, strlen(p->extn)))
377                         break;
378
379                 p++;
380         }
381
382         /* will return FormatCode or PTP_FORMATCODE_UNDEFINED
383          * if we hit end of list.
384          */
385         return p->fmt_code;
386 }
387
388 /*
389  * This function gets the file extension.
390  * @param[in]           fileName                Specifies the file name.
391  * @param[out]          file_extn               holds the extension of the file.
392  * @return              Returns TRUE on success and FALSE on failure.
393  */
394 mtp_bool _util_get_file_extn(const mtp_char *f_name, mtp_char *f_extn)
395 {
396         char *ptr;
397
398         retv_if(NULL == f_name, FALSE);
399         retv_if(NULL == f_extn, FALSE);
400
401         ptr = strrchr(f_name, '.');
402
403         if (ptr != NULL) {
404                 g_strlcpy(f_extn, ptr + 1, MTP_MAX_PATHNAME_SIZE);
405                 return TRUE;
406         }
407
408         return FALSE;
409 }
410
411 mtp_bool _util_get_file_name(const mtp_char *fullpath, mtp_char *f_name)
412 {
413         mtp_int32 i, j;
414
415         retv_if(f_name == NULL, FALSE);
416         retv_if(fullpath == NULL, FALSE);
417
418         i = strlen(fullpath);
419
420         for (j = 0; i >= 0; i--) {
421                 if (fullpath[i] == '/') {
422                         g_strlcpy(f_name, &fullpath[i + 1], j);
423                         return TRUE;
424                 }
425                 j++;
426         }
427         g_strlcpy(f_name, fullpath, j);
428
429         return TRUE;
430 }
431 /*
432  * This function gives file name without extension.
433  * @param[in]   fullpath        pointer to absolute file path
434  * @param[out]  f_name  gets filled with filename w/o extension
435  * @return      True or False based on success or failure
436  */
437 /* LCOV_EXCL_START */
438 mtp_bool _util_get_file_name_wo_extn(const mtp_char *fullpath, mtp_char *f_name)
439 {
440         mtp_char *fname_ptr;
441         mtp_uint32 fname_len;
442         mtp_char *extn_ptr = NULL;
443
444         retv_if(f_name == NULL, FALSE);
445         retv_if(fullpath == NULL, FALSE);
446
447         fname_ptr = strrchr(fullpath, '/');
448
449         if (fname_ptr == NULL) {
450                 ERR("Invalid File Name");
451                 return FALSE;
452         }
453
454         fname_ptr = fname_ptr + sizeof(char);
455         fname_len = strlen(fname_ptr);
456         extn_ptr = strrchr(fname_ptr, '.');
457
458         if (extn_ptr == NULL) {
459                 g_strlcpy(f_name, fname_ptr, fname_len + 1);
460                 return TRUE;
461         }
462
463         g_strlcpy(f_name, fname_ptr, ((mtp_uint32)(extn_ptr - fname_ptr) + 1));
464         return TRUE;
465 }
466 /* LCOV_EXCL_STOP */
467
468 mtp_bool _util_is_path_len_valid(const mtp_char *path)
469 {
470         mtp_uint32 limit = 0;
471         mtp_uint32 mtp_path_len = 0;
472         mtp_uint32 root_path_len = 0;
473
474         static mtp_uint32 max_store_len = 0;
475         static mtp_bool is_initialized = FALSE;
476         static mtp_uint32 internal_store_len = 0;
477         static mtp_uint32 external_store_len = 0;
478
479         char ext_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
480         char inter_path[MTP_MAX_PATHNAME_SIZE + 1] = { 0 };
481
482         retv_if(path == NULL, FALSE);
483
484         _util_get_external_path(ext_path);
485         _util_get_internal_path(inter_path);
486
487         if (!is_initialized) {
488                 is_initialized = TRUE;
489                 internal_store_len = strlen(inter_path);
490                 external_store_len = strlen(ext_path);
491
492                 max_store_len = internal_store_len > external_store_len ?
493                         internal_store_len : external_store_len;
494
495                 DBG("max store len : [%u]\n", max_store_len);
496         }
497
498         if (!strncmp(path, inter_path, internal_store_len)) {
499                 root_path_len = internal_store_len;
500         } else if (!strncmp(path, ext_path, external_store_len)) {
501                 root_path_len = external_store_len;
502         } else {
503                 ERR("Unknown store's path : %s\n", path);
504                 return FALSE;
505         }
506
507         /* Path len should be calculated except root path(eg. /opt/usr/media) */
508         mtp_path_len = strlen(path) - root_path_len;
509
510         /* MTP_MAX_PATHNAME_SIZE includes maximum length of root path */
511         limit = MTP_MAX_PATHNAME_SIZE - max_store_len;
512
513         if (mtp_path_len > limit) {
514                 ERR("Too long path : [%u] > [%u]\n", mtp_path_len, limit);
515                 return FALSE;
516         }
517
518         return TRUE;
519 }
520
521 mtp_bool _util_create_path(mtp_char *path, mtp_uint32 size, const mtp_char *dir,
522                 const mtp_char *filename)
523 {
524         mtp_int32 ret = 0;
525         mtp_uint32 len = 0;
526
527         retv_if(dir == NULL, FALSE);
528         retv_if(path == NULL, FALSE);
529         retv_if(filename == NULL, FALSE);
530
531         len = strlen(filename);
532         if (len > MTP_MAX_FILENAME_SIZE) {
533         /* LCOV_EXCL_START */
534                 ERR("filename is too long :[%u] > [%u]\n", len,
535                                 MTP_MAX_FILENAME_SIZE);
536                 return FALSE;
537         }
538
539         ret = g_snprintf(path, size, "%s/%s", dir, filename);
540         if (ret > size) {
541                 ERR("path is truncated");
542                 return FALSE;
543         }
544
545         if (_util_is_path_len_valid(path) == FALSE) {
546                 ERR("path length exceeds the limit");
547                 return FALSE;
548         }
549
550         return TRUE;
551 }
552
553 /*
554  * This function gets the parent path.
555  * @param[in]   fullpath        Pointer to a buffer containing full file path.
556  * @param[out]  p_path  Points the buffer to hold parent path.
557  * @return      None
558  */
559 void _util_get_parent_path(const mtp_char *fullpath, mtp_char *p_path)
560 {
561         mtp_char *ptr = NULL;
562
563         ret_if(NULL == p_path);
564         ret_if(NULL == fullpath);
565
566         ptr = strrchr(fullpath, '/');
567         if (!ptr) {
568                 ERR("Path does not have parent path");
569                 return;
570         }
571
572         g_strlcpy(p_path, fullpath, (mtp_uint32)(ptr - fullpath) + 1);
573         return;
574 }
575
576 void _util_conv_wstr_to_guid(mtp_wchar *wstr, mtp_uint64 *guid)
577 {
578         mtp_uint32 skip_idx;
579         mtp_uint32 cur_idx;
580         mtp_uint64 temp[2];
581         mtp_int32 count = 0;
582         mtp_int32 cpy_sz = 0;
583
584         ret_if(wstr == NULL);
585         ret_if(guid == NULL);
586
587         while (wstr[count] != 0)
588                 count++;
589
590         memset(guid, 0, sizeof(temp));
591         skip_idx = sizeof(temp) / sizeof(mtp_wchar);
592
593         for (cur_idx = 0; cur_idx < count; cur_idx += skip_idx) {
594
595                 memset(temp, 0, sizeof(temp));
596                 cpy_sz = (count - cur_idx) * sizeof(mtp_wchar);
597                 if (cpy_sz > sizeof(temp))
598                         cpy_sz = sizeof(temp);
599
600                 memcpy(temp, &(wstr[cur_idx]), cpy_sz);
601                 guid[0] += temp[0];
602                 guid[1] += temp[1];
603         }
604
605         return;
606 }
607
608 mtp_bool _util_get_unique_dir_path(const mtp_char *exist_path,
609                 mtp_char *new_path, mtp_uint32 new_path_buf_len)
610 {
611         mtp_uint32 num_bytes = 0;
612         mtp_uint32 max_value = 1;
613         mtp_uint32 count = 1;
614         mtp_uint32 val = 1;
615         mtp_char *buf = NULL;
616         mtp_uint32 len = strlen(exist_path);
617
618         retv_if(new_path == NULL, FALSE);
619         retv_if(exist_path == NULL, FALSE);
620
621         /* Excluding '_' */
622         num_bytes = new_path_buf_len - len - 1;
623         if (num_bytes <= 0) {
624                 ERR("No space to append data[%d]\n", num_bytes);
625                 return FALSE;
626         }
627
628         if (num_bytes >= MTP_BUF_SIZE_FOR_INT - 1) {
629                 max_value = UINT_MAX;
630         } else {
631                 while (count <= num_bytes) {
632                         max_value *= 10;
633                         count++;
634                 }
635                 DBG("max_value[%u]\n", max_value);
636         }
637
638         buf = (mtp_char *)g_malloc(new_path_buf_len);
639         if (buf == NULL) {
640                 ERR("g_malloc Fail");
641                 return FALSE;
642         }
643         g_strlcpy(buf, exist_path, new_path_buf_len);
644         while (val < max_value) {
645                 /* Including NUL and '_' */
646                 g_snprintf(&buf[len], num_bytes + 2, "_%u", val++);
647                 if (access(buf, F_OK) < 0)
648                         goto SUCCESS;
649         }
650
651         g_free(buf);
652         ERR("Unable to generate Unique Dir Name");
653         return FALSE;
654
655 SUCCESS:
656         g_strlcpy(new_path, buf, new_path_buf_len);
657         g_free(buf);
658         DBG_SECURE("Unique dir name[%s]\n", new_path);
659         return TRUE;
660 }
661
662 mtp_int32 _util_system_cmd_wait(const mtp_char *cmd)
663 {
664
665         int pid = 0;
666         int status = 0;
667
668         if (cmd == NULL)
669                 return -1;
670
671         pid = fork();
672
673         if (pid == -1)
674                 return -1;
675
676         if (pid == 0) {
677                 char *argv[4];
678                 argv[0] = "sh";
679                 argv[1] = "-c";
680                 argv[2] = (char*)cmd;
681                 argv[3] = 0;
682                 execv("/bin/sh", argv);
683                 exit(127);
684         }
685
686         do {
687                 if (waitpid(pid, &status, 0) == -1) {
688                         if (errno != EINTR)
689                                 return -1;
690                 } else {
691                         return status;
692                 }
693         } while (1);
694 }
695 /* LCOV_EXCL_STOP */