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