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