Change timeout value to 5sec when get object
[platform/upstream/libmtp.git] / src / libmtp.c
1 /**
2  * \file libmtp.c
3  *
4  * Copyright (C) 2005-2011 Linus Walleij <triad@df.lth.se>
5  * Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
6  * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
7  * Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
8  * Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *
25  * This file provides an interface "glue" to the underlying
26  * PTP implementation from libgphoto2. It uses some local
27  * code to convert from/to UTF-8 (stored in unicode.c/.h)
28  * and some small utility functions, mainly for debugging
29  * (stored in util.c/.h).
30  *
31  * The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
32  * plain copied from the libhphoto2 codebase.
33  *
34  * The files libusb-glue.c/.h are just what they say: an
35  * interface to libusb for the actual, physical USB traffic.
36  */
37 #include "config.h"
38 #include "libmtp.h"
39 #include "unicode.h"
40 #include "ptp.h"
41 #include "libusb-glue.h"
42 #include "device-flags.h"
43 #include "playlist-spl.h"
44 #include "util.h"
45
46 #include "mtpz.h"
47
48 #include <stdlib.h>
49 #include <limits.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <fcntl.h>
55 #include <time.h>
56 #include <errno.h>
57 #ifdef TIZEN_EXT
58 #include <libexif/exif-data.h>
59 #endif /* TIZEN_EXT */
60 #ifdef _MSC_VER // For MSVC++
61 #define USE_WINDOWS_IO_H
62 #include <io.h>
63 #endif
64
65
66 /**
67  * Global debug level
68  * We use a flag system to enable a part of logs.
69  *
70  * The LIBMTP_DEBUG environment variable sets the debug flags for any binary
71  * that uses libmtp and calls LIBMTP_Init. The value can be given in decimal
72  * (must not start with "0" or it will be interpreted in octal), or in
73  * hexadecimal (must start with "0x").
74  *
75  * The value "-1" enables all debug flags.
76  *
77  * Some of the utilities in examples/ also take a command-line flag "-d" that
78  * enables LIBMTP_DEBUG_PTP and LIBMTP_DEBUG_DATA (same as setting
79  * LIBMTP_DEBUG=9).
80  *
81  * Flags (combine by adding the hex values):
82  *  0x00 [0000 0000] : LIBMTP_DEBUG_NONE  : no debug (default)
83  *  0x01 [0000 0001] : LIBMTP_DEBUG_PTP   : PTP debug
84  *  0x02 [0000 0010] : LIBMTP_DEBUG_PLST  : Playlist debug
85  *  0x04 [0000 0100] : LIBMTP_DEBUG_USB   : USB debug
86  *  0x08 [0000 1000] : LIBMTP_DEBUG_DATA  : USB data debug
87  *
88  * (Please keep this list in sync with libmtp.h.)
89  */
90 int LIBMTP_debug = LIBMTP_DEBUG_NONE;
91
92
93 /*
94  * This is a mapping between libmtp internal MTP filetypes and
95  * the libgphoto2/PTP equivalent defines. We need this because
96  * otherwise the libmtp.h device has to be dependent on ptp.h
97  * to be installed too, and we don't want that.
98  */
99 //typedef struct filemap_struct filemap_t;
100 typedef struct filemap_struct {
101   char *description; /**< Text description for the file type */
102   LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
103   uint16_t ptp_id; /**< PTP ID for the filetype */
104   struct filemap_struct *next;
105 } filemap_t;
106
107 /*
108  * This is a mapping between libmtp internal MTP properties and
109  * the libgphoto2/PTP equivalent defines. We need this because
110  * otherwise the libmtp.h device has to be dependent on ptp.h
111  * to be installed too, and we don't want that.
112  */
113 typedef struct propertymap_struct {
114   char *description; /**< Text description for the property */
115   LIBMTP_property_t id; /**< LIBMTP internal type for the property */
116   uint16_t ptp_id; /**< PTP ID for the property */
117   struct propertymap_struct *next;
118 } propertymap_t;
119
120 // Global variables
121 // This holds the global filetype mapping table
122 static filemap_t *g_filemap = NULL;
123 // This holds the global property mapping table
124 static propertymap_t *g_propertymap = NULL;
125
126 /*
127  * Forward declarations of local (static) functions.
128  */
129 static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
130                              uint16_t const ptp_id);
131 static void init_filemap();
132 static int register_property(char const * const description, LIBMTP_property_t const id,
133                              uint16_t const ptp_id);
134 static void init_propertymap();
135 static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
136                                     LIBMTP_error_number_t errornumber,
137                                     char const * const error_text);
138 static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
139                                         uint16_t ptp_error,
140                                         char const * const error_text);
141 static void flush_handles(LIBMTP_mtpdevice_t *device);
142 static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
143                                     PTPParams *params,
144                                     uint32_t storageid,
145                                     uint32_t parent);
146 static void free_storage_list(LIBMTP_mtpdevice_t *device);
147 static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
148 static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
149                                         uint64_t fitsize);
150 static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
151                                  LIBMTP_devicestorage_t *storage,
152                                  uint64_t *freespace);
153 static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
154                               LIBMTP_devicestorage_t *storage,
155                               uint64_t const filesize);
156 static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
157 static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
158 static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty);
159 static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t intype);
160 static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
161                                        char **unicstring, uint16_t property);
162 static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd);
163 static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd);
164 static char *get_iso8601_stamp(void);
165 static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
166                                     uint16_t const attribute_id);
167 static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
168                                     uint16_t const attribute_id, uint64_t const value_default);
169 static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
170                                     uint16_t const attribute_id, uint32_t const value_default);
171 static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
172                                     uint16_t const attribute_id, uint16_t const value_default);
173 static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
174                                   uint16_t const attribute_id, uint8_t const value_default);
175 static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
176                              uint16_t const attribute_id, char const * const string);
177 static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
178                           uint16_t const attribute_id, uint32_t const value);
179 static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
180                           uint16_t const attribute_id, uint16_t const value);
181 static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
182                          uint16_t const attribute_id, uint8_t const value);
183 static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
184                                LIBMTP_track_t *track);
185 static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent);
186 static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
187                                     char const * const name,
188                                     char const * const artist,
189                                     char const * const composer,
190                                     char const * const genre,
191                                     uint32_t const parenthandle,
192                                     uint32_t const storageid,
193                                     uint16_t const objectformat,
194                                     char const * const suffix,
195                                     uint32_t * const newid,
196                                     uint32_t const * const tracks,
197                                     uint32_t const no_tracks);
198 static int update_abstract_list(LIBMTP_mtpdevice_t *device,
199                                 char const * const name,
200                                 char const * const artist,
201                                 char const * const composer,
202                                 char const * const genre,
203                                 uint32_t const objecthandle,
204                                 uint16_t const objectformat,
205                                 uint32_t const * const tracks,
206                                 uint32_t const no_tracks);
207 static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata);
208 static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
209 static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
210 static int set_object_filename(LIBMTP_mtpdevice_t *device,
211                 uint32_t object_id,
212                 uint16_t ptp_type,
213                 const char **newname);
214 static char *generate_unique_filename(PTPParams* params, char const * const filename);
215 static int check_filename_exists(PTPParams* params, char const * const filename);
216
217 #ifdef TIZEN_EXT
218 int _is_exist_handler(uint32_t **object_list, int size, int current_handler);
219 #endif /* TIZEN_EXT */
220
221 /**
222  * These are to wrap the get/put handlers to convert from the MTP types to PTP types
223  * in a reliable way
224  */
225 typedef struct _MTPDataHandler {
226         MTPDataGetFunc          getfunc;
227         MTPDataPutFunc          putfunc;
228         void                    *priv;
229 } MTPDataHandler;
230
231 static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen);
232 static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen);
233
234 /**
235  * Checks if a filename ends with ".ogg". Used in various
236  * situations when the device has no idea that it support
237  * OGG but still does.
238  *
239  * @param name string to be checked.
240  * @return 0 if this does not end with ogg, any other
241  *           value means it does.
242  */
243 static int has_ogg_extension(char *name) {
244   char *ptype;
245
246   if (name == NULL)
247     return 0;
248   ptype = strrchr(name,'.');
249   if (ptype == NULL)
250     return 0;
251   if (!strcasecmp (ptype, ".ogg"))
252     return 1;
253   return 0;
254 }
255
256 /**
257  * Checks if a filename ends with ".flac". Used in various
258  * situations when the device has no idea that it support
259  * FLAC but still does.
260  *
261  * @param name string to be checked.
262  * @return 0 if this does not end with flac, any other
263  *           value means it does.
264  */
265 static int has_flac_extension(char *name) {
266   char *ptype;
267
268   if (name == NULL)
269     return 0;
270   ptype = strrchr(name,'.');
271   if (ptype == NULL)
272     return 0;
273   if (!strcasecmp (ptype, ".flac"))
274     return 1;
275   return 0;
276 }
277
278
279
280 /**
281  * Create a new file mapping entry
282  * @return a newly allocated filemapping entry.
283  */
284 static filemap_t *new_filemap_entry()
285 {
286   filemap_t *filemap;
287
288   filemap = (filemap_t *)malloc(sizeof(filemap_t));
289
290   if( filemap != NULL ) {
291     filemap->description = NULL;
292     filemap->id = LIBMTP_FILETYPE_UNKNOWN;
293     filemap->ptp_id = PTP_OFC_Undefined;
294     filemap->next = NULL;
295   }
296
297   return filemap;
298 }
299
300 /**
301  * Register an MTP or PTP filetype for data retrieval
302  *
303  * @param description Text description of filetype
304  * @param id libmtp internal filetype id
305  * @param ptp_id PTP filetype id
306  * @return 0 for success any other value means error.
307 */
308 static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
309                              uint16_t const ptp_id)
310 {
311   filemap_t *new = NULL, *current;
312
313   // Has this LIBMTP filetype been registered before ?
314   current = g_filemap;
315   while (current != NULL) {
316     if(current->id == id) {
317       break;
318     }
319     current = current->next;
320   }
321
322   // Create the entry
323   if(current == NULL) {
324     new = new_filemap_entry();
325     if(new == NULL) {
326       return 1;
327     }
328
329     new->id = id;
330     if(description != NULL) {
331       new->description = strdup(description);
332     }
333     new->ptp_id = ptp_id;
334
335     // Add the entry to the list
336     if(g_filemap == NULL) {
337       g_filemap = new;
338     } else {
339       current = g_filemap;
340       while (current->next != NULL ) current=current->next;
341       current->next = new;
342     }
343     // Update the existing entry
344   } else {
345     if (current->description != NULL) {
346       free(current->description);
347     }
348     current->description = NULL;
349     if(description != NULL) {
350       current->description = strdup(description);
351     }
352     current->ptp_id = ptp_id;
353   }
354
355   return 0;
356 }
357
358 static void init_filemap()
359 {
360   register_filetype("Folder", LIBMTP_FILETYPE_FOLDER, PTP_OFC_Association);
361   register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
362   register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
363   register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
364   register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
365   register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
366   register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
367   register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
368   register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
369   register_filetype("MPEG-4 Part 14 Container Format (Audio Emphasis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
370   register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Emphasis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
371   register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
372   register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
373   register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
374   register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
375   register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
376   register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
377   register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
378   register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
379   register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
380   register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
381   register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
382   register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
383   register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
384   register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
385   register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
386   register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
387   register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
388   register_filetype("HEIF file", LIBMTP_FILETYPE_HEIF, PTP_OFC_HEIF);
389   register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
390   register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
391   register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
392   register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
393   register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
394   register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
395   register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
396   register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
397   register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
398   register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
399   register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
400   register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
401   register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
402   register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
403   register_filetype("Abstract Album file", LIBMTP_FILETYPE_ALBUM, PTP_OFC_MTP_AbstractAudioAlbum);
404   register_filetype("Abstract Playlist file", LIBMTP_FILETYPE_PLAYLIST, PTP_OFC_MTP_AbstractAudioVideoPlaylist);
405   register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
406 }
407
408 /**
409  * Returns the PTP filetype that maps to a certain libmtp internal file type.
410  * @param intype the MTP library interface type
411  * @return the PTP (libgphoto2) interface type
412  */
413 static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
414 {
415   filemap_t *current;
416
417   current = g_filemap;
418
419   while (current != NULL) {
420     if(current->id == intype) {
421       return current->ptp_id;
422     }
423     current = current->next;
424   }
425   // printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
426   return PTP_OFC_Undefined;
427 }
428
429
430 /**
431  * Returns the MTP internal interface type that maps to a certain ptp
432  * interface type.
433  * @param intype the PTP (libgphoto2) interface type
434  * @return the MTP library interface type
435  */
436 static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
437 {
438   filemap_t *current;
439
440   current = g_filemap;
441
442   while (current != NULL) {
443     if(current->ptp_id == intype) {
444       return current->id;
445     }
446     current = current->next;
447   }
448   // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
449   return LIBMTP_FILETYPE_UNKNOWN;
450 }
451
452 /**
453  * Create a new property mapping entry
454  * @return a newly allocated propertymapping entry.
455  */
456 static propertymap_t *new_propertymap_entry()
457 {
458   propertymap_t *propertymap;
459
460   propertymap = (propertymap_t *)malloc(sizeof(propertymap_t));
461
462   if( propertymap != NULL ) {
463     propertymap->description = NULL;
464     propertymap->id = LIBMTP_PROPERTY_UNKNOWN;
465     propertymap->ptp_id = 0;
466     propertymap->next = NULL;
467   }
468
469   return propertymap;
470 }
471
472 /**
473  * Register an MTP or PTP property for data retrieval
474  *
475  * @param description Text description of property
476  * @param id libmtp internal property id
477  * @param ptp_id PTP property id
478  * @return 0 for success any other value means error.
479 */
480 static int register_property(char const * const description, LIBMTP_property_t const id,
481                              uint16_t const ptp_id)
482 {
483   propertymap_t *new = NULL, *current;
484
485   // Has this LIBMTP propety been registered before ?
486   current = g_propertymap;
487   while (current != NULL) {
488     if(current->id == id) {
489       break;
490     }
491     current = current->next;
492   }
493
494   // Create the entry
495   if(current == NULL) {
496     new = new_propertymap_entry();
497     if(new == NULL) {
498       return 1;
499     }
500
501     new->id = id;
502     if(description != NULL) {
503       new->description = strdup(description);
504     }
505     new->ptp_id = ptp_id;
506
507     // Add the entry to the list
508     if(g_propertymap == NULL) {
509       g_propertymap = new;
510     } else {
511       current = g_propertymap;
512       while (current->next != NULL ) current=current->next;
513       current->next = new;
514     }
515     // Update the existing entry
516   } else {
517     if (current->description != NULL) {
518       free(current->description);
519     }
520     current->description = NULL;
521     if(description != NULL) {
522       current->description = strdup(description);
523     }
524     current->ptp_id = ptp_id;
525   }
526
527   return 0;
528 }
529
530 static void init_propertymap()
531 {
532   register_property("Storage ID", LIBMTP_PROPERTY_StorageID, PTP_OPC_StorageID);
533   register_property("Object Format", LIBMTP_PROPERTY_ObjectFormat, PTP_OPC_ObjectFormat);
534   register_property("Protection Status", LIBMTP_PROPERTY_ProtectionStatus, PTP_OPC_ProtectionStatus);
535   register_property("Object Size", LIBMTP_PROPERTY_ObjectSize, PTP_OPC_ObjectSize);
536   register_property("Association Type", LIBMTP_PROPERTY_AssociationType, PTP_OPC_AssociationType);
537   register_property("Association Desc", LIBMTP_PROPERTY_AssociationDesc, PTP_OPC_AssociationDesc);
538   register_property("Object File Name", LIBMTP_PROPERTY_ObjectFileName, PTP_OPC_ObjectFileName);
539   register_property("Date Created", LIBMTP_PROPERTY_DateCreated, PTP_OPC_DateCreated);
540   register_property("Date Modified", LIBMTP_PROPERTY_DateModified, PTP_OPC_DateModified);
541   register_property("Keywords", LIBMTP_PROPERTY_Keywords, PTP_OPC_Keywords);
542   register_property("Parent Object", LIBMTP_PROPERTY_ParentObject, PTP_OPC_ParentObject);
543   register_property("Allowed Folder Contents", LIBMTP_PROPERTY_AllowedFolderContents, PTP_OPC_AllowedFolderContents);
544   register_property("Hidden", LIBMTP_PROPERTY_Hidden, PTP_OPC_Hidden);
545   register_property("System Object", LIBMTP_PROPERTY_SystemObject, PTP_OPC_SystemObject);
546   register_property("Persistant Unique Object Identifier", LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier, PTP_OPC_PersistantUniqueObjectIdentifier);
547   register_property("Sync ID", LIBMTP_PROPERTY_SyncID, PTP_OPC_SyncID);
548   register_property("Property Bag", LIBMTP_PROPERTY_PropertyBag, PTP_OPC_PropertyBag);
549   register_property("Name", LIBMTP_PROPERTY_Name, PTP_OPC_Name);
550   register_property("Created By", LIBMTP_PROPERTY_CreatedBy, PTP_OPC_CreatedBy);
551   register_property("Artist", LIBMTP_PROPERTY_Artist, PTP_OPC_Artist);
552   register_property("Date Authored", LIBMTP_PROPERTY_DateAuthored, PTP_OPC_DateAuthored);
553   register_property("Description", LIBMTP_PROPERTY_Description, PTP_OPC_Description);
554   register_property("URL Reference", LIBMTP_PROPERTY_URLReference, PTP_OPC_URLReference);
555   register_property("Language Locale", LIBMTP_PROPERTY_LanguageLocale, PTP_OPC_LanguageLocale);
556   register_property("Copyright Information", LIBMTP_PROPERTY_CopyrightInformation, PTP_OPC_CopyrightInformation);
557   register_property("Source", LIBMTP_PROPERTY_Source, PTP_OPC_Source);
558   register_property("Origin Location", LIBMTP_PROPERTY_OriginLocation, PTP_OPC_OriginLocation);
559   register_property("Date Added", LIBMTP_PROPERTY_DateAdded, PTP_OPC_DateAdded);
560   register_property("Non Consumable", LIBMTP_PROPERTY_NonConsumable, PTP_OPC_NonConsumable);
561   register_property("Corrupt Or Unplayable", LIBMTP_PROPERTY_CorruptOrUnplayable, PTP_OPC_CorruptOrUnplayable);
562   register_property("Producer Serial Number", LIBMTP_PROPERTY_ProducerSerialNumber, PTP_OPC_ProducerSerialNumber);
563   register_property("Representative Sample Format", LIBMTP_PROPERTY_RepresentativeSampleFormat, PTP_OPC_RepresentativeSampleFormat);
564   register_property("Representative Sample Sise", LIBMTP_PROPERTY_RepresentativeSampleSize, PTP_OPC_RepresentativeSampleSize);
565   register_property("Representative Sample Height", LIBMTP_PROPERTY_RepresentativeSampleHeight, PTP_OPC_RepresentativeSampleHeight);
566   register_property("Representative Sample Width", LIBMTP_PROPERTY_RepresentativeSampleWidth, PTP_OPC_RepresentativeSampleWidth);
567   register_property("Representative Sample Duration", LIBMTP_PROPERTY_RepresentativeSampleDuration, PTP_OPC_RepresentativeSampleDuration);
568   register_property("Representative Sample Data", LIBMTP_PROPERTY_RepresentativeSampleData, PTP_OPC_RepresentativeSampleData);
569   register_property("Width", LIBMTP_PROPERTY_Width, PTP_OPC_Width);
570   register_property("Height", LIBMTP_PROPERTY_Height, PTP_OPC_Height);
571   register_property("Duration", LIBMTP_PROPERTY_Duration, PTP_OPC_Duration);
572   register_property("Rating", LIBMTP_PROPERTY_Rating, PTP_OPC_Rating);
573   register_property("Track", LIBMTP_PROPERTY_Track, PTP_OPC_Track);
574   register_property("Genre", LIBMTP_PROPERTY_Genre, PTP_OPC_Genre);
575   register_property("Credits", LIBMTP_PROPERTY_Credits, PTP_OPC_Credits);
576   register_property("Lyrics", LIBMTP_PROPERTY_Lyrics, PTP_OPC_Lyrics);
577   register_property("Subscription Content ID", LIBMTP_PROPERTY_SubscriptionContentID, PTP_OPC_SubscriptionContentID);
578   register_property("Produced By", LIBMTP_PROPERTY_ProducedBy, PTP_OPC_ProducedBy);
579   register_property("Use Count", LIBMTP_PROPERTY_UseCount, PTP_OPC_UseCount);
580   register_property("Skip Count", LIBMTP_PROPERTY_SkipCount, PTP_OPC_SkipCount);
581   register_property("Last Accessed", LIBMTP_PROPERTY_LastAccessed, PTP_OPC_LastAccessed);
582   register_property("Parental Rating", LIBMTP_PROPERTY_ParentalRating, PTP_OPC_ParentalRating);
583   register_property("Meta Genre", LIBMTP_PROPERTY_MetaGenre, PTP_OPC_MetaGenre);
584   register_property("Composer", LIBMTP_PROPERTY_Composer, PTP_OPC_Composer);
585   register_property("Effective Rating", LIBMTP_PROPERTY_EffectiveRating, PTP_OPC_EffectiveRating);
586   register_property("Subtitle", LIBMTP_PROPERTY_Subtitle, PTP_OPC_Subtitle);
587   register_property("Original Release Date", LIBMTP_PROPERTY_OriginalReleaseDate, PTP_OPC_OriginalReleaseDate);
588   register_property("Album Name", LIBMTP_PROPERTY_AlbumName, PTP_OPC_AlbumName);
589   register_property("Album Artist", LIBMTP_PROPERTY_AlbumArtist, PTP_OPC_AlbumArtist);
590   register_property("Mood", LIBMTP_PROPERTY_Mood, PTP_OPC_Mood);
591   register_property("DRM Status", LIBMTP_PROPERTY_DRMStatus, PTP_OPC_DRMStatus);
592   register_property("Sub Description", LIBMTP_PROPERTY_SubDescription, PTP_OPC_SubDescription);
593   register_property("Is Cropped", LIBMTP_PROPERTY_IsCropped, PTP_OPC_IsCropped);
594   register_property("Is Color Corrected", LIBMTP_PROPERTY_IsColorCorrected, PTP_OPC_IsColorCorrected);
595   register_property("Image Bit Depth", LIBMTP_PROPERTY_ImageBitDepth, PTP_OPC_ImageBitDepth);
596   register_property("f Number", LIBMTP_PROPERTY_Fnumber, PTP_OPC_Fnumber);
597   register_property("Exposure Time", LIBMTP_PROPERTY_ExposureTime, PTP_OPC_ExposureTime);
598   register_property("Exposure Index", LIBMTP_PROPERTY_ExposureIndex, PTP_OPC_ExposureIndex);
599   register_property("Display Name", LIBMTP_PROPERTY_DisplayName, PTP_OPC_DisplayName);
600   register_property("Body Text", LIBMTP_PROPERTY_BodyText, PTP_OPC_BodyText);
601   register_property("Subject", LIBMTP_PROPERTY_Subject, PTP_OPC_Subject);
602   register_property("Priority", LIBMTP_PROPERTY_Priority, PTP_OPC_Priority);
603   register_property("Given Name", LIBMTP_PROPERTY_GivenName, PTP_OPC_GivenName);
604   register_property("Middle Names", LIBMTP_PROPERTY_MiddleNames, PTP_OPC_MiddleNames);
605   register_property("Family Name", LIBMTP_PROPERTY_FamilyName, PTP_OPC_FamilyName);
606   register_property("Prefix", LIBMTP_PROPERTY_Prefix, PTP_OPC_Prefix);
607   register_property("Suffix", LIBMTP_PROPERTY_Suffix, PTP_OPC_Suffix);
608   register_property("Phonetic Given Name", LIBMTP_PROPERTY_PhoneticGivenName, PTP_OPC_PhoneticGivenName);
609   register_property("Phonetic Family Name", LIBMTP_PROPERTY_PhoneticFamilyName, PTP_OPC_PhoneticFamilyName);
610   register_property("Email: Primary", LIBMTP_PROPERTY_EmailPrimary, PTP_OPC_EmailPrimary);
611   register_property("Email: Personal 1", LIBMTP_PROPERTY_EmailPersonal1, PTP_OPC_EmailPersonal1);
612   register_property("Email: Personal 2", LIBMTP_PROPERTY_EmailPersonal2, PTP_OPC_EmailPersonal2);
613   register_property("Email: Business 1", LIBMTP_PROPERTY_EmailBusiness1, PTP_OPC_EmailBusiness1);
614   register_property("Email: Business 2", LIBMTP_PROPERTY_EmailBusiness2, PTP_OPC_EmailBusiness2);
615   register_property("Email: Others", LIBMTP_PROPERTY_EmailOthers, PTP_OPC_EmailOthers);
616   register_property("Phone Number: Primary", LIBMTP_PROPERTY_PhoneNumberPrimary, PTP_OPC_PhoneNumberPrimary);
617   register_property("Phone Number: Personal", LIBMTP_PROPERTY_PhoneNumberPersonal, PTP_OPC_PhoneNumberPersonal);
618   register_property("Phone Number: Personal 2", LIBMTP_PROPERTY_PhoneNumberPersonal2, PTP_OPC_PhoneNumberPersonal2);
619   register_property("Phone Number: Business", LIBMTP_PROPERTY_PhoneNumberBusiness, PTP_OPC_PhoneNumberBusiness);
620   register_property("Phone Number: Business 2", LIBMTP_PROPERTY_PhoneNumberBusiness2, PTP_OPC_PhoneNumberBusiness2);
621   register_property("Phone Number: Mobile", LIBMTP_PROPERTY_PhoneNumberMobile, PTP_OPC_PhoneNumberMobile);
622   register_property("Phone Number: Mobile 2", LIBMTP_PROPERTY_PhoneNumberMobile2, PTP_OPC_PhoneNumberMobile2);
623   register_property("Fax Number: Primary", LIBMTP_PROPERTY_FaxNumberPrimary, PTP_OPC_FaxNumberPrimary);
624   register_property("Fax Number: Personal", LIBMTP_PROPERTY_FaxNumberPersonal, PTP_OPC_FaxNumberPersonal);
625   register_property("Fax Number: Business", LIBMTP_PROPERTY_FaxNumberBusiness, PTP_OPC_FaxNumberBusiness);
626   register_property("Pager Number", LIBMTP_PROPERTY_PagerNumber, PTP_OPC_PagerNumber);
627   register_property("Phone Number: Others", LIBMTP_PROPERTY_PhoneNumberOthers, PTP_OPC_PhoneNumberOthers);
628   register_property("Primary Web Address", LIBMTP_PROPERTY_PrimaryWebAddress, PTP_OPC_PrimaryWebAddress);
629   register_property("Personal Web Address", LIBMTP_PROPERTY_PersonalWebAddress, PTP_OPC_PersonalWebAddress);
630   register_property("Business Web Address", LIBMTP_PROPERTY_BusinessWebAddress, PTP_OPC_BusinessWebAddress);
631   register_property("Instant Messenger Address 1", LIBMTP_PROPERTY_InstantMessengerAddress, PTP_OPC_InstantMessengerAddress);
632   register_property("Instant Messenger Address 2", LIBMTP_PROPERTY_InstantMessengerAddress2, PTP_OPC_InstantMessengerAddress2);
633   register_property("Instant Messenger Address 3", LIBMTP_PROPERTY_InstantMessengerAddress3, PTP_OPC_InstantMessengerAddress3);
634   register_property("Postal Address: Personal: Full", LIBMTP_PROPERTY_PostalAddressPersonalFull, PTP_OPC_PostalAddressPersonalFull);
635   register_property("Postal Address: Personal: Line 1", LIBMTP_PROPERTY_PostalAddressPersonalFullLine1, PTP_OPC_PostalAddressPersonalFullLine1);
636   register_property("Postal Address: Personal: Line 2", LIBMTP_PROPERTY_PostalAddressPersonalFullLine2, PTP_OPC_PostalAddressPersonalFullLine2);
637   register_property("Postal Address: Personal: City", LIBMTP_PROPERTY_PostalAddressPersonalFullCity, PTP_OPC_PostalAddressPersonalFullCity);
638   register_property("Postal Address: Personal: Region", LIBMTP_PROPERTY_PostalAddressPersonalFullRegion, PTP_OPC_PostalAddressPersonalFullRegion);
639   register_property("Postal Address: Personal: Postal Code", LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode, PTP_OPC_PostalAddressPersonalFullPostalCode);
640   register_property("Postal Address: Personal: Country", LIBMTP_PROPERTY_PostalAddressPersonalFullCountry, PTP_OPC_PostalAddressPersonalFullCountry);
641   register_property("Postal Address: Business: Full", LIBMTP_PROPERTY_PostalAddressBusinessFull, PTP_OPC_PostalAddressBusinessFull);
642   register_property("Postal Address: Business: Line 1", LIBMTP_PROPERTY_PostalAddressBusinessLine1, PTP_OPC_PostalAddressBusinessLine1);
643   register_property("Postal Address: Business: Line 2", LIBMTP_PROPERTY_PostalAddressBusinessLine2, PTP_OPC_PostalAddressBusinessLine2);
644   register_property("Postal Address: Business: City", LIBMTP_PROPERTY_PostalAddressBusinessCity, PTP_OPC_PostalAddressBusinessCity);
645   register_property("Postal Address: Business: Region", LIBMTP_PROPERTY_PostalAddressBusinessRegion, PTP_OPC_PostalAddressBusinessRegion);
646   register_property("Postal Address: Business: Postal Code", LIBMTP_PROPERTY_PostalAddressBusinessPostalCode, PTP_OPC_PostalAddressBusinessPostalCode);
647   register_property("Postal Address: Business: Country", LIBMTP_PROPERTY_PostalAddressBusinessCountry, PTP_OPC_PostalAddressBusinessCountry);
648   register_property("Postal Address: Other: Full", LIBMTP_PROPERTY_PostalAddressOtherFull, PTP_OPC_PostalAddressOtherFull);
649   register_property("Postal Address: Other: Line 1", LIBMTP_PROPERTY_PostalAddressOtherLine1, PTP_OPC_PostalAddressOtherLine1);
650   register_property("Postal Address: Other: Line 2", LIBMTP_PROPERTY_PostalAddressOtherLine2, PTP_OPC_PostalAddressOtherLine2);
651   register_property("Postal Address: Other: City", LIBMTP_PROPERTY_PostalAddressOtherCity, PTP_OPC_PostalAddressOtherCity);
652   register_property("Postal Address: Other: Region", LIBMTP_PROPERTY_PostalAddressOtherRegion, PTP_OPC_PostalAddressOtherRegion);
653   register_property("Postal Address: Other: Postal Code", LIBMTP_PROPERTY_PostalAddressOtherPostalCode, PTP_OPC_PostalAddressOtherPostalCode);
654   register_property("Postal Address: Other: Counrtry", LIBMTP_PROPERTY_PostalAddressOtherCountry, PTP_OPC_PostalAddressOtherCountry);
655   register_property("Organization Name", LIBMTP_PROPERTY_OrganizationName, PTP_OPC_OrganizationName);
656   register_property("Phonetic Organization Name", LIBMTP_PROPERTY_PhoneticOrganizationName, PTP_OPC_PhoneticOrganizationName);
657   register_property("Role", LIBMTP_PROPERTY_Role, PTP_OPC_Role);
658   register_property("Birthdate", LIBMTP_PROPERTY_Birthdate, PTP_OPC_Birthdate);
659   register_property("Message To", LIBMTP_PROPERTY_MessageTo, PTP_OPC_MessageTo);
660   register_property("Message CC", LIBMTP_PROPERTY_MessageCC, PTP_OPC_MessageCC);
661   register_property("Message BCC", LIBMTP_PROPERTY_MessageBCC, PTP_OPC_MessageBCC);
662   register_property("Message Read", LIBMTP_PROPERTY_MessageRead, PTP_OPC_MessageRead);
663   register_property("Message Received Time", LIBMTP_PROPERTY_MessageReceivedTime, PTP_OPC_MessageReceivedTime);
664   register_property("Message Sender", LIBMTP_PROPERTY_MessageSender, PTP_OPC_MessageSender);
665   register_property("Activity Begin Time", LIBMTP_PROPERTY_ActivityBeginTime, PTP_OPC_ActivityBeginTime);
666   register_property("Activity End Time", LIBMTP_PROPERTY_ActivityEndTime, PTP_OPC_ActivityEndTime);
667   register_property("Activity Location", LIBMTP_PROPERTY_ActivityLocation, PTP_OPC_ActivityLocation);
668   register_property("Activity Required Attendees", LIBMTP_PROPERTY_ActivityRequiredAttendees, PTP_OPC_ActivityRequiredAttendees);
669   register_property("Optional Attendees", LIBMTP_PROPERTY_ActivityOptionalAttendees, PTP_OPC_ActivityOptionalAttendees);
670   register_property("Activity Resources", LIBMTP_PROPERTY_ActivityResources, PTP_OPC_ActivityResources);
671   register_property("Activity Accepted", LIBMTP_PROPERTY_ActivityAccepted, PTP_OPC_ActivityAccepted);
672   register_property("Owner", LIBMTP_PROPERTY_Owner, PTP_OPC_Owner);
673   register_property("Editor", LIBMTP_PROPERTY_Editor, PTP_OPC_Editor);
674   register_property("Webmaster", LIBMTP_PROPERTY_Webmaster, PTP_OPC_Webmaster);
675   register_property("URL Source", LIBMTP_PROPERTY_URLSource, PTP_OPC_URLSource);
676   register_property("URL Destination", LIBMTP_PROPERTY_URLDestination, PTP_OPC_URLDestination);
677   register_property("Time Bookmark", LIBMTP_PROPERTY_TimeBookmark, PTP_OPC_TimeBookmark);
678   register_property("Object Bookmark", LIBMTP_PROPERTY_ObjectBookmark, PTP_OPC_ObjectBookmark);
679   register_property("Byte Bookmark", LIBMTP_PROPERTY_ByteBookmark, PTP_OPC_ByteBookmark);
680   register_property("Last Build Date", LIBMTP_PROPERTY_LastBuildDate, PTP_OPC_LastBuildDate);
681   register_property("Time To Live", LIBMTP_PROPERTY_TimetoLive, PTP_OPC_TimetoLive);
682   register_property("Media GUID", LIBMTP_PROPERTY_MediaGUID, PTP_OPC_MediaGUID);
683   register_property("Total Bit Rate", LIBMTP_PROPERTY_TotalBitRate, PTP_OPC_TotalBitRate);
684   register_property("Bit Rate Type", LIBMTP_PROPERTY_BitRateType, PTP_OPC_BitRateType);
685   register_property("Sample Rate", LIBMTP_PROPERTY_SampleRate, PTP_OPC_SampleRate);
686   register_property("Number Of Channels", LIBMTP_PROPERTY_NumberOfChannels, PTP_OPC_NumberOfChannels);
687   register_property("Audio Bit Depth", LIBMTP_PROPERTY_AudioBitDepth, PTP_OPC_AudioBitDepth);
688   register_property("Scan Depth", LIBMTP_PROPERTY_ScanDepth, PTP_OPC_ScanDepth);
689   register_property("Audio WAVE Codec", LIBMTP_PROPERTY_AudioWAVECodec, PTP_OPC_AudioWAVECodec);
690   register_property("Audio Bit Rate", LIBMTP_PROPERTY_AudioBitRate, PTP_OPC_AudioBitRate);
691   register_property("Video Four CC Codec", LIBMTP_PROPERTY_VideoFourCCCodec, PTP_OPC_VideoFourCCCodec);
692   register_property("Video Bit Rate", LIBMTP_PROPERTY_VideoBitRate, PTP_OPC_VideoBitRate);
693   register_property("Frames Per Thousand Seconds", LIBMTP_PROPERTY_FramesPerThousandSeconds, PTP_OPC_FramesPerThousandSeconds);
694   register_property("Key Frame Distance", LIBMTP_PROPERTY_KeyFrameDistance, PTP_OPC_KeyFrameDistance);
695   register_property("Buffer Size", LIBMTP_PROPERTY_BufferSize, PTP_OPC_BufferSize);
696   register_property("Encoding Quality", LIBMTP_PROPERTY_EncodingQuality, PTP_OPC_EncodingQuality);
697   register_property("Encoding Profile", LIBMTP_PROPERTY_EncodingProfile, PTP_OPC_EncodingProfile);
698   register_property("Buy flag", LIBMTP_PROPERTY_BuyFlag, PTP_OPC_BuyFlag);
699   register_property("Unknown property", LIBMTP_PROPERTY_UNKNOWN, 0);
700 }
701
702 /**
703  * Returns the PTP property that maps to a certain libmtp internal property type.
704  * @param inproperty the MTP library interface property
705  * @return the PTP (libgphoto2) property type
706  */
707 static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty)
708 {
709   propertymap_t *current;
710
711   current = g_propertymap;
712
713   while (current != NULL) {
714     if(current->id == inproperty) {
715       return current->ptp_id;
716     }
717     current = current->next;
718   }
719   return 0;
720 }
721
722
723 /**
724  * Returns the MTP internal interface property that maps to a certain ptp
725  * interface property.
726  * @param inproperty the PTP (libgphoto2) interface property
727  * @return the MTP library interface property
728  */
729 static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t inproperty)
730 {
731   propertymap_t *current;
732
733   current = g_propertymap;
734
735   while (current != NULL) {
736     if(current->ptp_id == inproperty) {
737       return current->id;
738     }
739     current = current->next;
740   }
741   // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
742   return LIBMTP_PROPERTY_UNKNOWN;
743 }
744
745
746 /**
747  * Set the debug level.
748  *
749  * By default, the debug level is set to '0' (disable).
750  */
751 void LIBMTP_Set_Debug(int level)
752 {
753   if (LIBMTP_debug || level)
754     LIBMTP_ERROR("LIBMTP_Set_Debug: Setting debugging level to %d (0x%02x) "
755                  "(%s)\n", level, level, level ? "on" : "off");
756
757   LIBMTP_debug = level;
758 }
759
760
761 /**
762  * Initialize the library. You are only supposed to call this
763  * one, before using the library for the first time in a program.
764  * Never re-initialize libmtp!
765  *
766  * The only thing this does at the moment is to initialise the
767  * filetype mapping table, as well as load MTPZ data if necessary.
768  */
769 void LIBMTP_Init(void)
770 {
771   const char *env_debug = getenv("LIBMTP_DEBUG");
772   if (env_debug) {
773     const long debug_flags = strtol(env_debug, NULL, 0);
774     if (debug_flags != LONG_MIN && debug_flags != LONG_MAX &&
775         INT_MIN <= debug_flags && debug_flags <= INT_MAX) {
776       LIBMTP_Set_Debug(debug_flags);
777     } else {
778       fprintf(stderr, "LIBMTP_Init: error setting debug flags from environment "
779                       "value \"%s\"\n", env_debug);
780     }
781   }
782
783   init_filemap();
784   init_propertymap();
785
786   if (mtpz_loaddata() == -1)
787     use_mtpz = 0;
788   else
789     use_mtpz = 1;
790
791   return;
792 }
793
794
795 /**
796  * This helper function returns a textual description for a libmtp
797  * file type to be used in dialog boxes etc.
798  * @param intype the libmtp internal filetype to get a description for.
799  * @return a string representing the filetype, this must <b>NOT</b>
800  *         be free():ed by the caller!
801  */
802 char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
803 {
804   filemap_t *current;
805
806   current = g_filemap;
807
808   while (current != NULL) {
809     if(current->id == intype) {
810       return current->description;
811     }
812     current = current->next;
813   }
814
815   return "Unknown filetype";
816 }
817
818 /**
819  * This helper function returns a textual description for a libmtp
820  * property to be used in dialog boxes etc.
821  * @param inproperty the libmtp internal property to get a description for.
822  * @return a string representing the filetype, this must <b>NOT</b>
823  *         be free():ed by the caller!
824  */
825 char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty)
826 {
827   propertymap_t *current;
828
829   current = g_propertymap;
830
831   while (current != NULL) {
832     if(current->id == inproperty) {
833       return current->description;
834     }
835     current = current->next;
836   }
837
838   return "Unknown property";
839 }
840
841 /**
842  * This function will do its best to fit a 16bit
843  * value into a PTP object property if the property
844  * is limited in range or step sizes.
845  */
846 static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd)
847 {
848   switch (opd->FormFlag) {
849   case PTP_DPFF_Range:
850     if (val < opd->FORM.Range.MinimumValue.u16) {
851       return opd->FORM.Range.MinimumValue.u16;
852     }
853     if (val > opd->FORM.Range.MaximumValue.u16) {
854       return opd->FORM.Range.MaximumValue.u16;
855     }
856     // Round down to last step.
857     if (val % opd->FORM.Range.StepSize.u16 != 0) {
858       return val - (val % opd->FORM.Range.StepSize.u16);
859     }
860     return val;
861     break;
862   case PTP_DPFF_Enumeration:
863     {
864       int i;
865       uint16_t bestfit = opd->FORM.Enum.SupportedValue[0].u16;
866
867       for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
868         if (val == opd->FORM.Enum.SupportedValue[i].u16) {
869           return val;
870         }
871         // Rough guess of best fit
872         if (opd->FORM.Enum.SupportedValue[i].u16 < val) {
873           bestfit = opd->FORM.Enum.SupportedValue[i].u16;
874         }
875       }
876       // Just some default that'll work.
877       return bestfit;
878     }
879   default:
880     // Will accept any value
881     break;
882   }
883   return val;
884 }
885
886 /**
887  * This function will do its best to fit a 32bit
888  * value into a PTP object property if the property
889  * is limited in range or step sizes.
890  */
891 static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd)
892 {
893   switch (opd->FormFlag) {
894   case PTP_DPFF_Range:
895     if (val < opd->FORM.Range.MinimumValue.u32) {
896       return opd->FORM.Range.MinimumValue.u32;
897     }
898     if (val > opd->FORM.Range.MaximumValue.u32) {
899       return opd->FORM.Range.MaximumValue.u32;
900     }
901     // Round down to last step.
902     if (val % opd->FORM.Range.StepSize.u32 != 0) {
903       return val - (val % opd->FORM.Range.StepSize.u32);
904     }
905     return val;
906     break;
907   case PTP_DPFF_Enumeration:
908     {
909       int i;
910       uint32_t bestfit = opd->FORM.Enum.SupportedValue[0].u32;
911
912       for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
913         if (val == opd->FORM.Enum.SupportedValue[i].u32) {
914           return val;
915         }
916         // Rough guess of best fit
917         if (opd->FORM.Enum.SupportedValue[i].u32 < val) {
918           bestfit = opd->FORM.Enum.SupportedValue[i].u32;
919         }
920       }
921       // Just some default that'll work.
922       return bestfit;
923     }
924   default:
925     // Will accept any value
926     break;
927   }
928   return val;
929 }
930
931 /**
932  * This function returns a newly created ISO 8601 timestamp with the
933  * current time in as high precision as possible. It even adds
934  * the time zone if it can.
935  */
936 static char *get_iso8601_stamp(void)
937 {
938   time_t curtime;
939   struct tm *loctime;
940   char tmp[64];
941
942   curtime = time(NULL);
943   loctime = localtime(&curtime);
944   strftime (tmp, sizeof(tmp), "%Y%m%dT%H%M%S.0%z", loctime);
945   return strdup(tmp);
946 }
947
948 /**
949  * Gets the allowed values (range or enum) for a property
950  * @param device a pointer to an MTP device
951  * @param property the property to query
952  * @param filetype the filetype of the object you want to set values for
953  * @param allowed_vals pointer to a LIBMTP_allowed_values_t struct to
954  *        receive the allowed values.  Call LIBMTP_destroy_allowed_values_t
955  *        on this on successful completion.
956  * @return 0 on success, any other value means failure
957  */
958 int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
959             LIBMTP_filetype_t const filetype, LIBMTP_allowed_values_t *allowed_vals)
960 {
961   PTPObjectPropDesc opd;
962   uint16_t ret = 0;
963
964   ret = ptp_mtp_getobjectpropdesc(device->params, map_libmtp_property_to_ptp_property(property), map_libmtp_type_to_ptp_type(filetype), &opd);
965   if (ret != PTP_RC_OK) {
966     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Allowed_Property_Values(): could not get property description.");
967     return -1;
968   }
969
970   if (opd.FormFlag == PTP_OPFF_Enumeration) {
971     int i = 0;
972
973     allowed_vals->is_range = 0;
974     allowed_vals->num_entries = opd.FORM.Enum.NumberOfValues;
975
976     switch (opd.DataType)
977     {
978       case PTP_DTC_INT8:
979         allowed_vals->i8vals = malloc(sizeof(int8_t) * opd.FORM.Enum.NumberOfValues);
980         allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
981         break;
982       case PTP_DTC_UINT8:
983         allowed_vals->u8vals = malloc(sizeof(uint8_t) * opd.FORM.Enum.NumberOfValues);
984         allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
985         break;
986       case PTP_DTC_INT16:
987         allowed_vals->i16vals = malloc(sizeof(int16_t) * opd.FORM.Enum.NumberOfValues);
988         allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
989         break;
990       case PTP_DTC_UINT16:
991         allowed_vals->u16vals = malloc(sizeof(uint16_t) * opd.FORM.Enum.NumberOfValues);
992         allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
993         break;
994       case PTP_DTC_INT32:
995         allowed_vals->i32vals = malloc(sizeof(int32_t) * opd.FORM.Enum.NumberOfValues);
996         allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
997         break;
998       case PTP_DTC_UINT32:
999         allowed_vals->u32vals = malloc(sizeof(uint32_t) * opd.FORM.Enum.NumberOfValues);
1000         allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
1001         break;
1002       case PTP_DTC_INT64:
1003         allowed_vals->i64vals = malloc(sizeof(int64_t) * opd.FORM.Enum.NumberOfValues);
1004         allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
1005         break;
1006       case PTP_DTC_UINT64:
1007         allowed_vals->u64vals = malloc(sizeof(uint64_t) * opd.FORM.Enum.NumberOfValues);
1008         allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
1009         break;
1010     }
1011
1012     for (i = 0; i < opd.FORM.Enum.NumberOfValues; i++) {
1013       switch (opd.DataType)
1014       {
1015         case PTP_DTC_INT8:
1016           allowed_vals->i8vals[i] = opd.FORM.Enum.SupportedValue[i].i8;
1017           break;
1018         case PTP_DTC_UINT8:
1019           allowed_vals->u8vals[i] = opd.FORM.Enum.SupportedValue[i].u8;
1020           break;
1021         case PTP_DTC_INT16:
1022           allowed_vals->i16vals[i] = opd.FORM.Enum.SupportedValue[i].i16;
1023           break;
1024         case PTP_DTC_UINT16:
1025           allowed_vals->u16vals[i] = opd.FORM.Enum.SupportedValue[i].u16;
1026           break;
1027         case PTP_DTC_INT32:
1028           allowed_vals->i32vals[i] = opd.FORM.Enum.SupportedValue[i].i32;
1029           break;
1030         case PTP_DTC_UINT32:
1031           allowed_vals->u32vals[i] = opd.FORM.Enum.SupportedValue[i].u32;
1032           break;
1033         case PTP_DTC_INT64:
1034           allowed_vals->i64vals[i] = opd.FORM.Enum.SupportedValue[i].i64;
1035           break;
1036         case PTP_DTC_UINT64:
1037           allowed_vals->u64vals[i] = opd.FORM.Enum.SupportedValue[i].u64;
1038           break;
1039       }
1040     }
1041     ptp_free_objectpropdesc(&opd);
1042     return 0;
1043   } else if (opd.FormFlag == PTP_OPFF_Range) {
1044     allowed_vals->is_range = 1;
1045
1046     switch (opd.DataType)
1047     {
1048       case PTP_DTC_INT8:
1049         allowed_vals->i8min = opd.FORM.Range.MinimumValue.i8;
1050         allowed_vals->i8max = opd.FORM.Range.MaximumValue.i8;
1051         allowed_vals->i8step = opd.FORM.Range.StepSize.i8;
1052         allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
1053         break;
1054       case PTP_DTC_UINT8:
1055         allowed_vals->u8min = opd.FORM.Range.MinimumValue.u8;
1056         allowed_vals->u8max = opd.FORM.Range.MaximumValue.u8;
1057         allowed_vals->u8step = opd.FORM.Range.StepSize.u8;
1058         allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
1059         break;
1060       case PTP_DTC_INT16:
1061         allowed_vals->i16min = opd.FORM.Range.MinimumValue.i16;
1062         allowed_vals->i16max = opd.FORM.Range.MaximumValue.i16;
1063         allowed_vals->i16step = opd.FORM.Range.StepSize.i16;
1064         allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
1065         break;
1066       case PTP_DTC_UINT16:
1067         allowed_vals->u16min = opd.FORM.Range.MinimumValue.u16;
1068         allowed_vals->u16max = opd.FORM.Range.MaximumValue.u16;
1069         allowed_vals->u16step = opd.FORM.Range.StepSize.u16;
1070         allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
1071         break;
1072       case PTP_DTC_INT32:
1073         allowed_vals->i32min = opd.FORM.Range.MinimumValue.i32;
1074         allowed_vals->i32max = opd.FORM.Range.MaximumValue.i32;
1075         allowed_vals->i32step = opd.FORM.Range.StepSize.i32;
1076         allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
1077         break;
1078       case PTP_DTC_UINT32:
1079         allowed_vals->u32min = opd.FORM.Range.MinimumValue.u32;
1080         allowed_vals->u32max = opd.FORM.Range.MaximumValue.u32;
1081         allowed_vals->u32step = opd.FORM.Range.StepSize.u32;
1082         allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
1083         break;
1084       case PTP_DTC_INT64:
1085         allowed_vals->i64min = opd.FORM.Range.MinimumValue.i64;
1086         allowed_vals->i64max = opd.FORM.Range.MaximumValue.i64;
1087         allowed_vals->i64step = opd.FORM.Range.StepSize.i64;
1088         allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
1089         break;
1090       case PTP_DTC_UINT64:
1091         allowed_vals->u64min = opd.FORM.Range.MinimumValue.u64;
1092         allowed_vals->u64max = opd.FORM.Range.MaximumValue.u64;
1093         allowed_vals->u64step = opd.FORM.Range.StepSize.u64;
1094         allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
1095         break;
1096     }
1097     return 0;
1098   } else
1099     return -1;
1100 }
1101
1102 /**
1103  * Destroys a LIBMTP_allowed_values_t struct
1104  * @param allowed_vals the struct to destroy
1105  */
1106 void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t *allowed_vals)
1107 {
1108   if (!allowed_vals->is_range)
1109   {
1110     switch (allowed_vals->datatype)
1111     {
1112       case LIBMTP_DATATYPE_INT8:
1113         if (allowed_vals->i8vals)
1114           free(allowed_vals->i8vals);
1115         break;
1116       case LIBMTP_DATATYPE_UINT8:
1117         if (allowed_vals->u8vals)
1118           free(allowed_vals->u8vals);
1119         break;
1120       case LIBMTP_DATATYPE_INT16:
1121         if (allowed_vals->i16vals)
1122           free(allowed_vals->i16vals);
1123         break;
1124       case LIBMTP_DATATYPE_UINT16:
1125         if (allowed_vals->u16vals)
1126           free(allowed_vals->u16vals);
1127         break;
1128       case LIBMTP_DATATYPE_INT32:
1129         if (allowed_vals->i32vals)
1130           free(allowed_vals->i32vals);
1131         break;
1132       case LIBMTP_DATATYPE_UINT32:
1133         if (allowed_vals->u32vals)
1134           free(allowed_vals->u32vals);
1135         break;
1136       case LIBMTP_DATATYPE_INT64:
1137         if (allowed_vals->i64vals)
1138           free(allowed_vals->i64vals);
1139         break;
1140       case LIBMTP_DATATYPE_UINT64:
1141         if (allowed_vals->u64vals)
1142           free(allowed_vals->u64vals);
1143         break;
1144     }
1145   }
1146 }
1147
1148 /**
1149  * Determine if a property is supported for a given file type
1150  * @param device a pointer to an MTP device
1151  * @param property the property to query
1152  * @param filetype the filetype of the object you want to set values for
1153  * @return 0 if not supported, positive if supported, negative on error
1154  */
1155 int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
1156             LIBMTP_filetype_t const filetype)
1157 {
1158   uint16_t *props = NULL;
1159   uint32_t propcnt = 0;
1160   uint16_t ret = 0;
1161   int i = 0;
1162   int supported = 0;
1163   uint16_t ptp_prop = map_libmtp_property_to_ptp_property(property);
1164
1165   if (!ptp_operation_issupported(device->params, PTP_OC_MTP_GetObjectPropsSupported))
1166     return 0;
1167
1168   ret = ptp_mtp_getobjectpropssupported(device->params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
1169   if (ret != PTP_RC_OK) {
1170     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Is_Property_Supported(): could not get properties supported.");
1171     return -1;
1172   }
1173
1174         for (i = 0; i < propcnt; i++) {
1175     if (props[i] == ptp_prop) {
1176       supported = 1;
1177       break;
1178     }
1179   }
1180
1181   free(props);
1182
1183   return supported;
1184 }
1185
1186 /**
1187  * Retrieves a string from an object
1188  *
1189  * @param device a pointer to an MTP device.
1190  * @param object_id Object reference
1191  * @param attribute_id MTP attribute ID
1192  * @return valid string or NULL on failure. The returned string
1193  *         must bee <code>free()</code>:ed by the caller after
1194  *         use.
1195  */
1196 char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1197                                     LIBMTP_property_t const attribute_id)
1198 {
1199   return get_string_from_object(device, object_id, attribute_id);
1200 }
1201
1202 /**
1203 * Retrieves an unsigned 64-bit integer from an object attribute
1204  *
1205  * @param device a pointer to an MTP device.
1206  * @param object_id Object reference
1207  * @param attribute_id MTP attribute ID
1208  * @param value_default Default value to return on failure
1209  * @return the value
1210  */
1211 uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1212                                     LIBMTP_property_t const attribute_id, uint64_t const value_default)
1213 {
1214   return get_u64_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1215 }
1216
1217 /**
1218  * Retrieves an unsigned 32-bit integer from an object attribute
1219  *
1220  * @param device a pointer to an MTP device.
1221  * @param object_id Object reference
1222  * @param attribute_id MTP attribute ID
1223  * @param value_default Default value to return on failure
1224  * @return the value
1225  */
1226 uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1227                                     LIBMTP_property_t const attribute_id, uint32_t const value_default)
1228 {
1229   return get_u32_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1230 }
1231
1232 /**
1233  * Retrieves an unsigned 16-bit integer from an object attribute
1234  *
1235  * @param device a pointer to an MTP device.
1236  * @param object_id Object reference
1237  * @param attribute_id MTP attribute ID
1238  * @param value_default Default value to return on failure
1239  * @return a value
1240  */
1241 uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1242                                     LIBMTP_property_t const attribute_id, uint16_t const value_default)
1243 {
1244   return get_u16_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1245 }
1246
1247 /**
1248  * Retrieves an unsigned 8-bit integer from an object attribute
1249  *
1250  * @param device a pointer to an MTP device.
1251  * @param object_id Object reference
1252  * @param attribute_id MTP attribute ID
1253  * @param value_default Default value to return on failure
1254  * @return a value
1255  */
1256 uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1257                                   LIBMTP_property_t const attribute_id, uint8_t const value_default)
1258 {
1259   return get_u8_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1260 }
1261
1262 /**
1263  * Sets an object attribute from a string
1264  *
1265  * @param device a pointer to an MTP device.
1266  * @param object_id Object reference
1267  * @param attribute_id MTP attribute ID
1268  * @param string string value to set
1269  * @return 0 on success, any other value means failure
1270  */
1271 int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1272                              LIBMTP_property_t const attribute_id, char const * const string)
1273 {
1274   return set_object_string(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), string);
1275 }
1276
1277
1278 /**
1279  * Sets an object attribute from an unsigned 32-bit integer
1280  *
1281  * @param device a pointer to an MTP device.
1282  * @param object_id Object reference
1283  * @param attribute_id MTP attribute ID
1284  * @param value 32-bit unsigned integer to set
1285  * @return 0 on success, any other value means failure
1286  */
1287 int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1288                           LIBMTP_property_t const attribute_id, uint32_t const value)
1289 {
1290   return set_object_u32(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1291 }
1292
1293 /**
1294  * Sets an object attribute from an unsigned 16-bit integer
1295  *
1296  * @param device a pointer to an MTP device.
1297  * @param object_id Object reference
1298  * @param attribute_id MTP attribute ID
1299  * @param value 16-bit unsigned integer to set
1300  * @return 0 on success, any other value means failure
1301  */
1302 int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1303                           LIBMTP_property_t const attribute_id, uint16_t const value)
1304 {
1305   return set_object_u16(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1306 }
1307
1308 /**
1309  * Sets an object attribute from an unsigned 8-bit integer
1310  *
1311  * @param device a pointer to an MTP device.
1312  * @param object_id Object reference
1313  * @param attribute_id MTP attribute ID
1314  * @param value 8-bit unsigned integer to set
1315  * @return 0 on success, any other value means failure
1316  */
1317 int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1318                          LIBMTP_property_t const attribute_id, uint8_t const value)
1319 {
1320   return set_object_u8(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1321 }
1322
1323 /**
1324  * Retrieves a string from an object
1325  *
1326  * @param device a pointer to an MTP device.
1327  * @param object_id Object reference
1328  * @param attribute_id PTP attribute ID
1329  * @return valid string or NULL on failure. The returned string
1330  *         must bee <code>free()</code>:ed by the caller after
1331  *         use.
1332  */
1333 static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1334                                     uint16_t const attribute_id)
1335 {
1336   PTPPropertyValue propval;
1337   char *retstring = NULL;
1338   PTPParams *params;
1339   uint16_t ret;
1340   MTPProperties *prop;
1341
1342   if (!device || !object_id)
1343     return NULL;
1344
1345   params = (PTPParams *) device->params;
1346
1347   prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1348   if (prop) {
1349     if (prop->propval.str != NULL)
1350       return strdup(prop->propval.str);
1351     else
1352       return NULL;
1353   }
1354
1355   ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
1356   if (ret == PTP_RC_OK) {
1357     if (propval.str != NULL) {
1358       retstring = (char *) strdup(propval.str);
1359       free(propval.str);
1360     }
1361   } else {
1362     add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
1363   }
1364
1365   return retstring;
1366 }
1367
1368 /**
1369 * Retrieves an unsigned 64-bit integer from an object attribute
1370  *
1371  * @param device a pointer to an MTP device.
1372  * @param object_id Object reference
1373  * @param attribute_id PTP attribute ID
1374  * @param value_default Default value to return on failure
1375  * @return the value
1376  */
1377 static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1378                                     uint16_t const attribute_id, uint64_t const value_default)
1379 {
1380   PTPPropertyValue propval;
1381   uint64_t retval = value_default;
1382   PTPParams *params;
1383   uint16_t ret;
1384   MTPProperties *prop;
1385
1386   if (!device)
1387     return value_default;
1388
1389   params = (PTPParams *) device->params;
1390
1391   prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1392   if (prop)
1393     return prop->propval.u64;
1394
1395   ret = ptp_mtp_getobjectpropvalue(params, object_id,
1396                                    attribute_id,
1397                                    &propval,
1398                                    PTP_DTC_UINT64);
1399   if (ret == PTP_RC_OK) {
1400     retval = propval.u64;
1401   } else {
1402     add_ptp_error_to_errorstack(device, ret, "get_u64_from_object(): could not get unsigned 64bit integer from object.");
1403   }
1404
1405   return retval;
1406 }
1407
1408 /**
1409  * Retrieves an unsigned 32-bit integer from an object attribute
1410  *
1411  * @param device a pointer to an MTP device.
1412  * @param object_id Object reference
1413  * @param attribute_id PTP attribute ID
1414  * @param value_default Default value to return on failure
1415  * @return the value
1416  */
1417 static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1418                                     uint16_t const attribute_id, uint32_t const value_default)
1419 {
1420   PTPPropertyValue propval;
1421   uint32_t retval = value_default;
1422   PTPParams *params;
1423   uint16_t ret;
1424   MTPProperties *prop;
1425
1426   if (!device)
1427     return value_default;
1428
1429   params = (PTPParams *) device->params;
1430
1431   prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1432   if (prop)
1433     return prop->propval.u32;
1434
1435   ret = ptp_mtp_getobjectpropvalue(params, object_id,
1436                                    attribute_id,
1437                                    &propval,
1438                                    PTP_DTC_UINT32);
1439   if (ret == PTP_RC_OK) {
1440     retval = propval.u32;
1441   } else {
1442     add_ptp_error_to_errorstack(device, ret, "get_u32_from_object(): could not get unsigned 32bit integer from object.");
1443   }
1444   return retval;
1445 }
1446
1447 /**
1448  * Retrieves an unsigned 16-bit integer from an object attribute
1449  *
1450  * @param device a pointer to an MTP device.
1451  * @param object_id Object reference
1452  * @param attribute_id PTP attribute ID
1453  * @param value_default Default value to return on failure
1454  * @return a value
1455  */
1456 static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1457                                     uint16_t const attribute_id, uint16_t const value_default)
1458 {
1459   PTPPropertyValue propval;
1460   uint16_t retval = value_default;
1461   PTPParams *params;
1462   uint16_t ret;
1463   MTPProperties *prop;
1464
1465   if (!device)
1466     return value_default;
1467
1468   params = (PTPParams *) device->params;
1469
1470   // This O(n) search should not be used so often, since code
1471   // using the cached properties don't usually call this function.
1472   prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1473   if (prop)
1474     return prop->propval.u16;
1475
1476   ret = ptp_mtp_getobjectpropvalue(params, object_id,
1477                                    attribute_id,
1478                                    &propval,
1479                                    PTP_DTC_UINT16);
1480   if (ret == PTP_RC_OK) {
1481     retval = propval.u16;
1482   } else {
1483     add_ptp_error_to_errorstack(device, ret, "get_u16_from_object(): could not get unsigned 16bit integer from object.");
1484   }
1485
1486   return retval;
1487 }
1488
1489 /**
1490  * Retrieves an unsigned 8-bit integer from an object attribute
1491  *
1492  * @param device a pointer to an MTP device.
1493  * @param object_id Object reference
1494  * @param attribute_id PTP attribute ID
1495  * @param value_default Default value to return on failure
1496  * @return a value
1497  */
1498 static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1499                                   uint16_t const attribute_id, uint8_t const value_default)
1500 {
1501   PTPPropertyValue propval;
1502   uint8_t retval = value_default;
1503   PTPParams *params;
1504   uint16_t ret;
1505   MTPProperties *prop;
1506
1507   if (!device)
1508     return value_default;
1509
1510   params = (PTPParams *) device->params;
1511
1512   // This O(n) search should not be used so often, since code
1513   // using the cached properties don't usually call this function.
1514   prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1515   if (prop)
1516     return prop->propval.u8;
1517
1518   ret = ptp_mtp_getobjectpropvalue(params, object_id,
1519                                    attribute_id,
1520                                    &propval,
1521                                    PTP_DTC_UINT8);
1522   if (ret == PTP_RC_OK) {
1523     retval = propval.u8;
1524   } else {
1525     add_ptp_error_to_errorstack(device, ret, "get_u8_from_object(): could not get unsigned 8bit integer from object.");
1526   }
1527
1528   return retval;
1529 }
1530
1531 /**
1532  * Sets an object attribute from a string
1533  *
1534  * @param device a pointer to an MTP device.
1535  * @param object_id Object reference
1536  * @param attribute_id PTP attribute ID
1537  * @param string string value to set
1538  * @return 0 on success, any other value means failure
1539  */
1540 static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1541                              uint16_t const attribute_id, char const * const string)
1542 {
1543   PTPPropertyValue propval;
1544   PTPParams *params;
1545   uint16_t ret;
1546
1547   if (!device || !string)
1548     return -1;
1549
1550   params = (PTPParams *) device->params;
1551
1552   if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1553     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
1554                                 "PTP_OC_MTP_SetObjectPropValue not supported.");
1555     return -1;
1556   }
1557   propval.str = (char *) string;
1558   ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
1559   if (ret != PTP_RC_OK) {
1560     add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
1561     return -1;
1562   }
1563
1564   return 0;
1565 }
1566
1567
1568 /**
1569  * Sets an object attribute from an unsigned 32-bit integer
1570  *
1571  * @param device a pointer to an MTP device.
1572  * @param object_id Object reference
1573  * @param attribute_id PTP attribute ID
1574  * @param value 32-bit unsigned integer to set
1575  * @return 0 on success, any other value means failure
1576  */
1577 static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1578                           uint16_t const attribute_id, uint32_t const value)
1579 {
1580   PTPPropertyValue propval;
1581   PTPParams *params;
1582   uint16_t ret;
1583
1584   if (!device)
1585     return -1;
1586
1587   params = (PTPParams *) device->params;
1588
1589   if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1590     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
1591                                 "PTP_OC_MTP_SetObjectPropValue not supported.");
1592     return -1;
1593   }
1594
1595   propval.u32 = value;
1596   ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
1597   if (ret != PTP_RC_OK) {
1598     add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
1599     return -1;
1600   }
1601
1602   return 0;
1603 }
1604
1605 /**
1606  * Sets an object attribute from an unsigned 16-bit integer
1607  *
1608  * @param device a pointer to an MTP device.
1609  * @param object_id Object reference
1610  * @param attribute_id PTP attribute ID
1611  * @param value 16-bit unsigned integer to set
1612  * @return 0 on success, any other value means failure
1613  */
1614 static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1615                           uint16_t const attribute_id, uint16_t const value)
1616 {
1617   PTPPropertyValue propval;
1618   PTPParams *params;
1619   uint16_t ret;
1620
1621   if (!device)
1622     return 1;
1623
1624   params = (PTPParams *) device->params;
1625
1626   if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1627     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
1628                                 "PTP_OC_MTP_SetObjectPropValue not supported.");
1629     return -1;
1630   }
1631   propval.u16 = value;
1632   ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
1633   if (ret != PTP_RC_OK) {
1634     add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
1635     return 1;
1636   }
1637
1638   return 0;
1639 }
1640
1641 /**
1642  * Sets an object attribute from an unsigned 8-bit integer
1643  *
1644  * @param device a pointer to an MTP device.
1645  * @param object_id Object reference
1646  * @param attribute_id PTP attribute ID
1647  * @param value 8-bit unsigned integer to set
1648  * @return 0 on success, any other value means failure
1649  */
1650 static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1651                          uint16_t const attribute_id, uint8_t const value)
1652 {
1653   PTPPropertyValue propval;
1654   PTPParams *params;
1655   uint16_t ret;
1656
1657   if (!device)
1658     return 1;
1659
1660   params = (PTPParams *) device->params;
1661
1662   if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1663     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
1664                             "PTP_OC_MTP_SetObjectPropValue not supported.");
1665     return -1;
1666   }
1667   propval.u8 = value;
1668   ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
1669   if (ret != PTP_RC_OK) {
1670     add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
1671     return 1;
1672   }
1673
1674   return 0;
1675 }
1676
1677 /**
1678  * Get the first (as in "first in the list of") connected MTP device.
1679  * @return a device pointer.
1680  * @see LIBMTP_Get_Connected_Devices()
1681  */
1682 LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
1683 {
1684   LIBMTP_mtpdevice_t *first_device = NULL;
1685   LIBMTP_raw_device_t *devices;
1686   int numdevs;
1687   LIBMTP_error_number_t ret;
1688
1689   ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
1690   if (ret != LIBMTP_ERROR_NONE) {
1691     return NULL;
1692   }
1693
1694 #ifdef TIZEN_EXT
1695   if (devices == NULL) {
1696     return NULL;
1697   }
1698
1699   if (numdevs == 0) {
1700     if (devices) {
1701       free(devices);
1702     }
1703     return NULL;
1704   }
1705 #else /* TIZEN_EXT */
1706   if (devices == NULL || numdevs == 0) {
1707     return NULL;
1708   }
1709 #endif /* TIZEN_EXT */
1710
1711   first_device = LIBMTP_Open_Raw_Device(&devices[0]);
1712   free(devices);
1713   return first_device;
1714 }
1715
1716 /**
1717  * Overriding debug function.
1718  * This way we can disable debug prints.
1719  */
1720 static void
1721 #ifdef __GNUC__
1722 __attribute__((__format__(printf,2,0)))
1723 #endif
1724 LIBMTP_ptp_debug(void *data, const char *format, va_list args)
1725 {
1726   if ((LIBMTP_debug & LIBMTP_DEBUG_PTP) != 0) {
1727     vfprintf (stderr, format, args);
1728     fprintf (stderr, "\n");
1729     fflush (stderr);
1730   }
1731 }
1732
1733 /**
1734  * Overriding error function.
1735  * This way we can capture all error etc to our errorstack.
1736  */
1737 static void
1738 #ifdef __GNUC__
1739 __attribute__((__format__(printf,2,0)))
1740 #endif
1741 LIBMTP_ptp_error(void *data, const char *format, va_list args)
1742 {
1743   // if (data == NULL) {
1744     vfprintf (stderr, format, args);
1745     fflush (stderr);
1746   /*
1747     FIXME: find out how we shall get the device here.
1748   } else {
1749     PTP_USB *ptp_usb = data;
1750     LIBMTP_mtpdevice_t *device = ...;
1751     char buf[2048];
1752
1753     vsnprintf (buf, sizeof (buf), format, args);
1754     add_error_to_errorstack(device,
1755                             LIBMTP_ERROR_PTP_LAYER,
1756                             buf);
1757   }
1758   */
1759 }
1760
1761 /**
1762  * Parses the extension descriptor, there may be stuff in
1763  * this that we want to know about.
1764  */
1765 static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
1766                                        char *desc)
1767 {
1768   int start = 0;
1769   int end = 0;
1770
1771   /* NULL on Canon A70 */
1772   if (!desc)
1773     return;
1774
1775   /* descriptors are divided by semicolons */
1776   while (end < strlen(desc)) {
1777     /* Skip past initial whitespace */
1778     while ((end < strlen(desc)) && (desc[start] == ' ' )) {
1779       start++;
1780       end++;
1781     }
1782     /* Detect extension */
1783     while ((end < strlen(desc)) && (desc[end] != ';'))
1784       end++;
1785     if (end < strlen(desc)) {
1786       char *element = strndup(desc + start, end-start);
1787       if (element) {
1788         int i = 0;
1789         // printf("  Element: \"%s\"\n", element);
1790
1791         /* Parse for an extension */
1792         while ((i < strlen(element)) && (element[i] != ':'))
1793           i++;
1794         if (i < strlen(element)) {
1795           char *name = strndup(element, i);
1796           int majstart = i+1;
1797           // printf("    Extension: \"%s\"\n", name);
1798
1799           /* Parse for minor/major punctuation mark for this extension */
1800           while ((i < strlen(element)) && (element[i] != '.'))
1801             i++;
1802           if (i > majstart && i < strlen(element)) {
1803             LIBMTP_device_extension_t *extension;
1804             int major = 0;
1805             int minor = 0;
1806             char *majorstr = strndup(element + majstart, i - majstart);
1807             char *minorstr = strndup(element + i + 1, strlen(element) - i - 1);
1808             major = atoi(majorstr);
1809             minor = atoi(minorstr);
1810             // printf("    Major: \"%s\" (parsed %d) Minor: \"%s\" (parsed %d)\n",
1811             //      majorstr, major, minorstr, minor);
1812             free(majorstr);
1813             free(minorstr);
1814             extension = malloc(sizeof(LIBMTP_device_extension_t));
1815             if (extension != NULL) {
1816               extension->name = name;
1817               extension->major = major;
1818               extension->minor = minor;
1819               extension->next = NULL;
1820             }
1821             if (mtpdevice->extensions == NULL) {
1822               mtpdevice->extensions = extension;
1823             } else {
1824               LIBMTP_device_extension_t *tmp = mtpdevice->extensions;
1825               while (tmp->next != NULL)
1826                 tmp = tmp->next;
1827               tmp->next = extension;
1828             }
1829           } else {
1830             LIBMTP_ERROR("LIBMTP ERROR: couldnt parse extension %s\n",
1831                          element);
1832           }
1833         }
1834         free(element);
1835       }
1836     }
1837     end++;
1838     start = end;
1839   }
1840 }
1841
1842 /**
1843  * This function opens a device from a raw device. It is the
1844  * preferred way to access devices in the new interface where
1845  * several devices can come and go as the library is working
1846  * on a certain device.
1847  * @param rawdevice the raw device to open a "real" device for.
1848  * @return an open device.
1849  */
1850 LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevice)
1851 {
1852   LIBMTP_mtpdevice_t *mtp_device;
1853   uint8_t bs = 0;
1854   PTPParams *current_params;
1855   PTP_USB *ptp_usb;
1856   LIBMTP_error_number_t err;
1857   int i;
1858
1859   /* Allocate dynamic space for our device */
1860   mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
1861   /* Check if there was a memory allocation error */
1862   if(mtp_device == NULL) {
1863     /* There has been an memory allocation error. We are going to ignore this
1864        device and attempt to continue */
1865
1866     /* TODO: This error statement could probably be a bit more robust */
1867     LIBMTP_ERROR("LIBMTP PANIC: connect_usb_devices encountered a memory "
1868             "allocation error with device %d on bus %d, trying to continue",
1869             rawdevice->devnum, rawdevice->bus_location);
1870
1871     return NULL;
1872   }
1873   memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
1874   // Non-cached by default
1875   mtp_device->cached = 0;
1876
1877   /* Create PTP params */
1878   current_params = (PTPParams *) malloc(sizeof(PTPParams));
1879   if (current_params == NULL) {
1880     free(mtp_device);
1881     return NULL;
1882   }
1883   memset(current_params, 0, sizeof(PTPParams));
1884   current_params->device_flags = rawdevice->device_entry.device_flags;
1885   current_params->nrofobjects = 0;
1886   current_params->objects = NULL;
1887   current_params->response_packet_size = 0;
1888   current_params->response_packet = NULL;
1889   /* This will be a pointer to PTP_USB later */
1890   current_params->data = NULL;
1891   /* Set upp local debug and error functions */
1892   current_params->debug_func = LIBMTP_ptp_debug;
1893   current_params->error_func = LIBMTP_ptp_error;
1894   /* TODO: Will this always be little endian? */
1895   current_params->byteorder = PTP_DL_LE;
1896   current_params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8");
1897   current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UCS-2LE");
1898
1899   if(current_params->cd_locale_to_ucs2 == (iconv_t) -1 ||
1900      current_params->cd_ucs2_to_locale == (iconv_t) -1) {
1901     LIBMTP_ERROR("LIBMTP PANIC: Cannot open iconv() converters to/from UCS-2!\n"
1902             "Too old stdlibc, glibc and libiconv?\n");
1903     free(current_params);
1904     free(mtp_device);
1905     return NULL;
1906   }
1907   mtp_device->params = current_params;
1908
1909   /* Create usbinfo, this also opens the session */
1910   err = configure_usb_device(rawdevice,
1911                              current_params,
1912                              &mtp_device->usbinfo);
1913   if (err != LIBMTP_ERROR_NONE) {
1914     free(current_params);
1915     free(mtp_device);
1916     return NULL;
1917   }
1918   ptp_usb = (PTP_USB*) mtp_device->usbinfo;
1919   /* Set pointer back to params */
1920   ptp_usb->params = current_params;
1921
1922   /* Cache the device information for later use */
1923   if (ptp_getdeviceinfo(current_params,
1924                         &current_params->deviceinfo) != PTP_RC_OK) {
1925     LIBMTP_ERROR("LIBMTP PANIC: Unable to read device information on device "
1926             "%d on bus %d, trying to continue",
1927             rawdevice->devnum, rawdevice->bus_location);
1928
1929     /* Prevent memory leaks for this device */
1930     free(mtp_device->usbinfo);
1931     free(mtp_device->params);
1932     current_params = NULL;
1933     free(mtp_device);
1934     return NULL;
1935   }
1936
1937   /* Check: if this is a PTP device, is it really tagged as MTP? */
1938   if (current_params->deviceinfo.VendorExtensionID != 0x00000006) {
1939     LIBMTP_ERROR("LIBMTP WARNING: no MTP vendor extension on device "
1940                  "%d on bus %d",
1941                  rawdevice->devnum, rawdevice->bus_location);
1942     LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionID: %08x",
1943                  current_params->deviceinfo.VendorExtensionID);
1944     LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionDesc: %s",
1945                  current_params->deviceinfo.VendorExtensionDesc);
1946     LIBMTP_ERROR("LIBMTP WARNING: this typically means the device is PTP "
1947                  "(i.e. a camera) but not an MTP device at all. "
1948                  "Trying to continue anyway.");
1949   }
1950
1951   parse_extension_descriptor(mtp_device,
1952                              current_params->deviceinfo.VendorExtensionDesc);
1953
1954   /*
1955    * Android has a number of bugs, force-assign these bug flags
1956    * if Android is encountered. Same thing for devices we detect
1957    * as SONY NWZ Walkmen. I have no clue what "sony.net/WMFU" means
1958    * I just know only NWZs have it.
1959    */
1960   {
1961     LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
1962     int is_microsoft_com_wpdna = 0;
1963     int is_android = 0;
1964     int is_sony_net_wmfu = 0;
1965     int is_sonyericsson_com_se = 0;
1966
1967     /* Loop over extensions and set flags */
1968     while (tmpext != NULL) {
1969       if (!strcmp(tmpext->name, "microsoft.com/WPDNA"))
1970         is_microsoft_com_wpdna = 1;
1971       if (!strcmp(tmpext->name, "android.com"))
1972         is_android = 1;
1973       if (!strcmp(tmpext->name, "sony.net/WMFU"))
1974         is_sony_net_wmfu = 1;
1975       if (!strcmp(tmpext->name, "sonyericsson.com/SE"))
1976         is_sonyericsson_com_se = 1;
1977       tmpext = tmpext->next;
1978     }
1979
1980     /* Check for specific stacks */
1981     if (is_microsoft_com_wpdna && is_sonyericsson_com_se && !is_android) {
1982       /*
1983        * The Aricent stack seems to be detected by providing WPDNA, the SonyEricsson
1984        * extension and NO Android extension.
1985        */
1986       ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ARICENT_BUGS;
1987       LIBMTP_INFO("Aricent MTP stack device detected, assigning default bug flags\n");
1988     }
1989     else if (is_android) {
1990       /*
1991        * If bugs are fixed in later versions, test on tmpext->major, tmpext->minor
1992        */
1993       ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ANDROID_BUGS;
1994       LIBMTP_INFO("Android device detected, assigning default bug flags\n");
1995     }
1996     else if (is_sony_net_wmfu) {
1997       ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_SONY_NWZ_BUGS;
1998       LIBMTP_INFO("SONY NWZ device detected, assigning default bug flags\n");
1999     }
2000   }
2001
2002   /*
2003    * If the OGG or FLAC filetypes are flagged as "unknown", check
2004    * if the firmware has been updated to actually support it.
2005    */
2006   if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
2007     for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
2008       if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_OGG) {
2009         /* This is not unknown anymore, unflag it */
2010         ptp_usb->rawdevice.device_entry.device_flags &=
2011           ~DEVICE_FLAG_OGG_IS_UNKNOWN;
2012         break;
2013       }
2014     }
2015   }
2016   if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
2017     for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
2018       if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_FLAC) {
2019         /* This is not unknown anymore, unflag it */
2020         ptp_usb->rawdevice.device_entry.device_flags &=
2021           ~DEVICE_FLAG_FLAC_IS_UNKNOWN;
2022         break;
2023       }
2024     }
2025   }
2026
2027   /* Determine if the object size supported is 32 or 64 bit wide */
2028   if (ptp_operation_issupported(current_params,PTP_OC_MTP_GetObjectPropsSupported)) {
2029     for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
2030       PTPObjectPropDesc opd;
2031
2032       if (ptp_mtp_getobjectpropdesc(current_params,
2033                                     PTP_OPC_ObjectSize,
2034                                     current_params->deviceinfo.ImageFormats[i],
2035                                     &opd) != PTP_RC_OK) {
2036         LIBMTP_ERROR("LIBMTP PANIC: "
2037                      "could not inspect object property descriptions!\n");
2038       } else {
2039         if (opd.DataType == PTP_DTC_UINT32) {
2040           if (bs == 0) {
2041             bs = 32;
2042           } else if (bs != 32) {
2043             LIBMTP_ERROR("LIBMTP PANIC: "
2044                          "different objects support different object sizes!\n");
2045             bs = 0;
2046             break;
2047           }
2048         } else if (opd.DataType == PTP_DTC_UINT64) {
2049           if (bs == 0) {
2050             bs = 64;
2051           } else if (bs != 64) {
2052             LIBMTP_ERROR("LIBMTP PANIC: "
2053                          "different objects support different object sizes!\n");
2054             bs = 0;
2055             break;
2056           }
2057         } else {
2058           // Ignore if other size.
2059           LIBMTP_ERROR("LIBMTP PANIC: "
2060                        "awkward object size data type: %04x\n", opd.DataType);
2061           bs = 0;
2062           break;
2063         }
2064       }
2065     }
2066   }
2067   if (bs == 0) {
2068     // Could not detect object bitsize, assume 32 bits
2069     bs = 32;
2070   }
2071   mtp_device->object_bitsize = bs;
2072
2073   /* No Errors yet for this device */
2074   mtp_device->errorstack = NULL;
2075
2076   /* Default Max Battery Level, we will adjust this if possible */
2077   mtp_device->maximum_battery_level = 100;
2078
2079   /* Check if device supports reading maximum battery level */
2080   if(!FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) &&
2081      ptp_property_issupported( current_params, PTP_DPC_BatteryLevel)) {
2082     PTPDevicePropDesc dpd;
2083
2084     /* Try to read maximum battery level */
2085     if(ptp_getdevicepropdesc(current_params,
2086                              PTP_DPC_BatteryLevel,
2087                              &dpd) != PTP_RC_OK) {
2088       add_error_to_errorstack(mtp_device,
2089                               LIBMTP_ERROR_CONNECTING,
2090                               "Unable to read Maximum Battery Level for this "
2091                               "device even though the device supposedly "
2092                               "supports this functionality");
2093     }
2094
2095     /* TODO: is this appropriate? */
2096     /* If max battery level is 0 then leave the default, otherwise assign */
2097     if (dpd.FORM.Range.MaximumValue.u8 != 0) {
2098       mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
2099     }
2100
2101     ptp_free_devicepropdesc(&dpd);
2102   }
2103
2104   /* Set all default folders to 0xffffffffU (root directory) */
2105   mtp_device->default_music_folder = 0xffffffffU;
2106   mtp_device->default_playlist_folder = 0xffffffffU;
2107   mtp_device->default_picture_folder = 0xffffffffU;
2108   mtp_device->default_video_folder = 0xffffffffU;
2109   mtp_device->default_organizer_folder = 0xffffffffU;
2110   mtp_device->default_zencast_folder = 0xffffffffU;
2111   mtp_device->default_album_folder = 0xffffffffU;
2112   mtp_device->default_text_folder = 0xffffffffU;
2113
2114   /* Set initial storage information */
2115   mtp_device->storage = NULL;
2116   if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) {
2117     add_error_to_errorstack(mtp_device,
2118                             LIBMTP_ERROR_GENERAL,
2119                             "Get Storage information failed.");
2120     mtp_device->storage = NULL;
2121   }
2122
2123
2124   return mtp_device;
2125 }
2126
2127 LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
2128 {
2129   LIBMTP_mtpdevice_t *mtp_device = LIBMTP_Open_Raw_Device_Uncached(rawdevice);
2130
2131   if (mtp_device == NULL)
2132     return NULL;
2133
2134   /* Check for MTPZ devices. */
2135   if (use_mtpz) {
2136     LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
2137
2138     while (tmpext != NULL) {
2139       if (!strcmp(tmpext->name, "microsoft.com/MTPZ")) {
2140         LIBMTP_INFO("MTPZ device detected. Authenticating...\n");
2141         if (PTP_RC_OK == ptp_mtpz_handshake(mtp_device->params)) {
2142           LIBMTP_INFO ("(MTPZ) Successfully authenticated with device.\n");
2143         } else {
2144           LIBMTP_INFO ("(MTPZ) Failure - could not authenticate with device.\n");
2145         }
2146         break;
2147       }
2148       tmpext = tmpext->next;
2149     }
2150   }
2151
2152   // Set up this device as cached
2153   mtp_device->cached = 1;
2154   /*
2155    * Then get the handles and try to locate the default folders.
2156    * This has the desired side effect of caching all handles from
2157    * the device which speeds up later operations.
2158    */
2159   flush_handles(mtp_device);
2160   return mtp_device;
2161 }
2162
2163 /**
2164  * To read events sent by the device, repeatedly call this function from a secondary
2165  * thread until the return value is < 0.
2166  *
2167  * @param device a pointer to the MTP device to poll for events.
2168  * @param event contains a pointer to be filled in with the event retrieved if the call
2169  * is successful.
2170  * @param out1 contains the param1 value from the raw event.
2171  * @return 0 on success, any other value means the polling loop shall be
2172  * terminated immediately for this session.
2173  */
2174 int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_t *out1)
2175 {
2176   /*
2177    * FIXME: Potential race-condition here, if client deallocs device
2178    * while we're *not* waiting for input. As we'll be waiting for
2179    * input most of the time, it's unlikely but still worth considering
2180    * for improvement. Also we cannot affect the state of the cache etc
2181    * unless we know we are the sole user on the device. A spinlock or
2182    * mutex in the LIBMTP_mtpdevice_t is needed for this to work.
2183    */
2184   PTPParams *params = (PTPParams *) device->params;
2185   PTPContainer ptp_event;
2186   uint16_t ret = ptp_usb_event_wait(params, &ptp_event);
2187   uint16_t code;
2188   uint32_t session_id;
2189   uint32_t param1;
2190
2191   if (ret != PTP_RC_OK) {
2192     /* Device is closing down or other fatal stuff, exit thread */
2193         LIBMTP_INFO("ptp_usb_event_wait ret %u\n", ret);
2194     return -1;
2195   }
2196
2197   *event = LIBMTP_EVENT_NONE;
2198
2199   /* Process the event */
2200   code = ptp_event.Code;
2201   session_id = ptp_event.SessionID;
2202   param1 = ptp_event.Param1;
2203
2204   switch(code) {
2205     case PTP_EC_Undefined:
2206       LIBMTP_INFO("Received event PTP_EC_Undefined in session %u\n", session_id);
2207       break;
2208     case PTP_EC_CancelTransaction:
2209       LIBMTP_INFO("Received event PTP_EC_CancelTransaction in session %u\n", session_id);
2210       break;
2211     case PTP_EC_ObjectAdded:
2212       LIBMTP_INFO("Received event PTP_EC_ObjectAdded in session %u\n", session_id);
2213       *event = LIBMTP_EVENT_OBJECT_ADDED;
2214       *out1 = param1;
2215       break;
2216     case PTP_EC_ObjectRemoved:
2217       LIBMTP_INFO("Received event PTP_EC_ObjectRemoved in session %u\n", session_id);
2218       *event = LIBMTP_EVENT_OBJECT_REMOVED;
2219       *out1 = param1;
2220       break;
2221     case PTP_EC_StoreAdded:
2222       LIBMTP_INFO("Received event PTP_EC_StoreAdded in session %u\n", session_id);
2223       /* TODO: rescan storages */
2224       *event = LIBMTP_EVENT_STORE_ADDED;
2225       *out1 = param1;
2226       break;
2227     case PTP_EC_StoreRemoved:
2228       LIBMTP_INFO("Received event PTP_EC_StoreRemoved in session %u\n", session_id);
2229       /* TODO: rescan storages */
2230       *event = LIBMTP_EVENT_STORE_REMOVED;
2231       *out1 = param1;
2232       break;
2233     case PTP_EC_DevicePropChanged:
2234       LIBMTP_INFO("Received event PTP_EC_DevicePropChanged in session %u\n", session_id);
2235       /* TODO: update device properties */
2236       break;
2237     case PTP_EC_ObjectInfoChanged:
2238       LIBMTP_INFO("Received event PTP_EC_ObjectInfoChanged in session %u\n", session_id);
2239       /* TODO: rescan object cache or just for this one object */
2240       break;
2241     case PTP_EC_DeviceInfoChanged:
2242       LIBMTP_INFO("Received event PTP_EC_DeviceInfoChanged in session %u\n", session_id);
2243       /* TODO: update device info */
2244       break;
2245     case PTP_EC_RequestObjectTransfer:
2246       LIBMTP_INFO("Received event PTP_EC_RequestObjectTransfer in session %u\n", session_id);
2247       break;
2248     case PTP_EC_StoreFull:
2249       LIBMTP_INFO("Received event PTP_EC_StoreFull in session %u\n", session_id);
2250       break;
2251     case PTP_EC_DeviceReset:
2252       LIBMTP_INFO("Received event PTP_EC_DeviceReset in session %u\n", session_id);
2253       break;
2254     case PTP_EC_StorageInfoChanged :
2255       LIBMTP_INFO( "Received event PTP_EC_StorageInfoChanged in session %u\n", session_id);
2256      /* TODO: update storage info */
2257       break;
2258     case PTP_EC_CaptureComplete :
2259       LIBMTP_INFO( "Received event PTP_EC_CaptureComplete in session %u\n", session_id);
2260       break;
2261     case PTP_EC_UnreportedStatus :
2262       LIBMTP_INFO( "Received event PTP_EC_UnreportedStatus in session %u\n", session_id);
2263       break;
2264     default :
2265       LIBMTP_INFO( "Received unknown event in session %u\n", session_id);
2266       break;
2267   }
2268
2269   return 0;
2270 }
2271
2272 /**
2273  * Recursive function that adds MTP devices to a linked list
2274  * @param devices a list of raw devices to have real devices created for.
2275  * @return a device pointer to a newly created mtpdevice (used in linked
2276  * list creation).
2277  */
2278 static LIBMTP_mtpdevice_t * create_usb_mtp_devices(LIBMTP_raw_device_t *devices, int numdevs)
2279 {
2280   uint8_t i;
2281   LIBMTP_mtpdevice_t *mtp_device_list = NULL;
2282   LIBMTP_mtpdevice_t *current_device = NULL;
2283
2284   for (i=0; i < numdevs; i++) {
2285     LIBMTP_mtpdevice_t *mtp_device;
2286     mtp_device = LIBMTP_Open_Raw_Device(&devices[i]);
2287
2288     /* On error, try next device */
2289     if (mtp_device == NULL)
2290       continue;
2291
2292     /* Add the device to the list */
2293     mtp_device->next = NULL;
2294     if (mtp_device_list == NULL) {
2295       mtp_device_list = current_device = mtp_device;
2296     } else {
2297       current_device->next = mtp_device;
2298       current_device = mtp_device;
2299     }
2300   }
2301   return mtp_device_list;
2302 }
2303
2304 /**
2305  * Get the number of devices that are available in the listed device list
2306  * @param device_list Pointer to a linked list of devices
2307  * @return Number of devices in the device list device_list
2308  * @see LIBMTP_Get_Connected_Devices()
2309  */
2310 uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *device_list)
2311 {
2312   uint32_t numdevices = 0;
2313   LIBMTP_mtpdevice_t *iter;
2314   for(iter = device_list; iter != NULL; iter = iter->next)
2315     numdevices++;
2316
2317   return numdevices;
2318 }
2319
2320 /**
2321  * Get the first connected MTP device node in the linked list of devices.
2322  * Currently this only provides access to USB devices
2323  * @param device_list A list of devices ready to be used by the caller. You
2324  *        need to know how many there are.
2325  * @return Any error information gathered from device connections
2326  * @see LIBMTP_Number_Devices_In_List()
2327  */
2328 LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_list)
2329 {
2330   LIBMTP_raw_device_t *devices;
2331   int numdevs;
2332   LIBMTP_error_number_t ret;
2333
2334   ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
2335   if (ret != LIBMTP_ERROR_NONE) {
2336     *device_list = NULL;
2337     return ret;
2338   }
2339
2340   /* Assign linked list of devices */
2341 #ifdef TIZEN_EXT
2342         if (devices == NULL) {
2343           *device_list = NULL;
2344           return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
2345         }
2346
2347         if (numdevs == 0) {
2348       if (devices) {
2349         free(devices);
2350       }
2351           *device_list = NULL;
2352           return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
2353         }
2354 #else /* TIZEN_EXT */
2355         if (devices == NULL || numdevs == 0) {
2356           *device_list = NULL;
2357           return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
2358         }
2359 #endif /* TIZEN_EXT */
2360
2361   *device_list = create_usb_mtp_devices(devices, numdevs);
2362   free(devices);
2363
2364   /* TODO: Add wifi device access here */
2365
2366   /* We have found some devices but create failed */
2367   if (*device_list == NULL)
2368     return LIBMTP_ERROR_CONNECTING;
2369
2370   return LIBMTP_ERROR_NONE;
2371 }
2372
2373 /**
2374  * This closes and releases an allocated MTP device.
2375  * @param device a pointer to the MTP device to release.
2376  */
2377 void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
2378 {
2379   if(device != NULL)
2380   {
2381     if(device->next != NULL)
2382     {
2383       LIBMTP_Release_Device_List(device->next);
2384     }
2385
2386     LIBMTP_Release_Device(device);
2387   }
2388 }
2389
2390 /**
2391  * This closes and releases an allocated MTP device.
2392  * @param device a pointer to the MTP device to release.
2393  */
2394 void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
2395 {
2396   PTPParams *params = (PTPParams *) device->params;
2397   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2398
2399   close_device(ptp_usb, params);
2400   // Clear error stack
2401   LIBMTP_Clear_Errorstack(device);
2402   // Free iconv() converters...
2403   iconv_close(params->cd_locale_to_ucs2);
2404   iconv_close(params->cd_ucs2_to_locale);
2405   free(ptp_usb);
2406   ptp_free_params(params);
2407   free(params);
2408   free_storage_list(device);
2409   // Free extension list...
2410   if (device->extensions != NULL) {
2411     LIBMTP_device_extension_t *tmp = device->extensions;
2412
2413     while (tmp != NULL) {
2414       LIBMTP_device_extension_t *next = tmp->next;
2415
2416       if (tmp->name)
2417         free(tmp->name);
2418       free(tmp);
2419       tmp = next;
2420     }
2421   }
2422   free(device);
2423 }
2424
2425 /**
2426  * This can be used by any libmtp-intrinsic code that
2427  * need to stack up an error on the stack. You are only
2428  * supposed to add errors to the error stack using this
2429  * function, do not create and reference error entries
2430  * directly.
2431  */
2432 static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
2433                                     LIBMTP_error_number_t errornumber,
2434                                     char const * const error_text)
2435 {
2436   LIBMTP_error_t *newerror;
2437
2438   if (device == NULL) {
2439     LIBMTP_ERROR("LIBMTP PANIC: Trying to add error to a NULL device!\n");
2440     return;
2441   }
2442   newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
2443   if (newerror != NULL) {
2444     newerror->errornumber = errornumber;
2445     newerror->error_text = strdup(error_text);
2446     newerror->next = NULL;
2447   }
2448   if (device->errorstack == NULL) {
2449     device->errorstack = newerror;
2450   } else {
2451     LIBMTP_error_t *tmp = device->errorstack;
2452
2453     while (tmp->next != NULL) {
2454       tmp = tmp->next;
2455     }
2456     tmp->next = newerror;
2457   }
2458 }
2459
2460 /**
2461  * Adds an error from the PTP layer to the error stack.
2462  */
2463 static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
2464                                         uint16_t ptp_error,
2465                                         char const * const error_text)
2466 {
2467   if (device == NULL) {
2468     LIBMTP_ERROR("LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
2469     return;
2470   } else {
2471     char outstr[256];
2472     snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
2473     outstr[sizeof(outstr)-1] = '\0';
2474     add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
2475
2476     snprintf(outstr, sizeof(outstr), "Error %04x: %s", ptp_error, ptp_strerror(ptp_error));
2477     outstr[sizeof(outstr)-1] = '\0';
2478     add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
2479   }
2480 }
2481
2482 /**
2483  * This returns the error stack for a device in case you
2484  * need to either reference the error numbers (e.g. when
2485  * creating multilingual apps with multiple-language text
2486  * representations for each error number) or when you need
2487  * to build a multi-line error text widget or something like
2488  * that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
2489  * to clear it when you're finished with it.
2490  * @param device a pointer to the MTP device to get the error
2491  *        stack for.
2492  * @return the error stack or NULL if there are no errors
2493  *         on the stack.
2494  * @see LIBMTP_Clear_Errorstack()
2495  * @see LIBMTP_Dump_Errorstack()
2496  */
2497 LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
2498 {
2499   if (device == NULL) {
2500     LIBMTP_ERROR("LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
2501     return NULL;
2502   }
2503   return device->errorstack;
2504 }
2505
2506 /**
2507  * This function clears the error stack of a device and frees
2508  * any memory used by it. Call this when you're finished with
2509  * using the errors.
2510  * @param device a pointer to the MTP device to clear the error
2511  *        stack for.
2512  */
2513 void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
2514 {
2515   if (device == NULL) {
2516     LIBMTP_ERROR("LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
2517   } else {
2518     LIBMTP_error_t *tmp = device->errorstack;
2519
2520     while (tmp != NULL) {
2521       LIBMTP_error_t *tmp2;
2522
2523       if (tmp->error_text != NULL) {
2524         free(tmp->error_text);
2525       }
2526       tmp2 = tmp;
2527       tmp = tmp->next;
2528       free(tmp2);
2529     }
2530     device->errorstack = NULL;
2531   }
2532 }
2533
2534 /**
2535  * This function dumps the error stack to <code>stderr</code>.
2536  * (You still have to clear the stack though.)
2537  * @param device a pointer to the MTP device to dump the error
2538  *        stack for.
2539  */
2540 void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
2541 {
2542   if (device == NULL) {
2543     LIBMTP_ERROR("LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
2544   } else {
2545     LIBMTP_error_t *tmp = device->errorstack;
2546
2547     while (tmp != NULL) {
2548       if (tmp->error_text != NULL) {
2549         LIBMTP_ERROR("Error %d: %s\n", tmp->errornumber, tmp->error_text);
2550       } else {
2551         LIBMTP_ERROR("Error %d: (unknown)\n", tmp->errornumber);
2552       }
2553       tmp = tmp->next;
2554     }
2555   }
2556 }
2557
2558 /**
2559  * This command gets all handles and stuff by FAST directory retrieveal
2560  * which is available by getting all metadata for object
2561  * <code>0xffffffff</code> which simply means "all metadata for all objects".
2562  * This works on the vast majority of MTP devices (there ARE exceptions!)
2563  * and is quite quick. Check the error stack to see if there were
2564  * problems getting the metadata.
2565  * @return 0 if all was OK, -1 on failure.
2566  */
2567 static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
2568 {
2569   PTPParams      *params = (PTPParams *) device->params;
2570   int            cnt = 0;
2571   int            i, j, nrofprops;
2572   uint32_t       lasthandle = 0xffffffff;
2573   MTPProperties  *props = NULL;
2574   MTPProperties  *prop;
2575   uint16_t       ret;
2576   int            oldtimeout;
2577   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2578
2579   /*
2580    * The follow request causes the device to generate
2581    * a list of every file on the device and return it
2582    * in a single response.
2583    *
2584    * Some slow devices as well as devices with very
2585    * large file systems can easily take longer then
2586    * the standard timeout value before it is able
2587    * to return a response.
2588    *
2589    * Temporarly set timeout to allow working with
2590    * widest range of devices.
2591    */
2592   get_usb_device_timeout(ptp_usb, &oldtimeout);
2593   set_usb_device_timeout(ptp_usb, 60000);
2594
2595   ret = ptp_mtp_getobjectproplist(params, 0xffffffff, &props, &nrofprops);
2596   set_usb_device_timeout(ptp_usb, oldtimeout);
2597
2598   if (ret == PTP_RC_MTP_Specification_By_Group_Unsupported) {
2599     // What's the point in the device implementing this command if
2600     // you cannot use it to get all props for AT LEAST one object?
2601     // Well, whatever...
2602     add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
2603     "cannot retrieve all metadata for an object on this device.");
2604     return -1;
2605   }
2606   if (ret != PTP_RC_OK) {
2607     add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
2608     "could not get proplist of all objects.");
2609     return -1;
2610   }
2611   if (props == NULL && nrofprops != 0) {
2612     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
2613                             "get_all_metadata_fast(): "
2614                             "call to ptp_mtp_getobjectproplist() returned "
2615                             "inconsistent results.");
2616     return -1;
2617   }
2618   /*
2619    * We count the number of objects by counting the ObjectHandle
2620    * references, whenever it changes we get a new object, when it's
2621    * the same, it is just different properties of the same object.
2622    */
2623   prop = props;
2624   for (i=0;i<nrofprops;i++) {
2625       if (lasthandle != prop->ObjectHandle) {
2626         cnt++;
2627         lasthandle = prop->ObjectHandle;
2628       }
2629       prop++;
2630   }
2631   lasthandle = 0xffffffff;
2632   params->objects = calloc (cnt, sizeof(PTPObject));
2633   if (params->objects == NULL) {
2634     free(props);
2635     return -1;
2636   }
2637
2638   prop = props;
2639   i = -1;
2640   for (j=0;j<nrofprops;j++) {
2641     if (lasthandle != prop->ObjectHandle) {
2642       if (i >= 0) {
2643         params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
2644         if (!params->objects[i].oi.Filename) {
2645           /* I have one such file on my Creative (Marcus) */
2646           params->objects[i].oi.Filename = strdup("<null>");
2647         }
2648       }
2649       i++;
2650       lasthandle = prop->ObjectHandle;
2651       params->objects[i].oid = prop->ObjectHandle;
2652     }
2653     switch (prop->property) {
2654     case PTP_OPC_ParentObject:
2655       params->objects[i].oi.ParentObject = prop->propval.u32;
2656       params->objects[i].flags |= PTPOBJECT_PARENTOBJECT_LOADED;
2657       break;
2658     case PTP_OPC_ObjectFormat:
2659       params->objects[i].oi.ObjectFormat = prop->propval.u16;
2660       break;
2661     case PTP_OPC_ObjectSize:
2662       // We loose precision here, up to 32 bits! However the commands that
2663       // retrieve metadata for files and tracks will make sure that the
2664       // PTP_OPC_ObjectSize is read in and duplicated again.
2665       if (device->object_bitsize == 64) {
2666         params->objects[i].oi.ObjectCompressedSize = (uint32_t) prop->propval.u64;
2667       } else {
2668         params->objects[i].oi.ObjectCompressedSize = prop->propval.u32;
2669       }
2670       break;
2671     case PTP_OPC_StorageID:
2672       params->objects[i].oi.StorageID = prop->propval.u32;
2673       params->objects[i].flags |= PTPOBJECT_STORAGEID_LOADED;
2674       break;
2675     case PTP_OPC_ObjectFileName:
2676       if (prop->propval.str != NULL)
2677         params->objects[i].oi.Filename = strdup(prop->propval.str);
2678       break;
2679     default: {
2680       MTPProperties *newprops;
2681
2682       /* Copy all of the other MTP oprierties into the per-object proplist */
2683       if (params->objects[i].nrofmtpprops) {
2684         newprops = realloc(params->objects[i].mtpprops,
2685                 (params->objects[i].nrofmtpprops+1)*sizeof(MTPProperties));
2686       } else {
2687         newprops = calloc(1,sizeof(MTPProperties));
2688       }
2689       if (!newprops) return 0; /* FIXME: error handling? */
2690       params->objects[i].mtpprops = newprops;
2691       memcpy(&params->objects[i].mtpprops[params->objects[i].nrofmtpprops],
2692              &props[j],sizeof(props[j]));
2693       params->objects[i].nrofmtpprops++;
2694       params->objects[i].flags |= PTPOBJECT_MTPPROPLIST_LOADED;
2695       break;
2696     }
2697     }
2698     prop++;
2699   }
2700   /* mark last entry also */
2701   params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
2702   params->nrofobjects = i+1;
2703   free (props);
2704   /* The device might not give the list in linear ascending order */
2705   ptp_objects_sort (params);
2706   return 0;
2707 }
2708
2709 /**
2710  * This function will recurse through all the directories on the device,
2711  * starting at the root directory, gathering metadata as it moves along.
2712  * It works better on some devices that will only return data for a
2713  * certain directory and does not respect the option to get all metadata
2714  * for all objects.
2715  */
2716 static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
2717                                     PTPParams *params,
2718                                     uint32_t storageid,
2719                                     uint32_t parent)
2720 {
2721   PTPObjectHandles currentHandles;
2722   int i = 0;
2723   uint16_t ret = ptp_getobjecthandles(params,
2724                                       storageid,
2725                                       PTP_GOH_ALL_FORMATS,
2726                                       parent,
2727                                       &currentHandles);
2728
2729   if (ret != PTP_RC_OK) {
2730     add_ptp_error_to_errorstack(device, ret, "get_handles_recursively(): could not get object handles.");
2731     return;
2732   }
2733
2734   if (currentHandles.Handler == NULL || currentHandles.n == 0)
2735     return;
2736
2737   // Now descend into any subdirectories found
2738   for (i = 0; i < currentHandles.n; i++) {
2739     PTPObject *ob;
2740     ret = ptp_object_want(params,currentHandles.Handler[i],
2741                           PTPOBJECT_OBJECTINFO_LOADED, &ob);
2742     if (ret == PTP_RC_OK) {
2743       if (ob->oi.ObjectFormat == PTP_OFC_Association)
2744         get_handles_recursively(device, params,
2745                                 storageid, currentHandles.Handler[i]);
2746     } else {
2747       add_error_to_errorstack(device,
2748                               LIBMTP_ERROR_CONNECTING,
2749                               "Found a bad handle, trying to ignore it.");
2750     }
2751   }
2752   free(currentHandles.Handler);
2753 }
2754
2755 /**
2756  * This function refresh the internal handle list whenever
2757  * the items stored inside the device is altered. On operations
2758  * that do not add or remove objects, this is typically not
2759  * called.
2760  * @param device a pointer to the MTP device to flush handles for.
2761  */
2762 static void flush_handles(LIBMTP_mtpdevice_t *device)
2763 {
2764   PTPParams *params = (PTPParams *) device->params;
2765   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2766   int ret;
2767   uint32_t i;
2768
2769   if (!device->cached) {
2770     return;
2771   }
2772
2773   if (params->objects != NULL) {
2774     for (i=0;i<params->nrofobjects;i++)
2775       ptp_free_object (&params->objects[i]);
2776     free(params->objects);
2777     params->objects = NULL;
2778     params->nrofobjects = 0;
2779   }
2780
2781   if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
2782       && !FLAG_BROKEN_MTPGETOBJPROPLIST(ptp_usb)
2783       && !FLAG_BROKEN_MTPGETOBJPROPLIST_ALL(ptp_usb)) {
2784     // Use the fast method. Ignore return value for now.
2785     ret = get_all_metadata_fast(device);
2786   }
2787
2788   // If the previous failed or returned no objects, use classic
2789   // methods instead.
2790   if (params->nrofobjects == 0) {
2791     // Get all the handles using just standard commands.
2792     if (device->storage == NULL) {
2793       get_handles_recursively(device, params,
2794                               PTP_GOH_ALL_STORAGE,
2795                               PTP_GOH_ROOT_PARENT);
2796     } else {
2797       // Get handles for each storage in turn.
2798       LIBMTP_devicestorage_t *storage = device->storage;
2799       while(storage != NULL) {
2800         get_handles_recursively(device, params,
2801                                 storage->id,
2802                                 PTP_GOH_ROOT_PARENT);
2803         storage = storage->next;
2804       }
2805     }
2806   }
2807
2808   /*
2809    * Loop over the handles, fix up any NULL filenames or
2810    * keywords, then attempt to locate some default folders
2811    * in the root directory of the primary storage.
2812    */
2813   for(i = 0; i < params->nrofobjects; i++) {
2814     PTPObject *ob, *xob;
2815
2816     ob = &params->objects[i];
2817     ret = ptp_object_want(params,params->objects[i].oid,
2818                           PTPOBJECT_OBJECTINFO_LOADED, &xob);
2819     if (ret != PTP_RC_OK) {
2820         LIBMTP_ERROR("broken! %x not found\n", params->objects[i].oid);
2821     }
2822     if (ob->oi.Filename == NULL)
2823       ob->oi.Filename = strdup("<null>");
2824     if (ob->oi.Keywords == NULL)
2825       ob->oi.Keywords = strdup("<null>");
2826
2827     /* Ignore handles that point to non-folders */
2828     if(ob->oi.ObjectFormat != PTP_OFC_Association)
2829       continue;
2830     /* Only look in the root folder */
2831     if (ob->oi.ParentObject == 0xffffffffU) {
2832       LIBMTP_ERROR("object %x has parent 0xffffffff (-1) continuing anyway\n",
2833                    ob->oid);
2834     } else if (ob->oi.ParentObject != 0x00000000U)
2835       continue;
2836     /* Only look in the primary storage */
2837     if (device->storage != NULL && ob->oi.StorageID != device->storage->id)
2838       continue;
2839
2840     /* Is this the Music Folder */
2841     if (!strcasecmp(ob->oi.Filename, "My Music") ||
2842         !strcasecmp(ob->oi.Filename, "My_Music") ||
2843         !strcasecmp(ob->oi.Filename, "Music")) {
2844       device->default_music_folder = ob->oid;
2845     }
2846     else if (!strcasecmp(ob->oi.Filename, "My Playlists") ||
2847              !strcasecmp(ob->oi.Filename, "My_Playlists") ||
2848              !strcasecmp(ob->oi.Filename, "Playlists")) {
2849       device->default_playlist_folder = ob->oid;
2850     }
2851     else if (!strcasecmp(ob->oi.Filename, "My Pictures") ||
2852              !strcasecmp(ob->oi.Filename, "My_Pictures") ||
2853              !strcasecmp(ob->oi.Filename, "Pictures")) {
2854       device->default_picture_folder = ob->oid;
2855     }
2856     else if (!strcasecmp(ob->oi.Filename, "My Video") ||
2857              !strcasecmp(ob->oi.Filename, "My_Video") ||
2858              !strcasecmp(ob->oi.Filename, "Video")) {
2859         device->default_video_folder = ob->oid;
2860     }
2861     else if (!strcasecmp(ob->oi.Filename, "My Organizer") ||
2862              !strcasecmp(ob->oi.Filename, "My_Organizer")) {
2863       device->default_organizer_folder = ob->oid;
2864     }
2865     else if (!strcasecmp(ob->oi.Filename, "ZENcast") ||
2866              !strcasecmp(ob->oi.Filename, "Datacasts")) {
2867       device->default_zencast_folder = ob->oid;
2868     }
2869     else if (!strcasecmp(ob->oi.Filename, "My Albums") ||
2870              !strcasecmp(ob->oi.Filename, "My_Albums") ||
2871              !strcasecmp(ob->oi.Filename, "Albums")) {
2872       device->default_album_folder = ob->oid;
2873     }
2874     else if (!strcasecmp(ob->oi.Filename, "Text") ||
2875              !strcasecmp(ob->oi.Filename, "Texts")) {
2876       device->default_text_folder = ob->oid;
2877     }
2878   }
2879 }
2880
2881 /**
2882  * This function traverses a devices storage list freeing up the
2883  * strings and the structs.
2884  * @param device a pointer to the MTP device to free the storage
2885  * list for.
2886  */
2887 static void free_storage_list(LIBMTP_mtpdevice_t *device)
2888 {
2889   LIBMTP_devicestorage_t *storage;
2890   LIBMTP_devicestorage_t *tmp;
2891
2892   storage = device->storage;
2893   while(storage != NULL) {
2894     if (storage->StorageDescription != NULL) {
2895       free(storage->StorageDescription);
2896     }
2897     if (storage->VolumeIdentifier != NULL) {
2898       free(storage->VolumeIdentifier);
2899     }
2900     tmp = storage;
2901     storage = storage->next;
2902     free(tmp);
2903   }
2904   device->storage = NULL;
2905
2906   return;
2907 }
2908
2909 /**
2910  * This function traverses a devices storage list freeing up the
2911  * strings and the structs.
2912  * @param device a pointer to the MTP device to free the storage
2913  * list for.
2914  */
2915 static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
2916 {
2917   LIBMTP_devicestorage_t *oldhead, *ptr1, *ptr2, *newlist;
2918
2919   if (device->storage == NULL)
2920     return -1;
2921   if (sortby == LIBMTP_STORAGE_SORTBY_NOTSORTED)
2922     return 0;
2923
2924   oldhead = ptr1 = ptr2 = device->storage;
2925
2926   newlist = NULL;
2927
2928   while(oldhead != NULL) {
2929     ptr1 = ptr2 = oldhead;
2930     while(ptr1 != NULL) {
2931
2932       if (sortby == LIBMTP_STORAGE_SORTBY_FREESPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
2933         ptr2 = ptr1;
2934       if (sortby == LIBMTP_STORAGE_SORTBY_MAXSPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
2935         ptr2 = ptr1;
2936
2937       ptr1 = ptr1->next;
2938     }
2939
2940     // Make our previous entries next point to our next
2941     if(ptr2->prev != NULL) {
2942       ptr1 = ptr2->prev;
2943       ptr1->next = ptr2->next;
2944     } else {
2945       oldhead = ptr2->next;
2946       if(oldhead != NULL)
2947         oldhead->prev = NULL;
2948     }
2949
2950     // Make our next entries previous point to our previous
2951     ptr1 = ptr2->next;
2952     if(ptr1 != NULL) {
2953       ptr1->prev = ptr2->prev;
2954     } else {
2955       ptr1 = ptr2->prev;
2956       if(ptr1 != NULL)
2957         ptr1->next = NULL;
2958     }
2959
2960     if(newlist == NULL) {
2961       newlist = ptr2;
2962       newlist->prev = NULL;
2963     } else {
2964       ptr2->prev = newlist;
2965       newlist->next = ptr2;
2966       newlist = newlist->next;
2967     }
2968   }
2969
2970   if (newlist != NULL) {
2971     newlist->next = NULL;
2972     while(newlist->prev != NULL)
2973       newlist = newlist->prev;
2974     device->storage = newlist;
2975   }
2976
2977   return 0;
2978 }
2979
2980 /**
2981  * This function grabs the first writeable storageid from the
2982  * device storage list.
2983  * @param device a pointer to the MTP device to locate writeable
2984  *        storage for.
2985  * @param fitsize a file of this file must fit on the device.
2986  */
2987 static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
2988                                         uint64_t fitsize)
2989 {
2990   LIBMTP_devicestorage_t *storage;
2991   uint32_t store = 0x00000000; //Should this be 0xffffffffu instead?
2992   int subcall_ret;
2993
2994   // See if there is some storage we can fit this file on.
2995   storage = device->storage;
2996   if (storage == NULL) {
2997     // Sometimes the storage just cannot be detected.
2998     store = 0x00000000U;
2999   } else {
3000     while(storage != NULL) {
3001       // These storages cannot be used.
3002       if (storage->StorageType == PTP_ST_FixedROM ||
3003           storage->StorageType == PTP_ST_RemovableROM) {
3004         storage = storage->next;
3005         continue;
3006       }
3007       // Storage IDs with the lower 16 bits 0x0000 are not supposed
3008       // to be writeable.
3009       if ((storage->id & 0x0000FFFFU) == 0x00000000U) {
3010         storage = storage->next;
3011         continue;
3012       }
3013       // Also check the access capability to avoid e.g. deletable only storages
3014       if (storage->AccessCapability == PTP_AC_ReadOnly ||
3015           storage->AccessCapability == PTP_AC_ReadOnly_with_Object_Deletion) {
3016         storage = storage->next;
3017         continue;
3018       }
3019       // Then see if we can fit the file.
3020       subcall_ret = check_if_file_fits(device, storage, fitsize);
3021       if (subcall_ret != 0) {
3022         storage = storage->next;
3023       } else {
3024         // We found a storage that is writable and can fit the file!
3025         break;
3026       }
3027     }
3028     if (storage == NULL) {
3029       add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL,
3030                               "get_writeable_storageid(): "
3031                               "all device storage is full or corrupt.");
3032       return -1;
3033     }
3034     store = storage->id;
3035   }
3036
3037   return store;
3038 }
3039
3040 /**
3041  * Tries to suggest a storage_id of a given ID when we have a parent
3042  * @param device a pointer to the device where to search for the storage ID
3043  * @param fitsize a file of this file must fit on the device.
3044  * @param parent_id look for this ID
3045  * @ret storageID
3046  */
3047 static int get_suggested_storage_id(LIBMTP_mtpdevice_t *device,
3048                                     uint64_t fitsize,
3049                                     uint32_t parent_id)
3050 {
3051   PTPParams *params = (PTPParams *) device->params;
3052   PTPObject *ob;
3053   uint16_t ret;
3054
3055   ret = ptp_object_want(params, parent_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
3056   if ((ret != PTP_RC_OK) || (ob->oi.StorageID == 0)) {
3057     add_ptp_error_to_errorstack(device, ret, "get_suggested_storage_id(): "
3058                                 "could not get storage id from parent id.");
3059     return get_writeable_storageid(device, fitsize);
3060   } else {
3061     /* OK we know the parent storage, then use that */
3062     return ob->oi.StorageID;
3063   }
3064 }
3065
3066 /**
3067  * This function grabs the freespace from a certain storage in
3068  * device storage list.
3069  * @param device a pointer to the MTP device to free the storage
3070  * list for.
3071  * @param storageid the storage ID for the storage to flush and
3072  * get free space for.
3073  * @param freespace the free space on this storage will be returned
3074  * in this variable.
3075  */
3076 static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
3077                                  LIBMTP_devicestorage_t *storage,
3078                                  uint64_t *freespace)
3079 {
3080   PTPParams *params = (PTPParams *) device->params;
3081
3082   // Always query the device about this, since some models explicitly
3083   // needs that. We flush all data on queries storage here.
3084   if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
3085     PTPStorageInfo storageInfo;
3086     uint16_t ret;
3087
3088     ret = ptp_getstorageinfo(params, storage->id, &storageInfo);
3089     if (ret != PTP_RC_OK) {
3090       add_ptp_error_to_errorstack(device, ret,
3091                 "get_storage_freespace(): could not get storage info.");
3092       return -1;
3093     }
3094     if (storage->StorageDescription != NULL) {
3095       free(storage->StorageDescription);
3096     }
3097     if (storage->VolumeIdentifier != NULL) {
3098       free(storage->VolumeIdentifier);
3099     }
3100     storage->StorageType = storageInfo.StorageType;
3101     storage->FilesystemType = storageInfo.FilesystemType;
3102     storage->AccessCapability = storageInfo.AccessCapability;
3103     storage->MaxCapacity = storageInfo.MaxCapability;
3104     storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
3105     storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
3106     storage->StorageDescription = storageInfo.StorageDescription;
3107     storage->VolumeIdentifier = storageInfo.VolumeLabel;
3108   }
3109   if(storage->FreeSpaceInBytes == (uint64_t) -1)
3110     return -1;
3111   *freespace = storage->FreeSpaceInBytes;
3112   return 0;
3113 }
3114
3115 /**
3116  * This function dumps out a large chunk of textual information
3117  * provided from the PTP protocol and additionally some extra
3118  * MTP-specific information where applicable.
3119  * @param device a pointer to the MTP device to report info from.
3120  */
3121 void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
3122 {
3123   int i;
3124   PTPParams *params = (PTPParams *) device->params;
3125   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
3126   LIBMTP_devicestorage_t *storage = device->storage;
3127   LIBMTP_device_extension_t *tmpext = device->extensions;
3128
3129   printf("USB low-level info:\n");
3130   dump_usbinfo(ptp_usb);
3131   /* Print out some verbose information */
3132   printf("Device info:\n");
3133   printf("   Manufacturer: %s\n", params->deviceinfo.Manufacturer);
3134   printf("   Model: %s\n", params->deviceinfo.Model);
3135   printf("   Device version: %s\n", params->deviceinfo.DeviceVersion);
3136   printf("   Serial number: %s\n", params->deviceinfo.SerialNumber);
3137   printf("   Vendor extension ID: 0x%08x\n",
3138          params->deviceinfo.VendorExtensionID);
3139   printf("   Vendor extension description: %s\n",
3140          params->deviceinfo.VendorExtensionDesc);
3141   printf("   Detected object size: %d bits\n",
3142          device->object_bitsize);
3143   printf("   Extensions:\n");
3144   while (tmpext != NULL) {
3145     printf("        %s: %d.%d\n",
3146            tmpext->name,
3147            tmpext->major,
3148            tmpext->minor);
3149     tmpext = tmpext->next;
3150   }
3151   LIBMTP_INFO("Supported operations:");
3152   for (i=0;i<params->deviceinfo.OperationsSupported_len;i++) {
3153     char txt[256];
3154
3155     (void) ptp_render_opcode(params, params->deviceinfo.OperationsSupported[i],
3156                              sizeof(txt), txt);
3157     LIBMTP_INFO("   %04x: %s", params->deviceinfo.OperationsSupported[i], txt);
3158   }
3159   LIBMTP_INFO("Events supported:\n");
3160   if (params->deviceinfo.EventsSupported_len == 0) {
3161     LIBMTP_INFO("   None.\n");
3162   } else {
3163     for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
3164       LIBMTP_INFO("   0x%04x\n", params->deviceinfo.EventsSupported[i]);
3165     }
3166   }
3167   LIBMTP_INFO("Device Properties Supported:\n");
3168   for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
3169     char const *propdesc = ptp_get_property_description(params,
3170                         params->deviceinfo.DevicePropertiesSupported[i]);
3171
3172     if (propdesc != NULL) {
3173       LIBMTP_INFO("   0x%04x: %s\n",
3174              params->deviceinfo.DevicePropertiesSupported[i], propdesc);
3175     } else {
3176       uint16_t prop = params->deviceinfo.DevicePropertiesSupported[i];
3177       LIBMTP_INFO("   0x%04x: Unknown property\n", prop);
3178     }
3179   }
3180
3181   if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
3182     printf("Playable File (Object) Types and Object Properties Supported:\n");
3183     for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
3184       char txt[256];
3185       uint16_t ret;
3186       uint16_t *props = NULL;
3187       uint32_t propcnt = 0;
3188       int j;
3189
3190       (void) ptp_render_ofc (params, params->deviceinfo.ImageFormats[i],
3191                              sizeof(txt), txt);
3192       printf("   %04x: %s\n", params->deviceinfo.ImageFormats[i], txt);
3193
3194       ret = ptp_mtp_getobjectpropssupported (params,
3195                         params->deviceinfo.ImageFormats[i], &propcnt, &props);
3196       if (ret != PTP_RC_OK) {
3197         add_ptp_error_to_errorstack(device, ret, "LIBMTP_Dump_Device_Info(): "
3198                                     "error on query for object properties.");
3199       } else {
3200         for (j=0;j<propcnt;j++) {
3201           PTPObjectPropDesc opd;
3202           int k;
3203
3204           printf("      %04x: %s", props[j],
3205                  LIBMTP_Get_Property_Description(map_ptp_property_to_libmtp_property(props[j])));
3206           // Get a more verbose description
3207           ret = ptp_mtp_getobjectpropdesc(params, props[j],
3208                                           params->deviceinfo.ImageFormats[i],
3209                                           &opd);
3210           if (ret != PTP_RC_OK) {
3211             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
3212                                     "LIBMTP_Dump_Device_Info(): "
3213                                     "could not get property description.");
3214             break;
3215           }
3216
3217           if (opd.DataType == PTP_DTC_STR) {
3218             printf(" STRING data type");
3219             switch (opd.FormFlag) {
3220             case PTP_OPFF_DateTime:
3221               printf(" DATETIME FORM");
3222               break;
3223             case PTP_OPFF_RegularExpression:
3224               printf(" REGULAR EXPRESSION FORM");
3225               break;
3226             case PTP_OPFF_LongString:
3227               printf(" LONG STRING FORM");
3228               break;
3229             default:
3230               break;
3231             }
3232           } else {
3233             if (opd.DataType & PTP_DTC_ARRAY_MASK) {
3234               printf(" array of");
3235             }
3236
3237             switch (opd.DataType & (~PTP_DTC_ARRAY_MASK)) {
3238
3239             case PTP_DTC_UNDEF:
3240               printf(" UNDEFINED data type");
3241               break;
3242             case PTP_DTC_INT8:
3243               printf(" INT8 data type");
3244               switch (opd.FormFlag) {
3245               case PTP_OPFF_Range:
3246                 printf(" range: MIN %d, MAX %d, STEP %d",
3247                        opd.FORM.Range.MinimumValue.i8,
3248                        opd.FORM.Range.MaximumValue.i8,
3249                        opd.FORM.Range.StepSize.i8);
3250                 break;
3251               case PTP_OPFF_Enumeration:
3252                 printf(" enumeration: ");
3253                 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3254                   printf("%d, ", opd.FORM.Enum.SupportedValue[k].i8);
3255                 }
3256                 break;
3257               case PTP_OPFF_ByteArray:
3258                 printf(" byte array: ");
3259                 break;
3260               default:
3261                 printf(" ANY 8BIT VALUE form");
3262                 break;
3263               }
3264               break;
3265
3266             case PTP_DTC_UINT8:
3267               printf(" UINT8 data type");
3268               switch (opd.FormFlag) {
3269               case PTP_OPFF_Range:
3270                 printf(" range: MIN %d, MAX %d, STEP %d",
3271                        opd.FORM.Range.MinimumValue.u8,
3272                        opd.FORM.Range.MaximumValue.u8,
3273                        opd.FORM.Range.StepSize.u8);
3274                 break;
3275               case PTP_OPFF_Enumeration:
3276                 printf(" enumeration: ");
3277                 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3278                   printf("%d, ", opd.FORM.Enum.SupportedValue[k].u8);
3279                 }
3280                 break;
3281               case PTP_OPFF_ByteArray:
3282                 printf(" byte array: ");
3283                 break;
3284               default:
3285                 printf(" ANY 8BIT VALUE form");
3286                 break;
3287               }
3288               break;
3289
3290             case PTP_DTC_INT16:
3291               printf(" INT16 data type");
3292               switch (opd.FormFlag) {
3293               case PTP_OPFF_Range:
3294               printf(" range: MIN %d, MAX %d, STEP %d",
3295                      opd.FORM.Range.MinimumValue.i16,
3296                      opd.FORM.Range.MaximumValue.i16,
3297                      opd.FORM.Range.StepSize.i16);
3298               break;
3299               case PTP_OPFF_Enumeration:
3300                 printf(" enumeration: ");
3301                 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3302                   printf("%d, ", opd.FORM.Enum.SupportedValue[k].i16);
3303                 }
3304                 break;
3305               default:
3306                 printf(" ANY 16BIT VALUE form");
3307                 break;
3308               }
3309               break;
3310
3311             case PTP_DTC_UINT16:
3312               printf(" UINT16 data type");
3313               switch (opd.FormFlag) {
3314               case PTP_OPFF_Range:
3315                 printf(" range: MIN %d, MAX %d, STEP %d",
3316                        opd.FORM.Range.MinimumValue.u16,
3317                        opd.FORM.Range.MaximumValue.u16,
3318                        opd.FORM.Range.StepSize.u16);
3319                 break;
3320               case PTP_OPFF_Enumeration:
3321                 printf(" enumeration: ");
3322                 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3323                   printf("%d, ", opd.FORM.Enum.SupportedValue[k].u16);
3324                 }
3325                 break;
3326               default:
3327                 printf(" ANY 16BIT VALUE form");
3328                 break;
3329               }
3330               break;
3331
3332             case PTP_DTC_INT32:
3333               printf(" INT32 data type");
3334               switch (opd.FormFlag) {
3335               case PTP_OPFF_Range:
3336                 printf(" range: MIN %d, MAX %d, STEP %d",
3337                        opd.FORM.Range.MinimumValue.i32,
3338                        opd.FORM.Range.MaximumValue.i32,
3339                        opd.FORM.Range.StepSize.i32);
3340                 break;
3341               case PTP_OPFF_Enumeration:
3342                 printf(" enumeration: ");
3343                 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3344                   printf("%d, ", opd.FORM.Enum.SupportedValue[k].i32);
3345                 }
3346                 break;
3347               default:
3348                 printf(" ANY 32BIT VALUE form");
3349                 break;
3350               }
3351               break;
3352
3353             case PTP_DTC_UINT32:
3354               printf(" UINT32 data type");
3355               switch (opd.FormFlag) {
3356               case PTP_OPFF_Range:
3357                 printf(" range: MIN %d, MAX %d, STEP %d",
3358                        opd.FORM.Range.MinimumValue.u32,
3359                        opd.FORM.Range.MaximumValue.u32,
3360                        opd.FORM.Range.StepSize.u32);
3361                 break;
3362               case PTP_OPFF_Enumeration:
3363                 // Special pretty-print for FOURCC codes
3364                 if (params->deviceinfo.ImageFormats[i] == PTP_OPC_VideoFourCCCodec) {
3365                   printf(" enumeration of u32 casted FOURCC: ");
3366                   for (k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3367                     if (opd.FORM.Enum.SupportedValue[k].u32 == 0) {
3368                       printf("ANY, ");
3369                     } else {
3370                       char fourcc[6];
3371                       fourcc[0] = (opd.FORM.Enum.SupportedValue[k].u32 >> 24) & 0xFFU;
3372                       fourcc[1] = (opd.FORM.Enum.SupportedValue[k].u32 >> 16) & 0xFFU;
3373                       fourcc[2] = (opd.FORM.Enum.SupportedValue[k].u32 >> 8) & 0xFFU;
3374                       fourcc[3] = opd.FORM.Enum.SupportedValue[k].u32 & 0xFFU;
3375                       fourcc[4] = '\n';
3376                       fourcc[5] = '\0';
3377                       printf("\"%s\", ", fourcc);
3378                     }
3379                   }
3380                 } else {
3381                   printf(" enumeration: ");
3382                   for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3383                     printf("%d, ", opd.FORM.Enum.SupportedValue[k].u32);
3384                   }
3385                 }
3386                 break;
3387               default:
3388                 printf(" ANY 32BIT VALUE form");
3389                 break;
3390               }
3391               break;
3392
3393             case PTP_DTC_INT64:
3394               printf(" INT64 data type");
3395               break;
3396
3397             case PTP_DTC_UINT64:
3398               printf(" UINT64 data type");
3399               break;
3400
3401             case PTP_DTC_INT128:
3402               printf(" INT128 data type");
3403               break;
3404
3405             case PTP_DTC_UINT128:
3406               printf(" UINT128 data type");
3407               break;
3408
3409             default:
3410               printf(" UNKNOWN data type");
3411               break;
3412             }
3413           }
3414           if (opd.GetSet) {
3415             printf(" GET/SET");
3416           } else {
3417             printf(" READ ONLY");
3418           }
3419           printf("\n");
3420           ptp_free_objectpropdesc(&opd);
3421         }
3422         free(props);
3423       }
3424     }
3425   }
3426
3427   if(storage != NULL &&
3428      ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
3429     printf("Storage Devices:\n");
3430     while(storage != NULL) {
3431       printf("   StorageID: 0x%08x\n",storage->id);
3432       printf("      StorageType: 0x%04x ",storage->StorageType);
3433       switch (storage->StorageType) {
3434       case PTP_ST_Undefined:
3435         printf("(undefined)\n");
3436         break;
3437       case PTP_ST_FixedROM:
3438         printf("fixed ROM storage\n");
3439         break;
3440       case PTP_ST_RemovableROM:
3441         printf("removable ROM storage\n");
3442         break;
3443       case PTP_ST_FixedRAM:
3444         printf("fixed RAM storage\n");
3445         break;
3446       case PTP_ST_RemovableRAM:
3447         printf("removable RAM storage\n");
3448         break;
3449       default:
3450         printf("UNKNOWN storage\n");
3451         break;
3452       }
3453       printf("      FilesystemType: 0x%04x ",storage->FilesystemType);
3454       switch(storage->FilesystemType) {
3455       case PTP_FST_Undefined:
3456         printf("(undefined)\n");
3457         break;
3458       case PTP_FST_GenericFlat:
3459         printf("generic flat filesystem\n");
3460         break;
3461       case PTP_FST_GenericHierarchical:
3462         printf("generic hierarchical\n");
3463         break;
3464       case PTP_FST_DCF:
3465         printf("DCF\n");
3466         break;
3467       default:
3468         printf("UNKNONWN filesystem type\n");
3469         break;
3470       }
3471       printf("      AccessCapability: 0x%04x ",storage->AccessCapability);
3472       switch(storage->AccessCapability) {
3473       case PTP_AC_ReadWrite:
3474         printf("read/write\n");
3475         break;
3476       case PTP_AC_ReadOnly:
3477         printf("read only\n");
3478         break;
3479       case PTP_AC_ReadOnly_with_Object_Deletion:
3480         printf("read only + object deletion\n");
3481         break;
3482       default:
3483         printf("UNKNOWN access capability\n");
3484         break;
3485       }
3486       printf("      MaxCapacity: %llu\n",
3487              (long long unsigned int) storage->MaxCapacity);
3488       printf("      FreeSpaceInBytes: %llu\n",
3489              (long long unsigned int) storage->FreeSpaceInBytes);
3490       printf("      FreeSpaceInObjects: %llu\n",
3491              (long long unsigned int) storage->FreeSpaceInObjects);
3492       printf("      StorageDescription: %s\n",storage->StorageDescription);
3493       printf("      VolumeIdentifier: %s\n",storage->VolumeIdentifier);
3494       storage = storage->next;
3495     }
3496   }
3497
3498   printf("Special directories:\n");
3499   printf("   Default music folder: 0x%08x\n",
3500          device->default_music_folder);
3501   printf("   Default playlist folder: 0x%08x\n",
3502          device->default_playlist_folder);
3503   printf("   Default picture folder: 0x%08x\n",
3504          device->default_picture_folder);
3505   printf("   Default video folder: 0x%08x\n",
3506          device->default_video_folder);
3507   printf("   Default organizer folder: 0x%08x\n",
3508          device->default_organizer_folder);
3509   printf("   Default zencast folder: 0x%08x\n",
3510          device->default_zencast_folder);
3511   printf("   Default album folder: 0x%08x\n",
3512          device->default_album_folder);
3513   printf("   Default text folder: 0x%08x\n",
3514          device->default_text_folder);
3515 }
3516
3517 /**
3518  * This resets a device in case it supports the <code>PTP_OC_ResetDevice</code>
3519  * operation code (0x1010).
3520  * @param device a pointer to the device to reset.
3521  * @return 0 on success, any other value means failure.
3522  */
3523 int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t *device)
3524 {
3525   PTPParams *params = (PTPParams *) device->params;
3526   uint16_t ret;
3527
3528   if (!ptp_operation_issupported(params,PTP_OC_ResetDevice)) {
3529     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
3530                             "LIBMTP_Reset_Device(): "
3531                             "device does not support resetting.");
3532     return -1;
3533   }
3534   ret = ptp_resetdevice(params);
3535   if (ret != PTP_RC_OK) {
3536     add_ptp_error_to_errorstack(device, ret, "Error resetting.");
3537     return -1;
3538   }
3539   return 0;
3540 }
3541
3542 /**
3543  * This retrieves the manufacturer name of an MTP device.
3544  * @param device a pointer to the device to get the manufacturer name for.
3545  * @return a newly allocated UTF-8 string representing the manufacturer name.
3546  *         The string must be freed by the caller after use. If the call
3547  *         was unsuccessful this will contain NULL.
3548  */
3549 char *LIBMTP_Get_Manufacturername(LIBMTP_mtpdevice_t *device)
3550 {
3551   char *retmanuf = NULL;
3552   PTPParams *params = (PTPParams *) device->params;
3553
3554   if (params->deviceinfo.Manufacturer != NULL) {
3555     retmanuf = strdup(params->deviceinfo.Manufacturer);
3556   }
3557   return retmanuf;
3558 }
3559
3560 /**
3561  * This retrieves the model name (often equal to product name)
3562  * of an MTP device.
3563  * @param device a pointer to the device to get the model name for.
3564  * @return a newly allocated UTF-8 string representing the model name.
3565  *         The string must be freed by the caller after use. If the call
3566  *         was unsuccessful this will contain NULL.
3567  */
3568 char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t *device)
3569 {
3570   char *retmodel = NULL;
3571   PTPParams *params = (PTPParams *) device->params;
3572
3573   if (params->deviceinfo.Model != NULL) {
3574     retmodel = strdup(params->deviceinfo.Model);
3575   }
3576   return retmodel;
3577 }
3578
3579 /**
3580  * This retrieves the serial number of an MTP device.
3581  * @param device a pointer to the device to get the serial number for.
3582  * @return a newly allocated UTF-8 string representing the serial number.
3583  *         The string must be freed by the caller after use. If the call
3584  *         was unsuccessful this will contain NULL.
3585  */
3586 char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t *device)
3587 {
3588   char *retnumber = NULL;
3589   PTPParams *params = (PTPParams *) device->params;
3590
3591   if (params->deviceinfo.SerialNumber != NULL) {
3592     retnumber = strdup(params->deviceinfo.SerialNumber);
3593   }
3594   return retnumber;
3595 }
3596
3597 /**
3598  * This retrieves the device version (hardware and firmware version) of an
3599  * MTP device.
3600  * @param device a pointer to the device to get the device version for.
3601  * @return a newly allocated UTF-8 string representing the device version.
3602  *         The string must be freed by the caller after use. If the call
3603  *         was unsuccessful this will contain NULL.
3604  */
3605 char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t *device)
3606 {
3607   char *retversion = NULL;
3608   PTPParams *params = (PTPParams *) device->params;
3609
3610   if (params->deviceinfo.DeviceVersion != NULL) {
3611     retversion = strdup(params->deviceinfo.DeviceVersion);
3612   }
3613   return retversion;
3614 }
3615
3616
3617 /**
3618  * This retrieves the "friendly name" of an MTP device. Usually
3619  * this is simply the name of the owner or something like
3620  * "John Doe's Digital Audio Player". This property should be supported
3621  * by all MTP devices.
3622  * @param device a pointer to the device to get the friendly name for.
3623  * @return a newly allocated UTF-8 string representing the friendly name.
3624  *         The string must be freed by the caller after use.
3625  * @see LIBMTP_Set_Friendlyname()
3626  */
3627 char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t *device)
3628 {
3629   PTPPropertyValue propval;
3630   char *retstring = NULL;
3631   PTPParams *params = (PTPParams *) device->params;
3632   uint16_t ret;
3633
3634   if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
3635     return NULL;
3636   }
3637
3638   ret = ptp_getdevicepropvalue(params,
3639                                PTP_DPC_MTP_DeviceFriendlyName,
3640                                &propval,
3641                                PTP_DTC_STR);
3642   if (ret != PTP_RC_OK) {
3643     add_ptp_error_to_errorstack(device, ret, "Error getting friendlyname.");
3644     return NULL;
3645   }
3646   if (propval.str != NULL) {
3647     retstring = strdup(propval.str);
3648     free(propval.str);
3649   }
3650   return retstring;
3651 }
3652
3653 /**
3654  * Sets the "friendly name" of an MTP device.
3655  * @param device a pointer to the device to set the friendly name for.
3656  * @param friendlyname the new friendly name for the device.
3657  * @return 0 on success, any other value means failure.
3658  * @see LIBMTP_Get_Friendlyname()
3659  */
3660 int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t *device,
3661                          char const * const friendlyname)
3662 {
3663   PTPPropertyValue propval;
3664   PTPParams *params = (PTPParams *) device->params;
3665   uint16_t ret;
3666
3667   if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
3668     return -1;
3669   }
3670   propval.str = (char *) friendlyname;
3671   ret = ptp_setdevicepropvalue(params,
3672                                PTP_DPC_MTP_DeviceFriendlyName,
3673                                &propval,
3674                                PTP_DTC_STR);
3675   if (ret != PTP_RC_OK) {
3676     add_ptp_error_to_errorstack(device, ret, "Error setting friendlyname.");
3677     return -1;
3678   }
3679   return 0;
3680 }
3681
3682 /**
3683  * This retrieves the syncronization partner of an MTP device. This
3684  * property should be supported by all MTP devices.
3685  * @param device a pointer to the device to get the sync partner for.
3686  * @return a newly allocated UTF-8 string representing the synchronization
3687  *         partner. The string must be freed by the caller after use.
3688  * @see LIBMTP_Set_Syncpartner()
3689  */
3690 char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t *device)
3691 {
3692   PTPPropertyValue propval;
3693   char *retstring = NULL;
3694   PTPParams *params = (PTPParams *) device->params;
3695   uint16_t ret;
3696
3697   if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
3698     return NULL;
3699   }
3700
3701   ret = ptp_getdevicepropvalue(params,
3702                                PTP_DPC_MTP_SynchronizationPartner,
3703                                &propval,
3704                                PTP_DTC_STR);
3705   if (ret != PTP_RC_OK) {
3706     add_ptp_error_to_errorstack(device, ret, "Error getting syncpartner.");
3707     return NULL;
3708   }
3709   if (propval.str != NULL) {
3710     retstring = strdup(propval.str);
3711     free(propval.str);
3712   }
3713   return retstring;
3714 }
3715
3716
3717 /**
3718  * Sets the synchronization partner of an MTP device. Note that
3719  * we have no idea what the effect of setting this to "foobar"
3720  * may be. But the general idea seems to be to tell which program
3721  * shall synchronize with this device and tell others to leave
3722  * it alone.
3723  * @param device a pointer to the device to set the sync partner for.
3724  * @param syncpartner the new synchronization partner for the device.
3725  * @return 0 on success, any other value means failure.
3726  * @see LIBMTP_Get_Syncpartner()
3727  */
3728 int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t *device,
3729                          char const * const syncpartner)
3730 {
3731   PTPPropertyValue propval;
3732   PTPParams *params = (PTPParams *) device->params;
3733   uint16_t ret;
3734
3735   if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
3736     return -1;
3737   }
3738   propval.str = (char *) syncpartner;
3739   ret = ptp_setdevicepropvalue(params,
3740                                PTP_DPC_MTP_SynchronizationPartner,
3741                                &propval,
3742                                PTP_DTC_STR);
3743   if (ret != PTP_RC_OK) {
3744     add_ptp_error_to_errorstack(device, ret, "Error setting syncpartner.");
3745     return -1;
3746   }
3747   return 0;
3748 }
3749
3750 /**
3751  * Checks if the device can stora a file of this size or
3752  * if it's too big.
3753  * @param device a pointer to the device.
3754  * @param filesize the size of the file to check whether it will fit.
3755  * @param storageid the ID of the storage to try to fit the file on.
3756  * @return 0 if the file fits, any other value means failure.
3757  */
3758 static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
3759                               LIBMTP_devicestorage_t *storage,
3760                               uint64_t const filesize) {
3761   PTPParams *params = (PTPParams *) device->params;
3762   uint64_t freebytes;
3763   int ret;
3764
3765   // If we cannot check the storage, no big deal.
3766   if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
3767     return 0;
3768   }
3769
3770   ret = get_storage_freespace(device, storage, &freebytes);
3771   if (ret != 0) {
3772     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
3773                             "check_if_file_fits(): error checking free storage.");
3774     return -1;
3775   } else {
3776     // See if it fits.
3777     if (filesize > freebytes) {
3778       return -1;
3779     }
3780   }
3781   return 0;
3782 }
3783
3784
3785 /**
3786  * This function retrieves the current battery level on the device.
3787  * @param device a pointer to the device to get the battery level for.
3788  * @param maximum_level a pointer to a variable that will hold the
3789  *        maximum level of the battery if the call was successful.
3790  * @param current_level a pointer to a variable that will hold the
3791  *        current level of the battery if the call was successful.
3792  *        A value of 0 means that the device is on external power.
3793  * @return 0 if the storage info was successfully retrieved, any other
3794  *        means failure. A typical cause of failure is that
3795  *        the device does not support the battery level property.
3796  */
3797 int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *device,
3798                             uint8_t * const maximum_level,
3799                             uint8_t * const current_level)
3800 {
3801   PTPPropertyValue propval;
3802   uint16_t ret;
3803   PTPParams *params = (PTPParams *) device->params;
3804   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
3805
3806   *maximum_level = 0;
3807   *current_level = 0;
3808
3809   if (FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) ||
3810       !ptp_property_issupported(params, PTP_DPC_BatteryLevel)) {
3811     return -1;
3812   }
3813
3814   ret = ptp_getdevicepropvalue(params, PTP_DPC_BatteryLevel,
3815                                &propval, PTP_DTC_UINT8);
3816   if (ret != PTP_RC_OK) {
3817     add_ptp_error_to_errorstack(device, ret,
3818                                 "LIBMTP_Get_Batterylevel(): "
3819                                 "could not get device property value.");
3820     return -1;
3821   }
3822
3823   *maximum_level = device->maximum_battery_level;
3824   *current_level = propval.u8;
3825
3826   return 0;
3827 }
3828
3829
3830 /**
3831  * Formats device storage (if the device supports the operation).
3832  * WARNING: This WILL delete all data from the device. Make sure you've
3833  * got confirmation from the user BEFORE you call this function.
3834  *
3835  * @param device a pointer to the device containing the storage to format.
3836  * @param storage the actual storage to format.
3837  * @return 0 on success, any other value means failure.
3838  */
3839 int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *device,
3840                           LIBMTP_devicestorage_t *storage)
3841 {
3842   uint16_t ret;
3843   PTPParams *params = (PTPParams *) device->params;
3844
3845   if (!ptp_operation_issupported(params,PTP_OC_FormatStore)) {
3846     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
3847                             "LIBMTP_Format_Storage(): "
3848                             "device does not support formatting storage.");
3849     return -1;
3850   }
3851   ret = ptp_formatstore(params, storage->id);
3852   if (ret != PTP_RC_OK) {
3853     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Format_Storage(): "
3854                                 "failed to format storage.");
3855     return -1;
3856   }
3857   return 0;
3858 }
3859
3860 /**
3861  * Helper function to extract a unicode property off a device.
3862  * This is the standard way of retrieveing unicode device
3863  * properties as described by the PTP spec.
3864  * @param device a pointer to the device to get the property from.
3865  * @param unicstring a pointer to a pointer that will hold the
3866  *        property after this call is completed.
3867  * @param property the property to retrieve.
3868  * @return 0 on success, any other value means failure.
3869  */
3870 static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
3871                                        char **unicstring, uint16_t property)
3872 {
3873   PTPPropertyValue propval;
3874   PTPParams *params = (PTPParams *) device->params;
3875   uint16_t *tmp;
3876   uint16_t ret;
3877   int i;
3878
3879   if (!ptp_property_issupported(params, property)) {
3880     return -1;
3881   }
3882
3883   // Unicode strings are 16bit unsigned integer arrays.
3884   ret = ptp_getdevicepropvalue(params,
3885                                property,
3886                                &propval,
3887                                PTP_DTC_AUINT16);
3888   if (ret != PTP_RC_OK) {
3889     // TODO: add a note on WHICH property that we failed to get.
3890     *unicstring = NULL;
3891     add_ptp_error_to_errorstack(device, ret,
3892                                 "get_device_unicode_property(): "
3893                                 "failed to get unicode property.");
3894     return -1;
3895   }
3896
3897   // Extract the actual array.
3898   // printf("Array of %d elements\n", propval.a.count);
3899   tmp = malloc((propval.a.count + 1)*sizeof(uint16_t));
3900   if (tmp == NULL) {
3901     free(propval.a.v);
3902     return -1;
3903   }
3904
3905   for (i = 0; i < propval.a.count; i++) {
3906     tmp[i] = propval.a.v[i].u16;
3907     // printf("%04x ", tmp[i]);
3908   }
3909   tmp[propval.a.count] = 0x0000U;
3910   free(propval.a.v);
3911
3912   *unicstring = utf16_to_utf8(device, tmp);
3913
3914   free(tmp);
3915
3916   return 0;
3917 }
3918
3919 /**
3920  * This function returns the secure time as an XML document string from
3921  * the device.
3922  * @param device a pointer to the device to get the secure time for.
3923  * @param sectime the secure time string as an XML document or NULL if the call
3924  *         failed or the secure time property is not supported. This string
3925  *         must be <code>free()</code>:ed by the caller after use.
3926  * @return 0 on success, any other value means failure.
3927  */
3928 int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *device, char ** const sectime)
3929 {
3930   return get_device_unicode_property(device, sectime, PTP_DPC_MTP_SecureTime);
3931 }
3932
3933 /**
3934  * This function returns the device (public key) certificate as an
3935  * XML document string from the device.
3936  * @param device a pointer to the device to get the device certificate for.
3937  * @param devcert the device certificate as an XML string or NULL if the call
3938  *        failed or the device certificate property is not supported. This
3939  *        string must be <code>free()</code>:ed by the caller after use.
3940  * @return 0 on success, any other value means failure.
3941  */
3942 int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *device, char ** const devcert)
3943 {
3944   return get_device_unicode_property(device, devcert,
3945                                      PTP_DPC_MTP_DeviceCertificate);
3946 }
3947
3948 /**
3949  * This function retrieves a list of supported file types, i.e. the file
3950  * types that this device claims it supports, e.g. audio file types that
3951  * the device can play etc. This list is mitigated to
3952  * inlcude the file types that libmtp can handle, i.e. it will not list
3953  * filetypes that libmtp will handle internally like playlists and folders.
3954  * @param device a pointer to the device to get the filetype capabilities for.
3955  * @param filetypes a pointer to a pointer that will hold the list of
3956  *        supported filetypes if the call was successful. This list must
3957  *        be <code>free()</code>:ed by the caller after use.
3958  * @param length a pointer to a variable that will hold the length of the
3959  *        list of supported filetypes if the call was successful.
3960  * @return 0 on success, any other value means failure.
3961  * @see LIBMTP_Get_Filetype_Description()
3962  */
3963 int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const filetypes,
3964                                   uint16_t * const length)
3965 {
3966   PTPParams *params = (PTPParams *) device->params;
3967   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
3968   uint16_t *localtypes;
3969   uint16_t localtypelen;
3970   uint32_t i;
3971
3972   // This is more memory than needed if there are unknown types, but what the heck.
3973   localtypes = (uint16_t *) malloc(params->deviceinfo.ImageFormats_len * sizeof(uint16_t));
3974   if (localtypes == NULL)
3975     return -1;
3976
3977   localtypelen = 0;
3978
3979   for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
3980     uint16_t localtype = map_ptp_type_to_libmtp_type(params->deviceinfo.ImageFormats[i]);
3981     if (localtype != LIBMTP_FILETYPE_UNKNOWN) {
3982       localtypes[localtypelen] = localtype;
3983       localtypelen++;
3984     }
3985   }
3986   // The forgotten Ogg support on YP-10 and others...
3987   if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
3988     localtypes = (uint16_t *) realloc(localtypes,
3989                 (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
3990     if (localtypes == NULL)
3991       return -1;
3992
3993     localtypes[localtypelen] = LIBMTP_FILETYPE_OGG;
3994     localtypelen++;
3995   }
3996   // The forgotten FLAC support on Cowon iAudio S9 and others...
3997   if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
3998     localtypes = (uint16_t *) realloc(localtypes,
3999                 (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
4000     if (localtypes == NULL)
4001       return -1;
4002
4003     localtypes[localtypelen] = LIBMTP_FILETYPE_FLAC;
4004     localtypelen++;
4005   }
4006
4007   *filetypes = localtypes;
4008   *length = localtypelen;
4009
4010   return 0;
4011 }
4012
4013 /**
4014  * This function checks if the device has some specific capabilities, in
4015  * order to avoid calling APIs that may disturb the device.
4016  *
4017  * @param device a pointer to the device to check the capability on.
4018  * @param cap the capability to check.
4019  * @return 0 if not supported, any other value means the device has the
4020  * requested capability.
4021  */
4022 int LIBMTP_Check_Capability(LIBMTP_mtpdevice_t *device, LIBMTP_devicecap_t cap)
4023 {
4024   switch (cap) {
4025   case LIBMTP_DEVICECAP_GetPartialObject:
4026     return (ptp_operation_issupported(device->params,
4027                                       PTP_OC_GetPartialObject) ||
4028             ptp_operation_issupported(device->params,
4029                                       PTP_OC_ANDROID_GetPartialObject64));
4030   case LIBMTP_DEVICECAP_SendPartialObject:
4031     return ptp_operation_issupported(device->params,
4032                                      PTP_OC_ANDROID_SendPartialObject);
4033   case LIBMTP_DEVICECAP_EditObjects:
4034     return (ptp_operation_issupported(device->params,
4035                                       PTP_OC_ANDROID_TruncateObject) &&
4036             ptp_operation_issupported(device->params,
4037                                       PTP_OC_ANDROID_BeginEditObject) &&
4038             ptp_operation_issupported(device->params,
4039                                       PTP_OC_ANDROID_EndEditObject));
4040   /*
4041    * Handle other capabilities here, this is also a good place to
4042    * blacklist some advanced operations on specific devices if need
4043    * be.
4044    */
4045
4046   default:
4047     break;
4048   }
4049   return 0;
4050 }
4051
4052 /**
4053  * This function updates all the storage id's of a device and their
4054  * properties, then creates a linked list and puts the list head into
4055  * the device struct. It also optionally sorts this list. If you want
4056  * to display storage information in your application you should call
4057  * this function, then dereference the device struct
4058  * (<code>device-&gt;storage</code>) to get out information on the storage.
4059  *
4060  * You need to call this everytime you want to update the
4061  * <code>device-&gt;storage</code> list, for example anytime you need
4062  * to check available storage somewhere.
4063  *
4064  * <b>WARNING:</b> since this list is dynamically updated, do not
4065  * reference its fields in external applications by pointer! E.g
4066  * do not put a reference to any <code>char *</code> field. instead
4067  * <code>strncpy()</code> it!
4068  *
4069  * @param device a pointer to the device to get the storage for.
4070  * @param sortby an integer that determines the sorting of the storage list.
4071  *        Valid sort methods are defined in libmtp.h with beginning with
4072  *        LIBMTP_STORAGE_SORTBY_. 0 or LIBMTP_STORAGE_SORTBY_NOTSORTED to not
4073  *        sort.
4074  * @return 0 on success, 1 success but only with storage id's, storage
4075  *        properities could not be retrieved and -1 means failure.
4076  */
4077 int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *device, int const sortby)
4078 {
4079   uint32_t i = 0;
4080   PTPStorageInfo storageInfo;
4081   PTPParams *params = (PTPParams *) device->params;
4082   PTPStorageIDs storageIDs;
4083   LIBMTP_devicestorage_t *storage = NULL;
4084   LIBMTP_devicestorage_t *storageprev = NULL;
4085
4086   if (device->storage != NULL)
4087     free_storage_list(device);
4088
4089   // if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs))
4090   //   return -1;
4091   if (ptp_getstorageids (params, &storageIDs) != PTP_RC_OK)
4092     return -1;
4093   if (storageIDs.n < 1)
4094     return -1;
4095
4096   if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
4097     for (i = 0; i < storageIDs.n; i++) {
4098
4099       storage = (LIBMTP_devicestorage_t *)
4100         malloc(sizeof(LIBMTP_devicestorage_t));
4101       if (storage == NULL)
4102         return -1;
4103       storage->prev = storageprev;
4104       if (storageprev != NULL)
4105         storageprev->next = storage;
4106       if (device->storage == NULL)
4107         device->storage = storage;
4108
4109       storage->id = storageIDs.Storage[i];
4110       storage->StorageType = PTP_ST_Undefined;
4111       storage->FilesystemType = PTP_FST_Undefined;
4112       storage->AccessCapability = PTP_AC_ReadWrite;
4113       storage->MaxCapacity = (uint64_t) -1;
4114       storage->FreeSpaceInBytes = (uint64_t) -1;
4115       storage->FreeSpaceInObjects = (uint64_t) -1;
4116       storage->StorageDescription = strdup("Unknown storage");
4117       storage->VolumeIdentifier = strdup("Unknown volume");
4118       storage->next = NULL;
4119
4120       storageprev = storage;
4121     }
4122     free(storageIDs.Storage);
4123     return 1;
4124   } else {
4125     for (i = 0; i < storageIDs.n; i++) {
4126       uint16_t ret;
4127       ret = ptp_getstorageinfo(params, storageIDs.Storage[i], &storageInfo);
4128       if (ret != PTP_RC_OK) {
4129         add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Storage(): "
4130                                     "Could not get storage info.");
4131         if (device->storage != NULL) {
4132           free_storage_list(device);
4133         }
4134         return -1;
4135       }
4136
4137       storage = (LIBMTP_devicestorage_t *)
4138         malloc(sizeof(LIBMTP_devicestorage_t));
4139       if (storage == NULL)
4140         return -1;
4141       storage->prev = storageprev;
4142       if (storageprev != NULL)
4143         storageprev->next = storage;
4144       if (device->storage == NULL)
4145         device->storage = storage;
4146
4147       storage->id = storageIDs.Storage[i];
4148       storage->StorageType = storageInfo.StorageType;
4149       storage->FilesystemType = storageInfo.FilesystemType;
4150       storage->AccessCapability = storageInfo.AccessCapability;
4151       storage->MaxCapacity = storageInfo.MaxCapability;
4152       storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
4153       storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
4154       storage->StorageDescription = storageInfo.StorageDescription;
4155       storage->VolumeIdentifier = storageInfo.VolumeLabel;
4156       storage->next = NULL;
4157
4158       storageprev = storage;
4159     }
4160
4161     if (storage != NULL)
4162       storage->next = NULL;
4163
4164     sort_storage_by(device,sortby);
4165     free(storageIDs.Storage);
4166     return 0;
4167   }
4168 }
4169
4170 /**
4171  * This creates a new file metadata structure and allocates memory
4172  * for it. Notice that if you add strings to this structure they
4173  * will be freed by the corresponding <code>LIBMTP_destroy_file_t</code>
4174  * operation later, so be careful of using strdup() when assigning
4175  * strings, e.g.:
4176  *
4177  * <pre>
4178  * LIBMTP_file_t *file = LIBMTP_new_file_t();
4179  * file->filename = strdup(namestr);
4180  * ....
4181  * LIBMTP_destroy_file_t(file);
4182  * </pre>
4183  *
4184  * @return a pointer to the newly allocated metadata structure.
4185  * @see LIBMTP_destroy_file_t()
4186  */
4187 LIBMTP_file_t *LIBMTP_new_file_t(void)
4188 {
4189   LIBMTP_file_t *new = (LIBMTP_file_t *) malloc(sizeof(LIBMTP_file_t));
4190   if (new == NULL) {
4191     return NULL;
4192   }
4193   new->filename = NULL;
4194   new->item_id = 0;
4195   new->parent_id = 0;
4196   new->storage_id = 0;
4197   new->filesize = 0;
4198   new->modificationdate = 0;
4199   new->filetype = LIBMTP_FILETYPE_UNKNOWN;
4200   new->next = NULL;
4201   return new;
4202 }
4203
4204 /**
4205  * This destroys a file metadata structure and deallocates the memory
4206  * used by it, including any strings. Never use a file metadata
4207  * structure again after calling this function on it.
4208  * @param file the file metadata to destroy.
4209  * @see LIBMTP_new_file_t()
4210  */
4211 void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
4212 {
4213   if (file == NULL) {
4214     return;
4215   }
4216   if (file->filename != NULL)
4217     free(file->filename);
4218   free(file);
4219   return;
4220 }
4221
4222 /**
4223  * Helper function that takes one PTP object and creates a
4224  * LIBMTP_file_t metadata entry.
4225  */
4226 static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
4227 {
4228   PTPParams *params = (PTPParams *) device->params;
4229   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
4230   LIBMTP_file_t *file;
4231   int i;
4232
4233   // Allocate a new file type
4234   file = LIBMTP_new_file_t();
4235
4236   file->parent_id = ob->oi.ParentObject;
4237   file->storage_id = ob->oi.StorageID;
4238
4239   // Set the filetype
4240   file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
4241
4242   /*
4243    * A special quirk for devices that doesn't quite
4244    * remember that some files marked as "unknown" type are
4245    * actually OGG or FLAC files. We look at the filename extension
4246    * and see if it happens that this was atleast named "ogg" or "flac"
4247    * and fall back on this heuristic approach in that case,
4248    * for these bugged devices only.
4249    */
4250   if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
4251     if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
4252          FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
4253         has_ogg_extension(file->filename)) {
4254       file->filetype = LIBMTP_FILETYPE_OGG;
4255     }
4256
4257     if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && has_flac_extension(file->filename)) {
4258         file->filetype = LIBMTP_FILETYPE_FLAC;
4259     }
4260   }
4261
4262   // Set the modification date
4263   file->modificationdate = ob->oi.ModificationDate;
4264
4265   // We only have 32-bit file size here; later we use the PTP_OPC_ObjectSize property
4266   file->filesize = ob->oi.ObjectCompressedSize;
4267   if (ob->oi.Filename != NULL) {
4268     file->filename = strdup(ob->oi.Filename);
4269   }
4270
4271   // This is a unique ID so we can keep track of the file.
4272   file->item_id = ob->oid;
4273
4274   /*
4275    * If we have a cached, large set of metadata, then use it!
4276    */
4277   if (ob->mtpprops) {
4278     MTPProperties *prop = ob->mtpprops;
4279
4280     for (i=0; i < ob->nrofmtpprops; i++, prop++) {
4281       // Pick ObjectSize here...
4282       if (prop->property == PTP_OPC_ObjectSize) {
4283         // This may already be set, but this 64bit precision value
4284         // is better than the PTP 32bit value, so let it override.
4285         if (device->object_bitsize == 64) {
4286           file->filesize = prop->propval.u64;
4287         } else {
4288           file->filesize = prop->propval.u32;
4289         }
4290         break;
4291       }
4292     }
4293   } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
4294     uint16_t *props = NULL;
4295     uint32_t propcnt = 0;
4296     int ret;
4297
4298     // First see which properties can be retrieved for this object format
4299     ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(file->filetype), &propcnt, &props);
4300     if (ret != PTP_RC_OK) {
4301       add_ptp_error_to_errorstack(device, ret, "obj2file: call to ptp_mtp_getobjectpropssupported() failed.");
4302       // Silently fall through.
4303     } else {
4304       for (i = 0; i < propcnt; i++) {
4305         switch (props[i]) {
4306         case PTP_OPC_ObjectSize:
4307           if (device->object_bitsize == 64) {
4308             file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
4309           } else {
4310             file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
4311           }
4312           break;
4313         default:
4314           break;
4315         }
4316       }
4317       free(props);
4318     }
4319   }
4320
4321   return file;
4322 }
4323
4324
4325 /**
4326  * This function retrieves the metadata for a single file off
4327  * the device.
4328  *
4329  * Do not call this function repeatedly! The file handles are linearly
4330  * searched O(n) and the call may involve (slow) USB traffic, so use
4331  * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
4332  * as an efficient data structure such as a hash list.
4333  *
4334  * Incidentally this function will return metadata for
4335  * a folder (association) as well, but this is not a proper use
4336  * of it, it is intended for file manipulation, not folder manipulation.
4337  *
4338  * @param device a pointer to the device to get the file metadata from.
4339  * @param fileid the object ID of the file that you want the metadata for.
4340  * @return a metadata entry on success or NULL on failure.
4341  * @see LIBMTP_Get_Filelisting()
4342  */
4343 LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
4344 {
4345   PTPParams *params = (PTPParams *) device->params;
4346   uint16_t ret;
4347   PTPObject *ob;
4348
4349   // Get all the handles if we haven't already done that
4350   // (Only on cached devices.)
4351   if (device->cached && params->nrofobjects == 0) {
4352     flush_handles(device);
4353   }
4354
4355   ret = ptp_object_want(params, fileid, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
4356   if (ret != PTP_RC_OK)
4357     return NULL;
4358
4359   return obj2file(device, ob);
4360 }
4361
4362 /**
4363 * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
4364  * NOT TO USE IT.
4365  * @see LIBMTP_Get_Filelisting_With_Callback()
4366  */
4367 LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *device)
4368 {
4369   LIBMTP_INFO("WARNING: LIBMTP_Get_Filelisting() is deprecated.\n");
4370   LIBMTP_INFO("WARNING: please update your code to use LIBMTP_Get_Filelisting_With_Callback()\n");
4371   return LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
4372 }
4373
4374 /**
4375  * This returns a long list of all files available
4376  * on the current MTP device. Folders will not be returned, but abstract
4377  * entities like playlists and albums will show up as "files". Typical usage:
4378  *
4379  * <pre>
4380  * LIBMTP_file_t *filelist;
4381  *
4382  * filelist = LIBMTP_Get_Filelisting_With_Callback(device, callback, data);
4383  * while (filelist != NULL) {
4384  *   LIBMTP_file_t *tmp;
4385  *
4386  *   // Do something on each element in the list here...
4387  *   tmp = filelist;
4388  *   filelist = filelist->next;
4389  *   LIBMTP_destroy_file_t(tmp);
4390  * }
4391  * </pre>
4392  *
4393  * If you want to group your file listing by storage (per storage unit) or
4394  * arrange files into folders, you must dereference the <code>storage_id</code>
4395  * and/or <code>parent_id</code> field of the returned <code>LIBMTP_file_t</code>
4396  * struct. To arrange by folders or files you typically have to create the proper
4397  * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
4398  * <code>LIBMTP_Get_Folder_List()</code> first.
4399  *
4400  * @param device a pointer to the device to get the file listing for.
4401  * @param callback a function to be called during the tracklisting retrieveal
4402  *        for displaying progress bars etc, or NULL if you don't want
4403  *        any callbacks.
4404  * @param data a user-defined pointer that is passed along to
4405  *        the <code>progress</code> function in order to
4406  *        pass along some user defined data to the progress
4407  *        updates. If not used, set this to NULL.
4408  * @return a list of files that can be followed using the <code>next</code>
4409  *        field of the <code>LIBMTP_file_t</code> data structure.
4410  *        Each of the metadata tags must be freed after use, and may
4411  *        contain only partial metadata information, i.e. one or several
4412  *        fields may be NULL or 0.
4413  * @see LIBMTP_Get_Filemetadata()
4414  */
4415 LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
4416                                                     LIBMTP_progressfunc_t const callback,
4417                                                     void const * const data)
4418 {
4419   uint32_t i = 0;
4420   LIBMTP_file_t *retfiles = NULL;
4421   LIBMTP_file_t *curfile = NULL;
4422   PTPParams *params = (PTPParams *) device->params;
4423
4424   // Get all the handles if we haven't already done that
4425   if (params->nrofobjects == 0) {
4426     flush_handles(device);
4427   }
4428
4429   for (i = 0; i < params->nrofobjects; i++) {
4430     LIBMTP_file_t *file;
4431     PTPObject *ob;
4432
4433     if (callback != NULL)
4434       callback(i, params->nrofobjects, data);
4435
4436     ob = &params->objects[i];
4437
4438     if (ob->oi.ObjectFormat == PTP_OFC_Association) {
4439       // MTP use this object format for folders which means
4440       // these "files" will turn up on a folder listing instead.
4441       continue;
4442     }
4443
4444     // Look up metadata
4445     file = obj2file(device, ob);
4446     if (file == NULL) {
4447       continue;
4448     }
4449
4450     // Add track to a list that will be returned afterwards.
4451     if (retfiles == NULL) {
4452       retfiles = file;
4453       curfile = file;
4454     } else {
4455       curfile->next = file;
4456       curfile = file;
4457     }
4458
4459     // Call listing callback
4460     // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
4461
4462   } // Handle counting loop
4463   return retfiles;
4464 }
4465
4466 /**
4467  * This function retrieves the contents of a certain folder
4468  * with id parent on a certain storage on a certain device.
4469  * The result contains both files and folders.
4470  * The device used with this operations must have been opened with
4471  * LIBMTP_Open_Raw_Device_Uncached() or it will fail.
4472  *
4473  * NOTE: the request will always perform I/O with the device.
4474  * @param device a pointer to the MTP device to report info from.
4475  * @param storage a storage on the device to report info from. If
4476  *        0 is passed in, the files for the given parent will be
4477  *        searched across all available storages.
4478  * @param parent the parent folder id.
4479  */
4480 LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device,
4481                              uint32_t const storage,
4482                              uint32_t const parent)
4483 {
4484   PTPParams *params = (PTPParams *) device->params;
4485   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
4486   LIBMTP_file_t *retfiles = NULL;
4487   LIBMTP_file_t *curfile = NULL;
4488   PTPObjectHandles currentHandles;
4489   uint32_t storageid;
4490   uint16_t ret;
4491   int i = 0;
4492
4493   if (device->cached) {
4494     // This function is only supposed to be used by devices
4495     // opened as uncached!
4496     LIBMTP_ERROR("tried to use %s on a cached device!\n",
4497                  __func__);
4498     return NULL;
4499   }
4500
4501   if (FLAG_BROKEN_GET_OBJECT_PROPVAL(ptp_usb)) {
4502     // These devices cannot handle the commands needed for
4503     // Uncached access!
4504     LIBMTP_ERROR("tried to use %s on an unsupported device, "
4505                  "this command does not work on all devices "
4506                  "due to missing low-level support to read "
4507                  "information on individual tracks\n",
4508                  __func__);
4509     return NULL;
4510   }
4511
4512   if (storage == 0)
4513     storageid = PTP_GOH_ALL_STORAGE;
4514   else
4515     storageid = storage;
4516
4517   ret = ptp_getobjecthandles(params,
4518                              storageid,
4519                              PTP_GOH_ALL_FORMATS,
4520                              parent,
4521                              &currentHandles);
4522
4523   if (ret != PTP_RC_OK) {
4524     add_ptp_error_to_errorstack(device, ret,
4525                 "LIBMTP_Get_Files_And_Folders(): could not get object handles.");
4526     return NULL;
4527   }
4528
4529   if (currentHandles.Handler == NULL || currentHandles.n == 0)
4530     return NULL;
4531
4532   for (i = 0; i < currentHandles.n; i++) {
4533     LIBMTP_file_t *file;
4534
4535     // Get metadata for one file, if it fails, try next file
4536     file = LIBMTP_Get_Filemetadata(device, currentHandles.Handler[i]);
4537     if (file == NULL)
4538       continue;
4539
4540     // Add track to a list that will be returned afterwards.
4541     if (curfile == NULL) {
4542       curfile = file;
4543       retfiles = file;
4544     } else {
4545       curfile->next = file;
4546       curfile = file;
4547     }
4548   }
4549
4550   free(currentHandles.Handler);
4551
4552   // Return a pointer to the original first file
4553   // in the big list.
4554   return retfiles;
4555 }
4556
4557
4558 /**
4559  * This creates a new track metadata structure and allocates memory
4560  * for it. Notice that if you add strings to this structure they
4561  * will be freed by the corresponding <code>LIBMTP_destroy_track_t</code>
4562  * operation later, so be careful of using strdup() when assigning
4563  * strings, e.g.:
4564  *
4565  * <pre>
4566  * LIBMTP_track_t *track = LIBMTP_new_track_t();
4567  * track->title = strdup(titlestr);
4568  * ....
4569  * LIBMTP_destroy_track_t(track);
4570  * </pre>
4571  *
4572  * @return a pointer to the newly allocated metadata structure.
4573  * @see LIBMTP_destroy_track_t()
4574  */
4575 LIBMTP_track_t *LIBMTP_new_track_t(void)
4576 {
4577   LIBMTP_track_t *new = (LIBMTP_track_t *) malloc(sizeof(LIBMTP_track_t));
4578   if (new == NULL) {
4579     return NULL;
4580   }
4581   new->item_id = 0;
4582   new->parent_id = 0;
4583   new->storage_id = 0;
4584   new->title = NULL;
4585   new->artist = NULL;
4586   new->composer = NULL;
4587   new->album = NULL;
4588   new->genre = NULL;
4589   new->date = NULL;
4590   new->filename = NULL;
4591   new->duration = 0;
4592   new->tracknumber = 0;
4593   new->filesize = 0;
4594   new->filetype = LIBMTP_FILETYPE_UNKNOWN;
4595   new->samplerate = 0;
4596   new->nochannels = 0;
4597   new->wavecodec = 0;
4598   new->bitrate = 0;
4599   new->bitratetype = 0;
4600   new->rating = 0;
4601   new->usecount = 0;
4602   new->modificationdate = 0;
4603   new->next = NULL;
4604   return new;
4605 }
4606
4607 /**
4608  * This destroys a track metadata structure and deallocates the memory
4609  * used by it, including any strings. Never use a track metadata
4610  * structure again after calling this function on it.
4611  * @param track the track metadata to destroy.
4612  * @see LIBMTP_new_track_t()
4613  */
4614 void LIBMTP_destroy_track_t(LIBMTP_track_t *track)
4615 {
4616   if (track == NULL) {
4617     return;
4618   }
4619   if (track->title != NULL)
4620     free(track->title);
4621   if (track->artist != NULL)
4622     free(track->artist);
4623   if (track->composer != NULL)
4624     free(track->composer);
4625   if (track->album != NULL)
4626     free(track->album);
4627   if (track->genre != NULL)
4628     free(track->genre);
4629   if (track->date != NULL)
4630     free(track->date);
4631   if (track->filename != NULL)
4632     free(track->filename);
4633   free(track);
4634   return;
4635 }
4636
4637 /**
4638  * This function maps and copies a property onto the track metadata if applicable.
4639  */
4640 static void pick_property_to_track_metadata(LIBMTP_mtpdevice_t *device, MTPProperties *prop, LIBMTP_track_t *track)
4641 {
4642   switch (prop->property) {
4643   case PTP_OPC_Name:
4644     if (prop->propval.str != NULL)
4645       track->title = strdup(prop->propval.str);
4646     else
4647       track->title = NULL;
4648     break;
4649   case PTP_OPC_Artist:
4650     if (prop->propval.str != NULL)
4651       track->artist = strdup(prop->propval.str);
4652     else
4653       track->artist = NULL;
4654     break;
4655   case PTP_OPC_Composer:
4656     if (prop->propval.str != NULL)
4657       track->composer = strdup(prop->propval.str);
4658     else
4659       track->composer = NULL;
4660     break;
4661   case PTP_OPC_Duration:
4662     track->duration = prop->propval.u32;
4663     break;
4664   case PTP_OPC_Track:
4665     track->tracknumber = prop->propval.u16;
4666     break;
4667   case PTP_OPC_Genre:
4668     if (prop->propval.str != NULL)
4669       track->genre = strdup(prop->propval.str);
4670     else
4671       track->genre = NULL;
4672     break;
4673   case PTP_OPC_AlbumName:
4674     if (prop->propval.str != NULL)
4675       track->album = strdup(prop->propval.str);
4676     else
4677       track->album = NULL;
4678     break;
4679   case PTP_OPC_OriginalReleaseDate:
4680     if (prop->propval.str != NULL)
4681       track->date = strdup(prop->propval.str);
4682     else
4683       track->date = NULL;
4684     break;
4685     // These are, well not so important.
4686   case PTP_OPC_SampleRate:
4687     track->samplerate = prop->propval.u32;
4688     break;
4689   case PTP_OPC_NumberOfChannels:
4690     track->nochannels = prop->propval.u16;
4691     break;
4692   case PTP_OPC_AudioWAVECodec:
4693     track->wavecodec = prop->propval.u32;
4694     break;
4695   case PTP_OPC_AudioBitRate:
4696     track->bitrate = prop->propval.u32;
4697     break;
4698   case PTP_OPC_BitRateType:
4699     track->bitratetype = prop->propval.u16;
4700     break;
4701   case PTP_OPC_Rating:
4702     track->rating = prop->propval.u16;
4703     break;
4704   case PTP_OPC_UseCount:
4705     track->usecount = prop->propval.u32;
4706     break;
4707   case PTP_OPC_ObjectSize:
4708     if (device->object_bitsize == 64) {
4709       track->filesize = prop->propval.u64;
4710     } else {
4711       track->filesize = prop->propval.u32;
4712     }
4713     break;
4714   default:
4715     break;
4716   }
4717 }
4718
4719 /**
4720  * This function retrieves the track metadata for a track
4721  * given by a unique ID.
4722  * @param device a pointer to the device to get the track metadata off.
4723  * @param trackid the unique ID of the track.
4724  * @param objectformat the object format of this track, so we know what it supports.
4725  * @param track a metadata set to fill in.
4726  */
4727 static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
4728                                LIBMTP_track_t *track)
4729 {
4730   uint16_t ret;
4731   PTPParams *params = (PTPParams *) device->params;
4732   uint32_t i;
4733   MTPProperties *prop;
4734   PTPObject *ob;
4735
4736   /*
4737    * If we have a cached, large set of metadata, then use it!
4738    */
4739   ret = ptp_object_want(params, track->item_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
4740   if (ob->mtpprops) {
4741     prop = ob->mtpprops;
4742     for (i=0;i<ob->nrofmtpprops;i++,prop++)
4743       pick_property_to_track_metadata(device, prop, track);
4744   } else {
4745     uint16_t *props = NULL;
4746     uint32_t propcnt = 0;
4747
4748     // First see which properties can be retrieved for this object format
4749     ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(track->filetype), &propcnt, &props);
4750     if (ret != PTP_RC_OK) {
4751       add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
4752       // Just bail out for now, nothing is ever set.
4753       return;
4754     } else {
4755       for (i=0;i<propcnt;i++) {
4756         switch (props[i]) {
4757         case PTP_OPC_Name:
4758           track->title = get_string_from_object(device, track->item_id, PTP_OPC_Name);
4759           break;
4760         case PTP_OPC_Artist:
4761           track->artist = get_string_from_object(device, track->item_id, PTP_OPC_Artist);
4762           break;
4763         case PTP_OPC_Composer:
4764           track->composer = get_string_from_object(device, track->item_id, PTP_OPC_Composer);
4765           break;
4766         case PTP_OPC_Duration:
4767           track->duration = get_u32_from_object(device, track->item_id, PTP_OPC_Duration, 0);
4768           break;
4769         case PTP_OPC_Track:
4770           track->tracknumber = get_u16_from_object(device, track->item_id, PTP_OPC_Track, 0);
4771           break;
4772         case PTP_OPC_Genre:
4773           track->genre = get_string_from_object(device, track->item_id, PTP_OPC_Genre);
4774           break;
4775         case PTP_OPC_AlbumName:
4776           track->album = get_string_from_object(device, track->item_id, PTP_OPC_AlbumName);
4777           break;
4778         case PTP_OPC_OriginalReleaseDate:
4779           track->date = get_string_from_object(device, track->item_id, PTP_OPC_OriginalReleaseDate);
4780           break;
4781           // These are, well not so important.
4782         case PTP_OPC_SampleRate:
4783           track->samplerate = get_u32_from_object(device, track->item_id, PTP_OPC_SampleRate, 0);
4784           break;
4785         case PTP_OPC_NumberOfChannels:
4786           track->nochannels = get_u16_from_object(device, track->item_id, PTP_OPC_NumberOfChannels, 0);
4787           break;
4788         case PTP_OPC_AudioWAVECodec:
4789           track->wavecodec = get_u32_from_object(device, track->item_id, PTP_OPC_AudioWAVECodec, 0);
4790           break;
4791         case PTP_OPC_AudioBitRate:
4792           track->bitrate = get_u32_from_object(device, track->item_id, PTP_OPC_AudioBitRate, 0);
4793           break;
4794         case PTP_OPC_BitRateType:
4795           track->bitratetype = get_u16_from_object(device, track->item_id, PTP_OPC_BitRateType, 0);
4796           break;
4797         case PTP_OPC_Rating:
4798           track->rating = get_u16_from_object(device, track->item_id, PTP_OPC_Rating, 0);
4799           break;
4800         case PTP_OPC_UseCount:
4801           track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0);
4802           break;
4803         case PTP_OPC_ObjectSize:
4804           if (device->object_bitsize == 64) {
4805             track->filesize = get_u64_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
4806           } else {
4807             track->filesize = (uint64_t) get_u32_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
4808           }
4809           break;
4810         }
4811       }
4812       free(props);
4813     }
4814   }
4815 }
4816
4817 /**
4818  * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
4819  * NOT TO USE IT.
4820  * @see LIBMTP_Get_Tracklisting_With_Callback()
4821  */
4822 LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t *device)
4823 {
4824   LIBMTP_INFO("WARNING: LIBMTP_Get_Tracklisting() is deprecated.\n");
4825   LIBMTP_INFO("WARNING: please update your code to use LIBMTP_Get_Tracklisting_With_Callback()\n");
4826   return LIBMTP_Get_Tracklisting_With_Callback(device, NULL, NULL);
4827 }
4828
4829 /**
4830  * This returns a long list of all tracks available on the current MTP device.
4831  * Tracks include multimedia objects, both music tracks and video tracks.
4832  * Typical usage:
4833  *
4834  * <pre>
4835  * LIBMTP_track_t *tracklist;
4836  *
4837  * tracklist = LIBMTP_Get_Tracklisting_With_Callback(device, callback, data);
4838  * while (tracklist != NULL) {
4839  *   LIBMTP_track_t *tmp;
4840  *
4841  *   // Do something on each element in the list here...
4842  *   tmp = tracklist;
4843  *   tracklist = tracklist->next;
4844  *   LIBMTP_destroy_track_t(tmp);
4845  * }
4846  * </pre>
4847  *
4848  * If you want to group your track listing by storage (per storage unit) or
4849  * arrange tracks into folders, you must dereference the <code>storage_id</code>
4850  * and/or <code>parent_id</code> field of the returned <code>LIBMTP_track_t</code>
4851  * struct. To arrange by folders or files you typically have to create the proper
4852  * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
4853  * <code>LIBMTP_Get_Folder_List()</code> first.
4854  *
4855  * @param device a pointer to the device to get the track listing for.
4856  * @param callback a function to be called during the tracklisting retrieveal
4857  *        for displaying progress bars etc, or NULL if you don't want
4858  *        any callbacks.
4859  * @param data a user-defined pointer that is passed along to
4860  *        the <code>progress</code> function in order to
4861  *        pass along some user defined data to the progress
4862  *        updates. If not used, set this to NULL.
4863  * @return a list of tracks that can be followed using the <code>next</code>
4864  *        field of the <code>LIBMTP_track_t</code> data structure.
4865  *        Each of the metadata tags must be freed after use, and may
4866  *        contain only partial metadata information, i.e. one or several
4867  *        fields may be NULL or 0.
4868  * @see LIBMTP_Get_Trackmetadata()
4869  */
4870 LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t *device,
4871                                                       LIBMTP_progressfunc_t const callback,
4872                                                       void const * const data)
4873 {
4874         return LIBMTP_Get_Tracklisting_With_Callback_For_Storage(device, 0, callback, data);
4875 }
4876
4877
4878 /**
4879  * This returns a long list of all tracks available on the current MTP device.
4880  * Tracks include multimedia objects, both music tracks and video tracks.
4881  * Typical usage:
4882  *
4883  * <pre>
4884  * LIBMTP_track_t *tracklist;
4885  *
4886  * tracklist = LIBMTP_Get_Tracklisting_With_Callback_For_Storage(device, storage_id, callback, data);
4887  * while (tracklist != NULL) {
4888  *   LIBMTP_track_t *tmp;
4889  *
4890  *   // Do something on each element in the list here...
4891  *   tmp = tracklist;
4892  *   tracklist = tracklist->next;
4893  *   LIBMTP_destroy_track_t(tmp);
4894  * }
4895  * </pre>
4896  *
4897  * If you want to group your track listing by storage (per storage unit) or
4898  * arrange tracks into folders, you must dereference the <code>storage_id</code>
4899  * and/or <code>parent_id</code> field of the returned <code>LIBMTP_track_t</code>
4900  * struct. To arrange by folders or files you typically have to create the proper
4901  * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
4902  * <code>LIBMTP_Get_Folder_List()</code> first.
4903  *
4904  * @param device a pointer to the device to get the track listing for.
4905  * @param storage_id ID of device storage (if null, no filter)
4906  * @param callback a function to be called during the tracklisting retrieveal
4907  *        for displaying progress bars etc, or NULL if you don't want
4908  *        any callbacks.
4909  * @param data a user-defined pointer that is passed along to
4910  *        the <code>progress</code> function in order to
4911  *        pass along some user defined data to the progress
4912  *        updates. If not used, set this to NULL.
4913  * @return a list of tracks that can be followed using the <code>next</code>
4914  *        field of the <code>LIBMTP_track_t</code> data structure.
4915  *        Each of the metadata tags must be freed after use, and may
4916  *        contain only partial metadata information, i.e. one or several
4917  *        fields may be NULL or 0.
4918  * @see LIBMTP_Get_Trackmetadata()
4919  */
4920 LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback_For_Storage(LIBMTP_mtpdevice_t *device, uint32_t const storage_id,
4921                                                       LIBMTP_progressfunc_t const callback,
4922                                                       void const * const data)
4923 {
4924   uint32_t i = 0;
4925   LIBMTP_track_t *retracks = NULL;
4926   LIBMTP_track_t *curtrack = NULL;
4927   PTPParams *params = (PTPParams *) device->params;
4928   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
4929
4930   // Get all the handles if we haven't already done that
4931   if (params->nrofobjects == 0) {
4932     flush_handles(device);
4933   }
4934
4935   for (i = 0; i < params->nrofobjects; i++) {
4936     LIBMTP_track_t *track;
4937     PTPObject *ob;
4938     LIBMTP_filetype_t mtptype;
4939
4940     if (callback != NULL)
4941       callback(i, params->nrofobjects, data);
4942
4943     ob = &params->objects[i];
4944     mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
4945
4946     // Ignore stuff we don't know how to handle...
4947     // TODO: get this list as an intersection of the sets
4948     // supported by the device and the from the device and
4949     // all known track files?
4950     if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
4951         // This row lets through undefined files for examination since they may be forgotten OGG files.
4952         (ob->oi.ObjectFormat != PTP_OFC_Undefined ||
4953          (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
4954           !FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
4955           !FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
4956         ) {
4957       //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
4958       continue;
4959     }
4960
4961         // Ignore stuff that isn't into the storage device
4962         if ((storage_id != 0) && (ob->oi.StorageID != storage_id ))
4963                 continue;
4964
4965     // Allocate a new track type
4966     track = LIBMTP_new_track_t();
4967
4968     // This is some sort of unique ID so we can keep track of the track.
4969     track->item_id = ob->oid;
4970     track->parent_id = ob->oi.ParentObject;
4971     track->storage_id = ob->oi.StorageID;
4972     track->modificationdate = ob->oi.ModificationDate;
4973
4974     track->filetype = mtptype;
4975
4976     // Original file-specific properties
4977     track->filesize = ob->oi.ObjectCompressedSize;
4978     if (ob->oi.Filename != NULL) {
4979       track->filename = strdup(ob->oi.Filename);
4980     }
4981
4982     get_track_metadata(device, ob->oi.ObjectFormat, track);
4983
4984     /*
4985      * A special quirk for iriver devices that doesn't quite
4986      * remember that some files marked as "unknown" type are
4987      * actually OGG or FLAC files. We look at the filename extension
4988      * and see if it happens that this was atleast named "ogg" or "flac"
4989      * and fall back on this heuristic approach in that case,
4990      * for these bugged devices only.
4991      */
4992     if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
4993         track->filename != NULL) {
4994       if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
4995            FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
4996           has_ogg_extension(track->filename))
4997         track->filetype = LIBMTP_FILETYPE_OGG;
4998       else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
4999                has_flac_extension(track->filename))
5000         track->filetype = LIBMTP_FILETYPE_FLAC;
5001       else {
5002         // This was not an OGG/FLAC file so discard it and continue
5003         LIBMTP_destroy_track_t(track);
5004         continue;
5005       }
5006     }
5007
5008     // Add track to a list that will be returned afterwards.
5009     if (retracks == NULL) {
5010       retracks = track;
5011       curtrack = track;
5012     } else {
5013       curtrack->next = track;
5014       curtrack = track;
5015     }
5016
5017     // Call listing callback
5018     // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
5019
5020   } // Handle counting loop
5021   return retracks;
5022 }
5023
5024 /**
5025  * This function retrieves the metadata for a single track off
5026  * the device.
5027  *
5028  * Do not call this function repeatedly! The track handles are linearly
5029  * searched O(n) and the call may involve (slow) USB traffic, so use
5030  * <code>LIBMTP_Get_Tracklisting()</code> and cache the tracks, preferably
5031  * as an efficient data structure such as a hash list.
5032  *
5033  * @param device a pointer to the device to get the track metadata from.
5034  * @param trackid the object ID of the track that you want the metadata for.
5035  * @return a track metadata entry on success or NULL on failure.
5036  * @see LIBMTP_Get_Tracklisting()
5037  */
5038 LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t *device, uint32_t const trackid)
5039 {
5040   PTPParams *params = (PTPParams *) device->params;
5041   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5042   PTPObject *ob;
5043   LIBMTP_track_t *track;
5044   LIBMTP_filetype_t mtptype;
5045   uint16_t ret;
5046
5047   // Get all the handles if we haven't already done that
5048   if (params->nrofobjects == 0)
5049     flush_handles(device);
5050
5051   ret = ptp_object_want (params, trackid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5052   if (ret != PTP_RC_OK)
5053     return NULL;
5054
5055   mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
5056
5057   // Ignore stuff we don't know how to handle...
5058   if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
5059       /*
5060        * This row lets through undefined files for examination
5061        * since they may be forgotten OGG or FLAC files.
5062        */
5063       (ob->oi.ObjectFormat != PTP_OFC_Undefined ||
5064        (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
5065         !FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
5066         !FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
5067       ) {
5068     //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
5069     return NULL;
5070   }
5071
5072   // Allocate a new track type
5073   track = LIBMTP_new_track_t();
5074
5075   // This is some sort of unique ID so we can keep track of the track.
5076   track->item_id = ob->oid;
5077   track->parent_id = ob->oi.ParentObject;
5078   track->storage_id = ob->oi.StorageID;
5079   track->modificationdate = ob->oi.ModificationDate;
5080
5081   track->filetype = mtptype;
5082
5083   // Original file-specific properties
5084   track->filesize = ob->oi.ObjectCompressedSize;
5085   if (ob->oi.Filename != NULL) {
5086     track->filename = strdup(ob->oi.Filename);
5087   }
5088
5089   /*
5090    * A special quirk for devices that doesn't quite
5091    * remember that some files marked as "unknown" type are
5092    * actually OGG or FLAC files. We look at the filename extension
5093    * and see if it happens that this was atleast named "ogg"
5094    * and fall back on this heuristic approach in that case,
5095    * for these bugged devices only.
5096    */
5097   if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
5098       track->filename != NULL) {
5099     if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
5100          FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
5101         has_ogg_extension(track->filename))
5102       track->filetype = LIBMTP_FILETYPE_OGG;
5103     else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
5104              has_flac_extension(track->filename))
5105       track->filetype = LIBMTP_FILETYPE_FLAC;
5106     else {
5107       // This was not an OGG/FLAC file so discard it
5108       LIBMTP_destroy_track_t(track);
5109       return NULL;
5110     }
5111   }
5112   get_track_metadata(device, ob->oi.ObjectFormat, track);
5113   return track;
5114 }
5115
5116 /**
5117  * This is a manual conversion from MTPDataGetFunc to PTPDataGetFunc
5118  * to isolate the internal type.
5119  */
5120 static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen)
5121 {
5122   MTPDataHandler *handler = (MTPDataHandler *)priv;
5123   uint16_t ret;
5124   uint32_t local_gotlen = 0;
5125   ret = handler->getfunc(params, handler->priv, wantlen, data, &local_gotlen);
5126   *gotlen = local_gotlen;
5127   switch (ret)
5128   {
5129     case LIBMTP_HANDLER_RETURN_OK:
5130       return PTP_RC_OK;
5131     case LIBMTP_HANDLER_RETURN_ERROR:
5132       return PTP_ERROR_IO;
5133     case LIBMTP_HANDLER_RETURN_CANCEL:
5134       return PTP_ERROR_CANCEL;
5135     default:
5136       return PTP_ERROR_IO;
5137   }
5138 }
5139
5140 /**
5141  * This is a manual conversion from MTPDataPutFunc to PTPDataPutFunc
5142  * to isolate the internal type.
5143  */
5144 static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen)
5145 {
5146   MTPDataHandler *handler = (MTPDataHandler *)priv;
5147   uint16_t ret;
5148   uint32_t local_putlen = 0;
5149   ret = handler->putfunc(params, handler->priv, sendlen, data, &local_putlen);
5150   *putlen = local_putlen;
5151   switch (ret)
5152   {
5153     case LIBMTP_HANDLER_RETURN_OK:
5154       return PTP_RC_OK;
5155     case LIBMTP_HANDLER_RETURN_ERROR:
5156       return PTP_ERROR_IO;
5157     case LIBMTP_HANDLER_RETURN_CANCEL:
5158       return PTP_ERROR_CANCEL;
5159     default:
5160       return PTP_ERROR_IO;
5161   }
5162 }
5163
5164 /**
5165  * This gets a file off the device to a local file identified
5166  * by a filename.
5167  * @param device a pointer to the device to get the track from.
5168  * @param id the file ID of the file to retrieve.
5169  * @param path a filename to use for the retrieved file.
5170  * @param callback a progress indicator function or NULL to ignore.
5171  * @param data a user-defined pointer that is passed along to
5172  *             the <code>progress</code> function in order to
5173  *             pass along some user defined data to the progress
5174  *             updates. If not used, set this to NULL.
5175  * @return 0 if the transfer was successful, any other value means
5176  *           failure.
5177  * @see LIBMTP_Get_File_To_File_Descriptor()
5178  */
5179 int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
5180                          char const * const path, LIBMTP_progressfunc_t const callback,
5181                          void const * const data)
5182 {
5183   int fd = -1;
5184   int ret;
5185
5186   // Sanity check
5187   if (path == NULL) {
5188     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Bad arguments, path was NULL.");
5189     return -1;
5190   }
5191
5192   // Open file
5193 #ifdef __WIN32__
5194 #ifdef USE_WINDOWS_IO_H
5195   if ( (fd = _open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,_S_IREAD)) == -1 ) {
5196 #else
5197   if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IRWXU)) == -1 ) {
5198 #endif
5199 #else
5200   if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP)) == -1) {
5201 #endif
5202     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Could not create file.");
5203     return -1;
5204   }
5205
5206   ret = LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
5207
5208   // Close file
5209   close(fd);
5210
5211   // Delete partial file.
5212   if (ret == -1) {
5213     unlink(path);
5214   }
5215
5216   return ret;
5217 }
5218
5219 /**
5220  * This gets a file off the device to a file identified
5221  * by a file descriptor.
5222  *
5223  * This function can potentially be used for streaming
5224  * files off the device for playback or broadcast for example,
5225  * by downloading the file into a stream sink e.g. a socket.
5226  *
5227  * @param device a pointer to the device to get the file from.
5228  * @param id the file ID of the file to retrieve.
5229  * @param fd a local file descriptor to write the file to.
5230  * @param callback a progress indicator function or NULL to ignore.
5231  * @param data a user-defined pointer that is passed along to
5232  *             the <code>progress</code> function in order to
5233  *             pass along some user defined data to the progress
5234  *             updates. If not used, set this to NULL.
5235  * @return 0 if the transfer was successful, any other value means
5236  *           failure.
5237  * @see LIBMTP_Get_File_To_File()
5238  */
5239 int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
5240                                         uint32_t const id,
5241                                         int const fd,
5242                                         LIBMTP_progressfunc_t const callback,
5243                                         void const * const data)
5244 {
5245   uint16_t ret;
5246   PTPParams *params = (PTPParams *) device->params;
5247   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5248   PTPObject *ob;
5249
5250   int oldtimeout;
5251
5252   get_usb_device_timeout(ptp_usb, &oldtimeout);
5253   set_usb_device_timeout(ptp_usb, 5000);
5254
5255   LIBMTP_INFO("priv set timeout value : %d, now, set timeout value to 5 sec", oldtimeout);
5256
5257   ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5258   if (ret != PTP_RC_OK) {
5259     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
5260     set_usb_device_timeout(ptp_usb, oldtimeout);
5261     return -1;
5262   }
5263   if (ob->oi.ObjectFormat == PTP_OFC_Association) {
5264     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
5265     set_usb_device_timeout(ptp_usb, oldtimeout);
5266     return -1;
5267   }
5268
5269   // Callbacks
5270   ptp_usb->callback_active = 1;
5271   ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
5272     PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
5273   ptp_usb->current_transfer_complete = 0;
5274   ptp_usb->current_transfer_callback = callback;
5275   ptp_usb->current_transfer_callback_data = data;
5276
5277   ret = ptp_getobject_tofd(params, id, fd);
5278
5279   ptp_usb->callback_active = 0;
5280   ptp_usb->current_transfer_callback = NULL;
5281   ptp_usb->current_transfer_callback_data = NULL;
5282
5283   set_usb_device_timeout(ptp_usb, oldtimeout);
5284
5285   if (ret == PTP_ERROR_CANCEL) {
5286     add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
5287     return -1;
5288   }
5289   if (ret != PTP_RC_OK) {
5290     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
5291     return -1;
5292   }
5293
5294   return 0;
5295 }
5296
5297 /**
5298  * This gets a file off the device and calls put_func
5299  * with chunks of data
5300  *
5301  * @param device a pointer to the device to get the file from.
5302  * @param id the file ID of the file to retrieve.
5303  * @param put_func the function to call when we have data.
5304  * @param priv the user-defined pointer that is passed to
5305  *             <code>put_func</code>.
5306  * @param callback a progress indicator function or NULL to ignore.
5307  * @param data a user-defined pointer that is passed along to
5308  *             the <code>progress</code> function in order to
5309  *             pass along some user defined data to the progress
5310  *             updates. If not used, set this to NULL.
5311  * @return 0 if the transfer was successful, any other value means
5312  *           failure.
5313  */
5314 int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *device,
5315                                         uint32_t const id,
5316                                         MTPDataPutFunc put_func,
5317           void * priv,
5318                                         LIBMTP_progressfunc_t const callback,
5319                                         void const * const data)
5320 {
5321   PTPObject *ob;
5322   uint16_t ret;
5323   PTPParams *params = (PTPParams *) device->params;
5324   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5325
5326   ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5327   if (ret != PTP_RC_OK) {
5328     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
5329     return -1;
5330   }
5331   if (ob->oi.ObjectFormat == PTP_OFC_Association) {
5332     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
5333     return -1;
5334   }
5335
5336   // Callbacks
5337   ptp_usb->callback_active = 1;
5338   ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
5339     PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
5340   ptp_usb->current_transfer_complete = 0;
5341   ptp_usb->current_transfer_callback = callback;
5342   ptp_usb->current_transfer_callback_data = data;
5343
5344   MTPDataHandler mtp_handler;
5345   mtp_handler.getfunc = NULL;
5346   mtp_handler.putfunc = put_func;
5347   mtp_handler.priv = priv;
5348
5349   PTPDataHandler handler;
5350   handler.getfunc = NULL;
5351   handler.putfunc = put_func_wrapper;
5352   handler.priv = &mtp_handler;
5353
5354   ret = ptp_getobject_to_handler(params, id, &handler);
5355
5356   ptp_usb->callback_active = 0;
5357   ptp_usb->current_transfer_callback = NULL;
5358   ptp_usb->current_transfer_callback_data = NULL;
5359
5360   if (ret == PTP_ERROR_CANCEL) {
5361     add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
5362     return -1;
5363   }
5364   if (ret != PTP_RC_OK) {
5365     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
5366     return -1;
5367   }
5368
5369   return 0;
5370 }
5371
5372
5373 /**
5374  * This gets a track off the device to a file identified
5375  * by a filename. This is actually just a wrapper for the
5376  * \c LIBMTP_Get_Track_To_File() function.
5377  * @param device a pointer to the device to get the track from.
5378  * @param id the track ID of the track to retrieve.
5379  * @param path a filename to use for the retrieved track.
5380  * @param callback a progress indicator function or NULL to ignore.
5381  * @param data a user-defined pointer that is passed along to
5382  *             the <code>progress</code> function in order to
5383  *             pass along some user defined data to the progress
5384  *             updates. If not used, set this to NULL.
5385  * @return 0 if the transfer was successful, any other value means
5386  *           failure.
5387  * @see LIBMTP_Get_Track_To_File_Descriptor()
5388  */
5389 int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
5390                          char const * const path, LIBMTP_progressfunc_t const callback,
5391                          void const * const data)
5392 {
5393   // This is just a wrapper
5394   return LIBMTP_Get_File_To_File(device, id, path, callback, data);
5395 }
5396
5397 /**
5398  * This gets a track off the device to a file identified
5399  * by a file descriptor. This is actually just a wrapper for
5400  * the \c LIBMTP_Get_File_To_File_Descriptor() function.
5401  * @param device a pointer to the device to get the track from.
5402  * @param id the track ID of the track to retrieve.
5403  * @param fd a file descriptor to write the track to.
5404  * @param callback a progress indicator function or NULL to ignore.
5405  * @param data a user-defined pointer that is passed along to
5406  *             the <code>progress</code> function in order to
5407  *             pass along some user defined data to the progress
5408  *             updates. If not used, set this to NULL.
5409  * @return 0 if the transfer was successful, any other value means
5410  *           failure.
5411  * @see LIBMTP_Get_Track_To_File()
5412  */
5413 int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
5414                                         uint32_t const id,
5415                                         int const fd,
5416                                         LIBMTP_progressfunc_t const callback,
5417                                         void const * const data)
5418 {
5419   // This is just a wrapper
5420   return LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
5421 }
5422
5423 /**
5424  * This gets a track off the device to a handler function.
5425  * This is actually just a wrapper for
5426  * the \c LIBMTP_Get_File_To_Handler() function.
5427  * @param device a pointer to the device to get the track from.
5428  * @param id the track ID of the track to retrieve.
5429  * @param put_func the function to call when we have data.
5430  * @param priv the user-defined pointer that is passed to
5431  *             <code>put_func</code>.
5432  * @param callback a progress indicator function or NULL to ignore.
5433  * @param data a user-defined pointer that is passed along to
5434  *             the <code>progress</code> function in order to
5435  *             pass along some user defined data to the progress
5436  *             updates. If not used, set this to NULL.
5437  * @return 0 if the transfer was successful, any other value means
5438  *           failure.
5439  */
5440 int LIBMTP_Get_Track_To_Handler(LIBMTP_mtpdevice_t *device,
5441                                         uint32_t const id,
5442                                         MTPDataPutFunc put_func,
5443           void * priv,
5444                                         LIBMTP_progressfunc_t const callback,
5445                                         void const * const data)
5446 {
5447   // This is just a wrapper
5448   return LIBMTP_Get_File_To_Handler(device, id, put_func, priv, callback, data);
5449 }
5450
5451 /**
5452  * This function sends a track from a local file to an
5453  * MTP device. A filename and a set of metadata must be
5454  * given as input.
5455  * @param device a pointer to the device to send the track to.
5456  * @param path the filename of a local file which will be sent.
5457  * @param metadata a track metadata set to be written along with the file.
5458  *        After this call the field <code>metadata-&gt;item_id</code>
5459  *        will contain the new track ID. Other fields such
5460  *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
5461  *        or <code>metadata-&gt;storage_id</code> may also change during this
5462  *        operation due to device restrictions, so do not rely on the
5463  *        contents of this struct to be preserved in any way.
5464  *        <ul>
5465  *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
5466  *        (e.g. folder) to store this track in. Since some
5467  *        devices are a bit picky about where files
5468  *        are placed, a default folder will be chosen if libmtp
5469  *        has detected one for the current filetype and this
5470  *        parameter is set to 0. If this is 0 and no default folder
5471  *        can be found, the file will be stored in the root folder.
5472  *        <li><code>metadata-&gt;storage_id</code> should be set to the
5473  *        desired storage (e.g. memory card or whatever your device
5474  *        presents) to store this track in. Setting this to 0 will store
5475  *        the track on the primary storage.
5476  *        </ul>
5477  * @param callback a progress indicator function or NULL to ignore.
5478  * @param data a user-defined pointer that is passed along to
5479  *             the <code>progress</code> function in order to
5480  *             pass along some user defined data to the progress
5481  *             updates. If not used, set this to NULL.
5482  * @return 0 if the transfer was successful, any other value means
5483  *           failure.
5484  * @see LIBMTP_Send_Track_From_File_Descriptor()
5485  * @see LIBMTP_Send_File_From_File()
5486  * @see LIBMTP_Delete_Object()
5487  */
5488 int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device,
5489                          char const * const path, LIBMTP_track_t * const metadata,
5490                          LIBMTP_progressfunc_t const callback,
5491                          void const * const data)
5492 {
5493   int fd;
5494   int ret;
5495
5496   // Sanity check
5497   if (path == NULL) {
5498     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File(): Bad arguments, path was NULL.");
5499     return -1;
5500   }
5501
5502   // Open file
5503 #ifdef __WIN32__
5504 #ifdef USE_WINDOWS_IO_H
5505   if ( (fd = _open(path, O_RDONLY|O_BINARY)) == -1 ) {
5506 #else
5507   if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
5508 #endif
5509 #else
5510   if ( (fd = open(path, O_RDONLY)) == -1) {
5511 #endif
5512     LIBMTP_ERROR("LIBMTP_Send_Track_From_File(): Could not open source file \"%s\"\n", path);
5513     return -1;
5514   }
5515
5516   ret = LIBMTP_Send_Track_From_File_Descriptor(device, fd, metadata, callback, data);
5517
5518   // Close file.
5519 #ifdef USE_WINDOWS_IO_H
5520   _close(fd);
5521 #else
5522   close(fd);
5523 #endif
5524
5525   return ret;
5526 }
5527
5528
5529
5530 /**
5531  * This helper function checks if a filename already exists on the device
5532  * @param PTPParams*
5533  * @param string representing the filename
5534  * @return 0 if the filename doesn't exist, -1 if it does
5535  */
5536 static int check_filename_exists(PTPParams* params, char const * const filename)
5537 {
5538   int i;
5539
5540   for (i = 0; i < params->nrofobjects; i++) {
5541     char *fname = params->objects[i].oi.Filename;
5542     if ((fname != NULL) && (strcmp(filename, fname) == 0))
5543     {
5544       return -1;
5545     }
5546   }
5547
5548   return 0;
5549 }
5550
5551 /**
5552  * This helper function returns a unique filename, with a random string before the extension
5553  * @param string representing the original filename
5554  * @return a string representing the unique filename
5555  */
5556 static char *generate_unique_filename(PTPParams* params, char const * const filename)
5557 {
5558   int suffix;
5559   char * extension_position;
5560
5561   if (check_filename_exists(params, filename))
5562   {
5563     extension_position = strrchr(filename,'.');
5564 #ifdef TIZEN_EXT
5565     if (extension_position == NULL)
5566                 return NULL;
5567 #endif
5568     char basename[extension_position - filename + 1];
5569     strncpy(basename, filename, extension_position - filename);
5570     basename[extension_position - filename] = '\0';
5571
5572     suffix = 1;
5573     char newname[ strlen(basename) + 6 + strlen(extension_position)];
5574     snprintf(newname, strlen(basename) + 5 + strlen(extension_position), "%s_%d%s", basename, suffix, extension_position);
5575     while ((check_filename_exists(params, newname)) && (suffix < 1000000)) {
5576       suffix++;
5577       snprintf(newname, strlen(basename) + 5 + strlen(extension_position), "%s_%d%s", basename, suffix, extension_position);
5578     }
5579   return strdup(newname);
5580   }
5581   else
5582   {
5583     return strdup(filename);
5584   }
5585 }
5586
5587 /**
5588  * This function sends a track from a file descriptor to an
5589  * MTP device. A filename and a set of metadata must be
5590  * given as input.
5591  * @param device a pointer to the device to send the track to.
5592  * @param fd the filedescriptor for a local file which will be sent.
5593  * @param metadata a track metadata set to be written along with the file.
5594  *        After this call the field <code>metadata-&gt;item_id</code>
5595  *        will contain the new track ID. Other fields such
5596  *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
5597  *        or <code>metadata-&gt;storage_id</code> may also change during this
5598  *        operation due to device restrictions, so do not rely on the
5599  *        contents of this struct to be preserved in any way.
5600  *        <ul>
5601  *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
5602  *        (e.g. folder) to store this track in. Since some
5603  *        devices are a bit picky about where files
5604  *        are placed, a default folder will be chosen if libmtp
5605  *        has detected one for the current filetype and this
5606  *        parameter is set to 0. If this is 0 and no default folder
5607  *        can be found, the file will be stored in the root folder.
5608  *        <li><code>metadata-&gt;storage_id</code> should be set to the
5609  *        desired storage (e.g. memory card or whatever your device
5610  *        presents) to store this track in. Setting this to 0 will store
5611  *        the track on the primary storage.
5612  *        </ul>
5613  * @param callback a progress indicator function or NULL to ignore.
5614  * @param data a user-defined pointer that is passed along to
5615  *             the <code>progress</code> function in order to
5616  *             pass along some user defined data to the progress
5617  *             updates. If not used, set this to NULL.
5618  * @return 0 if the transfer was successful, any other value means
5619  *           failure.
5620  * @see LIBMTP_Send_Track_From_File()
5621  * @see LIBMTP_Delete_Object()
5622  */
5623 int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
5624                          int const fd, LIBMTP_track_t * const metadata,
5625                          LIBMTP_progressfunc_t const callback,
5626                          void const * const data)
5627 {
5628   int subcall_ret;
5629   LIBMTP_file_t filedata;
5630   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5631   PTPParams *params = (PTPParams *) device->params;
5632
5633   // Sanity check, is this really a track?
5634   if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
5635     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5636                             "LIBMTP_Send_Track_From_File_Descriptor(): "
5637                             "I don't think this is actually a track, strange filetype...");
5638   }
5639
5640   // Wrap around the file transfer function
5641   filedata.item_id = metadata->item_id;
5642   filedata.parent_id = metadata->parent_id;
5643   filedata.storage_id = metadata->storage_id;
5644   if FLAG_UNIQUE_FILENAMES(ptp_usb) {
5645     filedata.filename = generate_unique_filename(params, metadata->filename);
5646   }
5647   else {
5648     filedata.filename = metadata->filename;
5649   }
5650   filedata.filesize = metadata->filesize;
5651   filedata.filetype = metadata->filetype;
5652   filedata.next = NULL;
5653
5654   subcall_ret = LIBMTP_Send_File_From_File_Descriptor(device,
5655                                                       fd,
5656                                                       &filedata,
5657                                                       callback,
5658                                                       data);
5659
5660   if (subcall_ret != 0) {
5661     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5662                             "LIBMTP_Send_Track_From_File_Descriptor(): "
5663                             "subcall to LIBMTP_Send_File_From_File_Descriptor failed.");
5664     // We used to delete the file here, but don't... It might be OK after all.
5665     // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5666     return -1;
5667   }
5668
5669   // Pick up new item (and parent, storage) ID
5670   metadata->item_id = filedata.item_id;
5671   metadata->parent_id = filedata.parent_id;
5672   metadata->storage_id = filedata.storage_id;
5673
5674   // Set track metadata for the new fine track
5675   subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
5676   if (subcall_ret != 0) {
5677     // Subcall will add error to errorstack
5678     // We used to delete the file here, but don't... It might be OK after all.
5679     // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5680     return -1;
5681   }
5682
5683   // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
5684   // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
5685
5686   return 0;
5687 }
5688
5689 /**
5690  * This function sends a track from a handler function to an
5691  * MTP device. A filename and a set of metadata must be
5692  * given as input.
5693  * @param device a pointer to the device to send the track to.
5694  * @param get_func the function to call when we have data.
5695  * @param priv the user-defined pointer that is passed to
5696  *             <code>get_func</code>.
5697  * @param metadata a track metadata set to be written along with the file.
5698  *        After this call the field <code>metadata-&gt;item_id</code>
5699  *        will contain the new track ID. Other fields such
5700  *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
5701  *        or <code>metadata-&gt;storage_id</code> may also change during this
5702  *        operation due to device restrictions, so do not rely on the
5703  *        contents of this struct to be preserved in any way.
5704  *        <ul>
5705  *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
5706  *        (e.g. folder) to store this track in. Since some
5707  *        devices are a bit picky about where files
5708  *        are placed, a default folder will be chosen if libmtp
5709  *        has detected one for the current filetype and this
5710  *        parameter is set to 0. If this is 0 and no default folder
5711  *        can be found, the file will be stored in the root folder.
5712  *        <li><code>metadata-&gt;storage_id</code> should be set to the
5713  *        desired storage (e.g. memory card or whatever your device
5714  *        presents) to store this track in. Setting this to 0 will store
5715  *        the track on the primary storage.
5716  *        </ul>
5717  * @param callback a progress indicator function or NULL to ignore.
5718  * @param data a user-defined pointer that is passed along to
5719  *             the <code>progress</code> function in order to
5720  *             pass along some user defined data to the progress
5721  *             updates. If not used, set this to NULL.
5722  * @return 0 if the transfer was successful, any other value means
5723  *           failure.
5724  * @see LIBMTP_Send_Track_From_File()
5725  * @see LIBMTP_Delete_Object()
5726  */
5727 int LIBMTP_Send_Track_From_Handler(LIBMTP_mtpdevice_t *device,
5728                          MTPDataGetFunc get_func, void * priv, LIBMTP_track_t * const metadata,
5729                          LIBMTP_progressfunc_t const callback,
5730                          void const * const data)
5731 {
5732   int subcall_ret;
5733   LIBMTP_file_t filedata;
5734   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5735   PTPParams *params = (PTPParams *) device->params;
5736
5737   // Sanity check, is this really a track?
5738   if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
5739     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5740                             "LIBMTP_Send_Track_From_Handler(): "
5741                             "I don't think this is actually a track, strange filetype...");
5742   }
5743
5744   // Wrap around the file transfer function
5745   filedata.item_id = metadata->item_id;
5746   filedata.parent_id = metadata->parent_id;
5747   filedata.storage_id = metadata->storage_id;
5748   if FLAG_UNIQUE_FILENAMES(ptp_usb) {
5749     filedata.filename = generate_unique_filename(params, metadata->filename);
5750   }
5751   else {
5752     filedata.filename = metadata->filename;
5753   }
5754   filedata.filesize = metadata->filesize;
5755   filedata.filetype = metadata->filetype;
5756   filedata.next = NULL;
5757
5758   subcall_ret = LIBMTP_Send_File_From_Handler(device,
5759                                               get_func,
5760                                               priv,
5761                                               &filedata,
5762                                               callback,
5763                                               data);
5764
5765   if (subcall_ret != 0) {
5766     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5767                             "LIBMTP_Send_Track_From_Handler(): "
5768                             "subcall to LIBMTP_Send_File_From_Handler failed.");
5769     // We used to delete the file here, but don't... It might be OK after all.
5770     // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5771     return -1;
5772   }
5773
5774   // Pick up new item (and parent, storage) ID
5775   metadata->item_id = filedata.item_id;
5776   metadata->parent_id = filedata.parent_id;
5777   metadata->storage_id = filedata.storage_id;
5778
5779   // Set track metadata for the new fine track
5780   subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
5781   if (subcall_ret != 0) {
5782     // Subcall will add error to errorstack
5783     // We used to delete the file here, but don't... It might be OK after all.
5784     // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5785     return -1;
5786   }
5787
5788   // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
5789   // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
5790
5791   return 0;
5792 }
5793
5794 /**
5795  * This function sends a local file to an MTP device.
5796  * A filename and a set of metadata must be
5797  * given as input.
5798  * @param device a pointer to the device to send the track to.
5799  * @param path the filename of a local file which will be sent.
5800  * @param filedata a file metadata set to be written along with the file.
5801  *        After this call the field <code>filedata-&gt;item_id</code>
5802  *        will contain the new file ID. Other fields such
5803  *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
5804  *        or <code>filedata-&gt;storage_id</code> may also change during this
5805  *        operation due to device restrictions, so do not rely on the
5806  *        contents of this struct to be preserved in any way.
5807  *        <ul>
5808  *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
5809  *        (e.g. folder) to store this file in. If this is 0,
5810  *        the file will be stored in the root folder.
5811  *        <li><code>filedata-&gt;storage_id</code> should be set to the
5812  *        desired storage (e.g. memory card or whatever your device
5813  *        presents) to store this file in. Setting this to 0 will store
5814  *        the file on the primary storage.
5815  *        </ul>
5816  * @param callback a progress indicator function or NULL to ignore.
5817  * @param data a user-defined pointer that is passed along to
5818  *             the <code>progress</code> function in order to
5819  *             pass along some user defined data to the progress
5820  *             updates. If not used, set this to NULL.
5821  * @return 0 if the transfer was successful, any other value means
5822  *           failure.
5823  * @see LIBMTP_Send_File_From_File_Descriptor()
5824  * @see LIBMTP_Delete_Object()
5825  */
5826 int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
5827                                char const * const path, LIBMTP_file_t * const filedata,
5828                                LIBMTP_progressfunc_t const callback,
5829                                void const * const data)
5830 {
5831   int fd;
5832   int ret;
5833
5834   // Sanity check
5835   if (path == NULL) {
5836     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Bad arguments, path was NULL.");
5837     return -1;
5838   }
5839
5840   // Open file
5841 #ifdef __WIN32__
5842 #ifdef USE_WINDOWS_IO_H
5843   if ( (fd = _open(path, O_RDONLY|O_BINARY)) == -1 ) {
5844 #else
5845   if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
5846 #endif
5847 #else
5848   if ( (fd = open(path, O_RDONLY)) == -1) {
5849 #endif
5850     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Could not open source file.");
5851     return -1;
5852   }
5853
5854   ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data);
5855
5856   // Close file.
5857 #ifdef USE_WINDOWS_IO_H
5858   _close(fd);
5859 #else
5860   close(fd);
5861 #endif
5862
5863   return ret;
5864 }
5865
5866 /**
5867  * This function sends a generic file from a file descriptor to an
5868  * MTP device. A filename and a set of metadata must be
5869  * given as input.
5870  *
5871  * This can potentially be used for sending in a stream of unknown
5872  * length. Send music files with
5873  * <code>LIBMTP_Send_Track_From_File_Descriptor()</code>
5874  *
5875  * @param device a pointer to the device to send the file to.
5876  * @param fd the filedescriptor for a local file which will be sent.
5877  * @param filedata a file metadata set to be written along with the file.
5878  *        After this call the field <code>filedata-&gt;item_id</code>
5879  *        will contain the new file ID. Other fields such
5880  *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
5881  *        or <code>filedata-&gt;storage_id</code> may also change during this
5882  *        operation due to device restrictions, so do not rely on the
5883  *        contents of this struct to be preserved in any way.
5884  *        <ul>
5885  *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
5886  *        (e.g. folder) to store this file in. If this is 0,
5887  *        the file will be stored in the root folder.
5888  *        <li><code>filedata-&gt;storage_id</code> should be set to the
5889  *        desired storage (e.g. memory card or whatever your device
5890  *        presents) to store this file in. Setting this to 0 will store
5891  *        the file on the primary storage.
5892  *        </ul>
5893  * @param callback a progress indicator function or NULL to ignore.
5894  * @param data a user-defined pointer that is passed along to
5895  *             the <code>progress</code> function in order to
5896  *             pass along some user defined data to the progress
5897  *             updates. If not used, set this to NULL.
5898  * @return 0 if the transfer was successful, any other value means
5899  *           failure.
5900  * @see LIBMTP_Send_File_From_File()
5901  * @see LIBMTP_Send_Track_From_File_Descriptor()
5902  * @see LIBMTP_Delete_Object()
5903  */
5904 int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
5905                          int const fd, LIBMTP_file_t * const filedata,
5906                          LIBMTP_progressfunc_t const callback,
5907                          void const * const data)
5908 {
5909   uint16_t ret;
5910   PTPParams *params = (PTPParams *) device->params;
5911   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5912   LIBMTP_file_t *newfilemeta;
5913   int oldtimeout;
5914   int timeout;
5915
5916   if (send_file_object_info(device, filedata))
5917   {
5918     // no need to output an error since send_file_object_info will already have done so
5919     return -1;
5920   }
5921
5922   // Callbacks
5923   ptp_usb->callback_active = 1;
5924   // The callback will deactivate itself after this amount of data has been sent
5925   // One BULK header for the request, one for the data phase. No parameters to the request.
5926   ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
5927   ptp_usb->current_transfer_complete = 0;
5928   ptp_usb->current_transfer_callback = callback;
5929   ptp_usb->current_transfer_callback_data = data;
5930
5931   /*
5932    * We might need to increase the timeout here, files can be pretty
5933    * large. Take the default timeout and add the calculated time for
5934    * this transfer
5935    */
5936   get_usb_device_timeout(ptp_usb, &oldtimeout);
5937   timeout = oldtimeout +
5938     (ptp_usb->current_transfer_total / guess_usb_speed(ptp_usb)) * 1000;
5939   set_usb_device_timeout(ptp_usb, timeout);
5940
5941   ret = ptp_sendobject_fromfd(params, fd, filedata->filesize);
5942
5943   ptp_usb->callback_active = 0;
5944   ptp_usb->current_transfer_callback = NULL;
5945   ptp_usb->current_transfer_callback_data = NULL;
5946   set_usb_device_timeout(ptp_usb, oldtimeout);
5947
5948   if (ret == PTP_ERROR_CANCEL) {
5949     add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer.");
5950     return -1;
5951   }
5952   if (ret != PTP_RC_OK) {
5953     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): "
5954                                 "Could not send object.");
5955     return -1;
5956   }
5957
5958   add_object_to_cache(device, filedata->item_id);
5959
5960   /*
5961    * Get the device-assigned parent_id from the cache.
5962    * The operation that adds it to the cache will
5963    * look it up from the device, so we get the new
5964    * parent_id from the cache.
5965    */
5966   newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
5967   if (newfilemeta != NULL) {
5968     filedata->parent_id = newfilemeta->parent_id;
5969     filedata->storage_id = newfilemeta->storage_id;
5970     LIBMTP_destroy_file_t(newfilemeta);
5971   } else {
5972     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5973                             "LIBMTP_Send_File_From_File_Descriptor(): "
5974                             "Could not retrieve updated metadata.");
5975     return -1;
5976   }
5977
5978   return 0;
5979 }
5980
5981 /**
5982  * This function sends a generic file from a handler function to an
5983  * MTP device. A filename and a set of metadata must be
5984  * given as input.
5985  *
5986  * This can potentially be used for sending in a stream of unknown
5987  * length. Send music files with
5988  * <code>LIBMTP_Send_Track_From_Handler()</code>
5989  *
5990  * @param device a pointer to the device to send the file to.
5991  * @param get_func the function to call to get data to write
5992  * @param priv a user-defined pointer that is passed along to
5993  *        <code>get_func</code>. If not used, this is set to NULL.
5994  * @param filedata a file metadata set to be written along with the file.
5995  *        After this call the field <code>filedata-&gt;item_id</code>
5996  *        will contain the new file ID. Other fields such
5997  *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
5998  *        or <code>filedata-&gt;storage_id</code> may also change during this
5999  *        operation due to device restrictions, so do not rely on the
6000  *        contents of this struct to be preserved in any way.
6001  *        <ul>
6002  *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
6003  *        (e.g. folder) to store this file in. If this is 0,
6004  *        the file will be stored in the root folder.
6005  *        <li><code>filedata-&gt;storage_id</code> should be set to the
6006  *        desired storage (e.g. memory card or whatever your device
6007  *        presents) to store this file in. Setting this to 0 will store
6008  *        the file on the primary storage.
6009  *        </ul>
6010  * @param callback a progress indicator function or NULL to ignore.
6011  * @param data a user-defined pointer that is passed along to
6012  *             the <code>progress</code> function in order to
6013  *             pass along some user defined data to the progress
6014  *             updates. If not used, set this to NULL.
6015  * @return 0 if the transfer was successful, any other value means
6016  *           failure.
6017  * @see LIBMTP_Send_File_From_File()
6018  * @see LIBMTP_Send_Track_From_File_Descriptor()
6019  * @see LIBMTP_Delete_Object()
6020  */
6021 int LIBMTP_Send_File_From_Handler(LIBMTP_mtpdevice_t *device,
6022                          MTPDataGetFunc get_func, void * priv, LIBMTP_file_t * const filedata,
6023        LIBMTP_progressfunc_t const callback, void const * const data)
6024 {
6025   uint16_t ret;
6026   PTPParams *params = (PTPParams *) device->params;
6027   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6028   LIBMTP_file_t *newfilemeta;
6029
6030   if (send_file_object_info(device, filedata))
6031   {
6032     // no need to output an error since send_file_object_info will already have done so
6033     return -1;
6034   }
6035
6036   // Callbacks
6037   ptp_usb->callback_active = 1;
6038   // The callback will deactivate itself after this amount of data has been sent
6039   // One BULK header for the request, one for the data phase. No parameters to the request.
6040   ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
6041   ptp_usb->current_transfer_complete = 0;
6042   ptp_usb->current_transfer_callback = callback;
6043   ptp_usb->current_transfer_callback_data = data;
6044
6045   MTPDataHandler mtp_handler;
6046   mtp_handler.getfunc = get_func;
6047   mtp_handler.putfunc = NULL;
6048   mtp_handler.priv = priv;
6049
6050   PTPDataHandler handler;
6051   handler.getfunc = get_func_wrapper;
6052   handler.putfunc = NULL;
6053   handler.priv = &mtp_handler;
6054
6055   ret = ptp_sendobject_from_handler(params, &handler, filedata->filesize);
6056
6057   ptp_usb->callback_active = 0;
6058   ptp_usb->current_transfer_callback = NULL;
6059   ptp_usb->current_transfer_callback_data = NULL;
6060
6061   if (ret == PTP_ERROR_CANCEL) {
6062     add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_Handler(): Cancelled transfer.");
6063     return -1;
6064   }
6065   if (ret != PTP_RC_OK) {
6066     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_Handler(): "
6067                                 "Could not send object.");
6068     return -1;
6069   }
6070
6071   add_object_to_cache(device, filedata->item_id);
6072
6073   /*
6074    * Get the device-assined parent_id from the cache.
6075    * The operation that adds it to the cache will
6076    * look it up from the device, so we get the new
6077    * parent_id from the cache.
6078    */
6079   newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
6080   if (newfilemeta != NULL) {
6081     filedata->parent_id = newfilemeta->parent_id;
6082     filedata->storage_id = newfilemeta->storage_id;
6083     LIBMTP_destroy_file_t(newfilemeta);
6084   } else {
6085     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
6086                             "LIBMTP_Send_File_From_Handler(): "
6087                             "Could not retrieve updated metadata.");
6088     return -1;
6089   }
6090
6091   return 0;
6092 }
6093
6094 /**
6095  * This function sends the file object info, ready for sendobject
6096  * @param device a pointer to the device to send the file to.
6097  * @param filedata a file metadata set to be written along with the file.
6098  * @return 0 if the transfer was successful, any other value means
6099  *           failure.
6100  */
6101 static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata)
6102 {
6103   PTPParams *params = (PTPParams *) device->params;
6104   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6105   uint32_t store;
6106   int use_primary_storage = 1;
6107   uint16_t of = map_libmtp_type_to_ptp_type(filedata->filetype);
6108   LIBMTP_devicestorage_t *storage;
6109   uint32_t localph = filedata->parent_id;
6110   uint16_t ret;
6111   int i;
6112
6113 #if 0
6114   // Sanity check: no zerolength files on some devices?
6115   // If the zerolength files cause problems on some devices,
6116   // then add a bug flag for this.
6117   if (filedata->filesize == 0) {
6118     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "send_file_object_info(): "
6119                             "File of zero size.");
6120     return -1;
6121   }
6122 #endif
6123   if (filedata->storage_id != 0) {
6124     store = filedata->storage_id;
6125   } else {
6126     store = get_suggested_storage_id(device, filedata->filesize, localph);
6127   }
6128
6129   // Detect if something non-primary is in use.
6130   storage = device->storage;
6131   if (storage != NULL && store != storage->id) {
6132     use_primary_storage = 0;
6133   }
6134
6135   /*
6136    * If no destination folder was given, look up a default
6137    * folder if possible. Perhaps there is some way of retrieveing
6138    * the default folder for different forms of content, what
6139    * do I know, we use a fixed list in lack of any better method.
6140    * Some devices obviously need to have their files in certain
6141    * folders in order to find/display them at all (hello Creative),
6142    * so we have to have a method for this. We only do this if the
6143    * primary storage is in use.
6144    */
6145
6146   if (localph == 0 && use_primary_storage) {
6147     if (LIBMTP_FILETYPE_IS_AUDIO(filedata->filetype)) {
6148       localph = device->default_music_folder;
6149     } else if (LIBMTP_FILETYPE_IS_VIDEO(filedata->filetype)) {
6150       localph = device->default_video_folder;
6151     } else if (of == PTP_OFC_EXIF_JPEG ||
6152                of == PTP_OFC_JP2 ||
6153                of == PTP_OFC_JPX ||
6154                of == PTP_OFC_JFIF ||
6155                of == PTP_OFC_TIFF ||
6156                of == PTP_OFC_TIFF_IT ||
6157                of == PTP_OFC_BMP ||
6158                of == PTP_OFC_GIF ||
6159                of == PTP_OFC_PICT ||
6160                of == PTP_OFC_PNG ||
6161                of == PTP_OFC_MTP_WindowsImageFormat) {
6162       localph = device->default_picture_folder;
6163     } else if (of == PTP_OFC_MTP_vCalendar1 ||
6164                of == PTP_OFC_MTP_vCalendar2 ||
6165                of == PTP_OFC_MTP_UndefinedContact ||
6166                of == PTP_OFC_MTP_vCard2 ||
6167                of == PTP_OFC_MTP_vCard3 ||
6168                of == PTP_OFC_MTP_UndefinedCalendarItem) {
6169       localph = device->default_organizer_folder;
6170     } else if (of == PTP_OFC_Text) {
6171       localph = device->default_text_folder;
6172     }
6173   }
6174
6175   // Here we wire the type to unknown on bugged, but
6176   // Ogg or FLAC-supportive devices.
6177   if (FLAG_OGG_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_OGG) {
6178     of = PTP_OFC_Undefined;
6179   }
6180   if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_FLAC) {
6181     of = PTP_OFC_Undefined;
6182   }
6183
6184   if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
6185       !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
6186     /*
6187      * MTP enhanched does it this way (from a sniff):
6188      * -> PTP_OC_MTP_SendObjectPropList (0x9808):
6189      *    20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
6190      *    FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
6191      *    Length: 0x00000020
6192      *    Type:   0x0001 PTP_USB_CONTAINER_COMMAND
6193      *    Code:   0x9808
6194      *    Transaction ID: 0x0000001B
6195      *    Param1: 0x00010001 <- store
6196      *    Param2: 0xffffffff <- parent handle (-1 ?)
6197      *    Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
6198      *    Param4: 0x00000000 <- file length MSB (-0x0c header len)
6199      *    Param5: 0x00005e12 <- file length LSB (-0x0c header len)
6200      *
6201      * -> PTP_OC_MTP_SendObjectPropList (0x9808):
6202      *    46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
6203      *    00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
6204      *    00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
6205      *    00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
6206      *    00 4F DC 02 00 01                               - dc4f = non consumable
6207      *    Length: 0x00000046
6208      *    Type:   0x0002 PTP_USB_CONTAINER_DATA
6209      *    Code:   0x9808
6210      *    Transaction ID: 0x0000001B
6211      *    Metadata....
6212      *    0x00000003 <- Number of metadata items
6213      *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6214      *    0xdc07     <- metadata type: file name
6215      *    0xffff     <- metadata type: string
6216      *    0x0d       <- number of (uint16_t) characters
6217      *    4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
6218      *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6219      *    0xdc03     <- metadata type: protection status
6220      *    0x0004     <- metadata type: uint16_t
6221      *    0x0000     <- not protected
6222      *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6223      *    0xdc4f     <- non consumable
6224      *    0x0002     <- metadata type: uint8_t
6225      *    0x01       <- non-consumable (this device cannot display PDF)
6226      *
6227      * <- Read 0x18 bytes back
6228      *    18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
6229      *    00 00 00 00 01 40 00 00
6230      *    Length: 0x000000018
6231      *    Type:   0x0003 PTP_USB_CONTAINER_RESPONSE
6232      *    Code:   0x2001 PTP_OK
6233      *    Transaction ID: 0x0000001B
6234      *    Param1: 0x00010001 <- store
6235      *    Param2: 0x00000000 <- parent handle
6236      *    Param3: 0x00004001 <- new file/object ID
6237      *
6238      * -> PTP_OC_SendObject (0x100d)
6239      *    0C 00 00 00 01 00 0D 10 1C 00 00 00
6240      * -> ... all the bytes ...
6241      * <- Read 0x0c bytes back
6242      *    0C 00 00 00 03 00 01 20 1C 00 00 00
6243      *    ... Then update metadata one-by one, actually (instead of sending it first!) ...
6244      */
6245     MTPProperties *props = NULL;
6246     int nrofprops = 0;
6247     MTPProperties *prop = NULL;
6248     uint16_t *properties = NULL;
6249     uint32_t propcnt = 0;
6250
6251     // default parent handle
6252     if (localph == 0)
6253       localph = 0xFFFFFFFFU; // Set to -1
6254
6255     // Must be 0x00000000U for new objects
6256     filedata->item_id = 0x00000000U;
6257
6258     ret = ptp_mtp_getobjectpropssupported(params, of, &propcnt, &properties);
6259
6260     for (i=0;i<propcnt;i++) {
6261       PTPObjectPropDesc opd;
6262
6263       ret = ptp_mtp_getobjectpropdesc(params, properties[i], of, &opd);
6264       if (ret != PTP_RC_OK) {
6265         add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
6266                                 "could not get property description.");
6267       } else if (opd.GetSet) {
6268         switch (properties[i]) {
6269         case PTP_OPC_ObjectFileName:
6270           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6271           prop->ObjectHandle = filedata->item_id;
6272           prop->property = PTP_OPC_ObjectFileName;
6273           prop->datatype = PTP_DTC_STR;
6274           if (filedata->filename != NULL) {
6275             prop->propval.str = strdup(filedata->filename);
6276             if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
6277               strip_7bit_from_utf8(prop->propval.str);
6278             }
6279           }
6280           break;
6281         case PTP_OPC_ProtectionStatus:
6282           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6283           prop->ObjectHandle = filedata->item_id;
6284           prop->property = PTP_OPC_ProtectionStatus;
6285           prop->datatype = PTP_DTC_UINT16;
6286           prop->propval.u16 = 0x0000U; /* Not protected */
6287           break;
6288         case PTP_OPC_NonConsumable:
6289           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6290           prop->ObjectHandle = filedata->item_id;
6291           prop->property = PTP_OPC_NonConsumable;
6292           prop->datatype = PTP_DTC_UINT8;
6293           prop->propval.u8 = 0x00; /* It is supported, then it is consumable */
6294           break;
6295         case PTP_OPC_Name:
6296           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6297           prop->ObjectHandle = filedata->item_id;
6298           prop->property = PTP_OPC_Name;
6299           prop->datatype = PTP_DTC_STR;
6300           if (filedata->filename != NULL)
6301             prop->propval.str = strdup(filedata->filename);
6302           break;
6303         case PTP_OPC_DateModified:
6304           // Tag with current time if that is supported
6305           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
6306             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6307             prop->ObjectHandle = filedata->item_id;
6308             prop->property = PTP_OPC_DateModified;
6309             prop->datatype = PTP_DTC_STR;
6310             prop->propval.str = get_iso8601_stamp();
6311             filedata->modificationdate = time(NULL);
6312           }
6313           break;
6314         }
6315       }
6316       ptp_free_objectpropdesc(&opd);
6317     }
6318     free(properties);
6319
6320     if (nrofprops == 0 || props == NULL) {
6321         LIBMTP_INFO("prop list doesn't exist");
6322         return -1;
6323     }
6324
6325     ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id,
6326                                      of, filedata->filesize, props, nrofprops);
6327
6328     /* Free property list */
6329     ptp_destroy_object_prop_list(props, nrofprops);
6330
6331     if (ret != PTP_RC_OK) {
6332       add_ptp_error_to_errorstack(device, ret, "send_file_object_info():"
6333                                   "Could not send object property list.");
6334       if (ret == PTP_RC_AccessDenied) {
6335         add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
6336       }
6337       return -1;
6338     }
6339   } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
6340     PTPObjectInfo new_file;
6341
6342     memset(&new_file, 0, sizeof(PTPObjectInfo));
6343
6344     new_file.Filename = filedata->filename;
6345     if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
6346       strip_7bit_from_utf8(new_file.Filename);
6347     }
6348     if (filedata->filesize > 0xFFFFFFFFL) {
6349       // This is a kludge in the MTP standard for large files.
6350       new_file.ObjectCompressedSize = (uint32_t) 0xFFFFFFFF;
6351     } else {
6352       new_file.ObjectCompressedSize = (uint32_t) filedata->filesize;
6353     }
6354     new_file.ObjectFormat = of;
6355     new_file.StorageID = store;
6356     new_file.ParentObject = localph;
6357     new_file.ModificationDate = time(NULL);
6358
6359     // Create the object
6360     ret = ptp_sendobjectinfo(params, &store, &localph, &filedata->item_id, &new_file);
6361
6362     if (ret != PTP_RC_OK) {
6363       add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
6364                                   "Could not send object info.");
6365       if (ret == PTP_RC_AccessDenied) {
6366         add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
6367       }
6368       return -1;
6369     }
6370     // NOTE: the char* pointers inside new_file are not copies so don't
6371     // try to destroy this objectinfo!
6372   }
6373
6374   // Now there IS an object with this parent handle.
6375   filedata->parent_id = localph;
6376
6377   return 0;
6378 }
6379
6380 /**
6381  * This function updates the MTP track object metadata on a
6382  * single file identified by an object ID.
6383  * @param device a pointer to the device to update the track
6384  *        metadata on.
6385  * @param metadata a track metadata set to be written to the file.
6386  *        notice that the <code>track_id</code> field of the
6387  *        metadata structure must be correct so that the
6388  *        function can update the right file. If some properties
6389  *        of this metadata are set to NULL (strings) or 0
6390  *        (numerical values) they will be discarded and the
6391  *        track will not be tagged with these blank values.
6392  * @return 0 on success, any other value means failure. If some
6393  *        or all of the properties fail to update we will still
6394  *        return success. On some devices (notably iRiver T30)
6395  *        properties that exist cannot be updated.
6396  */
6397 int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *device,
6398                                  LIBMTP_track_t const * const metadata)
6399 {
6400   uint16_t ret;
6401   PTPParams *params = (PTPParams *) device->params;
6402   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6403   uint32_t i;
6404   uint16_t *properties = NULL;
6405   uint32_t propcnt = 0;
6406
6407   // First see which properties can be set on this file format and apply accordingly
6408   // i.e only try to update this metadata for object tags that exist on the current player.
6409   ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &properties);
6410   if (ret != PTP_RC_OK) {
6411     // Just bail out for now, nothing is ever set.
6412     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6413                             "could not retrieve supported object properties.");
6414     return -1;
6415   }
6416   if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
6417       !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
6418     MTPProperties *props = NULL;
6419     MTPProperties *prop = NULL;
6420     int nrofprops = 0;
6421
6422     for (i=0;i<propcnt;i++) {
6423       PTPObjectPropDesc opd;
6424
6425       ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
6426       if (ret != PTP_RC_OK) {
6427         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6428                                 "could not get property description.");
6429       } else if (opd.GetSet) {
6430         switch (properties[i]) {
6431         case PTP_OPC_Name:
6432           if (metadata->title == NULL)
6433             break;
6434           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6435           prop->ObjectHandle = metadata->item_id;
6436           prop->property = PTP_OPC_Name;
6437           prop->datatype = PTP_DTC_STR;
6438           prop->propval.str = strdup(metadata->title);
6439           break;
6440         case PTP_OPC_AlbumName:
6441           if (metadata->album == NULL)
6442             break;
6443           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6444           prop->ObjectHandle = metadata->item_id;
6445           prop->property = PTP_OPC_AlbumName;
6446           prop->datatype = PTP_DTC_STR;
6447           prop->propval.str = strdup(metadata->album);
6448           break;
6449         case PTP_OPC_Artist:
6450           if (metadata->artist == NULL)
6451             break;
6452           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6453           prop->ObjectHandle = metadata->item_id;
6454           prop->property = PTP_OPC_Artist;
6455           prop->datatype = PTP_DTC_STR;
6456           prop->propval.str = strdup(metadata->artist);
6457           break;
6458         case PTP_OPC_Composer:
6459           if (metadata->composer == NULL)
6460             break;
6461           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6462           prop->ObjectHandle = metadata->item_id;
6463           prop->property = PTP_OPC_Composer;
6464           prop->datatype = PTP_DTC_STR;
6465           prop->propval.str = strdup(metadata->composer);
6466           break;
6467         case PTP_OPC_Genre:
6468           if (metadata->genre == NULL)
6469             break;
6470           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6471           prop->ObjectHandle = metadata->item_id;
6472           prop->property = PTP_OPC_Genre;
6473           prop->datatype = PTP_DTC_STR;
6474           prop->propval.str = strdup(metadata->genre);
6475           break;
6476         case PTP_OPC_Duration:
6477           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6478           prop->ObjectHandle = metadata->item_id;
6479           prop->property = PTP_OPC_Duration;
6480           prop->datatype = PTP_DTC_UINT32;
6481           prop->propval.u32 = adjust_u32(metadata->duration, &opd);
6482           break;
6483         case PTP_OPC_Track:
6484           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6485           prop->ObjectHandle = metadata->item_id;
6486           prop->property = PTP_OPC_Track;
6487           prop->datatype = PTP_DTC_UINT16;
6488           prop->propval.u16 = adjust_u16(metadata->tracknumber, &opd);
6489           break;
6490         case PTP_OPC_OriginalReleaseDate:
6491           if (metadata->date == NULL)
6492             break;
6493           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6494           prop->ObjectHandle = metadata->item_id;
6495           prop->property = PTP_OPC_OriginalReleaseDate;
6496           prop->datatype = PTP_DTC_STR;
6497           prop->propval.str = strdup(metadata->date);
6498           break;
6499         case PTP_OPC_SampleRate:
6500           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6501           prop->ObjectHandle = metadata->item_id;
6502           prop->property = PTP_OPC_SampleRate;
6503           prop->datatype = PTP_DTC_UINT32;
6504           prop->propval.u32 = adjust_u32(metadata->samplerate, &opd);
6505           break;
6506         case PTP_OPC_NumberOfChannels:
6507           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6508           prop->ObjectHandle = metadata->item_id;
6509           prop->property = PTP_OPC_NumberOfChannels;
6510           prop->datatype = PTP_DTC_UINT16;
6511           prop->propval.u16 = adjust_u16(metadata->nochannels, &opd);
6512           break;
6513         case PTP_OPC_AudioWAVECodec:
6514           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6515           prop->ObjectHandle = metadata->item_id;
6516           prop->property = PTP_OPC_AudioWAVECodec;
6517           prop->datatype = PTP_DTC_UINT32;
6518           prop->propval.u32 = adjust_u32(metadata->wavecodec, &opd);
6519           break;
6520         case PTP_OPC_AudioBitRate:
6521           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6522           prop->ObjectHandle = metadata->item_id;
6523           prop->property = PTP_OPC_AudioBitRate;
6524           prop->datatype = PTP_DTC_UINT32;
6525           prop->propval.u32 = adjust_u32(metadata->bitrate, &opd);
6526           break;
6527         case PTP_OPC_BitRateType:
6528           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6529           prop->ObjectHandle = metadata->item_id;
6530           prop->property = PTP_OPC_BitRateType;
6531           prop->datatype = PTP_DTC_UINT16;
6532           prop->propval.u16 = adjust_u16(metadata->bitratetype, &opd);
6533           break;
6534         case PTP_OPC_Rating:
6535           // TODO: shall this be set for rating 0?
6536           if (metadata->rating == 0)
6537             break;
6538           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6539           prop->ObjectHandle = metadata->item_id;
6540           prop->property = PTP_OPC_Rating;
6541           prop->datatype = PTP_DTC_UINT16;
6542           prop->propval.u16 = adjust_u16(metadata->rating, &opd);
6543           break;
6544         case PTP_OPC_UseCount:
6545           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6546           prop->ObjectHandle = metadata->item_id;
6547           prop->property = PTP_OPC_UseCount;
6548           prop->datatype = PTP_DTC_UINT32;
6549           prop->propval.u32 = adjust_u32(metadata->usecount, &opd);
6550           break;
6551         case PTP_OPC_DateModified:
6552           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
6553             // Tag with current time if that is supported
6554             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6555             prop->ObjectHandle = metadata->item_id;
6556             prop->property = PTP_OPC_DateModified;
6557             prop->datatype = PTP_DTC_STR;
6558             prop->propval.str = get_iso8601_stamp();
6559           }
6560           break;
6561         default:
6562           break;
6563         }
6564       }
6565       ptp_free_objectpropdesc(&opd);
6566     }
6567
6568     // NOTE: File size is not updated, this should not change anyway.
6569     // neither will we change the filename.
6570
6571     ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
6572
6573     ptp_destroy_object_prop_list(props, nrofprops);
6574
6575     if (ret != PTP_RC_OK) {
6576       // TODO: return error of which property we couldn't set
6577       add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6578                               "could not set object property list.");
6579       free(properties);
6580       return -1;
6581     }
6582
6583   } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
6584     for (i=0;i<propcnt;i++) {
6585       PTPObjectPropDesc opd;
6586
6587       ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
6588       if (ret != PTP_RC_OK) {
6589         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6590                                 "could not get property description.");
6591       } else if (opd.GetSet) {
6592         switch (properties[i]) {
6593         case PTP_OPC_Name:
6594           // Update title
6595           ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title);
6596           if (ret != 0) {
6597             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6598                                     "could not set track title.");
6599           }
6600           break;
6601         case PTP_OPC_AlbumName:
6602           // Update album
6603           ret = set_object_string(device, metadata->item_id, PTP_OPC_AlbumName, metadata->album);
6604           if (ret != 0) {
6605             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6606                                     "could not set track album name.");
6607           }
6608           break;
6609         case PTP_OPC_Artist:
6610           // Update artist
6611           ret = set_object_string(device, metadata->item_id, PTP_OPC_Artist, metadata->artist);
6612           if (ret != 0) {
6613             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6614                                     "could not set track artist name.");
6615           }
6616           break;
6617         case PTP_OPC_Composer:
6618           // Update composer
6619           ret = set_object_string(device, metadata->item_id, PTP_OPC_Composer, metadata->composer);
6620           if (ret != 0) {
6621             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6622                                     "could not set track composer name.");
6623           }
6624           break;
6625         case PTP_OPC_Genre:
6626           // Update genre (but only if valid)
6627           if (metadata->genre) {
6628             ret = set_object_string(device, metadata->item_id, PTP_OPC_Genre, metadata->genre);
6629             if (ret != 0) {
6630               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
6631                                       "could not set genre.");
6632             }
6633           }
6634           break;
6635         case PTP_OPC_Duration:
6636           // Update duration
6637           if (metadata->duration != 0) {
6638             ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, adjust_u32(metadata->duration, &opd));
6639             if (ret != 0) {
6640               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6641                                       "could not set track duration.");
6642             }
6643           }
6644           break;
6645         case PTP_OPC_Track:
6646           // Update track number.
6647           if (metadata->tracknumber != 0) {
6648             ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, adjust_u16(metadata->tracknumber, &opd));
6649             if (ret != 0) {
6650               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6651                                       "could not set track tracknumber.");
6652             }
6653           }
6654           break;
6655         case PTP_OPC_OriginalReleaseDate:
6656           // Update creation datetime
6657           // The date can be zero, but some devices do not support setting zero
6658           // dates (and it seems that a zero date should never be set anyway)
6659           if (metadata->date) {
6660             ret = set_object_string(device, metadata->item_id, PTP_OPC_OriginalReleaseDate, metadata->date);
6661             if (ret != 0) {
6662               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6663                                       "could not set track release date.");
6664             }
6665           }
6666           break;
6667           // These are, well not so important.
6668         case PTP_OPC_SampleRate:
6669           // Update sample rate
6670           if (metadata->samplerate != 0) {
6671             ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, adjust_u32(metadata->samplerate, &opd));
6672             if (ret != 0) {
6673               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6674                                       "could not set samplerate.");
6675             }
6676           }
6677           break;
6678         case PTP_OPC_NumberOfChannels:
6679           // Update number of channels
6680           if (metadata->nochannels != 0) {
6681             ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, adjust_u16(metadata->nochannels, &opd));
6682           if (ret != 0) {
6683             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6684                                     "could not set number of channels.");
6685           }
6686         }
6687           break;
6688         case PTP_OPC_AudioWAVECodec:
6689           // Update WAVE codec
6690           if (metadata->wavecodec != 0) {
6691             ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, adjust_u32(metadata->wavecodec, &opd));
6692             if (ret != 0) {
6693               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6694                                       "could not set WAVE codec.");
6695             }
6696           }
6697           break;
6698         case PTP_OPC_AudioBitRate:
6699           // Update bitrate
6700           if (metadata->bitrate != 0) {
6701             ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, adjust_u32(metadata->bitrate, &opd));
6702             if (ret != 0) {
6703               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6704                                       "could not set bitrate.");
6705           }
6706           }
6707           break;
6708         case PTP_OPC_BitRateType:
6709           // Update bitrate type
6710           if (metadata->bitratetype != 0) {
6711             ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, adjust_u16(metadata->bitratetype, &opd));
6712             if (ret != 0) {
6713               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6714                                       "could not set bitratetype.");
6715             }
6716           }
6717           break;
6718         case PTP_OPC_Rating:
6719           // Update user rating
6720           // TODO: shall this be set for rating 0?
6721           if (metadata->rating != 0) {
6722             ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, adjust_u16(metadata->rating, &opd));
6723             if (ret != 0) {
6724               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6725                                       "could not set user rating.");
6726             }
6727           }
6728           break;
6729         case PTP_OPC_UseCount:
6730           // Update use count, set even to zero if desired.
6731           ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, adjust_u32(metadata->usecount, &opd));
6732           if (ret != 0) {
6733             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6734                                   "could not set use count.");
6735           }
6736           break;
6737         case PTP_OPC_DateModified:
6738           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
6739             // Update modification time if supported
6740             char *tmpstamp = get_iso8601_stamp();
6741             ret = set_object_string(device, metadata->item_id, PTP_OPC_DateModified, tmpstamp);
6742             if (ret != 0) {
6743               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6744                                       "could not set modification date.");
6745             }
6746             free(tmpstamp);
6747           }
6748           break;
6749
6750           // NOTE: File size is not updated, this should not change anyway.
6751           // neither will we change the filename.
6752         default:
6753           break;
6754         }
6755       }
6756       ptp_free_objectpropdesc(&opd);
6757     }
6758   } else {
6759     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6760                             "Your device doesn't seem to support any known way of setting metadata.");
6761     free(properties);
6762     return -1;
6763   }
6764
6765   // update cached object properties if metadata cache exists
6766   update_metadata_cache(device, metadata->item_id);
6767
6768   free(properties);
6769
6770   return 0;
6771 }
6772
6773 /**
6774  * This function deletes a single file, track, playlist, folder or
6775  * any other object off the MTP device, identified by the object ID.
6776  *
6777  * If you delete a folder, there is no guarantee that the device will
6778  * really delete all the files that were in that folder, rather it is
6779  * expected that they will not be deleted, and will turn up in object
6780  * listings with parent set to a non-existant object ID. The safe way
6781  * to do this is to recursively delete all files (and folders) contained
6782  * in the folder, then the folder itself.
6783  *
6784  * @param device a pointer to the device to delete the object from.
6785  * @param object_id the object to delete.
6786  * @return 0 on success, any other value means failure.
6787  */
6788 int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
6789                          uint32_t object_id)
6790 {
6791   uint16_t ret;
6792   PTPParams *params = (PTPParams *) device->params;
6793
6794   ret = ptp_deleteobject(params, object_id, 0);
6795   if (ret != PTP_RC_OK) {
6796     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Delete_Object(): could not delete object.");
6797     return -1;
6798   }
6799
6800   return 0;
6801 }
6802
6803 /**
6804  * Internal function to update an object filename property.
6805  */
6806 static int set_object_filename(LIBMTP_mtpdevice_t *device,
6807                                uint32_t object_id, uint16_t ptp_type,
6808                                const char **newname_ptr)
6809 {
6810   PTPParams             *params = (PTPParams *) device->params;
6811   PTP_USB               *ptp_usb = (PTP_USB*) device->usbinfo;
6812   PTPObjectPropDesc     opd;
6813   uint16_t              ret;
6814   char                  *newname;
6815
6816   // See if we can modify the filename on this kind of files.
6817   ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_ObjectFileName, ptp_type, &opd);
6818   if (ret != PTP_RC_OK) {
6819     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6820                             "could not get property description.");
6821     return -1;
6822   }
6823
6824   if (!opd.GetSet) {
6825     ptp_free_objectpropdesc(&opd);
6826     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6827             " property is not settable.");
6828     // TODO: we COULD actually upload/download the object here, if we feel
6829     //       like wasting time for the user.
6830     return -1;
6831   }
6832
6833   newname = strdup(*newname_ptr);
6834
6835   if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
6836     strip_7bit_from_utf8(newname);
6837   }
6838
6839   if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
6840       !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
6841     MTPProperties *props = NULL;
6842     MTPProperties *prop = NULL;
6843     int nrofprops = 0;
6844
6845     prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6846     prop->ObjectHandle = object_id;
6847     prop->property = PTP_OPC_ObjectFileName;
6848     prop->datatype = PTP_DTC_STR;
6849     prop->propval.str = newname;
6850
6851     ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
6852
6853     ptp_destroy_object_prop_list(props, nrofprops);
6854
6855     if (ret != PTP_RC_OK) {
6856         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6857               " could not set object property list.");
6858         ptp_free_objectpropdesc(&opd);
6859         return -1;
6860     }
6861   } else if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjectPropValue)) {
6862     ret = set_object_string(device, object_id, PTP_OPC_ObjectFileName, newname);
6863     if (ret != 0) {
6864       add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6865               " could not set object filename.");
6866       ptp_free_objectpropdesc(&opd);
6867       return -1;
6868     }
6869   } else {
6870     free(newname);
6871     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6872               " your device doesn't seem to support any known way of setting metadata.");
6873     ptp_free_objectpropdesc(&opd);
6874     return -1;
6875   }
6876
6877   ptp_free_objectpropdesc(&opd);
6878
6879   // update cached object properties if metadata cache exists
6880   update_metadata_cache(device, object_id);
6881
6882   return 0;
6883 }
6884
6885 /**
6886  * This function renames a single file.
6887  * This simply means that the PTP_OPC_ObjectFileName property
6888  * is updated, if this is supported by the device.
6889  *
6890  * @param device a pointer to the device that contains the file.
6891  * @param file the file metadata of the file to rename.
6892  *        On success, the filename member is updated. Be aware, that
6893  *        this name can be different than newname depending of device restrictions.
6894  * @param newname the new filename for this object.
6895  * @return 0 on success, any other value means failure.
6896  */
6897 int LIBMTP_Set_File_Name(LIBMTP_mtpdevice_t *device,
6898                    LIBMTP_file_t *file, const char *newname)
6899 {
6900   int         ret;
6901
6902   ret = set_object_filename(device, file->item_id,
6903                             map_libmtp_type_to_ptp_type(file->filetype),
6904                             &newname);
6905
6906   if (ret != 0) {
6907     return ret;
6908   }
6909
6910   free(file->filename);
6911   file->filename = strdup(newname);
6912   return ret;
6913 }
6914
6915 /**
6916  * This function renames a single folder.
6917  * This simply means that the PTP_OPC_ObjectFileName property
6918  * is updated, if this is supported by the device.
6919  *
6920  * @param device a pointer to the device that contains the file.
6921  * @param folder the folder metadata of the folder to rename.
6922  *        On success, the name member is updated. Be aware, that
6923  *        this name can be different than newname depending of device restrictions.
6924  * @param newname the new name for this object.
6925  * @return 0 on success, any other value means failure.
6926  */
6927 int LIBMTP_Set_Folder_Name(LIBMTP_mtpdevice_t *device,
6928                    LIBMTP_folder_t *folder, const char* newname)
6929 {
6930   int ret;
6931
6932   ret = set_object_filename(device, folder->folder_id,
6933                             PTP_OFC_Association,
6934                             &newname);
6935
6936   if (ret != 0) {
6937     return ret;
6938     }
6939
6940   free(folder->name);
6941   folder->name = strdup(newname);
6942   return ret;
6943 }
6944
6945 /**
6946  * This function renames a single track.
6947  * This simply means that the PTP_OPC_ObjectFileName property
6948  * is updated, if this is supported by the device.
6949  *
6950  * @param device a pointer to the device that contains the file.
6951  * @param track the track metadata of the track to rename.
6952  *        On success, the filename member is updated. Be aware, that
6953  *        this name can be different than newname depending of device restrictions.
6954  * @param newname the new filename for this object.
6955  * @return 0 on success, any other value means failure.
6956  */
6957 int LIBMTP_Set_Track_Name(LIBMTP_mtpdevice_t *device,
6958                    LIBMTP_track_t *track, const char* newname)
6959 {
6960   int         ret;
6961
6962   ret = set_object_filename(device, track->item_id,
6963                             map_libmtp_type_to_ptp_type(track->filetype),
6964                             &newname);
6965
6966   if (ret != 0) {
6967     return ret;
6968   }
6969
6970   free(track->filename);
6971   track->filename = strdup(newname);
6972   return ret;
6973 }
6974
6975 /**
6976  * This function renames a single playlist object file holder.
6977  * This simply means that the <code>PTP_OPC_ObjectFileName</code>
6978  * property is updated, if this is supported by the device.
6979  * The playlist filename should nominally end with an extension
6980  * like ".pla".
6981  *
6982  * NOTE: if you want to change the metadata the device display
6983  * about a playlist you must <i>not</i> use this function,
6984  * use <code>LIBMTP_Update_Playlist()</code> instead!
6985  *
6986  * @param device a pointer to the device that contains the file.
6987  * @param playlist the playlist metadata of the playlist to rename.
6988  *        On success, the name member is updated. Be aware, that
6989  *        this name can be different than newname depending of device restrictions.
6990  * @param newname the new name for this object.
6991  * @return 0 on success, any other value means failure.
6992  * @see LIBMTP_Update_Playlist()
6993  */
6994 int LIBMTP_Set_Playlist_Name(LIBMTP_mtpdevice_t *device,
6995                    LIBMTP_playlist_t *playlist, const char* newname)
6996 {
6997   int ret;
6998
6999   ret = set_object_filename(device, playlist->playlist_id,
7000                             PTP_OFC_MTP_AbstractAudioVideoPlaylist,
7001                             &newname);
7002
7003   if (ret != 0) {
7004     return ret;
7005   }
7006
7007   free(playlist->name);
7008   playlist->name = strdup(newname);
7009   return ret;
7010 }
7011
7012 /**
7013  * This function renames a single album.
7014  * This simply means that the <code>PTP_OPC_ObjectFileName</code>
7015  * property is updated, if this is supported by the device.
7016  * The album filename should nominally end with an extension
7017  * like ".alb".
7018  *
7019  * NOTE: if you want to change the metadata the device display
7020  * about a playlist you must <i>not</i> use this function,
7021  * use <code>LIBMTP_Update_Album()</code> instead!
7022  *
7023  * @param device a pointer to the device that contains the file.
7024  * @param album the album metadata of the album to rename.
7025  *        On success, the name member is updated. Be aware, that
7026  *        this name can be different than newname depending of device restrictions.
7027  * @param newname the new name for this object.
7028  * @return 0 on success, any other value means failure.
7029  * @see LIBMTP_Update_Album()
7030  */
7031 int LIBMTP_Set_Album_Name(LIBMTP_mtpdevice_t *device,
7032                    LIBMTP_album_t *album, const char* newname)
7033 {
7034   int ret;
7035
7036   ret = set_object_filename(device, album->album_id,
7037                             PTP_OFC_MTP_AbstractAudioAlbum,
7038                             &newname);
7039
7040   if (ret != 0) {
7041     return ret;
7042   }
7043
7044   free(album->name);
7045   album->name = strdup(newname);
7046   return ret;
7047 }
7048
7049 /**
7050  * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
7051  * NOT TO USE IT.
7052  *
7053  * @see LIBMTP_Set_File_Name()
7054  * @see LIBMTP_Set_Track_Name()
7055  * @see LIBMTP_Set_Folder_Name()
7056  * @see LIBMTP_Set_Playlist_Name()
7057  * @see LIBMTP_Set_Album_Name()
7058  */
7059 int LIBMTP_Set_Object_Filename(LIBMTP_mtpdevice_t *device,
7060                    uint32_t object_id, char* newname)
7061 {
7062   int             ret;
7063   LIBMTP_file_t   *file;
7064
7065   file = LIBMTP_Get_Filemetadata(device, object_id);
7066
7067   if (file == NULL) {
7068     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Set_Object_Filename(): "
7069                             "could not get file metadata for target object.");
7070     return -1;
7071   }
7072
7073   ret = set_object_filename(device, object_id, map_libmtp_type_to_ptp_type(file->filetype), (const char **) &newname);
7074
7075   free(file);
7076
7077   return ret;
7078 }
7079
7080 /**
7081  * Helper function. This indicates if a track exists on the device
7082  * @param device a pointer to the device to get the track from.
7083  * @param id the track ID of the track to retrieve.
7084  * @return TRUE (!=0) if the track exists, FALSE (0) if not
7085  */
7086 int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *device,
7087            uint32_t const id)
7088 {
7089   PTPParams *params = (PTPParams *) device->params;
7090   uint16_t ret;
7091   PTPObject *ob;
7092
7093   ret = ptp_object_want (params, id, 0, &ob);
7094   if (ret == PTP_RC_OK)
7095       return -1;
7096   return 0;
7097 }
7098
7099 /**
7100  * This creates a new folder structure and allocates memory
7101  * for it. Notice that if you add strings to this structure they
7102  * will be freed by the corresponding <code>LIBMTP_folder_track_t</code>
7103  * operation later, so be careful of using strdup() when assigning
7104  * strings, e.g.:
7105  *
7106  * @return a pointer to the newly allocated folder structure.
7107  * @see LIBMTP_destroy_folder_t()
7108  */
7109 LIBMTP_folder_t *LIBMTP_new_folder_t(void)
7110 {
7111   LIBMTP_folder_t *new = (LIBMTP_folder_t *) malloc(sizeof(LIBMTP_folder_t));
7112   if (new == NULL) {
7113     return NULL;
7114   }
7115   new->folder_id = 0;
7116   new->parent_id = 0;
7117   new->storage_id = 0;
7118   new->name = NULL;
7119   new->sibling = NULL;
7120   new->child = NULL;
7121   return new;
7122 }
7123
7124 /**
7125  * This recursively deletes the memory for a folder structure.
7126  * This shall typically be called on a top-level folder list to
7127  * detsroy the entire folder tree.
7128  *
7129  * @param folder folder structure to destroy
7130  * @see LIBMTP_new_folder_t()
7131  */
7132 void LIBMTP_destroy_folder_t(LIBMTP_folder_t *folder)
7133 {
7134
7135   if(folder == NULL) {
7136      return;
7137   }
7138
7139   //Destroy from the bottom up
7140   if(folder->child != NULL) {
7141      LIBMTP_destroy_folder_t(folder->child);
7142   }
7143
7144   if(folder->sibling != NULL) {
7145     LIBMTP_destroy_folder_t(folder->sibling);
7146   }
7147
7148   if(folder->name != NULL) {
7149     free(folder->name);
7150   }
7151
7152   free(folder);
7153 }
7154
7155 /**
7156  * Helper function. Returns a folder structure for a
7157  * specified id.
7158  *
7159  * @param folderlist list of folders to search
7160  * @id id of folder to look for
7161  * @return a folder or NULL if not found
7162  */
7163 LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t *folderlist, uint32_t id)
7164 {
7165   LIBMTP_folder_t *ret = NULL;
7166
7167   if(folderlist == NULL) {
7168     return NULL;
7169   }
7170
7171   if(folderlist->folder_id == id) {
7172     return folderlist;
7173   }
7174
7175   if(folderlist->sibling) {
7176     ret = LIBMTP_Find_Folder(folderlist->sibling, id);
7177   }
7178
7179   if(folderlist->child && ret == NULL) {
7180     ret = LIBMTP_Find_Folder(folderlist->child, id);
7181   }
7182
7183   return ret;
7184 }
7185
7186 /**
7187  * Function used to recursively get subfolders from params.
7188  */
7189 static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent)
7190 {
7191   LIBMTP_folder_t *retfolders = NULL;
7192   LIBMTP_folder_t *children, *iter, *curr;
7193
7194   iter = list->sibling;
7195   while(iter != list) {
7196     if (iter->parent_id != parent) {
7197       iter = iter->sibling;
7198       continue;
7199     }
7200
7201     /* We know that iter is a child of 'parent', therefore we can safely
7202      * hold on to 'iter' locally since no one else will steal it
7203      * from the 'list' as we recurse. */
7204     children = get_subfolders_for_folder(list, iter->folder_id);
7205
7206     curr = iter;
7207     iter = iter->sibling;
7208
7209     // Remove curr from the list.
7210     curr->child->sibling = curr->sibling;
7211     curr->sibling->child = curr->child;
7212
7213     // Attach the children to curr.
7214     curr->child = children;
7215
7216     // Put this folder into the list of siblings.
7217     curr->sibling = retfolders;
7218     retfolders = curr;
7219   }
7220
7221   return retfolders;
7222 }
7223
7224 /**
7225  * This returns a list of all folders available
7226  * on the current MTP device.
7227  *
7228  * @param device a pointer to the device to get the folder listing for.
7229  * @param storage a storage ID to get the folder list from
7230  * @return a list of folders
7231  */
7232  LIBMTP_folder_t *LIBMTP_Get_Folder_List_For_Storage(LIBMTP_mtpdevice_t *device,
7233                                                     uint32_t const storage)
7234 {
7235   PTPParams *params = (PTPParams *) device->params;
7236   LIBMTP_folder_t head, *rv;
7237   int i;
7238
7239   // Get all the handles if we haven't already done that
7240   if (params->nrofobjects == 0) {
7241     flush_handles(device);
7242   }
7243
7244   /*
7245    * This creates a temporary list of the folders, this is in a
7246    * reverse order and uses the Folder pointers that are already
7247    * in the Folder structure. From this we can then build up the
7248    * folder hierarchy with only looking at this temporary list,
7249    * and removing the folders from this temporary list as we go.
7250    * This significantly reduces the number of operations that we
7251    * have to do in building the folder hierarchy. Also since the
7252    * temp list is in reverse order, when we prepend to the sibling
7253    * list things are in the same order as they were originally
7254    * in the handle list.
7255    */
7256   head.sibling = &head;
7257   head.child = &head;
7258   for (i = 0; i < params->nrofobjects; i++) {
7259     LIBMTP_folder_t *folder;
7260     PTPObject *ob;
7261
7262     ob = &params->objects[i];
7263     if (ob->oi.ObjectFormat != PTP_OFC_Association) {
7264       continue;
7265     }
7266
7267     if (storage != PTP_GOH_ALL_STORAGE && storage != ob->oi.StorageID) {
7268       continue;
7269     }
7270
7271     /*
7272      * Do we know how to handle these? They are part
7273      * of the MTP 1.0 specification paragraph 3.6.4.
7274      * For AssociationDesc 0x00000001U ptp_mtp_getobjectreferences()
7275      * should be called on these to get the contained objects, but
7276      * we basically don't care. Hopefully parent_id is maintained for all
7277      * children, because we rely on that instead.
7278      */
7279     if (ob->oi.AssociationDesc != 0x00000000U) {
7280       LIBMTP_INFO("MTP extended association type 0x%08x encountered\n", ob->oi.AssociationDesc);
7281     }
7282
7283     // Create a folder struct...
7284     folder = LIBMTP_new_folder_t();
7285     if (folder == NULL) {
7286       // malloc failure or so.
7287       return NULL;
7288     }
7289     folder->folder_id = ob->oid;
7290     folder->parent_id = ob->oi.ParentObject;
7291     folder->storage_id = ob->oi.StorageID;
7292     folder->name = (ob->oi.Filename) ? (char *)strdup(ob->oi.Filename) : NULL;
7293
7294     // pretend sibling says next, and child says prev.
7295     folder->sibling = head.sibling;
7296     folder->child = &head;
7297     head.sibling->child = folder;
7298     head.sibling = folder;
7299   }
7300
7301   // We begin at the given root folder and get them all recursively
7302   rv = get_subfolders_for_folder(&head, 0x00000000U);
7303
7304   // Some buggy devices may have some files in the "root folder"
7305   // 0xffffffff so if 0x00000000 didn't return any folders,
7306   // look for children of the root 0xffffffffU
7307   if (rv == NULL) {
7308     rv = get_subfolders_for_folder(&head, 0xffffffffU);
7309     if (rv != NULL)
7310       LIBMTP_ERROR("Device have files in \"root folder\" 0xffffffffU - "
7311                    "this is a firmware bug (but continuing)\n");
7312   }
7313
7314   // The temp list should be empty. Clean up any orphans just in case.
7315   while(head.sibling != &head) {
7316     LIBMTP_folder_t *curr = head.sibling;
7317
7318     LIBMTP_INFO("Orphan folder with ID: 0x%08x name: \"%s\" encountered.\n",
7319            curr->folder_id,
7320            curr->name);
7321     curr->sibling->child = curr->child;
7322     curr->child->sibling = curr->sibling;
7323     curr->child = NULL;
7324     curr->sibling = NULL;
7325     LIBMTP_destroy_folder_t(curr);
7326   }
7327
7328   return rv;
7329 }
7330
7331 /**
7332  * This returns a list of all folders available
7333  * on the current MTP device.
7334  *
7335  * @param device a pointer to the device to get the folder listing for.
7336  * @return a list of folders
7337  */
7338 LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
7339 {
7340   return LIBMTP_Get_Folder_List_For_Storage(device, PTP_GOH_ALL_STORAGE);
7341 }
7342
7343 /**
7344  * This create a folder on the current MTP device. The PTP name
7345  * for a folder is "association". The PTP/MTP devices does not
7346  * have an internal "folder" concept really, it contains a flat
7347  * list of all files and some file are "associations" that other
7348  * files and folders may refer to as its "parent".
7349  *
7350  * @param device a pointer to the device to create the folder on.
7351  * @param name the name of the new folder. Note this can be modified
7352  *        if the device does not support all the characters in the
7353  *        name.
7354  * @param parent_id id of parent folder to add the new folder to,
7355  *        or 0xFFFFFFFF to put it in the root directory.
7356  * @param storage_id id of the storage to add this new folder to.
7357  *        notice that you cannot mismatch storage id and parent id:
7358  *        they must both be on the same storage! Pass in 0 if you
7359  *        want to create this folder on the default storage.
7360  * @return id to new folder or 0 if an error occured
7361  */
7362 uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name,
7363                               uint32_t parent_id, uint32_t storage_id)
7364 {
7365   PTPParams *params = (PTPParams *) device->params;
7366   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7367   uint32_t parenthandle = 0;
7368   uint32_t store;
7369   PTPObjectInfo new_folder;
7370   uint16_t ret;
7371   uint32_t new_id = 0;
7372
7373   if (storage_id == 0) {
7374     // I'm just guessing that a folder may require 512 bytes
7375     store = get_suggested_storage_id(device, 512, parent_id);
7376   } else {
7377     store = storage_id;
7378   }
7379   parenthandle = parent_id;
7380
7381   memset(&new_folder, 0, sizeof(new_folder));
7382   new_folder.Filename = name;
7383   if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
7384     strip_7bit_from_utf8(new_folder.Filename);
7385   }
7386   new_folder.ObjectCompressedSize = 0;
7387   new_folder.ObjectFormat = PTP_OFC_Association;
7388   new_folder.ProtectionStatus = PTP_PS_NoProtection;
7389   new_folder.AssociationType = PTP_AT_GenericFolder;
7390   new_folder.ParentObject = parent_id;
7391   new_folder.StorageID = store;
7392
7393   // Create the object
7394   if (!(params->device_flags & DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST) &&
7395         ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
7396         MTPProperties *props = (MTPProperties*)calloc(2,sizeof(MTPProperties));
7397    if (props == NULL)
7398      return -1;
7399
7400         props[0].property = PTP_OPC_ObjectFileName;
7401         props[0].datatype = PTP_DTC_STR;
7402         props[0].propval.str = name;
7403
7404         props[1].property = PTP_OPC_Name;
7405         props[1].datatype = PTP_DTC_STR;
7406         props[1].propval.str = name;
7407
7408         ret = ptp_mtp_sendobjectproplist(params, &store, &parenthandle, &new_id, PTP_OFC_Association,
7409                         0, props, 1);
7410         free(props);
7411   } else {
7412         ret = ptp_sendobjectinfo(params, &store, &parenthandle, &new_id, &new_folder);
7413   }
7414
7415   if (ret != PTP_RC_OK) {
7416     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Create_Folder: Could not send object info.");
7417     if (ret == PTP_RC_AccessDenied) {
7418       add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
7419     }
7420     return 0;
7421   }
7422   // NOTE: don't destroy the new_folder objectinfo, because it is statically referencing
7423   // several strings.
7424
7425   add_object_to_cache(device, new_id);
7426
7427   return new_id;
7428 }
7429
7430 /**
7431  * This creates a new playlist metadata structure and allocates memory
7432  * for it. Notice that if you add strings to this structure they
7433  * will be freed by the corresponding <code>LIBMTP_destroy_playlist_t</code>
7434  * operation later, so be careful of using strdup() when assigning
7435  * strings, e.g.:
7436  *
7437  * <pre>
7438  * LIBMTP_playlist_t *pl = LIBMTP_new_playlist_t();
7439  * pl->name = strdup(str);
7440  * ....
7441  * LIBMTP_destroy_playlist_t(pl);
7442  * </pre>
7443  *
7444  * @return a pointer to the newly allocated metadata structure.
7445  * @see LIBMTP_destroy_playlist_t()
7446  */
7447 LIBMTP_playlist_t *LIBMTP_new_playlist_t(void)
7448 {
7449   LIBMTP_playlist_t *new = (LIBMTP_playlist_t *) malloc(sizeof(LIBMTP_playlist_t));
7450   if (new == NULL) {
7451     return NULL;
7452   }
7453   new->playlist_id = 0;
7454   new->parent_id = 0;
7455   new->storage_id = 0;
7456   new->name = NULL;
7457   new->tracks = NULL;
7458   new->no_tracks = 0;
7459   new->next = NULL;
7460   return new;
7461 }
7462
7463 /**
7464  * This destroys a playlist metadata structure and deallocates the memory
7465  * used by it, including any strings. Never use a track metadata
7466  * structure again after calling this function on it.
7467  * @param playlist the playlist metadata to destroy.
7468  * @see LIBMTP_new_playlist_t()
7469  */
7470 void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *playlist)
7471 {
7472   if (playlist == NULL) {
7473     return;
7474   }
7475   if (playlist->name != NULL)
7476     free(playlist->name);
7477   if (playlist->tracks != NULL)
7478     free(playlist->tracks);
7479   free(playlist);
7480   return;
7481 }
7482
7483 /**
7484  * This function returns a list of the playlists available on the
7485  * device. Typical usage:
7486  *
7487  * <pre>
7488  * </pre>
7489  *
7490  * @param device a pointer to the device to get the playlist listing from.
7491  * @return a playlist list on success, else NULL. If there are no playlists
7492  *         on the device, NULL will be returned as well.
7493  * @see LIBMTP_Get_Playlist()
7494  */
7495 LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *device)
7496 {
7497   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7498   const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
7499   PTPParams *params = (PTPParams *) device->params;
7500   LIBMTP_playlist_t *retlists = NULL;
7501   LIBMTP_playlist_t *curlist = NULL;
7502   uint32_t i;
7503
7504   // Get all the handles if we haven't already done that
7505   if (params->nrofobjects == 0) {
7506     flush_handles(device);
7507   }
7508
7509   for (i = 0; i < params->nrofobjects; i++) {
7510     LIBMTP_playlist_t *pl;
7511     PTPObject *ob;
7512     uint16_t ret;
7513
7514     ob = &params->objects[i];
7515
7516     // Ignore stuff that isn't playlists
7517
7518     // For Samsung players we must look for the .spl extension explicitly since
7519     // playlists are not stored as playlist objects.
7520     if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
7521       // Allocate a new playlist type
7522       pl = LIBMTP_new_playlist_t();
7523       spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
7524     }
7525     else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
7526       continue;
7527     }
7528     else {
7529       // Allocate a new playlist type
7530       pl = LIBMTP_new_playlist_t();
7531
7532       // Try to look up proper name, else use the oi->Filename field.
7533       pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
7534       if (pl->name == NULL) {
7535         pl->name = strdup(ob->oi.Filename);
7536       }
7537       pl->playlist_id = ob->oid;
7538       pl->parent_id = ob->oi.ParentObject;
7539       pl->storage_id = ob->oi.StorageID;
7540
7541       // Then get the track listing for this playlist
7542       ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
7543       if (ret != PTP_RC_OK) {
7544         add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist_List(): "
7545                                     "could not get object references.");
7546         pl->tracks = NULL;
7547         pl->no_tracks = 0;
7548       }
7549     }
7550
7551     // Add playlist to a list that will be returned afterwards.
7552     if (retlists == NULL) {
7553       retlists = pl;
7554       curlist = pl;
7555     } else {
7556       curlist->next = pl;
7557       curlist = pl;
7558     }
7559
7560     // Call callback here if we decide to add that possibility...
7561   }
7562   return retlists;
7563 }
7564
7565
7566 /**
7567  * This function retrieves an individual playlist from the device.
7568  * @param device a pointer to the device to get the playlist from.
7569  * @param plid the unique ID of the playlist to retrieve.
7570  * @return a valid playlist metadata post or NULL on failure.
7571  * @see LIBMTP_Get_Playlist_List()
7572  */
7573 LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *device, uint32_t const plid)
7574 {
7575   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7576   const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
7577   PTPParams *params = (PTPParams *) device->params;
7578   PTPObject *ob;
7579   LIBMTP_playlist_t *pl;
7580   uint16_t ret;
7581
7582   // Get all the handles if we haven't already done that
7583   if (params->nrofobjects == 0) {
7584     flush_handles(device);
7585   }
7586
7587   ret = ptp_object_want (params, plid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
7588   if (ret != PTP_RC_OK)
7589     return NULL;
7590
7591   // For Samsung players we must look for the .spl extension explicitly since
7592   // playlists are not stored as playlist objects.
7593   if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
7594     // Allocate a new playlist type
7595     pl = LIBMTP_new_playlist_t();
7596     spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
7597     return pl;
7598   }
7599
7600   // Ignore stuff that isn't playlists
7601   else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
7602     return NULL;
7603   }
7604
7605   // Allocate a new playlist type
7606   pl = LIBMTP_new_playlist_t();
7607
7608   pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
7609   if (pl->name == NULL) {
7610     pl->name = strdup(ob->oi.Filename);
7611   }
7612   pl->playlist_id = ob->oid;
7613   pl->parent_id = ob->oi.ParentObject;
7614   pl->storage_id = ob->oi.StorageID;
7615
7616   // Then get the track listing for this playlist
7617   ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
7618   if (ret != PTP_RC_OK) {
7619     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist(): Could not get object references.");
7620     pl->tracks = NULL;
7621     pl->no_tracks = 0;
7622   }
7623
7624   return pl;
7625 }
7626
7627 /**
7628  * This function creates a new abstract list such as a playlist
7629  * or an album.
7630  *
7631  * @param device a pointer to the device to create the new abstract list
7632  *        on.
7633  * @param name the name of the new abstract list.
7634  * @param artist the artist of the new abstract list or NULL.
7635  * @param genre the genre of the new abstract list or NULL.
7636  * @param parenthandle the handle of the parent or 0 for no parent
7637  *        i.e. the root folder.
7638  * @param objectformat the abstract list type to create.
7639  * @param suffix the ".foo" (4 characters) suffix to use for the virtual
7640  *        "file" created by this operation.
7641  * @param newid a pointer to a variable that will hold the new object
7642  *        ID if this call is successful.
7643  * @param tracks an array of tracks to associate with this list.
7644  * @param no_tracks the number of tracks in the list.
7645  * @return 0 on success, any other value means failure.
7646  */
7647 static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
7648                                     char const * const name,
7649                                     char const * const artist,
7650                                     char const * const composer,
7651                                     char const * const genre,
7652                                     uint32_t const parenthandle,
7653                                     uint32_t const storageid,
7654                                     uint16_t const objectformat,
7655                                     char const * const suffix,
7656                                     uint32_t * const newid,
7657                                     uint32_t const * const tracks,
7658                                     uint32_t const no_tracks)
7659
7660 {
7661   int i;
7662   int supported = 0;
7663   uint16_t ret;
7664   uint16_t *properties = NULL;
7665   uint32_t propcnt = 0;
7666   uint32_t store;
7667   uint32_t localph = parenthandle;
7668   uint8_t nonconsumable = 0x00U; /* By default it is consumable */
7669   PTPParams *params = (PTPParams *) device->params;
7670   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7671   char fname[256];
7672   //uint8_t data[2];
7673
7674   // NULL check
7675   if (!name) {
7676     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): list name was NULL, using default name \"Unknown\"");
7677     return -1;
7678   }
7679
7680   if (storageid == 0) {
7681     // I'm just guessing that an abstract list may require 512 bytes
7682     store = get_suggested_storage_id(device, 512, localph);
7683   } else {
7684     store = storageid;
7685   }
7686
7687   // Check if we can create an object of this type
7688   for ( i=0; i < params->deviceinfo.ImageFormats_len; i++ ) {
7689     if (params->deviceinfo.ImageFormats[i] == objectformat) {
7690       supported = 1;
7691       break;
7692     }
7693   }
7694   if (!supported) {
7695     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): player does not support this abstract type");
7696     LIBMTP_ERROR("Unsupported abstract list type: %04x\n", objectformat);
7697     return -1;
7698   }
7699
7700   // add the new suffix if it isn't there
7701   fname[0] = '\0';
7702   if (strlen(name) > strlen(suffix)) {
7703     char const * const suff = &name[strlen(name)-strlen(suffix)];
7704     if (!strcmp(suff, suffix)) {
7705       // Home free.
7706       strncpy(fname, name, sizeof(fname));
7707     }
7708   }
7709   // If it didn't end with "<suffix>" then add that here.
7710   if (fname[0] == '\0') {
7711     strncpy(fname, name, sizeof(fname)-strlen(suffix)-1);
7712     strcat(fname, suffix);
7713     fname[sizeof(fname)-1] = '\0';
7714   }
7715
7716   if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
7717       !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
7718     MTPProperties *props = NULL;
7719     MTPProperties *prop = NULL;
7720     int nrofprops = 0;
7721
7722     *newid = 0x00000000U;
7723
7724     ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
7725
7726     for (i=0;i<propcnt;i++) {
7727       PTPObjectPropDesc opd;
7728
7729       ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
7730       if (ret != PTP_RC_OK) {
7731         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
7732                                 "could not get property description.");
7733       } else if (opd.GetSet) {
7734         switch (properties[i]) {
7735         case PTP_OPC_ObjectFileName:
7736           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7737           prop->ObjectHandle = *newid;
7738           prop->property = PTP_OPC_ObjectFileName;
7739           prop->datatype = PTP_DTC_STR;
7740           prop->propval.str = strdup(fname);
7741           if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
7742             strip_7bit_from_utf8(prop->propval.str);
7743           }
7744           break;
7745         case PTP_OPC_ProtectionStatus:
7746           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7747           prop->ObjectHandle = *newid;
7748           prop->property = PTP_OPC_ProtectionStatus;
7749           prop->datatype = PTP_DTC_UINT16;
7750           prop->propval.u16 = 0x0000U; /* Not protected */
7751           break;
7752         case PTP_OPC_NonConsumable:
7753           prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7754           prop->ObjectHandle = *newid;
7755           prop->property = PTP_OPC_NonConsumable;
7756           prop->datatype = PTP_DTC_UINT8;
7757           prop->propval.u8 = nonconsumable;
7758           break;
7759         case PTP_OPC_Name:
7760           if (name != NULL) {
7761             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7762             prop->ObjectHandle = *newid;
7763             prop->property = PTP_OPC_Name;
7764             prop->datatype = PTP_DTC_STR;
7765             prop->propval.str = strdup(name);
7766           }
7767           break;
7768         case PTP_OPC_AlbumArtist:
7769           if (artist != NULL) {
7770             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7771             prop->ObjectHandle = *newid;
7772             prop->property = PTP_OPC_AlbumArtist;
7773             prop->datatype = PTP_DTC_STR;
7774             prop->propval.str = strdup(artist);
7775           }
7776           break;
7777         case PTP_OPC_Artist:
7778           if (artist != NULL) {
7779             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7780             prop->ObjectHandle = *newid;
7781             prop->property = PTP_OPC_Artist;
7782             prop->datatype = PTP_DTC_STR;
7783             prop->propval.str = strdup(artist);
7784           }
7785           break;
7786         case PTP_OPC_Composer:
7787           if (composer != NULL) {
7788             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7789             prop->ObjectHandle = *newid;
7790             prop->property = PTP_OPC_Composer;
7791             prop->datatype = PTP_DTC_STR;
7792             prop->propval.str = strdup(composer);
7793           }
7794           break;
7795         case PTP_OPC_Genre:
7796           if (genre != NULL) {
7797             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7798             prop->ObjectHandle = *newid;
7799             prop->property = PTP_OPC_Genre;
7800             prop->datatype = PTP_DTC_STR;
7801             prop->propval.str = strdup(genre);
7802           }
7803           break;
7804         case PTP_OPC_DateModified:
7805           // Tag with current time if that is supported
7806           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
7807             prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7808             prop->ObjectHandle = *newid;
7809             prop->property = PTP_OPC_DateModified;
7810             prop->datatype = PTP_DTC_STR;
7811             prop->propval.str = get_iso8601_stamp();
7812           }
7813           break;
7814         }
7815       }
7816       ptp_free_objectpropdesc(&opd);
7817     }
7818     free(properties);
7819
7820     if (nrofprops == 0 || props == NULL) {
7821         LIBMTP_INFO("prop list doesn't exist");
7822         return -1;
7823     }
7824
7825     ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid,
7826                                      objectformat, 0, props, nrofprops);
7827
7828     /* Free property list */
7829     ptp_destroy_object_prop_list(props, nrofprops);
7830
7831     if (ret != PTP_RC_OK) {
7832       add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list.");
7833       if (ret == PTP_RC_AccessDenied) {
7834         add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
7835       }
7836       return -1;
7837     }
7838
7839     // now send the blank object
7840     ret = ptp_sendobject(params, NULL, 0);
7841     if (ret != PTP_RC_OK) {
7842       add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
7843       return -1;
7844     }
7845
7846   } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
7847     PTPObjectInfo new_object;
7848
7849     new_object.Filename = fname;
7850     if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
7851       strip_7bit_from_utf8(new_object.Filename);
7852     }
7853     // At one point this had to be one
7854     new_object.ObjectCompressedSize = 0;
7855     new_object.ObjectFormat = objectformat;
7856
7857     // Create the object
7858     ret = ptp_sendobjectinfo(params, &store, &localph, newid, &new_object);
7859     if (ret != PTP_RC_OK) {
7860       add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object info (the playlist itself).");
7861       if (ret == PTP_RC_AccessDenied) {
7862         add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
7863       }
7864       return -1;
7865     }
7866     // NOTE: don't destroy new_object objectinfo afterwards - the strings it contains are
7867     // not copies.
7868
7869 #if 0
7870     /*
7871      * At one time we had to send this one blank data byte.
7872      * If we didn't, the handle will not be created and thus there is
7873      * no playlist. Possibly this was masking some bug, so removing it
7874      * now.
7875      */
7876     data[0] = '\0';
7877     data[1] = '\0';
7878     ret = ptp_sendobject(params, data, 1);
7879     if (ret != PTP_RC_OK) {
7880       add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
7881       return -1;
7882     }
7883 #endif
7884
7885     // set the properties one by one
7886     ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
7887
7888     for (i=0;i<propcnt;i++) {
7889       PTPObjectPropDesc opd;
7890
7891       ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
7892       if (ret != PTP_RC_OK) {
7893         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
7894                                 "could not get property description.");
7895       } else if (opd.GetSet) {
7896         switch (properties[i]) {
7897         case PTP_OPC_Name:
7898           if (name != NULL) {
7899             ret = set_object_string(device, *newid, PTP_OPC_Name, name);
7900             if (ret != 0) {
7901               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity name.");
7902               return -1;
7903             }
7904           }
7905           break;
7906         case PTP_OPC_AlbumArtist:
7907           if (artist != NULL) {
7908             ret = set_object_string(device, *newid, PTP_OPC_AlbumArtist, artist);
7909             if (ret != 0) {
7910               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity album artist.");
7911               return -1;
7912             }
7913           }
7914           break;
7915         case PTP_OPC_Artist:
7916           if (artist != NULL) {
7917             ret = set_object_string(device, *newid, PTP_OPC_Artist, artist);
7918             if (ret != 0) {
7919               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity artist.");
7920               return -1;
7921             }
7922           }
7923           break;
7924         case PTP_OPC_Composer:
7925           if (composer != NULL) {
7926             ret = set_object_string(device, *newid, PTP_OPC_Composer, composer);
7927             if (ret != 0) {
7928               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity composer.");
7929               return -1;
7930             }
7931           }
7932           break;
7933         case PTP_OPC_Genre:
7934           if (genre != NULL) {
7935             ret = set_object_string(device, *newid, PTP_OPC_Genre, genre);
7936             if (ret != 0) {
7937               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity genre.");
7938               return -1;
7939             }
7940           }
7941           break;
7942         case PTP_OPC_DateModified:
7943           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
7944             ret = set_object_string(device, *newid, PTP_OPC_DateModified, get_iso8601_stamp());
7945             if (ret != 0) {
7946               add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set date modified.");
7947               return -1;
7948             }
7949           }
7950           break;
7951         }
7952       }
7953       ptp_free_objectpropdesc(&opd);
7954     }
7955     free(properties);
7956   }
7957
7958   if (no_tracks > 0) {
7959     // Add tracks to the list as object references.
7960     ret = ptp_mtp_setobjectreferences (params, *newid, (uint32_t *) tracks, no_tracks);
7961     if (ret != PTP_RC_OK) {
7962       add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): could not add tracks as object references.");
7963       return -1;
7964     }
7965   }
7966
7967   add_object_to_cache(device, *newid);
7968
7969   return 0;
7970 }
7971
7972 /**
7973  * This updates the metadata and track listing
7974  * for an abstract list.
7975  * @param device a pointer to the device that the abstract list
7976  *        resides on.
7977  * @param name the name of the abstract list.
7978  * @param artist the artist of the abstract list or NULL.
7979  * @param genre the genre of the abstract list or NULL.
7980  * @param objecthandle the object to be updated.
7981  * @param objectformat the abstract list type to update.
7982  * @param tracks an array of tracks to associate with this list.
7983  * @param no_tracks the number of tracks in the list.
7984  * @return 0 on success, any other value means failure.
7985  */
7986 static int update_abstract_list(LIBMTP_mtpdevice_t *device,
7987                                 char const * const name,
7988                                 char const * const artist,
7989                                 char const * const composer,
7990                                 char const * const genre,
7991                                 uint32_t const objecthandle,
7992                                 uint16_t const objectformat,
7993                                 uint32_t const * const tracks,
7994                                 uint32_t const no_tracks)
7995 {
7996   uint16_t ret;
7997   PTPParams *params = (PTPParams *) device->params;
7998   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7999   uint16_t *properties = NULL;
8000   uint32_t propcnt = 0;
8001   int i;
8002
8003   // First see which properties can be set
8004   // i.e only try to update this metadata for object tags that exist on the current player.
8005   ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
8006   if (ret != PTP_RC_OK) {
8007     // Just bail out for now, nothing is ever set.
8008     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8009                             "could not retrieve supported object properties.");
8010     return -1;
8011   }
8012   if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList) &&
8013       !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
8014     MTPProperties *props = NULL;
8015     MTPProperties *prop = NULL;
8016     int nrofprops = 0;
8017
8018     for (i=0;i<propcnt;i++) {
8019       PTPObjectPropDesc opd;
8020
8021       ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
8022       if (ret != PTP_RC_OK) {
8023         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8024                                 "could not get property description.");
8025       } else if (opd.GetSet) {
8026         switch (properties[i]) {
8027         case PTP_OPC_Name:
8028           prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8029           prop->ObjectHandle = objecthandle;
8030           prop->property = PTP_OPC_Name;
8031           prop->datatype = PTP_DTC_STR;
8032           if (name != NULL)
8033             prop->propval.str = strdup(name);
8034           break;
8035         case PTP_OPC_AlbumArtist:
8036           if (artist != NULL) {
8037             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8038             prop->ObjectHandle = objecthandle;
8039             prop->property = PTP_OPC_AlbumArtist;
8040             prop->datatype = PTP_DTC_STR;
8041             prop->propval.str = strdup(artist);
8042           }
8043           break;
8044         case PTP_OPC_Artist:
8045           if (artist != NULL) {
8046             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8047             prop->ObjectHandle = objecthandle;
8048             prop->property = PTP_OPC_Artist;
8049             prop->datatype = PTP_DTC_STR;
8050             prop->propval.str = strdup(artist);
8051           }
8052           break;
8053         case PTP_OPC_Composer:
8054           if (composer != NULL) {
8055             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8056             prop->ObjectHandle = objecthandle;
8057             prop->property = PTP_OPC_Composer;
8058             prop->datatype = PTP_DTC_STR;
8059             prop->propval.str = strdup(composer);
8060           }
8061           break;
8062         case PTP_OPC_Genre:
8063           if (genre != NULL) {
8064             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8065             prop->ObjectHandle = objecthandle;
8066             prop->property = PTP_OPC_Genre;
8067             prop->datatype = PTP_DTC_STR;
8068             prop->propval.str = strdup(genre);
8069           }
8070           break;
8071         case PTP_OPC_DateModified:
8072           if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
8073             // Tag with current time if that is supported
8074             prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8075             prop->ObjectHandle = objecthandle;
8076             prop->property = PTP_OPC_DateModified;
8077             prop->datatype = PTP_DTC_STR;
8078             prop->propval.str = get_iso8601_stamp();
8079           }
8080           break;
8081         default:
8082           break;
8083         }
8084       }
8085       ptp_free_objectpropdesc(&opd);
8086     }
8087
8088     // proplist could be NULL if we can't write any properties
8089     if (props != NULL) {
8090       ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
8091
8092       ptp_destroy_object_prop_list(props, nrofprops);
8093
8094       if (ret != PTP_RC_OK) {
8095         // TODO: return error of which property we couldn't set
8096         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8097                                 "could not set object property list.");
8098         free(properties);
8099         return -1;
8100       }
8101     }
8102
8103   } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
8104     for (i=0;i<propcnt;i++) {
8105       switch (properties[i]) {
8106       case PTP_OPC_Name:
8107         // Update title
8108         ret = set_object_string(device, objecthandle, PTP_OPC_Name, name);
8109         if (ret != 0) {
8110           add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8111                                   "could not set title.");
8112         }
8113         break;
8114       case PTP_OPC_AlbumArtist:
8115         // Update album artist
8116         ret = set_object_string(device, objecthandle, PTP_OPC_AlbumArtist, artist);
8117         if (ret != 0) {
8118           add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8119                                   "could not set album artist name.");
8120         }
8121         break;
8122       case PTP_OPC_Artist:
8123         // Update artist
8124         ret = set_object_string(device, objecthandle, PTP_OPC_Artist, artist);
8125         if (ret != 0) {
8126           add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8127                                   "could not set artist name.");
8128         }
8129         break;
8130       case PTP_OPC_Composer:
8131         // Update composer
8132         ret = set_object_string(device, objecthandle, PTP_OPC_Composer, composer);
8133         if (ret != 0) {
8134           add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8135                                   "could not set composer name.");
8136         }
8137         break;
8138       case PTP_OPC_Genre:
8139         // Update genre (but only if valid)
8140         if(genre) {
8141           ret = set_object_string(device, objecthandle, PTP_OPC_Genre, genre);
8142           if (ret != 0) {
8143             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8144                                     "could not set genre.");
8145           }
8146         }
8147         break;
8148       case PTP_OPC_DateModified:
8149         // Update date modified
8150         if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
8151           char *tmpdate = get_iso8601_stamp();
8152           ret = set_object_string(device, objecthandle, PTP_OPC_DateModified, tmpdate);
8153           if (ret != 0) {
8154             add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8155                                     "could not set modification date.");
8156           }
8157           free(tmpdate);
8158         }
8159         break;
8160       default:
8161         break;
8162       }
8163     }
8164   } else {
8165     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8166                             "Your device doesn't seem to support any known way of setting metadata.");
8167     free(properties);
8168     return -1;
8169   }
8170
8171   // Then the object references...
8172   ret = ptp_mtp_setobjectreferences (params, objecthandle, (uint32_t *) tracks, no_tracks);
8173   if (ret != PTP_RC_OK) {
8174     add_ptp_error_to_errorstack(device, ret, "update_abstract_list(): could not add tracks as object references.");
8175     free(properties);
8176     return -1;
8177   }
8178
8179   free(properties);
8180
8181   update_metadata_cache(device, objecthandle);
8182
8183   return 0;
8184 }
8185
8186
8187 /**
8188  * This routine creates a new playlist based on the metadata
8189  * supplied. If the <code>tracks</code> field of the metadata
8190  * contains a track listing, these tracks will be added to the
8191  * playlist.
8192  * @param device a pointer to the device to create the new playlist on.
8193  * @param metadata the metadata for the new playlist. If the function
8194  *        exits with success, the <code>playlist_id</code> field of this
8195  *        struct will contain the new playlist ID of the playlist.
8196  *        <ul>
8197  *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
8198  *        (e.g. folder) to store this track in. Since some
8199  *        devices are a bit picky about where files
8200  *        are placed, a default folder will be chosen if libmtp
8201  *        has detected one for the current filetype and this
8202  *        parameter is set to 0. If this is 0 and no default folder
8203  *        can be found, the file will be stored in the root folder.
8204  *        <li><code>metadata-&gt;storage_id</code> should be set to the
8205  *        desired storage (e.g. memory card or whatever your device
8206  *        presents) to store this track in. Setting this to 0 will store
8207  *        the track on the primary storage.
8208  *        </ul>
8209  * @return 0 on success, any other value means failure.
8210  * @see LIBMTP_Update_Playlist()
8211  * @see LIBMTP_Delete_Object()
8212  */
8213 int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *device,
8214                                LIBMTP_playlist_t * const metadata)
8215 {
8216   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
8217   uint32_t localph = metadata->parent_id;
8218
8219   // Use a default folder if none given
8220   if (localph == 0) {
8221     if (device->default_playlist_folder != 0)
8222       localph = device->default_playlist_folder;
8223     else
8224       localph = device->default_music_folder;
8225   }
8226   metadata->parent_id = localph;
8227
8228   // Samsung needs its own special type of playlists
8229   if(FLAG_PLAYLIST_SPL(ptp_usb)) {
8230     return playlist_t_to_spl(device, metadata);
8231   }
8232
8233   // Just create a new abstract audio/video playlist...
8234   return create_new_abstract_list(device,
8235                                   metadata->name,
8236                                   NULL,
8237                                   NULL,
8238                                   NULL,
8239                                   localph,
8240                                   metadata->storage_id,
8241                                   PTP_OFC_MTP_AbstractAudioVideoPlaylist,
8242                                   get_playlist_extension(ptp_usb),
8243                                   &metadata->playlist_id,
8244                                   metadata->tracks,
8245                                   metadata->no_tracks);
8246 }
8247
8248 /**
8249  * This routine updates a playlist based on the metadata
8250  * supplied. If the <code>tracks</code> field of the metadata
8251  * contains a track listing, these tracks will be added to the
8252  * playlist in place of those already present, i.e. the
8253  * previous track listing will be deleted. For Samsung devices the
8254  * playlist id (metadata->playlist_id) is likely to change.
8255  * @param device a pointer to the device to create the new playlist on.
8256  * @param metadata the metadata for the playlist to be updated.
8257  *                 notice that the field <code>playlist_id</code>
8258  *                 must contain the apropriate playlist ID. Playlist ID
8259  *                 be modified to a new playlist ID by the time the
8260  *                 function returns since edit-in-place is not always possible.
8261  * @return 0 on success, any other value means failure.
8262  * @see LIBMTP_Create_New_Playlist()
8263  * @see LIBMTP_Delete_Object()
8264  */
8265 int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device,
8266                            LIBMTP_playlist_t * const metadata)
8267 {
8268
8269   // Samsung needs its own special type of playlists
8270   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
8271   if(FLAG_PLAYLIST_SPL(ptp_usb)) {
8272     return update_spl_playlist(device, metadata);
8273   }
8274
8275   return update_abstract_list(device,
8276                               metadata->name,
8277                               NULL,
8278                               NULL,
8279                               NULL,
8280                               metadata->playlist_id,
8281                               PTP_OFC_MTP_AbstractAudioVideoPlaylist,
8282                               metadata->tracks,
8283                               metadata->no_tracks);
8284 }
8285
8286 /**
8287  * This creates a new album metadata structure and allocates memory
8288  * for it. Notice that if you add strings to this structure they
8289  * will be freed by the corresponding <code>LIBMTP_destroy_album_t</code>
8290  * operation later, so be careful of using strdup() when assigning
8291  * strings.
8292  *
8293  * @return a pointer to the newly allocated metadata structure.
8294  * @see LIBMTP_destroy_album_t()
8295  */
8296 LIBMTP_album_t *LIBMTP_new_album_t(void)
8297 {
8298   LIBMTP_album_t *new = (LIBMTP_album_t *) malloc(sizeof(LIBMTP_album_t));
8299   if (new == NULL) {
8300     return NULL;
8301   }
8302   new->album_id = 0;
8303   new->parent_id = 0;
8304   new->storage_id = 0;
8305   new->name = NULL;
8306   new->artist = NULL;
8307   new->composer = NULL;
8308   new->genre = NULL;
8309   new->tracks = NULL;
8310   new->no_tracks = 0;
8311   new->next = NULL;
8312   return new;
8313 }
8314
8315 /**
8316  * This recursively deletes the memory for an album structure
8317  *
8318  * @param album structure to destroy
8319  * @see LIBMTP_new_album_t()
8320  */
8321 void LIBMTP_destroy_album_t(LIBMTP_album_t *album)
8322 {
8323   if (album == NULL) {
8324     return;
8325   }
8326   if (album->name != NULL)
8327     free(album->name);
8328   if (album->artist != NULL)
8329     free(album->artist);
8330   if (album->composer != NULL)
8331     free(album->composer);
8332   if (album->genre != NULL)
8333     free(album->genre);
8334   if (album->tracks != NULL)
8335     free(album->tracks);
8336   free(album);
8337   return;
8338 }
8339
8340 /**
8341  * This function maps and copies a property onto the album metadata if applicable.
8342  */
8343 static void pick_property_to_album_metadata(LIBMTP_mtpdevice_t *device,
8344                                             MTPProperties *prop, LIBMTP_album_t *alb)
8345 {
8346   switch (prop->property) {
8347   case PTP_OPC_Name:
8348     if (prop->propval.str != NULL)
8349       alb->name = strdup(prop->propval.str);
8350     else
8351       alb->name = NULL;
8352     break;
8353   case PTP_OPC_AlbumArtist:
8354     if (prop->propval.str != NULL) {
8355       // This should take precedence over plain "Artist"
8356       if (alb->artist != NULL)
8357         free(alb->artist);
8358       alb->artist = strdup(prop->propval.str);
8359     } else
8360       alb->artist = NULL;
8361     break;
8362   case PTP_OPC_Artist:
8363     if (prop->propval.str != NULL) {
8364       // Only use of AlbumArtist is not set
8365       if (alb->artist == NULL)
8366         alb->artist = strdup(prop->propval.str);
8367     } else
8368       alb->artist = NULL;
8369     break;
8370   case PTP_OPC_Composer:
8371     if (prop->propval.str != NULL)
8372       alb->composer = strdup(prop->propval.str);
8373     else
8374       alb->composer = NULL;
8375     break;
8376   case PTP_OPC_Genre:
8377     if (prop->propval.str != NULL)
8378       alb->genre = strdup(prop->propval.str);
8379     else
8380       alb->genre = NULL;
8381     break;
8382   }
8383 }
8384
8385 /**
8386  * This function retrieves the album metadata for an album
8387  * given by a unique ID.
8388  * @param device a pointer to the device to get the track metadata off.
8389  * @param alb an album metadata metadata set to fill in.
8390  */
8391 static void get_album_metadata(LIBMTP_mtpdevice_t *device,
8392                                LIBMTP_album_t *alb)
8393 {
8394   uint16_t ret;
8395   PTPParams *params = (PTPParams *) device->params;
8396   uint32_t i;
8397   MTPProperties *prop;
8398   PTPObject *ob;
8399
8400   /*
8401    * If we have a cached, large set of metadata, then use it!
8402    */
8403   ret = ptp_object_want(params, alb->album_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
8404   if (ob->mtpprops) {
8405     prop = ob->mtpprops;
8406     for (i=0;i<ob->nrofmtpprops;i++,prop++)
8407       pick_property_to_album_metadata(device, prop, alb);
8408   } else {
8409     uint16_t *props = NULL;
8410     uint32_t propcnt = 0;
8411
8412     // First see which properties can be retrieved for albums
8413     ret = ptp_mtp_getobjectpropssupported(params, PTP_OFC_MTP_AbstractAudioAlbum, &propcnt, &props);
8414     if (ret != PTP_RC_OK) {
8415       add_ptp_error_to_errorstack(device, ret, "get_album_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
8416       // Just bail out for now, nothing is ever set.
8417       return;
8418     } else {
8419       for (i=0;i<propcnt;i++) {
8420         switch (props[i]) {
8421         case PTP_OPC_Name:
8422           alb->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
8423           break;
8424         case PTP_OPC_AlbumArtist:
8425           if (alb->artist != NULL)
8426             free(alb->artist);
8427           alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_AlbumArtist);
8428           break;
8429         case PTP_OPC_Artist:
8430           if (alb->artist == NULL)
8431             alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_Artist);
8432           break;
8433         case PTP_OPC_Composer:
8434           alb->composer = get_string_from_object(device, ob->oid, PTP_OPC_Composer);
8435           break;
8436         case PTP_OPC_Genre:
8437           alb->genre = get_string_from_object(device, ob->oid, PTP_OPC_Genre);
8438           break;
8439         default:
8440           break;
8441         }
8442       }
8443       free(props);
8444     }
8445   }
8446 }
8447
8448
8449 /**
8450  * This function returns a list of the albums available on the
8451  * device.
8452  *
8453  * @param device a pointer to the device to get the album listing from.
8454  * @return an album list on success, else NULL. If there are no albums
8455  *         on the device, NULL will be returned as well.
8456  * @see LIBMTP_Get_Album()
8457  */
8458 LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *device)
8459 {
8460         // Read all storage devices
8461         return LIBMTP_Get_Album_List_For_Storage(device, 0);
8462 }
8463
8464
8465 /**
8466  * This function returns a list of the albums available on the
8467  * device. You can filter on the storage ID.
8468  *
8469  * @param device a pointer to the device to get the album listing from.
8470  * @param storage_id ID of device storage (if null, all storages)
8471  *
8472  * @return an album list on success, else NULL. If there are no albums
8473  *         on the device, NULL will be returned as well.
8474  * @see LIBMTP_Get_Album()
8475  */
8476 LIBMTP_album_t *LIBMTP_Get_Album_List_For_Storage(LIBMTP_mtpdevice_t *device, uint32_t const storage_id)
8477 {
8478   PTPParams *params = (PTPParams *) device->params;
8479   LIBMTP_album_t *retalbums = NULL;
8480   LIBMTP_album_t *curalbum = NULL;
8481   uint32_t i;
8482
8483   // Get all the handles if we haven't already done that
8484   if (params->nrofobjects == 0)
8485     flush_handles(device);
8486
8487   for (i = 0; i < params->nrofobjects; i++) {
8488     LIBMTP_album_t *alb;
8489     PTPObject *ob;
8490     uint16_t ret;
8491
8492     ob = &params->objects[i];
8493
8494     // Ignore stuff that isn't an album
8495     if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum )
8496       continue;
8497
8498         // Ignore stuff that isn't into the storage device
8499         if ((storage_id != 0) && (ob->oi.StorageID != storage_id ))
8500                 continue;
8501
8502     // Allocate a new album type
8503     alb = LIBMTP_new_album_t();
8504     alb->album_id = ob->oid;
8505     alb->parent_id = ob->oi.ParentObject;
8506     alb->storage_id = ob->oi.StorageID;
8507
8508     // Fetch supported metadata
8509     get_album_metadata(device, alb);
8510
8511     // Then get the track listing for this album
8512     ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
8513     if (ret != PTP_RC_OK) {
8514       add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Could not get object references.");
8515       alb->tracks = NULL;
8516       alb->no_tracks = 0;
8517     }
8518
8519     // Add album to a list that will be returned afterwards.
8520     if (retalbums == NULL) {
8521       retalbums = alb;
8522       curalbum = alb;
8523     } else {
8524       curalbum->next = alb;
8525       curalbum = alb;
8526     }
8527
8528   }
8529   return retalbums;
8530 }
8531
8532 /**
8533  * This function retrieves an individual album from the device.
8534  * @param device a pointer to the device to get the album from.
8535  * @param albid the unique ID of the album to retrieve.
8536  * @return a valid album metadata or NULL on failure.
8537  * @see LIBMTP_Get_Album_List()
8538  */
8539 LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *device, uint32_t const albid)
8540 {
8541   PTPParams *params = (PTPParams *) device->params;
8542   uint16_t ret;
8543   PTPObject *ob;
8544   LIBMTP_album_t *alb;
8545
8546   // Get all the handles if we haven't already done that
8547   if (params->nrofobjects == 0)
8548     flush_handles(device);
8549
8550   ret = ptp_object_want(params, albid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
8551   if (ret != PTP_RC_OK)
8552     return NULL;
8553
8554   // Ignore stuff that isn't an album
8555   if (ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum)
8556     return NULL;
8557
8558   // Allocate a new album type
8559   alb = LIBMTP_new_album_t();
8560   alb->album_id = ob->oid;
8561   alb->parent_id = ob->oi.ParentObject;
8562   alb->storage_id = ob->oi.StorageID;
8563
8564   // Fetch supported metadata
8565   get_album_metadata(device, alb);
8566
8567   // Then get the track listing for this album
8568   ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
8569   if (ret != PTP_RC_OK) {
8570     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album: Could not get object references.");
8571     alb->tracks = NULL;
8572     alb->no_tracks = 0;
8573   }
8574
8575   return alb;
8576 }
8577
8578 /**
8579  * This routine creates a new album based on the metadata
8580  * supplied. If the <code>tracks</code> field of the metadata
8581  * contains a track listing, these tracks will be added to the
8582  * album.
8583  * @param device a pointer to the device to create the new album on.
8584  * @param metadata the metadata for the new album. If the function
8585  *        exits with success, the <code>album_id</code> field of this
8586  *        struct will contain the new ID of the album.
8587  *        <ul>
8588  *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
8589  *        (e.g. folder) to store this track in. Since some
8590  *        devices are a bit picky about where files
8591  *        are placed, a default folder will be chosen if libmtp
8592  *        has detected one for the current filetype and this
8593  *        parameter is set to 0. If this is 0 and no default folder
8594  *        can be found, the file will be stored in the root folder.
8595  *        <li><code>metadata-&gt;storage_id</code> should be set to the
8596  *        desired storage (e.g. memory card or whatever your device
8597  *        presents) to store this track in. Setting this to 0 will store
8598  *        the track on the primary storage.
8599  *        </ul>
8600  * @return 0 on success, any other value means failure.
8601  * @see LIBMTP_Update_Album()
8602  * @see LIBMTP_Delete_Object()
8603  */
8604 int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *device,
8605                             LIBMTP_album_t * const metadata)
8606 {
8607   uint32_t localph = metadata->parent_id;
8608
8609   // Use a default folder if none given
8610   if (localph == 0) {
8611     if (device->default_album_folder != 0)
8612       localph = device->default_album_folder;
8613     else
8614       localph = device->default_music_folder;
8615   }
8616   metadata->parent_id = localph;
8617
8618   // Just create a new abstract album...
8619   return create_new_abstract_list(device,
8620                                   metadata->name,
8621                                   metadata->artist,
8622                                   metadata->composer,
8623                                   metadata->genre,
8624                                   localph,
8625                                   metadata->storage_id,
8626                                   PTP_OFC_MTP_AbstractAudioAlbum,
8627                                   ".alb",
8628                                   &metadata->album_id,
8629                                   metadata->tracks,
8630                                   metadata->no_tracks);
8631 }
8632
8633 /**
8634  * This creates a new sample data metadata structure and allocates memory
8635  * for it. Notice that if you add strings to this structure they
8636  * will be freed by the corresponding <code>LIBMTP_destroy_sampledata_t</code>
8637  * operation later, so be careful of using strdup() when assigning
8638  * strings.
8639  *
8640  * @return a pointer to the newly allocated metadata structure.
8641  * @see LIBMTP_destroy_sampledata_t()
8642  */
8643 LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void)
8644 {
8645   LIBMTP_filesampledata_t *new = (LIBMTP_filesampledata_t *) malloc(sizeof(LIBMTP_filesampledata_t));
8646   if (new == NULL) {
8647     return NULL;
8648   }
8649   new->height=0;
8650   new->width = 0;
8651   new->data = NULL;
8652   new->duration = 0;
8653   new->size = 0;
8654   return new;
8655 }
8656
8657 /**
8658  * This destroys a file sample metadata type.
8659  * @param sample the file sample metadata to be destroyed.
8660  */
8661 void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t * sample)
8662 {
8663   if (sample == NULL) {
8664     return;
8665   }
8666   if (sample->data != NULL) {
8667     free(sample->data);
8668   }
8669   free(sample);
8670 }
8671
8672 /**
8673  * This routine figures out whether a certain filetype supports
8674  * representative samples (small thumbnail images) or not. This
8675  * typically applies to JPEG files, MP3 files and Album abstract
8676  * playlists, but in theory any filetype could support representative
8677  * samples.
8678  * @param device a pointer to the device which is to be examined.
8679  * @param filetype the fileype to examine, and return the representative sample
8680  *        properties for.
8681  * @param sample this will contain a new sample type with the fields
8682  *        filled in with suitable default values. For example, the
8683  *        supported sample type will be set, the supported height and
8684  *        width will be set to max values if it is an image sample,
8685  *        and duration will also be given some suitable default value
8686  *        which should not be exceeded on audio samples. If the
8687  *        device does not support samples for this filetype, this
8688  *        pointer will be NULL. If it is not NULL, the user must
8689  *        destroy this struct with <code>LIBMTP_destroy_filesampledata_t()</code>
8690  *        after use.
8691  * @return 0 on success, any other value means failure.
8692  * @see LIBMTP_Send_Representative_Sample()
8693  * @see LIBMTP_Create_New_Album()
8694  */
8695 int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *device,
8696                                             LIBMTP_filetype_t const filetype,
8697                                             LIBMTP_filesampledata_t ** sample)
8698 {
8699   uint16_t ret;
8700   PTPParams *params = (PTPParams *) device->params;
8701   uint16_t *props = NULL;
8702   uint32_t propcnt = 0;
8703   int i;
8704   // TODO: Get rid of these when we can properly query the device.
8705   int support_data = 0;
8706   int support_format = 0;
8707   int support_height = 0;
8708   int support_width = 0;
8709   int support_duration = 0;
8710   int support_size = 0;
8711
8712   PTPObjectPropDesc opd_height;
8713   PTPObjectPropDesc opd_width;
8714   PTPObjectPropDesc opd_format;
8715   PTPObjectPropDesc opd_duration;
8716   PTPObjectPropDesc opd_size;
8717
8718   // Default to no type supported.
8719   *sample = NULL;
8720
8721   ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
8722   if (ret != PTP_RC_OK) {
8723     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample_Format(): could not get object properties.");
8724     return -1;
8725   }
8726   /*
8727    * TODO: when walking through these object properties, make calls to
8728    * a new function in ptp.h/ptp.c that can send the command
8729    * PTP_OC_MTP_GetObjectPropDesc to get max/min values of the properties
8730    * supported.
8731    */
8732   for (i = 0; i < propcnt; i++) {
8733     switch(props[i]) {
8734     case PTP_OPC_RepresentativeSampleData:
8735       support_data = 1;
8736       break;
8737     case PTP_OPC_RepresentativeSampleFormat:
8738       support_format = 1;
8739       break;
8740     case PTP_OPC_RepresentativeSampleSize:
8741       support_size = 1;
8742       break;
8743     case PTP_OPC_RepresentativeSampleHeight:
8744       support_height = 1;
8745       break;
8746     case PTP_OPC_RepresentativeSampleWidth:
8747       support_width = 1;
8748       break;
8749     case PTP_OPC_RepresentativeSampleDuration:
8750       support_duration = 1;
8751       break;
8752     default:
8753       break;
8754     }
8755   }
8756   free(props);
8757
8758   if (support_data && support_format && support_height && support_width && !support_duration) {
8759     // Something that supports height and width and not duration is likely to be JPEG
8760     LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
8761     /*
8762      * Populate the sample format with the first supported format
8763      *
8764      * TODO: figure out how to pass back more than one format if more are
8765      * supported by the device.
8766      */
8767     ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);
8768     retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
8769     ptp_free_objectpropdesc(&opd_format);
8770     /* Populate the maximum image height */
8771     ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleWidth, map_libmtp_type_to_ptp_type(filetype), &opd_width);
8772     retsam->width = opd_width.FORM.Range.MaximumValue.u32;
8773     ptp_free_objectpropdesc(&opd_width);
8774     /* Populate the maximum image width */
8775     ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleHeight, map_libmtp_type_to_ptp_type(filetype), &opd_height);
8776     retsam->height = opd_height.FORM.Range.MaximumValue.u32;
8777     ptp_free_objectpropdesc(&opd_height);
8778     /* Populate the maximum size */
8779     if (support_size) {
8780       ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
8781       retsam->size = opd_size.FORM.Range.MaximumValue.u32;
8782       ptp_free_objectpropdesc(&opd_size);
8783     }
8784     *sample = retsam;
8785   } else if (support_data && support_format && !support_height && !support_width && support_duration) {
8786     // Another qualified guess
8787     LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
8788     /*
8789      * Populate the sample format with the first supported format
8790      *
8791      * TODO: figure out how to pass back more than one format if more are
8792      * supported by the device.
8793      */
8794     ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);
8795     retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
8796     ptp_free_objectpropdesc(&opd_format);
8797     /* Populate the maximum duration */
8798     ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleDuration, map_libmtp_type_to_ptp_type(filetype), &opd_duration);
8799     retsam->duration = opd_duration.FORM.Range.MaximumValue.u32;
8800     ptp_free_objectpropdesc(&opd_duration);
8801     /* Populate the maximum size */
8802     if (support_size) {
8803       ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
8804       retsam->size = opd_size.FORM.Range.MaximumValue.u32;
8805       ptp_free_objectpropdesc(&opd_size);
8806     }
8807     *sample = retsam;
8808   }
8809   return 0;
8810 }
8811
8812 /**
8813  * This routine sends representative sample data for an object.
8814  * This uses the RepresentativeSampleData property of the album,
8815  * if the device supports it. The data should be of a format acceptable
8816  * to the player (for iRiver and Creative, this seems to be JPEG) and
8817  * must not be too large. (for a Creative, max seems to be about 20KB.)
8818  * Check by calling LIBMTP_Get_Representative_Sample_Format() to get
8819  * maximum size, dimensions, etc..
8820  * @param device a pointer to the device which the object is on.
8821  * @param id unique id of the object to set artwork for.
8822  * @param pointer to LIBMTP_filesampledata_t struct containing data
8823  * @return 0 on success, any other value means failure.
8824  * @see LIBMTP_Get_Representative_Sample()
8825  * @see LIBMTP_Get_Representative_Sample_Format()
8826  * @see LIBMTP_Create_New_Album()
8827  */
8828 int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *device,
8829                           uint32_t const id,
8830                           LIBMTP_filesampledata_t *sampledata)
8831 {
8832   uint16_t ret;
8833   PTPParams *params = (PTPParams *) device->params;
8834   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
8835   PTPPropertyValue propval;
8836   PTPObject *ob;
8837   uint32_t i;
8838   uint16_t *props = NULL;
8839   uint32_t propcnt = 0;
8840   int supported = 0;
8841
8842   // get the file format for the object we're going to send representative data for
8843   ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
8844   if (ret != PTP_RC_OK) {
8845     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): could not get object info.");
8846     return -1;
8847   }
8848
8849   // check that we can send representative sample data for this object format
8850   ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
8851   if (ret != PTP_RC_OK) {
8852     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object properties.");
8853     return -1;
8854   }
8855
8856   for (i = 0; i < propcnt; i++) {
8857     if (props[i] == PTP_OPC_RepresentativeSampleData) {
8858       supported = 1;
8859       break;
8860     }
8861   }
8862   if (!supported) {
8863     free(props);
8864     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
8865     return -1;
8866   }
8867   free(props);
8868
8869   // Go ahead and send the data
8870   propval.a.count = sampledata->size;
8871   propval.a.v = malloc(sizeof(PTPPropertyValue) * sampledata->size);
8872   if (propval.a.v == NULL)
8873       return -1;
8874
8875   for (i = 0; i < sampledata->size; i++) {
8876     propval.a.v[i].u8 = sampledata->data[i];
8877   }
8878
8879   ret = ptp_mtp_setobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
8880                                    &propval,PTP_DTC_AUINT8);
8881   if (ret != PTP_RC_OK) {
8882     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not send sample data.");
8883     free(propval.a.v);
8884     return -1;
8885   }
8886   free(propval.a.v);
8887
8888   /* Set the height and width if the sample is an image, otherwise just
8889    * set the duration and size */
8890   switch(sampledata->filetype) {
8891   case LIBMTP_FILETYPE_JPEG:
8892   case LIBMTP_FILETYPE_JFIF:
8893   case LIBMTP_FILETYPE_TIFF:
8894   case LIBMTP_FILETYPE_BMP:
8895   case LIBMTP_FILETYPE_GIF:
8896   case LIBMTP_FILETYPE_PICT:
8897   case LIBMTP_FILETYPE_PNG:
8898     if (!FLAG_BROKEN_SET_SAMPLE_DIMENSIONS(ptp_usb)) {
8899       // For images, set the height and width
8900       set_object_u32(device, id, PTP_OPC_RepresentativeSampleHeight, sampledata->height);
8901       set_object_u32(device, id, PTP_OPC_RepresentativeSampleWidth, sampledata->width);
8902     }
8903     break;
8904   default:
8905     // For anything not an image, set the duration and size
8906     set_object_u32(device, id, PTP_OPC_RepresentativeSampleDuration, sampledata->duration);
8907     set_object_u32(device, id, PTP_OPC_RepresentativeSampleSize, sampledata->size);
8908     break;
8909   }
8910
8911   return 0;
8912 }
8913
8914 /**
8915  * This routine gets representative sample data for an object.
8916  * This uses the RepresentativeSampleData property of the album,
8917  * if the device supports it.
8918  * @param device a pointer to the device which the object is on.
8919  * @param id unique id of the object to get data for.
8920  * @param pointer to LIBMTP_filesampledata_t struct to receive data
8921  * @return 0 on success, any other value means failure.
8922  * @see LIBMTP_Send_Representative_Sample()
8923  * @see LIBMTP_Get_Representative_Sample_Format()
8924  * @see LIBMTP_Create_New_Album()
8925  */
8926 int LIBMTP_Get_Representative_Sample(LIBMTP_mtpdevice_t *device,
8927                           uint32_t const id,
8928                           LIBMTP_filesampledata_t *sampledata)
8929 {
8930   uint16_t ret;
8931   PTPParams *params = (PTPParams *) device->params;
8932   PTPPropertyValue propval;
8933   PTPObject *ob;
8934   uint32_t i;
8935   uint16_t *props = NULL;
8936   uint32_t propcnt = 0;
8937   int supported = 0;
8938
8939   // get the file format for the object we're going to send representative data for
8940   ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
8941   if (ret != PTP_RC_OK) {
8942     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): could not get object info.");
8943     return -1;
8944   }
8945
8946   // check that we can store representative sample data for this object format
8947   ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
8948   if (ret != PTP_RC_OK) {
8949     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get object properties.");
8950     return -1;
8951   }
8952
8953   for (i = 0; i < propcnt; i++) {
8954     if (props[i] == PTP_OPC_RepresentativeSampleData) {
8955       supported = 1;
8956       break;
8957     }
8958   }
8959   if (!supported) {
8960     free(props);
8961     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
8962     return -1;
8963   }
8964   free(props);
8965
8966   // Get the data
8967   ret = ptp_mtp_getobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
8968                                    &propval,PTP_DTC_AUINT8);
8969   if (ret != PTP_RC_OK) {
8970     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get sample data.");
8971     return -1;
8972   }
8973
8974   // Store it
8975   sampledata->size = propval.a.count;
8976   sampledata->data = malloc(sizeof(PTPPropertyValue) * propval.a.count);
8977   for (i = 0; i < propval.a.count; i++) {
8978     sampledata->data[i] = propval.a.v[i].u8;
8979   }
8980   free(propval.a.v);
8981
8982   // Get the other properties
8983   sampledata->width = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleWidth, 0);
8984   sampledata->height = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleHeight, 0);
8985   sampledata->duration = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleDuration, 0);
8986   sampledata->filetype = map_ptp_type_to_libmtp_type(
8987         get_u16_from_object(device, id, PTP_OPC_RepresentativeSampleFormat, LIBMTP_FILETYPE_UNKNOWN));
8988
8989   return 0;
8990 }
8991
8992 /**
8993  * Retrieve the thumbnail for a file.
8994  * @param device a pointer to the device to get the thumbnail from.
8995  * @param id the object ID of the file to retrieve the thumbnail for.
8996  * @return 0 on success, any other value means failure.
8997  */
8998 int LIBMTP_Get_Thumbnail(LIBMTP_mtpdevice_t *device, uint32_t const id,
8999                          unsigned char **data, unsigned int *size)
9000 {
9001   LIBMTP_INFO("LIBMTP_Get_Thumbnail start");
9002   PTPParams *params = (PTPParams *) device->params;
9003   uint16_t ret;
9004
9005   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
9006
9007   int oldtimeout;
9008
9009   get_usb_device_timeout(ptp_usb, &oldtimeout);
9010   set_usb_device_timeout(ptp_usb, 3000);
9011
9012   LIBMTP_INFO("priv set timeout value : %d, now, set timeout value to 3 sec", oldtimeout);
9013
9014   ret = ptp_getthumb(params, id, data, size);
9015   if (ret != PTP_RC_OK) {
9016       LIBMTP_INFO("ptp_getthumb failed : %d", ret);
9017       set_usb_device_timeout(ptp_usb, oldtimeout);
9018       return -1;
9019   }
9020
9021   set_usb_device_timeout(ptp_usb, oldtimeout);
9022   LIBMTP_INFO("LIBMTP_Get_Thumbnail end");
9023   return 0;
9024 }
9025
9026 #ifdef TIZEN_EXT
9027 int LIBMTP_Get_Thumbnail_From_Exif_Data(LIBMTP_mtpdevice_t *device, uint32_t const id,
9028                          unsigned char **data, unsigned int *size)
9029 {
9030   LIBMTP_INFO("LIBMTP_Get_Thumbnail_From_Exif_Data start");
9031   PTPParams *params = (PTPParams *) device->params;
9032   uint16_t ret;
9033
9034   PTPObject *ob;
9035   uint32_t offset, maxbytes;
9036   uint32_t jpeg_header_size, app1_marker_size;
9037
9038   unsigned char *jpeg_header = NULL;
9039   unsigned char *app1_marker = NULL;
9040   ExifData *exif_data = NULL;
9041
9042   PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
9043
9044   int oldtimeout;
9045
9046   get_usb_device_timeout(ptp_usb, &oldtimeout);
9047
9048   if (!ptp_operation_issupported(params, PTP_OC_GetPartialObject)) {
9049     LIBMTP_INFO("ptp_operation_issupported fail - id %d", id);
9050     return -1;
9051   }
9052
9053   if (ptp_object_want(params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob) != PTP_RC_OK) {
9054     LIBMTP_INFO("ptp_object_want fail - id %d", id);
9055     return -2;
9056   }
9057
9058   if (ob->oi.ObjectCompressedSize < 10) {
9059     LIBMTP_INFO("ObjectCompressedSize fail - id %d", id);
9060     return -3;
9061   }
9062
9063   if (ob->oi.ObjectFormat != PTP_OFC_EXIF_JPEG) {
9064     LIBMTP_INFO("ObjectFormat fail - id %d", id);
9065     return -4;
9066   }
9067
9068   //Get App1 Marker header
9069   set_usb_device_timeout(ptp_usb, 3000);
9070
9071   ret = ptp_getpartialobject (params, id, 0, 10, &jpeg_header, &jpeg_header_size);
9072   if (ret != PTP_RC_OK) {
9073     LIBMTP_INFO("first ptp_getpartialobject fail - id %d", id);
9074     set_usb_device_timeout(ptp_usb, oldtimeout);
9075     return -5;
9076   }
9077
9078   set_usb_device_timeout(ptp_usb, oldtimeout);
9079
9080   if (!((jpeg_header[0] == 0xff) && (jpeg_header[1] == 0xd8))) {    /* 0XFF 0xD8 means SOI (Start Of Image) */
9081     LIBMTP_INFO("SOI fail - id %d", id);
9082     free(jpeg_header);
9083     return -6;
9084   }
9085
9086   if (!((jpeg_header[2] == 0xff) && (jpeg_header[3] == 0xe1))) {    /* 0xFF 0xE1 means App1 Marker (EXIF) */
9087     LIBMTP_INFO("App1 Marker fail - id %d", id);
9088     free(jpeg_header);
9089     return -7;
9090   }
9091
9092   if (0 != memcmp(jpeg_header+6, "Exif", 4)) { /* check exif header */
9093     LIBMTP_INFO("Exif Header Check fail");
9094     free(jpeg_header);
9095     return -8;
9096   }
9097
9098   offset = 2;
9099   maxbytes = (jpeg_header[4] << 8 ) + jpeg_header[5];
9100   maxbytes += 2; // include end of Image
9101
9102   free(jpeg_header);
9103
9104   LIBMTP_INFO("maxbytes is %d", maxbytes);
9105
9106   //Get App1 Marker : EXIF Data
9107   set_usb_device_timeout(ptp_usb, 3000);
9108
9109   ret = ptp_getpartialobject (params, id, offset, maxbytes, &app1_marker, &app1_marker_size);
9110   if (ret != PTP_RC_OK) {
9111     LIBMTP_INFO("second ptp_getpartialobject fail - id %d", id);
9112     set_usb_device_timeout(ptp_usb, oldtimeout);
9113     return -9;
9114   }
9115
9116   set_usb_device_timeout(ptp_usb, oldtimeout);
9117
9118   if (app1_marker == NULL) {
9119     LIBMTP_INFO("app1_marker is NULL - id %d", id);
9120     return -10;
9121   }
9122
9123   LIBMTP_INFO("app1_marker_size is %d", app1_marker_size);
9124
9125   exif_data = exif_data_new_from_data(app1_marker, app1_marker_size);
9126   if (exif_data == NULL) {
9127     LIBMTP_INFO("Exif data is NULL - id %d", id);
9128     free(app1_marker);
9129     return -11;
9130   }
9131
9132   if (exif_data->data == NULL) {
9133     LIBMTP_INFO("thumbnail is NULL - id %d", id);
9134     free(app1_marker);
9135     return -12;
9136   }
9137
9138   *data = (unsigned char *)malloc(sizeof(unsigned char) * exif_data->size);
9139   memcpy(*data, exif_data->data, exif_data->size);
9140   free(app1_marker);
9141
9142   *size = exif_data->size;
9143
9144   LIBMTP_INFO("thumbnail extract success - size : %d, id : %d", exif_data->size, id);
9145
9146   return 0;
9147 }
9148 #endif /* TIZEN_EXT */
9149
9150 int LIBMTP_GetPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9151                             uint32_t offset, uint32_t maxbytes,
9152                             unsigned char **data, unsigned int *size)
9153 {
9154   PTPParams *params = (PTPParams *) device->params;
9155   uint16_t ret;
9156   PTPObject *ob;
9157
9158   if  (!ptp_operation_issupported(params, PTP_OC_GetPartialObject)) {
9159         add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9160         "LIBMTP_GetPartialObject: PTP_OC_GetPartialObject not supported");
9161         return -1;
9162   }
9163
9164   if (ptp_object_want(params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob) != PTP_RC_OK) {
9165     LIBMTP_INFO("ptp_object_want fail - id %d", id);
9166     return -2;
9167   }
9168
9169   ret = ptp_getpartialobject(params, id, offset, maxbytes, data, size);
9170   if (ret == PTP_RC_OK)
9171       return 0;
9172   return ret;
9173 }
9174
9175
9176 int LIBMTP_SendPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9177                              uint64_t offset, unsigned char *data, unsigned int size)
9178 {
9179   PTPParams *params = (PTPParams *) device->params;
9180   uint16_t ret;
9181
9182   if (!ptp_operation_issupported(params, PTP_OC_ANDROID_SendPartialObject)) {
9183     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9184       "LIBMTP_SendPartialObject: PTP_OC_ANDROID_SendPartialObject not supported");
9185     return -1;
9186   }
9187
9188   ret = ptp_android_sendpartialobject(params, id, offset, data, size);
9189   if (ret == PTP_RC_OK)
9190       return 0;
9191   return -1;
9192 }
9193
9194
9195 int LIBMTP_BeginEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
9196 {
9197   PTPParams *params = (PTPParams *) device->params;
9198   uint16_t ret;
9199
9200   if (!ptp_operation_issupported(params, PTP_OC_ANDROID_BeginEditObject)) {
9201     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9202       "LIBMTP_BeginEditObject: PTP_OC_ANDROID_BeginEditObject not supported");
9203     return -1;
9204   }
9205
9206   ret = ptp_android_begineditobject(params, id);
9207   if (ret == PTP_RC_OK)
9208       return 0;
9209   return -1;
9210 }
9211
9212
9213 int LIBMTP_EndEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
9214 {
9215   PTPParams *params = (PTPParams *) device->params;
9216   uint16_t ret;
9217
9218   if (!ptp_operation_issupported(params, PTP_OC_ANDROID_EndEditObject)) {
9219     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9220       "LIBMTP_EndEditObject: PTP_OC_ANDROID_EndEditObject not supported");
9221     return -1;
9222   }
9223
9224   ret = ptp_android_endeditobject(params, id);
9225   if (ret == PTP_RC_OK) {
9226       // update cached object properties if metadata cache exists
9227       update_metadata_cache(device, id);
9228       return 0;
9229   }
9230   return -1;
9231 }
9232
9233
9234 int LIBMTP_TruncateObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9235                           uint64_t offset)
9236 {
9237   PTPParams *params = (PTPParams *) device->params;
9238   uint16_t ret;
9239
9240   if (!ptp_operation_issupported(params, PTP_OC_ANDROID_TruncateObject)) {
9241     add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9242       "LIBMTP_TruncateObject: PTP_OC_ANDROID_TruncateObject not supported");
9243     return -1;
9244   }
9245
9246   ret = ptp_android_truncate(params, id, offset);
9247   if (ret == PTP_RC_OK)
9248       return 0;
9249   return -1;
9250 }
9251
9252
9253 /**
9254  * This routine updates an album based on the metadata
9255  * supplied. If the <code>tracks</code> field of the metadata
9256  * contains a track listing, these tracks will be added to the
9257  * album in place of those already present, i.e. the
9258  * previous track listing will be deleted.
9259  * @param device a pointer to the device to create the new album on.
9260  * @param metadata the metadata for the album to be updated.
9261  *                 notice that the field <code>album_id</code>
9262  *                 must contain the apropriate album ID.
9263  * @return 0 on success, any other value means failure.
9264  * @see LIBMTP_Create_New_Album()
9265  * @see LIBMTP_Delete_Object()
9266  */
9267 int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device,
9268                            LIBMTP_album_t const * const metadata)
9269 {
9270   return update_abstract_list(device,
9271                               metadata->name,
9272                               metadata->artist,
9273                               metadata->composer,
9274                               metadata->genre,
9275                               metadata->album_id,
9276                               PTP_OFC_MTP_AbstractAudioAlbum,
9277                               metadata->tracks,
9278                               metadata->no_tracks);
9279 }
9280
9281 /**
9282  * Dummy function needed to interface to upstream
9283  * ptp.c/ptp.h files.
9284  */
9285 void ptp_nikon_getptpipguid (unsigned char* guid) {
9286   return;
9287 }
9288
9289 /**
9290  * Add an object to cache.
9291  * @param device the device which may have a cache to which the object should be added.
9292  * @param object_id the object to add to the cache.
9293  */
9294 static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
9295 {
9296   PTPParams *params = (PTPParams *)device->params;
9297   uint16_t ret;
9298
9299   ret = ptp_add_object_to_cache(params, object_id);
9300   if (ret != PTP_RC_OK) {
9301     add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): couldn't add object to cache");
9302   }
9303 }
9304
9305
9306 /**
9307  * Update cache after object has been modified
9308  * @param device the device which may have a cache to which the object should be updated.
9309  * @param object_id the object to update.
9310  */
9311 static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
9312 {
9313   PTPParams *params = (PTPParams *)device->params;
9314
9315   ptp_remove_object_from_cache(params, object_id);
9316   add_object_to_cache(device, object_id);
9317 }
9318
9319 #ifdef TIZEN_EXT
9320 int _is_exist_handler(uint32_t **object_list, int size, int current_handler)
9321 {
9322         int i;
9323
9324         for (i = 0; i < size; i++) {
9325                 if ((*object_list)[i] == current_handler)
9326                         return 1;
9327         }
9328
9329         return 0;
9330 }
9331
9332 int LIBMTP_Get_Object_Handles(LIBMTP_mtpdevice_t *device, uint32_t storage,
9333         uint32_t format, uint32_t parent, uint32_t **object_list, uint32_t *object_num)
9334 {
9335         int i;
9336         PTPParams *params = (PTPParams *)device->params;
9337         PTPObjectHandles currentHandles;
9338         uint16_t ret;
9339         int storageid;
9340         int parentid;
9341         int temp = 0;
9342
9343         if (!ptp_operation_issupported(params, PTP_OC_GetObjectHandles)) {
9344                 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9345                         "LIBMTP_Get_Object_List: LIBMTP_Get_Object_List not supported");
9346                 return -1;
9347         }
9348
9349         if (storage == 0)
9350                 storageid = PTP_GOH_ALL_STORAGE;
9351         else
9352                 storageid = storage;
9353
9354         if (parent == 0)
9355                 parentid = PTP_GOH_ROOT_PARENT;
9356         else
9357                 parentid = parent;
9358
9359         ret = ptp_getobjecthandles(params, storageid, PTP_GOH_ALL_FORMATS, parentid, &currentHandles);
9360
9361         if (ret != PTP_RC_OK) {
9362                 LIBMTP_INFO("LIBMTP_Get_Object_Handles ret %u\n", ret);
9363                 return ret;
9364         }
9365
9366         LIBMTP_INFO("LIBMTP_Get_Object_Handles currentHandles.n %u\n", currentHandles.n);
9367
9368         if (currentHandles.n == 0) {
9369                 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9370                         "LIBMTP_Get_Object_List: object handle is not exist");
9371                 return -3;
9372         }
9373
9374         *object_list = (uint32_t *)malloc(currentHandles.n * sizeof(int));
9375         if (*object_list == NULL) {
9376                 LIBMTP_INFO("object list is NULL");
9377                 if (currentHandles.Handler != NULL) {
9378                         free(currentHandles.Handler);
9379                         currentHandles.Handler = NULL;
9380                 }
9381
9382                 return -4;
9383         }
9384
9385         for (i = 0; i < currentHandles.n; i++) {
9386                 if(_is_exist_handler(object_list, temp, currentHandles.Handler[i]))
9387                         continue;
9388
9389                 (*object_list)[temp] = currentHandles.Handler[i];
9390                 temp++;
9391         }
9392
9393         *object_num = temp;
9394
9395         if (currentHandles.Handler != NULL) {
9396                 free(currentHandles.Handler);
9397                 currentHandles.Handler = NULL;
9398         }
9399
9400         return 0;
9401 }
9402
9403 static const char *__get_filename_ext(const char *filename) {
9404         const char *dot = strrchr(filename, '.');
9405         if(!dot || dot == filename) return "";
9406         return dot + 1;
9407 }
9408
9409 static int __is_high_efficiency_image(char *filename)
9410 {
9411         if (filename == NULL)
9412                 return 0;
9413
9414         const char *ext = __get_filename_ext(filename);
9415         if (ext == NULL)
9416                 return 0;
9417
9418         if (0 == strncmp(ext, "heic", strlen(ext))
9419         || 0 == strncmp(ext, "heif", strlen(ext))) {
9420                 return 1;
9421         }
9422
9423         return 0;
9424 }
9425
9426 MTPObjectInfo *LIBMTP_Get_Object_Info(LIBMTP_mtpdevice_t *device, uint32_t object_id)
9427 {
9428         PTPParams *params = (PTPParams *)device->params;
9429         PTPObject *ob;
9430         MTPObjectInfo *object_info;
9431         uint16_t ret;
9432
9433         if (!ptp_operation_issupported(params, PTP_OC_GetObjectInfo)) {
9434                 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9435                         "LIBMTP_Get_Object_info: LIBMTP_Get_Object_info not supported");
9436                 return NULL;
9437         }
9438
9439         object_info = (MTPObjectInfo *)malloc(sizeof(MTPObjectInfo));
9440         if (object_info == NULL)
9441                 return NULL;
9442
9443         ret = ptp_object_want(params, object_id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
9444
9445         if (ret != PTP_RC_OK) {
9446                 if (object_info != NULL)
9447                         free(object_info);
9448                 return NULL;
9449         }
9450
9451         object_info->StorageID = ob->oi.StorageID;
9452         object_info->ObjectFormat = ob->oi.ObjectFormat;
9453         object_info->ProtectionStatus = ob->oi.ProtectionStatus;
9454         object_info->ObjectCompressedSize = ob->oi.ObjectCompressedSize;
9455         object_info->ThumbFormat = ob->oi.ThumbFormat;
9456         object_info->ThumbCompressedSize = ob->oi.ThumbCompressedSize;
9457         object_info->ThumbPixWidth = ob->oi.ThumbPixWidth;
9458         object_info->ThumbPixHeight = ob->oi.ThumbPixHeight;
9459         object_info->ImagePixWidth = ob->oi.ImagePixWidth;
9460         object_info->ImagePixHeight = ob->oi.ImagePixHeight;
9461         object_info->ImageBitDepth = ob->oi.ImageBitDepth;
9462
9463         object_info->ParentObject = ob->oi.ParentObject;
9464         object_info->AssociationType = ob->oi.AssociationType;
9465         object_info->AssociationDesc = ob->oi.AssociationDesc;
9466         object_info->SequenceNumber = ob->oi.SequenceNumber;
9467         object_info->CaptureDate = ob->oi.CaptureDate;
9468         object_info->ModificationDate = ob->oi.ModificationDate;
9469
9470         if (ob->oi.Filename != NULL)
9471                 object_info->Filename = strdup(ob->oi.Filename);
9472         else
9473                 object_info->Filename = NULL;
9474
9475         if (ob->oi.Keywords != NULL)
9476                 object_info->Keywords = strdup(ob->oi.Keywords);
9477         else
9478                 object_info->Keywords = NULL;
9479
9480         LIBMTP_INFO("Filename : %s, Object Format : %hu", object_info->Filename, object_info->ObjectFormat);
9481         if (object_info->ObjectFormat == PTP_OFC_Defined && __is_high_efficiency_image(object_info->Filename)) {
9482                 LIBMTP_INFO("It is HEIF image");
9483                 object_info->ObjectFormat = LIBMTP_FILETYPE_HEIF;
9484         } else {
9485                 object_info->ObjectFormat = map_ptp_type_to_libmtp_type(object_info->ObjectFormat);
9486         }
9487
9488         object_info->ThumbFormat = map_ptp_type_to_libmtp_type(object_info->ThumbFormat);
9489
9490         return object_info;
9491 }
9492
9493
9494 int LIBMTP_Get_Num_Objects(LIBMTP_mtpdevice_t *device, uint32_t format, uint32_t *object_num)
9495 {
9496         PTPParams *params = (PTPParams *)device->params;
9497         uint16_t ret;
9498
9499         if (!ptp_operation_issupported(params, PTP_OC_GetNumObjects)) {
9500                 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9501                         "LIBMTP_Get_Num_Objects: LIBMTP_Get_Num_Objects not supported");
9502                 return -1;
9503         }
9504
9505         ret = ptp_getnumobjects(params, PTP_GOH_ALL_STORAGE, map_libmtp_type_to_ptp_type(format),
9506                                                         PTP_GOH_ROOT_PARENT, object_num);
9507
9508         if (ret != PTP_RC_OK)
9509                  return -2;
9510
9511         return 0;
9512 }
9513 #endif /* TIZEN_EXT */