blob: 7a0ac1395e214e9d362299c477db4c49ac4d387a [file] [log] [blame]
Linus Walleij10c58422006-06-02 08:51:53 +00001/**
2 * \file libmtp.c
Linus Walleij2f45d222007-02-02 22:47:39 +00003 *
Linus Walleija8b88892011-03-03 19:58:26 +01004 * Copyright (C) 2005-2011 Linus Walleij <triad@df.lth.se>
Richard Low36e447c2008-01-20 15:24:55 +00005 * Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
Linus Walleij9521e2b2007-07-10 21:50:42 +00006 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
7 * Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
Linus Walleijde8193f2008-01-27 14:55:31 +00008 * Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
Linus Walleij2f45d222007-02-02 22:47:39 +00009 *
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 *
Linus Walleij10c58422006-06-02 08:51:53 +000025 * 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 *
mopoke96143402006-10-30 04:37:26 +000031 * The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
Linus Walleij10c58422006-06-02 08:51:53 +000032 * 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 */
Linus Walleijc6d7c982009-05-10 13:24:36 +000037#include "config.h"
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000038#include "libmtp.h"
Linus Walleijb9256fd2006-02-15 09:40:43 +000039#include "unicode.h"
Linus Walleij394bbbe2006-02-22 16:10:53 +000040#include "ptp.h"
Linus Walleij7cf02642011-11-16 23:44:58 +010041#include "libusb-glue.h"
Linus Walleij3e667ae2007-10-29 23:29:39 +000042#include "device-flags.h"
Linus Walleijf3c44052008-08-16 21:14:56 +000043#include "playlist-spl.h"
Linus Walleij8f6e0d92009-10-10 21:39:44 +000044#include "util.h"
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000045
Sajid Anwar8dca41d2012-08-18 20:36:13 +020046#include "mtpz.h"
47
Marcin Niestroj718b2902017-06-10 16:36:18 +020048#include <stdarg.h>
Linus Walleij2eaaff02009-01-15 21:30:36 +000049#include <stdlib.h>
Catalin Patuleada25de02012-07-19 00:57:42 -040050#include <limits.h>
Linus Walleij2eaaff02009-01-15 21:30:36 +000051#include <unistd.h>
Linus Walleij7beba572006-11-29 08:56:12 +000052#include <string.h>
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <fcntl.h>
Linus Walleij4126b302007-09-22 22:13:55 +000056#include <time.h>
Linus Walleijf3c44052008-08-16 21:14:56 +000057#include <errno.h>
Linus Walleij7beba572006-11-29 08:56:12 +000058#ifdef _MSC_VER // For MSVC++
59#define USE_WINDOWS_IO_H
60#include <io.h>
61#endif
62
Linus Walleij7cf02642011-11-16 23:44:58 +010063
nicklas79daadbf22009-09-28 18:19:34 +000064/**
65 * Global debug level
66 * We use a flag system to enable a part of logs.
Catalin Patuleada25de02012-07-19 00:57:42 -040067 *
68 * The LIBMTP_DEBUG environment variable sets the debug flags for any binary
69 * that uses libmtp and calls LIBMTP_Init. The value can be given in decimal
70 * (must not start with "0" or it will be interpreted in octal), or in
71 * hexadecimal (must start with "0x").
72 *
73 * The value "-1" enables all debug flags.
74 *
75 * Some of the utilities in examples/ also take a command-line flag "-d" that
Catalin Patulea406d5f92012-07-20 19:00:24 -040076 * enables LIBMTP_DEBUG_PTP and LIBMTP_DEBUG_DATA (same as setting
77 * LIBMTP_DEBUG=9).
Catalin Patuleada25de02012-07-19 00:57:42 -040078 *
79 * Flags (combine by adding the hex values):
Catalin Patulea406d5f92012-07-20 19:00:24 -040080 * 0x00 [0000 0000] : LIBMTP_DEBUG_NONE : no debug (default)
81 * 0x01 [0000 0001] : LIBMTP_DEBUG_PTP : PTP debug
82 * 0x02 [0000 0010] : LIBMTP_DEBUG_PLST : Playlist debug
83 * 0x04 [0000 0100] : LIBMTP_DEBUG_USB : USB debug
84 * 0x08 [0000 1000] : LIBMTP_DEBUG_DATA : USB data debug
85 *
86 * (Please keep this list in sync with libmtp.h.)
nicklas79daadbf22009-09-28 18:19:34 +000087 */
nicklas7903584082009-09-28 18:20:16 +000088int LIBMTP_debug = LIBMTP_DEBUG_NONE;
nicklas79daadbf22009-09-28 18:19:34 +000089
Linus Walleijd1e14e02008-12-14 01:07:30 +000090
Linus Walleijc86afbd2006-05-04 19:05:24 +000091/*
92 * This is a mapping between libmtp internal MTP filetypes and
93 * the libgphoto2/PTP equivalent defines. We need this because
94 * otherwise the libmtp.h device has to be dependent on ptp.h
95 * to be installed too, and we don't want that.
96 */
Linus Walleijcf42f452006-11-28 08:32:49 +000097//typedef struct filemap_struct filemap_t;
98typedef struct filemap_struct {
raveloxd9a28642006-05-26 23:42:22 +000099 char *description; /**< Text description for the file type */
100 LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
101 uint16_t ptp_id; /**< PTP ID for the filetype */
Linus Walleijcf42f452006-11-28 08:32:49 +0000102 struct filemap_struct *next;
103} filemap_t;
Linus Walleijc86afbd2006-05-04 19:05:24 +0000104
Richard Low6f070842009-05-03 10:26:16 +0000105/*
106 * This is a mapping between libmtp internal MTP properties and
107 * the libgphoto2/PTP equivalent defines. We need this because
108 * otherwise the libmtp.h device has to be dependent on ptp.h
109 * to be installed too, and we don't want that.
110 */
111typedef struct propertymap_struct {
112 char *description; /**< Text description for the property */
113 LIBMTP_property_t id; /**< LIBMTP internal type for the property */
114 uint16_t ptp_id; /**< PTP ID for the property */
115 struct propertymap_struct *next;
116} propertymap_t;
117
Philip Langdale0a576512016-04-09 14:22:25 -0700118/*
119 * This is a simple container for holding our callback and user_data
120 * for parsing onwards to the usb_event_async function.
121 */
122typedef struct event_cb_data_struct {
123 LIBMTP_event_cb_fn cb;
124 void *user_data;
125} event_cb_data_t;
126
Linus Walleijf67bca92006-05-29 09:33:39 +0000127// Global variables
Linus Walleij438bd7f2006-06-08 11:35:44 +0000128// This holds the global filetype mapping table
Lei Zhang81732132013-09-04 17:27:35 -0700129static filemap_t *g_filemap = NULL;
Richard Low6f070842009-05-03 10:26:16 +0000130// This holds the global property mapping table
Lei Zhang81732132013-09-04 17:27:35 -0700131static propertymap_t *g_propertymap = NULL;
Linus Walleijf67bca92006-05-29 09:33:39 +0000132
Jerry Zhang60d02262017-06-08 18:19:59 -0700133static int load_cache_on_demand = 0;
Linus Walleijcf42f452006-11-28 08:32:49 +0000134/*
135 * Forward declarations of local (static) functions.
136 */
137static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
138 uint16_t const ptp_id);
Linus Walleij7beba572006-11-29 08:56:12 +0000139static void init_filemap();
Richard Low6f070842009-05-03 10:26:16 +0000140static int register_property(char const * const description, LIBMTP_property_t const id,
141 uint16_t const ptp_id);
142static void init_propertymap();
Linus Walleij2715c442007-01-20 22:35:29 +0000143static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
144 LIBMTP_error_number_t errornumber,
145 char const * const error_text);
Linus Walleij070e9b42007-01-22 08:49:28 +0000146static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
147 uint16_t ptp_error,
148 char const * const error_text);
Linus Walleij438bd7f2006-06-08 11:35:44 +0000149static void flush_handles(LIBMTP_mtpdevice_t *device);
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000150static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
151 PTPParams *params,
Linus Walleij6bf68b42007-09-03 22:50:02 +0000152 uint32_t storageid,
153 uint32_t parent);
Linus Walleij9e1b0812006-12-12 19:22:02 +0000154static void free_storage_list(LIBMTP_mtpdevice_t *device);
155static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
Linus Walleij88babc82014-06-02 21:32:14 +0200156static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
157 uint64_t fitsize);
Linus Walleij6bf68b42007-09-03 22:50:02 +0000158static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
159 LIBMTP_devicestorage_t *storage,
160 uint64_t *freespace);
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000161static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
Linus Walleij6bf68b42007-09-03 22:50:02 +0000162 LIBMTP_devicestorage_t *storage,
163 uint64_t const filesize);
Linus Walleijf67bca92006-05-29 09:33:39 +0000164static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
165static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
Richard Low6f070842009-05-03 10:26:16 +0000166static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty);
167static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t intype);
mopoke96143402006-10-30 04:37:26 +0000168static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +0000169 char **unicstring, uint16_t property);
Linus Walleij29559562007-08-22 21:38:04 +0000170static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd);
171static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd);
Linus Walleij7783b9b2007-09-22 22:10:44 +0000172static char *get_iso8601_stamp(void);
Linus Walleij9901e222006-11-30 12:28:19 +0000173static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
174 uint16_t const attribute_id);
Richard Lowc3f5fc32007-07-29 09:40:50 +0000175static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
176 uint16_t const attribute_id, uint64_t const value_default);
Linus Walleij9901e222006-11-30 12:28:19 +0000177static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
178 uint16_t const attribute_id, uint32_t const value_default);
179static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
180 uint16_t const attribute_id, uint16_t const value_default);
181static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
182 uint16_t const attribute_id, uint8_t const value_default);
183static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
184 uint16_t const attribute_id, char const * const string);
185static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
186 uint16_t const attribute_id, uint32_t const value);
187static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
188 uint16_t const attribute_id, uint16_t const value);
189static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
190 uint16_t const attribute_id, uint8_t const value);
Linus Walleij8ab54262006-06-21 07:12:28 +0000191static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
192 LIBMTP_track_t *track);
Linus Walleija89a7942008-12-14 23:19:34 +0000193static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent);
Linus Walleij7beba572006-11-29 08:56:12 +0000194static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
195 char const * const name,
Linus Walleijadce4a52007-04-23 07:28:11 +0000196 char const * const artist,
Linus Walleij31b74292008-05-02 23:29:06 +0000197 char const * const composer,
Linus Walleijadce4a52007-04-23 07:28:11 +0000198 char const * const genre,
Linus Walleij7beba572006-11-29 08:56:12 +0000199 uint32_t const parenthandle,
Linus Walleijea68f1f2008-06-22 21:54:44 +0000200 uint32_t const storageid,
Linus Walleij7beba572006-11-29 08:56:12 +0000201 uint16_t const objectformat,
202 char const * const suffix,
203 uint32_t * const newid,
204 uint32_t const * const tracks,
205 uint32_t const no_tracks);
Linus Walleij7783b9b2007-09-22 22:10:44 +0000206static int update_abstract_list(LIBMTP_mtpdevice_t *device,
207 char const * const name,
208 char const * const artist,
Linus Walleij31b74292008-05-02 23:29:06 +0000209 char const * const composer,
Linus Walleij7783b9b2007-09-22 22:10:44 +0000210 char const * const genre,
211 uint32_t const objecthandle,
212 uint16_t const objectformat,
213 uint32_t const * const tracks,
214 uint32_t const no_tracks);
Richard Lowd3b17022009-04-11 12:37:39 +0000215static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata);
Linus Walleija6d0d482007-10-31 08:54:56 +0000216static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
Linus Walleij7752b952007-10-19 20:11:46 +0000217static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
Linus Walleij8d8c4352008-09-23 23:04:16 +0000218static int set_object_filename(LIBMTP_mtpdevice_t *device,
219 uint32_t object_id,
220 uint16_t ptp_type,
221 const char **newname);
Linus Walleij094b4502009-09-22 22:28:33 +0000222static char *generate_unique_filename(PTPParams* params, char const * const filename);
223static int check_filename_exists(PTPParams* params, char const * const filename);
Philip Langdale0a576512016-04-09 14:22:25 -0700224static void LIBMTP_Handle_Event(PTPContainer *ptp_event,
225 LIBMTP_event_t *event, uint32_t *out1);
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000226
Richard Low5b4023c2009-04-16 19:14:38 +0000227/**
228 * These are to wrap the get/put handlers to convert from the MTP types to PTP types
229 * in a reliable way
230 */
231typedef struct _MTPDataHandler {
232 MTPDataGetFunc getfunc;
233 MTPDataPutFunc putfunc;
Linus Walleijd866d242009-08-23 21:50:39 +0000234 void *priv;
Richard Low5b4023c2009-04-16 19:14:38 +0000235} MTPDataHandler;
raveloxd9a28642006-05-26 23:42:22 +0000236
Linus Walleijd866d242009-08-23 21:50:39 +0000237static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen);
Marcus Meissner3d692dc2016-02-21 12:34:06 +0100238static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data);
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000239
Linus Walleijcf42f452006-11-28 08:32:49 +0000240/**
Linus Walleij01fc9c82009-03-10 23:52:09 +0000241 * Checks if a filename ends with ".ogg". Used in various
242 * situations when the device has no idea that it support
243 * OGG but still does.
Linus Walleijd98e8972009-03-08 22:57:17 +0000244 *
245 * @param name string to be checked.
246 * @return 0 if this does not end with ogg, any other
247 * value means it does.
248 */
249static int has_ogg_extension(char *name) {
250 char *ptype;
251
252 if (name == NULL)
253 return 0;
254 ptype = strrchr(name,'.');
255 if (ptype == NULL)
256 return 0;
257 if (!strcasecmp (ptype, ".ogg"))
258 return 1;
259 return 0;
260}
261
Linus Walleij89bb1cd2009-07-24 21:03:36 +0000262/**
263 * Checks if a filename ends with ".flac". Used in various
264 * situations when the device has no idea that it support
265 * FLAC but still does.
266 *
267 * @param name string to be checked.
268 * @return 0 if this does not end with flac, any other
269 * value means it does.
270 */
271static int has_flac_extension(char *name) {
272 char *ptype;
273
274 if (name == NULL)
275 return 0;
276 ptype = strrchr(name,'.');
277 if (ptype == NULL)
278 return 0;
279 if (!strcasecmp (ptype, ".flac"))
280 return 1;
281 return 0;
282}
283
284
Linus Walleijd98e8972009-03-08 22:57:17 +0000285
286/**
Linus Walleijcf42f452006-11-28 08:32:49 +0000287 * Create a new file mapping entry
288 * @return a newly allocated filemapping entry.
289 */
290static filemap_t *new_filemap_entry()
raveloxd9a28642006-05-26 23:42:22 +0000291{
Linus Walleijcf42f452006-11-28 08:32:49 +0000292 filemap_t *filemap;
mopoke96143402006-10-30 04:37:26 +0000293
Linus Walleijcf42f452006-11-28 08:32:49 +0000294 filemap = (filemap_t *)malloc(sizeof(filemap_t));
mopoke96143402006-10-30 04:37:26 +0000295
Linus Walleijf67bca92006-05-29 09:33:39 +0000296 if( filemap != NULL ) {
297 filemap->description = NULL;
298 filemap->id = LIBMTP_FILETYPE_UNKNOWN;
299 filemap->ptp_id = PTP_OFC_Undefined;
Linus Walleijf67bca92006-05-29 09:33:39 +0000300 filemap->next = NULL;
301 }
mopoke96143402006-10-30 04:37:26 +0000302
Linus Walleijf67bca92006-05-29 09:33:39 +0000303 return filemap;
raveloxd9a28642006-05-26 23:42:22 +0000304}
305
raveloxd9a28642006-05-26 23:42:22 +0000306/**
307 * Register an MTP or PTP filetype for data retrieval
308 *
309 * @param description Text description of filetype
Linus Walleijf0f3d482006-05-29 14:10:21 +0000310 * @param id libmtp internal filetype id
raveloxd9a28642006-05-26 23:42:22 +0000311 * @param ptp_id PTP filetype id
Linus Walleijf0f3d482006-05-29 14:10:21 +0000312 * @return 0 for success any other value means error.
raveloxd9a28642006-05-26 23:42:22 +0000313*/
Linus Walleijcf42f452006-11-28 08:32:49 +0000314static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
315 uint16_t const ptp_id)
raveloxd9a28642006-05-26 23:42:22 +0000316{
Linus Walleijcf42f452006-11-28 08:32:49 +0000317 filemap_t *new = NULL, *current;
raveloxd9a28642006-05-26 23:42:22 +0000318
319 // Has this LIBMTP filetype been registered before ?
Lei Zhang81732132013-09-04 17:27:35 -0700320 current = g_filemap;
raveloxd9a28642006-05-26 23:42:22 +0000321 while (current != NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000322 if(current->id == id) {
323 break;
324 }
Linus Walleijf0f3d482006-05-29 14:10:21 +0000325 current = current->next;
raveloxd9a28642006-05-26 23:42:22 +0000326 }
mopoke96143402006-10-30 04:37:26 +0000327
raveloxd9a28642006-05-26 23:42:22 +0000328 // Create the entry
329 if(current == NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000330 new = new_filemap_entry();
Linus Walleijf0f3d482006-05-29 14:10:21 +0000331 if(new == NULL) {
332 return 1;
333 }
mopoke96143402006-10-30 04:37:26 +0000334
Linus Walleijf67bca92006-05-29 09:33:39 +0000335 new->id = id;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000336 if(description != NULL) {
337 new->description = strdup(description);
338 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000339 new->ptp_id = ptp_id;
mopoke96143402006-10-30 04:37:26 +0000340
Linus Walleijf67bca92006-05-29 09:33:39 +0000341 // Add the entry to the list
Lei Zhang81732132013-09-04 17:27:35 -0700342 if(g_filemap == NULL) {
343 g_filemap = new;
Linus Walleijf67bca92006-05-29 09:33:39 +0000344 } else {
Lei Zhang81732132013-09-04 17:27:35 -0700345 current = g_filemap;
Linus Walleijf67bca92006-05-29 09:33:39 +0000346 while (current->next != NULL ) current=current->next;
347 current->next = new;
348 }
349 // Update the existing entry
raveloxd9a28642006-05-26 23:42:22 +0000350 } else {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000351 if (current->description != NULL) {
352 free(current->description);
353 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000354 current->description = NULL;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000355 if(description != NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000356 current->description = strdup(description);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000357 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000358 current->ptp_id = ptp_id;
raveloxd9a28642006-05-26 23:42:22 +0000359 }
360
Linus Walleijf0f3d482006-05-29 14:10:21 +0000361 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000362}
363
raveloxd9a28642006-05-26 23:42:22 +0000364static void init_filemap()
365{
Linus Walleija8b88892011-03-03 19:58:26 +0100366 register_filetype("Folder", LIBMTP_FILETYPE_FOLDER, PTP_OFC_Association);
Linus Walleij5fb47132006-12-30 15:35:48 +0000367 register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
Linus Walleijcf42f452006-11-28 08:32:49 +0000368 register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
Linus Walleij5fb47132006-12-30 15:35:48 +0000369 register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
370 register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
Linus Walleijcf42f452006-11-28 08:32:49 +0000371 register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
372 register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000373 register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
374 register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
Richard Lowd3b17022009-04-11 12:37:39 +0000375 register_filetype("MPEG-4 Part 14 Container Format (Audio Emphasis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
376 register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Emphasis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
Linus Walleijcf42f452006-11-28 08:32:49 +0000377 register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
Linus Walleijcf42f452006-11-28 08:32:49 +0000378 register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
379 register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
380 register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
381 register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
382 register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
383 register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
384 register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
385 register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000386 register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
387 register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
Linus Walleijcf42f452006-11-28 08:32:49 +0000388 register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
389 register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
390 register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
391 register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
392 register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
393 register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
394 register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
395 register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
396 register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
397 register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
398 register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
399 register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
400 register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
401 register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
Linus Walleij5fb47132006-12-30 15:35:48 +0000402 register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
403 register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
404 register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
405 register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
406 register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
Linus Walleija05b9802006-12-08 08:50:30 +0000407 register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
Richard Lowd3b17022009-04-11 12:37:39 +0000408 register_filetype("Abstract Album file", LIBMTP_FILETYPE_ALBUM, PTP_OFC_MTP_AbstractAudioAlbum);
409 register_filetype("Abstract Playlist file", LIBMTP_FILETYPE_PLAYLIST, PTP_OFC_MTP_AbstractAudioVideoPlaylist);
Linus Walleijcf42f452006-11-28 08:32:49 +0000410 register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
raveloxd9a28642006-05-26 23:42:22 +0000411}
Linus Walleij16c51f02006-05-04 13:20:22 +0000412
Linus Walleij16c51f02006-05-04 13:20:22 +0000413/**
414 * Returns the PTP filetype that maps to a certain libmtp internal file type.
415 * @param intype the MTP library interface type
416 * @return the PTP (libgphoto2) interface type
417 */
418static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
419{
Linus Walleijcf42f452006-11-28 08:32:49 +0000420 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000421
Lei Zhang81732132013-09-04 17:27:35 -0700422 current = g_filemap;
raveloxd9a28642006-05-26 23:42:22 +0000423
424 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000425 if(current->id == intype) {
426 return current->ptp_id;
427 }
428 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000429 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000430 // printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000431 return PTP_OFC_Undefined;
432}
433
434
435/**
Linus Walleij31b74292008-05-02 23:29:06 +0000436 * Returns the MTP internal interface type that maps to a certain ptp
437 * interface type.
mopoke96143402006-10-30 04:37:26 +0000438 * @param intype the PTP (libgphoto2) interface type
Linus Walleij8ab54262006-06-21 07:12:28 +0000439 * @return the MTP library interface type
Linus Walleij16c51f02006-05-04 13:20:22 +0000440 */
441static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
442{
Linus Walleijcf42f452006-11-28 08:32:49 +0000443 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000444
Lei Zhang81732132013-09-04 17:27:35 -0700445 current = g_filemap;
raveloxd9a28642006-05-26 23:42:22 +0000446
447 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000448 if(current->ptp_id == intype) {
449 return current->id;
450 }
451 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000452 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000453 // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000454 return LIBMTP_FILETYPE_UNKNOWN;
455}
456
Richard Low6f070842009-05-03 10:26:16 +0000457/**
458 * Create a new property mapping entry
459 * @return a newly allocated propertymapping entry.
460 */
461static propertymap_t *new_propertymap_entry()
462{
463 propertymap_t *propertymap;
464
465 propertymap = (propertymap_t *)malloc(sizeof(propertymap_t));
466
467 if( propertymap != NULL ) {
468 propertymap->description = NULL;
469 propertymap->id = LIBMTP_PROPERTY_UNKNOWN;
470 propertymap->ptp_id = 0;
471 propertymap->next = NULL;
472 }
473
474 return propertymap;
475}
476
477/**
478 * Register an MTP or PTP property for data retrieval
479 *
480 * @param description Text description of property
481 * @param id libmtp internal property id
482 * @param ptp_id PTP property id
483 * @return 0 for success any other value means error.
484*/
485static int register_property(char const * const description, LIBMTP_property_t const id,
486 uint16_t const ptp_id)
487{
488 propertymap_t *new = NULL, *current;
489
490 // Has this LIBMTP propety been registered before ?
Lei Zhang81732132013-09-04 17:27:35 -0700491 current = g_propertymap;
Richard Low6f070842009-05-03 10:26:16 +0000492 while (current != NULL) {
493 if(current->id == id) {
494 break;
495 }
496 current = current->next;
497 }
498
499 // Create the entry
500 if(current == NULL) {
501 new = new_propertymap_entry();
502 if(new == NULL) {
503 return 1;
504 }
505
506 new->id = id;
507 if(description != NULL) {
508 new->description = strdup(description);
509 }
510 new->ptp_id = ptp_id;
511
512 // Add the entry to the list
Lei Zhang81732132013-09-04 17:27:35 -0700513 if(g_propertymap == NULL) {
514 g_propertymap = new;
Richard Low6f070842009-05-03 10:26:16 +0000515 } else {
Lei Zhang81732132013-09-04 17:27:35 -0700516 current = g_propertymap;
Richard Low6f070842009-05-03 10:26:16 +0000517 while (current->next != NULL ) current=current->next;
518 current->next = new;
519 }
520 // Update the existing entry
521 } else {
522 if (current->description != NULL) {
523 free(current->description);
524 }
525 current->description = NULL;
526 if(description != NULL) {
527 current->description = strdup(description);
528 }
529 current->ptp_id = ptp_id;
530 }
531
532 return 0;
533}
534
535static void init_propertymap()
536{
537 register_property("Storage ID", LIBMTP_PROPERTY_StorageID, PTP_OPC_StorageID);
538 register_property("Object Format", LIBMTP_PROPERTY_ObjectFormat, PTP_OPC_ObjectFormat);
539 register_property("Protection Status", LIBMTP_PROPERTY_ProtectionStatus, PTP_OPC_ProtectionStatus);
540 register_property("Object Size", LIBMTP_PROPERTY_ObjectSize, PTP_OPC_ObjectSize);
541 register_property("Association Type", LIBMTP_PROPERTY_AssociationType, PTP_OPC_AssociationType);
542 register_property("Association Desc", LIBMTP_PROPERTY_AssociationDesc, PTP_OPC_AssociationDesc);
543 register_property("Object File Name", LIBMTP_PROPERTY_ObjectFileName, PTP_OPC_ObjectFileName);
544 register_property("Date Created", LIBMTP_PROPERTY_DateCreated, PTP_OPC_DateCreated);
545 register_property("Date Modified", LIBMTP_PROPERTY_DateModified, PTP_OPC_DateModified);
546 register_property("Keywords", LIBMTP_PROPERTY_Keywords, PTP_OPC_Keywords);
547 register_property("Parent Object", LIBMTP_PROPERTY_ParentObject, PTP_OPC_ParentObject);
548 register_property("Allowed Folder Contents", LIBMTP_PROPERTY_AllowedFolderContents, PTP_OPC_AllowedFolderContents);
549 register_property("Hidden", LIBMTP_PROPERTY_Hidden, PTP_OPC_Hidden);
550 register_property("System Object", LIBMTP_PROPERTY_SystemObject, PTP_OPC_SystemObject);
551 register_property("Persistant Unique Object Identifier", LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier, PTP_OPC_PersistantUniqueObjectIdentifier);
552 register_property("Sync ID", LIBMTP_PROPERTY_SyncID, PTP_OPC_SyncID);
553 register_property("Property Bag", LIBMTP_PROPERTY_PropertyBag, PTP_OPC_PropertyBag);
554 register_property("Name", LIBMTP_PROPERTY_Name, PTP_OPC_Name);
555 register_property("Created By", LIBMTP_PROPERTY_CreatedBy, PTP_OPC_CreatedBy);
556 register_property("Artist", LIBMTP_PROPERTY_Artist, PTP_OPC_Artist);
557 register_property("Date Authored", LIBMTP_PROPERTY_DateAuthored, PTP_OPC_DateAuthored);
558 register_property("Description", LIBMTP_PROPERTY_Description, PTP_OPC_Description);
559 register_property("URL Reference", LIBMTP_PROPERTY_URLReference, PTP_OPC_URLReference);
560 register_property("Language Locale", LIBMTP_PROPERTY_LanguageLocale, PTP_OPC_LanguageLocale);
561 register_property("Copyright Information", LIBMTP_PROPERTY_CopyrightInformation, PTP_OPC_CopyrightInformation);
562 register_property("Source", LIBMTP_PROPERTY_Source, PTP_OPC_Source);
563 register_property("Origin Location", LIBMTP_PROPERTY_OriginLocation, PTP_OPC_OriginLocation);
564 register_property("Date Added", LIBMTP_PROPERTY_DateAdded, PTP_OPC_DateAdded);
565 register_property("Non Consumable", LIBMTP_PROPERTY_NonConsumable, PTP_OPC_NonConsumable);
566 register_property("Corrupt Or Unplayable", LIBMTP_PROPERTY_CorruptOrUnplayable, PTP_OPC_CorruptOrUnplayable);
567 register_property("Producer Serial Number", LIBMTP_PROPERTY_ProducerSerialNumber, PTP_OPC_ProducerSerialNumber);
568 register_property("Representative Sample Format", LIBMTP_PROPERTY_RepresentativeSampleFormat, PTP_OPC_RepresentativeSampleFormat);
569 register_property("Representative Sample Sise", LIBMTP_PROPERTY_RepresentativeSampleSize, PTP_OPC_RepresentativeSampleSize);
570 register_property("Representative Sample Height", LIBMTP_PROPERTY_RepresentativeSampleHeight, PTP_OPC_RepresentativeSampleHeight);
571 register_property("Representative Sample Width", LIBMTP_PROPERTY_RepresentativeSampleWidth, PTP_OPC_RepresentativeSampleWidth);
572 register_property("Representative Sample Duration", LIBMTP_PROPERTY_RepresentativeSampleDuration, PTP_OPC_RepresentativeSampleDuration);
573 register_property("Representative Sample Data", LIBMTP_PROPERTY_RepresentativeSampleData, PTP_OPC_RepresentativeSampleData);
574 register_property("Width", LIBMTP_PROPERTY_Width, PTP_OPC_Width);
575 register_property("Height", LIBMTP_PROPERTY_Height, PTP_OPC_Height);
576 register_property("Duration", LIBMTP_PROPERTY_Duration, PTP_OPC_Duration);
577 register_property("Rating", LIBMTP_PROPERTY_Rating, PTP_OPC_Rating);
578 register_property("Track", LIBMTP_PROPERTY_Track, PTP_OPC_Track);
579 register_property("Genre", LIBMTP_PROPERTY_Genre, PTP_OPC_Genre);
580 register_property("Credits", LIBMTP_PROPERTY_Credits, PTP_OPC_Credits);
581 register_property("Lyrics", LIBMTP_PROPERTY_Lyrics, PTP_OPC_Lyrics);
582 register_property("Subscription Content ID", LIBMTP_PROPERTY_SubscriptionContentID, PTP_OPC_SubscriptionContentID);
583 register_property("Produced By", LIBMTP_PROPERTY_ProducedBy, PTP_OPC_ProducedBy);
584 register_property("Use Count", LIBMTP_PROPERTY_UseCount, PTP_OPC_UseCount);
585 register_property("Skip Count", LIBMTP_PROPERTY_SkipCount, PTP_OPC_SkipCount);
586 register_property("Last Accessed", LIBMTP_PROPERTY_LastAccessed, PTP_OPC_LastAccessed);
587 register_property("Parental Rating", LIBMTP_PROPERTY_ParentalRating, PTP_OPC_ParentalRating);
588 register_property("Meta Genre", LIBMTP_PROPERTY_MetaGenre, PTP_OPC_MetaGenre);
589 register_property("Composer", LIBMTP_PROPERTY_Composer, PTP_OPC_Composer);
590 register_property("Effective Rating", LIBMTP_PROPERTY_EffectiveRating, PTP_OPC_EffectiveRating);
591 register_property("Subtitle", LIBMTP_PROPERTY_Subtitle, PTP_OPC_Subtitle);
592 register_property("Original Release Date", LIBMTP_PROPERTY_OriginalReleaseDate, PTP_OPC_OriginalReleaseDate);
593 register_property("Album Name", LIBMTP_PROPERTY_AlbumName, PTP_OPC_AlbumName);
594 register_property("Album Artist", LIBMTP_PROPERTY_AlbumArtist, PTP_OPC_AlbumArtist);
595 register_property("Mood", LIBMTP_PROPERTY_Mood, PTP_OPC_Mood);
596 register_property("DRM Status", LIBMTP_PROPERTY_DRMStatus, PTP_OPC_DRMStatus);
597 register_property("Sub Description", LIBMTP_PROPERTY_SubDescription, PTP_OPC_SubDescription);
598 register_property("Is Cropped", LIBMTP_PROPERTY_IsCropped, PTP_OPC_IsCropped);
599 register_property("Is Color Corrected", LIBMTP_PROPERTY_IsColorCorrected, PTP_OPC_IsColorCorrected);
600 register_property("Image Bit Depth", LIBMTP_PROPERTY_ImageBitDepth, PTP_OPC_ImageBitDepth);
601 register_property("f Number", LIBMTP_PROPERTY_Fnumber, PTP_OPC_Fnumber);
602 register_property("Exposure Time", LIBMTP_PROPERTY_ExposureTime, PTP_OPC_ExposureTime);
603 register_property("Exposure Index", LIBMTP_PROPERTY_ExposureIndex, PTP_OPC_ExposureIndex);
604 register_property("Display Name", LIBMTP_PROPERTY_DisplayName, PTP_OPC_DisplayName);
605 register_property("Body Text", LIBMTP_PROPERTY_BodyText, PTP_OPC_BodyText);
606 register_property("Subject", LIBMTP_PROPERTY_Subject, PTP_OPC_Subject);
607 register_property("Priority", LIBMTP_PROPERTY_Priority, PTP_OPC_Priority);
608 register_property("Given Name", LIBMTP_PROPERTY_GivenName, PTP_OPC_GivenName);
609 register_property("Middle Names", LIBMTP_PROPERTY_MiddleNames, PTP_OPC_MiddleNames);
610 register_property("Family Name", LIBMTP_PROPERTY_FamilyName, PTP_OPC_FamilyName);
611 register_property("Prefix", LIBMTP_PROPERTY_Prefix, PTP_OPC_Prefix);
612 register_property("Suffix", LIBMTP_PROPERTY_Suffix, PTP_OPC_Suffix);
613 register_property("Phonetic Given Name", LIBMTP_PROPERTY_PhoneticGivenName, PTP_OPC_PhoneticGivenName);
614 register_property("Phonetic Family Name", LIBMTP_PROPERTY_PhoneticFamilyName, PTP_OPC_PhoneticFamilyName);
615 register_property("Email: Primary", LIBMTP_PROPERTY_EmailPrimary, PTP_OPC_EmailPrimary);
616 register_property("Email: Personal 1", LIBMTP_PROPERTY_EmailPersonal1, PTP_OPC_EmailPersonal1);
617 register_property("Email: Personal 2", LIBMTP_PROPERTY_EmailPersonal2, PTP_OPC_EmailPersonal2);
618 register_property("Email: Business 1", LIBMTP_PROPERTY_EmailBusiness1, PTP_OPC_EmailBusiness1);
619 register_property("Email: Business 2", LIBMTP_PROPERTY_EmailBusiness2, PTP_OPC_EmailBusiness2);
620 register_property("Email: Others", LIBMTP_PROPERTY_EmailOthers, PTP_OPC_EmailOthers);
621 register_property("Phone Number: Primary", LIBMTP_PROPERTY_PhoneNumberPrimary, PTP_OPC_PhoneNumberPrimary);
622 register_property("Phone Number: Personal", LIBMTP_PROPERTY_PhoneNumberPersonal, PTP_OPC_PhoneNumberPersonal);
623 register_property("Phone Number: Personal 2", LIBMTP_PROPERTY_PhoneNumberPersonal2, PTP_OPC_PhoneNumberPersonal2);
624 register_property("Phone Number: Business", LIBMTP_PROPERTY_PhoneNumberBusiness, PTP_OPC_PhoneNumberBusiness);
625 register_property("Phone Number: Business 2", LIBMTP_PROPERTY_PhoneNumberBusiness2, PTP_OPC_PhoneNumberBusiness2);
626 register_property("Phone Number: Mobile", LIBMTP_PROPERTY_PhoneNumberMobile, PTP_OPC_PhoneNumberMobile);
627 register_property("Phone Number: Mobile 2", LIBMTP_PROPERTY_PhoneNumberMobile2, PTP_OPC_PhoneNumberMobile2);
628 register_property("Fax Number: Primary", LIBMTP_PROPERTY_FaxNumberPrimary, PTP_OPC_FaxNumberPrimary);
629 register_property("Fax Number: Personal", LIBMTP_PROPERTY_FaxNumberPersonal, PTP_OPC_FaxNumberPersonal);
630 register_property("Fax Number: Business", LIBMTP_PROPERTY_FaxNumberBusiness, PTP_OPC_FaxNumberBusiness);
631 register_property("Pager Number", LIBMTP_PROPERTY_PagerNumber, PTP_OPC_PagerNumber);
632 register_property("Phone Number: Others", LIBMTP_PROPERTY_PhoneNumberOthers, PTP_OPC_PhoneNumberOthers);
633 register_property("Primary Web Address", LIBMTP_PROPERTY_PrimaryWebAddress, PTP_OPC_PrimaryWebAddress);
634 register_property("Personal Web Address", LIBMTP_PROPERTY_PersonalWebAddress, PTP_OPC_PersonalWebAddress);
635 register_property("Business Web Address", LIBMTP_PROPERTY_BusinessWebAddress, PTP_OPC_BusinessWebAddress);
636 register_property("Instant Messenger Address 1", LIBMTP_PROPERTY_InstantMessengerAddress, PTP_OPC_InstantMessengerAddress);
637 register_property("Instant Messenger Address 2", LIBMTP_PROPERTY_InstantMessengerAddress2, PTP_OPC_InstantMessengerAddress2);
638 register_property("Instant Messenger Address 3", LIBMTP_PROPERTY_InstantMessengerAddress3, PTP_OPC_InstantMessengerAddress3);
639 register_property("Postal Address: Personal: Full", LIBMTP_PROPERTY_PostalAddressPersonalFull, PTP_OPC_PostalAddressPersonalFull);
640 register_property("Postal Address: Personal: Line 1", LIBMTP_PROPERTY_PostalAddressPersonalFullLine1, PTP_OPC_PostalAddressPersonalFullLine1);
641 register_property("Postal Address: Personal: Line 2", LIBMTP_PROPERTY_PostalAddressPersonalFullLine2, PTP_OPC_PostalAddressPersonalFullLine2);
642 register_property("Postal Address: Personal: City", LIBMTP_PROPERTY_PostalAddressPersonalFullCity, PTP_OPC_PostalAddressPersonalFullCity);
643 register_property("Postal Address: Personal: Region", LIBMTP_PROPERTY_PostalAddressPersonalFullRegion, PTP_OPC_PostalAddressPersonalFullRegion);
644 register_property("Postal Address: Personal: Postal Code", LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode, PTP_OPC_PostalAddressPersonalFullPostalCode);
645 register_property("Postal Address: Personal: Country", LIBMTP_PROPERTY_PostalAddressPersonalFullCountry, PTP_OPC_PostalAddressPersonalFullCountry);
646 register_property("Postal Address: Business: Full", LIBMTP_PROPERTY_PostalAddressBusinessFull, PTP_OPC_PostalAddressBusinessFull);
647 register_property("Postal Address: Business: Line 1", LIBMTP_PROPERTY_PostalAddressBusinessLine1, PTP_OPC_PostalAddressBusinessLine1);
648 register_property("Postal Address: Business: Line 2", LIBMTP_PROPERTY_PostalAddressBusinessLine2, PTP_OPC_PostalAddressBusinessLine2);
649 register_property("Postal Address: Business: City", LIBMTP_PROPERTY_PostalAddressBusinessCity, PTP_OPC_PostalAddressBusinessCity);
650 register_property("Postal Address: Business: Region", LIBMTP_PROPERTY_PostalAddressBusinessRegion, PTP_OPC_PostalAddressBusinessRegion);
651 register_property("Postal Address: Business: Postal Code", LIBMTP_PROPERTY_PostalAddressBusinessPostalCode, PTP_OPC_PostalAddressBusinessPostalCode);
652 register_property("Postal Address: Business: Country", LIBMTP_PROPERTY_PostalAddressBusinessCountry, PTP_OPC_PostalAddressBusinessCountry);
653 register_property("Postal Address: Other: Full", LIBMTP_PROPERTY_PostalAddressOtherFull, PTP_OPC_PostalAddressOtherFull);
654 register_property("Postal Address: Other: Line 1", LIBMTP_PROPERTY_PostalAddressOtherLine1, PTP_OPC_PostalAddressOtherLine1);
655 register_property("Postal Address: Other: Line 2", LIBMTP_PROPERTY_PostalAddressOtherLine2, PTP_OPC_PostalAddressOtherLine2);
656 register_property("Postal Address: Other: City", LIBMTP_PROPERTY_PostalAddressOtherCity, PTP_OPC_PostalAddressOtherCity);
657 register_property("Postal Address: Other: Region", LIBMTP_PROPERTY_PostalAddressOtherRegion, PTP_OPC_PostalAddressOtherRegion);
658 register_property("Postal Address: Other: Postal Code", LIBMTP_PROPERTY_PostalAddressOtherPostalCode, PTP_OPC_PostalAddressOtherPostalCode);
659 register_property("Postal Address: Other: Counrtry", LIBMTP_PROPERTY_PostalAddressOtherCountry, PTP_OPC_PostalAddressOtherCountry);
660 register_property("Organization Name", LIBMTP_PROPERTY_OrganizationName, PTP_OPC_OrganizationName);
661 register_property("Phonetic Organization Name", LIBMTP_PROPERTY_PhoneticOrganizationName, PTP_OPC_PhoneticOrganizationName);
662 register_property("Role", LIBMTP_PROPERTY_Role, PTP_OPC_Role);
663 register_property("Birthdate", LIBMTP_PROPERTY_Birthdate, PTP_OPC_Birthdate);
664 register_property("Message To", LIBMTP_PROPERTY_MessageTo, PTP_OPC_MessageTo);
665 register_property("Message CC", LIBMTP_PROPERTY_MessageCC, PTP_OPC_MessageCC);
666 register_property("Message BCC", LIBMTP_PROPERTY_MessageBCC, PTP_OPC_MessageBCC);
667 register_property("Message Read", LIBMTP_PROPERTY_MessageRead, PTP_OPC_MessageRead);
668 register_property("Message Received Time", LIBMTP_PROPERTY_MessageReceivedTime, PTP_OPC_MessageReceivedTime);
669 register_property("Message Sender", LIBMTP_PROPERTY_MessageSender, PTP_OPC_MessageSender);
670 register_property("Activity Begin Time", LIBMTP_PROPERTY_ActivityBeginTime, PTP_OPC_ActivityBeginTime);
671 register_property("Activity End Time", LIBMTP_PROPERTY_ActivityEndTime, PTP_OPC_ActivityEndTime);
672 register_property("Activity Location", LIBMTP_PROPERTY_ActivityLocation, PTP_OPC_ActivityLocation);
673 register_property("Activity Required Attendees", LIBMTP_PROPERTY_ActivityRequiredAttendees, PTP_OPC_ActivityRequiredAttendees);
674 register_property("Optional Attendees", LIBMTP_PROPERTY_ActivityOptionalAttendees, PTP_OPC_ActivityOptionalAttendees);
675 register_property("Activity Resources", LIBMTP_PROPERTY_ActivityResources, PTP_OPC_ActivityResources);
676 register_property("Activity Accepted", LIBMTP_PROPERTY_ActivityAccepted, PTP_OPC_ActivityAccepted);
677 register_property("Owner", LIBMTP_PROPERTY_Owner, PTP_OPC_Owner);
678 register_property("Editor", LIBMTP_PROPERTY_Editor, PTP_OPC_Editor);
679 register_property("Webmaster", LIBMTP_PROPERTY_Webmaster, PTP_OPC_Webmaster);
680 register_property("URL Source", LIBMTP_PROPERTY_URLSource, PTP_OPC_URLSource);
681 register_property("URL Destination", LIBMTP_PROPERTY_URLDestination, PTP_OPC_URLDestination);
682 register_property("Time Bookmark", LIBMTP_PROPERTY_TimeBookmark, PTP_OPC_TimeBookmark);
683 register_property("Object Bookmark", LIBMTP_PROPERTY_ObjectBookmark, PTP_OPC_ObjectBookmark);
684 register_property("Byte Bookmark", LIBMTP_PROPERTY_ByteBookmark, PTP_OPC_ByteBookmark);
685 register_property("Last Build Date", LIBMTP_PROPERTY_LastBuildDate, PTP_OPC_LastBuildDate);
686 register_property("Time To Live", LIBMTP_PROPERTY_TimetoLive, PTP_OPC_TimetoLive);
687 register_property("Media GUID", LIBMTP_PROPERTY_MediaGUID, PTP_OPC_MediaGUID);
688 register_property("Total Bit Rate", LIBMTP_PROPERTY_TotalBitRate, PTP_OPC_TotalBitRate);
689 register_property("Bit Rate Type", LIBMTP_PROPERTY_BitRateType, PTP_OPC_BitRateType);
690 register_property("Sample Rate", LIBMTP_PROPERTY_SampleRate, PTP_OPC_SampleRate);
691 register_property("Number Of Channels", LIBMTP_PROPERTY_NumberOfChannels, PTP_OPC_NumberOfChannels);
692 register_property("Audio Bit Depth", LIBMTP_PROPERTY_AudioBitDepth, PTP_OPC_AudioBitDepth);
693 register_property("Scan Depth", LIBMTP_PROPERTY_ScanDepth, PTP_OPC_ScanDepth);
694 register_property("Audio WAVE Codec", LIBMTP_PROPERTY_AudioWAVECodec, PTP_OPC_AudioWAVECodec);
695 register_property("Audio Bit Rate", LIBMTP_PROPERTY_AudioBitRate, PTP_OPC_AudioBitRate);
696 register_property("Video Four CC Codec", LIBMTP_PROPERTY_VideoFourCCCodec, PTP_OPC_VideoFourCCCodec);
697 register_property("Video Bit Rate", LIBMTP_PROPERTY_VideoBitRate, PTP_OPC_VideoBitRate);
698 register_property("Frames Per Thousand Seconds", LIBMTP_PROPERTY_FramesPerThousandSeconds, PTP_OPC_FramesPerThousandSeconds);
699 register_property("Key Frame Distance", LIBMTP_PROPERTY_KeyFrameDistance, PTP_OPC_KeyFrameDistance);
700 register_property("Buffer Size", LIBMTP_PROPERTY_BufferSize, PTP_OPC_BufferSize);
701 register_property("Encoding Quality", LIBMTP_PROPERTY_EncodingQuality, PTP_OPC_EncodingQuality);
702 register_property("Encoding Profile", LIBMTP_PROPERTY_EncodingProfile, PTP_OPC_EncodingProfile);
703 register_property("Buy flag", LIBMTP_PROPERTY_BuyFlag, PTP_OPC_BuyFlag);
704 register_property("Unknown property", LIBMTP_PROPERTY_UNKNOWN, 0);
705}
706
707/**
708 * Returns the PTP property that maps to a certain libmtp internal property type.
709 * @param inproperty the MTP library interface property
710 * @return the PTP (libgphoto2) property type
711 */
712static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty)
713{
714 propertymap_t *current;
715
Lei Zhang81732132013-09-04 17:27:35 -0700716 current = g_propertymap;
Richard Low6f070842009-05-03 10:26:16 +0000717
718 while (current != NULL) {
719 if(current->id == inproperty) {
720 return current->ptp_id;
721 }
722 current = current->next;
723 }
724 return 0;
725}
726
727
728/**
729 * Returns the MTP internal interface property that maps to a certain ptp
730 * interface property.
731 * @param inproperty the PTP (libgphoto2) interface property
732 * @return the MTP library interface property
733 */
734static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t inproperty)
735{
736 propertymap_t *current;
737
Lei Zhang81732132013-09-04 17:27:35 -0700738 current = g_propertymap;
Richard Low6f070842009-05-03 10:26:16 +0000739
740 while (current != NULL) {
741 if(current->ptp_id == inproperty) {
742 return current->id;
743 }
744 current = current->next;
745 }
746 // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
747 return LIBMTP_PROPERTY_UNKNOWN;
748}
749
Linus Walleijfa1374c2006-02-27 07:41:46 +0000750
Linus Walleij6946ac52006-03-21 06:51:22 +0000751/**
nicklas79daadbf22009-09-28 18:19:34 +0000752 * Set the debug level.
753 *
754 * By default, the debug level is set to '0' (disable).
755 */
756void LIBMTP_Set_Debug(int level)
757{
758 if (LIBMTP_debug || level)
Catalin Patuleada25de02012-07-19 00:57:42 -0400759 LIBMTP_ERROR("LIBMTP_Set_Debug: Setting debugging level to %d (0x%02x) "
760 "(%s)\n", level, level, level ? "on" : "off");
nicklas79daadbf22009-09-28 18:19:34 +0000761
762 LIBMTP_debug = level;
763}
764
765
766/**
Linus Walleijf0f3d482006-05-29 14:10:21 +0000767 * Initialize the library. You are only supposed to call this
768 * one, before using the library for the first time in a program.
769 * Never re-initialize libmtp!
770 *
raveloxd9a28642006-05-26 23:42:22 +0000771 * The only thing this does at the moment is to initialise the
Sajid Anwar8dca41d2012-08-18 20:36:13 +0200772 * filetype mapping table, as well as load MTPZ data if necessary.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000773 */
774void LIBMTP_Init(void)
775{
Catalin Patuleada25de02012-07-19 00:57:42 -0400776 const char *env_debug = getenv("LIBMTP_DEBUG");
777 if (env_debug) {
778 const long debug_flags = strtol(env_debug, NULL, 0);
779 if (debug_flags != LONG_MIN && debug_flags != LONG_MAX &&
780 INT_MIN <= debug_flags && debug_flags <= INT_MAX) {
781 LIBMTP_Set_Debug(debug_flags);
782 } else {
783 fprintf(stderr, "LIBMTP_Init: error setting debug flags from environment "
784 "value \"%s\"\n", env_debug);
785 }
786 }
nicklas79daadbf22009-09-28 18:19:34 +0000787
raveloxd9a28642006-05-26 23:42:22 +0000788 init_filemap();
Richard Low6f070842009-05-03 10:26:16 +0000789 init_propertymap();
Sajid Anwar8dca41d2012-08-18 20:36:13 +0200790
791 if (mtpz_loaddata() == -1)
792 use_mtpz = 0;
793 else
794 use_mtpz = 1;
795
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000796 return;
797}
798
Linus Walleijcf42f452006-11-28 08:32:49 +0000799
800/**
801 * This helper function returns a textual description for a libmtp
802 * file type to be used in dialog boxes etc.
803 * @param intype the libmtp internal filetype to get a description for.
804 * @return a string representing the filetype, this must <b>NOT</b>
805 * be free():ed by the caller!
806 */
807char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
808{
809 filemap_t *current;
810
Lei Zhang81732132013-09-04 17:27:35 -0700811 current = g_filemap;
Linus Walleijcf42f452006-11-28 08:32:49 +0000812
813 while (current != NULL) {
814 if(current->id == intype) {
815 return current->description;
816 }
817 current = current->next;
818 }
819
820 return "Unknown filetype";
821}
822
Linus Walleij7783b9b2007-09-22 22:10:44 +0000823/**
Richard Low6f070842009-05-03 10:26:16 +0000824 * This helper function returns a textual description for a libmtp
825 * property to be used in dialog boxes etc.
826 * @param inproperty the libmtp internal property to get a description for.
827 * @return a string representing the filetype, this must <b>NOT</b>
828 * be free():ed by the caller!
829 */
830char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty)
831{
832 propertymap_t *current;
833
Lei Zhang81732132013-09-04 17:27:35 -0700834 current = g_propertymap;
Richard Low6f070842009-05-03 10:26:16 +0000835
836 while (current != NULL) {
837 if(current->id == inproperty) {
838 return current->description;
839 }
840 current = current->next;
841 }
842
843 return "Unknown property";
844}
845
846/**
Linus Walleij7783b9b2007-09-22 22:10:44 +0000847 * This function will do its best to fit a 16bit
848 * value into a PTP object property if the property
849 * is limited in range or step sizes.
850 */
Linus Walleij29559562007-08-22 21:38:04 +0000851static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd)
852{
853 switch (opd->FormFlag) {
854 case PTP_DPFF_Range:
855 if (val < opd->FORM.Range.MinimumValue.u16) {
856 return opd->FORM.Range.MinimumValue.u16;
857 }
858 if (val > opd->FORM.Range.MaximumValue.u16) {
859 return opd->FORM.Range.MaximumValue.u16;
860 }
861 // Round down to last step.
862 if (val % opd->FORM.Range.StepSize.u16 != 0) {
863 return val - (val % opd->FORM.Range.StepSize.u16);
864 }
865 return val;
866 break;
867 case PTP_DPFF_Enumeration:
868 {
869 int i;
870 uint16_t bestfit = opd->FORM.Enum.SupportedValue[0].u16;
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000871
Linus Walleij29559562007-08-22 21:38:04 +0000872 for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
873 if (val == opd->FORM.Enum.SupportedValue[i].u16) {
874 return val;
875 }
876 // Rough guess of best fit
877 if (opd->FORM.Enum.SupportedValue[i].u16 < val) {
878 bestfit = opd->FORM.Enum.SupportedValue[i].u16;
879 }
880 }
881 // Just some default that'll work.
882 return bestfit;
883 }
884 default:
885 // Will accept any value
886 break;
887 }
888 return val;
889}
890
Linus Walleij7783b9b2007-09-22 22:10:44 +0000891/**
892 * This function will do its best to fit a 32bit
893 * value into a PTP object property if the property
894 * is limited in range or step sizes.
895 */
Linus Walleij29559562007-08-22 21:38:04 +0000896static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd)
897{
898 switch (opd->FormFlag) {
899 case PTP_DPFF_Range:
900 if (val < opd->FORM.Range.MinimumValue.u32) {
901 return opd->FORM.Range.MinimumValue.u32;
902 }
903 if (val > opd->FORM.Range.MaximumValue.u32) {
904 return opd->FORM.Range.MaximumValue.u32;
905 }
906 // Round down to last step.
907 if (val % opd->FORM.Range.StepSize.u32 != 0) {
908 return val - (val % opd->FORM.Range.StepSize.u32);
909 }
910 return val;
911 break;
912 case PTP_DPFF_Enumeration:
913 {
914 int i;
915 uint32_t bestfit = opd->FORM.Enum.SupportedValue[0].u32;
916
917 for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
918 if (val == opd->FORM.Enum.SupportedValue[i].u32) {
919 return val;
920 }
921 // Rough guess of best fit
922 if (opd->FORM.Enum.SupportedValue[i].u32 < val) {
923 bestfit = opd->FORM.Enum.SupportedValue[i].u32;
924 }
925 }
926 // Just some default that'll work.
927 return bestfit;
928 }
929 default:
930 // Will accept any value
931 break;
932 }
933 return val;
934}
935
Linus Walleij7783b9b2007-09-22 22:10:44 +0000936/**
Linus Walleij7783b9b2007-09-22 22:10:44 +0000937 * This function returns a newly created ISO 8601 timestamp with the
938 * current time in as high precision as possible. It even adds
939 * the time zone if it can.
940 */
941static char *get_iso8601_stamp(void)
942{
943 time_t curtime;
944 struct tm *loctime;
945 char tmp[64];
946
947 curtime = time(NULL);
948 loctime = localtime(&curtime);
949 strftime (tmp, sizeof(tmp), "%Y%m%dT%H%M%S.0%z", loctime);
950 return strdup(tmp);
951}
952
953/**
Richard Low6f070842009-05-03 10:26:16 +0000954 * Gets the allowed values (range or enum) for a property
955 * @param device a pointer to an MTP device
956 * @param property the property to query
957 * @param filetype the filetype of the object you want to set values for
958 * @param allowed_vals pointer to a LIBMTP_allowed_values_t struct to
959 * receive the allowed values. Call LIBMTP_destroy_allowed_values_t
960 * on this on successful completion.
961 * @return 0 on success, any other value means failure
962 */
963int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
964 LIBMTP_filetype_t const filetype, LIBMTP_allowed_values_t *allowed_vals)
965{
966 PTPObjectPropDesc opd;
967 uint16_t ret = 0;
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000968
Richard Low6f070842009-05-03 10:26:16 +0000969 ret = ptp_mtp_getobjectpropdesc(device->params, map_libmtp_property_to_ptp_property(property), map_libmtp_type_to_ptp_type(filetype), &opd);
970 if (ret != PTP_RC_OK) {
971 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Allowed_Property_Values(): could not get property description.");
972 return -1;
973 }
974
975 if (opd.FormFlag == PTP_OPFF_Enumeration) {
976 int i = 0;
Linus Walleij2c1bbd62009-11-07 15:15:03 +0000977
Richard Low6f070842009-05-03 10:26:16 +0000978 allowed_vals->is_range = 0;
979 allowed_vals->num_entries = opd.FORM.Enum.NumberOfValues;
980
981 switch (opd.DataType)
982 {
983 case PTP_DTC_INT8:
984 allowed_vals->i8vals = malloc(sizeof(int8_t) * opd.FORM.Enum.NumberOfValues);
985 allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
986 break;
987 case PTP_DTC_UINT8:
988 allowed_vals->u8vals = malloc(sizeof(uint8_t) * opd.FORM.Enum.NumberOfValues);
989 allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
990 break;
991 case PTP_DTC_INT16:
992 allowed_vals->i16vals = malloc(sizeof(int16_t) * opd.FORM.Enum.NumberOfValues);
993 allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
994 break;
995 case PTP_DTC_UINT16:
996 allowed_vals->u16vals = malloc(sizeof(uint16_t) * opd.FORM.Enum.NumberOfValues);
997 allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
998 break;
999 case PTP_DTC_INT32:
1000 allowed_vals->i32vals = malloc(sizeof(int32_t) * opd.FORM.Enum.NumberOfValues);
1001 allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
1002 break;
1003 case PTP_DTC_UINT32:
1004 allowed_vals->u32vals = malloc(sizeof(uint32_t) * opd.FORM.Enum.NumberOfValues);
1005 allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
1006 break;
1007 case PTP_DTC_INT64:
1008 allowed_vals->i64vals = malloc(sizeof(int64_t) * opd.FORM.Enum.NumberOfValues);
1009 allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
1010 break;
1011 case PTP_DTC_UINT64:
1012 allowed_vals->u64vals = malloc(sizeof(uint64_t) * opd.FORM.Enum.NumberOfValues);
1013 allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
1014 break;
1015 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001016
Richard Low6f070842009-05-03 10:26:16 +00001017 for (i = 0; i < opd.FORM.Enum.NumberOfValues; i++) {
1018 switch (opd.DataType)
1019 {
1020 case PTP_DTC_INT8:
1021 allowed_vals->i8vals[i] = opd.FORM.Enum.SupportedValue[i].i8;
1022 break;
1023 case PTP_DTC_UINT8:
1024 allowed_vals->u8vals[i] = opd.FORM.Enum.SupportedValue[i].u8;
1025 break;
1026 case PTP_DTC_INT16:
1027 allowed_vals->i16vals[i] = opd.FORM.Enum.SupportedValue[i].i16;
1028 break;
1029 case PTP_DTC_UINT16:
1030 allowed_vals->u16vals[i] = opd.FORM.Enum.SupportedValue[i].u16;
1031 break;
1032 case PTP_DTC_INT32:
1033 allowed_vals->i32vals[i] = opd.FORM.Enum.SupportedValue[i].i32;
1034 break;
1035 case PTP_DTC_UINT32:
1036 allowed_vals->u32vals[i] = opd.FORM.Enum.SupportedValue[i].u32;
1037 break;
1038 case PTP_DTC_INT64:
1039 allowed_vals->i64vals[i] = opd.FORM.Enum.SupportedValue[i].i64;
1040 break;
1041 case PTP_DTC_UINT64:
1042 allowed_vals->u64vals[i] = opd.FORM.Enum.SupportedValue[i].u64;
1043 break;
1044 }
1045 }
1046 ptp_free_objectpropdesc(&opd);
1047 return 0;
1048 } else if (opd.FormFlag == PTP_OPFF_Range) {
1049 allowed_vals->is_range = 1;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001050
Richard Low6f070842009-05-03 10:26:16 +00001051 switch (opd.DataType)
1052 {
1053 case PTP_DTC_INT8:
1054 allowed_vals->i8min = opd.FORM.Range.MinimumValue.i8;
1055 allowed_vals->i8max = opd.FORM.Range.MaximumValue.i8;
1056 allowed_vals->i8step = opd.FORM.Range.StepSize.i8;
1057 allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
1058 break;
1059 case PTP_DTC_UINT8:
1060 allowed_vals->u8min = opd.FORM.Range.MinimumValue.u8;
1061 allowed_vals->u8max = opd.FORM.Range.MaximumValue.u8;
1062 allowed_vals->u8step = opd.FORM.Range.StepSize.u8;
1063 allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
1064 break;
1065 case PTP_DTC_INT16:
1066 allowed_vals->i16min = opd.FORM.Range.MinimumValue.i16;
1067 allowed_vals->i16max = opd.FORM.Range.MaximumValue.i16;
1068 allowed_vals->i16step = opd.FORM.Range.StepSize.i16;
1069 allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
1070 break;
1071 case PTP_DTC_UINT16:
1072 allowed_vals->u16min = opd.FORM.Range.MinimumValue.u16;
1073 allowed_vals->u16max = opd.FORM.Range.MaximumValue.u16;
1074 allowed_vals->u16step = opd.FORM.Range.StepSize.u16;
1075 allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
1076 break;
1077 case PTP_DTC_INT32:
1078 allowed_vals->i32min = opd.FORM.Range.MinimumValue.i32;
1079 allowed_vals->i32max = opd.FORM.Range.MaximumValue.i32;
1080 allowed_vals->i32step = opd.FORM.Range.StepSize.i32;
1081 allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
1082 break;
1083 case PTP_DTC_UINT32:
1084 allowed_vals->u32min = opd.FORM.Range.MinimumValue.u32;
1085 allowed_vals->u32max = opd.FORM.Range.MaximumValue.u32;
1086 allowed_vals->u32step = opd.FORM.Range.StepSize.u32;
1087 allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
1088 break;
1089 case PTP_DTC_INT64:
1090 allowed_vals->i64min = opd.FORM.Range.MinimumValue.i64;
1091 allowed_vals->i64max = opd.FORM.Range.MaximumValue.i64;
1092 allowed_vals->i64step = opd.FORM.Range.StepSize.i64;
1093 allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
1094 break;
1095 case PTP_DTC_UINT64:
1096 allowed_vals->u64min = opd.FORM.Range.MinimumValue.u64;
1097 allowed_vals->u64max = opd.FORM.Range.MaximumValue.u64;
1098 allowed_vals->u64step = opd.FORM.Range.StepSize.u64;
1099 allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
1100 break;
1101 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001102 return 0;
Richard Low6f070842009-05-03 10:26:16 +00001103 } else
1104 return -1;
1105}
1106
1107/**
1108 * Destroys a LIBMTP_allowed_values_t struct
1109 * @param allowed_vals the struct to destroy
1110 */
1111void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t *allowed_vals)
1112{
1113 if (!allowed_vals->is_range)
1114 {
1115 switch (allowed_vals->datatype)
1116 {
1117 case LIBMTP_DATATYPE_INT8:
1118 if (allowed_vals->i8vals)
1119 free(allowed_vals->i8vals);
1120 break;
1121 case LIBMTP_DATATYPE_UINT8:
1122 if (allowed_vals->u8vals)
1123 free(allowed_vals->u8vals);
1124 break;
1125 case LIBMTP_DATATYPE_INT16:
1126 if (allowed_vals->i16vals)
1127 free(allowed_vals->i16vals);
1128 break;
1129 case LIBMTP_DATATYPE_UINT16:
1130 if (allowed_vals->u16vals)
1131 free(allowed_vals->u16vals);
1132 break;
1133 case LIBMTP_DATATYPE_INT32:
1134 if (allowed_vals->i32vals)
1135 free(allowed_vals->i32vals);
1136 break;
1137 case LIBMTP_DATATYPE_UINT32:
1138 if (allowed_vals->u32vals)
1139 free(allowed_vals->u32vals);
1140 break;
1141 case LIBMTP_DATATYPE_INT64:
1142 if (allowed_vals->i64vals)
1143 free(allowed_vals->i64vals);
1144 break;
1145 case LIBMTP_DATATYPE_UINT64:
1146 if (allowed_vals->u64vals)
1147 free(allowed_vals->u64vals);
1148 break;
1149 }
1150 }
1151}
1152
1153/**
1154 * Determine if a property is supported for a given file type
1155 * @param device a pointer to an MTP device
1156 * @param property the property to query
1157 * @param filetype the filetype of the object you want to set values for
1158 * @return 0 if not supported, positive if supported, negative on error
1159 */
1160int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
1161 LIBMTP_filetype_t const filetype)
1162{
1163 uint16_t *props = NULL;
1164 uint32_t propcnt = 0;
1165 uint16_t ret = 0;
1166 int i = 0;
1167 int supported = 0;
1168 uint16_t ptp_prop = map_libmtp_property_to_ptp_property(property);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001169
Lei Zhangcaa1bbc2013-02-13 15:12:02 -08001170 if (!ptp_operation_issupported(device->params, PTP_OC_MTP_GetObjectPropsSupported))
1171 return 0;
1172
Richard Low6f070842009-05-03 10:26:16 +00001173 ret = ptp_mtp_getobjectpropssupported(device->params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
1174 if (ret != PTP_RC_OK) {
1175 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Is_Property_Supported(): could not get properties supported.");
1176 return -1;
1177 }
1178
1179 for (i = 0; i < propcnt; i++) {
1180 if (props[i] == ptp_prop) {
1181 supported = 1;
1182 break;
1183 }
1184 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001185
Richard Low6f070842009-05-03 10:26:16 +00001186 free(props);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001187
Richard Low6f070842009-05-03 10:26:16 +00001188 return supported;
1189}
1190
1191/**
1192 * Retrieves a string from an object
1193 *
1194 * @param device a pointer to an MTP device.
1195 * @param object_id Object reference
1196 * @param attribute_id MTP attribute ID
1197 * @return valid string or NULL on failure. The returned string
1198 * must bee <code>free()</code>:ed by the caller after
1199 * use.
1200 */
1201char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1202 LIBMTP_property_t const attribute_id)
1203{
1204 return get_string_from_object(device, object_id, attribute_id);
1205}
1206
1207/**
1208* Retrieves an unsigned 64-bit integer from an object attribute
1209 *
1210 * @param device a pointer to an MTP device.
1211 * @param object_id Object reference
1212 * @param attribute_id MTP attribute ID
1213 * @param value_default Default value to return on failure
1214 * @return the value
1215 */
1216uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1217 LIBMTP_property_t const attribute_id, uint64_t const value_default)
1218{
1219 return get_u64_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1220}
1221
1222/**
1223 * Retrieves an unsigned 32-bit integer from an object attribute
1224 *
1225 * @param device a pointer to an MTP device.
1226 * @param object_id Object reference
1227 * @param attribute_id MTP attribute ID
1228 * @param value_default Default value to return on failure
1229 * @return the value
1230 */
1231uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1232 LIBMTP_property_t const attribute_id, uint32_t const value_default)
1233{
1234 return get_u32_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1235}
1236
1237/**
1238 * Retrieves an unsigned 16-bit integer from an object attribute
1239 *
1240 * @param device a pointer to an MTP device.
1241 * @param object_id Object reference
1242 * @param attribute_id MTP attribute ID
1243 * @param value_default Default value to return on failure
1244 * @return a value
1245 */
1246uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1247 LIBMTP_property_t const attribute_id, uint16_t const value_default)
1248{
1249 return get_u16_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1250}
1251
1252/**
1253 * Retrieves an unsigned 8-bit integer from an object attribute
1254 *
1255 * @param device a pointer to an MTP device.
1256 * @param object_id Object reference
1257 * @param attribute_id MTP attribute ID
1258 * @param value_default Default value to return on failure
1259 * @return a value
1260 */
1261uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1262 LIBMTP_property_t const attribute_id, uint8_t const value_default)
1263{
1264 return get_u8_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
1265}
1266
1267/**
1268 * Sets an object attribute from a string
1269 *
1270 * @param device a pointer to an MTP device.
1271 * @param object_id Object reference
1272 * @param attribute_id MTP attribute ID
1273 * @param string string value to set
1274 * @return 0 on success, any other value means failure
1275 */
1276int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1277 LIBMTP_property_t const attribute_id, char const * const string)
1278{
1279 return set_object_string(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), string);
1280}
1281
1282
1283/**
1284 * Sets an object attribute from an unsigned 32-bit integer
1285 *
1286 * @param device a pointer to an MTP device.
1287 * @param object_id Object reference
1288 * @param attribute_id MTP attribute ID
1289 * @param value 32-bit unsigned integer to set
1290 * @return 0 on success, any other value means failure
1291 */
1292int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1293 LIBMTP_property_t const attribute_id, uint32_t const value)
1294{
1295 return set_object_u32(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1296}
1297
1298/**
1299 * Sets an object attribute from an unsigned 16-bit integer
1300 *
1301 * @param device a pointer to an MTP device.
1302 * @param object_id Object reference
1303 * @param attribute_id MTP attribute ID
1304 * @param value 16-bit unsigned integer to set
1305 * @return 0 on success, any other value means failure
1306 */
1307int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1308 LIBMTP_property_t const attribute_id, uint16_t const value)
1309{
1310 return set_object_u16(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1311}
1312
1313/**
1314 * Sets an object attribute from an unsigned 8-bit integer
1315 *
1316 * @param device a pointer to an MTP device.
1317 * @param object_id Object reference
1318 * @param attribute_id MTP attribute ID
1319 * @param value 8-bit unsigned integer to set
1320 * @return 0 on success, any other value means failure
1321 */
1322int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1323 LIBMTP_property_t const attribute_id, uint8_t const value)
1324{
1325 return set_object_u8(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
1326}
1327
1328/**
raveloxd9a28642006-05-26 23:42:22 +00001329 * Retrieves a string from an object
1330 *
1331 * @param device a pointer to an MTP device.
1332 * @param object_id Object reference
1333 * @param attribute_id PTP attribute ID
Linus Walleij438bd7f2006-06-08 11:35:44 +00001334 * @return valid string or NULL on failure. The returned string
1335 * must bee <code>free()</code>:ed by the caller after
1336 * use.
raveloxd9a28642006-05-26 23:42:22 +00001337 */
Linus Walleij9901e222006-11-30 12:28:19 +00001338static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001339 uint16_t const attribute_id)
raveloxd9a28642006-05-26 23:42:22 +00001340{
1341 PTPPropertyValue propval;
1342 char *retstring = NULL;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001343 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001344 uint16_t ret;
Linus Walleijd4637502009-06-14 23:03:33 +00001345 MTPProperties *prop;
mopoke96143402006-10-30 04:37:26 +00001346
Marcus Meissner9d404be2015-03-22 14:37:54 +01001347 if (!device || !object_id)
Linus Walleijf0f3d482006-05-29 14:10:21 +00001348 return NULL;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001349
1350 params = (PTPParams *) device->params;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001351
Linus Walleijd4637502009-06-14 23:03:33 +00001352 prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1353 if (prop) {
1354 if (prop->propval.str != NULL)
1355 return strdup(prop->propval.str);
1356 else
1357 return NULL;
Linus Walleij338ade42007-07-03 20:44:08 +00001358 }
mopoke96143402006-10-30 04:37:26 +00001359
Linus Walleija823a702006-08-27 21:27:46 +00001360 ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
raveloxd9a28642006-05-26 23:42:22 +00001361 if (ret == PTP_RC_OK) {
Linus Walleija823a702006-08-27 21:27:46 +00001362 if (propval.str != NULL) {
1363 retstring = (char *) strdup(propval.str);
1364 free(propval.str);
raveloxd9a28642006-05-26 23:42:22 +00001365 }
Linus Walleij070e9b42007-01-22 08:49:28 +00001366 } else {
1367 add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
raveloxd9a28642006-05-26 23:42:22 +00001368 }
mopoke96143402006-10-30 04:37:26 +00001369
raveloxd9a28642006-05-26 23:42:22 +00001370 return retstring;
1371}
1372
1373/**
Richard Lowc3f5fc32007-07-29 09:40:50 +00001374* Retrieves an unsigned 64-bit integer from an object attribute
1375 *
1376 * @param device a pointer to an MTP device.
1377 * @param object_id Object reference
1378 * @param attribute_id PTP attribute ID
1379 * @param value_default Default value to return on failure
1380 * @return the value
1381 */
1382static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
1383 uint16_t const attribute_id, uint64_t const value_default)
1384{
1385 PTPPropertyValue propval;
1386 uint64_t retval = value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001387 PTPParams *params;
Richard Lowc3f5fc32007-07-29 09:40:50 +00001388 uint16_t ret;
Linus Walleijd4637502009-06-14 23:03:33 +00001389 MTPProperties *prop;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001390
Marcus Meissner9d404be2015-03-22 14:37:54 +01001391 if (!device)
Richard Lowc3f5fc32007-07-29 09:40:50 +00001392 return value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001393
1394 params = (PTPParams *) device->params;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001395
Linus Walleijd4637502009-06-14 23:03:33 +00001396 prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1397 if (prop)
1398 return prop->propval.u64;
Linus Walleij1e9a0332007-09-12 19:35:56 +00001399
Richard Lowc3f5fc32007-07-29 09:40:50 +00001400 ret = ptp_mtp_getobjectpropvalue(params, object_id,
1401 attribute_id,
1402 &propval,
1403 PTP_DTC_UINT64);
1404 if (ret == PTP_RC_OK) {
1405 retval = propval.u64;
1406 } else {
1407 add_ptp_error_to_errorstack(device, ret, "get_u64_from_object(): could not get unsigned 64bit integer from object.");
1408 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001409
Richard Lowc3f5fc32007-07-29 09:40:50 +00001410 return retval;
1411}
1412
1413/**
raveloxd9a28642006-05-26 23:42:22 +00001414 * Retrieves an unsigned 32-bit integer from an object attribute
1415 *
1416 * @param device a pointer to an MTP device.
1417 * @param object_id Object reference
1418 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +00001419 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +00001420 * @return the value
raveloxd9a28642006-05-26 23:42:22 +00001421 */
Linus Walleij9901e222006-11-30 12:28:19 +00001422static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001423 uint16_t const attribute_id, uint32_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +00001424{
1425 PTPPropertyValue propval;
1426 uint32_t retval = value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001427 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001428 uint16_t ret;
Linus Walleijd4637502009-06-14 23:03:33 +00001429 MTPProperties *prop;
mopoke96143402006-10-30 04:37:26 +00001430
Marcus Meissner9d404be2015-03-22 14:37:54 +01001431 if (!device)
Linus Walleijf0f3d482006-05-29 14:10:21 +00001432 return value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001433
1434 params = (PTPParams *) device->params;
raveloxd9a28642006-05-26 23:42:22 +00001435
Linus Walleijd4637502009-06-14 23:03:33 +00001436 prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1437 if (prop)
1438 return prop->propval.u32;
Linus Walleij338ade42007-07-03 20:44:08 +00001439
raveloxd9a28642006-05-26 23:42:22 +00001440 ret = ptp_mtp_getobjectpropvalue(params, object_id,
1441 attribute_id,
1442 &propval,
1443 PTP_DTC_UINT32);
1444 if (ret == PTP_RC_OK) {
1445 retval = propval.u32;
Linus Walleij070e9b42007-01-22 08:49:28 +00001446 } else {
1447 add_ptp_error_to_errorstack(device, ret, "get_u32_from_object(): could not get unsigned 32bit integer from object.");
raveloxd9a28642006-05-26 23:42:22 +00001448 }
raveloxd9a28642006-05-26 23:42:22 +00001449 return retval;
1450}
1451
1452/**
1453 * Retrieves an unsigned 16-bit integer from an object attribute
1454 *
1455 * @param device a pointer to an MTP device.
1456 * @param object_id Object reference
1457 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +00001458 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +00001459 * @return a value
raveloxd9a28642006-05-26 23:42:22 +00001460 */
Linus Walleij9901e222006-11-30 12:28:19 +00001461static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001462 uint16_t const attribute_id, uint16_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +00001463{
1464 PTPPropertyValue propval;
1465 uint16_t retval = value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001466 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001467 uint16_t ret;
Linus Walleijd4637502009-06-14 23:03:33 +00001468 MTPProperties *prop;
raveloxd9a28642006-05-26 23:42:22 +00001469
Marcus Meissner9d404be2015-03-22 14:37:54 +01001470 if (!device)
Linus Walleijf0f3d482006-05-29 14:10:21 +00001471 return value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001472
1473 params = (PTPParams *) device->params;
raveloxd9a28642006-05-26 23:42:22 +00001474
Linus Walleij338ade42007-07-03 20:44:08 +00001475 // This O(n) search should not be used so often, since code
1476 // using the cached properties don't usually call this function.
Linus Walleijd4637502009-06-14 23:03:33 +00001477 prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1478 if (prop)
1479 return prop->propval.u16;
Linus Walleij338ade42007-07-03 20:44:08 +00001480
raveloxd9a28642006-05-26 23:42:22 +00001481 ret = ptp_mtp_getobjectpropvalue(params, object_id,
1482 attribute_id,
1483 &propval,
1484 PTP_DTC_UINT16);
1485 if (ret == PTP_RC_OK) {
1486 retval = propval.u16;
Linus Walleij070e9b42007-01-22 08:49:28 +00001487 } else {
1488 add_ptp_error_to_errorstack(device, ret, "get_u16_from_object(): could not get unsigned 16bit integer from object.");
raveloxd9a28642006-05-26 23:42:22 +00001489 }
mopoke96143402006-10-30 04:37:26 +00001490
raveloxd9a28642006-05-26 23:42:22 +00001491 return retval;
1492}
1493
1494/**
Linus Walleij99310d42006-11-01 08:29:39 +00001495 * Retrieves an unsigned 8-bit integer from an object attribute
1496 *
1497 * @param device a pointer to an MTP device.
1498 * @param object_id Object reference
1499 * @param attribute_id PTP attribute ID
1500 * @param value_default Default value to return on failure
1501 * @return a value
1502 */
Linus Walleij9901e222006-11-30 12:28:19 +00001503static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij99310d42006-11-01 08:29:39 +00001504 uint16_t const attribute_id, uint8_t const value_default)
1505{
1506 PTPPropertyValue propval;
1507 uint8_t retval = value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001508 PTPParams *params;
Linus Walleij99310d42006-11-01 08:29:39 +00001509 uint16_t ret;
Linus Walleijd4637502009-06-14 23:03:33 +00001510 MTPProperties *prop;
Linus Walleij99310d42006-11-01 08:29:39 +00001511
Marcus Meissner9d404be2015-03-22 14:37:54 +01001512 if (!device)
Linus Walleij99310d42006-11-01 08:29:39 +00001513 return value_default;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001514
1515 params = (PTPParams *) device->params;
Linus Walleij99310d42006-11-01 08:29:39 +00001516
Linus Walleij338ade42007-07-03 20:44:08 +00001517 // This O(n) search should not be used so often, since code
1518 // using the cached properties don't usually call this function.
Linus Walleijd4637502009-06-14 23:03:33 +00001519 prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
1520 if (prop)
1521 return prop->propval.u8;
Linus Walleij338ade42007-07-03 20:44:08 +00001522
Linus Walleij99310d42006-11-01 08:29:39 +00001523 ret = ptp_mtp_getobjectpropvalue(params, object_id,
1524 attribute_id,
1525 &propval,
1526 PTP_DTC_UINT8);
1527 if (ret == PTP_RC_OK) {
1528 retval = propval.u8;
Linus Walleij070e9b42007-01-22 08:49:28 +00001529 } else {
1530 add_ptp_error_to_errorstack(device, ret, "get_u8_from_object(): could not get unsigned 8bit integer from object.");
Linus Walleij99310d42006-11-01 08:29:39 +00001531 }
1532
1533 return retval;
1534}
1535
1536/**
raveloxd9a28642006-05-26 23:42:22 +00001537 * Sets an object attribute from a string
1538 *
1539 * @param device a pointer to an MTP device.
1540 * @param object_id Object reference
1541 * @param attribute_id PTP attribute ID
1542 * @param string string value to set
Linus Walleijf0f3d482006-05-29 14:10:21 +00001543 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +00001544 */
Linus Walleij9901e222006-11-30 12:28:19 +00001545static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001546 uint16_t const attribute_id, char const * const string)
raveloxd9a28642006-05-26 23:42:22 +00001547{
1548 PTPPropertyValue propval;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001549 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001550 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +00001551
Marcus Meissner9d404be2015-03-22 14:37:54 +01001552 if (!device || !string)
Linus Walleij438bd7f2006-06-08 11:35:44 +00001553 return -1;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001554
1555 params = (PTPParams *) device->params;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001556
Linus Walleijb2753182007-02-26 08:11:38 +00001557 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1558 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
1559 "PTP_OC_MTP_SetObjectPropValue not supported.");
1560 return -1;
1561 }
Linus Walleija823a702006-08-27 21:27:46 +00001562 propval.str = (char *) string;
1563 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
Linus Walleijf0f3d482006-05-29 14:10:21 +00001564 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001565 add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00001566 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +00001567 }
mopoke96143402006-10-30 04:37:26 +00001568
Linus Walleijf0f3d482006-05-29 14:10:21 +00001569 return 0;
raveloxd9a28642006-05-26 23:42:22 +00001570}
1571
Linus Walleij29559562007-08-22 21:38:04 +00001572
raveloxd9a28642006-05-26 23:42:22 +00001573/**
1574 * Sets an object attribute from an unsigned 32-bit integer
1575 *
1576 * @param device a pointer to an MTP device.
1577 * @param object_id Object reference
1578 * @param attribute_id PTP attribute ID
1579 * @param value 32-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +00001580 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +00001581 */
Linus Walleij9901e222006-11-30 12:28:19 +00001582static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001583 uint16_t const attribute_id, uint32_t const value)
raveloxd9a28642006-05-26 23:42:22 +00001584{
1585 PTPPropertyValue propval;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001586 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001587 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +00001588
Marcus Meissner9d404be2015-03-22 14:37:54 +01001589 if (!device)
Linus Walleij438bd7f2006-06-08 11:35:44 +00001590 return -1;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001591
1592 params = (PTPParams *) device->params;
raveloxd9a28642006-05-26 23:42:22 +00001593
Linus Walleijb2753182007-02-26 08:11:38 +00001594 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1595 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
1596 "PTP_OC_MTP_SetObjectPropValue not supported.");
1597 return -1;
1598 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001599
raveloxd9a28642006-05-26 23:42:22 +00001600 propval.u32 = value;
1601 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
Linus Walleijf0f3d482006-05-29 14:10:21 +00001602 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001603 add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00001604 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +00001605 }
mopoke96143402006-10-30 04:37:26 +00001606
Linus Walleijf0f3d482006-05-29 14:10:21 +00001607 return 0;
raveloxd9a28642006-05-26 23:42:22 +00001608}
1609
1610/**
1611 * Sets an object attribute from an unsigned 16-bit integer
1612 *
1613 * @param device a pointer to an MTP device.
1614 * @param object_id Object reference
1615 * @param attribute_id PTP attribute ID
1616 * @param value 16-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +00001617 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +00001618 */
Linus Walleij9901e222006-11-30 12:28:19 +00001619static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +00001620 uint16_t const attribute_id, uint16_t const value)
raveloxd9a28642006-05-26 23:42:22 +00001621{
1622 PTPPropertyValue propval;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001623 PTPParams *params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00001624 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +00001625
Marcus Meissner9d404be2015-03-22 14:37:54 +01001626 if (!device)
Linus Walleijf0f3d482006-05-29 14:10:21 +00001627 return 1;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001628
1629 params = (PTPParams *) device->params;
raveloxd9a28642006-05-26 23:42:22 +00001630
Linus Walleijb2753182007-02-26 08:11:38 +00001631 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1632 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
1633 "PTP_OC_MTP_SetObjectPropValue not supported.");
1634 return -1;
1635 }
raveloxd9a28642006-05-26 23:42:22 +00001636 propval.u16 = value;
1637 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
Linus Walleijf0f3d482006-05-29 14:10:21 +00001638 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001639 add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +00001640 return 1;
1641 }
raveloxd9a28642006-05-26 23:42:22 +00001642
Linus Walleijf0f3d482006-05-29 14:10:21 +00001643 return 0;
raveloxd9a28642006-05-26 23:42:22 +00001644}
1645
1646/**
Linus Walleij99310d42006-11-01 08:29:39 +00001647 * Sets an object attribute from an unsigned 8-bit integer
1648 *
1649 * @param device a pointer to an MTP device.
1650 * @param object_id Object reference
1651 * @param attribute_id PTP attribute ID
1652 * @param value 8-bit unsigned integer to set
1653 * @return 0 on success, any other value means failure
1654 */
Linus Walleij9901e222006-11-30 12:28:19 +00001655static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
1656 uint16_t const attribute_id, uint8_t const value)
Linus Walleij99310d42006-11-01 08:29:39 +00001657{
1658 PTPPropertyValue propval;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001659 PTPParams *params;
Linus Walleij99310d42006-11-01 08:29:39 +00001660 uint16_t ret;
1661
Marcus Meissner9d404be2015-03-22 14:37:54 +01001662 if (!device)
Linus Walleij99310d42006-11-01 08:29:39 +00001663 return 1;
Marcus Meissner9d404be2015-03-22 14:37:54 +01001664
1665 params = (PTPParams *) device->params;
Linus Walleij99310d42006-11-01 08:29:39 +00001666
Linus Walleijb2753182007-02-26 08:11:38 +00001667 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
1668 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
1669 "PTP_OC_MTP_SetObjectPropValue not supported.");
1670 return -1;
1671 }
Linus Walleij99310d42006-11-01 08:29:39 +00001672 propval.u8 = value;
1673 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
1674 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001675 add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +00001676 return 1;
raveloxd9a28642006-05-26 23:42:22 +00001677 }
mopoke96143402006-10-30 04:37:26 +00001678
Linus Walleijf0f3d482006-05-29 14:10:21 +00001679 return 0;
raveloxd9a28642006-05-26 23:42:22 +00001680}
1681
raveloxd9a28642006-05-26 23:42:22 +00001682/**
Linus Walleij039d1dd2007-02-23 22:34:07 +00001683 * Get the first (as in "first in the list of") connected MTP device.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00001684 * @return a device pointer.
Linus Walleij039d1dd2007-02-23 22:34:07 +00001685 * @see LIBMTP_Get_Connected_Devices()
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00001686 */
Linus Walleijb9256fd2006-02-15 09:40:43 +00001687LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00001688{
tedbullocke7713642007-02-18 23:10:09 +00001689 LIBMTP_mtpdevice_t *first_device = NULL;
Linus Walleij64691b72008-05-31 22:55:18 +00001690 LIBMTP_raw_device_t *devices;
1691 int numdevs;
1692 LIBMTP_error_number_t ret;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001693
Linus Walleij64691b72008-05-31 22:55:18 +00001694 ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
1695 if (ret != LIBMTP_ERROR_NONE) {
Richard Low0f794762007-02-23 22:06:27 +00001696 return NULL;
Linus Walleija8a19cc2007-02-02 22:13:17 +00001697 }
tedbullock44b0ff72007-02-22 22:20:28 +00001698
Linus Walleij64691b72008-05-31 22:55:18 +00001699 if (devices == NULL || numdevs == 0) {
Marcus Meissnerb5214772015-10-04 13:08:20 +02001700 free(devices);
Linus Walleij64691b72008-05-31 22:55:18 +00001701 return NULL;
tedbullock44b0ff72007-02-22 22:20:28 +00001702 }
Linus Walleij64691b72008-05-31 22:55:18 +00001703
1704 first_device = LIBMTP_Open_Raw_Device(&devices[0]);
1705 free(devices);
tedbullocke7713642007-02-18 23:10:09 +00001706 return first_device;
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00001707}
1708
1709/**
Linus Walleijd1e14e02008-12-14 01:07:30 +00001710 * Overriding debug function.
1711 * This way we can disable debug prints.
1712 */
1713static void
1714#ifdef __GNUC__
1715__attribute__((__format__(printf,2,0)))
1716#endif
1717LIBMTP_ptp_debug(void *data, const char *format, va_list args)
1718{
nicklas7903584082009-09-28 18:20:16 +00001719 if ((LIBMTP_debug & LIBMTP_DEBUG_PTP) != 0) {
nicklas79daadbf22009-09-28 18:19:34 +00001720 vfprintf (stderr, format, args);
Catalin Patuleada25de02012-07-19 00:57:42 -04001721 fprintf (stderr, "\n");
nicklas79daadbf22009-09-28 18:19:34 +00001722 fflush (stderr);
1723 }
Linus Walleijd1e14e02008-12-14 01:07:30 +00001724}
1725
1726/**
1727 * Overriding error function.
1728 * This way we can capture all error etc to our errorstack.
1729 */
1730static void
1731#ifdef __GNUC__
1732__attribute__((__format__(printf,2,0)))
1733#endif
1734LIBMTP_ptp_error(void *data, const char *format, va_list args)
1735{
1736 // if (data == NULL) {
1737 vfprintf (stderr, format, args);
1738 fflush (stderr);
1739 /*
1740 FIXME: find out how we shall get the device here.
1741 } else {
1742 PTP_USB *ptp_usb = data;
1743 LIBMTP_mtpdevice_t *device = ...;
1744 char buf[2048];
1745
1746 vsnprintf (buf, sizeof (buf), format, args);
1747 add_error_to_errorstack(device,
1748 LIBMTP_ERROR_PTP_LAYER,
1749 buf);
1750 }
1751 */
1752}
1753
1754/**
Linus Walleij38d7ee82010-07-24 22:17:21 +00001755 * Parses the extension descriptor, there may be stuff in
1756 * this that we want to know about.
1757 */
1758static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
1759 char *desc)
1760{
1761 int start = 0;
1762 int end = 0;
1763
Lei Zhang766bcb82013-02-13 15:12:30 -08001764 /* NULL on Canon A70 */
1765 if (!desc)
1766 return;
1767
Linus Walleij38d7ee82010-07-24 22:17:21 +00001768 /* descriptors are divided by semicolons */
1769 while (end < strlen(desc)) {
Linus Walleij03f0c032011-10-22 00:01:14 +02001770 /* Skip past initial whitespace */
Marcus Meissner1f1d8652015-04-07 23:30:53 +02001771 while ((end < strlen(desc)) && (desc[start] == ' ' )) {
Linus Walleij03f0c032011-10-22 00:01:14 +02001772 start++;
1773 end++;
1774 }
1775 /* Detect extension */
Marcus Meissner1f1d8652015-04-07 23:30:53 +02001776 while ((end < strlen(desc)) && (desc[end] != ';'))
Linus Walleij38d7ee82010-07-24 22:17:21 +00001777 end++;
1778 if (end < strlen(desc)) {
1779 char *element = strndup(desc + start, end-start);
1780 if (element) {
1781 int i = 0;
1782 // printf(" Element: \"%s\"\n", element);
1783
1784 /* Parse for an extension */
Marcus Meissner1f1d8652015-04-07 23:30:53 +02001785 while ((i < strlen(element)) && (element[i] != ':'))
Linus Walleij38d7ee82010-07-24 22:17:21 +00001786 i++;
1787 if (i < strlen(element)) {
1788 char *name = strndup(element, i);
Marcus Meissnerf93179d2016-10-24 22:42:38 +02001789 int major = 0, minor = 0;
Linus Walleij38d7ee82010-07-24 22:17:21 +00001790
Marcus Meissnerf93179d2016-10-24 22:42:38 +02001791 /* extension versions have to be MAJOR.MINOR, but Samsung has one
1792 * with just 0, so just cope with those cases too */
1793 if ( (2 == sscanf(element+i+1,"%d.%d",&major,&minor)) ||
1794 (1 == sscanf(element+i+1,"%d",&major))
1795 ) {
Linus Walleij38d7ee82010-07-24 22:17:21 +00001796 LIBMTP_device_extension_t *extension;
Marcus Meissnerf93179d2016-10-24 22:42:38 +02001797
Linus Walleij38d7ee82010-07-24 22:17:21 +00001798 extension = malloc(sizeof(LIBMTP_device_extension_t));
1799 extension->name = name;
1800 extension->major = major;
1801 extension->minor = minor;
1802 extension->next = NULL;
1803 if (mtpdevice->extensions == NULL) {
1804 mtpdevice->extensions = extension;
1805 } else {
1806 LIBMTP_device_extension_t *tmp = mtpdevice->extensions;
1807 while (tmp->next != NULL)
1808 tmp = tmp->next;
1809 tmp->next = extension;
1810 }
Linus Walleij38d7ee82010-07-24 22:17:21 +00001811 } else {
1812 LIBMTP_ERROR("LIBMTP ERROR: couldnt parse extension %s\n",
1813 element);
1814 }
1815 }
1816 free(element);
1817 }
1818 }
1819 end++;
1820 start = end;
1821 }
1822}
1823
1824/**
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001825 * This function opens a device from a raw device. It is the
1826 * preferred way to access devices in the new interface where
1827 * several devices can come and go as the library is working
1828 * on a certain device.
1829 * @param rawdevice the raw device to open a "real" device for.
1830 * @return an open device.
1831 */
Linus Walleija8b88892011-03-03 19:58:26 +01001832LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevice)
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001833{
1834 LIBMTP_mtpdevice_t *mtp_device;
1835 uint8_t bs = 0;
1836 PTPParams *current_params;
Linus Walleij3c643252008-05-31 23:22:28 +00001837 PTP_USB *ptp_usb;
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001838 LIBMTP_error_number_t err;
1839 int i;
1840
1841 /* Allocate dynamic space for our device */
1842 mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
1843 /* Check if there was a memory allocation error */
1844 if(mtp_device == NULL) {
1845 /* There has been an memory allocation error. We are going to ignore this
1846 device and attempt to continue */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001847
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001848 /* TODO: This error statement could probably be a bit more robust */
nicklas79daadbf22009-09-28 18:19:34 +00001849 LIBMTP_ERROR("LIBMTP PANIC: connect_usb_devices encountered a memory "
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001850 "allocation error with device %d on bus %d, trying to continue",
1851 rawdevice->devnum, rawdevice->bus_location);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001852
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001853 return NULL;
1854 }
Marcus Meissner097a0522015-03-22 14:30:36 +01001855 memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
Linus Walleija8b88892011-03-03 19:58:26 +01001856 // Non-cached by default
1857 mtp_device->cached = 0;
Linus Walleij3c643252008-05-31 23:22:28 +00001858
1859 /* Create PTP params */
1860 current_params = (PTPParams *) malloc(sizeof(PTPParams));
1861 if (current_params == NULL) {
1862 free(mtp_device);
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001863 return NULL;
Linus Walleij3c643252008-05-31 23:22:28 +00001864 }
1865 memset(current_params, 0, sizeof(PTPParams));
Linus Walleijd4637502009-06-14 23:03:33 +00001866 current_params->device_flags = rawdevice->device_entry.device_flags;
1867 current_params->nrofobjects = 0;
Marcus Meissneraa7d91a2017-03-16 15:59:48 +01001868 current_params->cachetime = 2;
Linus Walleijd4637502009-06-14 23:03:33 +00001869 current_params->objects = NULL;
1870 current_params->response_packet_size = 0;
1871 current_params->response_packet = NULL;
Linus Walleijd1e14e02008-12-14 01:07:30 +00001872 /* This will be a pointer to PTP_USB later */
1873 current_params->data = NULL;
1874 /* Set upp local debug and error functions */
1875 current_params->debug_func = LIBMTP_ptp_debug;
1876 current_params->error_func = LIBMTP_ptp_error;
Linus Walleij3c643252008-05-31 23:22:28 +00001877 /* TODO: Will this always be little endian? */
1878 current_params->byteorder = PTP_DL_LE;
1879 current_params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8");
1880 current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UCS-2LE");
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001881
Linus Walleij3c643252008-05-31 23:22:28 +00001882 if(current_params->cd_locale_to_ucs2 == (iconv_t) -1 ||
1883 current_params->cd_ucs2_to_locale == (iconv_t) -1) {
nicklas79daadbf22009-09-28 18:19:34 +00001884 LIBMTP_ERROR("LIBMTP PANIC: Cannot open iconv() converters to/from UCS-2!\n"
Linus Walleij3c643252008-05-31 23:22:28 +00001885 "Too old stdlibc, glibc and libiconv?\n");
1886 free(current_params);
1887 free(mtp_device);
1888 return NULL;
1889 }
1890 mtp_device->params = current_params;
1891
Linus Walleij3c643252008-05-31 23:22:28 +00001892 /* Create usbinfo, this also opens the session */
1893 err = configure_usb_device(rawdevice,
1894 current_params,
1895 &mtp_device->usbinfo);
1896 if (err != LIBMTP_ERROR_NONE) {
1897 free(current_params);
1898 free(mtp_device);
1899 return NULL;
1900 }
1901 ptp_usb = (PTP_USB*) mtp_device->usbinfo;
1902 /* Set pointer back to params */
1903 ptp_usb->params = current_params;
1904
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001905 /* Cache the device information for later use */
1906 if (ptp_getdeviceinfo(current_params,
1907 &current_params->deviceinfo) != PTP_RC_OK) {
nicklas79daadbf22009-09-28 18:19:34 +00001908 LIBMTP_ERROR("LIBMTP PANIC: Unable to read device information on device "
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001909 "%d on bus %d, trying to continue",
1910 rawdevice->devnum, rawdevice->bus_location);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001911
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001912 /* Prevent memory leaks for this device */
1913 free(mtp_device->usbinfo);
1914 free(mtp_device->params);
1915 current_params = NULL;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001916 free(mtp_device);
Linus Walleijbdb89bd2008-05-28 23:32:35 +00001917 return NULL;
1918 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00001919
Linus Walleij14735952010-04-25 04:56:49 +00001920 /* Check: if this is a PTP device, is it really tagged as MTP? */
1921 if (current_params->deviceinfo.VendorExtensionID != 0x00000006) {
1922 LIBMTP_ERROR("LIBMTP WARNING: no MTP vendor extension on device "
1923 "%d on bus %d",
1924 rawdevice->devnum, rawdevice->bus_location);
1925 LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionID: %08x",
1926 current_params->deviceinfo.VendorExtensionID);
1927 LIBMTP_ERROR("LIBMTP WARNING: VendorExtensionDesc: %s",
1928 current_params->deviceinfo.VendorExtensionDesc);
1929 LIBMTP_ERROR("LIBMTP WARNING: this typically means the device is PTP "
1930 "(i.e. a camera) but not an MTP device at all. "
1931 "Trying to continue anyway.");
1932 }
1933
Linus Walleij38d7ee82010-07-24 22:17:21 +00001934 parse_extension_descriptor(mtp_device,
1935 current_params->deviceinfo.VendorExtensionDesc);
1936
Linus Walleija566f602010-07-24 22:46:36 +00001937 /*
Linus Walleij03f0c032011-10-22 00:01:14 +02001938 * Android has a number of bugs, force-assign these bug flags
Linus Walleij15c7d1b2012-01-07 01:37:31 +01001939 * if Android is encountered. Same thing for devices we detect
1940 * as SONY NWZ Walkmen. I have no clue what "sony.net/WMFU" means
1941 * I just know only NWZs have it.
Linus Walleij03f0c032011-10-22 00:01:14 +02001942 */
1943 {
1944 LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
Linus Walleij4c570a92012-07-17 22:33:58 +02001945 int is_microsoft_com_wpdna = 0;
1946 int is_android = 0;
1947 int is_sony_net_wmfu = 0;
1948 int is_sonyericsson_com_se = 0;
Linus Walleij03f0c032011-10-22 00:01:14 +02001949
Linus Walleij4c570a92012-07-17 22:33:58 +02001950 /* Loop over extensions and set flags */
Linus Walleij03f0c032011-10-22 00:01:14 +02001951 while (tmpext != NULL) {
Linus Walleij4c570a92012-07-17 22:33:58 +02001952 if (!strcmp(tmpext->name, "microsoft.com/WPDNA"))
1953 is_microsoft_com_wpdna = 1;
1954 if (!strcmp(tmpext->name, "android.com"))
1955 is_android = 1;
1956 if (!strcmp(tmpext->name, "sony.net/WMFU"))
1957 is_sony_net_wmfu = 1;
1958 if (!strcmp(tmpext->name, "sonyericsson.com/SE"))
1959 is_sonyericsson_com_se = 1;
Linus Walleij03f0c032011-10-22 00:01:14 +02001960 tmpext = tmpext->next;
1961 }
Linus Walleij4c570a92012-07-17 22:33:58 +02001962
1963 /* Check for specific stacks */
1964 if (is_microsoft_com_wpdna && is_sonyericsson_com_se && !is_android) {
1965 /*
1966 * The Aricent stack seems to be detected by providing WPDNA, the SonyEricsson
1967 * extension and NO Android extension.
1968 */
1969 ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ARICENT_BUGS;
1970 LIBMTP_INFO("Aricent MTP stack device detected, assigning default bug flags\n");
1971 }
1972 else if (is_android) {
1973 /*
1974 * If bugs are fixed in later versions, test on tmpext->major, tmpext->minor
1975 */
1976 ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_ANDROID_BUGS;
1977 LIBMTP_INFO("Android device detected, assigning default bug flags\n");
1978 }
1979 else if (is_sony_net_wmfu) {
1980 ptp_usb->rawdevice.device_entry.device_flags |= DEVICE_FLAGS_SONY_NWZ_BUGS;
1981 LIBMTP_INFO("SONY NWZ device detected, assigning default bug flags\n");
1982 }
Linus Walleij03f0c032011-10-22 00:01:14 +02001983 }
1984
1985 /*
Linus Walleija566f602010-07-24 22:46:36 +00001986 * If the OGG or FLAC filetypes are flagged as "unknown", check
1987 * if the firmware has been updated to actually support it.
1988 */
1989 if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
1990 for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
1991 if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_OGG) {
1992 /* This is not unknown anymore, unflag it */
Linus Walleija8b88892011-03-03 19:58:26 +01001993 ptp_usb->rawdevice.device_entry.device_flags &=
Linus Walleija566f602010-07-24 22:46:36 +00001994 ~DEVICE_FLAG_OGG_IS_UNKNOWN;
1995 break;
1996 }
1997 }
1998 }
1999 if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
2000 for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
2001 if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_FLAC) {
2002 /* This is not unknown anymore, unflag it */
Linus Walleija8b88892011-03-03 19:58:26 +01002003 ptp_usb->rawdevice.device_entry.device_flags &=
Linus Walleija566f602010-07-24 22:46:36 +00002004 ~DEVICE_FLAG_FLAC_IS_UNKNOWN;
2005 break;
2006 }
2007 }
2008 }
2009
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002010 /* Determine if the object size supported is 32 or 64 bit wide */
Lei Zhangcaa1bbc2013-02-13 15:12:02 -08002011 if (ptp_operation_issupported(current_params,PTP_OC_MTP_GetObjectPropsSupported)) {
2012 for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
2013 PTPObjectPropDesc opd;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002014
Lei Zhangcaa1bbc2013-02-13 15:12:02 -08002015 if (ptp_mtp_getobjectpropdesc(current_params,
2016 PTP_OPC_ObjectSize,
2017 current_params->deviceinfo.ImageFormats[i],
2018 &opd) != PTP_RC_OK) {
2019 LIBMTP_ERROR("LIBMTP PANIC: "
2020 "could not inspect object property descriptions!\n");
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002021 } else {
Lei Zhangcaa1bbc2013-02-13 15:12:02 -08002022 if (opd.DataType == PTP_DTC_UINT32) {
2023 if (bs == 0) {
2024 bs = 32;
2025 } else if (bs != 32) {
2026 LIBMTP_ERROR("LIBMTP PANIC: "
2027 "different objects support different object sizes!\n");
2028 bs = 0;
2029 break;
2030 }
2031 } else if (opd.DataType == PTP_DTC_UINT64) {
2032 if (bs == 0) {
2033 bs = 64;
2034 } else if (bs != 64) {
2035 LIBMTP_ERROR("LIBMTP PANIC: "
2036 "different objects support different object sizes!\n");
2037 bs = 0;
2038 break;
2039 }
2040 } else {
2041 // Ignore if other size.
2042 LIBMTP_ERROR("LIBMTP PANIC: "
2043 "awkward object size data type: %04x\n", opd.DataType);
2044 bs = 0;
2045 break;
2046 }
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002047 }
2048 }
2049 }
2050 if (bs == 0) {
2051 // Could not detect object bitsize, assume 32 bits
2052 bs = 32;
2053 }
2054 mtp_device->object_bitsize = bs;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002055
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002056 /* No Errors yet for this device */
2057 mtp_device->errorstack = NULL;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002058
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002059 /* Default Max Battery Level, we will adjust this if possible */
2060 mtp_device->maximum_battery_level = 100;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002061
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002062 /* Check if device supports reading maximum battery level */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002063 if(!FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) &&
Linus Walleij4096c882009-03-16 23:32:34 +00002064 ptp_property_issupported( current_params, PTP_DPC_BatteryLevel)) {
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002065 PTPDevicePropDesc dpd;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002066
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002067 /* Try to read maximum battery level */
2068 if(ptp_getdevicepropdesc(current_params,
2069 PTP_DPC_BatteryLevel,
2070 &dpd) != PTP_RC_OK) {
2071 add_error_to_errorstack(mtp_device,
2072 LIBMTP_ERROR_CONNECTING,
2073 "Unable to read Maximum Battery Level for this "
2074 "device even though the device supposedly "
2075 "supports this functionality");
2076 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002077
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002078 /* TODO: is this appropriate? */
2079 /* If max battery level is 0 then leave the default, otherwise assign */
2080 if (dpd.FORM.Range.MaximumValue.u8 != 0) {
2081 mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
2082 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002083
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002084 ptp_free_devicepropdesc(&dpd);
2085 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002086
Linus Walleij4fc19172010-01-19 00:10:26 +00002087 /* Set all default folders to 0xffffffffU (root directory) */
2088 mtp_device->default_music_folder = 0xffffffffU;
2089 mtp_device->default_playlist_folder = 0xffffffffU;
2090 mtp_device->default_picture_folder = 0xffffffffU;
2091 mtp_device->default_video_folder = 0xffffffffU;
2092 mtp_device->default_organizer_folder = 0xffffffffU;
2093 mtp_device->default_zencast_folder = 0xffffffffU;
2094 mtp_device->default_album_folder = 0xffffffffU;
2095 mtp_device->default_text_folder = 0xffffffffU;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002096
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002097 /* Set initial storage information */
2098 mtp_device->storage = NULL;
2099 if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) {
2100 add_error_to_errorstack(mtp_device,
2101 LIBMTP_ERROR_GENERAL,
2102 "Get Storage information failed.");
2103 mtp_device->storage = NULL;
2104 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002105
Linus Walleija8b88892011-03-03 19:58:26 +01002106
2107 return mtp_device;
2108}
2109
2110LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
2111{
2112 LIBMTP_mtpdevice_t *mtp_device = LIBMTP_Open_Raw_Device_Uncached(rawdevice);
2113
2114 if (mtp_device == NULL)
2115 return NULL;
2116
Sajid Anwar8dca41d2012-08-18 20:36:13 +02002117 /* Check for MTPZ devices. */
2118 if (use_mtpz) {
2119 LIBMTP_device_extension_t *tmpext = mtp_device->extensions;
2120
2121 while (tmpext != NULL) {
2122 if (!strcmp(tmpext->name, "microsoft.com/MTPZ")) {
2123 LIBMTP_INFO("MTPZ device detected. Authenticating...\n");
2124 if (PTP_RC_OK == ptp_mtpz_handshake(mtp_device->params)) {
2125 LIBMTP_INFO ("(MTPZ) Successfully authenticated with device.\n");
2126 } else {
2127 LIBMTP_INFO ("(MTPZ) Failure - could not authenticate with device.\n");
2128 }
2129 break;
2130 }
2131 tmpext = tmpext->next;
2132 }
2133 }
2134
Linus Walleija8b88892011-03-03 19:58:26 +01002135 // Set up this device as cached
2136 mtp_device->cached = 1;
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002137 /*
2138 * Then get the handles and try to locate the default folders.
2139 * This has the desired side effect of caching all handles from
2140 * the device which speeds up later operations.
2141 */
2142 flush_handles(mtp_device);
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002143 return mtp_device;
2144}
2145
2146/**
Linus Walleij335b6fb2011-03-22 23:43:32 +01002147 * To read events sent by the device, repeatedly call this function from a secondary
2148 * thread until the return value is < 0.
2149 *
2150 * @param device a pointer to the MTP device to poll for events.
2151 * @param event contains a pointer to be filled in with the event retrieved if the call
2152 * is successful.
Philip Langdaleda268792012-09-02 14:48:25 -07002153 * @param out1 contains the param1 value from the raw event.
Linus Walleij335b6fb2011-03-22 23:43:32 +01002154 * @return 0 on success, any other value means the polling loop shall be
2155 * terminated immediately for this session.
2156 */
Philip Langdaleda268792012-09-02 14:48:25 -07002157int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_t *out1)
Linus Walleij335b6fb2011-03-22 23:43:32 +01002158{
2159 /*
2160 * FIXME: Potential race-condition here, if client deallocs device
2161 * while we're *not* waiting for input. As we'll be waiting for
2162 * input most of the time, it's unlikely but still worth considering
2163 * for improvement. Also we cannot affect the state of the cache etc
2164 * unless we know we are the sole user on the device. A spinlock or
2165 * mutex in the LIBMTP_mtpdevice_t is needed for this to work.
2166 */
2167 PTPParams *params = (PTPParams *) device->params;
2168 PTPContainer ptp_event;
Linus Walleij6ba03452011-11-14 20:46:36 +01002169 uint16_t ret = ptp_usb_event_wait(params, &ptp_event);
Linus Walleij335b6fb2011-03-22 23:43:32 +01002170
2171 if (ret != PTP_RC_OK) {
2172 /* Device is closing down or other fatal stuff, exit thread */
2173 return -1;
2174 }
Philip Langdale0a576512016-04-09 14:22:25 -07002175 LIBMTP_Handle_Event(&ptp_event, event, out1);
2176 return 0;
2177}
2178
2179void LIBMTP_Handle_Event(PTPContainer *ptp_event,
2180 LIBMTP_event_t *event, uint32_t *out1) {
2181 uint16_t code;
2182 uint32_t session_id;
2183 uint32_t param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002184
2185 *event = LIBMTP_EVENT_NONE;
2186
2187 /* Process the event */
Philip Langdale0a576512016-04-09 14:22:25 -07002188 code = ptp_event->Code;
2189 session_id = ptp_event->SessionID;
2190 param1 = ptp_event->Param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002191
2192 switch(code) {
2193 case PTP_EC_Undefined:
2194 LIBMTP_INFO("Received event PTP_EC_Undefined in session %u\n", session_id);
2195 break;
2196 case PTP_EC_CancelTransaction:
2197 LIBMTP_INFO("Received event PTP_EC_CancelTransaction in session %u\n", session_id);
2198 break;
2199 case PTP_EC_ObjectAdded:
2200 LIBMTP_INFO("Received event PTP_EC_ObjectAdded in session %u\n", session_id);
Philip Langdale50dd3472013-03-10 12:22:51 -07002201 *event = LIBMTP_EVENT_OBJECT_ADDED;
2202 *out1 = param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002203 break;
2204 case PTP_EC_ObjectRemoved:
2205 LIBMTP_INFO("Received event PTP_EC_ObjectRemoved in session %u\n", session_id);
Philip Langdale50dd3472013-03-10 12:22:51 -07002206 *event = LIBMTP_EVENT_OBJECT_REMOVED;
2207 *out1 = param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002208 break;
2209 case PTP_EC_StoreAdded:
2210 LIBMTP_INFO("Received event PTP_EC_StoreAdded in session %u\n", session_id);
2211 /* TODO: rescan storages */
Philip Langdaleda268792012-09-02 14:48:25 -07002212 *event = LIBMTP_EVENT_STORE_ADDED;
2213 *out1 = param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002214 break;
2215 case PTP_EC_StoreRemoved:
2216 LIBMTP_INFO("Received event PTP_EC_StoreRemoved in session %u\n", session_id);
2217 /* TODO: rescan storages */
Philip Langdale50dd3472013-03-10 12:22:51 -07002218 *event = LIBMTP_EVENT_STORE_REMOVED;
2219 *out1 = param1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002220 break;
2221 case PTP_EC_DevicePropChanged:
2222 LIBMTP_INFO("Received event PTP_EC_DevicePropChanged in session %u\n", session_id);
2223 /* TODO: update device properties */
2224 break;
2225 case PTP_EC_ObjectInfoChanged:
2226 LIBMTP_INFO("Received event PTP_EC_ObjectInfoChanged in session %u\n", session_id);
2227 /* TODO: rescan object cache or just for this one object */
2228 break;
2229 case PTP_EC_DeviceInfoChanged:
2230 LIBMTP_INFO("Received event PTP_EC_DeviceInfoChanged in session %u\n", session_id);
2231 /* TODO: update device info */
2232 break;
2233 case PTP_EC_RequestObjectTransfer:
2234 LIBMTP_INFO("Received event PTP_EC_RequestObjectTransfer in session %u\n", session_id);
2235 break;
2236 case PTP_EC_StoreFull:
2237 LIBMTP_INFO("Received event PTP_EC_StoreFull in session %u\n", session_id);
2238 break;
2239 case PTP_EC_DeviceReset:
2240 LIBMTP_INFO("Received event PTP_EC_DeviceReset in session %u\n", session_id);
2241 break;
2242 case PTP_EC_StorageInfoChanged :
2243 LIBMTP_INFO( "Received event PTP_EC_StorageInfoChanged in session %u\n", session_id);
2244 /* TODO: update storage info */
2245 break;
2246 case PTP_EC_CaptureComplete :
2247 LIBMTP_INFO( "Received event PTP_EC_CaptureComplete in session %u\n", session_id);
2248 break;
2249 case PTP_EC_UnreportedStatus :
2250 LIBMTP_INFO( "Received event PTP_EC_UnreportedStatus in session %u\n", session_id);
2251 break;
2252 default :
2253 LIBMTP_INFO( "Received unknown event in session %u\n", session_id);
2254 break;
2255 }
Philip Langdale0a576512016-04-09 14:22:25 -07002256}
Linus Walleij335b6fb2011-03-22 23:43:32 +01002257
Philip Langdale0a576512016-04-09 14:22:25 -07002258static void LIBMTP_Read_Event_Cb(PTPParams *params, uint16_t ret_code,
2259 PTPContainer *ptp_event, void *user_data) {
2260 event_cb_data_t *data = user_data;
2261 LIBMTP_event_t event = LIBMTP_EVENT_NONE;
2262 uint32_t param1 = 0;
2263 int handler_ret;
2264
2265 switch (ret_code) {
2266 case PTP_RC_OK:
2267 handler_ret = LIBMTP_HANDLER_RETURN_OK;
2268 LIBMTP_Handle_Event(ptp_event, &event, &param1);
2269 break;
2270 case PTP_ERROR_CANCEL:
2271 handler_ret = LIBMTP_HANDLER_RETURN_CANCEL;
2272 break;
2273 default:
2274 handler_ret = LIBMTP_HANDLER_RETURN_ERROR;
2275 break;
2276 }
2277
2278 data->cb(handler_ret, event, param1, data->user_data);
2279 free(data);
2280}
2281
2282/**
2283 * This function reads events sent by the device, in a non-blocking manner.
2284 * The callback function will be called when an event is received, but for the function
2285 * to make progress, polling must take place, using LIBMTP_Handle_Events_Timeout_Completed.
2286 *
2287 * After an event is received, this function should be called again to listen for the next
2288 * event.
2289 *
2290 * For now, this non-blocking mechanism only works with libusb-1.0, and not any of the
2291 * other usb library backends. Attempting to call this method with another backend will
2292 * always return an error.
2293 *
2294 * @param device a pointer to the MTP device to poll for events.
2295 * @param cb a callback to be invoked when an event is received.
2296 * @param user_data arbitrary user data passed to the callback.
2297 * @return 0 on success, any other value means that the callback was not registered and
2298 * no event notification will take place.
2299 */
2300int LIBMTP_Read_Event_Async(LIBMTP_mtpdevice_t *device, LIBMTP_event_cb_fn cb, void *user_data) {
2301 PTPParams *params = (PTPParams *) device->params;
2302 event_cb_data_t *data = malloc(sizeof(event_cb_data_t));
2303 uint16_t ret;
2304
2305 data->cb = cb;
2306 data->user_data = user_data;
2307
2308 ret = ptp_usb_event_async(params, LIBMTP_Read_Event_Cb, data);
2309 return ret == PTP_RC_OK ? 0 : -1;
Linus Walleij335b6fb2011-03-22 23:43:32 +01002310}
2311
2312/**
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002313 * Recursive function that adds MTP devices to a linked list
Linus Walleija700d222008-05-28 23:06:14 +00002314 * @param devices a list of raw devices to have real devices created for.
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002315 * @return a device pointer to a newly created mtpdevice (used in linked
Linus Walleijb0ab5482007-08-29 21:08:54 +00002316 * list creation).
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002317 */
Linus Walleija700d222008-05-28 23:06:14 +00002318static LIBMTP_mtpdevice_t * create_usb_mtp_devices(LIBMTP_raw_device_t *devices, int numdevs)
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002319{
Linus Walleija700d222008-05-28 23:06:14 +00002320 uint8_t i;
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002321 LIBMTP_mtpdevice_t *mtp_device_list = NULL;
2322 LIBMTP_mtpdevice_t *current_device = NULL;
Linus Walleija700d222008-05-28 23:06:14 +00002323
2324 for (i=0; i < numdevs; i++) {
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002325 LIBMTP_mtpdevice_t *mtp_device;
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002326 mtp_device = LIBMTP_Open_Raw_Device(&devices[i]);
Linus Walleija700d222008-05-28 23:06:14 +00002327
Linus Walleijbdb89bd2008-05-28 23:32:35 +00002328 /* On error, try next device */
2329 if (mtp_device == NULL)
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002330 continue;
Linus Walleijddaba2f2007-10-02 21:20:30 +00002331
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002332 /* Add the device to the list */
2333 mtp_device->next = NULL;
2334 if (mtp_device_list == NULL) {
2335 mtp_device_list = current_device = mtp_device;
2336 } else {
2337 current_device->next = mtp_device;
2338 current_device = mtp_device;
2339 }
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002340 }
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002341 return mtp_device_list;
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002342}
2343
2344/**
tedbullock848009b2007-03-03 23:20:12 +00002345 * Get the number of devices that are available in the listed device list
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002346 * @param device_list Pointer to a linked list of devices
2347 * @return Number of devices in the device list device_list
2348 * @see LIBMTP_Get_Connected_Devices()
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002349 */
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002350uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *device_list)
tedbullock848009b2007-03-03 23:20:12 +00002351{
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002352 uint32_t numdevices = 0;
2353 LIBMTP_mtpdevice_t *iter;
2354 for(iter = device_list; iter != NULL; iter = iter->next)
2355 numdevices++;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002356
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002357 return numdevices;
tedbullock848009b2007-03-03 23:20:12 +00002358}
2359
2360/**
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002361 * Get the first connected MTP device node in the linked list of devices.
2362 * Currently this only provides access to USB devices
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002363 * @param device_list A list of devices ready to be used by the caller. You
2364 * need to know how many there are.
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002365 * @return Any error information gathered from device connections
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002366 * @see LIBMTP_Number_Devices_In_List()
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002367 */
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002368LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_list)
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002369{
Linus Walleija700d222008-05-28 23:06:14 +00002370 LIBMTP_raw_device_t *devices;
2371 int numdevs;
Linus Walleij5fbb77d2007-03-07 08:40:36 +00002372 LIBMTP_error_number_t ret;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002373
Linus Walleija700d222008-05-28 23:06:14 +00002374 ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
Linus Walleij5fbb77d2007-03-07 08:40:36 +00002375 if (ret != LIBMTP_ERROR_NONE) {
Linus Walleijf27d1cd2007-03-05 14:23:00 +00002376 *device_list = NULL;
Linus Walleij5fbb77d2007-03-07 08:40:36 +00002377 return ret;
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002378 }
2379
2380 /* Assign linked list of devices */
Linus Walleija700d222008-05-28 23:06:14 +00002381 if (devices == NULL || numdevs == 0) {
2382 *device_list = NULL;
Marcus Meissnerb5214772015-10-04 13:08:20 +02002383 free(devices);
Linus Walleija700d222008-05-28 23:06:14 +00002384 return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
2385 }
2386
2387 *device_list = create_usb_mtp_devices(devices, numdevs);
2388 free(devices);
Linus Walleij45a86372007-03-07 09:36:19 +00002389
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002390 /* TODO: Add wifi device access here */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002391
Richard Low99a93e82007-09-29 08:16:08 +00002392 /* We have found some devices but create failed */
2393 if (*device_list == NULL)
2394 return LIBMTP_ERROR_CONNECTING;
Linus Walleij2d3f7b82007-02-14 09:24:20 +00002395
2396 return LIBMTP_ERROR_NONE;
2397}
2398
2399/**
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00002400 * This closes and releases an allocated MTP device.
Linus Walleijb9256fd2006-02-15 09:40:43 +00002401 * @param device a pointer to the MTP device to release.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00002402 */
tedbullock0f033cb2007-02-14 20:56:54 +00002403void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
2404{
2405 if(device != NULL)
2406 {
2407 if(device->next != NULL)
2408 {
2409 LIBMTP_Release_Device_List(device->next);
2410 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002411
tedbullock0f033cb2007-02-14 20:56:54 +00002412 LIBMTP_Release_Device(device);
2413 }
2414}
2415
2416/**
2417 * This closes and releases an allocated MTP device.
2418 * @param device a pointer to the MTP device to release.
2419 */
Linus Walleijb9256fd2006-02-15 09:40:43 +00002420void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00002421{
Linus Walleij9b28da32006-03-16 13:47:58 +00002422 PTPParams *params = (PTPParams *) device->params;
Linus Walleij2d411db2006-03-22 12:13:09 +00002423 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij9b28da32006-03-16 13:47:58 +00002424
Linus Walleijb0ab5482007-08-29 21:08:54 +00002425 close_device(ptp_usb, params);
Linus Walleij2715c442007-01-20 22:35:29 +00002426 // Clear error stack
2427 LIBMTP_Clear_Errorstack(device);
Linus Walleij3ec86312006-08-21 13:25:24 +00002428 // Free iconv() converters...
Linus Walleija823a702006-08-27 21:27:46 +00002429 iconv_close(params->cd_locale_to_ucs2);
2430 iconv_close(params->cd_ucs2_to_locale);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002431 free(ptp_usb);
Linus Walleij073c4172007-02-02 22:26:33 +00002432 ptp_free_params(params);
Lei Zhange1082c12013-02-13 15:16:43 -08002433 free(params);
Linus Walleij9e1b0812006-12-12 19:22:02 +00002434 free_storage_list(device);
Linus Walleij38d7ee82010-07-24 22:17:21 +00002435 // Free extension list...
2436 if (device->extensions != NULL) {
2437 LIBMTP_device_extension_t *tmp = device->extensions;
2438
2439 while (tmp != NULL) {
2440 LIBMTP_device_extension_t *next = tmp->next;
2441
2442 if (tmp->name)
2443 free(tmp->name);
2444 free(tmp);
2445 tmp = next;
2446 }
2447 }
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00002448 free(device);
2449}
Linus Walleijb9256fd2006-02-15 09:40:43 +00002450
2451/**
Linus Walleij2715c442007-01-20 22:35:29 +00002452 * This can be used by any libmtp-intrinsic code that
Linus Walleij68b19c02007-02-15 11:50:37 +00002453 * need to stack up an error on the stack. You are only
2454 * supposed to add errors to the error stack using this
2455 * function, do not create and reference error entries
2456 * directly.
Linus Walleij2715c442007-01-20 22:35:29 +00002457 */
2458static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
2459 LIBMTP_error_number_t errornumber,
2460 char const * const error_text)
2461{
2462 LIBMTP_error_t *newerror;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002463
Linus Walleij68b19c02007-02-15 11:50:37 +00002464 if (device == NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002465 LIBMTP_ERROR("LIBMTP PANIC: Trying to add error to a NULL device!\n");
Linus Walleij68b19c02007-02-15 11:50:37 +00002466 return;
2467 }
Linus Walleij2715c442007-01-20 22:35:29 +00002468 newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
2469 newerror->errornumber = errornumber;
2470 newerror->error_text = strdup(error_text);
rreardon774503c2007-02-15 15:52:49 +00002471 newerror->next = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +00002472 if (device->errorstack == NULL) {
2473 device->errorstack = newerror;
2474 } else {
2475 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002476
Linus Walleij2715c442007-01-20 22:35:29 +00002477 while (tmp->next != NULL) {
2478 tmp = tmp->next;
2479 }
2480 tmp->next = newerror;
2481 }
2482}
2483
2484/**
Linus Walleij070e9b42007-01-22 08:49:28 +00002485 * Adds an error from the PTP layer to the error stack.
2486 */
2487static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
2488 uint16_t ptp_error,
2489 char const * const error_text)
2490{
Marcus Meissner3d692dc2016-02-21 12:34:06 +01002491 PTPParams *params = (PTPParams *) device->params;
2492
Linus Walleij68b19c02007-02-15 11:50:37 +00002493 if (device == NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002494 LIBMTP_ERROR("LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
Linus Walleij68b19c02007-02-15 11:50:37 +00002495 return;
2496 } else {
2497 char outstr[256];
2498 snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
2499 outstr[sizeof(outstr)-1] = '\0';
2500 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
Catalin Patulea73d26cf2012-07-03 02:16:23 -04002501
Marcus Meissner3d692dc2016-02-21 12:34:06 +01002502 snprintf(outstr, sizeof(outstr), "Error %04x: %s", ptp_error, ptp_strerror(ptp_error, params->deviceinfo.VendorExtensionID));
Catalin Patulea73d26cf2012-07-03 02:16:23 -04002503 outstr[sizeof(outstr)-1] = '\0';
2504 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
Linus Walleij68b19c02007-02-15 11:50:37 +00002505 }
Linus Walleij070e9b42007-01-22 08:49:28 +00002506}
2507
2508/**
Linus Walleij2715c442007-01-20 22:35:29 +00002509 * This returns the error stack for a device in case you
2510 * need to either reference the error numbers (e.g. when
2511 * creating multilingual apps with multiple-language text
2512 * representations for each error number) or when you need
2513 * to build a multi-line error text widget or something like
2514 * that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
2515 * to clear it when you're finished with it.
2516 * @param device a pointer to the MTP device to get the error
2517 * stack for.
2518 * @return the error stack or NULL if there are no errors
2519 * on the stack.
2520 * @see LIBMTP_Clear_Errorstack()
2521 * @see LIBMTP_Dump_Errorstack()
2522 */
2523LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
2524{
Linus Walleij68b19c02007-02-15 11:50:37 +00002525 if (device == NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002526 LIBMTP_ERROR("LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
Linus Walleij5c1499e2009-02-21 06:54:29 +00002527 return NULL;
Linus Walleij68b19c02007-02-15 11:50:37 +00002528 }
Linus Walleij2715c442007-01-20 22:35:29 +00002529 return device->errorstack;
2530}
2531
2532/**
2533 * This function clears the error stack of a device and frees
2534 * any memory used by it. Call this when you're finished with
2535 * using the errors.
2536 * @param device a pointer to the MTP device to clear the error
2537 * stack for.
2538 */
2539void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
2540{
Linus Walleij68b19c02007-02-15 11:50:37 +00002541 if (device == NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002542 LIBMTP_ERROR("LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
Linus Walleij68b19c02007-02-15 11:50:37 +00002543 } else {
2544 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002545
Linus Walleij68b19c02007-02-15 11:50:37 +00002546 while (tmp != NULL) {
2547 LIBMTP_error_t *tmp2;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002548
Linus Walleij68b19c02007-02-15 11:50:37 +00002549 if (tmp->error_text != NULL) {
2550 free(tmp->error_text);
2551 }
2552 tmp2 = tmp;
2553 tmp = tmp->next;
2554 free(tmp2);
Linus Walleij2715c442007-01-20 22:35:29 +00002555 }
Linus Walleij68b19c02007-02-15 11:50:37 +00002556 device->errorstack = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +00002557 }
Linus Walleij2715c442007-01-20 22:35:29 +00002558}
2559
2560/**
2561 * This function dumps the error stack to <code>stderr</code>.
2562 * (You still have to clear the stack though.)
2563 * @param device a pointer to the MTP device to dump the error
2564 * stack for.
2565 */
2566void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
2567{
Linus Walleij68b19c02007-02-15 11:50:37 +00002568 if (device == NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002569 LIBMTP_ERROR("LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
Linus Walleij68b19c02007-02-15 11:50:37 +00002570 } else {
2571 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2715c442007-01-20 22:35:29 +00002572
Linus Walleij68b19c02007-02-15 11:50:37 +00002573 while (tmp != NULL) {
2574 if (tmp->error_text != NULL) {
nicklas79daadbf22009-09-28 18:19:34 +00002575 LIBMTP_ERROR("Error %d: %s\n", tmp->errornumber, tmp->error_text);
Linus Walleij68b19c02007-02-15 11:50:37 +00002576 } else {
nicklas79daadbf22009-09-28 18:19:34 +00002577 LIBMTP_ERROR("Error %d: (unknown)\n", tmp->errornumber);
Linus Walleij68b19c02007-02-15 11:50:37 +00002578 }
2579 tmp = tmp->next;
Linus Walleij2715c442007-01-20 22:35:29 +00002580 }
Linus Walleij2715c442007-01-20 22:35:29 +00002581 }
2582}
2583
Jerry Zhang60d02262017-06-08 18:19:59 -07002584void LIBMTP_Set_Device_Timeout(LIBMTP_mtpdevice_t *device, int milliseconds)
2585{
2586 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2587 set_usb_device_timeout(ptp_usb, milliseconds);
2588}
2589
2590void LIBMTP_Get_Device_Timeout(LIBMTP_mtpdevice_t *device, int *milliseconds)
2591{
2592 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2593 get_usb_device_timeout(ptp_usb, milliseconds);
2594}
2595
Linus Walleij2715c442007-01-20 22:35:29 +00002596/**
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002597 * This command gets all handles and stuff by FAST directory retrieveal
2598 * which is available by getting all metadata for object
Linus Walleij338ade42007-07-03 20:44:08 +00002599 * <code>0xffffffff</code> which simply means "all metadata for all objects".
2600 * This works on the vast majority of MTP devices (there ARE exceptions!)
2601 * and is quite quick. Check the error stack to see if there were
2602 * problems getting the metadata.
Linus Walleij8533bf72007-08-28 10:03:59 +00002603 * @return 0 if all was OK, -1 on failure.
Linus Walleij338ade42007-07-03 20:44:08 +00002604 */
Catalin Patuleaa23fce32012-07-17 22:25:12 -04002605static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
Linus Walleij338ade42007-07-03 20:44:08 +00002606{
2607 PTPParams *params = (PTPParams *) device->params;
2608 int cnt = 0;
Linus Walleij1e9a0332007-09-12 19:35:56 +00002609 int i, j, nrofprops;
Linus Walleij338ade42007-07-03 20:44:08 +00002610 uint32_t lasthandle = 0xffffffff;
Linus Walleij1e9a0332007-09-12 19:35:56 +00002611 MTPProperties *props = NULL;
2612 MTPProperties *prop;
Linus Walleij338ade42007-07-03 20:44:08 +00002613 uint16_t ret;
Linus Walleij2f622812008-08-30 22:06:58 +00002614 int oldtimeout;
2615 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2616
Linus Walleije04a1b92011-03-09 18:00:24 +01002617 /*
2618 * The follow request causes the device to generate
2619 * a list of every file on the device and return it
Linus Walleij2f622812008-08-30 22:06:58 +00002620 * in a single response.
2621 *
2622 * Some slow devices as well as devices with very
2623 * large file systems can easily take longer then
2624 * the standard timeout value before it is able
2625 * to return a response.
2626 *
2627 * Temporarly set timeout to allow working with
2628 * widest range of devices.
2629 */
2630 get_usb_device_timeout(ptp_usb, &oldtimeout);
2631 set_usb_device_timeout(ptp_usb, 60000);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002632
Linus Walleijfcb43422008-05-23 21:53:55 +00002633 ret = ptp_mtp_getobjectproplist(params, 0xffffffff, &props, &nrofprops);
Linus Walleij2f622812008-08-30 22:06:58 +00002634 set_usb_device_timeout(ptp_usb, oldtimeout);
Linus Walleij8533bf72007-08-28 10:03:59 +00002635
2636 if (ret == PTP_RC_MTP_Specification_By_Group_Unsupported) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002637 // What's the point in the device implementing this command if
Linus Walleij8533bf72007-08-28 10:03:59 +00002638 // you cannot use it to get all props for AT LEAST one object?
2639 // Well, whatever...
2640 add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
2641 "cannot retrieve all metadata for an object on this device.");
Linus Walleij91e98132007-08-28 10:05:11 +00002642 return -1;
Linus Walleij8533bf72007-08-28 10:03:59 +00002643 }
Linus Walleij338ade42007-07-03 20:44:08 +00002644 if (ret != PTP_RC_OK) {
Linus Walleij8533bf72007-08-28 10:03:59 +00002645 add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
2646 "could not get proplist of all objects.");
2647 return -1;
Linus Walleij338ade42007-07-03 20:44:08 +00002648 }
Linus Walleijfcb43422008-05-23 21:53:55 +00002649 if (props == NULL && nrofprops != 0) {
2650 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
2651 "get_all_metadata_fast(): "
2652 "call to ptp_mtp_getobjectproplist() returned "
2653 "inconsistent results.");
2654 return -1;
2655 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002656 /*
Linus Walleij338ade42007-07-03 20:44:08 +00002657 * We count the number of objects by counting the ObjectHandle
Linus Walleij31b74292008-05-02 23:29:06 +00002658 * references, whenever it changes we get a new object, when it's
Linus Walleij338ade42007-07-03 20:44:08 +00002659 * the same, it is just different properties of the same object.
2660 */
Linus Walleij1e9a0332007-09-12 19:35:56 +00002661 prop = props;
2662 for (i=0;i<nrofprops;i++) {
Linus Walleij338ade42007-07-03 20:44:08 +00002663 if (lasthandle != prop->ObjectHandle) {
2664 cnt++;
2665 lasthandle = prop->ObjectHandle;
2666 }
Linus Walleij1e9a0332007-09-12 19:35:56 +00002667 prop++;
Linus Walleij338ade42007-07-03 20:44:08 +00002668 }
Linus Walleij338ade42007-07-03 20:44:08 +00002669 lasthandle = 0xffffffff;
Marcus Meissnerb5214772015-10-04 13:08:20 +02002670 params->objects = calloc (cnt, sizeof(PTPObject));
Linus Walleij1e9a0332007-09-12 19:35:56 +00002671 prop = props;
Linus Walleij338ade42007-07-03 20:44:08 +00002672 i = -1;
Linus Walleij1e9a0332007-09-12 19:35:56 +00002673 for (j=0;j<nrofprops;j++) {
Linus Walleij338ade42007-07-03 20:44:08 +00002674 if (lasthandle != prop->ObjectHandle) {
2675 if (i >= 0) {
Linus Walleij77127dc2009-08-25 16:38:42 +00002676 params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
Linus Walleijd4637502009-06-14 23:03:33 +00002677 if (!params->objects[i].oi.Filename) {
Linus Walleij6bf68b42007-09-03 22:50:02 +00002678 /* I have one such file on my Creative (Marcus) */
Linus Walleijd4637502009-06-14 23:03:33 +00002679 params->objects[i].oi.Filename = strdup("<null>");
Linus Walleij338ade42007-07-03 20:44:08 +00002680 }
2681 }
2682 i++;
2683 lasthandle = prop->ObjectHandle;
Linus Walleijd4637502009-06-14 23:03:33 +00002684 params->objects[i].oid = prop->ObjectHandle;
Linus Walleij338ade42007-07-03 20:44:08 +00002685 }
2686 switch (prop->property) {
2687 case PTP_OPC_ParentObject:
Linus Walleijd4637502009-06-14 23:03:33 +00002688 params->objects[i].oi.ParentObject = prop->propval.u32;
2689 params->objects[i].flags |= PTPOBJECT_PARENTOBJECT_LOADED;
Linus Walleij338ade42007-07-03 20:44:08 +00002690 break;
2691 case PTP_OPC_ObjectFormat:
Linus Walleijd4637502009-06-14 23:03:33 +00002692 params->objects[i].oi.ObjectFormat = prop->propval.u16;
Linus Walleij338ade42007-07-03 20:44:08 +00002693 break;
2694 case PTP_OPC_ObjectSize:
Linus Walleijd9d28d52007-08-04 19:01:18 +00002695 // We loose precision here, up to 32 bits! However the commands that
2696 // retrieve metadata for files and tracks will make sure that the
2697 // PTP_OPC_ObjectSize is read in and duplicated again.
Linus Walleijddaba2f2007-10-02 21:20:30 +00002698 if (device->object_bitsize == 64) {
Linus Walleijd4637502009-06-14 23:03:33 +00002699 params->objects[i].oi.ObjectCompressedSize = (uint32_t) prop->propval.u64;
Linus Walleijddaba2f2007-10-02 21:20:30 +00002700 } else {
Linus Walleijd4637502009-06-14 23:03:33 +00002701 params->objects[i].oi.ObjectCompressedSize = prop->propval.u32;
Linus Walleijddaba2f2007-10-02 21:20:30 +00002702 }
Linus Walleij338ade42007-07-03 20:44:08 +00002703 break;
2704 case PTP_OPC_StorageID:
Linus Walleijd4637502009-06-14 23:03:33 +00002705 params->objects[i].oi.StorageID = prop->propval.u32;
2706 params->objects[i].flags |= PTPOBJECT_STORAGEID_LOADED;
Linus Walleij338ade42007-07-03 20:44:08 +00002707 break;
2708 case PTP_OPC_ObjectFileName:
Richard Low8e8d9d42007-07-14 10:36:50 +00002709 if (prop->propval.str != NULL)
Linus Walleijd4637502009-06-14 23:03:33 +00002710 params->objects[i].oi.Filename = strdup(prop->propval.str);
Linus Walleij338ade42007-07-03 20:44:08 +00002711 break;
Linus Walleijd4637502009-06-14 23:03:33 +00002712 default: {
2713 MTPProperties *newprops;
2714
2715 /* Copy all of the other MTP oprierties into the per-object proplist */
2716 if (params->objects[i].nrofmtpprops) {
Linus Walleij43aba612011-11-25 09:40:34 +01002717 newprops = realloc(params->objects[i].mtpprops,
2718 (params->objects[i].nrofmtpprops+1)*sizeof(MTPProperties));
Linus Walleijd4637502009-06-14 23:03:33 +00002719 } else {
Marcus Meissnerb5214772015-10-04 13:08:20 +02002720 newprops = calloc(1,sizeof(MTPProperties));
Linus Walleijd4637502009-06-14 23:03:33 +00002721 }
2722 if (!newprops) return 0; /* FIXME: error handling? */
2723 params->objects[i].mtpprops = newprops;
Linus Walleij43aba612011-11-25 09:40:34 +01002724 memcpy(&params->objects[i].mtpprops[params->objects[i].nrofmtpprops],
2725 &props[j],sizeof(props[j]));
Linus Walleijd4637502009-06-14 23:03:33 +00002726 params->objects[i].nrofmtpprops++;
Linus Walleij362d13e2009-08-02 19:59:21 +00002727 params->objects[i].flags |= PTPOBJECT_MTPPROPLIST_LOADED;
Linus Walleij338ade42007-07-03 20:44:08 +00002728 break;
Linus Walleijd4637502009-06-14 23:03:33 +00002729 }
Linus Walleij338ade42007-07-03 20:44:08 +00002730 }
Linus Walleij1e9a0332007-09-12 19:35:56 +00002731 prop++;
Linus Walleij338ade42007-07-03 20:44:08 +00002732 }
Linus Walleij77127dc2009-08-25 16:38:42 +00002733 /* mark last entry also */
Marcus Meissner7e328872017-04-10 08:39:00 +02002734 if (i >= 0) {
2735 params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
2736 params->nrofobjects = i+1;
2737 } else {
2738 params->nrofobjects = 0;
2739 }
Marcus Meissner859f9b22015-10-04 13:20:22 +02002740 free (props);
Linus Walleij77127dc2009-08-25 16:38:42 +00002741 /* The device might not give the list in linear ascending order */
2742 ptp_objects_sort (params);
Linus Walleij8533bf72007-08-28 10:03:59 +00002743 return 0;
Linus Walleij338ade42007-07-03 20:44:08 +00002744}
2745
2746/**
2747 * This function will recurse through all the directories on the device,
2748 * starting at the root directory, gathering metadata as it moves along.
2749 * It works better on some devices that will only return data for a
2750 * certain directory and does not respect the option to get all metadata
2751 * for all objects.
2752 */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002753static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
2754 PTPParams *params,
Linus Walleij6bf68b42007-09-03 22:50:02 +00002755 uint32_t storageid,
2756 uint32_t parent)
Linus Walleij338ade42007-07-03 20:44:08 +00002757{
2758 PTPObjectHandles currentHandles;
2759 int i = 0;
Linus Walleij338ade42007-07-03 20:44:08 +00002760 uint16_t ret = ptp_getobjecthandles(params,
Linus Walleij6bf68b42007-09-03 22:50:02 +00002761 storageid,
Linus Walleij338ade42007-07-03 20:44:08 +00002762 PTP_GOH_ALL_FORMATS,
2763 parent,
2764 &currentHandles);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002765
Linus Walleij338ade42007-07-03 20:44:08 +00002766 if (ret != PTP_RC_OK) {
2767 add_ptp_error_to_errorstack(device, ret, "get_handles_recursively(): could not get object handles.");
2768 return;
2769 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002770
Linus Walleij338ade42007-07-03 20:44:08 +00002771 if (currentHandles.Handler == NULL || currentHandles.n == 0)
2772 return;
2773
Linus Walleij338ade42007-07-03 20:44:08 +00002774 // Now descend into any subdirectories found
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002775 for (i = 0; i < currentHandles.n; i++) {
Linus Walleijd4637502009-06-14 23:03:33 +00002776 PTPObject *ob;
Linus Walleij43aba612011-11-25 09:40:34 +01002777 ret = ptp_object_want(params,currentHandles.Handler[i],
2778 PTPOBJECT_OBJECTINFO_LOADED, &ob);
Linus Walleij338ade42007-07-03 20:44:08 +00002779 if (ret == PTP_RC_OK) {
Linus Walleijd4637502009-06-14 23:03:33 +00002780 if (ob->oi.ObjectFormat == PTP_OFC_Association)
Linus Walleij43aba612011-11-25 09:40:34 +01002781 get_handles_recursively(device, params,
2782 storageid, currentHandles.Handler[i]);
Linus Walleij338ade42007-07-03 20:44:08 +00002783 } else {
2784 add_error_to_errorstack(device,
2785 LIBMTP_ERROR_CONNECTING,
2786 "Found a bad handle, trying to ignore it.");
2787 }
2788 }
Linus Walleij338ade42007-07-03 20:44:08 +00002789 free(currentHandles.Handler);
2790}
2791
2792/**
Linus Walleij438bd7f2006-06-08 11:35:44 +00002793 * This function refresh the internal handle list whenever
2794 * the items stored inside the device is altered. On operations
2795 * that do not add or remove objects, this is typically not
2796 * called.
2797 * @param device a pointer to the MTP device to flush handles for.
2798 */
2799static void flush_handles(LIBMTP_mtpdevice_t *device)
2800{
2801 PTPParams *params = (PTPParams *) device->params;
Linus Walleij338ade42007-07-03 20:44:08 +00002802 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij8533bf72007-08-28 10:03:59 +00002803 int ret;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002804 uint32_t i;
2805
Linus Walleija8b88892011-03-03 19:58:26 +01002806 if (!device->cached) {
2807 return;
2808 }
2809
Jerry Zhang60d02262017-06-08 18:19:59 -07002810 if (load_cache_on_demand) {
2811 return;
2812 }
2813
Linus Walleijd4637502009-06-14 23:03:33 +00002814 if (params->objects != NULL) {
2815 for (i=0;i<params->nrofobjects;i++)
2816 ptp_free_object (&params->objects[i]);
2817 free(params->objects);
2818 params->objects = NULL;
2819 params->nrofobjects = 0;
Linus Walleij438bd7f2006-06-08 11:35:44 +00002820 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00002821
Linus Walleij338ade42007-07-03 20:44:08 +00002822 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
Linus Walleijfec4d562008-06-01 22:30:36 +00002823 && !FLAG_BROKEN_MTPGETOBJPROPLIST(ptp_usb)
2824 && !FLAG_BROKEN_MTPGETOBJPROPLIST_ALL(ptp_usb)) {
Linus Walleij139455e2007-08-28 10:25:06 +00002825 // Use the fast method. Ignore return value for now.
Catalin Patuleaa23fce32012-07-17 22:25:12 -04002826 ret = get_all_metadata_fast(device);
Linus Walleij8533bf72007-08-28 10:03:59 +00002827 }
Linus Walleija8b88892011-03-03 19:58:26 +01002828
Linus Walleij139455e2007-08-28 10:25:06 +00002829 // If the previous failed or returned no objects, use classic
2830 // methods instead.
Linus Walleijd4637502009-06-14 23:03:33 +00002831 if (params->nrofobjects == 0) {
Linus Walleij338ade42007-07-03 20:44:08 +00002832 // Get all the handles using just standard commands.
Linus Walleij6bf68b42007-09-03 22:50:02 +00002833 if (device->storage == NULL) {
2834 get_handles_recursively(device, params,
Linus Walleij6bf68b42007-09-03 22:50:02 +00002835 PTP_GOH_ALL_STORAGE,
2836 PTP_GOH_ROOT_PARENT);
2837 } else {
2838 // Get handles for each storage in turn.
2839 LIBMTP_devicestorage_t *storage = device->storage;
2840 while(storage != NULL) {
2841 get_handles_recursively(device, params,
Linus Walleij6bf68b42007-09-03 22:50:02 +00002842 storage->id,
2843 PTP_GOH_ROOT_PARENT);
2844 storage = storage->next;
2845 }
2846 }
Linus Walleij338ade42007-07-03 20:44:08 +00002847 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002848
Linus Walleij8f7f1aa2008-06-15 19:00:23 +00002849 /*
2850 * Loop over the handles, fix up any NULL filenames or
2851 * keywords, then attempt to locate some default folders
2852 * in the root directory of the primary storage.
2853 */
Linus Walleijd4637502009-06-14 23:03:33 +00002854 for(i = 0; i < params->nrofobjects; i++) {
2855 PTPObject *ob, *xob;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002856
Linus Walleijd4637502009-06-14 23:03:33 +00002857 ob = &params->objects[i];
Linus Walleij43aba612011-11-25 09:40:34 +01002858 ret = ptp_object_want(params,params->objects[i].oid,
2859 PTPOBJECT_OBJECTINFO_LOADED, &xob);
Linus Walleijd4637502009-06-14 23:03:33 +00002860 if (ret != PTP_RC_OK) {
nicklas79daadbf22009-09-28 18:19:34 +00002861 LIBMTP_ERROR("broken! %x not found\n", params->objects[i].oid);
Linus Walleijf0bf4372007-07-01 21:47:38 +00002862 }
Linus Walleijd4637502009-06-14 23:03:33 +00002863 if (ob->oi.Filename == NULL)
2864 ob->oi.Filename = strdup("<null>");
2865 if (ob->oi.Keywords == NULL)
2866 ob->oi.Keywords = strdup("<null>");
2867
Linus Walleijf0bf4372007-07-01 21:47:38 +00002868 /* Ignore handles that point to non-folders */
Linus Walleijd4637502009-06-14 23:03:33 +00002869 if(ob->oi.ObjectFormat != PTP_OFC_Association)
Linus Walleijf0bf4372007-07-01 21:47:38 +00002870 continue;
Linus Walleij8f7f1aa2008-06-15 19:00:23 +00002871 /* Only look in the root folder */
Linus Walleij4fc19172010-01-19 00:10:26 +00002872 if (ob->oi.ParentObject == 0xffffffffU) {
Linus Walleij43aba612011-11-25 09:40:34 +01002873 LIBMTP_ERROR("object %x has parent 0xffffffff (-1) continuing anyway\n",
2874 ob->oid);
Linus Walleij4fc19172010-01-19 00:10:26 +00002875 } else if (ob->oi.ParentObject != 0x00000000U)
Linus Walleijf0bf4372007-07-01 21:47:38 +00002876 continue;
Linus Walleijc40c9bf2008-06-13 23:24:17 +00002877 /* Only look in the primary storage */
Linus Walleijd4637502009-06-14 23:03:33 +00002878 if (device->storage != NULL && ob->oi.StorageID != device->storage->id)
Linus Walleijc40c9bf2008-06-13 23:24:17 +00002879 continue;
Linus Walleij8f7f1aa2008-06-15 19:00:23 +00002880
Linus Walleijf0bf4372007-07-01 21:47:38 +00002881 /* Is this the Music Folder */
Linus Walleijd4637502009-06-14 23:03:33 +00002882 if (!strcasecmp(ob->oi.Filename, "My Music") ||
Linus Walleij4c181a82010-01-07 09:21:49 +00002883 !strcasecmp(ob->oi.Filename, "My_Music") ||
Linus Walleijd4637502009-06-14 23:03:33 +00002884 !strcasecmp(ob->oi.Filename, "Music")) {
2885 device->default_music_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002886 }
Linus Walleijd4637502009-06-14 23:03:33 +00002887 else if (!strcasecmp(ob->oi.Filename, "My Playlists") ||
Linus Walleij4c181a82010-01-07 09:21:49 +00002888 !strcasecmp(ob->oi.Filename, "My_Playlists") ||
Linus Walleijd4637502009-06-14 23:03:33 +00002889 !strcasecmp(ob->oi.Filename, "Playlists")) {
2890 device->default_playlist_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002891 }
Linus Walleijd4637502009-06-14 23:03:33 +00002892 else if (!strcasecmp(ob->oi.Filename, "My Pictures") ||
Linus Walleij4c181a82010-01-07 09:21:49 +00002893 !strcasecmp(ob->oi.Filename, "My_Pictures") ||
Linus Walleijd4637502009-06-14 23:03:33 +00002894 !strcasecmp(ob->oi.Filename, "Pictures")) {
2895 device->default_picture_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002896 }
Linus Walleijd4637502009-06-14 23:03:33 +00002897 else if (!strcasecmp(ob->oi.Filename, "My Video") ||
Linus Walleij4c181a82010-01-07 09:21:49 +00002898 !strcasecmp(ob->oi.Filename, "My_Video") ||
Linus Walleijd4637502009-06-14 23:03:33 +00002899 !strcasecmp(ob->oi.Filename, "Video")) {
2900 device->default_video_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002901 }
Linus Walleij4c181a82010-01-07 09:21:49 +00002902 else if (!strcasecmp(ob->oi.Filename, "My Organizer") ||
2903 !strcasecmp(ob->oi.Filename, "My_Organizer")) {
Linus Walleijd4637502009-06-14 23:03:33 +00002904 device->default_organizer_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002905 }
Linus Walleijd4637502009-06-14 23:03:33 +00002906 else if (!strcasecmp(ob->oi.Filename, "ZENcast") ||
2907 !strcasecmp(ob->oi.Filename, "Datacasts")) {
2908 device->default_zencast_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002909 }
Linus Walleijd4637502009-06-14 23:03:33 +00002910 else if (!strcasecmp(ob->oi.Filename, "My Albums") ||
Linus Walleij4c181a82010-01-07 09:21:49 +00002911 !strcasecmp(ob->oi.Filename, "My_Albums") ||
Linus Walleijd4637502009-06-14 23:03:33 +00002912 !strcasecmp(ob->oi.Filename, "Albums")) {
2913 device->default_album_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002914 }
Linus Walleijd4637502009-06-14 23:03:33 +00002915 else if (!strcasecmp(ob->oi.Filename, "Text") ||
2916 !strcasecmp(ob->oi.Filename, "Texts")) {
2917 device->default_text_folder = ob->oid;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002918 }
2919 }
Richard Low6711f442007-05-05 19:00:59 +00002920}
Linus Walleij438bd7f2006-06-08 11:35:44 +00002921
Linus Walleij438bd7f2006-06-08 11:35:44 +00002922/**
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002923 * This function traverses a devices storage list freeing up the
Linus Walleij9e1b0812006-12-12 19:22:02 +00002924 * strings and the structs.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002925 * @param device a pointer to the MTP device to free the storage
Linus Walleij9e1b0812006-12-12 19:22:02 +00002926 * list for.
2927 */
2928static void free_storage_list(LIBMTP_mtpdevice_t *device)
2929{
Linus Walleije1ac07e2006-12-14 19:38:59 +00002930 LIBMTP_devicestorage_t *storage;
2931 LIBMTP_devicestorage_t *tmp;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002932
Linus Walleije1ac07e2006-12-14 19:38:59 +00002933 storage = device->storage;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002934 while(storage != NULL) {
Linus Walleije1ac07e2006-12-14 19:38:59 +00002935 if (storage->StorageDescription != NULL) {
2936 free(storage->StorageDescription);
2937 }
2938 if (storage->VolumeIdentifier != NULL) {
2939 free(storage->VolumeIdentifier);
2940 }
2941 tmp = storage;
2942 storage = storage->next;
2943 free(tmp);
Linus Walleij9e1b0812006-12-12 19:22:02 +00002944 }
2945 device->storage = NULL;
2946
2947 return;
2948}
2949
2950/**
2951 * This function traverses a devices storage list freeing up the
2952 * strings and the structs.
2953 * @param device a pointer to the MTP device to free the storage
2954 * list for.
2955 */
2956static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
2957{
2958 LIBMTP_devicestorage_t *oldhead, *ptr1, *ptr2, *newlist;
2959
2960 if (device->storage == NULL)
2961 return -1;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002962 if (sortby == LIBMTP_STORAGE_SORTBY_NOTSORTED)
Linus Walleij9e1b0812006-12-12 19:22:02 +00002963 return 0;
2964
2965 oldhead = ptr1 = ptr2 = device->storage;
2966
2967 newlist = NULL;
2968
2969 while(oldhead != NULL) {
2970 ptr1 = ptr2 = oldhead;
2971 while(ptr1 != NULL) {
2972
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002973 if (sortby == LIBMTP_STORAGE_SORTBY_FREESPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
Linus Walleij9e1b0812006-12-12 19:22:02 +00002974 ptr2 = ptr1;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002975 if (sortby == LIBMTP_STORAGE_SORTBY_MAXSPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
Linus Walleij9e1b0812006-12-12 19:22:02 +00002976 ptr2 = ptr1;
2977
2978 ptr1 = ptr1->next;
2979 }
2980
2981 // Make our previous entries next point to our next
2982 if(ptr2->prev != NULL) {
2983 ptr1 = ptr2->prev;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00002984 ptr1->next = ptr2->next;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002985 } else {
2986 oldhead = ptr2->next;
2987 if(oldhead != NULL)
2988 oldhead->prev = NULL;
2989 }
2990
2991 // Make our next entries previous point to our previous
2992 ptr1 = ptr2->next;
2993 if(ptr1 != NULL) {
2994 ptr1->prev = ptr2->prev;
2995 } else {
2996 ptr1 = ptr2->prev;
2997 if(ptr1 != NULL)
2998 ptr1->next = NULL;
2999 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003000
Linus Walleij9e1b0812006-12-12 19:22:02 +00003001 if(newlist == NULL) {
3002 newlist = ptr2;
3003 newlist->prev = NULL;
3004 } else {
3005 ptr2->prev = newlist;
3006 newlist->next = ptr2;
3007 newlist = newlist->next;
3008 }
3009 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003010
Linus Walleij5c1499e2009-02-21 06:54:29 +00003011 if (newlist != NULL) {
3012 newlist->next = NULL;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003013 while(newlist->prev != NULL)
Linus Walleij5c1499e2009-02-21 06:54:29 +00003014 newlist = newlist->prev;
3015 device->storage = newlist;
3016 }
Linus Walleij9e1b0812006-12-12 19:22:02 +00003017
3018 return 0;
3019}
3020
3021/**
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003022 * This function grabs the first writeable storageid from the
Linus Walleijd71d0b32008-09-22 08:21:03 +00003023 * device storage list.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003024 * @param device a pointer to the MTP device to locate writeable
Linus Walleijd71d0b32008-09-22 08:21:03 +00003025 * storage for.
3026 * @param fitsize a file of this file must fit on the device.
Linus Walleij9e1b0812006-12-12 19:22:02 +00003027 */
Linus Walleij88babc82014-06-02 21:32:14 +02003028static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
3029 uint64_t fitsize)
Linus Walleij9e1b0812006-12-12 19:22:02 +00003030{
Linus Walleij5c1499e2009-02-21 06:54:29 +00003031 LIBMTP_devicestorage_t *storage;
Linus Walleijfb28b632007-10-23 21:56:18 +00003032 uint32_t store = 0x00000000; //Should this be 0xffffffffu instead?
Linus Walleijd71d0b32008-09-22 08:21:03 +00003033 int subcall_ret;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003034
Linus Walleijd71d0b32008-09-22 08:21:03 +00003035 // See if there is some storage we can fit this file on.
3036 storage = device->storage;
3037 if (storage == NULL) {
3038 // Sometimes the storage just cannot be detected.
3039 store = 0x00000000U;
3040 } else {
3041 while(storage != NULL) {
3042 // These storages cannot be used.
Linus Walleij43aba612011-11-25 09:40:34 +01003043 if (storage->StorageType == PTP_ST_FixedROM ||
3044 storage->StorageType == PTP_ST_RemovableROM) {
Linus Walleijd71d0b32008-09-22 08:21:03 +00003045 storage = storage->next;
3046 continue;
3047 }
3048 // Storage IDs with the lower 16 bits 0x0000 are not supposed
3049 // to be writeable.
3050 if ((storage->id & 0x0000FFFFU) == 0x00000000U) {
3051 storage = storage->next;
3052 continue;
3053 }
3054 // Also check the access capability to avoid e.g. deletable only storages
Linus Walleij43aba612011-11-25 09:40:34 +01003055 if (storage->AccessCapability == PTP_AC_ReadOnly ||
3056 storage->AccessCapability == PTP_AC_ReadOnly_with_Object_Deletion) {
Linus Walleijd71d0b32008-09-22 08:21:03 +00003057 storage = storage->next;
3058 continue;
3059 }
3060 // Then see if we can fit the file.
3061 subcall_ret = check_if_file_fits(device, storage, fitsize);
3062 if (subcall_ret != 0) {
3063 storage = storage->next;
3064 } else {
3065 // We found a storage that is writable and can fit the file!
3066 break;
3067 }
3068 }
3069 if (storage == NULL) {
Linus Walleij43aba612011-11-25 09:40:34 +01003070 add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL,
3071 "get_writeable_storageid(): "
Linus Walleijd71d0b32008-09-22 08:21:03 +00003072 "all device storage is full or corrupt.");
3073 return -1;
3074 }
Linus Walleij9e1b0812006-12-12 19:22:02 +00003075 store = storage->id;
Linus Walleijd71d0b32008-09-22 08:21:03 +00003076 }
Linus Walleij9e1b0812006-12-12 19:22:02 +00003077
3078 return store;
3079}
3080
3081/**
Linus Walleij88babc82014-06-02 21:32:14 +02003082 * Tries to suggest a storage_id of a given ID when we have a parent
3083 * @param device a pointer to the device where to search for the storage ID
3084 * @param fitsize a file of this file must fit on the device.
3085 * @param parent_id look for this ID
3086 * @ret storageID
3087 */
3088static int get_suggested_storage_id(LIBMTP_mtpdevice_t *device,
3089 uint64_t fitsize,
3090 uint32_t parent_id)
3091{
3092 PTPParams *params = (PTPParams *) device->params;
3093 PTPObject *ob;
3094 uint16_t ret;
Linus Walleij88babc82014-06-02 21:32:14 +02003095
3096 ret = ptp_object_want(params, parent_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
3097 if ((ret != PTP_RC_OK) || (ob->oi.StorageID == 0)) {
3098 add_ptp_error_to_errorstack(device, ret, "get_suggested_storage_id(): "
3099 "could not get storage id from parent id.");
3100 return get_writeable_storageid(device, fitsize);
3101 } else {
3102 /* OK we know the parent storage, then use that */
3103 return ob->oi.StorageID;
3104 }
3105}
3106
3107/**
Linus Walleij6bf68b42007-09-03 22:50:02 +00003108 * This function grabs the freespace from a certain storage in
Linus Walleij9e1b0812006-12-12 19:22:02 +00003109 * device storage list.
3110 * @param device a pointer to the MTP device to free the storage
3111 * list for.
Linus Walleij6bf68b42007-09-03 22:50:02 +00003112 * @param storageid the storage ID for the storage to flush and
3113 * get free space for.
3114 * @param freespace the free space on this storage will be returned
3115 * in this variable.
Linus Walleij9e1b0812006-12-12 19:22:02 +00003116 */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003117static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
Linus Walleij6bf68b42007-09-03 22:50:02 +00003118 LIBMTP_devicestorage_t *storage,
3119 uint64_t *freespace)
Linus Walleij9e1b0812006-12-12 19:22:02 +00003120{
Linus Walleije1ac07e2006-12-14 19:38:59 +00003121 PTPParams *params = (PTPParams *) device->params;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003122
Linus Walleije1ac07e2006-12-14 19:38:59 +00003123 // Always query the device about this, since some models explicitly
Linus Walleij6bf68b42007-09-03 22:50:02 +00003124 // needs that. We flush all data on queries storage here.
Linus Walleije1ac07e2006-12-14 19:38:59 +00003125 if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
3126 PTPStorageInfo storageInfo;
Linus Walleij070e9b42007-01-22 08:49:28 +00003127 uint16_t ret;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003128
Linus Walleij070e9b42007-01-22 08:49:28 +00003129 ret = ptp_getstorageinfo(params, storage->id, &storageInfo);
3130 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01003131 add_ptp_error_to_errorstack(device, ret,
3132 "get_storage_freespace(): could not get storage info.");
Linus Walleije1ac07e2006-12-14 19:38:59 +00003133 return -1;
3134 }
3135 if (storage->StorageDescription != NULL) {
3136 free(storage->StorageDescription);
3137 }
3138 if (storage->VolumeIdentifier != NULL) {
3139 free(storage->VolumeIdentifier);
3140 }
3141 storage->StorageType = storageInfo.StorageType;
3142 storage->FilesystemType = storageInfo.FilesystemType;
3143 storage->AccessCapability = storageInfo.AccessCapability;
3144 storage->MaxCapacity = storageInfo.MaxCapability;
3145 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
3146 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
3147 storage->StorageDescription = storageInfo.StorageDescription;
3148 storage->VolumeIdentifier = storageInfo.VolumeLabel;
3149 }
3150 if(storage->FreeSpaceInBytes == (uint64_t) -1)
Linus Walleij9e1b0812006-12-12 19:22:02 +00003151 return -1;
3152 *freespace = storage->FreeSpaceInBytes;
3153 return 0;
3154}
3155
3156/**
Linus Walleij8c45b292006-04-26 14:12:44 +00003157 * This function dumps out a large chunk of textual information
3158 * provided from the PTP protocol and additionally some extra
3159 * MTP-specific information where applicable.
3160 * @param device a pointer to the MTP device to report info from.
3161 */
3162void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
3163{
3164 int i;
3165 PTPParams *params = (PTPParams *) device->params;
Linus Walleijc6210fb2006-05-08 11:11:41 +00003166 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleije1ac07e2006-12-14 19:38:59 +00003167 LIBMTP_devicestorage_t *storage = device->storage;
Linus Walleij38d7ee82010-07-24 22:17:21 +00003168 LIBMTP_device_extension_t *tmpext = device->extensions;
mopoke96143402006-10-30 04:37:26 +00003169
Linus Walleijc6210fb2006-05-08 11:11:41 +00003170 printf("USB low-level info:\n");
3171 dump_usbinfo(ptp_usb);
Linus Walleij8c45b292006-04-26 14:12:44 +00003172 /* Print out some verbose information */
3173 printf("Device info:\n");
3174 printf(" Manufacturer: %s\n", params->deviceinfo.Manufacturer);
3175 printf(" Model: %s\n", params->deviceinfo.Model);
3176 printf(" Device version: %s\n", params->deviceinfo.DeviceVersion);
3177 printf(" Serial number: %s\n", params->deviceinfo.SerialNumber);
Linus Walleij43aba612011-11-25 09:40:34 +01003178 printf(" Vendor extension ID: 0x%08x\n",
3179 params->deviceinfo.VendorExtensionID);
3180 printf(" Vendor extension description: %s\n",
3181 params->deviceinfo.VendorExtensionDesc);
3182 printf(" Detected object size: %d bits\n",
3183 device->object_bitsize);
Linus Walleij38d7ee82010-07-24 22:17:21 +00003184 printf(" Extensions:\n");
3185 while (tmpext != NULL) {
3186 printf(" %s: %d.%d\n",
3187 tmpext->name,
3188 tmpext->major,
3189 tmpext->minor);
3190 tmpext = tmpext->next;
3191 }
Linus Walleij8c45b292006-04-26 14:12:44 +00003192 printf("Supported operations:\n");
Marcus Meissner5e6ef6c2016-11-18 21:21:15 +01003193 for (i=0;i<params->deviceinfo.OperationsSupported_len;i++)
3194 printf(" %04x: %s\n", params->deviceinfo.OperationsSupported[i], ptp_get_opcode_name(params, params->deviceinfo.OperationsSupported[i]));
Linus Walleij8c45b292006-04-26 14:12:44 +00003195 printf("Events supported:\n");
3196 if (params->deviceinfo.EventsSupported_len == 0) {
3197 printf(" None.\n");
3198 } else {
3199 for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
Marcus Meissner5e6ef6c2016-11-18 21:21:15 +01003200 printf(" 0x%04x (%s)\n", params->deviceinfo.EventsSupported[i], ptp_strerror(params->deviceinfo.EventsSupported[i], params->deviceinfo.VendorExtensionID));
Linus Walleij8c45b292006-04-26 14:12:44 +00003201 }
3202 }
3203 printf("Device Properties Supported:\n");
3204 for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
Linus Walleij43aba612011-11-25 09:40:34 +01003205 char const *propdesc = ptp_get_property_description(params,
3206 params->deviceinfo.DevicePropertiesSupported[i]);
mopoke96143402006-10-30 04:37:26 +00003207
Linus Walleij545c7792006-06-13 15:22:30 +00003208 if (propdesc != NULL) {
Linus Walleij43aba612011-11-25 09:40:34 +01003209 printf(" 0x%04x: %s\n",
3210 params->deviceinfo.DevicePropertiesSupported[i], propdesc);
Linus Walleij545c7792006-06-13 15:22:30 +00003211 } else {
3212 uint16_t prop = params->deviceinfo.DevicePropertiesSupported[i];
Linus Walleijcf223e62006-06-19 09:31:53 +00003213 printf(" 0x%04x: Unknown property\n", prop);
Linus Walleij545c7792006-06-13 15:22:30 +00003214 }
Linus Walleij8c45b292006-04-26 14:12:44 +00003215 }
Linus Walleij0af979a2006-06-19 11:49:10 +00003216
3217 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
3218 printf("Playable File (Object) Types and Object Properties Supported:\n");
3219 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
3220 char txt[256];
3221 uint16_t ret;
3222 uint16_t *props = NULL;
3223 uint32_t propcnt = 0;
3224 int j;
mopoke96143402006-10-30 04:37:26 +00003225
Linus Walleij43aba612011-11-25 09:40:34 +01003226 (void) ptp_render_ofc (params, params->deviceinfo.ImageFormats[i],
3227 sizeof(txt), txt);
Linus Walleij0af979a2006-06-19 11:49:10 +00003228 printf(" %04x: %s\n", params->deviceinfo.ImageFormats[i], txt);
mopoke96143402006-10-30 04:37:26 +00003229
Linus Walleij43aba612011-11-25 09:40:34 +01003230 ret = ptp_mtp_getobjectpropssupported (params,
3231 params->deviceinfo.ImageFormats[i], &propcnt, &props);
Linus Walleij0af979a2006-06-19 11:49:10 +00003232 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01003233 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Dump_Device_Info(): "
3234 "error on query for object properties.");
Linus Walleij0af979a2006-06-19 11:49:10 +00003235 } else {
3236 for (j=0;j<propcnt;j++) {
Linus Walleij14830342007-03-23 13:32:00 +00003237 PTPObjectPropDesc opd;
3238 int k;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003239
Linus Walleij43aba612011-11-25 09:40:34 +01003240 printf(" %04x: %s", props[j],
3241 LIBMTP_Get_Property_Description(map_ptp_property_to_libmtp_property(props[j])));
Linus Walleij14830342007-03-23 13:32:00 +00003242 // Get a more verbose description
Linus Walleij43aba612011-11-25 09:40:34 +01003243 ret = ptp_mtp_getobjectpropdesc(params, props[j],
3244 params->deviceinfo.ImageFormats[i],
3245 &opd);
Linus Walleij14830342007-03-23 13:32:00 +00003246 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01003247 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
3248 "LIBMTP_Dump_Device_Info(): "
Linus Walleij14830342007-03-23 13:32:00 +00003249 "could not get property description.");
3250 break;
3251 }
3252
3253 if (opd.DataType == PTP_DTC_STR) {
3254 printf(" STRING data type");
Linus Walleij22cc4872007-11-05 22:57:19 +00003255 switch (opd.FormFlag) {
3256 case PTP_OPFF_DateTime:
3257 printf(" DATETIME FORM");
3258 break;
3259 case PTP_OPFF_RegularExpression:
3260 printf(" REGULAR EXPRESSION FORM");
3261 break;
3262 case PTP_OPFF_LongString:
3263 printf(" LONG STRING FORM");
3264 break;
3265 default:
3266 break;
3267 }
Linus Walleij14830342007-03-23 13:32:00 +00003268 } else {
3269 if (opd.DataType & PTP_DTC_ARRAY_MASK) {
3270 printf(" array of");
3271 }
3272
3273 switch (opd.DataType & (~PTP_DTC_ARRAY_MASK)) {
3274
3275 case PTP_DTC_UNDEF:
3276 printf(" UNDEFINED data type");
3277 break;
Linus Walleij14830342007-03-23 13:32:00 +00003278 case PTP_DTC_INT8:
3279 printf(" INT8 data type");
3280 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003281 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003282 printf(" range: MIN %d, MAX %d, STEP %d",
3283 opd.FORM.Range.MinimumValue.i8,
3284 opd.FORM.Range.MaximumValue.i8,
3285 opd.FORM.Range.StepSize.i8);
3286 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003287 case PTP_OPFF_Enumeration:
Linus Walleij14830342007-03-23 13:32:00 +00003288 printf(" enumeration: ");
3289 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3290 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i8);
3291 }
3292 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003293 case PTP_OPFF_ByteArray:
3294 printf(" byte array: ");
3295 break;
Linus Walleij14830342007-03-23 13:32:00 +00003296 default:
3297 printf(" ANY 8BIT VALUE form");
3298 break;
3299 }
3300 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003301
Linus Walleij14830342007-03-23 13:32:00 +00003302 case PTP_DTC_UINT8:
3303 printf(" UINT8 data type");
3304 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003305 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003306 printf(" range: MIN %d, MAX %d, STEP %d",
3307 opd.FORM.Range.MinimumValue.u8,
3308 opd.FORM.Range.MaximumValue.u8,
3309 opd.FORM.Range.StepSize.u8);
3310 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003311 case PTP_OPFF_Enumeration:
Linus Walleij14830342007-03-23 13:32:00 +00003312 printf(" enumeration: ");
3313 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3314 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u8);
3315 }
3316 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003317 case PTP_OPFF_ByteArray:
3318 printf(" byte array: ");
3319 break;
Linus Walleij14830342007-03-23 13:32:00 +00003320 default:
3321 printf(" ANY 8BIT VALUE form");
3322 break;
3323 }
3324 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003325
Linus Walleij14830342007-03-23 13:32:00 +00003326 case PTP_DTC_INT16:
3327 printf(" INT16 data type");
3328 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003329 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003330 printf(" range: MIN %d, MAX %d, STEP %d",
3331 opd.FORM.Range.MinimumValue.i16,
3332 opd.FORM.Range.MaximumValue.i16,
3333 opd.FORM.Range.StepSize.i16);
3334 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003335 case PTP_OPFF_Enumeration:
Linus Walleij14830342007-03-23 13:32:00 +00003336 printf(" enumeration: ");
3337 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3338 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i16);
3339 }
3340 break;
3341 default:
3342 printf(" ANY 16BIT VALUE form");
3343 break;
3344 }
3345 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003346
Linus Walleij14830342007-03-23 13:32:00 +00003347 case PTP_DTC_UINT16:
3348 printf(" UINT16 data type");
3349 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003350 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003351 printf(" range: MIN %d, MAX %d, STEP %d",
3352 opd.FORM.Range.MinimumValue.u16,
3353 opd.FORM.Range.MaximumValue.u16,
3354 opd.FORM.Range.StepSize.u16);
3355 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003356 case PTP_OPFF_Enumeration:
Linus Walleij14830342007-03-23 13:32:00 +00003357 printf(" enumeration: ");
3358 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3359 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u16);
3360 }
3361 break;
3362 default:
3363 printf(" ANY 16BIT VALUE form");
3364 break;
3365 }
3366 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003367
Linus Walleij14830342007-03-23 13:32:00 +00003368 case PTP_DTC_INT32:
3369 printf(" INT32 data type");
3370 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003371 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003372 printf(" range: MIN %d, MAX %d, STEP %d",
3373 opd.FORM.Range.MinimumValue.i32,
3374 opd.FORM.Range.MaximumValue.i32,
3375 opd.FORM.Range.StepSize.i32);
3376 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003377 case PTP_OPFF_Enumeration:
Linus Walleij14830342007-03-23 13:32:00 +00003378 printf(" enumeration: ");
3379 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3380 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i32);
3381 }
3382 break;
3383 default:
3384 printf(" ANY 32BIT VALUE form");
3385 break;
3386 }
3387 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003388
Linus Walleij14830342007-03-23 13:32:00 +00003389 case PTP_DTC_UINT32:
3390 printf(" UINT32 data type");
3391 switch (opd.FormFlag) {
Linus Walleij22cc4872007-11-05 22:57:19 +00003392 case PTP_OPFF_Range:
Linus Walleij14830342007-03-23 13:32:00 +00003393 printf(" range: MIN %d, MAX %d, STEP %d",
3394 opd.FORM.Range.MinimumValue.u32,
3395 opd.FORM.Range.MaximumValue.u32,
3396 opd.FORM.Range.StepSize.u32);
3397 break;
Linus Walleij22cc4872007-11-05 22:57:19 +00003398 case PTP_OPFF_Enumeration:
Linus Walleijd71d0b32008-09-22 08:21:03 +00003399 // Special pretty-print for FOURCC codes
3400 if (params->deviceinfo.ImageFormats[i] == PTP_OPC_VideoFourCCCodec) {
3401 printf(" enumeration of u32 casted FOURCC: ");
3402 for (k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3403 if (opd.FORM.Enum.SupportedValue[k].u32 == 0) {
3404 printf("ANY, ");
3405 } else {
3406 char fourcc[6];
3407 fourcc[0] = (opd.FORM.Enum.SupportedValue[k].u32 >> 24) & 0xFFU;
3408 fourcc[1] = (opd.FORM.Enum.SupportedValue[k].u32 >> 16) & 0xFFU;
3409 fourcc[2] = (opd.FORM.Enum.SupportedValue[k].u32 >> 8) & 0xFFU;
3410 fourcc[3] = opd.FORM.Enum.SupportedValue[k].u32 & 0xFFU;
3411 fourcc[4] = '\n';
3412 fourcc[5] = '\0';
3413 printf("\"%s\", ", fourcc);
3414 }
3415 }
3416 } else {
3417 printf(" enumeration: ");
3418 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
3419 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u32);
3420 }
Linus Walleij14830342007-03-23 13:32:00 +00003421 }
3422 break;
3423 default:
3424 printf(" ANY 32BIT VALUE form");
3425 break;
3426 }
3427 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003428
Linus Walleij14830342007-03-23 13:32:00 +00003429 case PTP_DTC_INT64:
3430 printf(" INT64 data type");
3431 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003432
Linus Walleij14830342007-03-23 13:32:00 +00003433 case PTP_DTC_UINT64:
3434 printf(" UINT64 data type");
3435 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003436
Linus Walleij14830342007-03-23 13:32:00 +00003437 case PTP_DTC_INT128:
3438 printf(" INT128 data type");
3439 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003440
Linus Walleij14830342007-03-23 13:32:00 +00003441 case PTP_DTC_UINT128:
3442 printf(" UINT128 data type");
3443 break;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003444
Linus Walleij14830342007-03-23 13:32:00 +00003445 default:
3446 printf(" UNKNOWN data type");
3447 break;
3448 }
3449 }
3450 if (opd.GetSet) {
3451 printf(" GET/SET");
3452 } else {
3453 printf(" READ ONLY");
3454 }
3455 printf("\n");
Linus Walleijdbcc8242007-08-05 22:31:26 +00003456 ptp_free_objectpropdesc(&opd);
Linus Walleij0af979a2006-06-19 11:49:10 +00003457 }
3458 free(props);
3459 }
3460 }
3461 }
mopoke96143402006-10-30 04:37:26 +00003462
Linus Walleij43aba612011-11-25 09:40:34 +01003463 if(storage != NULL &&
3464 ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
Linus Walleij9e1b0812006-12-12 19:22:02 +00003465 printf("Storage Devices:\n");
Linus Walleije1ac07e2006-12-14 19:38:59 +00003466 while(storage != NULL) {
3467 printf(" StorageID: 0x%08x\n",storage->id);
Linus Walleijd71d0b32008-09-22 08:21:03 +00003468 printf(" StorageType: 0x%04x ",storage->StorageType);
3469 switch (storage->StorageType) {
3470 case PTP_ST_Undefined:
3471 printf("(undefined)\n");
3472 break;
3473 case PTP_ST_FixedROM:
3474 printf("fixed ROM storage\n");
3475 break;
3476 case PTP_ST_RemovableROM:
3477 printf("removable ROM storage\n");
3478 break;
3479 case PTP_ST_FixedRAM:
3480 printf("fixed RAM storage\n");
3481 break;
3482 case PTP_ST_RemovableRAM:
3483 printf("removable RAM storage\n");
3484 break;
3485 default:
3486 printf("UNKNOWN storage\n");
3487 break;
3488 }
3489 printf(" FilesystemType: 0x%04x ",storage->FilesystemType);
3490 switch(storage->FilesystemType) {
3491 case PTP_FST_Undefined:
3492 printf("(undefined)\n");
3493 break;
3494 case PTP_FST_GenericFlat:
3495 printf("generic flat filesystem\n");
3496 break;
3497 case PTP_FST_GenericHierarchical:
3498 printf("generic hierarchical\n");
3499 break;
3500 case PTP_FST_DCF:
3501 printf("DCF\n");
3502 break;
3503 default:
3504 printf("UNKNONWN filesystem type\n");
3505 break;
3506 }
3507 printf(" AccessCapability: 0x%04x ",storage->AccessCapability);
3508 switch(storage->AccessCapability) {
3509 case PTP_AC_ReadWrite:
3510 printf("read/write\n");
3511 break;
3512 case PTP_AC_ReadOnly:
3513 printf("read only\n");
3514 break;
3515 case PTP_AC_ReadOnly_with_Object_Deletion:
3516 printf("read only + object deletion\n");
3517 break;
3518 default:
3519 printf("UNKNOWN access capability\n");
3520 break;
3521 }
Linus Walleij43aba612011-11-25 09:40:34 +01003522 printf(" MaxCapacity: %llu\n",
3523 (long long unsigned int) storage->MaxCapacity);
3524 printf(" FreeSpaceInBytes: %llu\n",
3525 (long long unsigned int) storage->FreeSpaceInBytes);
3526 printf(" FreeSpaceInObjects: %llu\n",
3527 (long long unsigned int) storage->FreeSpaceInObjects);
Linus Walleije1ac07e2006-12-14 19:38:59 +00003528 printf(" StorageDescription: %s\n",storage->StorageDescription);
3529 printf(" VolumeIdentifier: %s\n",storage->VolumeIdentifier);
3530 storage = storage->next;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003531 }
3532 }
3533
Linus Walleij545c7792006-06-13 15:22:30 +00003534 printf("Special directories:\n");
Linus Walleij43aba612011-11-25 09:40:34 +01003535 printf(" Default music folder: 0x%08x\n",
3536 device->default_music_folder);
3537 printf(" Default playlist folder: 0x%08x\n",
3538 device->default_playlist_folder);
3539 printf(" Default picture folder: 0x%08x\n",
3540 device->default_picture_folder);
3541 printf(" Default video folder: 0x%08x\n",
3542 device->default_video_folder);
3543 printf(" Default organizer folder: 0x%08x\n",
3544 device->default_organizer_folder);
3545 printf(" Default zencast folder: 0x%08x\n",
3546 device->default_zencast_folder);
3547 printf(" Default album folder: 0x%08x\n",
3548 device->default_album_folder);
3549 printf(" Default text folder: 0x%08x\n",
3550 device->default_text_folder);
Linus Walleij8c45b292006-04-26 14:12:44 +00003551}
3552
3553/**
Linus Walleij5d533bb2007-07-17 21:48:57 +00003554 * This resets a device in case it supports the <code>PTP_OC_ResetDevice</code>
3555 * operation code (0x1010).
3556 * @param device a pointer to the device to reset.
3557 * @return 0 on success, any other value means failure.
3558 */
3559int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t *device)
3560{
3561 PTPParams *params = (PTPParams *) device->params;
3562 uint16_t ret;
3563
3564 if (!ptp_operation_issupported(params,PTP_OC_ResetDevice)) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003565 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Linus Walleij43aba612011-11-25 09:40:34 +01003566 "LIBMTP_Reset_Device(): "
3567 "device does not support resetting.");
Linus Walleij5d533bb2007-07-17 21:48:57 +00003568 return -1;
3569 }
3570 ret = ptp_resetdevice(params);
3571 if (ret != PTP_RC_OK) {
3572 add_ptp_error_to_errorstack(device, ret, "Error resetting.");
3573 return -1;
3574 }
3575 return 0;
3576}
3577
3578/**
Linus Walleij2350b712008-01-14 22:54:37 +00003579 * This retrieves the manufacturer name of an MTP device.
3580 * @param device a pointer to the device to get the manufacturer name for.
3581 * @return a newly allocated UTF-8 string representing the manufacturer name.
3582 * The string must be freed by the caller after use. If the call
3583 * was unsuccessful this will contain NULL.
3584 */
3585char *LIBMTP_Get_Manufacturername(LIBMTP_mtpdevice_t *device)
3586{
3587 char *retmanuf = NULL;
3588 PTPParams *params = (PTPParams *) device->params;
3589
3590 if (params->deviceinfo.Manufacturer != NULL) {
3591 retmanuf = strdup(params->deviceinfo.Manufacturer);
3592 }
3593 return retmanuf;
3594}
3595
3596/**
mopoke96143402006-10-30 04:37:26 +00003597 * This retrieves the model name (often equal to product name)
Linus Walleij80124062006-03-15 10:26:09 +00003598 * of an MTP device.
3599 * @param device a pointer to the device to get the model name for.
3600 * @return a newly allocated UTF-8 string representing the model name.
3601 * The string must be freed by the caller after use. If the call
3602 * was unsuccessful this will contain NULL.
3603 */
3604char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t *device)
3605{
3606 char *retmodel = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00003607 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00003608
Linus Walleij9b28da32006-03-16 13:47:58 +00003609 if (params->deviceinfo.Model != NULL) {
3610 retmodel = strdup(params->deviceinfo.Model);
Linus Walleij80124062006-03-15 10:26:09 +00003611 }
3612 return retmodel;
3613}
3614
3615/**
3616 * This retrieves the serial number of an MTP device.
3617 * @param device a pointer to the device to get the serial number for.
3618 * @return a newly allocated UTF-8 string representing the serial number.
3619 * The string must be freed by the caller after use. If the call
3620 * was unsuccessful this will contain NULL.
3621 */
3622char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t *device)
3623{
3624 char *retnumber = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00003625 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00003626
Linus Walleij9b28da32006-03-16 13:47:58 +00003627 if (params->deviceinfo.SerialNumber != NULL) {
3628 retnumber = strdup(params->deviceinfo.SerialNumber);
Linus Walleij80124062006-03-15 10:26:09 +00003629 }
3630 return retnumber;
3631}
3632
3633/**
mopoke96143402006-10-30 04:37:26 +00003634 * This retrieves the device version (hardware and firmware version) of an
Linus Walleij80124062006-03-15 10:26:09 +00003635 * MTP device.
3636 * @param device a pointer to the device to get the device version for.
3637 * @return a newly allocated UTF-8 string representing the device version.
3638 * The string must be freed by the caller after use. If the call
3639 * was unsuccessful this will contain NULL.
3640 */
3641char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t *device)
3642{
3643 char *retversion = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00003644 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00003645
Linus Walleij9b28da32006-03-16 13:47:58 +00003646 if (params->deviceinfo.DeviceVersion != NULL) {
3647 retversion = strdup(params->deviceinfo.DeviceVersion);
Linus Walleij80124062006-03-15 10:26:09 +00003648 }
3649 return retversion;
3650}
3651
3652
3653/**
Linus Walleijfae27482006-08-19 20:13:25 +00003654 * This retrieves the "friendly name" of an MTP device. Usually
3655 * this is simply the name of the owner or something like
Linus Walleij30658792006-08-19 22:18:55 +00003656 * "John Doe's Digital Audio Player". This property should be supported
Linus Walleijfae27482006-08-19 20:13:25 +00003657 * by all MTP devices.
3658 * @param device a pointer to the device to get the friendly name for.
mopoke96143402006-10-30 04:37:26 +00003659 * @return a newly allocated UTF-8 string representing the friendly name.
Linus Walleijb9256fd2006-02-15 09:40:43 +00003660 * The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00003661 * @see LIBMTP_Set_Friendlyname()
Linus Walleijb9256fd2006-02-15 09:40:43 +00003662 */
Linus Walleij30658792006-08-19 22:18:55 +00003663char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t *device)
Linus Walleijb9256fd2006-02-15 09:40:43 +00003664{
Linus Walleijb02a0662006-04-25 08:05:09 +00003665 PTPPropertyValue propval;
Linus Walleijb9256fd2006-02-15 09:40:43 +00003666 char *retstring = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00003667 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00003668 uint16_t ret;
Linus Walleijb9256fd2006-02-15 09:40:43 +00003669
Linus Walleijcf223e62006-06-19 09:31:53 +00003670 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
3671 return NULL;
3672 }
3673
Linus Walleij070e9b42007-01-22 08:49:28 +00003674 ret = ptp_getdevicepropvalue(params,
3675 PTP_DPC_MTP_DeviceFriendlyName,
3676 &propval,
3677 PTP_DTC_STR);
3678 if (ret != PTP_RC_OK) {
3679 add_ptp_error_to_errorstack(device, ret, "Error getting friendlyname.");
Linus Walleijb9256fd2006-02-15 09:40:43 +00003680 return NULL;
3681 }
Linus Walleija823a702006-08-27 21:27:46 +00003682 if (propval.str != NULL) {
3683 retstring = strdup(propval.str);
3684 free(propval.str);
3685 }
Linus Walleijfae27482006-08-19 20:13:25 +00003686 return retstring;
3687}
3688
3689/**
Linus Walleij30658792006-08-19 22:18:55 +00003690 * Sets the "friendly name" of an MTP device.
3691 * @param device a pointer to the device to set the friendly name for.
3692 * @param friendlyname the new friendly name for the device.
3693 * @return 0 on success, any other value means failure.
Linus Walleijd5b34972008-09-24 20:24:12 +00003694 * @see LIBMTP_Get_Friendlyname()
Linus Walleij30658792006-08-19 22:18:55 +00003695 */
3696int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t *device,
3697 char const * const friendlyname)
3698{
3699 PTPPropertyValue propval;
3700 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00003701 uint16_t ret;
Linus Walleij30658792006-08-19 22:18:55 +00003702
3703 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
3704 return -1;
3705 }
Linus Walleija823a702006-08-27 21:27:46 +00003706 propval.str = (char *) friendlyname;
Linus Walleij070e9b42007-01-22 08:49:28 +00003707 ret = ptp_setdevicepropvalue(params,
3708 PTP_DPC_MTP_DeviceFriendlyName,
3709 &propval,
3710 PTP_DTC_STR);
3711 if (ret != PTP_RC_OK) {
3712 add_ptp_error_to_errorstack(device, ret, "Error setting friendlyname.");
Linus Walleij30658792006-08-19 22:18:55 +00003713 return -1;
3714 }
Linus Walleij30658792006-08-19 22:18:55 +00003715 return 0;
3716}
3717
3718/**
Linus Walleijfae27482006-08-19 20:13:25 +00003719 * This retrieves the syncronization partner of an MTP device. This
3720 * property should be supported by all MTP devices.
3721 * @param device a pointer to the device to get the sync partner for.
3722 * @return a newly allocated UTF-8 string representing the synchronization
3723 * partner. The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00003724 * @see LIBMTP_Set_Syncpartner()
Linus Walleijfae27482006-08-19 20:13:25 +00003725 */
3726char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t *device)
3727{
3728 PTPPropertyValue propval;
3729 char *retstring = NULL;
3730 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00003731 uint16_t ret;
Linus Walleijfae27482006-08-19 20:13:25 +00003732
3733 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
3734 return NULL;
3735 }
3736
Linus Walleij070e9b42007-01-22 08:49:28 +00003737 ret = ptp_getdevicepropvalue(params,
3738 PTP_DPC_MTP_SynchronizationPartner,
3739 &propval,
3740 PTP_DTC_STR);
3741 if (ret != PTP_RC_OK) {
3742 add_ptp_error_to_errorstack(device, ret, "Error getting syncpartner.");
Linus Walleijfae27482006-08-19 20:13:25 +00003743 return NULL;
3744 }
Linus Walleija823a702006-08-27 21:27:46 +00003745 if (propval.str != NULL) {
3746 retstring = strdup(propval.str);
3747 free(propval.str);
3748 }
Linus Walleijb9256fd2006-02-15 09:40:43 +00003749 return retstring;
3750}
3751
Linus Walleij30658792006-08-19 22:18:55 +00003752
3753/**
3754 * Sets the synchronization partner of an MTP device. Note that
3755 * we have no idea what the effect of setting this to "foobar"
3756 * may be. But the general idea seems to be to tell which program
3757 * shall synchronize with this device and tell others to leave
3758 * it alone.
3759 * @param device a pointer to the device to set the sync partner for.
3760 * @param syncpartner the new synchronization partner for the device.
3761 * @return 0 on success, any other value means failure.
3762 * @see LIBMTP_Get_Syncpartner()
3763 */
3764int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t *device,
3765 char const * const syncpartner)
3766{
3767 PTPPropertyValue propval;
3768 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00003769 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00003770
Linus Walleij30658792006-08-19 22:18:55 +00003771 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
3772 return -1;
3773 }
Linus Walleija823a702006-08-27 21:27:46 +00003774 propval.str = (char *) syncpartner;
Linus Walleij070e9b42007-01-22 08:49:28 +00003775 ret = ptp_setdevicepropvalue(params,
3776 PTP_DPC_MTP_SynchronizationPartner,
3777 &propval,
3778 PTP_DTC_STR);
3779 if (ret != PTP_RC_OK) {
3780 add_ptp_error_to_errorstack(device, ret, "Error setting syncpartner.");
Linus Walleij30658792006-08-19 22:18:55 +00003781 return -1;
3782 }
Linus Walleij30658792006-08-19 22:18:55 +00003783 return 0;
3784}
3785
Linus Walleij394bbbe2006-02-22 16:10:53 +00003786/**
Linus Walleijf5fcda32006-12-03 22:31:02 +00003787 * Checks if the device can stora a file of this size or
3788 * if it's too big.
3789 * @param device a pointer to the device.
3790 * @param filesize the size of the file to check whether it will fit.
Linus Walleij6bf68b42007-09-03 22:50:02 +00003791 * @param storageid the ID of the storage to try to fit the file on.
Linus Walleijf5fcda32006-12-03 22:31:02 +00003792 * @return 0 if the file fits, any other value means failure.
3793 */
Linus Walleij6bf68b42007-09-03 22:50:02 +00003794static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
3795 LIBMTP_devicestorage_t *storage,
3796 uint64_t const filesize) {
Linus Walleijf5fcda32006-12-03 22:31:02 +00003797 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf5fcda32006-12-03 22:31:02 +00003798 uint64_t freebytes;
Linus Walleijf5fcda32006-12-03 22:31:02 +00003799 int ret;
3800
3801 // If we cannot check the storage, no big deal.
3802 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
3803 return 0;
3804 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003805
Linus Walleij6bf68b42007-09-03 22:50:02 +00003806 ret = get_storage_freespace(device, storage, &freebytes);
Linus Walleijf5fcda32006-12-03 22:31:02 +00003807 if (ret != 0) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003808 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Linus Walleij6bf68b42007-09-03 22:50:02 +00003809 "check_if_file_fits(): error checking free storage.");
Linus Walleijf5fcda32006-12-03 22:31:02 +00003810 return -1;
3811 } else {
Linus Walleijfb28b632007-10-23 21:56:18 +00003812 // See if it fits.
Linus Walleijf5fcda32006-12-03 22:31:02 +00003813 if (filesize > freebytes) {
Linus Walleijf5fcda32006-12-03 22:31:02 +00003814 return -1;
3815 }
3816 }
3817 return 0;
3818}
3819
3820
Linus Walleijf5fcda32006-12-03 22:31:02 +00003821/**
Linus Walleijfa1374c2006-02-27 07:41:46 +00003822 * This function retrieves the current battery level on the device.
3823 * @param device a pointer to the device to get the battery level for.
mopoke96143402006-10-30 04:37:26 +00003824 * @param maximum_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00003825 * maximum level of the battery if the call was successful.
mopoke96143402006-10-30 04:37:26 +00003826 * @param current_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00003827 * current level of the battery if the call was successful.
Linus Walleij545c7792006-06-13 15:22:30 +00003828 * A value of 0 means that the device is on external power.
Linus Walleijfa1374c2006-02-27 07:41:46 +00003829 * @return 0 if the storage info was successfully retrieved, any other
Linus Walleij80439342006-09-12 10:42:26 +00003830 * means failure. A typical cause of failure is that
Linus Walleij545c7792006-06-13 15:22:30 +00003831 * the device does not support the battery level property.
Linus Walleijfa1374c2006-02-27 07:41:46 +00003832 */
mopoke96143402006-10-30 04:37:26 +00003833int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *device,
3834 uint8_t * const maximum_level,
Linus Walleijfa1374c2006-02-27 07:41:46 +00003835 uint8_t * const current_level)
3836{
Linus Walleijb02a0662006-04-25 08:05:09 +00003837 PTPPropertyValue propval;
Linus Walleijfa1374c2006-02-27 07:41:46 +00003838 uint16_t ret;
Linus Walleij9b28da32006-03-16 13:47:58 +00003839 PTPParams *params = (PTPParams *) device->params;
Linus Walleij4096c882009-03-16 23:32:34 +00003840 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijfa1374c2006-02-27 07:41:46 +00003841
Linus Walleij545c7792006-06-13 15:22:30 +00003842 *maximum_level = 0;
3843 *current_level = 0;
3844
Linus Walleij4096c882009-03-16 23:32:34 +00003845 if (FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) ||
3846 !ptp_property_issupported(params, PTP_DPC_BatteryLevel)) {
Linus Walleij545c7792006-06-13 15:22:30 +00003847 return -1;
3848 }
mopoke96143402006-10-30 04:37:26 +00003849
Linus Walleij43aba612011-11-25 09:40:34 +01003850 ret = ptp_getdevicepropvalue(params, PTP_DPC_BatteryLevel,
3851 &propval, PTP_DTC_UINT8);
Linus Walleijb02a0662006-04-25 08:05:09 +00003852 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01003853 add_ptp_error_to_errorstack(device, ret,
3854 "LIBMTP_Get_Batterylevel(): "
3855 "could not get device property value.");
Linus Walleijfa1374c2006-02-27 07:41:46 +00003856 return -1;
3857 }
mopoke96143402006-10-30 04:37:26 +00003858
Linus Walleijfa1374c2006-02-27 07:41:46 +00003859 *maximum_level = device->maximum_battery_level;
Linus Walleijb02a0662006-04-25 08:05:09 +00003860 *current_level = propval.u8;
mopoke96143402006-10-30 04:37:26 +00003861
Linus Walleijfa1374c2006-02-27 07:41:46 +00003862 return 0;
3863}
3864
Linus Walleij13374a42006-09-13 11:55:30 +00003865
3866/**
3867 * Formats device storage (if the device supports the operation).
3868 * WARNING: This WILL delete all data from the device. Make sure you've
3869 * got confirmation from the user BEFORE you call this function.
3870 *
Linus Walleijf8491912006-12-15 10:23:30 +00003871 * @param device a pointer to the device containing the storage to format.
3872 * @param storage the actual storage to format.
Linus Walleij13374a42006-09-13 11:55:30 +00003873 * @return 0 on success, any other value means failure.
3874 */
Linus Walleij43aba612011-11-25 09:40:34 +01003875int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *device,
3876 LIBMTP_devicestorage_t *storage)
Linus Walleij13374a42006-09-13 11:55:30 +00003877{
3878 uint16_t ret;
3879 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00003880
Linus Walleij13374a42006-09-13 11:55:30 +00003881 if (!ptp_operation_issupported(params,PTP_OC_FormatStore)) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00003882 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Linus Walleij43aba612011-11-25 09:40:34 +01003883 "LIBMTP_Format_Storage(): "
3884 "device does not support formatting storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00003885 return -1;
3886 }
Linus Walleijf8491912006-12-15 10:23:30 +00003887 ret = ptp_formatstore(params, storage->id);
Linus Walleij13374a42006-09-13 11:55:30 +00003888 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01003889 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Format_Storage(): "
3890 "failed to format storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00003891 return -1;
3892 }
3893 return 0;
3894}
3895
Linus Walleijfa1374c2006-02-27 07:41:46 +00003896/**
Linus Walleij545c7792006-06-13 15:22:30 +00003897 * Helper function to extract a unicode property off a device.
Linus Walleije46f12e2006-06-22 17:53:25 +00003898 * This is the standard way of retrieveing unicode device
3899 * properties as described by the PTP spec.
Linus Walleijcf223e62006-06-19 09:31:53 +00003900 * @param device a pointer to the device to get the property from.
mopoke96143402006-10-30 04:37:26 +00003901 * @param unicstring a pointer to a pointer that will hold the
Linus Walleijcf223e62006-06-19 09:31:53 +00003902 * property after this call is completed.
3903 * @param property the property to retrieve.
3904 * @return 0 on success, any other value means failure.
Linus Walleij545c7792006-06-13 15:22:30 +00003905 */
mopoke96143402006-10-30 04:37:26 +00003906static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +00003907 char **unicstring, uint16_t property)
Linus Walleij545c7792006-06-13 15:22:30 +00003908{
3909 PTPPropertyValue propval;
3910 PTPParams *params = (PTPParams *) device->params;
Linus Walleij16571dc2006-08-17 20:27:46 +00003911 uint16_t *tmp;
Linus Walleij070e9b42007-01-22 08:49:28 +00003912 uint16_t ret;
Linus Walleij545c7792006-06-13 15:22:30 +00003913 int i;
3914
3915 if (!ptp_property_issupported(params, property)) {
3916 return -1;
3917 }
3918
Linus Walleijcf223e62006-06-19 09:31:53 +00003919 // Unicode strings are 16bit unsigned integer arrays.
Linus Walleij070e9b42007-01-22 08:49:28 +00003920 ret = ptp_getdevicepropvalue(params,
3921 property,
3922 &propval,
3923 PTP_DTC_AUINT16);
3924 if (ret != PTP_RC_OK) {
3925 // TODO: add a note on WHICH property that we failed to get.
tedbullock4e51cb92007-02-15 11:48:34 +00003926 *unicstring = NULL;
Linus Walleij43aba612011-11-25 09:40:34 +01003927 add_ptp_error_to_errorstack(device, ret,
3928 "get_device_unicode_property(): "
3929 "failed to get unicode property.");
Linus Walleij545c7792006-06-13 15:22:30 +00003930 return -1;
3931 }
3932
3933 // Extract the actual array.
Linus Walleij16571dc2006-08-17 20:27:46 +00003934 // printf("Array of %d elements\n", propval.a.count);
3935 tmp = malloc((propval.a.count + 1)*sizeof(uint16_t));
Linus Walleij545c7792006-06-13 15:22:30 +00003936 for (i = 0; i < propval.a.count; i++) {
Linus Walleij16571dc2006-08-17 20:27:46 +00003937 tmp[i] = propval.a.v[i].u16;
3938 // printf("%04x ", tmp[i]);
Linus Walleij545c7792006-06-13 15:22:30 +00003939 }
Linus Walleij16571dc2006-08-17 20:27:46 +00003940 tmp[propval.a.count] = 0x0000U;
Linus Walleij545c7792006-06-13 15:22:30 +00003941 free(propval.a.v);
3942
Linus Walleij3ec86312006-08-21 13:25:24 +00003943 *unicstring = utf16_to_utf8(device, tmp);
Linus Walleij16571dc2006-08-17 20:27:46 +00003944
Linus Walleij545c7792006-06-13 15:22:30 +00003945 free(tmp);
3946
3947 return 0;
3948}
3949
3950/**
3951 * This function returns the secure time as an XML document string from
3952 * the device.
3953 * @param device a pointer to the device to get the secure time for.
3954 * @param sectime the secure time string as an XML document or NULL if the call
3955 * failed or the secure time property is not supported. This string
3956 * must be <code>free()</code>:ed by the caller after use.
3957 * @return 0 on success, any other value means failure.
3958 */
Linus Walleij8ab54262006-06-21 07:12:28 +00003959int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *device, char ** const sectime)
Linus Walleij545c7792006-06-13 15:22:30 +00003960{
3961 return get_device_unicode_property(device, sectime, PTP_DPC_MTP_SecureTime);
3962}
3963
3964/**
mopoke96143402006-10-30 04:37:26 +00003965 * This function returns the device (public key) certificate as an
Linus Walleij545c7792006-06-13 15:22:30 +00003966 * XML document string from the device.
3967 * @param device a pointer to the device to get the device certificate for.
3968 * @param devcert the device certificate as an XML string or NULL if the call
3969 * failed or the device certificate property is not supported. This
3970 * string must be <code>free()</code>:ed by the caller after use.
3971 * @return 0 on success, any other value means failure.
3972 */
Linus Walleij8ab54262006-06-21 07:12:28 +00003973int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *device, char ** const devcert)
Linus Walleij545c7792006-06-13 15:22:30 +00003974{
Linus Walleij43aba612011-11-25 09:40:34 +01003975 return get_device_unicode_property(device, devcert,
3976 PTP_DPC_MTP_DeviceCertificate);
Linus Walleij545c7792006-06-13 15:22:30 +00003977}
3978
3979/**
Linus Walleij8ab54262006-06-21 07:12:28 +00003980 * This function retrieves a list of supported file types, i.e. the file
3981 * types that this device claims it supports, e.g. audio file types that
3982 * the device can play etc. This list is mitigated to
3983 * inlcude the file types that libmtp can handle, i.e. it will not list
3984 * filetypes that libmtp will handle internally like playlists and folders.
3985 * @param device a pointer to the device to get the filetype capabilities for.
3986 * @param filetypes a pointer to a pointer that will hold the list of
3987 * supported filetypes if the call was successful. This list must
3988 * be <code>free()</code>:ed by the caller after use.
3989 * @param length a pointer to a variable that will hold the length of the
3990 * list of supported filetypes if the call was successful.
3991 * @return 0 on success, any other value means failure.
3992 * @see LIBMTP_Get_Filetype_Description()
3993 */
mopoke96143402006-10-30 04:37:26 +00003994int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const filetypes,
Linus Walleij8ab54262006-06-21 07:12:28 +00003995 uint16_t * const length)
3996{
3997 PTPParams *params = (PTPParams *) device->params;
Linus Walleija3544f62007-11-30 01:20:04 +00003998 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij8ab54262006-06-21 07:12:28 +00003999 uint16_t *localtypes;
4000 uint16_t localtypelen;
4001 uint32_t i;
mopoke96143402006-10-30 04:37:26 +00004002
Linus Walleij8ab54262006-06-21 07:12:28 +00004003 // This is more memory than needed if there are unknown types, but what the heck.
4004 localtypes = (uint16_t *) malloc(params->deviceinfo.ImageFormats_len * sizeof(uint16_t));
4005 localtypelen = 0;
mopoke96143402006-10-30 04:37:26 +00004006
Linus Walleij8ab54262006-06-21 07:12:28 +00004007 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
4008 uint16_t localtype = map_ptp_type_to_libmtp_type(params->deviceinfo.ImageFormats[i]);
4009 if (localtype != LIBMTP_FILETYPE_UNKNOWN) {
4010 localtypes[localtypelen] = localtype;
4011 localtypelen++;
4012 }
4013 }
Linus Walleija3544f62007-11-30 01:20:04 +00004014 // The forgotten Ogg support on YP-10 and others...
Linus Walleijfec4d562008-06-01 22:30:36 +00004015 if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
Linus Walleij43aba612011-11-25 09:40:34 +01004016 localtypes = (uint16_t *) realloc(localtypes,
4017 (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
Linus Walleija3544f62007-11-30 01:20:04 +00004018 localtypes[localtypelen] = LIBMTP_FILETYPE_OGG;
4019 localtypelen++;
4020 }
Linus Walleij89bb1cd2009-07-24 21:03:36 +00004021 // The forgotten FLAC support on Cowon iAudio S9 and others...
4022 if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
Linus Walleij43aba612011-11-25 09:40:34 +01004023 localtypes = (uint16_t *) realloc(localtypes,
4024 (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
Linus Walleij89bb1cd2009-07-24 21:03:36 +00004025 localtypes[localtypelen] = LIBMTP_FILETYPE_FLAC;
4026 localtypelen++;
4027 }
Linus Walleij8ab54262006-06-21 07:12:28 +00004028
4029 *filetypes = localtypes;
4030 *length = localtypelen;
4031
4032 return 0;
4033}
4034
raveloxd9a28642006-05-26 23:42:22 +00004035/**
Linus Walleija3895612013-04-29 21:56:38 +02004036 * This function checks if the device has some specific capabilities, in
4037 * order to avoid calling APIs that may disturb the device.
4038 *
4039 * @param device a pointer to the device to check the capability on.
4040 * @param cap the capability to check.
4041 * @return 0 if not supported, any other value means the device has the
4042 * requested capability.
4043 */
4044int LIBMTP_Check_Capability(LIBMTP_mtpdevice_t *device, LIBMTP_devicecap_t cap)
4045{
4046 switch (cap) {
4047 case LIBMTP_DEVICECAP_GetPartialObject:
4048 return (ptp_operation_issupported(device->params,
4049 PTP_OC_GetPartialObject) ||
4050 ptp_operation_issupported(device->params,
4051 PTP_OC_ANDROID_GetPartialObject64));
4052 case LIBMTP_DEVICECAP_SendPartialObject:
4053 return ptp_operation_issupported(device->params,
4054 PTP_OC_ANDROID_SendPartialObject);
4055 case LIBMTP_DEVICECAP_EditObjects:
4056 return (ptp_operation_issupported(device->params,
4057 PTP_OC_ANDROID_TruncateObject) &&
4058 ptp_operation_issupported(device->params,
4059 PTP_OC_ANDROID_BeginEditObject) &&
4060 ptp_operation_issupported(device->params,
4061 PTP_OC_ANDROID_EndEditObject));
4062 /*
4063 * Handle other capabilities here, this is also a good place to
4064 * blacklist some advanced operations on specific devices if need
4065 * be.
4066 */
4067
4068 default:
4069 break;
4070 }
4071 return 0;
4072}
4073
4074/**
Linus Walleij5b452bd2007-12-28 23:34:18 +00004075 * This function updates all the storage id's of a device and their
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004076 * properties, then creates a linked list and puts the list head into
Linus Walleijf8491912006-12-15 10:23:30 +00004077 * the device struct. It also optionally sorts this list. If you want
4078 * to display storage information in your application you should call
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004079 * this function, then dereference the device struct
Linus Walleij5b452bd2007-12-28 23:34:18 +00004080 * (<code>device-&gt;storage</code>) to get out information on the storage.
4081 *
4082 * You need to call this everytime you want to update the
4083 * <code>device-&gt;storage</code> list, for example anytime you need
4084 * to check available storage somewhere.
4085 *
4086 * <b>WARNING:</b> since this list is dynamically updated, do not
4087 * reference its fields in external applications by pointer! E.g
4088 * do not put a reference to any <code>char *</code> field. instead
4089 * <code>strncpy()</code> it!
Linus Walleijf8491912006-12-15 10:23:30 +00004090 *
Linus Walleije1b88e82008-07-02 20:12:53 +00004091 * @param device a pointer to the device to get the storage for.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004092 * @param sortby an integer that determines the sorting of the storage list.
Linus Walleij9e1b0812006-12-12 19:22:02 +00004093 * Valid sort methods are defined in libmtp.h with beginning with
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004094 * LIBMTP_STORAGE_SORTBY_. 0 or LIBMTP_STORAGE_SORTBY_NOTSORTED to not
Linus Walleij9e1b0812006-12-12 19:22:02 +00004095 * sort.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004096 * @return 0 on success, 1 success but only with storage id's, storage
Linus Walleij9e1b0812006-12-12 19:22:02 +00004097 * properities could not be retrieved and -1 means failure.
Linus Walleij9e1b0812006-12-12 19:22:02 +00004098 */
4099int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *device, int const sortby)
4100{
4101 uint32_t i = 0;
4102 PTPStorageInfo storageInfo;
4103 PTPParams *params = (PTPParams *) device->params;
4104 PTPStorageIDs storageIDs;
Linus Walleije1ac07e2006-12-14 19:38:59 +00004105 LIBMTP_devicestorage_t *storage = NULL;
4106 LIBMTP_devicestorage_t *storageprev = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004107
4108 if (device->storage != NULL)
4109 free_storage_list(device);
4110
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004111 // if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs))
Linus Walleij9e1b0812006-12-12 19:22:02 +00004112 // return -1;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004113 if (ptp_getstorageids (params, &storageIDs) != PTP_RC_OK)
Linus Walleij9e1b0812006-12-12 19:22:02 +00004114 return -1;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004115 if (storageIDs.n < 1)
Linus Walleij9e1b0812006-12-12 19:22:02 +00004116 return -1;
4117
4118 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
4119 for (i = 0; i < storageIDs.n; i++) {
4120
Linus Walleij43aba612011-11-25 09:40:34 +01004121 storage = (LIBMTP_devicestorage_t *)
4122 malloc(sizeof(LIBMTP_devicestorage_t));
Linus Walleije1ac07e2006-12-14 19:38:59 +00004123 storage->prev = storageprev;
4124 if (storageprev != NULL)
4125 storageprev->next = storage;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004126 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00004127 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004128
Linus Walleije1ac07e2006-12-14 19:38:59 +00004129 storage->id = storageIDs.Storage[i];
4130 storage->StorageType = PTP_ST_Undefined;
4131 storage->FilesystemType = PTP_FST_Undefined;
4132 storage->AccessCapability = PTP_AC_ReadWrite;
4133 storage->MaxCapacity = (uint64_t) -1;
4134 storage->FreeSpaceInBytes = (uint64_t) -1;
4135 storage->FreeSpaceInObjects = (uint64_t) -1;
4136 storage->StorageDescription = strdup("Unknown storage");
4137 storage->VolumeIdentifier = strdup("Unknown volume");
4138 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004139
Linus Walleije1ac07e2006-12-14 19:38:59 +00004140 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004141 }
4142 free(storageIDs.Storage);
4143 return 1;
4144 } else {
4145 for (i = 0; i < storageIDs.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004146 uint16_t ret;
4147 ret = ptp_getstorageinfo(params, storageIDs.Storage[i], &storageInfo);
4148 if (ret != PTP_RC_OK) {
Linus Walleij43aba612011-11-25 09:40:34 +01004149 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Storage(): "
4150 "Could not get storage info.");
Linus Walleij9e1b0812006-12-12 19:22:02 +00004151 if (device->storage != NULL) {
4152 free_storage_list(device);
4153 }
4154 return -1;
4155 }
4156
Linus Walleij43aba612011-11-25 09:40:34 +01004157 storage = (LIBMTP_devicestorage_t *)
4158 malloc(sizeof(LIBMTP_devicestorage_t));
Linus Walleije1ac07e2006-12-14 19:38:59 +00004159 storage->prev = storageprev;
4160 if (storageprev != NULL)
4161 storageprev->next = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004162 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00004163 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004164
Linus Walleije1ac07e2006-12-14 19:38:59 +00004165 storage->id = storageIDs.Storage[i];
4166 storage->StorageType = storageInfo.StorageType;
4167 storage->FilesystemType = storageInfo.FilesystemType;
4168 storage->AccessCapability = storageInfo.AccessCapability;
4169 storage->MaxCapacity = storageInfo.MaxCapability;
4170 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
4171 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
4172 storage->StorageDescription = storageInfo.StorageDescription;
4173 storage->VolumeIdentifier = storageInfo.VolumeLabel;
4174 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004175
Linus Walleije1ac07e2006-12-14 19:38:59 +00004176 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004177 }
4178
Linus Walleij5c1499e2009-02-21 06:54:29 +00004179 if (storage != NULL)
4180 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004181
4182 sort_storage_by(device,sortby);
4183 free(storageIDs.Storage);
4184 return 0;
4185 }
4186}
4187
4188/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00004189 * This creates a new file metadata structure and allocates memory
4190 * for it. Notice that if you add strings to this structure they
4191 * will be freed by the corresponding <code>LIBMTP_destroy_file_t</code>
mopoke96143402006-10-30 04:37:26 +00004192 * operation later, so be careful of using strdup() when assigning
Linus Walleijf6bc1782006-03-24 15:12:47 +00004193 * strings, e.g.:
4194 *
4195 * <pre>
4196 * LIBMTP_file_t *file = LIBMTP_new_file_t();
4197 * file->filename = strdup(namestr);
4198 * ....
4199 * LIBMTP_destroy_file_t(file);
4200 * </pre>
4201 *
4202 * @return a pointer to the newly allocated metadata structure.
4203 * @see LIBMTP_destroy_file_t()
4204 */
4205LIBMTP_file_t *LIBMTP_new_file_t(void)
4206{
4207 LIBMTP_file_t *new = (LIBMTP_file_t *) malloc(sizeof(LIBMTP_file_t));
4208 if (new == NULL) {
4209 return NULL;
4210 }
4211 new->filename = NULL;
Linus Walleijea68f1f2008-06-22 21:54:44 +00004212 new->item_id = 0;
4213 new->parent_id = 0;
4214 new->storage_id = 0;
Linus Walleijf6bc1782006-03-24 15:12:47 +00004215 new->filesize = 0;
Richard Lowd3b17022009-04-11 12:37:39 +00004216 new->modificationdate = 0;
Linus Walleijf6bc1782006-03-24 15:12:47 +00004217 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
4218 new->next = NULL;
4219 return new;
4220}
4221
4222/**
4223 * This destroys a file metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00004224 * used by it, including any strings. Never use a file metadata
Linus Walleijf6bc1782006-03-24 15:12:47 +00004225 * structure again after calling this function on it.
4226 * @param file the file metadata to destroy.
4227 * @see LIBMTP_new_file_t()
4228 */
4229void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
4230{
4231 if (file == NULL) {
4232 return;
4233 }
4234 if (file->filename != NULL)
4235 free(file->filename);
4236 free(file);
4237 return;
4238}
4239
4240/**
Linus Walleija8b88892011-03-03 19:58:26 +01004241 * Helper function that takes one PTP object and creates a
4242 * LIBMTP_file_t metadata entry.
4243 */
4244static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
4245{
4246 PTPParams *params = (PTPParams *) device->params;
4247 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
4248 LIBMTP_file_t *file;
4249 int i;
4250
4251 // Allocate a new file type
4252 file = LIBMTP_new_file_t();
4253
4254 file->parent_id = ob->oi.ParentObject;
4255 file->storage_id = ob->oi.StorageID;
4256
4257 // Set the filetype
4258 file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
4259
4260 /*
4261 * A special quirk for devices that doesn't quite
4262 * remember that some files marked as "unknown" type are
4263 * actually OGG or FLAC files. We look at the filename extension
4264 * and see if it happens that this was atleast named "ogg" or "flac"
4265 * and fall back on this heuristic approach in that case,
4266 * for these bugged devices only.
4267 */
4268 if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
4269 if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
4270 FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
4271 has_ogg_extension(file->filename)) {
4272 file->filetype = LIBMTP_FILETYPE_OGG;
4273 }
4274
4275 if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && has_flac_extension(file->filename)) {
4276 file->filetype = LIBMTP_FILETYPE_FLAC;
4277 }
4278 }
4279
4280 // Set the modification date
4281 file->modificationdate = ob->oi.ModificationDate;
4282
4283 // We only have 32-bit file size here; later we use the PTP_OPC_ObjectSize property
4284 file->filesize = ob->oi.ObjectCompressedSize;
4285 if (ob->oi.Filename != NULL) {
4286 file->filename = strdup(ob->oi.Filename);
4287 }
4288
4289 // This is a unique ID so we can keep track of the file.
4290 file->item_id = ob->oid;
4291
4292 /*
4293 * If we have a cached, large set of metadata, then use it!
4294 */
4295 if (ob->mtpprops) {
4296 MTPProperties *prop = ob->mtpprops;
4297
4298 for (i=0; i < ob->nrofmtpprops; i++, prop++) {
4299 // Pick ObjectSize here...
4300 if (prop->property == PTP_OPC_ObjectSize) {
4301 // This may already be set, but this 64bit precision value
4302 // is better than the PTP 32bit value, so let it override.
4303 if (device->object_bitsize == 64) {
4304 file->filesize = prop->propval.u64;
4305 } else {
4306 file->filesize = prop->propval.u32;
4307 }
4308 break;
4309 }
4310 }
Lei Zhangcaa1bbc2013-02-13 15:12:02 -08004311 } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
Linus Walleija8b88892011-03-03 19:58:26 +01004312 uint16_t *props = NULL;
4313 uint32_t propcnt = 0;
4314 int ret;
4315
4316 // First see which properties can be retrieved for this object format
4317 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(file->filetype), &propcnt, &props);
4318 if (ret != PTP_RC_OK) {
Linus Walleijf46d7d92011-03-04 20:17:01 +01004319 add_ptp_error_to_errorstack(device, ret, "obj2file: call to ptp_mtp_getobjectpropssupported() failed.");
Linus Walleija8b88892011-03-03 19:58:26 +01004320 // Silently fall through.
4321 } else {
4322 for (i = 0; i < propcnt; i++) {
4323 switch (props[i]) {
4324 case PTP_OPC_ObjectSize:
4325 if (device->object_bitsize == 64) {
4326 file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
4327 } else {
4328 file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
4329 }
4330 break;
4331 default:
4332 break;
4333 }
4334 }
4335 free(props);
4336 }
4337 }
4338
4339 return file;
4340}
4341
4342
4343/**
4344 * This function retrieves the metadata for a single file off
4345 * the device.
4346 *
4347 * Do not call this function repeatedly! The file handles are linearly
4348 * searched O(n) and the call may involve (slow) USB traffic, so use
4349 * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
4350 * as an efficient data structure such as a hash list.
4351 *
4352 * Incidentally this function will return metadata for
4353 * a folder (association) as well, but this is not a proper use
4354 * of it, it is intended for file manipulation, not folder manipulation.
4355 *
4356 * @param device a pointer to the device to get the file metadata from.
4357 * @param fileid the object ID of the file that you want the metadata for.
4358 * @return a metadata entry on success or NULL on failure.
4359 * @see LIBMTP_Get_Filelisting()
4360 */
4361LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
4362{
4363 PTPParams *params = (PTPParams *) device->params;
4364 uint16_t ret;
4365 PTPObject *ob;
4366
4367 // Get all the handles if we haven't already done that
4368 // (Only on cached devices.)
4369 if (device->cached && params->nrofobjects == 0) {
4370 flush_handles(device);
4371 }
4372
4373 ret = ptp_object_want(params, fileid, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
4374 if (ret != PTP_RC_OK)
4375 return NULL;
4376
4377 return obj2file(device, ob);
4378}
4379
4380/**
mopoke31364442006-11-20 04:53:04 +00004381* THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Richard Lowdc0b6c72006-11-13 09:22:23 +00004382 * NOT TO USE IT.
4383 * @see LIBMTP_Get_Filelisting_With_Callback()
4384 */
4385LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *device)
4386{
nicklas79daadbf22009-09-28 18:19:34 +00004387 LIBMTP_INFO("WARNING: LIBMTP_Get_Filelisting() is deprecated.\n");
4388 LIBMTP_INFO("WARNING: please update your code to use LIBMTP_Get_Filelisting_With_Callback()\n");
Richard Lowdc0b6c72006-11-13 09:22:23 +00004389 return LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
4390}
4391
4392/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00004393 * This returns a long list of all files available
Linus Walleij25d5c212008-08-14 06:49:13 +00004394 * on the current MTP device. Folders will not be returned, but abstract
4395 * entities like playlists and albums will show up as "files". Typical usage:
Linus Walleijf6bc1782006-03-24 15:12:47 +00004396 *
4397 * <pre>
4398 * LIBMTP_file_t *filelist;
4399 *
Richard Lowdc0b6c72006-11-13 09:22:23 +00004400 * filelist = LIBMTP_Get_Filelisting_With_Callback(device, callback, data);
Linus Walleijf6bc1782006-03-24 15:12:47 +00004401 * while (filelist != NULL) {
4402 * LIBMTP_file_t *tmp;
4403 *
4404 * // Do something on each element in the list here...
4405 * tmp = filelist;
4406 * filelist = filelist->next;
4407 * LIBMTP_destroy_file_t(tmp);
4408 * }
4409 * </pre>
4410 *
Linus Walleij25d5c212008-08-14 06:49:13 +00004411 * If you want to group your file listing by storage (per storage unit) or
4412 * arrange files into folders, you must dereference the <code>storage_id</code>
4413 * and/or <code>parent_id</code> field of the returned <code>LIBMTP_file_t</code>
4414 * struct. To arrange by folders or files you typically have to create the proper
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004415 * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
Linus Walleij25d5c212008-08-14 06:49:13 +00004416 * <code>LIBMTP_Get_Folder_List()</code> first.
4417 *
Linus Walleijf6bc1782006-03-24 15:12:47 +00004418 * @param device a pointer to the device to get the file listing for.
Richard Lowdc0b6c72006-11-13 09:22:23 +00004419 * @param callback a function to be called during the tracklisting retrieveal
Linus Walleij25d5c212008-08-14 06:49:13 +00004420 * for displaying progress bars etc, or NULL if you don't want
4421 * any callbacks.
Richard Lowdc0b6c72006-11-13 09:22:23 +00004422 * @param data a user-defined pointer that is passed along to
Linus Walleij25d5c212008-08-14 06:49:13 +00004423 * the <code>progress</code> function in order to
4424 * pass along some user defined data to the progress
4425 * updates. If not used, set this to NULL.
Linus Walleijf6bc1782006-03-24 15:12:47 +00004426 * @return a list of files that can be followed using the <code>next</code>
Linus Walleij25d5c212008-08-14 06:49:13 +00004427 * field of the <code>LIBMTP_file_t</code> data structure.
4428 * Each of the metadata tags must be freed after use, and may
4429 * contain only partial metadata information, i.e. one or several
4430 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004431 * @see LIBMTP_Get_Filemetadata()
Linus Walleijf6bc1782006-03-24 15:12:47 +00004432 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00004433LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
4434 LIBMTP_progressfunc_t const callback,
4435 void const * const data)
Linus Walleijf6bc1782006-03-24 15:12:47 +00004436{
4437 uint32_t i = 0;
4438 LIBMTP_file_t *retfiles = NULL;
4439 LIBMTP_file_t *curfile = NULL;
4440 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004441
mopoke96143402006-10-30 04:37:26 +00004442 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00004443 if (params->nrofobjects == 0) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00004444 flush_handles(device);
Linus Walleijf6bc1782006-03-24 15:12:47 +00004445 }
mopoke96143402006-10-30 04:37:26 +00004446
Linus Walleijd4637502009-06-14 23:03:33 +00004447 for (i = 0; i < params->nrofobjects; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004448 LIBMTP_file_t *file;
Linus Walleija8b88892011-03-03 19:58:26 +01004449 PTPObject *ob;
Linus Walleijf6bc1782006-03-24 15:12:47 +00004450
Richard Lowdc0b6c72006-11-13 09:22:23 +00004451 if (callback != NULL)
Linus Walleijd4637502009-06-14 23:03:33 +00004452 callback(i, params->nrofobjects, data);
mopoke31364442006-11-20 04:53:04 +00004453
Linus Walleijd4637502009-06-14 23:03:33 +00004454 ob = &params->objects[i];
Linus Walleijf6bc1782006-03-24 15:12:47 +00004455
Linus Walleijd4637502009-06-14 23:03:33 +00004456 if (ob->oi.ObjectFormat == PTP_OFC_Association) {
Linus Walleijd9d28d52007-08-04 19:01:18 +00004457 // MTP use this object format for folders which means
Linus Walleijf0bf4372007-07-01 21:47:38 +00004458 // these "files" will turn up on a folder listing instead.
4459 continue;
Linus Walleijf6bc1782006-03-24 15:12:47 +00004460 }
4461
Linus Walleija8b88892011-03-03 19:58:26 +01004462 // Look up metadata
4463 file = obj2file(device, ob);
4464 if (file == NULL) {
4465 continue;
Linus Walleijd9d28d52007-08-04 19:01:18 +00004466 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004467
Linus Walleijf0bf4372007-07-01 21:47:38 +00004468 // Add track to a list that will be returned afterwards.
4469 if (retfiles == NULL) {
4470 retfiles = file;
4471 curfile = file;
4472 } else {
4473 curfile->next = file;
4474 curfile = file;
4475 }
4476
4477 // Call listing callback
4478 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004479
Linus Walleijf6bc1782006-03-24 15:12:47 +00004480 } // Handle counting loop
4481 return retfiles;
4482}
4483
4484/**
Linus Walleij71c79292011-03-05 22:08:07 +01004485 * This function retrieves the contents of a certain folder
4486 * with id parent on a certain storage on a certain device.
4487 * The result contains both files and folders.
4488 * The device used with this operations must have been opened with
4489 * LIBMTP_Open_Raw_Device_Uncached() or it will fail.
4490 *
4491 * NOTE: the request will always perform I/O with the device.
4492 * @param device a pointer to the MTP device to report info from.
4493 * @param storage a storage on the device to report info from. If
Linus Walleij1e4dfea2011-03-05 22:50:39 +01004494 * 0 is passed in, the files for the given parent will be
Linus Walleij71c79292011-03-05 22:08:07 +01004495 * searched across all available storages.
4496 * @param parent the parent folder id.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004497 */
Linus Walleij71c79292011-03-05 22:08:07 +01004498LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device,
Linus Walleij8a199282011-03-05 22:28:41 +01004499 uint32_t const storage,
Linus Walleij71c79292011-03-05 22:08:07 +01004500 uint32_t const parent)
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004501{
Linus Walleij71c79292011-03-05 22:08:07 +01004502 PTPParams *params = (PTPParams *) device->params;
Linus Walleij6ccbe962011-11-25 09:40:00 +01004503 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij71c79292011-03-05 22:08:07 +01004504 LIBMTP_file_t *retfiles = NULL;
4505 LIBMTP_file_t *curfile = NULL;
Linus Walleija8b88892011-03-03 19:58:26 +01004506 PTPObjectHandles currentHandles;
Linus Walleij71c79292011-03-05 22:08:07 +01004507 uint32_t storageid;
4508 uint16_t ret;
4509 int i = 0;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004510
Linus Walleij71c79292011-03-05 22:08:07 +01004511 if (device->cached) {
4512 // This function is only supposed to be used by devices
4513 // opened as uncached!
4514 LIBMTP_ERROR("tried to use %s on a cached device!\n",
4515 __func__);
4516 return NULL;
Linus Walleij366dd1c2011-03-04 20:27:03 +01004517 }
4518
Linus Walleij6ccbe962011-11-25 09:40:00 +01004519 if (FLAG_BROKEN_GET_OBJECT_PROPVAL(ptp_usb)) {
4520 // These devices cannot handle the commands needed for
4521 // Uncached access!
4522 LIBMTP_ERROR("tried to use %s on an unsupported device, "
4523 "this command does not work on all devices "
4524 "due to missing low-level support to read "
4525 "information on individual tracks\n",
4526 __func__);
4527 return NULL;
4528 }
4529
Linus Walleij1e4dfea2011-03-05 22:50:39 +01004530 if (storage == 0)
Linus Walleij71c79292011-03-05 22:08:07 +01004531 storageid = PTP_GOH_ALL_STORAGE;
4532 else
Linus Walleij8a199282011-03-05 22:28:41 +01004533 storageid = storage;
Linus Walleij71c79292011-03-05 22:08:07 +01004534
4535 ret = ptp_getobjecthandles(params,
4536 storageid,
4537 PTP_GOH_ALL_FORMATS,
4538 parent,
4539 &currentHandles);
Linus Walleija8b88892011-03-03 19:58:26 +01004540
4541 if (ret != PTP_RC_OK) {
Linus Walleij71c79292011-03-05 22:08:07 +01004542 add_ptp_error_to_errorstack(device, ret,
4543 "LIBMTP_Get_Files_And_Folders(): could not get object handles.");
Linus Walleija8b88892011-03-03 19:58:26 +01004544 return NULL;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004545 }
4546
Linus Walleija8b88892011-03-03 19:58:26 +01004547 if (currentHandles.Handler == NULL || currentHandles.n == 0)
Linus Walleijd4637502009-06-14 23:03:33 +00004548 return NULL;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004549
Linus Walleija8b88892011-03-03 19:58:26 +01004550 for (i = 0; i < currentHandles.n; i++) {
4551 LIBMTP_file_t *file;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004552
Linus Walleija8b88892011-03-03 19:58:26 +01004553 // Get metadata for one file, if it fails, try next file
4554 file = LIBMTP_Get_Filemetadata(device, currentHandles.Handler[i]);
4555 if (file == NULL)
4556 continue;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004557
Linus Walleija8b88892011-03-03 19:58:26 +01004558 // Add track to a list that will be returned afterwards.
Linus Walleij366dd1c2011-03-04 20:27:03 +01004559 if (curfile == NULL) {
Linus Walleija8b88892011-03-03 19:58:26 +01004560 curfile = file;
Linus Walleij71c79292011-03-05 22:08:07 +01004561 retfiles = file;
Linus Walleijd4637502009-06-14 23:03:33 +00004562 } else {
Linus Walleija8b88892011-03-03 19:58:26 +01004563 curfile->next = file;
4564 curfile = file;
Linus Walleijd4637502009-06-14 23:03:33 +00004565 }
4566 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004567
Linus Walleija8b88892011-03-03 19:58:26 +01004568 free(currentHandles.Handler);
4569
Linus Walleij366dd1c2011-03-04 20:27:03 +01004570 // Return a pointer to the original first file
4571 // in the big list.
Linus Walleija8b88892011-03-03 19:58:26 +01004572 return retfiles;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004573}
4574
Jerry Zhang60d02262017-06-08 18:19:59 -07004575void LIBMTP_Set_Load_Cache_On_Demand(int flag)
4576{
4577 load_cache_on_demand = flag;
4578}
Linus Walleija8b88892011-03-03 19:58:26 +01004579
4580/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00004581 * This creates a new track metadata structure and allocates memory
4582 * for it. Notice that if you add strings to this structure they
4583 * will be freed by the corresponding <code>LIBMTP_destroy_track_t</code>
mopoke96143402006-10-30 04:37:26 +00004584 * operation later, so be careful of using strdup() when assigning
Linus Walleij394bbbe2006-02-22 16:10:53 +00004585 * strings, e.g.:
4586 *
Linus Walleij17e39f72006-02-23 15:54:28 +00004587 * <pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00004588 * LIBMTP_track_t *track = LIBMTP_new_track_t();
4589 * track->title = strdup(titlestr);
4590 * ....
4591 * LIBMTP_destroy_track_t(track);
Linus Walleij17e39f72006-02-23 15:54:28 +00004592 * </pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00004593 *
4594 * @return a pointer to the newly allocated metadata structure.
4595 * @see LIBMTP_destroy_track_t()
4596 */
4597LIBMTP_track_t *LIBMTP_new_track_t(void)
Linus Walleijb9256fd2006-02-15 09:40:43 +00004598{
4599 LIBMTP_track_t *new = (LIBMTP_track_t *) malloc(sizeof(LIBMTP_track_t));
4600 if (new == NULL) {
4601 return NULL;
4602 }
Linus Walleijea68f1f2008-06-22 21:54:44 +00004603 new->item_id = 0;
4604 new->parent_id = 0;
4605 new->storage_id = 0;
Linus Walleijb9256fd2006-02-15 09:40:43 +00004606 new->title = NULL;
4607 new->artist = NULL;
Linus Walleij31b74292008-05-02 23:29:06 +00004608 new->composer = NULL;
Linus Walleijb9256fd2006-02-15 09:40:43 +00004609 new->album = NULL;
4610 new->genre = NULL;
4611 new->date = NULL;
4612 new->filename = NULL;
4613 new->duration = 0;
4614 new->tracknumber = 0;
4615 new->filesize = 0;
Linus Walleijf6bc1782006-03-24 15:12:47 +00004616 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
Linus Walleijcf223e62006-06-19 09:31:53 +00004617 new->samplerate = 0;
4618 new->nochannels = 0;
4619 new->wavecodec = 0;
4620 new->bitrate = 0;
4621 new->bitratetype = 0;
4622 new->rating = 0;
4623 new->usecount = 0;
Richard Lowd3b17022009-04-11 12:37:39 +00004624 new->modificationdate = 0;
Linus Walleijb9256fd2006-02-15 09:40:43 +00004625 new->next = NULL;
4626 return new;
4627}
4628
Linus Walleij394bbbe2006-02-22 16:10:53 +00004629/**
4630 * This destroys a track metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00004631 * used by it, including any strings. Never use a track metadata
Linus Walleij394bbbe2006-02-22 16:10:53 +00004632 * structure again after calling this function on it.
4633 * @param track the track metadata to destroy.
4634 * @see LIBMTP_new_track_t()
4635 */
Linus Walleijb9256fd2006-02-15 09:40:43 +00004636void LIBMTP_destroy_track_t(LIBMTP_track_t *track)
4637{
4638 if (track == NULL) {
4639 return;
4640 }
4641 if (track->title != NULL)
4642 free(track->title);
4643 if (track->artist != NULL)
4644 free(track->artist);
Linus Walleij31b74292008-05-02 23:29:06 +00004645 if (track->composer != NULL)
4646 free(track->composer);
Linus Walleijb9256fd2006-02-15 09:40:43 +00004647 if (track->album != NULL)
4648 free(track->album);
4649 if (track->genre != NULL)
4650 free(track->genre);
4651 if (track->date != NULL)
4652 free(track->date);
4653 if (track->filename != NULL)
4654 free(track->filename);
4655 free(track);
4656 return;
4657}
4658
4659/**
Linus Walleij338ade42007-07-03 20:44:08 +00004660 * This function maps and copies a property onto the track metadata if applicable.
4661 */
Linus Walleijddaba2f2007-10-02 21:20:30 +00004662static void pick_property_to_track_metadata(LIBMTP_mtpdevice_t *device, MTPProperties *prop, LIBMTP_track_t *track)
Linus Walleij338ade42007-07-03 20:44:08 +00004663{
4664 switch (prop->property) {
4665 case PTP_OPC_Name:
4666 if (prop->propval.str != NULL)
4667 track->title = strdup(prop->propval.str);
4668 else
4669 track->title = NULL;
4670 break;
4671 case PTP_OPC_Artist:
4672 if (prop->propval.str != NULL)
4673 track->artist = strdup(prop->propval.str);
4674 else
4675 track->artist = NULL;
4676 break;
Linus Walleij31b74292008-05-02 23:29:06 +00004677 case PTP_OPC_Composer:
4678 if (prop->propval.str != NULL)
4679 track->composer = strdup(prop->propval.str);
4680 else
4681 track->composer = NULL;
4682 break;
Linus Walleij338ade42007-07-03 20:44:08 +00004683 case PTP_OPC_Duration:
4684 track->duration = prop->propval.u32;
4685 break;
4686 case PTP_OPC_Track:
4687 track->tracknumber = prop->propval.u16;
4688 break;
4689 case PTP_OPC_Genre:
4690 if (prop->propval.str != NULL)
4691 track->genre = strdup(prop->propval.str);
4692 else
4693 track->genre = NULL;
4694 break;
4695 case PTP_OPC_AlbumName:
4696 if (prop->propval.str != NULL)
4697 track->album = strdup(prop->propval.str);
4698 else
4699 track->album = NULL;
4700 break;
4701 case PTP_OPC_OriginalReleaseDate:
4702 if (prop->propval.str != NULL)
4703 track->date = strdup(prop->propval.str);
4704 else
4705 track->date = NULL;
4706 break;
4707 // These are, well not so important.
4708 case PTP_OPC_SampleRate:
4709 track->samplerate = prop->propval.u32;
4710 break;
4711 case PTP_OPC_NumberOfChannels:
4712 track->nochannels = prop->propval.u16;
4713 break;
4714 case PTP_OPC_AudioWAVECodec:
4715 track->wavecodec = prop->propval.u32;
4716 break;
4717 case PTP_OPC_AudioBitRate:
4718 track->bitrate = prop->propval.u32;
4719 break;
4720 case PTP_OPC_BitRateType:
4721 track->bitratetype = prop->propval.u16;
4722 break;
4723 case PTP_OPC_Rating:
4724 track->rating = prop->propval.u16;
4725 break;
4726 case PTP_OPC_UseCount:
4727 track->usecount = prop->propval.u32;
4728 break;
Linus Walleijd9d28d52007-08-04 19:01:18 +00004729 case PTP_OPC_ObjectSize:
Linus Walleijddaba2f2007-10-02 21:20:30 +00004730 if (device->object_bitsize == 64) {
4731 track->filesize = prop->propval.u64;
4732 } else {
4733 track->filesize = prop->propval.u32;
Linus Walleijca2a1702007-10-02 20:41:45 +00004734 }
Linus Walleijd9d28d52007-08-04 19:01:18 +00004735 break;
4736 default:
4737 break;
Linus Walleij338ade42007-07-03 20:44:08 +00004738 }
4739}
4740
4741/**
Linus Walleij8ab54262006-06-21 07:12:28 +00004742 * This function retrieves the track metadata for a track
4743 * given by a unique ID.
4744 * @param device a pointer to the device to get the track metadata off.
4745 * @param trackid the unique ID of the track.
4746 * @param objectformat the object format of this track, so we know what it supports.
4747 * @param track a metadata set to fill in.
4748 */
4749static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
4750 LIBMTP_track_t *track)
4751{
Linus Walleij00cf0642006-07-26 20:40:59 +00004752 uint16_t ret;
4753 PTPParams *params = (PTPParams *) device->params;
4754 uint32_t i;
Linus Walleijd4637502009-06-14 23:03:33 +00004755 MTPProperties *prop;
4756 PTPObject *ob;
Linus Walleij00cf0642006-07-26 20:40:59 +00004757
Linus Walleij338ade42007-07-03 20:44:08 +00004758 /*
4759 * If we have a cached, large set of metadata, then use it!
4760 */
Linus Walleij08a5fe12009-09-08 21:28:58 +00004761 ret = ptp_object_want(params, track->item_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
Linus Walleijd4637502009-06-14 23:03:33 +00004762 if (ob->mtpprops) {
4763 prop = ob->mtpprops;
4764 for (i=0;i<ob->nrofmtpprops;i++,prop++)
Linus Walleijddaba2f2007-10-02 21:20:30 +00004765 pick_property_to_track_metadata(device, prop, track);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004766 } else {
Linus Walleij3fcfea52006-11-13 07:07:36 +00004767 uint16_t *props = NULL;
4768 uint32_t propcnt = 0;
4769
4770 // First see which properties can be retrieved for this object format
Linus Walleij070e9b42007-01-22 08:49:28 +00004771 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(track->filetype), &propcnt, &props);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004772 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004773 add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
Linus Walleij3fcfea52006-11-13 07:07:36 +00004774 // Just bail out for now, nothing is ever set.
4775 return;
4776 } else {
4777 for (i=0;i<propcnt;i++) {
4778 switch (props[i]) {
4779 case PTP_OPC_Name:
Linus Walleij9901e222006-11-30 12:28:19 +00004780 track->title = get_string_from_object(device, track->item_id, PTP_OPC_Name);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004781 break;
4782 case PTP_OPC_Artist:
Linus Walleij9901e222006-11-30 12:28:19 +00004783 track->artist = get_string_from_object(device, track->item_id, PTP_OPC_Artist);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004784 break;
Linus Walleij31b74292008-05-02 23:29:06 +00004785 case PTP_OPC_Composer:
4786 track->composer = get_string_from_object(device, track->item_id, PTP_OPC_Composer);
4787 break;
Linus Walleij3fcfea52006-11-13 07:07:36 +00004788 case PTP_OPC_Duration:
Linus Walleij9901e222006-11-30 12:28:19 +00004789 track->duration = get_u32_from_object(device, track->item_id, PTP_OPC_Duration, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004790 break;
4791 case PTP_OPC_Track:
Linus Walleij9901e222006-11-30 12:28:19 +00004792 track->tracknumber = get_u16_from_object(device, track->item_id, PTP_OPC_Track, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004793 break;
4794 case PTP_OPC_Genre:
Linus Walleij9901e222006-11-30 12:28:19 +00004795 track->genre = get_string_from_object(device, track->item_id, PTP_OPC_Genre);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004796 break;
4797 case PTP_OPC_AlbumName:
Linus Walleij9901e222006-11-30 12:28:19 +00004798 track->album = get_string_from_object(device, track->item_id, PTP_OPC_AlbumName);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004799 break;
4800 case PTP_OPC_OriginalReleaseDate:
Linus Walleij9901e222006-11-30 12:28:19 +00004801 track->date = get_string_from_object(device, track->item_id, PTP_OPC_OriginalReleaseDate);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004802 break;
4803 // These are, well not so important.
4804 case PTP_OPC_SampleRate:
Linus Walleij9901e222006-11-30 12:28:19 +00004805 track->samplerate = get_u32_from_object(device, track->item_id, PTP_OPC_SampleRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004806 break;
4807 case PTP_OPC_NumberOfChannels:
Linus Walleij9901e222006-11-30 12:28:19 +00004808 track->nochannels = get_u16_from_object(device, track->item_id, PTP_OPC_NumberOfChannels, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004809 break;
4810 case PTP_OPC_AudioWAVECodec:
Linus Walleij9901e222006-11-30 12:28:19 +00004811 track->wavecodec = get_u32_from_object(device, track->item_id, PTP_OPC_AudioWAVECodec, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004812 break;
4813 case PTP_OPC_AudioBitRate:
Linus Walleij9901e222006-11-30 12:28:19 +00004814 track->bitrate = get_u32_from_object(device, track->item_id, PTP_OPC_AudioBitRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004815 break;
4816 case PTP_OPC_BitRateType:
Linus Walleij9901e222006-11-30 12:28:19 +00004817 track->bitratetype = get_u16_from_object(device, track->item_id, PTP_OPC_BitRateType, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004818 break;
4819 case PTP_OPC_Rating:
Linus Walleij9901e222006-11-30 12:28:19 +00004820 track->rating = get_u16_from_object(device, track->item_id, PTP_OPC_Rating, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004821 break;
4822 case PTP_OPC_UseCount:
Linus Walleij9901e222006-11-30 12:28:19 +00004823 track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004824 break;
Linus Walleijd9d28d52007-08-04 19:01:18 +00004825 case PTP_OPC_ObjectSize:
Linus Walleijddaba2f2007-10-02 21:20:30 +00004826 if (device->object_bitsize == 64) {
4827 track->filesize = get_u64_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
4828 } else {
4829 track->filesize = (uint64_t) get_u32_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
Linus Walleijca2a1702007-10-02 20:41:45 +00004830 }
Linus Walleijd9d28d52007-08-04 19:01:18 +00004831 break;
Linus Walleij3fcfea52006-11-13 07:07:36 +00004832 }
4833 }
4834 free(props);
4835 }
Linus Walleij00cf0642006-07-26 20:40:59 +00004836 }
Linus Walleij8ab54262006-06-21 07:12:28 +00004837}
4838
4839/**
mopoke31364442006-11-20 04:53:04 +00004840 * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Linus Walleij3fcfea52006-11-13 07:07:36 +00004841 * NOT TO USE IT.
4842 * @see LIBMTP_Get_Tracklisting_With_Callback()
4843 */
4844LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t *device)
4845{
nicklas79daadbf22009-09-28 18:19:34 +00004846 LIBMTP_INFO("WARNING: LIBMTP_Get_Tracklisting() is deprecated.\n");
4847 LIBMTP_INFO("WARNING: please update your code to use LIBMTP_Get_Tracklisting_With_Callback()\n");
Richard Lowdc0b6c72006-11-13 09:22:23 +00004848 return LIBMTP_Get_Tracklisting_With_Callback(device, NULL, NULL);
Linus Walleij3fcfea52006-11-13 07:07:36 +00004849}
4850
4851/**
Linus Walleij25d5c212008-08-14 06:49:13 +00004852 * This returns a long list of all tracks available on the current MTP device.
4853 * Tracks include multimedia objects, both music tracks and video tracks.
4854 * Typical usage:
Linus Walleija4982732006-02-24 15:46:02 +00004855 *
4856 * <pre>
4857 * LIBMTP_track_t *tracklist;
4858 *
Richard Low6711f442007-05-05 19:00:59 +00004859 * tracklist = LIBMTP_Get_Tracklisting_With_Callback(device, callback, data);
Linus Walleija4982732006-02-24 15:46:02 +00004860 * while (tracklist != NULL) {
4861 * LIBMTP_track_t *tmp;
4862 *
4863 * // Do something on each element in the list here...
4864 * tmp = tracklist;
4865 * tracklist = tracklist->next;
4866 * LIBMTP_destroy_track_t(tmp);
4867 * }
4868 * </pre>
4869 *
Linus Walleij25d5c212008-08-14 06:49:13 +00004870 * If you want to group your track listing by storage (per storage unit) or
4871 * arrange tracks into folders, you must dereference the <code>storage_id</code>
4872 * and/or <code>parent_id</code> field of the returned <code>LIBMTP_track_t</code>
4873 * struct. To arrange by folders or files you typically have to create the proper
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004874 * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
Linus Walleij25d5c212008-08-14 06:49:13 +00004875 * <code>LIBMTP_Get_Folder_List()</code> first.
4876 *
Linus Walleijb9256fd2006-02-15 09:40:43 +00004877 * @param device a pointer to the device to get the track listing for.
Linus Walleij3fcfea52006-11-13 07:07:36 +00004878 * @param callback a function to be called during the tracklisting retrieveal
Linus Walleij25d5c212008-08-14 06:49:13 +00004879 * for displaying progress bars etc, or NULL if you don't want
4880 * any callbacks.
Richard Lowdc0b6c72006-11-13 09:22:23 +00004881 * @param data a user-defined pointer that is passed along to
Linus Walleij25d5c212008-08-14 06:49:13 +00004882 * the <code>progress</code> function in order to
4883 * pass along some user defined data to the progress
4884 * updates. If not used, set this to NULL.
Linus Walleija4982732006-02-24 15:46:02 +00004885 * @return a list of tracks that can be followed using the <code>next</code>
Linus Walleij25d5c212008-08-14 06:49:13 +00004886 * field of the <code>LIBMTP_track_t</code> data structure.
4887 * Each of the metadata tags must be freed after use, and may
4888 * contain only partial metadata information, i.e. one or several
4889 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004890 * @see LIBMTP_Get_Trackmetadata()
Linus Walleijb9256fd2006-02-15 09:40:43 +00004891 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00004892LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t *device,
4893 LIBMTP_progressfunc_t const callback,
4894 void const * const data)
Linus Walleijb9256fd2006-02-15 09:40:43 +00004895{
nicklas7918b54d72009-11-07 19:54:07 +00004896 return LIBMTP_Get_Tracklisting_With_Callback_For_Storage(device, 0, callback, data);
4897}
4898
4899
4900/**
4901 * This returns a long list of all tracks available on the current MTP device.
4902 * Tracks include multimedia objects, both music tracks and video tracks.
4903 * Typical usage:
4904 *
4905 * <pre>
4906 * LIBMTP_track_t *tracklist;
4907 *
4908 * tracklist = LIBMTP_Get_Tracklisting_With_Callback_For_Storage(device, storage_id, callback, data);
4909 * while (tracklist != NULL) {
4910 * LIBMTP_track_t *tmp;
4911 *
4912 * // Do something on each element in the list here...
4913 * tmp = tracklist;
4914 * tracklist = tracklist->next;
4915 * LIBMTP_destroy_track_t(tmp);
4916 * }
4917 * </pre>
4918 *
4919 * If you want to group your track listing by storage (per storage unit) or
4920 * arrange tracks into folders, you must dereference the <code>storage_id</code>
4921 * and/or <code>parent_id</code> field of the returned <code>LIBMTP_track_t</code>
4922 * struct. To arrange by folders or files you typically have to create the proper
4923 * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or
4924 * <code>LIBMTP_Get_Folder_List()</code> first.
4925 *
4926 * @param device a pointer to the device to get the track listing for.
4927 * @param storage_id ID of device storage (if null, no filter)
4928 * @param callback a function to be called during the tracklisting retrieveal
4929 * for displaying progress bars etc, or NULL if you don't want
4930 * any callbacks.
4931 * @param data a user-defined pointer that is passed along to
4932 * the <code>progress</code> function in order to
4933 * pass along some user defined data to the progress
4934 * updates. If not used, set this to NULL.
4935 * @return a list of tracks that can be followed using the <code>next</code>
4936 * field of the <code>LIBMTP_track_t</code> data structure.
4937 * Each of the metadata tags must be freed after use, and may
4938 * contain only partial metadata information, i.e. one or several
4939 * fields may be NULL or 0.
4940 * @see LIBMTP_Get_Trackmetadata()
4941 */
4942LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback_For_Storage(LIBMTP_mtpdevice_t *device, uint32_t const storage_id,
4943 LIBMTP_progressfunc_t const callback,
4944 void const * const data)
4945{
Linus Walleijb9256fd2006-02-15 09:40:43 +00004946 uint32_t i = 0;
4947 LIBMTP_track_t *retracks = NULL;
4948 LIBMTP_track_t *curtrack = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00004949 PTPParams *params = (PTPParams *) device->params;
Linus Walleij2be40c72007-03-16 15:19:44 +00004950 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004951
mopoke96143402006-10-30 04:37:26 +00004952 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00004953 if (params->nrofobjects == 0) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00004954 flush_handles(device);
Linus Walleijb9256fd2006-02-15 09:40:43 +00004955 }
mopoke96143402006-10-30 04:37:26 +00004956
Linus Walleijd4637502009-06-14 23:03:33 +00004957 for (i = 0; i < params->nrofobjects; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004958 LIBMTP_track_t *track;
Linus Walleijd4637502009-06-14 23:03:33 +00004959 PTPObject *ob;
Linus Walleij46651f32008-04-26 23:30:19 +00004960 LIBMTP_filetype_t mtptype;
mopoke31364442006-11-20 04:53:04 +00004961
Richard Lowdc0b6c72006-11-13 09:22:23 +00004962 if (callback != NULL)
Linus Walleijd4637502009-06-14 23:03:33 +00004963 callback(i, params->nrofobjects, data);
mopoke31364442006-11-20 04:53:04 +00004964
Linus Walleijd4637502009-06-14 23:03:33 +00004965 ob = &params->objects[i];
4966 mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
mopoke96143402006-10-30 04:37:26 +00004967
Linus Walleijf0bf4372007-07-01 21:47:38 +00004968 // Ignore stuff we don't know how to handle...
4969 // TODO: get this list as an intersection of the sets
4970 // supported by the device and the from the device and
Linus Walleijd5356e22008-11-01 22:08:59 +00004971 // all known track files?
4972 if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
Linus Walleij46651f32008-04-26 23:30:19 +00004973 // This row lets through undefined files for examination since they may be forgotten OGG files.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004974 (ob->oi.ObjectFormat != PTP_OFC_Undefined ||
Richard Lowef197312008-11-01 18:29:41 +00004975 (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
Linus Walleij89bb1cd2009-07-24 21:03:36 +00004976 !FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
4977 !FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
Linus Walleij46651f32008-04-26 23:30:19 +00004978 ) {
Linus Walleijc8abc922008-09-28 18:38:42 +00004979 //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
Linus Walleijf0bf4372007-07-01 21:47:38 +00004980 continue;
Linus Walleijb9256fd2006-02-15 09:40:43 +00004981 }
4982
nicklas7918b54d72009-11-07 19:54:07 +00004983 // Ignore stuff that isn't into the storage device
4984 if ((storage_id != 0) && (ob->oi.StorageID != storage_id ))
4985 continue;
4986
Linus Walleijf0bf4372007-07-01 21:47:38 +00004987 // Allocate a new track type
4988 track = LIBMTP_new_track_t();
Linus Walleij2c1bbd62009-11-07 15:15:03 +00004989
Linus Walleijf0bf4372007-07-01 21:47:38 +00004990 // This is some sort of unique ID so we can keep track of the track.
Linus Walleijd4637502009-06-14 23:03:33 +00004991 track->item_id = ob->oid;
4992 track->parent_id = ob->oi.ParentObject;
4993 track->storage_id = ob->oi.StorageID;
4994 track->modificationdate = ob->oi.ModificationDate;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004995
Linus Walleij46651f32008-04-26 23:30:19 +00004996 track->filetype = mtptype;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004997
4998 // Original file-specific properties
Linus Walleijd4637502009-06-14 23:03:33 +00004999 track->filesize = ob->oi.ObjectCompressedSize;
5000 if (ob->oi.Filename != NULL) {
5001 track->filename = strdup(ob->oi.Filename);
Linus Walleijf0bf4372007-07-01 21:47:38 +00005002 }
5003
Linus Walleijd4637502009-06-14 23:03:33 +00005004 get_track_metadata(device, ob->oi.ObjectFormat, track);
Linus Walleijf0bf4372007-07-01 21:47:38 +00005005
5006 /*
5007 * A special quirk for iriver devices that doesn't quite
5008 * remember that some files marked as "unknown" type are
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005009 * actually OGG or FLAC files. We look at the filename extension
5010 * and see if it happens that this was atleast named "ogg" or "flac"
5011 * and fall back on this heuristic approach in that case,
Linus Walleijf0bf4372007-07-01 21:47:38 +00005012 * for these bugged devices only.
5013 */
5014 if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005015 track->filename != NULL) {
5016 if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
5017 FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
5018 has_ogg_extension(track->filename))
Linus Walleijf0bf4372007-07-01 21:47:38 +00005019 track->filetype = LIBMTP_FILETYPE_OGG;
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005020 else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
5021 has_flac_extension(track->filename))
5022 track->filetype = LIBMTP_FILETYPE_FLAC;
5023 else {
5024 // This was not an OGG/FLAC file so discard it and continue
Linus Walleijf0bf4372007-07-01 21:47:38 +00005025 LIBMTP_destroy_track_t(track);
5026 continue;
5027 }
5028 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005029
Linus Walleijf0bf4372007-07-01 21:47:38 +00005030 // Add track to a list that will be returned afterwards.
5031 if (retracks == NULL) {
5032 retracks = track;
5033 curtrack = track;
5034 } else {
5035 curtrack->next = track;
5036 curtrack = track;
5037 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005038
Linus Walleijf0bf4372007-07-01 21:47:38 +00005039 // Call listing callback
5040 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005041
Linus Walleijb9256fd2006-02-15 09:40:43 +00005042 } // Handle counting loop
5043 return retracks;
5044}
Linus Walleijdcde6082006-02-17 16:16:34 +00005045
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005046/**
5047 * This function retrieves the metadata for a single track off
5048 * the device.
5049 *
5050 * Do not call this function repeatedly! The track handles are linearly
5051 * searched O(n) and the call may involve (slow) USB traffic, so use
5052 * <code>LIBMTP_Get_Tracklisting()</code> and cache the tracks, preferably
5053 * as an efficient data structure such as a hash list.
5054 *
5055 * @param device a pointer to the device to get the track metadata from.
5056 * @param trackid the object ID of the track that you want the metadata for.
5057 * @return a track metadata entry on success or NULL on failure.
5058 * @see LIBMTP_Get_Tracklisting()
5059 */
5060LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t *device, uint32_t const trackid)
5061{
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005062 PTPParams *params = (PTPParams *) device->params;
Linus Walleijc8abc922008-09-28 18:38:42 +00005063 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijd4637502009-06-14 23:03:33 +00005064 PTPObject *ob;
5065 LIBMTP_track_t *track;
5066 LIBMTP_filetype_t mtptype;
5067 uint16_t ret;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005068
mopoke96143402006-10-30 04:37:26 +00005069 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00005070 if (params->nrofobjects == 0)
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005071 flush_handles(device);
Linus Walleijd4637502009-06-14 23:03:33 +00005072
5073 ret = ptp_object_want (params, trackid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5074 if (ret != PTP_RC_OK)
5075 return NULL;
5076
5077 mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
5078
5079 // Ignore stuff we don't know how to handle...
5080 if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005081 /*
5082 * This row lets through undefined files for examination
5083 * since they may be forgotten OGG or FLAC files.
5084 */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005085 (ob->oi.ObjectFormat != PTP_OFC_Undefined ||
Linus Walleijd4637502009-06-14 23:03:33 +00005086 (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005087 !FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
5088 !FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
Linus Walleijd4637502009-06-14 23:03:33 +00005089 ) {
5090 //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
5091 return NULL;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005092 }
5093
Linus Walleijd4637502009-06-14 23:03:33 +00005094 // Allocate a new track type
5095 track = LIBMTP_new_track_t();
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005096
Linus Walleijd4637502009-06-14 23:03:33 +00005097 // This is some sort of unique ID so we can keep track of the track.
5098 track->item_id = ob->oid;
5099 track->parent_id = ob->oi.ParentObject;
5100 track->storage_id = ob->oi.StorageID;
5101 track->modificationdate = ob->oi.ModificationDate;
mopoke96143402006-10-30 04:37:26 +00005102
Linus Walleijd4637502009-06-14 23:03:33 +00005103 track->filetype = mtptype;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005104
Linus Walleijd4637502009-06-14 23:03:33 +00005105 // Original file-specific properties
5106 track->filesize = ob->oi.ObjectCompressedSize;
5107 if (ob->oi.Filename != NULL) {
5108 track->filename = strdup(ob->oi.Filename);
5109 }
mopoke96143402006-10-30 04:37:26 +00005110
Linus Walleijd4637502009-06-14 23:03:33 +00005111 /*
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005112 * A special quirk for devices that doesn't quite
Linus Walleijd4637502009-06-14 23:03:33 +00005113 * remember that some files marked as "unknown" type are
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005114 * actually OGG or FLAC files. We look at the filename extension
Linus Walleijd4637502009-06-14 23:03:33 +00005115 * and see if it happens that this was atleast named "ogg"
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005116 * and fall back on this heuristic approach in that case,
Linus Walleijd4637502009-06-14 23:03:33 +00005117 * for these bugged devices only.
5118 */
5119 if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005120 track->filename != NULL) {
5121 if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
5122 FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
5123 has_ogg_extension(track->filename))
Linus Walleijd4637502009-06-14 23:03:33 +00005124 track->filetype = LIBMTP_FILETYPE_OGG;
Linus Walleij89bb1cd2009-07-24 21:03:36 +00005125 else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
5126 has_flac_extension(track->filename))
5127 track->filetype = LIBMTP_FILETYPE_FLAC;
5128 else {
5129 // This was not an OGG/FLAC file so discard it
Linus Walleijd4637502009-06-14 23:03:33 +00005130 LIBMTP_destroy_track_t(track);
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005131 return NULL;
5132 }
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005133 }
Linus Walleijd4637502009-06-14 23:03:33 +00005134 get_track_metadata(device, ob->oi.ObjectFormat, track);
5135 return track;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00005136}
5137
Richard Low5b4023c2009-04-16 19:14:38 +00005138/**
5139 * This is a manual conversion from MTPDataGetFunc to PTPDataGetFunc
5140 * to isolate the internal type.
5141 */
Linus Walleijd866d242009-08-23 21:50:39 +00005142static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen)
Richard Low5b4023c2009-04-16 19:14:38 +00005143{
Linus Walleijd866d242009-08-23 21:50:39 +00005144 MTPDataHandler *handler = (MTPDataHandler *)priv;
Richard Low5b4023c2009-04-16 19:14:38 +00005145 uint16_t ret;
5146 uint32_t local_gotlen = 0;
Linus Walleijd866d242009-08-23 21:50:39 +00005147 ret = handler->getfunc(params, handler->priv, wantlen, data, &local_gotlen);
Richard Low5b4023c2009-04-16 19:14:38 +00005148 *gotlen = local_gotlen;
Richard Low6f070842009-05-03 10:26:16 +00005149 switch (ret)
5150 {
5151 case LIBMTP_HANDLER_RETURN_OK:
5152 return PTP_RC_OK;
5153 case LIBMTP_HANDLER_RETURN_ERROR:
5154 return PTP_ERROR_IO;
5155 case LIBMTP_HANDLER_RETURN_CANCEL:
5156 return PTP_ERROR_CANCEL;
5157 default:
5158 return PTP_ERROR_IO;
5159 }
Richard Low5b4023c2009-04-16 19:14:38 +00005160}
5161
5162/**
5163 * This is a manual conversion from MTPDataPutFunc to PTPDataPutFunc
5164 * to isolate the internal type.
5165 */
Marcus Meissner3d692dc2016-02-21 12:34:06 +01005166static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data)
Richard Low5b4023c2009-04-16 19:14:38 +00005167{
Linus Walleijd866d242009-08-23 21:50:39 +00005168 MTPDataHandler *handler = (MTPDataHandler *)priv;
Richard Low5b4023c2009-04-16 19:14:38 +00005169 uint16_t ret;
5170 uint32_t local_putlen = 0;
Marcus Meissner3d692dc2016-02-21 12:34:06 +01005171
Linus Walleijd866d242009-08-23 21:50:39 +00005172 ret = handler->putfunc(params, handler->priv, sendlen, data, &local_putlen);
Marcus Meissner3d692dc2016-02-21 12:34:06 +01005173
Richard Low75981ac2009-05-03 12:54:06 +00005174 switch (ret)
5175 {
5176 case LIBMTP_HANDLER_RETURN_OK:
Marcus Meissner3d692dc2016-02-21 12:34:06 +01005177 if (local_putlen != sendlen)
5178 return PTP_ERROR_IO;
Richard Low75981ac2009-05-03 12:54:06 +00005179 return PTP_RC_OK;
5180 case LIBMTP_HANDLER_RETURN_ERROR:
5181 return PTP_ERROR_IO;
5182 case LIBMTP_HANDLER_RETURN_CANCEL:
5183 return PTP_ERROR_CANCEL;
5184 default:
5185 return PTP_ERROR_IO;
5186 }
Richard Low5b4023c2009-04-16 19:14:38 +00005187}
Linus Walleijf6bc1782006-03-24 15:12:47 +00005188
5189/**
5190 * This gets a file off the device to a local file identified
5191 * by a filename.
5192 * @param device a pointer to the device to get the track from.
5193 * @param id the file ID of the file to retrieve.
5194 * @param path a filename to use for the retrieved file.
5195 * @param callback a progress indicator function or NULL to ignore.
5196 * @param data a user-defined pointer that is passed along to
5197 * the <code>progress</code> function in order to
5198 * pass along some user defined data to the progress
5199 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005200 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00005201 * failure.
5202 * @see LIBMTP_Get_File_To_File_Descriptor()
5203 */
mopoke96143402006-10-30 04:37:26 +00005204int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00005205 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00005206 void const * const data)
5207{
5208 int fd = -1;
5209 int ret;
5210
5211 // Sanity check
5212 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005213 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Bad arguments, path was NULL.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00005214 return -1;
5215 }
5216
5217 // Open file
5218#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00005219#ifdef USE_WINDOWS_IO_H
5220 if ( (fd = _open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,_S_IREAD)) == -1 ) {
5221#else
Linus Walleij5b4a4d22009-01-10 12:18:14 +00005222 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IRWXU)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00005223#endif
Linus Walleijf6bc1782006-03-24 15:12:47 +00005224#else
5225 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP)) == -1) {
5226#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00005227 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Could not create file.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00005228 return -1;
5229 }
mopoke96143402006-10-30 04:37:26 +00005230
Linus Walleijf6bc1782006-03-24 15:12:47 +00005231 ret = LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
5232
5233 // Close file
5234 close(fd);
mopoke96143402006-10-30 04:37:26 +00005235
Linus Walleij25d33b52007-09-16 21:36:19 +00005236 // Delete partial file.
5237 if (ret == -1) {
5238 unlink(path);
5239 }
5240
Linus Walleijf6bc1782006-03-24 15:12:47 +00005241 return ret;
5242}
5243
5244/**
5245 * This gets a file off the device to a file identified
5246 * by a file descriptor.
Linus Walleij0558ac52006-09-07 06:55:03 +00005247 *
mopoke96143402006-10-30 04:37:26 +00005248 * This function can potentially be used for streaming
5249 * files off the device for playback or broadcast for example,
Linus Walleij0558ac52006-09-07 06:55:03 +00005250 * by downloading the file into a stream sink e.g. a socket.
5251 *
Linus Walleijf6bc1782006-03-24 15:12:47 +00005252 * @param device a pointer to the device to get the file from.
5253 * @param id the file ID of the file to retrieve.
5254 * @param fd a local file descriptor to write the file to.
5255 * @param callback a progress indicator function or NULL to ignore.
5256 * @param data a user-defined pointer that is passed along to
5257 * the <code>progress</code> function in order to
5258 * pass along some user defined data to the progress
5259 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005260 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00005261 * failure.
5262 * @see LIBMTP_Get_File_To_File()
5263 */
mopoke96143402006-10-30 04:37:26 +00005264int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
5265 uint32_t const id,
5266 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00005267 LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00005268 void const * const data)
5269{
Linus Walleij438bd7f2006-06-08 11:35:44 +00005270 uint16_t ret;
Linus Walleijf6bc1782006-03-24 15:12:47 +00005271 PTPParams *params = (PTPParams *) device->params;
Linus Walleijee73ef22006-08-27 19:56:00 +00005272 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijd4637502009-06-14 23:03:33 +00005273 PTPObject *ob;
Linus Walleijf6bc1782006-03-24 15:12:47 +00005274
Linus Walleijd4637502009-06-14 23:03:33 +00005275 ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5276 if (ret != PTP_RC_OK) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00005277 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00005278 return -1;
5279 }
Linus Walleijd4637502009-06-14 23:03:33 +00005280 if (ob->oi.ObjectFormat == PTP_OFC_Association) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005281 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00005282 return -1;
5283 }
Linus Walleijf6bc1782006-03-24 15:12:47 +00005284
Linus Walleijee73ef22006-08-27 19:56:00 +00005285 // Callbacks
5286 ptp_usb->callback_active = 1;
Linus Walleijd4637502009-06-14 23:03:33 +00005287 ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
Linus Walleij7f0c72e2006-09-06 13:01:58 +00005288 PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
Linus Walleijee73ef22006-08-27 19:56:00 +00005289 ptp_usb->current_transfer_complete = 0;
5290 ptp_usb->current_transfer_callback = callback;
5291 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00005292
Linus Walleij96c62432006-08-21 10:04:02 +00005293 ret = ptp_getobject_tofd(params, id, fd);
Linus Walleijee73ef22006-08-27 19:56:00 +00005294
5295 ptp_usb->callback_active = 0;
5296 ptp_usb->current_transfer_callback = NULL;
5297 ptp_usb->current_transfer_callback_data = NULL;
mopoke96143402006-10-30 04:37:26 +00005298
Linus Walleijbd3bf9e2007-09-14 19:31:54 +00005299 if (ret == PTP_ERROR_CANCEL) {
Linus Walleijfb28b632007-10-23 21:56:18 +00005300 add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
Linus Walleijbd3bf9e2007-09-14 19:31:54 +00005301 return -1;
5302 }
Linus Walleijb02a0662006-04-25 08:05:09 +00005303 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005304 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00005305 return -1;
5306 }
mopoke96143402006-10-30 04:37:26 +00005307
Linus Walleijf6bc1782006-03-24 15:12:47 +00005308 return 0;
5309}
5310
Linus Walleijdcde6082006-02-17 16:16:34 +00005311/**
Richard Lowd3b17022009-04-11 12:37:39 +00005312 * This gets a file off the device and calls put_func
5313 * with chunks of data
5314 *
5315 * @param device a pointer to the device to get the file from.
5316 * @param id the file ID of the file to retrieve.
5317 * @param put_func the function to call when we have data.
5318 * @param priv the user-defined pointer that is passed to
5319 * <code>put_func</code>.
5320 * @param callback a progress indicator function or NULL to ignore.
5321 * @param data a user-defined pointer that is passed along to
5322 * the <code>progress</code> function in order to
5323 * pass along some user defined data to the progress
5324 * updates. If not used, set this to NULL.
5325 * @return 0 if the transfer was successful, any other value means
5326 * failure.
5327 */
5328int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *device,
5329 uint32_t const id,
5330 MTPDataPutFunc put_func,
5331 void * priv,
5332 LIBMTP_progressfunc_t const callback,
5333 void const * const data)
5334{
Linus Walleijd4637502009-06-14 23:03:33 +00005335 PTPObject *ob;
Richard Lowd3b17022009-04-11 12:37:39 +00005336 uint16_t ret;
5337 PTPParams *params = (PTPParams *) device->params;
5338 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5339
Linus Walleijd4637502009-06-14 23:03:33 +00005340 ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
5341 if (ret != PTP_RC_OK) {
Richard Lowd3b17022009-04-11 12:37:39 +00005342 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
5343 return -1;
5344 }
Linus Walleijd4637502009-06-14 23:03:33 +00005345 if (ob->oi.ObjectFormat == PTP_OFC_Association) {
Richard Lowd3b17022009-04-11 12:37:39 +00005346 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
5347 return -1;
5348 }
5349
5350 // Callbacks
5351 ptp_usb->callback_active = 1;
Linus Walleijd4637502009-06-14 23:03:33 +00005352 ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
Richard Lowd3b17022009-04-11 12:37:39 +00005353 PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
5354 ptp_usb->current_transfer_complete = 0;
5355 ptp_usb->current_transfer_callback = callback;
5356 ptp_usb->current_transfer_callback_data = data;
5357
Richard Low5b4023c2009-04-16 19:14:38 +00005358 MTPDataHandler mtp_handler;
5359 mtp_handler.getfunc = NULL;
5360 mtp_handler.putfunc = put_func;
Linus Walleijd866d242009-08-23 21:50:39 +00005361 mtp_handler.priv = priv;
5362
Richard Lowd3b17022009-04-11 12:37:39 +00005363 PTPDataHandler handler;
5364 handler.getfunc = NULL;
Richard Low5b4023c2009-04-16 19:14:38 +00005365 handler.putfunc = put_func_wrapper;
Linus Walleijd866d242009-08-23 21:50:39 +00005366 handler.priv = &mtp_handler;
5367
Richard Lowd3b17022009-04-11 12:37:39 +00005368 ret = ptp_getobject_to_handler(params, id, &handler);
5369
5370 ptp_usb->callback_active = 0;
5371 ptp_usb->current_transfer_callback = NULL;
5372 ptp_usb->current_transfer_callback_data = NULL;
5373
5374 if (ret == PTP_ERROR_CANCEL) {
5375 add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
5376 return -1;
5377 }
5378 if (ret != PTP_RC_OK) {
5379 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
5380 return -1;
5381 }
5382
5383 return 0;
5384}
5385
5386
5387/**
Linus Walleijdcde6082006-02-17 16:16:34 +00005388 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00005389 * by a filename. This is actually just a wrapper for the
5390 * \c LIBMTP_Get_Track_To_File() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00005391 * @param device a pointer to the device to get the track from.
5392 * @param id the track ID of the track to retrieve.
5393 * @param path a filename to use for the retrieved track.
5394 * @param callback a progress indicator function or NULL to ignore.
5395 * @param data a user-defined pointer that is passed along to
5396 * the <code>progress</code> function in order to
5397 * pass along some user defined data to the progress
5398 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005399 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00005400 * failure.
5401 * @see LIBMTP_Get_Track_To_File_Descriptor()
5402 */
mopoke96143402006-10-30 04:37:26 +00005403int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00005404 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00005405 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00005406{
Linus Walleijf6bc1782006-03-24 15:12:47 +00005407 // This is just a wrapper
5408 return LIBMTP_Get_File_To_File(device, id, path, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00005409}
5410
5411/**
5412 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00005413 * by a file descriptor. This is actually just a wrapper for
5414 * the \c LIBMTP_Get_File_To_File_Descriptor() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00005415 * @param device a pointer to the device to get the track from.
5416 * @param id the track ID of the track to retrieve.
5417 * @param fd a file descriptor to write the track to.
5418 * @param callback a progress indicator function or NULL to ignore.
5419 * @param data a user-defined pointer that is passed along to
5420 * the <code>progress</code> function in order to
5421 * pass along some user defined data to the progress
5422 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005423 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00005424 * failure.
5425 * @see LIBMTP_Get_Track_To_File()
5426 */
mopoke96143402006-10-30 04:37:26 +00005427int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
5428 uint32_t const id,
5429 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00005430 LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00005431 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00005432{
Linus Walleijf6bc1782006-03-24 15:12:47 +00005433 // This is just a wrapper
5434 return LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00005435}
Linus Walleij394bbbe2006-02-22 16:10:53 +00005436
5437/**
Richard Lowd3b17022009-04-11 12:37:39 +00005438 * This gets a track off the device to a handler function.
5439 * This is actually just a wrapper for
5440 * the \c LIBMTP_Get_File_To_Handler() function.
5441 * @param device a pointer to the device to get the track from.
5442 * @param id the track ID of the track to retrieve.
5443 * @param put_func the function to call when we have data.
5444 * @param priv the user-defined pointer that is passed to
5445 * <code>put_func</code>.
5446 * @param callback a progress indicator function or NULL to ignore.
5447 * @param data a user-defined pointer that is passed along to
5448 * the <code>progress</code> function in order to
5449 * pass along some user defined data to the progress
5450 * updates. If not used, set this to NULL.
5451 * @return 0 if the transfer was successful, any other value means
5452 * failure.
5453 */
5454int LIBMTP_Get_Track_To_Handler(LIBMTP_mtpdevice_t *device,
5455 uint32_t const id,
5456 MTPDataPutFunc put_func,
5457 void * priv,
5458 LIBMTP_progressfunc_t const callback,
5459 void const * const data)
5460{
5461 // This is just a wrapper
5462 return LIBMTP_Get_File_To_Handler(device, id, put_func, priv, callback, data);
5463}
5464
5465/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00005466 * This function sends a track from a local file to an
5467 * MTP device. A filename and a set of metadata must be
5468 * given as input.
5469 * @param device a pointer to the device to send the track to.
5470 * @param path the filename of a local file which will be sent.
5471 * @param metadata a track metadata set to be written along with the file.
Linus Walleijea68f1f2008-06-22 21:54:44 +00005472 * After this call the field <code>metadata-&gt;item_id</code>
5473 * will contain the new track ID. Other fields such
5474 * as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005475 * or <code>metadata-&gt;storage_id</code> may also change during this
Linus Walleijea68f1f2008-06-22 21:54:44 +00005476 * operation due to device restrictions, so do not rely on the
5477 * contents of this struct to be preserved in any way.
5478 * <ul>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005479 * <li><code>metadata-&gt;parent_id</code> should be set to the parent
5480 * (e.g. folder) to store this track in. Since some
Linus Walleijea68f1f2008-06-22 21:54:44 +00005481 * devices are a bit picky about where files
5482 * are placed, a default folder will be chosen if libmtp
5483 * has detected one for the current filetype and this
5484 * parameter is set to 0. If this is 0 and no default folder
5485 * can be found, the file will be stored in the root folder.
5486 * <li><code>metadata-&gt;storage_id</code> should be set to the
5487 * desired storage (e.g. memory card or whatever your device
5488 * presents) to store this track in. Setting this to 0 will store
5489 * the track on the primary storage.
5490 * </ul>
Linus Walleij394bbbe2006-02-22 16:10:53 +00005491 * @param callback a progress indicator function or NULL to ignore.
5492 * @param data a user-defined pointer that is passed along to
5493 * the <code>progress</code> function in order to
5494 * pass along some user defined data to the progress
5495 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005496 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00005497 * failure.
5498 * @see LIBMTP_Send_Track_From_File_Descriptor()
Linus Walleij1b87ea72007-08-28 07:53:09 +00005499 * @see LIBMTP_Send_File_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00005500 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00005501 */
mopoke96143402006-10-30 04:37:26 +00005502int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00005503 char const * const path, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00005504 LIBMTP_progressfunc_t const callback,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005505 void const * const data)
Linus Walleij394bbbe2006-02-22 16:10:53 +00005506{
5507 int fd;
5508 int ret;
5509
5510 // Sanity check
5511 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005512 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File(): Bad arguments, path was NULL.");
Linus Walleij394bbbe2006-02-22 16:10:53 +00005513 return -1;
5514 }
5515
5516 // Open file
5517#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00005518#ifdef USE_WINDOWS_IO_H
Linus Walleij3017c562010-10-17 21:31:36 +00005519 if ( (fd = _open(path, O_RDONLY|O_BINARY)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00005520#else
Linus Walleij3017c562010-10-17 21:31:36 +00005521 if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00005522#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00005523#else
5524 if ( (fd = open(path, O_RDONLY)) == -1) {
5525#endif
nicklas79daadbf22009-09-28 18:19:34 +00005526 LIBMTP_ERROR("LIBMTP_Send_Track_From_File(): Could not open source file \"%s\"\n", path);
Linus Walleij394bbbe2006-02-22 16:10:53 +00005527 return -1;
5528 }
5529
Linus Walleijea68f1f2008-06-22 21:54:44 +00005530 ret = LIBMTP_Send_Track_From_File_Descriptor(device, fd, metadata, callback, data);
mopoke96143402006-10-30 04:37:26 +00005531
Linus Walleij394bbbe2006-02-22 16:10:53 +00005532 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00005533#ifdef USE_WINDOWS_IO_H
5534 _close(fd);
5535#else
Linus Walleij394bbbe2006-02-22 16:10:53 +00005536 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00005537#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00005538
5539 return ret;
5540}
5541
Linus Walleij094b4502009-09-22 22:28:33 +00005542
5543
5544/**
5545 * This helper function checks if a filename already exists on the device
5546 * @param PTPParams*
5547 * @param string representing the filename
5548 * @return 0 if the filename doesn't exist, -1 if it does
5549 */
5550static int check_filename_exists(PTPParams* params, char const * const filename)
5551{
5552 int i;
5553
5554 for (i = 0; i < params->nrofobjects; i++) {
5555 char *fname = params->objects[i].oi.Filename;
5556 if ((fname != NULL) && (strcmp(filename, fname) == 0))
5557 {
5558 return -1;
5559 }
5560 }
5561
5562 return 0;
5563}
5564
5565/**
5566 * This helper function returns a unique filename, with a random string before the extension
5567 * @param string representing the original filename
5568 * @return a string representing the unique filename
5569 */
5570static char *generate_unique_filename(PTPParams* params, char const * const filename)
5571{
5572 int suffix;
5573 char * extension_position;
5574
5575 if (check_filename_exists(params, filename))
5576 {
5577 extension_position = strrchr(filename,'.');
5578
5579 char basename[extension_position - filename + 1];
5580 strncpy(basename, filename, extension_position - filename);
5581 basename[extension_position - filename] = '\0';
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005582
Linus Walleij094b4502009-09-22 22:28:33 +00005583 suffix = 1;
5584 char newname[ strlen(basename) + 6 + strlen(extension_position)];
5585 sprintf(newname, "%s_%d%s", basename, suffix, extension_position);
5586 while ((check_filename_exists(params, newname)) && (suffix < 1000000)) {
5587 suffix++;
5588 sprintf(newname, "%s_%d%s", basename, suffix, extension_position);
5589 }
5590 return strdup(newname);
5591 }
5592 else
5593 {
5594 return strdup(filename);
5595 }
5596}
5597
Linus Walleijfa1374c2006-02-27 07:41:46 +00005598/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00005599 * This function sends a track from a file descriptor to an
5600 * MTP device. A filename and a set of metadata must be
5601 * given as input.
5602 * @param device a pointer to the device to send the track to.
5603 * @param fd the filedescriptor for a local file which will be sent.
5604 * @param metadata a track metadata set to be written along with the file.
Linus Walleijea68f1f2008-06-22 21:54:44 +00005605 * After this call the field <code>metadata-&gt;item_id</code>
5606 * will contain the new track ID. Other fields such
5607 * as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005608 * or <code>metadata-&gt;storage_id</code> may also change during this
Linus Walleijea68f1f2008-06-22 21:54:44 +00005609 * operation due to device restrictions, so do not rely on the
5610 * contents of this struct to be preserved in any way.
5611 * <ul>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005612 * <li><code>metadata-&gt;parent_id</code> should be set to the parent
5613 * (e.g. folder) to store this track in. Since some
Linus Walleijea68f1f2008-06-22 21:54:44 +00005614 * devices are a bit picky about where files
5615 * are placed, a default folder will be chosen if libmtp
5616 * has detected one for the current filetype and this
5617 * parameter is set to 0. If this is 0 and no default folder
5618 * can be found, the file will be stored in the root folder.
5619 * <li><code>metadata-&gt;storage_id</code> should be set to the
5620 * desired storage (e.g. memory card or whatever your device
5621 * presents) to store this track in. Setting this to 0 will store
5622 * the track on the primary storage.
5623 * </ul>
Linus Walleij394bbbe2006-02-22 16:10:53 +00005624 * @param callback a progress indicator function or NULL to ignore.
5625 * @param data a user-defined pointer that is passed along to
5626 * the <code>progress</code> function in order to
5627 * pass along some user defined data to the progress
5628 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005629 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00005630 * failure.
5631 * @see LIBMTP_Send_Track_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00005632 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00005633 */
mopoke96143402006-10-30 04:37:26 +00005634int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00005635 int const fd, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00005636 LIBMTP_progressfunc_t const callback,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005637 void const * const data)
Linus Walleij394bbbe2006-02-22 16:10:53 +00005638{
Linus Walleij17e39f72006-02-23 15:54:28 +00005639 int subcall_ret;
Linus Walleijd2778d12007-08-06 19:24:58 +00005640 LIBMTP_file_t filedata;
Linus Walleij094b4502009-09-22 22:28:33 +00005641 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5642 PTPParams *params = (PTPParams *) device->params;
Linus Walleij8ae78bb2006-11-20 21:45:52 +00005643
Linus Walleij16c51f02006-05-04 13:20:22 +00005644 // Sanity check, is this really a track?
Linus Walleij10e0ce72008-05-04 19:20:33 +00005645 if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005646 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Linus Walleij46651f32008-04-26 23:30:19 +00005647 "LIBMTP_Send_Track_From_File_Descriptor(): "
5648 "I don't think this is actually a track, strange filetype...");
Linus Walleij99310d42006-11-01 08:29:39 +00005649 }
Linus Walleij37253292006-10-11 08:38:14 +00005650
Linus Walleijd2778d12007-08-06 19:24:58 +00005651 // Wrap around the file transfer function
5652 filedata.item_id = metadata->item_id;
5653 filedata.parent_id = metadata->parent_id;
Linus Walleijea68f1f2008-06-22 21:54:44 +00005654 filedata.storage_id = metadata->storage_id;
Linus Walleij094b4502009-09-22 22:28:33 +00005655 if FLAG_UNIQUE_FILENAMES(ptp_usb) {
5656 filedata.filename = generate_unique_filename(params, metadata->filename);
5657 }
5658 else {
5659 filedata.filename = metadata->filename;
5660 }
Linus Walleijd2778d12007-08-06 19:24:58 +00005661 filedata.filesize = metadata->filesize;
5662 filedata.filetype = metadata->filetype;
5663 filedata.next = NULL;
Linus Walleij37253292006-10-11 08:38:14 +00005664
Linus Walleijd2778d12007-08-06 19:24:58 +00005665 subcall_ret = LIBMTP_Send_File_From_File_Descriptor(device,
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005666 fd,
Linus Walleijd2778d12007-08-06 19:24:58 +00005667 &filedata,
5668 callback,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005669 data);
mopoke31364442006-11-20 04:53:04 +00005670
Linus Walleijd2778d12007-08-06 19:24:58 +00005671 if (subcall_ret != 0) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005672 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Linus Walleij46651f32008-04-26 23:30:19 +00005673 "LIBMTP_Send_Track_From_File_Descriptor(): "
Linus Walleijd2778d12007-08-06 19:24:58 +00005674 "subcall to LIBMTP_Send_File_From_File_Descriptor failed.");
5675 // We used to delete the file here, but don't... It might be OK after all.
5676 // (void) LIBMTP_Delete_Object(device, metadata->item_id);
Linus Walleij394bbbe2006-02-22 16:10:53 +00005677 return -1;
5678 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005679
Linus Walleijea68f1f2008-06-22 21:54:44 +00005680 // Pick up new item (and parent, storage) ID
Linus Walleijd2778d12007-08-06 19:24:58 +00005681 metadata->item_id = filedata.item_id;
5682 metadata->parent_id = filedata.parent_id;
Linus Walleijea68f1f2008-06-22 21:54:44 +00005683 metadata->storage_id = filedata.storage_id;
mopoke96143402006-10-30 04:37:26 +00005684
Linus Walleij17e39f72006-02-23 15:54:28 +00005685 // Set track metadata for the new fine track
5686 subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
5687 if (subcall_ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005688 // Subcall will add error to errorstack
Linus Walleijb2753182007-02-26 08:11:38 +00005689 // We used to delete the file here, but don't... It might be OK after all.
5690 // (void) LIBMTP_Delete_Object(device, metadata->item_id);
Linus Walleij17e39f72006-02-23 15:54:28 +00005691 return -1;
5692 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00005693
Richard Low61edc1a2007-09-23 10:35:48 +00005694 // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
5695 // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
mopoke96143402006-10-30 04:37:26 +00005696
Linus Walleij17e39f72006-02-23 15:54:28 +00005697 return 0;
5698}
5699
Linus Walleijd6a49972006-05-02 08:24:54 +00005700/**
Richard Lowd3b17022009-04-11 12:37:39 +00005701 * This function sends a track from a handler function to an
5702 * MTP device. A filename and a set of metadata must be
5703 * given as input.
5704 * @param device a pointer to the device to send the track to.
5705 * @param get_func the function to call when we have data.
5706 * @param priv the user-defined pointer that is passed to
5707 * <code>get_func</code>.
5708 * @param metadata a track metadata set to be written along with the file.
5709 * After this call the field <code>metadata-&gt;item_id</code>
5710 * will contain the new track ID. Other fields such
5711 * as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005712 * or <code>metadata-&gt;storage_id</code> may also change during this
Richard Lowd3b17022009-04-11 12:37:39 +00005713 * operation due to device restrictions, so do not rely on the
5714 * contents of this struct to be preserved in any way.
5715 * <ul>
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005716 * <li><code>metadata-&gt;parent_id</code> should be set to the parent
5717 * (e.g. folder) to store this track in. Since some
Richard Lowd3b17022009-04-11 12:37:39 +00005718 * devices are a bit picky about where files
5719 * are placed, a default folder will be chosen if libmtp
5720 * has detected one for the current filetype and this
5721 * parameter is set to 0. If this is 0 and no default folder
5722 * can be found, the file will be stored in the root folder.
5723 * <li><code>metadata-&gt;storage_id</code> should be set to the
5724 * desired storage (e.g. memory card or whatever your device
5725 * presents) to store this track in. Setting this to 0 will store
5726 * the track on the primary storage.
5727 * </ul>
5728 * @param callback a progress indicator function or NULL to ignore.
5729 * @param data a user-defined pointer that is passed along to
5730 * the <code>progress</code> function in order to
5731 * pass along some user defined data to the progress
5732 * updates. If not used, set this to NULL.
5733 * @return 0 if the transfer was successful, any other value means
5734 * failure.
5735 * @see LIBMTP_Send_Track_From_File()
5736 * @see LIBMTP_Delete_Object()
5737 */
5738int LIBMTP_Send_Track_From_Handler(LIBMTP_mtpdevice_t *device,
5739 MTPDataGetFunc get_func, void * priv, LIBMTP_track_t * const metadata,
5740 LIBMTP_progressfunc_t const callback,
5741 void const * const data)
5742{
5743 int subcall_ret;
5744 LIBMTP_file_t filedata;
Linus Walleij094b4502009-09-22 22:28:33 +00005745 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
5746 PTPParams *params = (PTPParams *) device->params;
Richard Lowd3b17022009-04-11 12:37:39 +00005747
5748 // Sanity check, is this really a track?
5749 if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005750 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Richard Lowd3b17022009-04-11 12:37:39 +00005751 "LIBMTP_Send_Track_From_Handler(): "
5752 "I don't think this is actually a track, strange filetype...");
5753 }
5754
5755 // Wrap around the file transfer function
5756 filedata.item_id = metadata->item_id;
5757 filedata.parent_id = metadata->parent_id;
5758 filedata.storage_id = metadata->storage_id;
Linus Walleij094b4502009-09-22 22:28:33 +00005759 if FLAG_UNIQUE_FILENAMES(ptp_usb) {
5760 filedata.filename = generate_unique_filename(params, metadata->filename);
5761 }
5762 else {
5763 filedata.filename = metadata->filename;
5764 }
Richard Lowd3b17022009-04-11 12:37:39 +00005765 filedata.filesize = metadata->filesize;
5766 filedata.filetype = metadata->filetype;
5767 filedata.next = NULL;
5768
5769 subcall_ret = LIBMTP_Send_File_From_Handler(device,
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005770 get_func,
5771 priv,
5772 &filedata,
5773 callback,
5774 data);
Richard Lowd3b17022009-04-11 12:37:39 +00005775
5776 if (subcall_ret != 0) {
Linus Walleij2c1bbd62009-11-07 15:15:03 +00005777 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
Richard Lowd3b17022009-04-11 12:37:39 +00005778 "LIBMTP_Send_Track_From_Handler(): "
5779 "subcall to LIBMTP_Send_File_From_Handler failed.");
5780 // We used to delete the file here, but don't... It might be OK after all.
5781 // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5782 return -1;
5783 }
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005784
Richard Lowd3b17022009-04-11 12:37:39 +00005785 // Pick up new item (and parent, storage) ID
5786 metadata->item_id = filedata.item_id;
5787 metadata->parent_id = filedata.parent_id;
5788 metadata->storage_id = filedata.storage_id;
5789
5790 // Set track metadata for the new fine track
5791 subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
5792 if (subcall_ret != 0) {
5793 // Subcall will add error to errorstack
5794 // We used to delete the file here, but don't... It might be OK after all.
5795 // (void) LIBMTP_Delete_Object(device, metadata->item_id);
5796 return -1;
5797 }
5798
5799 // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
5800 // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
5801
5802 return 0;
5803}
5804
5805/**
mopoke96143402006-10-30 04:37:26 +00005806 * This function sends a local file to an MTP device.
Linus Walleijd6a49972006-05-02 08:24:54 +00005807 * A filename and a set of metadata must be
5808 * given as input.
5809 * @param device a pointer to the device to send the track to.
5810 * @param path the filename of a local file which will be sent.
Linus Walleijea68f1f2008-06-22 21:54:44 +00005811 * @param filedata a file metadata set to be written along with the file.
5812 * After this call the field <code>filedata-&gt;item_id</code>
5813 * will contain the new file ID. Other fields such
5814 * as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005815 * or <code>filedata-&gt;storage_id</code> may also change during this
Linus Walleijea68f1f2008-06-22 21:54:44 +00005816 * operation due to device restrictions, so do not rely on the
5817 * contents of this struct to be preserved in any way.
5818 * <ul>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005819 * <li><code>filedata-&gt;parent_id</code> should be set to the parent
5820 * (e.g. folder) to store this file in. If this is 0,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005821 * the file will be stored in the root folder.
5822 * <li><code>filedata-&gt;storage_id</code> should be set to the
5823 * desired storage (e.g. memory card or whatever your device
5824 * presents) to store this file in. Setting this to 0 will store
5825 * the file on the primary storage.
5826 * </ul>
Linus Walleijd6a49972006-05-02 08:24:54 +00005827 * @param callback a progress indicator function or NULL to ignore.
5828 * @param data a user-defined pointer that is passed along to
5829 * the <code>progress</code> function in order to
5830 * pass along some user defined data to the progress
5831 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005832 * @return 0 if the transfer was successful, any other value means
Linus Walleijd6a49972006-05-02 08:24:54 +00005833 * failure.
5834 * @see LIBMTP_Send_File_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00005835 * @see LIBMTP_Delete_Object()
Linus Walleijd6a49972006-05-02 08:24:54 +00005836 */
mopoke96143402006-10-30 04:37:26 +00005837int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleijd6a49972006-05-02 08:24:54 +00005838 char const * const path, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00005839 LIBMTP_progressfunc_t const callback,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005840 void const * const data)
Linus Walleijd6a49972006-05-02 08:24:54 +00005841{
5842 int fd;
5843 int ret;
5844
5845 // Sanity check
5846 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005847 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Bad arguments, path was NULL.");
Linus Walleijd6a49972006-05-02 08:24:54 +00005848 return -1;
5849 }
5850
5851 // Open file
5852#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00005853#ifdef USE_WINDOWS_IO_H
Linus Walleij3017c562010-10-17 21:31:36 +00005854 if ( (fd = _open(path, O_RDONLY|O_BINARY)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00005855#else
Linus Walleij3017c562010-10-17 21:31:36 +00005856 if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00005857#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00005858#else
5859 if ( (fd = open(path, O_RDONLY)) == -1) {
5860#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00005861 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Could not open source file.");
Linus Walleijd6a49972006-05-02 08:24:54 +00005862 return -1;
5863 }
5864
Linus Walleijea68f1f2008-06-22 21:54:44 +00005865 ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data);
mopoke96143402006-10-30 04:37:26 +00005866
Linus Walleijd6a49972006-05-02 08:24:54 +00005867 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00005868#ifdef USE_WINDOWS_IO_H
5869 _close(fd);
5870#else
Linus Walleijd6a49972006-05-02 08:24:54 +00005871 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00005872#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00005873
5874 return ret;
5875}
5876
Linus Walleijd208f9c2006-04-27 14:16:06 +00005877/**
5878 * This function sends a generic file from a file descriptor to an
5879 * MTP device. A filename and a set of metadata must be
5880 * given as input.
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00005881 *
5882 * This can potentially be used for sending in a stream of unknown
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005883 * length. Send music files with
Linus Walleij1b87ea72007-08-28 07:53:09 +00005884 * <code>LIBMTP_Send_Track_From_File_Descriptor()</code>
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00005885 *
Linus Walleijd208f9c2006-04-27 14:16:06 +00005886 * @param device a pointer to the device to send the file to.
5887 * @param fd the filedescriptor for a local file which will be sent.
Linus Walleijea68f1f2008-06-22 21:54:44 +00005888 * @param filedata a file metadata set to be written along with the file.
5889 * After this call the field <code>filedata-&gt;item_id</code>
5890 * will contain the new file ID. Other fields such
5891 * as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005892 * or <code>filedata-&gt;storage_id</code> may also change during this
Linus Walleijea68f1f2008-06-22 21:54:44 +00005893 * operation due to device restrictions, so do not rely on the
5894 * contents of this struct to be preserved in any way.
5895 * <ul>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005896 * <li><code>filedata-&gt;parent_id</code> should be set to the parent
5897 * (e.g. folder) to store this file in. If this is 0,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005898 * the file will be stored in the root folder.
5899 * <li><code>filedata-&gt;storage_id</code> should be set to the
5900 * desired storage (e.g. memory card or whatever your device
5901 * presents) to store this file in. Setting this to 0 will store
5902 * the file on the primary storage.
5903 * </ul>
Linus Walleijd208f9c2006-04-27 14:16:06 +00005904 * @param callback a progress indicator function or NULL to ignore.
5905 * @param data a user-defined pointer that is passed along to
5906 * the <code>progress</code> function in order to
5907 * pass along some user defined data to the progress
5908 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00005909 * @return 0 if the transfer was successful, any other value means
Linus Walleijd208f9c2006-04-27 14:16:06 +00005910 * failure.
Linus Walleijd6a49972006-05-02 08:24:54 +00005911 * @see LIBMTP_Send_File_From_File()
Linus Walleij1b87ea72007-08-28 07:53:09 +00005912 * @see LIBMTP_Send_Track_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00005913 * @see LIBMTP_Delete_Object()
Linus Walleijd208f9c2006-04-27 14:16:06 +00005914 */
mopoke96143402006-10-30 04:37:26 +00005915int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleijd208f9c2006-04-27 14:16:06 +00005916 int const fd, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00005917 LIBMTP_progressfunc_t const callback,
Linus Walleijea68f1f2008-06-22 21:54:44 +00005918 void const * const data)
Linus Walleijd208f9c2006-04-27 14:16:06 +00005919{
5920 uint16_t ret;
Linus Walleijd208f9c2006-04-27 14:16:06 +00005921 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd214b9b2006-08-26 22:08:37 +00005922 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij817579a2008-05-23 21:21:40 +00005923 LIBMTP_file_t *newfilemeta;
Linus Walleije04a1b92011-03-09 18:00:24 +01005924 int oldtimeout;
5925 int timeout;
Richard Lowab0d22d2006-11-30 22:17:49 +00005926
Richard Lowd3b17022009-04-11 12:37:39 +00005927 if (send_file_object_info(device, filedata))
5928 {
5929 // no need to output an error since send_file_object_info will already have done so
Linus Walleij07795772007-08-06 18:44:06 +00005930 return -1;
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00005931 }
Linus Walleij07795772007-08-06 18:44:06 +00005932
Richard Lowd3b17022009-04-11 12:37:39 +00005933 // Callbacks
5934 ptp_usb->callback_active = 1;
5935 // The callback will deactivate itself after this amount of data has been sent
5936 // One BULK header for the request, one for the data phase. No parameters to the request.
5937 ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
5938 ptp_usb->current_transfer_complete = 0;
5939 ptp_usb->current_transfer_callback = callback;
5940 ptp_usb->current_transfer_callback_data = data;
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005941
Linus Walleije04a1b92011-03-09 18:00:24 +01005942 /*
5943 * We might need to increase the timeout here, files can be pretty
5944 * large. Take the default timeout and add the calculated time for
5945 * this transfer
5946 */
5947 get_usb_device_timeout(ptp_usb, &oldtimeout);
5948 timeout = oldtimeout +
5949 (ptp_usb->current_transfer_total / guess_usb_speed(ptp_usb)) * 1000;
5950 set_usb_device_timeout(ptp_usb, timeout);
5951
Richard Lowd3b17022009-04-11 12:37:39 +00005952 ret = ptp_sendobject_fromfd(params, fd, filedata->filesize);
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005953
Richard Lowd3b17022009-04-11 12:37:39 +00005954 ptp_usb->callback_active = 0;
5955 ptp_usb->current_transfer_callback = NULL;
5956 ptp_usb->current_transfer_callback_data = NULL;
Linus Walleije04a1b92011-03-09 18:00:24 +01005957 set_usb_device_timeout(ptp_usb, oldtimeout);
Richard Lowd3b17022009-04-11 12:37:39 +00005958
5959 if (ret == PTP_ERROR_CANCEL) {
5960 add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer.");
5961 return -1;
5962 }
5963 if (ret != PTP_RC_OK) {
5964 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): "
5965 "Could not send object.");
5966 return -1;
5967 }
5968
5969 add_object_to_cache(device, filedata->item_id);
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005970
Richard Lowd3b17022009-04-11 12:37:39 +00005971 /*
Linus Walleij4fc19172010-01-19 00:10:26 +00005972 * Get the device-assigned parent_id from the cache.
Richard Lowd3b17022009-04-11 12:37:39 +00005973 * The operation that adds it to the cache will
5974 * look it up from the device, so we get the new
5975 * parent_id from the cache.
5976 */
5977 newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
5978 if (newfilemeta != NULL) {
5979 filedata->parent_id = newfilemeta->parent_id;
5980 filedata->storage_id = newfilemeta->storage_id;
5981 LIBMTP_destroy_file_t(newfilemeta);
5982 } else {
5983 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
5984 "LIBMTP_Send_File_From_File_Descriptor(): "
5985 "Could not retrieve updated metadata.");
5986 return -1;
5987 }
5988
5989 return 0;
5990}
5991
5992/**
5993 * This function sends a generic file from a handler function to an
5994 * MTP device. A filename and a set of metadata must be
5995 * given as input.
5996 *
5997 * This can potentially be used for sending in a stream of unknown
Linus Walleijd8fd9a52009-09-09 22:46:01 +00005998 * length. Send music files with
Richard Lowd3b17022009-04-11 12:37:39 +00005999 * <code>LIBMTP_Send_Track_From_Handler()</code>
6000 *
6001 * @param device a pointer to the device to send the file to.
6002 * @param get_func the function to call to get data to write
6003 * @param priv a user-defined pointer that is passed along to
6004 * <code>get_func</code>. If not used, this is set to NULL.
6005 * @param filedata a file metadata set to be written along with the file.
6006 * After this call the field <code>filedata-&gt;item_id</code>
6007 * will contain the new file ID. Other fields such
6008 * as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006009 * or <code>filedata-&gt;storage_id</code> may also change during this
Richard Lowd3b17022009-04-11 12:37:39 +00006010 * operation due to device restrictions, so do not rely on the
6011 * contents of this struct to be preserved in any way.
6012 * <ul>
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006013 * <li><code>filedata-&gt;parent_id</code> should be set to the parent
6014 * (e.g. folder) to store this file in. If this is 0,
Richard Lowd3b17022009-04-11 12:37:39 +00006015 * the file will be stored in the root folder.
6016 * <li><code>filedata-&gt;storage_id</code> should be set to the
6017 * desired storage (e.g. memory card or whatever your device
6018 * presents) to store this file in. Setting this to 0 will store
6019 * the file on the primary storage.
6020 * </ul>
6021 * @param callback a progress indicator function or NULL to ignore.
6022 * @param data a user-defined pointer that is passed along to
6023 * the <code>progress</code> function in order to
6024 * pass along some user defined data to the progress
6025 * updates. If not used, set this to NULL.
6026 * @return 0 if the transfer was successful, any other value means
6027 * failure.
6028 * @see LIBMTP_Send_File_From_File()
6029 * @see LIBMTP_Send_Track_From_File_Descriptor()
6030 * @see LIBMTP_Delete_Object()
6031 */
6032int LIBMTP_Send_File_From_Handler(LIBMTP_mtpdevice_t *device,
6033 MTPDataGetFunc get_func, void * priv, LIBMTP_file_t * const filedata,
6034 LIBMTP_progressfunc_t const callback, void const * const data)
6035{
6036 uint16_t ret;
6037 PTPParams *params = (PTPParams *) device->params;
6038 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6039 LIBMTP_file_t *newfilemeta;
6040
6041 if (send_file_object_info(device, filedata))
6042 {
6043 // no need to output an error since send_file_object_info will already have done so
6044 return -1;
6045 }
Linus Walleijd866d242009-08-23 21:50:39 +00006046
Richard Lowd3b17022009-04-11 12:37:39 +00006047 // Callbacks
6048 ptp_usb->callback_active = 1;
6049 // The callback will deactivate itself after this amount of data has been sent
6050 // One BULK header for the request, one for the data phase. No parameters to the request.
6051 ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
6052 ptp_usb->current_transfer_complete = 0;
6053 ptp_usb->current_transfer_callback = callback;
6054 ptp_usb->current_transfer_callback_data = data;
Linus Walleijd866d242009-08-23 21:50:39 +00006055
Richard Low5b4023c2009-04-16 19:14:38 +00006056 MTPDataHandler mtp_handler;
6057 mtp_handler.getfunc = get_func;
6058 mtp_handler.putfunc = NULL;
Linus Walleijd866d242009-08-23 21:50:39 +00006059 mtp_handler.priv = priv;
6060
Richard Lowd3b17022009-04-11 12:37:39 +00006061 PTPDataHandler handler;
Richard Low5b4023c2009-04-16 19:14:38 +00006062 handler.getfunc = get_func_wrapper;
Richard Lowd3b17022009-04-11 12:37:39 +00006063 handler.putfunc = NULL;
Linus Walleijd866d242009-08-23 21:50:39 +00006064 handler.priv = &mtp_handler;
6065
Richard Lowd3b17022009-04-11 12:37:39 +00006066 ret = ptp_sendobject_from_handler(params, &handler, filedata->filesize);
Linus Walleijd866d242009-08-23 21:50:39 +00006067
Richard Lowd3b17022009-04-11 12:37:39 +00006068 ptp_usb->callback_active = 0;
6069 ptp_usb->current_transfer_callback = NULL;
6070 ptp_usb->current_transfer_callback_data = NULL;
6071
6072 if (ret == PTP_ERROR_CANCEL) {
6073 add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_Handler(): Cancelled transfer.");
6074 return -1;
6075 }
6076 if (ret != PTP_RC_OK) {
6077 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_Handler(): "
6078 "Could not send object.");
6079 return -1;
6080 }
6081
6082 add_object_to_cache(device, filedata->item_id);
Linus Walleijd866d242009-08-23 21:50:39 +00006083
Richard Lowd3b17022009-04-11 12:37:39 +00006084 /*
6085 * Get the device-assined parent_id from the cache.
6086 * The operation that adds it to the cache will
6087 * look it up from the device, so we get the new
6088 * parent_id from the cache.
6089 */
6090 newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
6091 if (newfilemeta != NULL) {
6092 filedata->parent_id = newfilemeta->parent_id;
6093 filedata->storage_id = newfilemeta->storage_id;
6094 LIBMTP_destroy_file_t(newfilemeta);
6095 } else {
6096 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
6097 "LIBMTP_Send_File_From_Handler(): "
6098 "Could not retrieve updated metadata.");
6099 return -1;
6100 }
6101
6102 return 0;
6103}
6104
Linus Walleijd866d242009-08-23 21:50:39 +00006105/**
Richard Lowd3b17022009-04-11 12:37:39 +00006106 * This function sends the file object info, ready for sendobject
6107 * @param device a pointer to the device to send the file to.
6108 * @param filedata a file metadata set to be written along with the file.
6109 * @return 0 if the transfer was successful, any other value means
6110 * failure.
6111 */
6112static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata)
6113{
6114 PTPParams *params = (PTPParams *) device->params;
6115 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6116 uint32_t store;
6117 int use_primary_storage = 1;
6118 uint16_t of = map_libmtp_type_to_ptp_type(filedata->filetype);
6119 LIBMTP_devicestorage_t *storage;
6120 uint32_t localph = filedata->parent_id;
6121 uint16_t ret;
6122 int i;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006123
Linus Walleij90143282011-03-02 00:24:15 +01006124#if 0
6125 // Sanity check: no zerolength files on some devices?
6126 // If the zerolength files cause problems on some devices,
6127 // then add a bug flag for this.
Richard Lowd3b17022009-04-11 12:37:39 +00006128 if (filedata->filesize == 0) {
6129 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "send_file_object_info(): "
6130 "File of zero size.");
6131 return -1;
6132 }
Linus Walleij90143282011-03-02 00:24:15 +01006133#endif
Linus Walleijea68f1f2008-06-22 21:54:44 +00006134 if (filedata->storage_id != 0) {
6135 store = filedata->storage_id;
Linus Walleijfb28b632007-10-23 21:56:18 +00006136 } else {
Linus Walleij88babc82014-06-02 21:32:14 +02006137 store = get_suggested_storage_id(device, filedata->filesize, localph);
Linus Walleijea68f1f2008-06-22 21:54:44 +00006138 }
Linus Walleij88babc82014-06-02 21:32:14 +02006139
Linus Walleijea68f1f2008-06-22 21:54:44 +00006140 // Detect if something non-primary is in use.
6141 storage = device->storage;
Richard Low4d9165f2008-09-23 20:13:17 +00006142 if (storage != NULL && store != storage->id) {
Linus Walleijea68f1f2008-06-22 21:54:44 +00006143 use_primary_storage = 0;
Linus Walleij6bf68b42007-09-03 22:50:02 +00006144 }
Linus Walleijd208f9c2006-04-27 14:16:06 +00006145
mopoke96143402006-10-30 04:37:26 +00006146 /*
Linus Walleij05ccbe72006-06-13 07:46:58 +00006147 * If no destination folder was given, look up a default
6148 * folder if possible. Perhaps there is some way of retrieveing
6149 * the default folder for different forms of content, what
6150 * do I know, we use a fixed list in lack of any better method.
6151 * Some devices obviously need to have their files in certain
mopoke96143402006-10-30 04:37:26 +00006152 * folders in order to find/display them at all (hello Creative),
Linus Walleijc40c9bf2008-06-13 23:24:17 +00006153 * so we have to have a method for this. We only do this if the
6154 * primary storage is in use.
Linus Walleij05ccbe72006-06-13 07:46:58 +00006155 */
6156
Linus Walleijc40c9bf2008-06-13 23:24:17 +00006157 if (localph == 0 && use_primary_storage) {
Linus Walleij10e0ce72008-05-04 19:20:33 +00006158 if (LIBMTP_FILETYPE_IS_AUDIO(filedata->filetype)) {
Linus Walleij05ccbe72006-06-13 07:46:58 +00006159 localph = device->default_music_folder;
Linus Walleij10e0ce72008-05-04 19:20:33 +00006160 } else if (LIBMTP_FILETYPE_IS_VIDEO(filedata->filetype)) {
Linus Walleij05ccbe72006-06-13 07:46:58 +00006161 localph = device->default_video_folder;
6162 } else if (of == PTP_OFC_EXIF_JPEG ||
Linus Walleij5fb47132006-12-30 15:35:48 +00006163 of == PTP_OFC_JP2 ||
6164 of == PTP_OFC_JPX ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00006165 of == PTP_OFC_JFIF ||
6166 of == PTP_OFC_TIFF ||
Linus Walleij5fb47132006-12-30 15:35:48 +00006167 of == PTP_OFC_TIFF_IT ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00006168 of == PTP_OFC_BMP ||
6169 of == PTP_OFC_GIF ||
6170 of == PTP_OFC_PICT ||
6171 of == PTP_OFC_PNG ||
6172 of == PTP_OFC_MTP_WindowsImageFormat) {
6173 localph = device->default_picture_folder;
6174 } else if (of == PTP_OFC_MTP_vCalendar1 ||
Linus Walleijd7aa5b22006-09-02 11:52:31 +00006175 of == PTP_OFC_MTP_vCalendar2 ||
6176 of == PTP_OFC_MTP_UndefinedContact ||
6177 of == PTP_OFC_MTP_vCard2 ||
6178 of == PTP_OFC_MTP_vCard3 ||
6179 of == PTP_OFC_MTP_UndefinedCalendarItem) {
Linus Walleij05ccbe72006-06-13 07:46:58 +00006180 localph = device->default_organizer_folder;
Linus Walleij46651f32008-04-26 23:30:19 +00006181 } else if (of == PTP_OFC_Text) {
Linus Walleij9316e652006-12-07 09:55:21 +00006182 localph = device->default_text_folder;
Linus Walleij05ccbe72006-06-13 07:46:58 +00006183 }
6184 }
mopoke31364442006-11-20 04:53:04 +00006185
Linus Walleija3544f62007-11-30 01:20:04 +00006186 // Here we wire the type to unknown on bugged, but
Linus Walleij89bb1cd2009-07-24 21:03:36 +00006187 // Ogg or FLAC-supportive devices.
Linus Walleijfec4d562008-06-01 22:30:36 +00006188 if (FLAG_OGG_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_OGG) {
Linus Walleija3544f62007-11-30 01:20:04 +00006189 of = PTP_OFC_Undefined;
6190 }
Linus Walleij89bb1cd2009-07-24 21:03:36 +00006191 if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_FLAC) {
6192 of = PTP_OFC_Undefined;
6193 }
Linus Walleija3544f62007-11-30 01:20:04 +00006194
Linus Walleijf67c1ad2009-01-14 21:39:50 +00006195 if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
6196 !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
Linus Walleijd2778d12007-08-06 19:24:58 +00006197 /*
6198 * MTP enhanched does it this way (from a sniff):
6199 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
6200 * 20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
6201 * FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
6202 * Length: 0x00000020
6203 * Type: 0x0001 PTP_USB_CONTAINER_COMMAND
6204 * Code: 0x9808
6205 * Transaction ID: 0x0000001B
6206 * Param1: 0x00010001 <- store
6207 * Param2: 0xffffffff <- parent handle (-1 ?)
6208 * Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
6209 * Param4: 0x00000000 <- file length MSB (-0x0c header len)
6210 * Param5: 0x00005e12 <- file length LSB (-0x0c header len)
6211 *
6212 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
6213 * 46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
6214 * 00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
6215 * 00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
6216 * 00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
6217 * 00 4F DC 02 00 01 - dc4f = non consumable
6218 * Length: 0x00000046
6219 * Type: 0x0002 PTP_USB_CONTAINER_DATA
6220 * Code: 0x9808
6221 * Transaction ID: 0x0000001B
6222 * Metadata....
6223 * 0x00000003 <- Number of metadata items
6224 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6225 * 0xdc07 <- metadata type: file name
6226 * 0xffff <- metadata type: string
6227 * 0x0d <- number of (uint16_t) characters
6228 * 4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
6229 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6230 * 0xdc03 <- metadata type: protection status
6231 * 0x0004 <- metadata type: uint16_t
6232 * 0x0000 <- not protected
6233 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
6234 * 0xdc4f <- non consumable
6235 * 0x0002 <- metadata type: uint8_t
6236 * 0x01 <- non-consumable (this device cannot display PDF)
6237 *
6238 * <- Read 0x18 bytes back
6239 * 18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
6240 * 00 00 00 00 01 40 00 00
6241 * Length: 0x000000018
6242 * Type: 0x0003 PTP_USB_CONTAINER_RESPONSE
6243 * Code: 0x2001 PTP_OK
6244 * Transaction ID: 0x0000001B
6245 * Param1: 0x00010001 <- store
6246 * Param2: 0x00000000 <- parent handle
6247 * Param3: 0x00004001 <- new file/object ID
6248 *
6249 * -> PTP_OC_SendObject (0x100d)
6250 * 0C 00 00 00 01 00 0D 10 1C 00 00 00
6251 * -> ... all the bytes ...
6252 * <- Read 0x0c bytes back
6253 * 0C 00 00 00 03 00 01 20 1C 00 00 00
6254 * ... Then update metadata one-by one, actually (instead of sending it first!) ...
6255 */
Linus Walleij1e9a0332007-09-12 19:35:56 +00006256 MTPProperties *props = NULL;
6257 int nrofprops = 0;
6258 MTPProperties *prop = NULL;
6259 uint16_t *properties = NULL;
Linus Walleij07795772007-08-06 18:44:06 +00006260 uint32_t propcnt = 0;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006261
Linus Walleij9036b332008-05-23 21:01:36 +00006262 // default parent handle
Linus Walleij07795772007-08-06 18:44:06 +00006263 if (localph == 0)
6264 localph = 0xFFFFFFFFU; // Set to -1
6265
Richard Low6c0a6ce2006-11-26 10:42:08 +00006266 // Must be 0x00000000U for new objects
6267 filedata->item_id = 0x00000000U;
Linus Walleij05ccbe72006-06-13 07:46:58 +00006268
Linus Walleij1e9a0332007-09-12 19:35:56 +00006269 ret = ptp_mtp_getobjectpropssupported(params, of, &propcnt, &properties);
mopoke31364442006-11-20 04:53:04 +00006270
rreardonbbb4e562006-11-19 22:16:11 +00006271 for (i=0;i<propcnt;i++) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006272 PTPObjectPropDesc opd;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006273
Linus Walleij1e9a0332007-09-12 19:35:56 +00006274 ret = ptp_mtp_getobjectpropdesc(params, properties[i], of, &opd);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006275 if (ret != PTP_RC_OK) {
Richard Lowd3b17022009-04-11 12:37:39 +00006276 add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
Linus Walleijdbcc8242007-08-05 22:31:26 +00006277 "could not get property description.");
6278 } else if (opd.GetSet) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00006279 switch (properties[i]) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006280 case PTP_OPC_ObjectFileName:
Linus Walleija6d0d482007-10-31 08:54:56 +00006281 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006282 prop->ObjectHandle = filedata->item_id;
6283 prop->property = PTP_OPC_ObjectFileName;
6284 prop->datatype = PTP_DTC_STR;
Linus Walleijd3b78572007-08-24 21:28:24 +00006285 if (filedata->filename != NULL) {
Linus Walleij07795772007-08-06 18:44:06 +00006286 prop->propval.str = strdup(filedata->filename);
Linus Walleijfec4d562008-06-01 22:30:36 +00006287 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijd3b78572007-08-24 21:28:24 +00006288 strip_7bit_from_utf8(prop->propval.str);
6289 }
6290 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006291 break;
6292 case PTP_OPC_ProtectionStatus:
Linus Walleija6d0d482007-10-31 08:54:56 +00006293 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006294 prop->ObjectHandle = filedata->item_id;
6295 prop->property = PTP_OPC_ProtectionStatus;
6296 prop->datatype = PTP_DTC_UINT16;
6297 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleijdbcc8242007-08-05 22:31:26 +00006298 break;
6299 case PTP_OPC_NonConsumable:
Linus Walleija6d0d482007-10-31 08:54:56 +00006300 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006301 prop->ObjectHandle = filedata->item_id;
6302 prop->property = PTP_OPC_NonConsumable;
6303 prop->datatype = PTP_DTC_UINT8;
Linus Walleij7783b9b2007-09-22 22:10:44 +00006304 prop->propval.u8 = 0x00; /* It is supported, then it is consumable */
Linus Walleijdbcc8242007-08-05 22:31:26 +00006305 break;
6306 case PTP_OPC_Name:
Linus Walleija6d0d482007-10-31 08:54:56 +00006307 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006308 prop->ObjectHandle = filedata->item_id;
6309 prop->property = PTP_OPC_Name;
6310 prop->datatype = PTP_DTC_STR;
Linus Walleij07795772007-08-06 18:44:06 +00006311 if (filedata->filename != NULL)
6312 prop->propval.str = strdup(filedata->filename);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006313 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00006314 case PTP_OPC_DateModified:
6315 // Tag with current time if that is supported
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00006316 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
6317 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
6318 prop->ObjectHandle = filedata->item_id;
6319 prop->property = PTP_OPC_DateModified;
6320 prop->datatype = PTP_DTC_STR;
6321 prop->propval.str = get_iso8601_stamp();
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006322 filedata->modificationdate = time(NULL);
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00006323 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00006324 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006325 }
rreardonbbb4e562006-11-19 22:16:11 +00006326 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006327 ptp_free_objectpropdesc(&opd);
Linus Walleij99310d42006-11-01 08:29:39 +00006328 }
Linus Walleij1e9a0332007-09-12 19:35:56 +00006329 free(properties);
mopoke31364442006-11-20 04:53:04 +00006330
rreardonbbb4e562006-11-19 22:16:11 +00006331 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id,
Linus Walleij1e9a0332007-09-12 19:35:56 +00006332 of, filedata->filesize, props, nrofprops);
mopoke31364442006-11-20 04:53:04 +00006333
rreardonbbb4e562006-11-19 22:16:11 +00006334 /* Free property list */
Linus Walleija6d0d482007-10-31 08:54:56 +00006335 ptp_destroy_object_prop_list(props, nrofprops);
mopoke31364442006-11-20 04:53:04 +00006336
rreardonbbb4e562006-11-19 22:16:11 +00006337 if (ret != PTP_RC_OK) {
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006338 add_ptp_error_to_errorstack(device, ret, "send_file_object_info():"
Linus Walleij07795772007-08-06 18:44:06 +00006339 "Could not send object property list.");
rreardonbbb4e562006-11-19 22:16:11 +00006340 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00006341 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardonbbb4e562006-11-19 22:16:11 +00006342 }
6343 return -1;
6344 }
6345 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
Linus Walleij07795772007-08-06 18:44:06 +00006346 PTPObjectInfo new_file;
6347
6348 memset(&new_file, 0, sizeof(PTPObjectInfo));
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006349
Linus Walleij07795772007-08-06 18:44:06 +00006350 new_file.Filename = filedata->filename;
Linus Walleijfec4d562008-06-01 22:30:36 +00006351 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijd3b78572007-08-24 21:28:24 +00006352 strip_7bit_from_utf8(new_file.Filename);
6353 }
Philip Langdaleabdd59d2013-04-05 14:00:28 -07006354 if (filedata->filesize > 0xFFFFFFFFL) {
6355 // This is a kludge in the MTP standard for large files.
6356 new_file.ObjectCompressedSize = (uint32_t) 0xFFFFFFFF;
6357 } else {
6358 new_file.ObjectCompressedSize = (uint32_t) filedata->filesize;
6359 }
Linus Walleij07795772007-08-06 18:44:06 +00006360 new_file.ObjectFormat = of;
6361 new_file.StorageID = store;
6362 new_file.ParentObject = localph;
Richard Low5f6fff82009-04-11 12:45:04 +00006363 new_file.ModificationDate = time(NULL);
Linus Walleij07795772007-08-06 18:44:06 +00006364
Linus Walleij9be685b2006-11-21 09:44:53 +00006365 // Create the object
6366 ret = ptp_sendobjectinfo(params, &store, &localph, &filedata->item_id, &new_file);
Linus Walleij07795772007-08-06 18:44:06 +00006367
Linus Walleij9be685b2006-11-21 09:44:53 +00006368 if (ret != PTP_RC_OK) {
Richard Lowd3b17022009-04-11 12:37:39 +00006369 add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
Linus Walleij07795772007-08-06 18:44:06 +00006370 "Could not send object info.");
Linus Walleij9be685b2006-11-21 09:44:53 +00006371 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00006372 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij9be685b2006-11-21 09:44:53 +00006373 }
6374 return -1;
6375 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00006376 // NOTE: the char* pointers inside new_file are not copies so don't
6377 // try to destroy this objectinfo!
rreardonbbb4e562006-11-19 22:16:11 +00006378 }
Linus Walleijd2778d12007-08-06 19:24:58 +00006379
6380 // Now there IS an object with this parent handle.
6381 filedata->parent_id = localph;
Linus Walleijd8fd9a52009-09-09 22:46:01 +00006382
Linus Walleijd208f9c2006-04-27 14:16:06 +00006383 return 0;
6384}
6385
Linus Walleij17e39f72006-02-23 15:54:28 +00006386/**
Linus Walleij9036b332008-05-23 21:01:36 +00006387 * This function updates the MTP track object metadata on a
6388 * single file identified by an object ID.
mopoke96143402006-10-30 04:37:26 +00006389 * @param device a pointer to the device to update the track
Linus Walleij95698cd2006-02-24 10:40:40 +00006390 * metadata on.
Linus Walleij17e39f72006-02-23 15:54:28 +00006391 * @param metadata a track metadata set to be written to the file.
6392 * notice that the <code>track_id</code> field of the
6393 * metadata structure must be correct so that the
Linus Walleija4982732006-02-24 15:46:02 +00006394 * function can update the right file. If some properties
6395 * of this metadata are set to NULL (strings) or 0
6396 * (numerical values) they will be discarded and the
6397 * track will not be tagged with these blank values.
Richard Lowaf20b5d2006-12-17 18:00:59 +00006398 * @return 0 on success, any other value means failure. If some
6399 * or all of the properties fail to update we will still
6400 * return success. On some devices (notably iRiver T30)
6401 * properties that exist cannot be updated.
Linus Walleij17e39f72006-02-23 15:54:28 +00006402 */
mopoke96143402006-10-30 04:37:26 +00006403int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *device,
Linus Walleij17e39f72006-02-23 15:54:28 +00006404 LIBMTP_track_t const * const metadata)
6405{
Linus Walleij17e39f72006-02-23 15:54:28 +00006406 uint16_t ret;
Linus Walleij00cf0642006-07-26 20:40:59 +00006407 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf9267e92007-10-15 21:07:37 +00006408 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij00cf0642006-07-26 20:40:59 +00006409 uint32_t i;
Linus Walleij1e9a0332007-09-12 19:35:56 +00006410 uint16_t *properties = NULL;
Linus Walleij00cf0642006-07-26 20:40:59 +00006411 uint32_t propcnt = 0;
Linus Walleij17e39f72006-02-23 15:54:28 +00006412
mopoke96143402006-10-30 04:37:26 +00006413 // First see which properties can be set on this file format and apply accordingly
Linus Walleij00cf0642006-07-26 20:40:59 +00006414 // i.e only try to update this metadata for object tags that exist on the current player.
Linus Walleij1e9a0332007-09-12 19:35:56 +00006415 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &properties);
Linus Walleij00cf0642006-07-26 20:40:59 +00006416 if (ret != PTP_RC_OK) {
6417 // Just bail out for now, nothing is ever set.
Linus Walleije7df6532007-03-23 08:20:06 +00006418 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6419 "could not retrieve supported object properties.");
raveloxd9a28642006-05-26 23:42:22 +00006420 return -1;
Richard Low15731fe2007-03-22 20:27:20 +00006421 }
Linus Walleijf9267e92007-10-15 21:07:37 +00006422 if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
Linus Walleijfec4d562008-06-01 22:30:36 +00006423 !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00006424 MTPProperties *props = NULL;
6425 MTPProperties *prop = NULL;
6426 int nrofprops = 0;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006427
Richard Low15731fe2007-03-22 20:27:20 +00006428 for (i=0;i<propcnt;i++) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006429 PTPObjectPropDesc opd;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006430
Linus Walleij1e9a0332007-09-12 19:35:56 +00006431 ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006432 if (ret != PTP_RC_OK) {
6433 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6434 "could not get property description.");
6435 } else if (opd.GetSet) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00006436 switch (properties[i]) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006437 case PTP_OPC_Name:
6438 if (metadata->title == NULL)
6439 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006440 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006441 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006442 prop->property = PTP_OPC_Name;
6443 prop->datatype = PTP_DTC_STR;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006444 prop->propval.str = strdup(metadata->title);
Linus Walleije7df6532007-03-23 08:20:06 +00006445 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006446 case PTP_OPC_AlbumName:
6447 if (metadata->album == NULL)
6448 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006449 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleije7df6532007-03-23 08:20:06 +00006450 prop->ObjectHandle = metadata->item_id;
6451 prop->property = PTP_OPC_AlbumName;
6452 prop->datatype = PTP_DTC_STR;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006453 prop->propval.str = strdup(metadata->album);
Linus Walleije7df6532007-03-23 08:20:06 +00006454 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006455 case PTP_OPC_Artist:
6456 if (metadata->artist == NULL)
6457 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006458 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006459 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006460 prop->property = PTP_OPC_Artist;
6461 prop->datatype = PTP_DTC_STR;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006462 prop->propval.str = strdup(metadata->artist);
Linus Walleije7df6532007-03-23 08:20:06 +00006463 break;
Linus Walleij31b74292008-05-02 23:29:06 +00006464 case PTP_OPC_Composer:
6465 if (metadata->composer == NULL)
6466 break;
6467 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6468 prop->ObjectHandle = metadata->item_id;
6469 prop->property = PTP_OPC_Composer;
6470 prop->datatype = PTP_DTC_STR;
6471 prop->propval.str = strdup(metadata->composer);
6472 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006473 case PTP_OPC_Genre:
6474 if (metadata->genre == NULL)
6475 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006476 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006477 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006478 prop->property = PTP_OPC_Genre;
6479 prop->datatype = PTP_DTC_STR;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006480 prop->propval.str = strdup(metadata->genre);
Linus Walleije7df6532007-03-23 08:20:06 +00006481 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006482 case PTP_OPC_Duration:
Linus Walleija6d0d482007-10-31 08:54:56 +00006483 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleije7df6532007-03-23 08:20:06 +00006484 prop->ObjectHandle = metadata->item_id;
6485 prop->property = PTP_OPC_Duration;
6486 prop->datatype = PTP_DTC_UINT32;
Linus Walleij29559562007-08-22 21:38:04 +00006487 prop->propval.u32 = adjust_u32(metadata->duration, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006488 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006489 case PTP_OPC_Track:
Linus Walleija6d0d482007-10-31 08:54:56 +00006490 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleije7df6532007-03-23 08:20:06 +00006491 prop->ObjectHandle = metadata->item_id;
6492 prop->property = PTP_OPC_Track;
6493 prop->datatype = PTP_DTC_UINT16;
Linus Walleij29559562007-08-22 21:38:04 +00006494 prop->propval.u16 = adjust_u16(metadata->tracknumber, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006495 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006496 case PTP_OPC_OriginalReleaseDate:
6497 if (metadata->date == NULL)
6498 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006499 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006500 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006501 prop->property = PTP_OPC_OriginalReleaseDate;
6502 prop->datatype = PTP_DTC_STR;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006503 prop->propval.str = strdup(metadata->date);
Linus Walleije7df6532007-03-23 08:20:06 +00006504 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006505 case PTP_OPC_SampleRate:
Linus Walleija6d0d482007-10-31 08:54:56 +00006506 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006507 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006508 prop->property = PTP_OPC_SampleRate;
6509 prop->datatype = PTP_DTC_UINT32;
Linus Walleij29559562007-08-22 21:38:04 +00006510 prop->propval.u32 = adjust_u32(metadata->samplerate, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006511 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006512 case PTP_OPC_NumberOfChannels:
Linus Walleija6d0d482007-10-31 08:54:56 +00006513 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006514 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006515 prop->property = PTP_OPC_NumberOfChannels;
6516 prop->datatype = PTP_DTC_UINT16;
Linus Walleij29559562007-08-22 21:38:04 +00006517 prop->propval.u16 = adjust_u16(metadata->nochannels, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006518 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006519 case PTP_OPC_AudioWAVECodec:
Linus Walleija6d0d482007-10-31 08:54:56 +00006520 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006521 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006522 prop->property = PTP_OPC_AudioWAVECodec;
6523 prop->datatype = PTP_DTC_UINT32;
Linus Walleij29559562007-08-22 21:38:04 +00006524 prop->propval.u32 = adjust_u32(metadata->wavecodec, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006525 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006526 case PTP_OPC_AudioBitRate:
Linus Walleija6d0d482007-10-31 08:54:56 +00006527 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006528 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006529 prop->property = PTP_OPC_AudioBitRate;
6530 prop->datatype = PTP_DTC_UINT32;
Linus Walleij29559562007-08-22 21:38:04 +00006531 prop->propval.u32 = adjust_u32(metadata->bitrate, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006532 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006533 case PTP_OPC_BitRateType:
Linus Walleija6d0d482007-10-31 08:54:56 +00006534 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006535 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006536 prop->property = PTP_OPC_BitRateType;
6537 prop->datatype = PTP_DTC_UINT16;
Linus Walleij29559562007-08-22 21:38:04 +00006538 prop->propval.u16 = adjust_u16(metadata->bitratetype, &opd);
Linus Walleije7df6532007-03-23 08:20:06 +00006539 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006540 case PTP_OPC_Rating:
6541 // TODO: shall this be set for rating 0?
6542 if (metadata->rating == 0)
6543 break;
Linus Walleija6d0d482007-10-31 08:54:56 +00006544 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006545 prop->ObjectHandle = metadata->item_id;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006546 prop->property = PTP_OPC_Rating;
6547 prop->datatype = PTP_DTC_UINT16;
Linus Walleij29559562007-08-22 21:38:04 +00006548 prop->propval.u16 = adjust_u16(metadata->rating, &opd);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006549 break;
6550 case PTP_OPC_UseCount:
Linus Walleija6d0d482007-10-31 08:54:56 +00006551 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006552 prop->ObjectHandle = metadata->item_id;
Linus Walleije7df6532007-03-23 08:20:06 +00006553 prop->property = PTP_OPC_UseCount;
6554 prop->datatype = PTP_DTC_UINT32;
Linus Walleij29559562007-08-22 21:38:04 +00006555 prop->propval.u32 = adjust_u32(metadata->usecount, &opd);
Linus Walleij7783b9b2007-09-22 22:10:44 +00006556 break;
6557 case PTP_OPC_DateModified:
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00006558 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
Linus Walleij37588142008-10-16 19:10:47 +00006559 // Tag with current time if that is supported
6560 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
6561 prop->ObjectHandle = metadata->item_id;
6562 prop->property = PTP_OPC_DateModified;
6563 prop->datatype = PTP_DTC_STR;
6564 prop->propval.str = get_iso8601_stamp();
6565 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00006566 break;
Linus Walleijf9267e92007-10-15 21:07:37 +00006567 default:
6568 break;
Linus Walleije7df6532007-03-23 08:20:06 +00006569 }
Linus Walleij304433d2007-02-26 08:02:47 +00006570 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006571 ptp_free_objectpropdesc(&opd);
Richard Low15731fe2007-03-22 20:27:20 +00006572 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006573
Richard Low15731fe2007-03-22 20:27:20 +00006574 // NOTE: File size is not updated, this should not change anyway.
6575 // neither will we change the filename.
Linus Walleij08a5fe12009-09-08 21:28:58 +00006576
Linus Walleij1e9a0332007-09-12 19:35:56 +00006577 ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006578
Linus Walleija6d0d482007-10-31 08:54:56 +00006579 ptp_destroy_object_prop_list(props, nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006580
Richard Low15731fe2007-03-22 20:27:20 +00006581 if (ret != PTP_RC_OK) {
6582 // TODO: return error of which property we couldn't set
Linus Walleije7df6532007-03-23 08:20:06 +00006583 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6584 "could not set object property list.");
Richard Low016e3732007-09-23 14:53:46 +00006585 free(properties);
Linus Walleij304433d2007-02-26 08:02:47 +00006586 return -1;
6587 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00006588
Richard Low15731fe2007-03-22 20:27:20 +00006589 } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
Linus Walleij00cf0642006-07-26 20:40:59 +00006590 for (i=0;i<propcnt;i++) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006591 PTPObjectPropDesc opd;
Linus Walleij08a5fe12009-09-08 21:28:58 +00006592
Linus Walleij1e9a0332007-09-12 19:35:56 +00006593 ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
Linus Walleijdbcc8242007-08-05 22:31:26 +00006594 if (ret != PTP_RC_OK) {
6595 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6596 "could not get property description.");
6597 } else if (opd.GetSet) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00006598 switch (properties[i]) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00006599 case PTP_OPC_Name:
6600 // Update title
6601 ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title);
Linus Walleije7df6532007-03-23 08:20:06 +00006602 if (ret != 0) {
6603 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
Linus Walleijdbcc8242007-08-05 22:31:26 +00006604 "could not set track title.");
Linus Walleije7df6532007-03-23 08:20:06 +00006605 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006606 break;
6607 case PTP_OPC_AlbumName:
6608 // Update album
6609 ret = set_object_string(device, metadata->item_id, PTP_OPC_AlbumName, metadata->album);
Linus Walleije7df6532007-03-23 08:20:06 +00006610 if (ret != 0) {
6611 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
Linus Walleijdbcc8242007-08-05 22:31:26 +00006612 "could not set track album name.");
Linus Walleije7df6532007-03-23 08:20:06 +00006613 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006614 break;
6615 case PTP_OPC_Artist:
6616 // Update artist
6617 ret = set_object_string(device, metadata->item_id, PTP_OPC_Artist, metadata->artist);
Linus Walleije7df6532007-03-23 08:20:06 +00006618 if (ret != 0) {
6619 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
Linus Walleijdbcc8242007-08-05 22:31:26 +00006620 "could not set track artist name.");
Linus Walleije7df6532007-03-23 08:20:06 +00006621 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006622 break;
Linus Walleij31b74292008-05-02 23:29:06 +00006623 case PTP_OPC_Composer:
6624 // Update composer
6625 ret = set_object_string(device, metadata->item_id, PTP_OPC_Composer, metadata->composer);
6626 if (ret != 0) {
6627 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6628 "could not set track composer name.");
6629 }
6630 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00006631 case PTP_OPC_Genre:
Linus Walleijb7e8f972010-01-19 00:38:11 +00006632 // Update genre (but only if valid)
6633 if (metadata->genre) {
6634 ret = set_object_string(device, metadata->item_id, PTP_OPC_Genre, metadata->genre);
6635 if (ret != 0) {
6636 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
6637 "could not set genre.");
6638 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006639 }
6640 break;
6641 case PTP_OPC_Duration:
6642 // Update duration
6643 if (metadata->duration != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006644 ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, adjust_u32(metadata->duration, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006645 if (ret != 0) {
6646 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6647 "could not set track duration.");
6648 }
6649 }
6650 break;
6651 case PTP_OPC_Track:
6652 // Update track number.
6653 if (metadata->tracknumber != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006654 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, adjust_u16(metadata->tracknumber, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006655 if (ret != 0) {
6656 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6657 "could not set track tracknumber.");
6658 }
6659 }
6660 break;
6661 case PTP_OPC_OriginalReleaseDate:
6662 // Update creation datetime
Linus Walleijb7e8f972010-01-19 00:38:11 +00006663 // The date can be zero, but some devices do not support setting zero
6664 // dates (and it seems that a zero date should never be set anyway)
6665 if (metadata->date) {
6666 ret = set_object_string(device, metadata->item_id, PTP_OPC_OriginalReleaseDate, metadata->date);
6667 if (ret != 0) {
6668 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6669 "could not set track release date.");
6670 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006671 }
6672 break;
6673 // These are, well not so important.
6674 case PTP_OPC_SampleRate:
6675 // Update sample rate
6676 if (metadata->samplerate != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006677 ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, adjust_u32(metadata->samplerate, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006678 if (ret != 0) {
6679 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6680 "could not set samplerate.");
6681 }
6682 }
6683 break;
6684 case PTP_OPC_NumberOfChannels:
6685 // Update number of channels
6686 if (metadata->nochannels != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006687 ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, adjust_u16(metadata->nochannels, &opd));
Linus Walleije7df6532007-03-23 08:20:06 +00006688 if (ret != 0) {
6689 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6690 "could not set number of channels.");
6691 }
6692 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006693 break;
6694 case PTP_OPC_AudioWAVECodec:
6695 // Update WAVE codec
6696 if (metadata->wavecodec != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006697 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, adjust_u32(metadata->wavecodec, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006698 if (ret != 0) {
6699 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6700 "could not set WAVE codec.");
6701 }
6702 }
6703 break;
6704 case PTP_OPC_AudioBitRate:
6705 // Update bitrate
6706 if (metadata->bitrate != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006707 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, adjust_u32(metadata->bitrate, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006708 if (ret != 0) {
6709 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6710 "could not set bitrate.");
6711 }
6712 }
6713 break;
6714 case PTP_OPC_BitRateType:
6715 // Update bitrate type
6716 if (metadata->bitratetype != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006717 ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, adjust_u16(metadata->bitratetype, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006718 if (ret != 0) {
6719 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6720 "could not set bitratetype.");
6721 }
6722 }
6723 break;
6724 case PTP_OPC_Rating:
6725 // Update user rating
6726 // TODO: shall this be set for rating 0?
6727 if (metadata->rating != 0) {
Linus Walleij29559562007-08-22 21:38:04 +00006728 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, adjust_u16(metadata->rating, &opd));
Linus Walleijdbcc8242007-08-05 22:31:26 +00006729 if (ret != 0) {
6730 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6731 "could not set user rating.");
6732 }
6733 }
6734 break;
6735 case PTP_OPC_UseCount:
6736 // Update use count, set even to zero if desired.
Linus Walleij29559562007-08-22 21:38:04 +00006737 ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, adjust_u32(metadata->usecount, &opd));
Linus Walleije7df6532007-03-23 08:20:06 +00006738 if (ret != 0) {
6739 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
Linus Walleije7df6532007-03-23 08:20:06 +00006740 "could not set use count.");
Linus Walleijdbcc8242007-08-05 22:31:26 +00006741 }
6742 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00006743 case PTP_OPC_DateModified:
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00006744 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
Linus Walleij7783b9b2007-09-22 22:10:44 +00006745 // Update modification time if supported
6746 char *tmpstamp = get_iso8601_stamp();
6747 ret = set_object_string(device, metadata->item_id, PTP_OPC_DateModified, tmpstamp);
6748 if (ret != 0) {
6749 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6750 "could not set modification date.");
6751 }
6752 free(tmpstamp);
6753 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00006754 break;
6755
Linus Walleijdbcc8242007-08-05 22:31:26 +00006756 // NOTE: File size is not updated, this should not change anyway.
6757 // neither will we change the filename.
6758 default:
6759 break;
Linus Walleije7df6532007-03-23 08:20:06 +00006760 }
Linus Walleij00cf0642006-07-26 20:40:59 +00006761 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00006762 ptp_free_objectpropdesc(&opd);
Linus Walleija4982732006-02-24 15:46:02 +00006763 }
Richard Low15731fe2007-03-22 20:27:20 +00006764 } else {
6765 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
6766 "Your device doesn't seem to support any known way of setting metadata.");
Richard Low016e3732007-09-23 14:53:46 +00006767 free(properties);
Richard Low15731fe2007-03-22 20:27:20 +00006768 return -1;
Linus Walleij17e39f72006-02-23 15:54:28 +00006769 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00006770
tsaarnia3eb60a2007-07-06 17:41:30 +00006771 // update cached object properties if metadata cache exists
Linus Walleij7752b952007-10-19 20:11:46 +00006772 update_metadata_cache(device, metadata->item_id);
Linus Walleij338ade42007-07-03 20:44:08 +00006773
Richard Low016e3732007-09-23 14:53:46 +00006774 free(properties);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006775
Linus Walleijf1b02f22006-12-06 09:03:23 +00006776 return 0;
Linus Walleij394bbbe2006-02-22 16:10:53 +00006777}
Linus Walleij95698cd2006-02-24 10:40:40 +00006778
6779/**
Linus Walleij399807b2008-01-27 22:05:12 +00006780 * This function deletes a single file, track, playlist, folder or
6781 * any other object off the MTP device, identified by the object ID.
6782 *
6783 * If you delete a folder, there is no guarantee that the device will
6784 * really delete all the files that were in that folder, rather it is
6785 * expected that they will not be deleted, and will turn up in object
6786 * listings with parent set to a non-existant object ID. The safe way
Linus Walleij08a5fe12009-09-08 21:28:58 +00006787 * to do this is to recursively delete all files (and folders) contained
Linus Walleij399807b2008-01-27 22:05:12 +00006788 * in the folder, then the folder itself.
6789 *
6790 * @param device a pointer to the device to delete the object from.
6791 * @param object_id the object to delete.
Linus Walleij95698cd2006-02-24 10:40:40 +00006792 * @return 0 on success, any other value means failure.
6793 */
mopoke96143402006-10-30 04:37:26 +00006794int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
Linus Walleij438bd7f2006-06-08 11:35:44 +00006795 uint32_t object_id)
Linus Walleij95698cd2006-02-24 10:40:40 +00006796{
Linus Walleij438bd7f2006-06-08 11:35:44 +00006797 uint16_t ret;
6798 PTPParams *params = (PTPParams *) device->params;
6799
6800 ret = ptp_deleteobject(params, object_id, 0);
6801 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00006802 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Delete_Object(): could not delete object.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00006803 return -1;
6804 }
tsaarnia3eb60a2007-07-06 17:41:30 +00006805
Linus Walleij438bd7f2006-06-08 11:35:44 +00006806 return 0;
Linus Walleij95698cd2006-02-24 10:40:40 +00006807}
Linus Walleij9c6ca022006-04-21 10:24:15 +00006808
Linus Walleij9c6ca022006-04-21 10:24:15 +00006809/**
Linus Walleij8d8c4352008-09-23 23:04:16 +00006810 * Internal function to update an object filename property.
Linus Walleijde8193f2008-01-27 14:55:31 +00006811 */
Linus Walleij8d8c4352008-09-23 23:04:16 +00006812static int set_object_filename(LIBMTP_mtpdevice_t *device,
6813 uint32_t object_id, uint16_t ptp_type,
6814 const char **newname_ptr)
Linus Walleijde8193f2008-01-27 14:55:31 +00006815{
6816 PTPParams *params = (PTPParams *) device->params;
6817 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
6818 PTPObjectPropDesc opd;
6819 uint16_t ret;
Linus Walleij8d8c4352008-09-23 23:04:16 +00006820 char *newname;
Linus Walleij8be2c032008-01-28 21:23:47 +00006821
6822 // See if we can modify the filename on this kind of files.
6823 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_ObjectFileName, ptp_type, &opd);
Linus Walleijde8193f2008-01-27 14:55:31 +00006824 if (ret != PTP_RC_OK) {
Linus Walleij8d8c4352008-09-23 23:04:16 +00006825 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
Linus Walleij8be2c032008-01-28 21:23:47 +00006826 "could not get property description.");
Linus Walleijde8193f2008-01-27 14:55:31 +00006827 return -1;
6828 }
6829
6830 if (!opd.GetSet) {
6831 ptp_free_objectpropdesc(&opd);
Linus Walleij8d8c4352008-09-23 23:04:16 +00006832 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6833 " property is not settable.");
Linus Walleij399807b2008-01-27 22:05:12 +00006834 // TODO: we COULD actually upload/download the object here, if we feel
6835 // like wasting time for the user.
Linus Walleijde8193f2008-01-27 14:55:31 +00006836 return -1;
6837 }
6838
Linus Walleij8d8c4352008-09-23 23:04:16 +00006839 newname = strdup(*newname_ptr);
6840
Linus Walleijfec4d562008-06-01 22:30:36 +00006841 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijde8193f2008-01-27 14:55:31 +00006842 strip_7bit_from_utf8(newname);
6843 }
6844
6845 if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
Linus Walleijfec4d562008-06-01 22:30:36 +00006846 !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
Linus Walleijde8193f2008-01-27 14:55:31 +00006847 MTPProperties *props = NULL;
6848 MTPProperties *prop = NULL;
6849 int nrofprops = 0;
Linus Walleij8d8c4352008-09-23 23:04:16 +00006850
Linus Walleijde8193f2008-01-27 14:55:31 +00006851 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00006852 prop->ObjectHandle = object_id;
Linus Walleijde8193f2008-01-27 14:55:31 +00006853 prop->property = PTP_OPC_ObjectFileName;
6854 prop->datatype = PTP_DTC_STR;
Linus Walleij8d8c4352008-09-23 23:04:16 +00006855 prop->propval.str = newname;
6856
Linus Walleijde8193f2008-01-27 14:55:31 +00006857 ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
Linus Walleij8d8c4352008-09-23 23:04:16 +00006858
Linus Walleijde8193f2008-01-27 14:55:31 +00006859 ptp_destroy_object_prop_list(props, nrofprops);
Linus Walleij8d8c4352008-09-23 23:04:16 +00006860
Linus Walleijde8193f2008-01-27 14:55:31 +00006861 if (ret != PTP_RC_OK) {
Linus Walleij8d8c4352008-09-23 23:04:16 +00006862 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6863 " could not set object property list.");
6864 ptp_free_objectpropdesc(&opd);
6865 return -1;
Linus Walleijde8193f2008-01-27 14:55:31 +00006866 }
6867 } else if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjectPropValue)) {
6868 ret = set_object_string(device, object_id, PTP_OPC_ObjectFileName, newname);
6869 if (ret != 0) {
Linus Walleij8d8c4352008-09-23 23:04:16 +00006870 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6871 " could not set object filename.");
Linus Walleijde8193f2008-01-27 14:55:31 +00006872 ptp_free_objectpropdesc(&opd);
6873 return -1;
6874 }
6875 } else {
Linus Walleij8d8c4352008-09-23 23:04:16 +00006876 free(newname);
6877 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
6878 " your device doesn't seem to support any known way of setting metadata.");
Linus Walleijde8193f2008-01-27 14:55:31 +00006879 ptp_free_objectpropdesc(&opd);
6880 return -1;
6881 }
Linus Walleij8d8c4352008-09-23 23:04:16 +00006882
Linus Walleijde8193f2008-01-27 14:55:31 +00006883 ptp_free_objectpropdesc(&opd);
Linus Walleij8d8c4352008-09-23 23:04:16 +00006884
Linus Walleijde8193f2008-01-27 14:55:31 +00006885 // update cached object properties if metadata cache exists
6886 update_metadata_cache(device, object_id);
Linus Walleij8d8c4352008-09-23 23:04:16 +00006887
Linus Walleijde8193f2008-01-27 14:55:31 +00006888 return 0;
6889}
Linus Walleij399807b2008-01-27 22:05:12 +00006890
Linus Walleijde8193f2008-01-27 14:55:31 +00006891/**
Linus Walleij8d8c4352008-09-23 23:04:16 +00006892 * This function renames a single file.
6893 * This simply means that the PTP_OPC_ObjectFileName property
6894 * is updated, if this is supported by the device.
6895 *
6896 * @param device a pointer to the device that contains the file.
6897 * @param file the file metadata of the file to rename.
6898 * On success, the filename member is updated. Be aware, that
6899 * this name can be different than newname depending of device restrictions.
6900 * @param newname the new filename for this object.
6901 * @return 0 on success, any other value means failure.
6902 */
6903int LIBMTP_Set_File_Name(LIBMTP_mtpdevice_t *device,
6904 LIBMTP_file_t *file, const char *newname)
6905{
6906 int ret;
6907
6908 ret = set_object_filename(device, file->item_id,
6909 map_libmtp_type_to_ptp_type(file->filetype),
6910 &newname);
6911
6912 if (ret != 0) {
6913 return ret;
6914 }
6915
6916 free(file->filename);
6917 file->filename = strdup(newname);
6918 return ret;
6919}
6920
6921/**
6922 * This function renames a single folder.
6923 * This simply means that the PTP_OPC_ObjectFileName property
6924 * is updated, if this is supported by the device.
6925 *
6926 * @param device a pointer to the device that contains the file.
6927 * @param folder the folder metadata of the folder to rename.
6928 * On success, the name member is updated. Be aware, that
6929 * this name can be different than newname depending of device restrictions.
6930 * @param newname the new name for this object.
6931 * @return 0 on success, any other value means failure.
6932 */
6933int LIBMTP_Set_Folder_Name(LIBMTP_mtpdevice_t *device,
6934 LIBMTP_folder_t *folder, const char* newname)
6935{
6936 int ret;
6937
6938 ret = set_object_filename(device, folder->folder_id,
Linus Walleijfb7212f2008-09-24 20:30:13 +00006939 PTP_OFC_Association,
Linus Walleij8d8c4352008-09-23 23:04:16 +00006940 &newname);
6941
6942 if (ret != 0) {
6943 return ret;
6944 }
6945
6946 free(folder->name);
6947 folder->name = strdup(newname);
6948 return ret;
6949}
6950
6951/**
6952 * This function renames a single track.
6953 * This simply means that the PTP_OPC_ObjectFileName property
6954 * is updated, if this is supported by the device.
6955 *
6956 * @param device a pointer to the device that contains the file.
6957 * @param track the track metadata of the track to rename.
6958 * On success, the filename member is updated. Be aware, that
6959 * this name can be different than newname depending of device restrictions.
6960 * @param newname the new filename for this object.
6961 * @return 0 on success, any other value means failure.
6962 */
6963int LIBMTP_Set_Track_Name(LIBMTP_mtpdevice_t *device,
6964 LIBMTP_track_t *track, const char* newname)
6965{
6966 int ret;
6967
6968 ret = set_object_filename(device, track->item_id,
6969 map_libmtp_type_to_ptp_type(track->filetype),
6970 &newname);
6971
6972 if (ret != 0) {
6973 return ret;
6974 }
6975
6976 free(track->filename);
6977 track->filename = strdup(newname);
6978 return ret;
6979}
6980
6981/**
Linus Walleij12bbf312009-01-26 21:46:51 +00006982 * This function renames a single playlist object file holder.
6983 * This simply means that the <code>PTP_OPC_ObjectFileName</code>
6984 * property is updated, if this is supported by the device.
6985 * The playlist filename should nominally end with an extension
6986 * like ".pla".
6987 *
6988 * NOTE: if you want to change the metadata the device display
6989 * about a playlist you must <i>not</i> use this function,
6990 * use <code>LIBMTP_Update_Playlist()</code> instead!
Linus Walleij8d8c4352008-09-23 23:04:16 +00006991 *
6992 * @param device a pointer to the device that contains the file.
6993 * @param playlist the playlist metadata of the playlist to rename.
6994 * On success, the name member is updated. Be aware, that
6995 * this name can be different than newname depending of device restrictions.
6996 * @param newname the new name for this object.
6997 * @return 0 on success, any other value means failure.
Linus Walleij12bbf312009-01-26 21:46:51 +00006998 * @see LIBMTP_Update_Playlist()
Linus Walleij8d8c4352008-09-23 23:04:16 +00006999 */
7000int LIBMTP_Set_Playlist_Name(LIBMTP_mtpdevice_t *device,
7001 LIBMTP_playlist_t *playlist, const char* newname)
7002{
7003 int ret;
7004
7005 ret = set_object_filename(device, playlist->playlist_id,
7006 PTP_OFC_MTP_AbstractAudioVideoPlaylist,
7007 &newname);
7008
7009 if (ret != 0) {
7010 return ret;
7011 }
7012
7013 free(playlist->name);
7014 playlist->name = strdup(newname);
7015 return ret;
7016}
7017
7018/**
7019 * This function renames a single album.
Linus Walleij12bbf312009-01-26 21:46:51 +00007020 * This simply means that the <code>PTP_OPC_ObjectFileName</code>
7021 * property is updated, if this is supported by the device.
7022 * The album filename should nominally end with an extension
7023 * like ".alb".
7024 *
7025 * NOTE: if you want to change the metadata the device display
7026 * about a playlist you must <i>not</i> use this function,
7027 * use <code>LIBMTP_Update_Album()</code> instead!
Linus Walleij8d8c4352008-09-23 23:04:16 +00007028 *
7029 * @param device a pointer to the device that contains the file.
7030 * @param album the album metadata of the album to rename.
7031 * On success, the name member is updated. Be aware, that
7032 * this name can be different than newname depending of device restrictions.
7033 * @param newname the new name for this object.
7034 * @return 0 on success, any other value means failure.
Linus Walleij12bbf312009-01-26 21:46:51 +00007035 * @see LIBMTP_Update_Album()
Linus Walleij8d8c4352008-09-23 23:04:16 +00007036 */
7037int LIBMTP_Set_Album_Name(LIBMTP_mtpdevice_t *device,
7038 LIBMTP_album_t *album, const char* newname)
7039{
7040 int ret;
7041
7042 ret = set_object_filename(device, album->album_id,
7043 PTP_OFC_MTP_AbstractAudioAlbum,
7044 &newname);
7045
7046 if (ret != 0) {
7047 return ret;
7048 }
7049
7050 free(album->name);
7051 album->name = strdup(newname);
7052 return ret;
7053}
7054
7055/**
7056 * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
7057 * NOT TO USE IT.
7058 *
7059 * @see LIBMTP_Set_File_Name()
7060 * @see LIBMTP_Set_Track_Name()
7061 * @see LIBMTP_Set_Folder_Name()
7062 * @see LIBMTP_Set_Playlist_Name()
7063 * @see LIBMTP_Set_Album_Name()
7064 */
7065int LIBMTP_Set_Object_Filename(LIBMTP_mtpdevice_t *device,
7066 uint32_t object_id, char* newname)
7067{
7068 int ret;
7069 LIBMTP_file_t *file;
7070
7071 file = LIBMTP_Get_Filemetadata(device, object_id);
Linus Walleijfb7212f2008-09-24 20:30:13 +00007072
7073 if (file == NULL) {
7074 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Set_Object_Filename(): "
7075 "could not get file metadata for target object.");
7076 return -1;
7077 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00007078
Linus Walleijfb7212f2008-09-24 20:30:13 +00007079 ret = set_object_filename(device, object_id, map_libmtp_type_to_ptp_type(file->filetype), (const char **) &newname);
Linus Walleij08a5fe12009-09-08 21:28:58 +00007080
Linus Walleij8d8c4352008-09-23 23:04:16 +00007081 free(file);
7082
7083 return ret;
7084}
7085
7086/**
Linus Walleij9c6ca022006-04-21 10:24:15 +00007087 * Helper function. This indicates if a track exists on the device
7088 * @param device a pointer to the device to get the track from.
7089 * @param id the track ID of the track to retrieve.
Linus Walleij070e9b42007-01-22 08:49:28 +00007090 * @return TRUE (!=0) if the track exists, FALSE (0) if not
Linus Walleij9c6ca022006-04-21 10:24:15 +00007091 */
7092int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *device,
7093 uint32_t const id)
7094{
Linus Walleij9c6ca022006-04-21 10:24:15 +00007095 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd4637502009-06-14 23:03:33 +00007096 uint16_t ret;
7097 PTPObject *ob;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007098
Linus Walleijd4637502009-06-14 23:03:33 +00007099 ret = ptp_object_want (params, id, 0, &ob);
7100 if (ret == PTP_RC_OK)
Linus Walleijf0bf4372007-07-01 21:47:38 +00007101 return -1;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007102 return 0;
7103}
7104
7105/**
7106 * This creates a new folder structure and allocates memory
7107 * for it. Notice that if you add strings to this structure they
7108 * will be freed by the corresponding <code>LIBMTP_folder_track_t</code>
7109 * operation later, so be careful of using strdup() when assigning
7110 * strings, e.g.:
7111 *
7112 * @return a pointer to the newly allocated folder structure.
7113 * @see LIBMTP_destroy_folder_t()
7114 */
7115LIBMTP_folder_t *LIBMTP_new_folder_t(void)
7116{
7117 LIBMTP_folder_t *new = (LIBMTP_folder_t *) malloc(sizeof(LIBMTP_folder_t));
7118 if (new == NULL) {
7119 return NULL;
7120 }
7121 new->folder_id = 0;
7122 new->parent_id = 0;
Linus Walleijea68f1f2008-06-22 21:54:44 +00007123 new->storage_id = 0;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007124 new->name = NULL;
7125 new->sibling = NULL;
7126 new->child = NULL;
7127 return new;
7128}
7129
7130/**
Linus Walleij5573def2007-04-23 07:04:18 +00007131 * This recursively deletes the memory for a folder structure.
7132 * This shall typically be called on a top-level folder list to
7133 * detsroy the entire folder tree.
Linus Walleij9c6ca022006-04-21 10:24:15 +00007134 *
7135 * @param folder folder structure to destroy
7136 * @see LIBMTP_new_folder_t()
7137 */
7138void LIBMTP_destroy_folder_t(LIBMTP_folder_t *folder)
7139{
7140
7141 if(folder == NULL) {
7142 return;
7143 }
7144
7145 //Destroy from the bottom up
7146 if(folder->child != NULL) {
7147 LIBMTP_destroy_folder_t(folder->child);
7148 }
7149
7150 if(folder->sibling != NULL) {
7151 LIBMTP_destroy_folder_t(folder->sibling);
7152 }
7153
7154 if(folder->name != NULL) {
7155 free(folder->name);
7156 }
7157
7158 free(folder);
7159}
7160
7161/**
7162 * Helper function. Returns a folder structure for a
7163 * specified id.
7164 *
7165 * @param folderlist list of folders to search
7166 * @id id of folder to look for
7167 * @return a folder or NULL if not found
7168 */
7169LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t *folderlist, uint32_t id)
7170{
7171 LIBMTP_folder_t *ret = NULL;
mopoke96143402006-10-30 04:37:26 +00007172
Linus Walleij9c6ca022006-04-21 10:24:15 +00007173 if(folderlist == NULL) {
7174 return NULL;
7175 }
mopoke96143402006-10-30 04:37:26 +00007176
Linus Walleij9c6ca022006-04-21 10:24:15 +00007177 if(folderlist->folder_id == id) {
7178 return folderlist;
7179 }
mopoke96143402006-10-30 04:37:26 +00007180
Linus Walleij9c6ca022006-04-21 10:24:15 +00007181 if(folderlist->sibling) {
7182 ret = LIBMTP_Find_Folder(folderlist->sibling, id);
7183 }
mopoke96143402006-10-30 04:37:26 +00007184
Linus Walleij9c6ca022006-04-21 10:24:15 +00007185 if(folderlist->child && ret == NULL) {
7186 ret = LIBMTP_Find_Folder(folderlist->child, id);
7187 }
mopoke96143402006-10-30 04:37:26 +00007188
Linus Walleij9c6ca022006-04-21 10:24:15 +00007189 return ret;
7190}
7191
7192/**
Linus Walleij4d0deea2007-10-19 21:27:48 +00007193 * Function used to recursively get subfolders from params.
Linus Walleij9c6ca022006-04-21 10:24:15 +00007194 */
Linus Walleija89a7942008-12-14 23:19:34 +00007195static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent)
Linus Walleij9c6ca022006-04-21 10:24:15 +00007196{
Linus Walleij4fc19172010-01-19 00:10:26 +00007197 LIBMTP_folder_t *retfolders = NULL;
7198 LIBMTP_folder_t *children, *iter, *curr;
mopoke96143402006-10-30 04:37:26 +00007199
Linus Walleija89a7942008-12-14 23:19:34 +00007200 iter = list->sibling;
7201 while(iter != list) {
7202 if (iter->parent_id != parent) {
7203 iter = iter->sibling;
7204 continue;
7205 }
7206
7207 /* We know that iter is a child of 'parent', therefore we can safely
7208 * hold on to 'iter' locally since no one else will steal it
7209 * from the 'list' as we recurse. */
7210 children = get_subfolders_for_folder(list, iter->folder_id);
7211
7212 curr = iter;
7213 iter = iter->sibling;
7214
7215 // Remove curr from the list.
7216 curr->child->sibling = curr->sibling;
7217 curr->sibling->child = curr->child;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007218
Linus Walleija89a7942008-12-14 23:19:34 +00007219 // Attach the children to curr.
7220 curr->child = children;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007221
Linus Walleija89a7942008-12-14 23:19:34 +00007222 // Put this folder into the list of siblings.
7223 curr->sibling = retfolders;
7224 retfolders = curr;
7225 }
7226
7227 return retfolders;
7228}
7229
7230/**
7231 * This returns a list of all folders available
7232 * on the current MTP device.
7233 *
7234 * @param device a pointer to the device to get the folder listing for.
Linus Walleij1e4dfea2011-03-05 22:50:39 +01007235 * @param storage a storage ID to get the folder list from
Linus Walleija89a7942008-12-14 23:19:34 +00007236 * @return a list of folders
7237 */
Linus Walleij1e4dfea2011-03-05 22:50:39 +01007238 LIBMTP_folder_t *LIBMTP_Get_Folder_List_For_Storage(LIBMTP_mtpdevice_t *device,
7239 uint32_t const storage)
Linus Walleija89a7942008-12-14 23:19:34 +00007240{
7241 PTPParams *params = (PTPParams *) device->params;
Linus Walleijcdc12092010-09-18 11:28:56 +00007242 LIBMTP_folder_t head, *rv;
Linus Walleija89a7942008-12-14 23:19:34 +00007243 int i;
7244
7245 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00007246 if (params->nrofobjects == 0) {
Linus Walleija89a7942008-12-14 23:19:34 +00007247 flush_handles(device);
7248 }
7249
Linus Walleij4ab47b22008-12-14 23:24:26 +00007250 /*
7251 * This creates a temporary list of the folders, this is in a
7252 * reverse order and uses the Folder pointers that are already
7253 * in the Folder structure. From this we can then build up the
7254 * folder hierarchy with only looking at this temporary list,
7255 * and removing the folders from this temporary list as we go.
7256 * This significantly reduces the number of operations that we
7257 * have to do in building the folder hierarchy. Also since the
7258 * temp list is in reverse order, when we prepend to the sibling
7259 * list things are in the same order as they were originally
7260 * in the handle list.
7261 */
Linus Walleija89a7942008-12-14 23:19:34 +00007262 head.sibling = &head;
7263 head.child = &head;
Linus Walleijd4637502009-06-14 23:03:33 +00007264 for (i = 0; i < params->nrofobjects; i++) {
Linus Walleij9c6ca022006-04-21 10:24:15 +00007265 LIBMTP_folder_t *folder;
Linus Walleijd4637502009-06-14 23:03:33 +00007266 PTPObject *ob;
Linus Walleija89a7942008-12-14 23:19:34 +00007267
Linus Walleijd4637502009-06-14 23:03:33 +00007268 ob = &params->objects[i];
7269 if (ob->oi.ObjectFormat != PTP_OFC_Association) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00007270 continue;
7271 }
Linus Walleij1e4dfea2011-03-05 22:50:39 +01007272
7273 if (storage != PTP_GOH_ALL_STORAGE && storage != ob->oi.StorageID) {
7274 continue;
7275 }
7276
Linus Walleij4ab47b22008-12-14 23:24:26 +00007277 /*
7278 * Do we know how to handle these? They are part
7279 * of the MTP 1.0 specification paragraph 3.6.4.
Linus Walleij08a5fe12009-09-08 21:28:58 +00007280 * For AssociationDesc 0x00000001U ptp_mtp_getobjectreferences()
7281 * should be called on these to get the contained objects, but
Linus Walleij4ab47b22008-12-14 23:24:26 +00007282 * we basically don't care. Hopefully parent_id is maintained for all
7283 * children, because we rely on that instead.
7284 */
Linus Walleijd4637502009-06-14 23:03:33 +00007285 if (ob->oi.AssociationDesc != 0x00000000U) {
nicklas79daadbf22009-09-28 18:19:34 +00007286 LIBMTP_INFO("MTP extended association type 0x%08x encountered\n", ob->oi.AssociationDesc);
Linus Walleijd71d0b32008-09-22 08:21:03 +00007287 }
7288
Linus Walleij4d0deea2007-10-19 21:27:48 +00007289 // Create a folder struct...
Linus Walleijf0bf4372007-07-01 21:47:38 +00007290 folder = LIBMTP_new_folder_t();
Linus Walleij4d0deea2007-10-19 21:27:48 +00007291 if (folder == NULL) {
7292 // malloc failure or so.
7293 return NULL;
7294 }
Linus Walleijd4637502009-06-14 23:03:33 +00007295 folder->folder_id = ob->oid;
7296 folder->parent_id = ob->oi.ParentObject;
7297 folder->storage_id = ob->oi.StorageID;
7298 folder->name = (ob->oi.Filename) ? (char *)strdup(ob->oi.Filename) : NULL;
Linus Walleijf0bf4372007-07-01 21:47:38 +00007299
Linus Walleija89a7942008-12-14 23:19:34 +00007300 // pretend sibling says next, and child says prev.
7301 folder->sibling = head.sibling;
7302 folder->child = &head;
7303 head.sibling->child = folder;
7304 head.sibling = folder;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007305 }
Linus Walleij4d0deea2007-10-19 21:27:48 +00007306
Linus Walleij1e4dfea2011-03-05 22:50:39 +01007307 // We begin at the given root folder and get them all recursively
Linus Walleij4fc19172010-01-19 00:10:26 +00007308 rv = get_subfolders_for_folder(&head, 0x00000000U);
7309
7310 // Some buggy devices may have some files in the "root folder"
7311 // 0xffffffff so if 0x00000000 didn't return any folders,
7312 // look for children of the root 0xffffffffU
7313 if (rv == NULL) {
7314 rv = get_subfolders_for_folder(&head, 0xffffffffU);
7315 if (rv != NULL)
7316 LIBMTP_ERROR("Device have files in \"root folder\" 0xffffffffU - "
7317 "this is a firmware bug (but continuing)\n");
7318 }
Linus Walleija89a7942008-12-14 23:19:34 +00007319
7320 // The temp list should be empty. Clean up any orphans just in case.
7321 while(head.sibling != &head) {
7322 LIBMTP_folder_t *curr = head.sibling;
7323
nicklas79daadbf22009-09-28 18:19:34 +00007324 LIBMTP_INFO("Orphan folder with ID: 0x%08x name: \"%s\" encountered.\n",
Linus Walleija89a7942008-12-14 23:19:34 +00007325 curr->folder_id,
7326 curr->name);
7327 curr->sibling->child = curr->child;
7328 curr->child->sibling = curr->sibling;
7329 curr->child = NULL;
7330 curr->sibling = NULL;
7331 LIBMTP_destroy_folder_t(curr);
7332 }
7333
7334 return rv;
Linus Walleij4d0deea2007-10-19 21:27:48 +00007335}
7336
7337/**
Linus Walleij1e4dfea2011-03-05 22:50:39 +01007338 * This returns a list of all folders available
7339 * on the current MTP device.
7340 *
7341 * @param device a pointer to the device to get the folder listing for.
7342 * @return a list of folders
7343 */
7344LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
7345{
7346 return LIBMTP_Get_Folder_List_For_Storage(device, PTP_GOH_ALL_STORAGE);
7347}
7348
7349/**
Linus Walleijc86afbd2006-05-04 19:05:24 +00007350 * This create a folder on the current MTP device. The PTP name
7351 * for a folder is "association". The PTP/MTP devices does not
7352 * have an internal "folder" concept really, it contains a flat
7353 * list of all files and some file are "associations" that other
7354 * files and folders may refer to as its "parent".
Linus Walleij9c6ca022006-04-21 10:24:15 +00007355 *
Linus Walleijc86afbd2006-05-04 19:05:24 +00007356 * @param device a pointer to the device to create the folder on.
Richard Lowf8cd3d72009-05-04 16:38:33 +00007357 * @param name the name of the new folder. Note this can be modified
7358 * if the device does not support all the characters in the
7359 * name.
Linus Walleijc86afbd2006-05-04 19:05:24 +00007360 * @param parent_id id of parent folder to add the new folder to,
Reverend Homer8e3af292015-11-22 19:58:01 +03007361 * or 0xFFFFFFFF to put it in the root directory.
Linus Walleijea68f1f2008-06-22 21:54:44 +00007362 * @param storage_id id of the storage to add this new folder to.
7363 * notice that you cannot mismatch storage id and parent id:
7364 * they must both be on the same storage! Pass in 0 if you
7365 * want to create this folder on the default storage.
Linus Walleijc86afbd2006-05-04 19:05:24 +00007366 * @return id to new folder or 0 if an error occured
Linus Walleij9c6ca022006-04-21 10:24:15 +00007367 */
Richard Lowf8cd3d72009-05-04 16:38:33 +00007368uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name,
Linus Walleijea68f1f2008-06-22 21:54:44 +00007369 uint32_t parent_id, uint32_t storage_id)
Linus Walleij9c6ca022006-04-21 10:24:15 +00007370{
7371 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd3b78572007-08-24 21:28:24 +00007372 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007373 uint32_t parenthandle = 0;
Linus Walleijea68f1f2008-06-22 21:54:44 +00007374 uint32_t store;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007375 PTPObjectInfo new_folder;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007376 uint16_t ret;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007377 uint32_t new_id = 0;
7378
Linus Walleijea68f1f2008-06-22 21:54:44 +00007379 if (storage_id == 0) {
Linus Walleijd71d0b32008-09-22 08:21:03 +00007380 // I'm just guessing that a folder may require 512 bytes
Linus Walleij88babc82014-06-02 21:32:14 +02007381 store = get_suggested_storage_id(device, 512, parent_id);
Linus Walleijea68f1f2008-06-22 21:54:44 +00007382 } else {
7383 store = storage_id;
7384 }
7385 parenthandle = parent_id;
7386
Richard Low59426042009-05-04 16:40:09 +00007387 memset(&new_folder, 0, sizeof(new_folder));
Richard Lowf8cd3d72009-05-04 16:38:33 +00007388 new_folder.Filename = name;
Linus Walleijfec4d562008-06-01 22:30:36 +00007389 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijd3b78572007-08-24 21:28:24 +00007390 strip_7bit_from_utf8(new_folder.Filename);
7391 }
Linus Walleij755c2462011-03-04 20:11:01 +01007392 new_folder.ObjectCompressedSize = 0;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007393 new_folder.ObjectFormat = PTP_OFC_Association;
Linus Walleijdbbfecc2008-11-05 09:52:47 +00007394 new_folder.ProtectionStatus = PTP_PS_NoProtection;
Linus Walleij753a46a2008-11-05 09:10:52 +00007395 new_folder.AssociationType = PTP_AT_GenericFolder;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007396 new_folder.ParentObject = parent_id;
Linus Walleijea68f1f2008-06-22 21:54:44 +00007397 new_folder.StorageID = store;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007398
Linus Walleij9c6ca022006-04-21 10:24:15 +00007399 // Create the object
Ignacio Martinez5ab67472012-03-22 21:59:22 +01007400 if (!(params->device_flags & DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST) &&
7401 ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
7402 MTPProperties *props = (MTPProperties*)calloc(2,sizeof(MTPProperties));
7403
7404 props[0].property = PTP_OPC_ObjectFileName;
7405 props[0].datatype = PTP_DTC_STR;
7406 props[0].propval.str = name;
7407
7408 props[1].property = PTP_OPC_Name;
7409 props[1].datatype = PTP_DTC_STR;
7410 props[1].propval.str = name;
7411
7412 ret = ptp_mtp_sendobjectproplist(params, &store, &parenthandle, &new_id, PTP_OFC_Association,
7413 0, props, 1);
7414 free(props);
7415 } else {
7416 ret = ptp_sendobjectinfo(params, &store, &parenthandle, &new_id, &new_folder);
7417 }
7418
Linus Walleij9c6ca022006-04-21 10:24:15 +00007419 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007420 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Create_Folder: Could not send object info.");
Linus Walleij99310d42006-11-01 08:29:39 +00007421 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007422 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00007423 }
Linus Walleijc86afbd2006-05-04 19:05:24 +00007424 return 0;
Linus Walleij9c6ca022006-04-21 10:24:15 +00007425 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00007426 // NOTE: don't destroy the new_folder objectinfo, because it is statically referencing
Richard Lowf8cd3d72009-05-04 16:38:33 +00007427 // several strings.
tsaarnia3eb60a2007-07-06 17:41:30 +00007428
Linus Walleija6d0d482007-10-31 08:54:56 +00007429 add_object_to_cache(device, new_id);
tsaarnia3eb60a2007-07-06 17:41:30 +00007430
Linus Walleij9c6ca022006-04-21 10:24:15 +00007431 return new_id;
7432}
raveloxd9a28642006-05-26 23:42:22 +00007433
Linus Walleij438bd7f2006-06-08 11:35:44 +00007434/**
7435 * This creates a new playlist metadata structure and allocates memory
7436 * for it. Notice that if you add strings to this structure they
7437 * will be freed by the corresponding <code>LIBMTP_destroy_playlist_t</code>
mopoke96143402006-10-30 04:37:26 +00007438 * operation later, so be careful of using strdup() when assigning
Linus Walleij438bd7f2006-06-08 11:35:44 +00007439 * strings, e.g.:
7440 *
7441 * <pre>
7442 * LIBMTP_playlist_t *pl = LIBMTP_new_playlist_t();
7443 * pl->name = strdup(str);
7444 * ....
7445 * LIBMTP_destroy_playlist_t(pl);
7446 * </pre>
7447 *
7448 * @return a pointer to the newly allocated metadata structure.
7449 * @see LIBMTP_destroy_playlist_t()
7450 */
7451LIBMTP_playlist_t *LIBMTP_new_playlist_t(void)
7452{
7453 LIBMTP_playlist_t *new = (LIBMTP_playlist_t *) malloc(sizeof(LIBMTP_playlist_t));
7454 if (new == NULL) {
7455 return NULL;
7456 }
7457 new->playlist_id = 0;
Linus Walleij5ce59db2008-03-12 21:22:58 +00007458 new->parent_id = 0;
Linus Walleijea68f1f2008-06-22 21:54:44 +00007459 new->storage_id = 0;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007460 new->name = NULL;
7461 new->tracks = NULL;
7462 new->no_tracks = 0;
7463 new->next = NULL;
7464 return new;
7465}
7466
7467/**
7468 * This destroys a playlist metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00007469 * used by it, including any strings. Never use a track metadata
Linus Walleij438bd7f2006-06-08 11:35:44 +00007470 * structure again after calling this function on it.
7471 * @param playlist the playlist metadata to destroy.
7472 * @see LIBMTP_new_playlist_t()
7473 */
7474void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *playlist)
7475{
7476 if (playlist == NULL) {
7477 return;
7478 }
7479 if (playlist->name != NULL)
7480 free(playlist->name);
7481 if (playlist->tracks != NULL)
7482 free(playlist->tracks);
7483 free(playlist);
7484 return;
7485}
7486
7487/**
7488 * This function returns a list of the playlists available on the
7489 * device. Typical usage:
7490 *
7491 * <pre>
7492 * </pre>
7493 *
7494 * @param device a pointer to the device to get the playlist listing from.
7495 * @return a playlist list on success, else NULL. If there are no playlists
7496 * on the device, NULL will be returned as well.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007497 * @see LIBMTP_Get_Playlist()
Linus Walleij438bd7f2006-06-08 11:35:44 +00007498 */
7499LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *device)
7500{
Linus Walleijf3c44052008-08-16 21:14:56 +00007501 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7502 const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
Linus Walleij438bd7f2006-06-08 11:35:44 +00007503 PTPParams *params = (PTPParams *) device->params;
7504 LIBMTP_playlist_t *retlists = NULL;
7505 LIBMTP_playlist_t *curlist = NULL;
7506 uint32_t i;
7507
7508 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00007509 if (params->nrofobjects == 0) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00007510 flush_handles(device);
7511 }
7512
Linus Walleijd4637502009-06-14 23:03:33 +00007513 for (i = 0; i < params->nrofobjects; i++) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00007514 LIBMTP_playlist_t *pl;
Linus Walleijd4637502009-06-14 23:03:33 +00007515 PTPObject *ob;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007516 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00007517
Linus Walleijd4637502009-06-14 23:03:33 +00007518 ob = &params->objects[i];
mopoke96143402006-10-30 04:37:26 +00007519
Linus Walleijf0bf4372007-07-01 21:47:38 +00007520 // Ignore stuff that isn't playlists
Linus Walleijf3c44052008-08-16 21:14:56 +00007521
7522 // For Samsung players we must look for the .spl extension explicitly since
7523 // playlists are not stored as playlist objects.
Linus Walleijd4637502009-06-14 23:03:33 +00007524 if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
Linus Walleijf3c44052008-08-16 21:14:56 +00007525 // Allocate a new playlist type
7526 pl = LIBMTP_new_playlist_t();
Linus Walleijd4637502009-06-14 23:03:33 +00007527 spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
Linus Walleijf3c44052008-08-16 21:14:56 +00007528 }
Linus Walleijd4637502009-06-14 23:03:33 +00007529 else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00007530 continue;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007531 }
Linus Walleijf3c44052008-08-16 21:14:56 +00007532 else {
7533 // Allocate a new playlist type
7534 pl = LIBMTP_new_playlist_t();
Linus Walleijf0bf4372007-07-01 21:47:38 +00007535
Linus Walleijf3296622008-09-04 20:53:56 +00007536 // Try to look up proper name, else use the oi->Filename field.
Linus Walleijd4637502009-06-14 23:03:33 +00007537 pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
Linus Walleijf3296622008-09-04 20:53:56 +00007538 if (pl->name == NULL) {
Linus Walleijd4637502009-06-14 23:03:33 +00007539 pl->name = strdup(ob->oi.Filename);
Linus Walleijf3296622008-09-04 20:53:56 +00007540 }
Linus Walleijd4637502009-06-14 23:03:33 +00007541 pl->playlist_id = ob->oid;
7542 pl->parent_id = ob->oi.ParentObject;
7543 pl->storage_id = ob->oi.StorageID;
Linus Walleijf0bf4372007-07-01 21:47:38 +00007544
Linus Walleijf3c44052008-08-16 21:14:56 +00007545 // Then get the track listing for this playlist
7546 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
7547 if (ret != PTP_RC_OK) {
Linus Walleij8d8c4352008-09-23 23:04:16 +00007548 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist_List(): "
7549 "could not get object references.");
Linus Walleijf3c44052008-08-16 21:14:56 +00007550 pl->tracks = NULL;
7551 pl->no_tracks = 0;
7552 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00007553 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00007554
Linus Walleijf0bf4372007-07-01 21:47:38 +00007555 // Add playlist to a list that will be returned afterwards.
7556 if (retlists == NULL) {
7557 retlists = pl;
7558 curlist = pl;
7559 } else {
7560 curlist->next = pl;
7561 curlist = pl;
7562 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00007563
Linus Walleijf0bf4372007-07-01 21:47:38 +00007564 // Call callback here if we decide to add that possibility...
mopoke96143402006-10-30 04:37:26 +00007565 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00007566 return retlists;
7567}
7568
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007569
7570/**
7571 * This function retrieves an individual playlist from the device.
7572 * @param device a pointer to the device to get the playlist from.
7573 * @param plid the unique ID of the playlist to retrieve.
7574 * @return a valid playlist metadata post or NULL on failure.
7575 * @see LIBMTP_Get_Playlist_List()
7576 */
7577LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *device, uint32_t const plid)
7578{
Linus Walleijf3c44052008-08-16 21:14:56 +00007579 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
7580 const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007581 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd4637502009-06-14 23:03:33 +00007582 PTPObject *ob;
7583 LIBMTP_playlist_t *pl;
7584 uint16_t ret;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007585
7586 // Get all the handles if we haven't already done that
Linus Walleijd4637502009-06-14 23:03:33 +00007587 if (params->nrofobjects == 0) {
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007588 flush_handles(device);
7589 }
7590
Linus Walleijd4637502009-06-14 23:03:33 +00007591 ret = ptp_object_want (params, plid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
7592 if (ret != PTP_RC_OK)
7593 return NULL;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007594
Linus Walleijd4637502009-06-14 23:03:33 +00007595 // For Samsung players we must look for the .spl extension explicitly since
7596 // playlists are not stored as playlist objects.
7597 if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00007598 // Allocate a new playlist type
7599 pl = LIBMTP_new_playlist_t();
Linus Walleijd4637502009-06-14 23:03:33 +00007600 spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
Linus Walleijf0bf4372007-07-01 21:47:38 +00007601 return pl;
mopoke96143402006-10-30 04:37:26 +00007602 }
Linus Walleijd4637502009-06-14 23:03:33 +00007603
7604 // Ignore stuff that isn't playlists
7605 else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
7606 return NULL;
7607 }
7608
7609 // Allocate a new playlist type
7610 pl = LIBMTP_new_playlist_t();
7611
7612 pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
7613 if (pl->name == NULL) {
7614 pl->name = strdup(ob->oi.Filename);
7615 }
7616 pl->playlist_id = ob->oid;
7617 pl->parent_id = ob->oi.ParentObject;
7618 pl->storage_id = ob->oi.StorageID;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007619
Linus Walleijd4637502009-06-14 23:03:33 +00007620 // Then get the track listing for this playlist
7621 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
7622 if (ret != PTP_RC_OK) {
7623 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist(): Could not get object references.");
7624 pl->tracks = NULL;
7625 pl->no_tracks = 0;
7626 }
7627
7628 return pl;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00007629}
7630
Linus Walleij31b74292008-05-02 23:29:06 +00007631/**
Linus Walleijb7426d12006-11-25 23:19:11 +00007632 * This function creates a new abstract list such as a playlist
7633 * or an album.
Linus Walleij08a5fe12009-09-08 21:28:58 +00007634 *
Linus Walleijb7426d12006-11-25 23:19:11 +00007635 * @param device a pointer to the device to create the new abstract list
7636 * on.
7637 * @param name the name of the new abstract list.
Linus Walleij7783b9b2007-09-22 22:10:44 +00007638 * @param artist the artist of the new abstract list or NULL.
7639 * @param genre the genre of the new abstract list or NULL.
Linus Walleijb7426d12006-11-25 23:19:11 +00007640 * @param parenthandle the handle of the parent or 0 for no parent
7641 * i.e. the root folder.
7642 * @param objectformat the abstract list type to create.
7643 * @param suffix the ".foo" (4 characters) suffix to use for the virtual
7644 * "file" created by this operation.
7645 * @param newid a pointer to a variable that will hold the new object
7646 * ID if this call is successful.
7647 * @param tracks an array of tracks to associate with this list.
7648 * @param no_tracks the number of tracks in the list.
Linus Walleij438bd7f2006-06-08 11:35:44 +00007649 * @return 0 on success, any other value means failure.
Linus Walleij438bd7f2006-06-08 11:35:44 +00007650 */
Linus Walleijb7426d12006-11-25 23:19:11 +00007651static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
7652 char const * const name,
Linus Walleijadce4a52007-04-23 07:28:11 +00007653 char const * const artist,
Linus Walleij31b74292008-05-02 23:29:06 +00007654 char const * const composer,
Linus Walleijadce4a52007-04-23 07:28:11 +00007655 char const * const genre,
Linus Walleijb7426d12006-11-25 23:19:11 +00007656 uint32_t const parenthandle,
Linus Walleijea68f1f2008-06-22 21:54:44 +00007657 uint32_t const storageid,
Linus Walleijb7426d12006-11-25 23:19:11 +00007658 uint16_t const objectformat,
7659 char const * const suffix,
7660 uint32_t * const newid,
7661 uint32_t const * const tracks,
7662 uint32_t const no_tracks)
7663
Linus Walleij438bd7f2006-06-08 11:35:44 +00007664{
Linus Walleijb7426d12006-11-25 23:19:11 +00007665 int i;
7666 int supported = 0;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007667 uint16_t ret;
Linus Walleij1e9a0332007-09-12 19:35:56 +00007668 uint16_t *properties = NULL;
rreardon508705f2006-11-19 21:27:22 +00007669 uint32_t propcnt = 0;
Linus Walleijea68f1f2008-06-22 21:54:44 +00007670 uint32_t store;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007671 uint32_t localph = parenthandle;
Linus Walleijb7426d12006-11-25 23:19:11 +00007672 uint8_t nonconsumable = 0x00U; /* By default it is consumable */
7673 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd3b78572007-08-24 21:28:24 +00007674 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007675 char fname[256];
Linus Walleij71c79292011-03-05 22:08:07 +01007676 //uint8_t data[2];
Linus Walleij438bd7f2006-06-08 11:35:44 +00007677
Linus Walleijcdc12092010-09-18 11:28:56 +00007678 // NULL check
7679 if (!name) {
7680 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): list name was NULL, using default name \"Unknown\"");
7681 return -1;
7682 }
7683
Linus Walleijea68f1f2008-06-22 21:54:44 +00007684 if (storageid == 0) {
Linus Walleijd71d0b32008-09-22 08:21:03 +00007685 // I'm just guessing that an abstract list may require 512 bytes
Linus Walleij88babc82014-06-02 21:32:14 +02007686 store = get_suggested_storage_id(device, 512, localph);
Linus Walleijea68f1f2008-06-22 21:54:44 +00007687 } else {
7688 store = storageid;
7689 }
7690
Linus Walleijb7426d12006-11-25 23:19:11 +00007691 // Check if we can create an object of this type
7692 for ( i=0; i < params->deviceinfo.ImageFormats_len; i++ ) {
7693 if (params->deviceinfo.ImageFormats[i] == objectformat) {
7694 supported = 1;
7695 break;
Linus Walleij438bd7f2006-06-08 11:35:44 +00007696 }
7697 }
Linus Walleijb7426d12006-11-25 23:19:11 +00007698 if (!supported) {
Linus Walleijcdc12092010-09-18 11:28:56 +00007699 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): player does not support this abstract type");
nicklas79daadbf22009-09-28 18:19:34 +00007700 LIBMTP_ERROR("Unsupported abstract list type: %04x\n", objectformat);
Linus Walleijb7426d12006-11-25 23:19:11 +00007701 return -1;
7702 }
7703
Richard Low15731fe2007-03-22 20:27:20 +00007704 // add the new suffix if it isn't there
Linus Walleij629fe402006-12-12 23:39:15 +00007705 fname[0] = '\0';
Linus Walleijb7426d12006-11-25 23:19:11 +00007706 if (strlen(name) > strlen(suffix)) {
7707 char const * const suff = &name[strlen(name)-strlen(suffix)];
7708 if (!strcmp(suff, suffix)) {
7709 // Home free.
Linus Walleij629fe402006-12-12 23:39:15 +00007710 strncpy(fname, name, sizeof(fname));
Linus Walleijb7426d12006-11-25 23:19:11 +00007711 }
7712 }
7713 // If it didn't end with "<suffix>" then add that here.
Linus Walleij629fe402006-12-12 23:39:15 +00007714 if (fname[0] == '\0') {
Linus Walleijb7426d12006-11-25 23:19:11 +00007715 strncpy(fname, name, sizeof(fname)-strlen(suffix)-1);
7716 strcat(fname, suffix);
Linus Walleij438bd7f2006-06-08 11:35:44 +00007717 fname[sizeof(fname)-1] = '\0';
Linus Walleij438bd7f2006-06-08 11:35:44 +00007718 }
7719
Linus Walleijf67c1ad2009-01-14 21:39:50 +00007720 if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
7721 !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00007722 MTPProperties *props = NULL;
7723 MTPProperties *prop = NULL;
7724 int nrofprops = 0;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007725
Richard Low6c0a6ce2006-11-26 10:42:08 +00007726 *newid = 0x00000000U;
mopoke96143402006-10-30 04:37:26 +00007727
Linus Walleij1e9a0332007-09-12 19:35:56 +00007728 ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
mopoke31364442006-11-20 04:53:04 +00007729
rreardon508705f2006-11-19 21:27:22 +00007730 for (i=0;i<propcnt;i++) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00007731 PTPObjectPropDesc opd;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007732
Linus Walleij1e9a0332007-09-12 19:35:56 +00007733 ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007734 if (ret != PTP_RC_OK) {
7735 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
7736 "could not get property description.");
7737 } else if (opd.GetSet) {
Linus Walleij1e9a0332007-09-12 19:35:56 +00007738 switch (properties[i]) {
Linus Walleijdbcc8242007-08-05 22:31:26 +00007739 case PTP_OPC_ObjectFileName:
Linus Walleija6d0d482007-10-31 08:54:56 +00007740 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00007741 prop->ObjectHandle = *newid;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007742 prop->property = PTP_OPC_ObjectFileName;
7743 prop->datatype = PTP_DTC_STR;
7744 prop->propval.str = strdup(fname);
Linus Walleijfec4d562008-06-01 22:30:36 +00007745 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijd3b78572007-08-24 21:28:24 +00007746 strip_7bit_from_utf8(prop->propval.str);
7747 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00007748 break;
7749 case PTP_OPC_ProtectionStatus:
Linus Walleija6d0d482007-10-31 08:54:56 +00007750 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijadce4a52007-04-23 07:28:11 +00007751 prop->ObjectHandle = *newid;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007752 prop->property = PTP_OPC_ProtectionStatus;
7753 prop->datatype = PTP_DTC_UINT16;
7754 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleijdbcc8242007-08-05 22:31:26 +00007755 break;
7756 case PTP_OPC_NonConsumable:
Linus Walleija6d0d482007-10-31 08:54:56 +00007757 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijadce4a52007-04-23 07:28:11 +00007758 prop->ObjectHandle = *newid;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007759 prop->property = PTP_OPC_NonConsumable;
7760 prop->datatype = PTP_DTC_UINT8;
7761 prop->propval.u8 = nonconsumable;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007762 break;
7763 case PTP_OPC_Name:
7764 if (name != NULL) {
Linus Walleija6d0d482007-10-31 08:54:56 +00007765 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007766 prop->ObjectHandle = *newid;
7767 prop->property = PTP_OPC_Name;
7768 prop->datatype = PTP_DTC_STR;
7769 prop->propval.str = strdup(name);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007770 }
7771 break;
Linus Walleij0b75ab62007-12-28 00:47:34 +00007772 case PTP_OPC_AlbumArtist:
7773 if (artist != NULL) {
7774 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7775 prop->ObjectHandle = *newid;
7776 prop->property = PTP_OPC_AlbumArtist;
7777 prop->datatype = PTP_DTC_STR;
7778 prop->propval.str = strdup(artist);
7779 }
7780 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007781 case PTP_OPC_Artist:
7782 if (artist != NULL) {
Linus Walleija6d0d482007-10-31 08:54:56 +00007783 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007784 prop->ObjectHandle = *newid;
7785 prop->property = PTP_OPC_Artist;
7786 prop->datatype = PTP_DTC_STR;
7787 prop->propval.str = strdup(artist);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007788 }
7789 break;
Linus Walleij31b74292008-05-02 23:29:06 +00007790 case PTP_OPC_Composer:
7791 if (composer != NULL) {
7792 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7793 prop->ObjectHandle = *newid;
7794 prop->property = PTP_OPC_Composer;
7795 prop->datatype = PTP_DTC_STR;
7796 prop->propval.str = strdup(composer);
7797 }
7798 break;
Linus Walleijdbcc8242007-08-05 22:31:26 +00007799 case PTP_OPC_Genre:
7800 if (genre != NULL) {
Linus Walleija6d0d482007-10-31 08:54:56 +00007801 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007802 prop->ObjectHandle = *newid;
7803 prop->property = PTP_OPC_Genre;
7804 prop->datatype = PTP_DTC_STR;
7805 prop->propval.str = strdup(genre);
Linus Walleijdbcc8242007-08-05 22:31:26 +00007806 }
7807 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00007808 case PTP_OPC_DateModified:
7809 // Tag with current time if that is supported
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00007810 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
7811 prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
7812 prop->ObjectHandle = *newid;
7813 prop->property = PTP_OPC_DateModified;
7814 prop->datatype = PTP_DTC_STR;
7815 prop->propval.str = get_iso8601_stamp();
7816 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00007817 break;
Linus Walleijadce4a52007-04-23 07:28:11 +00007818 }
rreardon508705f2006-11-19 21:27:22 +00007819 }
Linus Walleijdbcc8242007-08-05 22:31:26 +00007820 ptp_free_objectpropdesc(&opd);
rreardon508705f2006-11-19 21:27:22 +00007821 }
Linus Walleij1e9a0332007-09-12 19:35:56 +00007822 free(properties);
mopoke31364442006-11-20 04:53:04 +00007823
Linus Walleijb7426d12006-11-25 23:19:11 +00007824 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid,
Linus Walleij1e9a0332007-09-12 19:35:56 +00007825 objectformat, 0, props, nrofprops);
mopoke31364442006-11-20 04:53:04 +00007826
rreardon508705f2006-11-19 21:27:22 +00007827 /* Free property list */
Linus Walleija6d0d482007-10-31 08:54:56 +00007828 ptp_destroy_object_prop_list(props, nrofprops);
mopoke31364442006-11-20 04:53:04 +00007829
rreardon508705f2006-11-19 21:27:22 +00007830 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007831 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list.");
rreardon508705f2006-11-19 21:27:22 +00007832 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007833 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardon508705f2006-11-19 21:27:22 +00007834 }
7835 return -1;
7836 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00007837
Richard Lowc3f5fc32007-07-29 09:40:50 +00007838 // now send the blank object
Richard Lowa6c924c2006-12-29 17:44:28 +00007839 ret = ptp_sendobject(params, NULL, 0);
7840 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007841 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00007842 return -1;
7843 }
Linus Walleij629fe402006-12-12 23:39:15 +00007844
rreardon508705f2006-11-19 21:27:22 +00007845 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
Linus Walleij629fe402006-12-12 23:39:15 +00007846 PTPObjectInfo new_object;
7847
7848 new_object.Filename = fname;
Linus Walleijfec4d562008-06-01 22:30:36 +00007849 if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
Linus Walleijd3b78572007-08-24 21:28:24 +00007850 strip_7bit_from_utf8(new_object.Filename);
7851 }
Linus Walleij755c2462011-03-04 20:11:01 +01007852 // At one point this had to be one
7853 new_object.ObjectCompressedSize = 0;
Linus Walleij629fe402006-12-12 23:39:15 +00007854 new_object.ObjectFormat = objectformat;
7855
Linus Walleij20698482006-11-24 20:54:21 +00007856 // Create the object
Linus Walleijb7426d12006-11-25 23:19:11 +00007857 ret = ptp_sendobjectinfo(params, &store, &localph, newid, &new_object);
Linus Walleij20698482006-11-24 20:54:21 +00007858 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007859 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object info (the playlist itself).");
Linus Walleij20698482006-11-24 20:54:21 +00007860 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007861 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij20698482006-11-24 20:54:21 +00007862 }
7863 return -1;
7864 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00007865 // NOTE: don't destroy new_object objectinfo afterwards - the strings it contains are
7866 // not copies.
Linus Walleij755c2462011-03-04 20:11:01 +01007867
7868#if 0
Richard Lowa6c924c2006-12-29 17:44:28 +00007869 /*
Linus Walleij755c2462011-03-04 20:11:01 +01007870 * At one time we had to send this one blank data byte.
7871 * If we didn't, the handle will not be created and thus there is
7872 * no playlist. Possibly this was masking some bug, so removing it
7873 * now.
Richard Lowa6c924c2006-12-29 17:44:28 +00007874 */
7875 data[0] = '\0';
7876 data[1] = '\0';
7877 ret = ptp_sendobject(params, data, 1);
7878 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007879 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00007880 return -1;
7881 }
Linus Walleij755c2462011-03-04 20:11:01 +01007882#endif
Linus Walleij08a5fe12009-09-08 21:28:58 +00007883
Richard Lowff16bfa2008-09-26 19:23:08 +00007884 // set the properties one by one
7885 ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
7886
7887 for (i=0;i<propcnt;i++) {
7888 PTPObjectPropDesc opd;
Linus Walleij08a5fe12009-09-08 21:28:58 +00007889
Richard Lowff16bfa2008-09-26 19:23:08 +00007890 ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
7891 if (ret != PTP_RC_OK) {
7892 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
7893 "could not get property description.");
7894 } else if (opd.GetSet) {
7895 switch (properties[i]) {
7896 case PTP_OPC_Name:
7897 if (name != NULL) {
Linus Walleij37588142008-10-16 19:10:47 +00007898 ret = set_object_string(device, *newid, PTP_OPC_Name, name);
7899 if (ret != 0) {
7900 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity name.");
7901 return -1;
7902 }
7903 }
Richard Lowff16bfa2008-09-26 19:23:08 +00007904 break;
7905 case PTP_OPC_AlbumArtist:
7906 if (artist != NULL) {
Linus Walleij37588142008-10-16 19:10:47 +00007907 ret = set_object_string(device, *newid, PTP_OPC_AlbumArtist, artist);
7908 if (ret != 0) {
7909 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity album artist.");
7910 return -1;
7911 }
7912 }
Richard Lowff16bfa2008-09-26 19:23:08 +00007913 break;
7914 case PTP_OPC_Artist:
7915 if (artist != NULL) {
Linus Walleij37588142008-10-16 19:10:47 +00007916 ret = set_object_string(device, *newid, PTP_OPC_Artist, artist);
7917 if (ret != 0) {
7918 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity artist.");
7919 return -1;
7920 }
7921 }
Richard Lowff16bfa2008-09-26 19:23:08 +00007922 break;
7923 case PTP_OPC_Composer:
7924 if (composer != NULL) {
Linus Walleij37588142008-10-16 19:10:47 +00007925 ret = set_object_string(device, *newid, PTP_OPC_Composer, composer);
7926 if (ret != 0) {
7927 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity composer.");
7928 return -1;
7929 }
7930 }
Richard Lowff16bfa2008-09-26 19:23:08 +00007931 break;
7932 case PTP_OPC_Genre:
7933 if (genre != NULL) {
Linus Walleij37588142008-10-16 19:10:47 +00007934 ret = set_object_string(device, *newid, PTP_OPC_Genre, genre);
7935 if (ret != 0) {
7936 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity genre.");
7937 return -1;
7938 }
7939 }
Richard Lowff16bfa2008-09-26 19:23:08 +00007940 break;
7941 case PTP_OPC_DateModified:
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00007942 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
7943 ret = set_object_string(device, *newid, PTP_OPC_DateModified, get_iso8601_stamp());
7944 if (ret != 0) {
7945 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set date modified.");
7946 return -1;
7947 }
Linus Walleij37588142008-10-16 19:10:47 +00007948 }
7949 break;
Richard Lowff16bfa2008-09-26 19:23:08 +00007950 }
7951 }
7952 ptp_free_objectpropdesc(&opd);
7953 }
7954 free(properties);
Linus Walleij438bd7f2006-06-08 11:35:44 +00007955 }
7956
Linus Walleijb7426d12006-11-25 23:19:11 +00007957 if (no_tracks > 0) {
Linus Walleij1ccd5862008-11-11 12:33:01 +00007958 // Add tracks to the list as object references.
Linus Walleijb7426d12006-11-25 23:19:11 +00007959 ret = ptp_mtp_setobjectreferences (params, *newid, (uint32_t *) tracks, no_tracks);
Linus Walleij438bd7f2006-06-08 11:35:44 +00007960 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00007961 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): could not add tracks as object references.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00007962 return -1;
7963 }
7964 }
mopoke96143402006-10-30 04:37:26 +00007965
Linus Walleija6d0d482007-10-31 08:54:56 +00007966 add_object_to_cache(device, *newid);
Linus Walleij438bd7f2006-06-08 11:35:44 +00007967
7968 return 0;
7969}
7970
Linus Walleij7783b9b2007-09-22 22:10:44 +00007971/**
Linus Walleij08a5fe12009-09-08 21:28:58 +00007972 * This updates the metadata and track listing
Linus Walleij7783b9b2007-09-22 22:10:44 +00007973 * for an abstract list.
7974 * @param device a pointer to the device that the abstract list
7975 * resides on.
7976 * @param name the name of the abstract list.
7977 * @param artist the artist of the abstract list or NULL.
7978 * @param genre the genre of the abstract list or NULL.
7979 * @param objecthandle the object to be updated.
7980 * @param objectformat the abstract list type to update.
7981 * @param tracks an array of tracks to associate with this list.
7982 * @param no_tracks the number of tracks in the list.
7983 * @return 0 on success, any other value means failure.
7984 */
7985static int update_abstract_list(LIBMTP_mtpdevice_t *device,
7986 char const * const name,
7987 char const * const artist,
Linus Walleij31b74292008-05-02 23:29:06 +00007988 char const * const composer,
Linus Walleij7783b9b2007-09-22 22:10:44 +00007989 char const * const genre,
7990 uint32_t const objecthandle,
7991 uint16_t const objectformat,
7992 uint32_t const * const tracks,
7993 uint32_t const no_tracks)
7994{
7995 uint16_t ret;
7996 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf9267e92007-10-15 21:07:37 +00007997 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij7783b9b2007-09-22 22:10:44 +00007998 uint16_t *properties = NULL;
7999 uint32_t propcnt = 0;
8000 int i;
Linus Walleij08a5fe12009-09-08 21:28:58 +00008001
Linus Walleij7783b9b2007-09-22 22:10:44 +00008002 // First see which properties can be set
8003 // i.e only try to update this metadata for object tags that exist on the current player.
8004 ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
8005 if (ret != PTP_RC_OK) {
8006 // Just bail out for now, nothing is ever set.
8007 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8008 "could not retrieve supported object properties.");
8009 return -1;
8010 }
Linus Walleijf9267e92007-10-15 21:07:37 +00008011 if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList) &&
Linus Walleijfec4d562008-06-01 22:30:36 +00008012 !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
Linus Walleij7783b9b2007-09-22 22:10:44 +00008013 MTPProperties *props = NULL;
8014 MTPProperties *prop = NULL;
8015 int nrofprops = 0;
Linus Walleij08a5fe12009-09-08 21:28:58 +00008016
Linus Walleij7783b9b2007-09-22 22:10:44 +00008017 for (i=0;i<propcnt;i++) {
8018 PTPObjectPropDesc opd;
8019
8020 ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
8021 if (ret != PTP_RC_OK) {
8022 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8023 "could not get property description.");
8024 } else if (opd.GetSet) {
8025 switch (properties[i]) {
8026 case PTP_OPC_Name:
Linus Walleija6d0d482007-10-31 08:54:56 +00008027 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00008028 prop->ObjectHandle = objecthandle;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008029 prop->property = PTP_OPC_Name;
8030 prop->datatype = PTP_DTC_STR;
8031 if (name != NULL)
8032 prop->propval.str = strdup(name);
8033 break;
Linus Walleij0b75ab62007-12-28 00:47:34 +00008034 case PTP_OPC_AlbumArtist:
8035 if (artist != NULL) {
8036 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8037 prop->ObjectHandle = objecthandle;
8038 prop->property = PTP_OPC_AlbumArtist;
8039 prop->datatype = PTP_DTC_STR;
8040 prop->propval.str = strdup(artist);
8041 }
8042 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008043 case PTP_OPC_Artist:
8044 if (artist != NULL) {
Linus Walleija6d0d482007-10-31 08:54:56 +00008045 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij0b75ab62007-12-28 00:47:34 +00008046 prop->ObjectHandle = objecthandle;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008047 prop->property = PTP_OPC_Artist;
8048 prop->datatype = PTP_DTC_STR;
8049 prop->propval.str = strdup(artist);
8050 }
8051 break;
Linus Walleij31b74292008-05-02 23:29:06 +00008052 case PTP_OPC_Composer:
8053 if (composer != NULL) {
8054 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8055 prop->ObjectHandle = objecthandle;
8056 prop->property = PTP_OPC_Composer;
8057 prop->datatype = PTP_DTC_STR;
8058 prop->propval.str = strdup(composer);
8059 }
8060 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008061 case PTP_OPC_Genre:
8062 if (genre != NULL) {
Linus Walleija6d0d482007-10-31 08:54:56 +00008063 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
Linus Walleij7783b9b2007-09-22 22:10:44 +00008064 prop->ObjectHandle = objecthandle;
8065 prop->property = PTP_OPC_Genre;
8066 prop->datatype = PTP_DTC_STR;
8067 prop->propval.str = strdup(genre);
8068 }
8069 break;
8070 case PTP_OPC_DateModified:
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00008071 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
Linus Walleij37588142008-10-16 19:10:47 +00008072 // Tag with current time if that is supported
8073 prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
8074 prop->ObjectHandle = objecthandle;
8075 prop->property = PTP_OPC_DateModified;
8076 prop->datatype = PTP_DTC_STR;
8077 prop->propval.str = get_iso8601_stamp();
8078 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00008079 break;
8080 default:
8081 break;
8082 }
8083 }
8084 ptp_free_objectpropdesc(&opd);
8085 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008086
Linus Walleij7783b9b2007-09-22 22:10:44 +00008087 // proplist could be NULL if we can't write any properties
8088 if (props != NULL) {
8089 ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
8090
Linus Walleija6d0d482007-10-31 08:54:56 +00008091 ptp_destroy_object_prop_list(props, nrofprops);
Linus Walleij08a5fe12009-09-08 21:28:58 +00008092
Linus Walleij7783b9b2007-09-22 22:10:44 +00008093 if (ret != PTP_RC_OK) {
8094 // TODO: return error of which property we couldn't set
8095 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8096 "could not set object property list.");
Richard Low016e3732007-09-23 14:53:46 +00008097 free(properties);
Linus Walleij7783b9b2007-09-22 22:10:44 +00008098 return -1;
8099 }
8100 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008101
Linus Walleij7783b9b2007-09-22 22:10:44 +00008102 } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
8103 for (i=0;i<propcnt;i++) {
8104 switch (properties[i]) {
8105 case PTP_OPC_Name:
8106 // Update title
8107 ret = set_object_string(device, objecthandle, PTP_OPC_Name, name);
8108 if (ret != 0) {
8109 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8110 "could not set title.");
8111 }
8112 break;
Linus Walleij0b75ab62007-12-28 00:47:34 +00008113 case PTP_OPC_AlbumArtist:
8114 // Update album artist
8115 ret = set_object_string(device, objecthandle, PTP_OPC_AlbumArtist, artist);
8116 if (ret != 0) {
8117 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8118 "could not set album artist name.");
8119 }
8120 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008121 case PTP_OPC_Artist:
8122 // Update artist
8123 ret = set_object_string(device, objecthandle, PTP_OPC_Artist, artist);
8124 if (ret != 0) {
8125 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8126 "could not set artist name.");
8127 }
Marcus Meissner113fa292015-04-09 23:11:52 +02008128 break;
Linus Walleij31b74292008-05-02 23:29:06 +00008129 case PTP_OPC_Composer:
8130 // Update composer
8131 ret = set_object_string(device, objecthandle, PTP_OPC_Composer, composer);
8132 if (ret != 0) {
8133 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8134 "could not set composer name.");
8135 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00008136 break;
8137 case PTP_OPC_Genre:
Linus Walleijb7e8f972010-01-19 00:38:11 +00008138 // Update genre (but only if valid)
8139 if(genre) {
8140 ret = set_object_string(device, objecthandle, PTP_OPC_Genre, genre);
8141 if (ret != 0) {
8142 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8143 "could not set genre.");
8144 }
8145 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00008146 break;
8147 case PTP_OPC_DateModified:
8148 // Update date modified
Linus Walleijcf8dc2b2008-10-21 13:58:36 +00008149 if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
Linus Walleij7783b9b2007-09-22 22:10:44 +00008150 char *tmpdate = get_iso8601_stamp();
8151 ret = set_object_string(device, objecthandle, PTP_OPC_DateModified, tmpdate);
8152 if (ret != 0) {
8153 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8154 "could not set modification date.");
8155 }
8156 free(tmpdate);
8157 }
Marcus Meissner113fa292015-04-09 23:11:52 +02008158 break;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008159 default:
8160 break;
8161 }
8162 }
Linus Walleij7783b9b2007-09-22 22:10:44 +00008163 } else {
8164 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
8165 "Your device doesn't seem to support any known way of setting metadata.");
Richard Low016e3732007-09-23 14:53:46 +00008166 free(properties);
Linus Walleij7783b9b2007-09-22 22:10:44 +00008167 return -1;
8168 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008169
Linus Walleij7783b9b2007-09-22 22:10:44 +00008170 // Then the object references...
Linus Walleij1ccd5862008-11-11 12:33:01 +00008171 ret = ptp_mtp_setobjectreferences (params, objecthandle, (uint32_t *) tracks, no_tracks);
8172 if (ret != PTP_RC_OK) {
8173 add_ptp_error_to_errorstack(device, ret, "update_abstract_list(): could not add tracks as object references.");
8174 free(properties);
8175 return -1;
Linus Walleij7783b9b2007-09-22 22:10:44 +00008176 }
8177
Richard Low016e3732007-09-23 14:53:46 +00008178 free(properties);
Linus Walleij7752b952007-10-19 20:11:46 +00008179
8180 update_metadata_cache(device, objecthandle);
Linus Walleij08a5fe12009-09-08 21:28:58 +00008181
Linus Walleij7783b9b2007-09-22 22:10:44 +00008182 return 0;
8183}
8184
Linus Walleijb7426d12006-11-25 23:19:11 +00008185
8186/**
8187 * This routine creates a new playlist based on the metadata
8188 * supplied. If the <code>tracks</code> field of the metadata
8189 * contains a track listing, these tracks will be added to the
8190 * playlist.
8191 * @param device a pointer to the device to create the new playlist on.
8192 * @param metadata the metadata for the new playlist. If the function
8193 * exits with success, the <code>playlist_id</code> field of this
8194 * struct will contain the new playlist ID of the playlist.
Linus Walleij2fe43e42009-01-30 20:31:26 +00008195 * <ul>
8196 * <li><code>metadata-&gt;parent_id</code> should be set to the parent
8197 * (e.g. folder) to store this track in. Since some
8198 * devices are a bit picky about where files
8199 * are placed, a default folder will be chosen if libmtp
8200 * has detected one for the current filetype and this
8201 * parameter is set to 0. If this is 0 and no default folder
8202 * can be found, the file will be stored in the root folder.
8203 * <li><code>metadata-&gt;storage_id</code> should be set to the
8204 * desired storage (e.g. memory card or whatever your device
8205 * presents) to store this track in. Setting this to 0 will store
8206 * the track on the primary storage.
8207 * </ul>
Linus Walleijb7426d12006-11-25 23:19:11 +00008208 * @return 0 on success, any other value means failure.
8209 * @see LIBMTP_Update_Playlist()
8210 * @see LIBMTP_Delete_Object()
8211 */
8212int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *device,
Linus Walleijea68f1f2008-06-22 21:54:44 +00008213 LIBMTP_playlist_t * const metadata)
Linus Walleijb7426d12006-11-25 23:19:11 +00008214{
Linus Walleija4e6bdc2008-05-23 22:31:53 +00008215 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijea68f1f2008-06-22 21:54:44 +00008216 uint32_t localph = metadata->parent_id;
Linus Walleijb7426d12006-11-25 23:19:11 +00008217
8218 // Use a default folder if none given
8219 if (localph == 0) {
Linus Walleijafac36b2009-01-30 20:39:13 +00008220 if (device->default_playlist_folder != 0)
8221 localph = device->default_playlist_folder;
8222 else
8223 localph = device->default_music_folder;
Linus Walleijb7426d12006-11-25 23:19:11 +00008224 }
Linus Walleij5ce59db2008-03-12 21:22:58 +00008225 metadata->parent_id = localph;
Linus Walleijb7426d12006-11-25 23:19:11 +00008226
Linus Walleijf3c44052008-08-16 21:14:56 +00008227 // Samsung needs its own special type of playlists
8228 if(FLAG_PLAYLIST_SPL(ptp_usb)) {
8229 return playlist_t_to_spl(device, metadata);
8230 }
8231
Linus Walleijb7426d12006-11-25 23:19:11 +00008232 // Just create a new abstract audio/video playlist...
8233 return create_new_abstract_list(device,
8234 metadata->name,
Linus Walleijadce4a52007-04-23 07:28:11 +00008235 NULL,
8236 NULL,
Linus Walleij31b74292008-05-02 23:29:06 +00008237 NULL,
Linus Walleijb7426d12006-11-25 23:19:11 +00008238 localph,
Linus Walleijea68f1f2008-06-22 21:54:44 +00008239 metadata->storage_id,
Linus Walleijb7426d12006-11-25 23:19:11 +00008240 PTP_OFC_MTP_AbstractAudioVideoPlaylist,
Linus Walleija4e6bdc2008-05-23 22:31:53 +00008241 get_playlist_extension(ptp_usb),
Linus Walleijb7426d12006-11-25 23:19:11 +00008242 &metadata->playlist_id,
8243 metadata->tracks,
8244 metadata->no_tracks);
8245}
8246
Linus Walleij438bd7f2006-06-08 11:35:44 +00008247/**
8248 * This routine updates a playlist based on the metadata
8249 * supplied. If the <code>tracks</code> field of the metadata
8250 * contains a track listing, these tracks will be added to the
8251 * playlist in place of those already present, i.e. the
alistair_boyle5e5fcb72008-11-17 14:38:19 +00008252 * previous track listing will be deleted. For Samsung devices the
8253 * playlist id (metadata->playlist_id) is likely to change.
Linus Walleij438bd7f2006-06-08 11:35:44 +00008254 * @param device a pointer to the device to create the new playlist on.
8255 * @param metadata the metadata for the playlist to be updated.
mopoke96143402006-10-30 04:37:26 +00008256 * notice that the field <code>playlist_id</code>
alistair_boyle5e5fcb72008-11-17 14:38:19 +00008257 * must contain the apropriate playlist ID. Playlist ID
8258 * be modified to a new playlist ID by the time the
8259 * function returns since edit-in-place is not always possible.
Linus Walleij438bd7f2006-06-08 11:35:44 +00008260 * @return 0 on success, any other value means failure.
8261 * @see LIBMTP_Create_New_Playlist()
8262 * @see LIBMTP_Delete_Object()
8263 */
mopoke96143402006-10-30 04:37:26 +00008264int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device,
alistair_boyle5e5fcb72008-11-17 14:38:19 +00008265 LIBMTP_playlist_t * const metadata)
Linus Walleij438bd7f2006-06-08 11:35:44 +00008266{
Linus Walleijf3c44052008-08-16 21:14:56 +00008267
8268 // Samsung needs its own special type of playlists
8269 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
8270 if(FLAG_PLAYLIST_SPL(ptp_usb)) {
8271 return update_spl_playlist(device, metadata);
8272 }
8273
Linus Walleij7783b9b2007-09-22 22:10:44 +00008274 return update_abstract_list(device,
8275 metadata->name,
8276 NULL,
8277 NULL,
Linus Walleij31b74292008-05-02 23:29:06 +00008278 NULL,
Linus Walleij7783b9b2007-09-22 22:10:44 +00008279 metadata->playlist_id,
8280 PTP_OFC_MTP_AbstractAudioVideoPlaylist,
8281 metadata->tracks,
8282 metadata->no_tracks);
Linus Walleij438bd7f2006-06-08 11:35:44 +00008283}
Linus Walleijaa4b0752006-07-26 22:21:04 +00008284
Linus Walleij0c33ec02006-10-27 10:15:40 +00008285/**
8286 * This creates a new album metadata structure and allocates memory
8287 * for it. Notice that if you add strings to this structure they
8288 * will be freed by the corresponding <code>LIBMTP_destroy_album_t</code>
8289 * operation later, so be careful of using strdup() when assigning
8290 * strings.
8291 *
8292 * @return a pointer to the newly allocated metadata structure.
8293 * @see LIBMTP_destroy_album_t()
8294 */
8295LIBMTP_album_t *LIBMTP_new_album_t(void)
8296{
8297 LIBMTP_album_t *new = (LIBMTP_album_t *) malloc(sizeof(LIBMTP_album_t));
8298 if (new == NULL) {
8299 return NULL;
8300 }
8301 new->album_id = 0;
Linus Walleij5ce59db2008-03-12 21:22:58 +00008302 new->parent_id = 0;
Linus Walleijea68f1f2008-06-22 21:54:44 +00008303 new->storage_id = 0;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008304 new->name = NULL;
Linus Walleijadce4a52007-04-23 07:28:11 +00008305 new->artist = NULL;
Linus Walleij31b74292008-05-02 23:29:06 +00008306 new->composer = NULL;
Linus Walleijadce4a52007-04-23 07:28:11 +00008307 new->genre = NULL;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008308 new->tracks = NULL;
8309 new->no_tracks = 0;
8310 new->next = NULL;
8311 return new;
8312}
8313
8314/**
8315 * This recursively deletes the memory for an album structure
8316 *
8317 * @param album structure to destroy
8318 * @see LIBMTP_new_album_t()
8319 */
8320void LIBMTP_destroy_album_t(LIBMTP_album_t *album)
8321{
8322 if (album == NULL) {
8323 return;
8324 }
8325 if (album->name != NULL)
8326 free(album->name);
Linus Walleijadce4a52007-04-23 07:28:11 +00008327 if (album->artist != NULL)
8328 free(album->artist);
Linus Walleij31b74292008-05-02 23:29:06 +00008329 if (album->composer != NULL)
8330 free(album->composer);
Linus Walleijadce4a52007-04-23 07:28:11 +00008331 if (album->genre != NULL)
8332 free(album->genre);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008333 if (album->tracks != NULL)
8334 free(album->tracks);
8335 free(album);
8336 return;
8337}
8338
8339/**
Linus Walleij08a5fe12009-09-08 21:28:58 +00008340 * This function maps and copies a property onto the album metadata if applicable.
8341 */
8342static void pick_property_to_album_metadata(LIBMTP_mtpdevice_t *device,
8343 MTPProperties *prop, LIBMTP_album_t *alb)
8344{
8345 switch (prop->property) {
8346 case PTP_OPC_Name:
8347 if (prop->propval.str != NULL)
8348 alb->name = strdup(prop->propval.str);
8349 else
8350 alb->name = NULL;
8351 break;
8352 case PTP_OPC_AlbumArtist:
8353 if (prop->propval.str != NULL) {
8354 // This should take precedence over plain "Artist"
8355 if (alb->artist != NULL)
8356 free(alb->artist);
8357 alb->artist = strdup(prop->propval.str);
8358 } else
8359 alb->artist = NULL;
8360 break;
8361 case PTP_OPC_Artist:
8362 if (prop->propval.str != NULL) {
8363 // Only use of AlbumArtist is not set
8364 if (alb->artist == NULL)
8365 alb->artist = strdup(prop->propval.str);
8366 } else
8367 alb->artist = NULL;
8368 break;
8369 case PTP_OPC_Composer:
8370 if (prop->propval.str != NULL)
8371 alb->composer = strdup(prop->propval.str);
8372 else
8373 alb->composer = NULL;
8374 break;
8375 case PTP_OPC_Genre:
8376 if (prop->propval.str != NULL)
8377 alb->genre = strdup(prop->propval.str);
8378 else
8379 alb->genre = NULL;
8380 break;
8381 }
8382}
8383
8384/**
8385 * This function retrieves the album metadata for an album
8386 * given by a unique ID.
8387 * @param device a pointer to the device to get the track metadata off.
8388 * @param alb an album metadata metadata set to fill in.
8389 */
8390static void get_album_metadata(LIBMTP_mtpdevice_t *device,
8391 LIBMTP_album_t *alb)
8392{
8393 uint16_t ret;
8394 PTPParams *params = (PTPParams *) device->params;
8395 uint32_t i;
8396 MTPProperties *prop;
8397 PTPObject *ob;
8398
8399 /*
8400 * If we have a cached, large set of metadata, then use it!
8401 */
8402 ret = ptp_object_want(params, alb->album_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
8403 if (ob->mtpprops) {
8404 prop = ob->mtpprops;
8405 for (i=0;i<ob->nrofmtpprops;i++,prop++)
8406 pick_property_to_album_metadata(device, prop, alb);
8407 } else {
8408 uint16_t *props = NULL;
8409 uint32_t propcnt = 0;
8410
8411 // First see which properties can be retrieved for albums
8412 ret = ptp_mtp_getobjectpropssupported(params, PTP_OFC_MTP_AbstractAudioAlbum, &propcnt, &props);
8413 if (ret != PTP_RC_OK) {
8414 add_ptp_error_to_errorstack(device, ret, "get_album_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
8415 // Just bail out for now, nothing is ever set.
8416 return;
8417 } else {
8418 for (i=0;i<propcnt;i++) {
8419 switch (props[i]) {
8420 case PTP_OPC_Name:
8421 alb->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
8422 break;
8423 case PTP_OPC_AlbumArtist:
8424 if (alb->artist != NULL)
8425 free(alb->artist);
8426 alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_AlbumArtist);
8427 break;
8428 case PTP_OPC_Artist:
8429 if (alb->artist == NULL)
8430 alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_Artist);
8431 break;
8432 case PTP_OPC_Composer:
8433 alb->composer = get_string_from_object(device, ob->oid, PTP_OPC_Composer);
8434 break;
8435 case PTP_OPC_Genre:
8436 alb->genre = get_string_from_object(device, ob->oid, PTP_OPC_Genre);
8437 break;
8438 default:
8439 break;
8440 }
8441 }
8442 free(props);
8443 }
8444 }
8445}
8446
nicklas795e0100b2009-11-07 19:53:46 +00008447
Linus Walleij08a5fe12009-09-08 21:28:58 +00008448/**
Linus Walleij0c33ec02006-10-27 10:15:40 +00008449 * This function returns a list of the albums available on the
8450 * device.
8451 *
8452 * @param device a pointer to the device to get the album listing from.
8453 * @return an album list on success, else NULL. If there are no albums
8454 * on the device, NULL will be returned as well.
8455 * @see LIBMTP_Get_Album()
8456 */
8457LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *device)
8458{
nicklas795e0100b2009-11-07 19:53:46 +00008459 // Read all storage devices
8460 return LIBMTP_Get_Album_List_For_Storage(device, 0);
8461}
8462
8463
8464/**
8465 * This function returns a list of the albums available on the
8466 * device. You can filter on the storage ID.
8467 *
8468 * @param device a pointer to the device to get the album listing from.
8469 * @param storage_id ID of device storage (if null, all storages)
8470 *
8471 * @return an album list on success, else NULL. If there are no albums
8472 * on the device, NULL will be returned as well.
8473 * @see LIBMTP_Get_Album()
8474 */
8475LIBMTP_album_t *LIBMTP_Get_Album_List_For_Storage(LIBMTP_mtpdevice_t *device, uint32_t const storage_id)
8476{
Linus Walleij0c33ec02006-10-27 10:15:40 +00008477 PTPParams *params = (PTPParams *) device->params;
8478 LIBMTP_album_t *retalbums = NULL;
8479 LIBMTP_album_t *curalbum = NULL;
8480 uint32_t i;
8481
8482 // Get all the handles if we haven't already done that
Linus Walleij08a5fe12009-09-08 21:28:58 +00008483 if (params->nrofobjects == 0)
Linus Walleij0c33ec02006-10-27 10:15:40 +00008484 flush_handles(device);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008485
Linus Walleijd4637502009-06-14 23:03:33 +00008486 for (i = 0; i < params->nrofobjects; i++) {
Linus Walleij0c33ec02006-10-27 10:15:40 +00008487 LIBMTP_album_t *alb;
Linus Walleijd4637502009-06-14 23:03:33 +00008488 PTPObject *ob;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008489 uint16_t ret;
8490
Linus Walleijd4637502009-06-14 23:03:33 +00008491 ob = &params->objects[i];
Linus Walleij0c33ec02006-10-27 10:15:40 +00008492
Linus Walleijf0bf4372007-07-01 21:47:38 +00008493 // Ignore stuff that isn't an album
Linus Walleij08a5fe12009-09-08 21:28:58 +00008494 if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum )
Linus Walleijf0bf4372007-07-01 21:47:38 +00008495 continue;
Linus Walleijf0bf4372007-07-01 21:47:38 +00008496
nicklas795e0100b2009-11-07 19:53:46 +00008497 // Ignore stuff that isn't into the storage device
8498 if ((storage_id != 0) && (ob->oi.StorageID != storage_id ))
8499 continue;
8500
Linus Walleijf0bf4372007-07-01 21:47:38 +00008501 // Allocate a new album type
8502 alb = LIBMTP_new_album_t();
Linus Walleijd4637502009-06-14 23:03:33 +00008503 alb->album_id = ob->oid;
8504 alb->parent_id = ob->oi.ParentObject;
8505 alb->storage_id = ob->oi.StorageID;
Linus Walleij5ce59db2008-03-12 21:22:58 +00008506
Linus Walleij08a5fe12009-09-08 21:28:58 +00008507 // Fetch supported metadata
8508 get_album_metadata(device, alb);
8509
Linus Walleijf0bf4372007-07-01 21:47:38 +00008510 // Then get the track listing for this album
8511 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
8512 if (ret != PTP_RC_OK) {
8513 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Could not get object references.");
8514 alb->tracks = NULL;
8515 alb->no_tracks = 0;
8516 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008517
Linus Walleijf0bf4372007-07-01 21:47:38 +00008518 // Add album to a list that will be returned afterwards.
8519 if (retalbums == NULL) {
8520 retalbums = alb;
8521 curalbum = alb;
8522 } else {
8523 curalbum->next = alb;
8524 curalbum = alb;
8525 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008526
Linus Walleij0c33ec02006-10-27 10:15:40 +00008527 }
8528 return retalbums;
8529}
8530
8531/**
8532 * This function retrieves an individual album from the device.
8533 * @param device a pointer to the device to get the album from.
8534 * @param albid the unique ID of the album to retrieve.
8535 * @return a valid album metadata or NULL on failure.
8536 * @see LIBMTP_Get_Album_List()
8537 */
8538LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *device, uint32_t const albid)
8539{
8540 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd4637502009-06-14 23:03:33 +00008541 uint16_t ret;
8542 PTPObject *ob;
8543 LIBMTP_album_t *alb;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008544
8545 // Get all the handles if we haven't already done that
Linus Walleij08a5fe12009-09-08 21:28:58 +00008546 if (params->nrofobjects == 0)
Linus Walleij0c33ec02006-10-27 10:15:40 +00008547 flush_handles(device);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008548
Linus Walleij08a5fe12009-09-08 21:28:58 +00008549 ret = ptp_object_want(params, albid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
Linus Walleijd4637502009-06-14 23:03:33 +00008550 if (ret != PTP_RC_OK)
8551 return NULL;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008552
Linus Walleijd4637502009-06-14 23:03:33 +00008553 // Ignore stuff that isn't an album
Linus Walleij08a5fe12009-09-08 21:28:58 +00008554 if (ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum)
Linus Walleijd4637502009-06-14 23:03:33 +00008555 return NULL;
Linus Walleijd4637502009-06-14 23:03:33 +00008556
8557 // Allocate a new album type
8558 alb = LIBMTP_new_album_t();
8559 alb->album_id = ob->oid;
8560 alb->parent_id = ob->oi.ParentObject;
8561 alb->storage_id = ob->oi.StorageID;
8562
Linus Walleij08a5fe12009-09-08 21:28:58 +00008563 // Fetch supported metadata
8564 get_album_metadata(device, alb);
8565
8566 // Then get the track listing for this album
Linus Walleijd4637502009-06-14 23:03:33 +00008567 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
8568 if (ret != PTP_RC_OK) {
8569 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album: Could not get object references.");
8570 alb->tracks = NULL;
8571 alb->no_tracks = 0;
8572 }
Linus Walleij08a5fe12009-09-08 21:28:58 +00008573
Linus Walleijd4637502009-06-14 23:03:33 +00008574 return alb;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008575}
8576
8577/**
8578 * This routine creates a new album based on the metadata
8579 * supplied. If the <code>tracks</code> field of the metadata
8580 * contains a track listing, these tracks will be added to the
8581 * album.
8582 * @param device a pointer to the device to create the new album on.
8583 * @param metadata the metadata for the new album. If the function
8584 * exits with success, the <code>album_id</code> field of this
8585 * struct will contain the new ID of the album.
Linus Walleij2fe43e42009-01-30 20:31:26 +00008586 * <ul>
Linus Walleij08a5fe12009-09-08 21:28:58 +00008587 * <li><code>metadata-&gt;parent_id</code> should be set to the parent
8588 * (e.g. folder) to store this track in. Since some
Linus Walleij2fe43e42009-01-30 20:31:26 +00008589 * devices are a bit picky about where files
8590 * are placed, a default folder will be chosen if libmtp
8591 * has detected one for the current filetype and this
8592 * parameter is set to 0. If this is 0 and no default folder
8593 * can be found, the file will be stored in the root folder.
8594 * <li><code>metadata-&gt;storage_id</code> should be set to the
8595 * desired storage (e.g. memory card or whatever your device
8596 * presents) to store this track in. Setting this to 0 will store
8597 * the track on the primary storage.
8598 * </ul>
Linus Walleij0c33ec02006-10-27 10:15:40 +00008599 * @return 0 on success, any other value means failure.
8600 * @see LIBMTP_Update_Album()
8601 * @see LIBMTP_Delete_Object()
8602 */
8603int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *device,
Linus Walleijea68f1f2008-06-22 21:54:44 +00008604 LIBMTP_album_t * const metadata)
Linus Walleij0c33ec02006-10-27 10:15:40 +00008605{
Linus Walleijea68f1f2008-06-22 21:54:44 +00008606 uint32_t localph = metadata->parent_id;
mopoke96143402006-10-30 04:37:26 +00008607
Linus Walleij0c33ec02006-10-27 10:15:40 +00008608 // Use a default folder if none given
8609 if (localph == 0) {
Linus Walleijafac36b2009-01-30 20:39:13 +00008610 if (device->default_album_folder != 0)
8611 localph = device->default_album_folder;
8612 else
8613 localph = device->default_music_folder;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008614 }
Linus Walleij5ce59db2008-03-12 21:22:58 +00008615 metadata->parent_id = localph;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008616
Linus Walleijb7426d12006-11-25 23:19:11 +00008617 // Just create a new abstract album...
8618 return create_new_abstract_list(device,
8619 metadata->name,
Linus Walleijadce4a52007-04-23 07:28:11 +00008620 metadata->artist,
Linus Walleij31b74292008-05-02 23:29:06 +00008621 metadata->composer,
Linus Walleijadce4a52007-04-23 07:28:11 +00008622 metadata->genre,
Linus Walleijb7426d12006-11-25 23:19:11 +00008623 localph,
Linus Walleijea68f1f2008-06-22 21:54:44 +00008624 metadata->storage_id,
Linus Walleijb7426d12006-11-25 23:19:11 +00008625 PTP_OFC_MTP_AbstractAudioAlbum,
8626 ".alb",
8627 &metadata->album_id,
8628 metadata->tracks,
8629 metadata->no_tracks);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008630}
8631
8632/**
rreardon5332f9c2006-12-05 10:18:23 +00008633 * This creates a new sample data metadata structure and allocates memory
8634 * for it. Notice that if you add strings to this structure they
8635 * will be freed by the corresponding <code>LIBMTP_destroy_sampledata_t</code>
8636 * operation later, so be careful of using strdup() when assigning
8637 * strings.
8638 *
8639 * @return a pointer to the newly allocated metadata structure.
8640 * @see LIBMTP_destroy_sampledata_t()
8641 */
8642LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void)
8643{
8644 LIBMTP_filesampledata_t *new = (LIBMTP_filesampledata_t *) malloc(sizeof(LIBMTP_filesampledata_t));
8645 if (new == NULL) {
8646 return NULL;
8647 }
8648 new->height=0;
8649 new->width = 0;
8650 new->data = NULL;
8651 new->duration = 0;
8652 new->size = 0;
8653 return new;
8654}
8655
Linus Walleijf1b02f22006-12-06 09:03:23 +00008656/**
8657 * This destroys a file sample metadata type.
8658 * @param sample the file sample metadata to be destroyed.
8659 */
8660void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t * sample)
8661{
8662 if (sample == NULL) {
8663 return;
8664 }
8665 if (sample->data != NULL) {
8666 free(sample->data);
8667 }
8668 free(sample);
8669}
8670
8671/**
8672 * This routine figures out whether a certain filetype supports
8673 * representative samples (small thumbnail images) or not. This
8674 * typically applies to JPEG files, MP3 files and Album abstract
8675 * playlists, but in theory any filetype could support representative
8676 * samples.
8677 * @param device a pointer to the device which is to be examined.
Linus Walleij9ffe9982008-01-20 13:42:52 +00008678 * @param filetype the fileype to examine, and return the representative sample
Linus Walleijf1b02f22006-12-06 09:03:23 +00008679 * properties for.
8680 * @param sample this will contain a new sample type with the fields
8681 * filled in with suitable default values. For example, the
8682 * supported sample type will be set, the supported height and
8683 * width will be set to max values if it is an image sample,
8684 * and duration will also be given some suitable default value
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008685 * which should not be exceeded on audio samples. If the
Linus Walleijf1b02f22006-12-06 09:03:23 +00008686 * device does not support samples for this filetype, this
8687 * pointer will be NULL. If it is not NULL, the user must
8688 * destroy this struct with <code>LIBMTP_destroy_filesampledata_t()</code>
8689 * after use.
8690 * @return 0 on success, any other value means failure.
8691 * @see LIBMTP_Send_Representative_Sample()
8692 * @see LIBMTP_Create_New_Album()
8693 */
8694int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *device,
8695 LIBMTP_filetype_t const filetype,
8696 LIBMTP_filesampledata_t ** sample)
8697{
8698 uint16_t ret;
8699 PTPParams *params = (PTPParams *) device->params;
8700 uint16_t *props = NULL;
8701 uint32_t propcnt = 0;
8702 int i;
8703 // TODO: Get rid of these when we can properly query the device.
8704 int support_data = 0;
8705 int support_format = 0;
8706 int support_height = 0;
8707 int support_width = 0;
8708 int support_duration = 0;
Richard Low6f070842009-05-03 10:26:16 +00008709 int support_size = 0;
Linus Walleijf1b02f22006-12-06 09:03:23 +00008710
rreardon21a3a9d2007-08-15 10:56:31 +00008711 PTPObjectPropDesc opd_height;
8712 PTPObjectPropDesc opd_width;
8713 PTPObjectPropDesc opd_format;
8714 PTPObjectPropDesc opd_duration;
Richard Low6f070842009-05-03 10:26:16 +00008715 PTPObjectPropDesc opd_size;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008716
Linus Walleijf1b02f22006-12-06 09:03:23 +00008717 // Default to no type supported.
8718 *sample = NULL;
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008719
Linus Walleijf1b02f22006-12-06 09:03:23 +00008720 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
8721 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00008722 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample_Format(): could not get object properties.");
Linus Walleijf1b02f22006-12-06 09:03:23 +00008723 return -1;
8724 }
8725 /*
8726 * TODO: when walking through these object properties, make calls to
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008727 * a new function in ptp.h/ptp.c that can send the command
Linus Walleijf1b02f22006-12-06 09:03:23 +00008728 * PTP_OC_MTP_GetObjectPropDesc to get max/min values of the properties
8729 * supported.
8730 */
8731 for (i = 0; i < propcnt; i++) {
8732 switch(props[i]) {
8733 case PTP_OPC_RepresentativeSampleData:
8734 support_data = 1;
8735 break;
8736 case PTP_OPC_RepresentativeSampleFormat:
8737 support_format = 1;
8738 break;
8739 case PTP_OPC_RepresentativeSampleSize:
Richard Low6f070842009-05-03 10:26:16 +00008740 support_size = 1;
Linus Walleijf1b02f22006-12-06 09:03:23 +00008741 break;
8742 case PTP_OPC_RepresentativeSampleHeight:
8743 support_height = 1;
8744 break;
8745 case PTP_OPC_RepresentativeSampleWidth:
8746 support_width = 1;
8747 break;
8748 case PTP_OPC_RepresentativeSampleDuration:
8749 support_duration = 1;
8750 break;
8751 default:
8752 break;
8753 }
8754 }
8755 free(props);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008756
Linus Walleijf1b02f22006-12-06 09:03:23 +00008757 if (support_data && support_format && support_height && support_width && !support_duration) {
8758 // Something that supports height and width and not duration is likely to be JPEG
8759 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008760 /*
8761 * Populate the sample format with the first supported format
8762 *
rreardon21a3a9d2007-08-15 10:56:31 +00008763 * TODO: figure out how to pass back more than one format if more are
8764 * supported by the device.
8765 */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008766 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);
rreardon21a3a9d2007-08-15 10:56:31 +00008767 retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
Richard Low6f070842009-05-03 10:26:16 +00008768 ptp_free_objectpropdesc(&opd_format);
rreardon21a3a9d2007-08-15 10:56:31 +00008769 /* Populate the maximum image height */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008770 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleWidth, map_libmtp_type_to_ptp_type(filetype), &opd_width);
rreardon21a3a9d2007-08-15 10:56:31 +00008771 retsam->width = opd_width.FORM.Range.MaximumValue.u32;
Richard Low6f070842009-05-03 10:26:16 +00008772 ptp_free_objectpropdesc(&opd_width);
rreardon21a3a9d2007-08-15 10:56:31 +00008773 /* Populate the maximum image width */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008774 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleHeight, map_libmtp_type_to_ptp_type(filetype), &opd_height);
rreardon21a3a9d2007-08-15 10:56:31 +00008775 retsam->height = opd_height.FORM.Range.MaximumValue.u32;
Richard Low6f070842009-05-03 10:26:16 +00008776 ptp_free_objectpropdesc(&opd_height);
8777 /* Populate the maximum size */
8778 if (support_size) {
8779 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
8780 retsam->size = opd_size.FORM.Range.MaximumValue.u32;
8781 ptp_free_objectpropdesc(&opd_size);
8782 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00008783 *sample = retsam;
8784 } else if (support_data && support_format && !support_height && !support_width && support_duration) {
8785 // Another qualified guess
8786 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008787 /*
8788 * Populate the sample format with the first supported format
8789 *
rreardon21a3a9d2007-08-15 10:56:31 +00008790 * TODO: figure out how to pass back more than one format if more are
8791 * supported by the device.
8792 */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008793 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);
rreardon21a3a9d2007-08-15 10:56:31 +00008794 retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
Richard Low6f070842009-05-03 10:26:16 +00008795 ptp_free_objectpropdesc(&opd_format);
8796 /* Populate the maximum duration */
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008797 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleDuration, map_libmtp_type_to_ptp_type(filetype), &opd_duration);
rreardon21a3a9d2007-08-15 10:56:31 +00008798 retsam->duration = opd_duration.FORM.Range.MaximumValue.u32;
Richard Low6f070842009-05-03 10:26:16 +00008799 ptp_free_objectpropdesc(&opd_duration);
8800 /* Populate the maximum size */
8801 if (support_size) {
8802 ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
8803 retsam->size = opd_size.FORM.Range.MaximumValue.u32;
8804 ptp_free_objectpropdesc(&opd_size);
8805 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00008806 *sample = retsam;
8807 }
8808 return 0;
8809}
rreardon5332f9c2006-12-05 10:18:23 +00008810
8811/**
8812 * This routine sends representative sample data for an object.
8813 * This uses the RepresentativeSampleData property of the album,
8814 * if the device supports it. The data should be of a format acceptable
8815 * to the player (for iRiver and Creative, this seems to be JPEG) and
8816 * must not be too large. (for a Creative, max seems to be about 20KB.)
Richard Low6f070842009-05-03 10:26:16 +00008817 * Check by calling LIBMTP_Get_Representative_Sample_Format() to get
8818 * maximum size, dimensions, etc..
Linus Walleij7e3f2762006-12-03 22:52:05 +00008819 * @param device a pointer to the device which the object is on.
8820 * @param id unique id of the object to set artwork for.
Richard Low36e447c2008-01-20 15:24:55 +00008821 * @param pointer to LIBMTP_filesampledata_t struct containing data
Linus Walleij0c33ec02006-10-27 10:15:40 +00008822 * @return 0 on success, any other value means failure.
Richard Low36e447c2008-01-20 15:24:55 +00008823 * @see LIBMTP_Get_Representative_Sample()
Linus Walleijf1b02f22006-12-06 09:03:23 +00008824 * @see LIBMTP_Get_Representative_Sample_Format()
Linus Walleij0c33ec02006-10-27 10:15:40 +00008825 * @see LIBMTP_Create_New_Album()
8826 */
Linus Walleij7e3f2762006-12-03 22:52:05 +00008827int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *device,
rreardon5332f9c2006-12-05 10:18:23 +00008828 uint32_t const id,
8829 LIBMTP_filesampledata_t *sampledata)
Linus Walleij0c33ec02006-10-27 10:15:40 +00008830{
8831 uint16_t ret;
8832 PTPParams *params = (PTPParams *) device->params;
Richard Lowbb9fb4a2008-05-18 14:49:34 +00008833 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij0c33ec02006-10-27 10:15:40 +00008834 PTPPropertyValue propval;
Linus Walleijd4637502009-06-14 23:03:33 +00008835 PTPObject *ob;
Linus Walleijf0bf4372007-07-01 21:47:38 +00008836 uint32_t i;
Linus Walleijf1b02f22006-12-06 09:03:23 +00008837 uint16_t *props = NULL;
8838 uint32_t propcnt = 0;
8839 int supported = 0;
rreardon5332f9c2006-12-05 10:18:23 +00008840
8841 // get the file format for the object we're going to send representative data for
Linus Walleijd4637502009-06-14 23:03:33 +00008842 ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
8843 if (ret != PTP_RC_OK) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00008844 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): could not get object info.");
rreardon5332f9c2006-12-05 10:18:23 +00008845 return -1;
8846 }
8847
8848 // check that we can send representative sample data for this object format
Linus Walleijd4637502009-06-14 23:03:33 +00008849 ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008850 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00008851 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object properties.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00008852 return -1;
8853 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00008854
Linus Walleij0c33ec02006-10-27 10:15:40 +00008855 for (i = 0; i < propcnt; i++) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00008856 if (props[i] == PTP_OPC_RepresentativeSampleData) {
Linus Walleij0c33ec02006-10-27 10:15:40 +00008857 supported = 1;
Linus Walleijf1b02f22006-12-06 09:03:23 +00008858 break;
8859 }
Linus Walleij0c33ec02006-10-27 10:15:40 +00008860 }
8861 if (!supported) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00008862 free(props);
Linus Walleij070e9b42007-01-22 08:49:28 +00008863 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00008864 return -1;
8865 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00008866 free(props);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008867
Linus Walleijf1b02f22006-12-06 09:03:23 +00008868 // Go ahead and send the data
rreardon5332f9c2006-12-05 10:18:23 +00008869 propval.a.count = sampledata->size;
8870 propval.a.v = malloc(sizeof(PTPPropertyValue) * sampledata->size);
8871 for (i = 0; i < sampledata->size; i++) {
8872 propval.a.v[i].u8 = sampledata->data[i];
Linus Walleij7e3f2762006-12-03 22:52:05 +00008873 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008874
Linus Walleij0c33ec02006-10-27 10:15:40 +00008875 ret = ptp_mtp_setobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
Linus Walleijf1b02f22006-12-06 09:03:23 +00008876 &propval,PTP_DTC_AUINT8);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008877 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00008878 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not send sample data.");
Linus Walleij7e3f2762006-12-03 22:52:05 +00008879 free(propval.a.v);
Linus Walleij0c33ec02006-10-27 10:15:40 +00008880 return -1;
8881 }
Linus Walleij7e3f2762006-12-03 22:52:05 +00008882 free(propval.a.v);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008883
rreardond2ddb632006-12-12 12:13:21 +00008884 /* Set the height and width if the sample is an image, otherwise just
8885 * set the duration and size */
8886 switch(sampledata->filetype) {
Linus Walleije1ac07e2006-12-14 19:38:59 +00008887 case LIBMTP_FILETYPE_JPEG:
8888 case LIBMTP_FILETYPE_JFIF:
8889 case LIBMTP_FILETYPE_TIFF:
8890 case LIBMTP_FILETYPE_BMP:
8891 case LIBMTP_FILETYPE_GIF:
8892 case LIBMTP_FILETYPE_PICT:
8893 case LIBMTP_FILETYPE_PNG:
Linus Walleijfec4d562008-06-01 22:30:36 +00008894 if (!FLAG_BROKEN_SET_SAMPLE_DIMENSIONS(ptp_usb)) {
Richard Lowbb9fb4a2008-05-18 14:49:34 +00008895 // For images, set the height and width
8896 set_object_u32(device, id, PTP_OPC_RepresentativeSampleHeight, sampledata->height);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008897 set_object_u32(device, id, PTP_OPC_RepresentativeSampleWidth, sampledata->width);
Richard Lowbb9fb4a2008-05-18 14:49:34 +00008898 }
Linus Walleije1ac07e2006-12-14 19:38:59 +00008899 break;
8900 default:
8901 // For anything not an image, set the duration and size
8902 set_object_u32(device, id, PTP_OPC_RepresentativeSampleDuration, sampledata->duration);
8903 set_object_u32(device, id, PTP_OPC_RepresentativeSampleSize, sampledata->size);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008904 break;
rreardond2ddb632006-12-12 12:13:21 +00008905 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008906
Linus Walleij0c33ec02006-10-27 10:15:40 +00008907 return 0;
8908}
8909
8910/**
Richard Low36e447c2008-01-20 15:24:55 +00008911 * This routine gets representative sample data for an object.
8912 * This uses the RepresentativeSampleData property of the album,
8913 * if the device supports it.
8914 * @param device a pointer to the device which the object is on.
8915 * @param id unique id of the object to get data for.
8916 * @param pointer to LIBMTP_filesampledata_t struct to receive data
8917 * @return 0 on success, any other value means failure.
8918 * @see LIBMTP_Send_Representative_Sample()
8919 * @see LIBMTP_Get_Representative_Sample_Format()
8920 * @see LIBMTP_Create_New_Album()
8921 */
8922int LIBMTP_Get_Representative_Sample(LIBMTP_mtpdevice_t *device,
8923 uint32_t const id,
8924 LIBMTP_filesampledata_t *sampledata)
8925{
8926 uint16_t ret;
8927 PTPParams *params = (PTPParams *) device->params;
8928 PTPPropertyValue propval;
Linus Walleijd4637502009-06-14 23:03:33 +00008929 PTPObject *ob;
Richard Low36e447c2008-01-20 15:24:55 +00008930 uint32_t i;
8931 uint16_t *props = NULL;
8932 uint32_t propcnt = 0;
8933 int supported = 0;
8934
8935 // get the file format for the object we're going to send representative data for
Linus Walleijd4637502009-06-14 23:03:33 +00008936 ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
8937 if (ret != PTP_RC_OK) {
Richard Low36e447c2008-01-20 15:24:55 +00008938 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): could not get object info.");
8939 return -1;
8940 }
8941
8942 // check that we can store representative sample data for this object format
Linus Walleijd4637502009-06-14 23:03:33 +00008943 ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
Richard Low36e447c2008-01-20 15:24:55 +00008944 if (ret != PTP_RC_OK) {
8945 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get object properties.");
8946 return -1;
8947 }
8948
8949 for (i = 0; i < propcnt; i++) {
8950 if (props[i] == PTP_OPC_RepresentativeSampleData) {
8951 supported = 1;
8952 break;
8953 }
8954 }
8955 if (!supported) {
8956 free(props);
8957 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
8958 return -1;
8959 }
8960 free(props);
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008961
Richard Low36e447c2008-01-20 15:24:55 +00008962 // Get the data
8963 ret = ptp_mtp_getobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
8964 &propval,PTP_DTC_AUINT8);
8965 if (ret != PTP_RC_OK) {
8966 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get sample data.");
8967 return -1;
8968 }
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008969
Richard Low36e447c2008-01-20 15:24:55 +00008970 // Store it
8971 sampledata->size = propval.a.count;
8972 sampledata->data = malloc(sizeof(PTPPropertyValue) * propval.a.count);
8973 for (i = 0; i < propval.a.count; i++) {
8974 sampledata->data[i] = propval.a.v[i].u8;
8975 }
8976 free(propval.a.v);
8977
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008978 // Get the other properties
Richard Low36e447c2008-01-20 15:24:55 +00008979 sampledata->width = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleWidth, 0);
8980 sampledata->height = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleHeight, 0);
8981 sampledata->duration = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleDuration, 0);
8982 sampledata->filetype = map_ptp_type_to_libmtp_type(
8983 get_u16_from_object(device, id, PTP_OPC_RepresentativeSampleFormat, LIBMTP_FILETYPE_UNKNOWN));
Linus Walleij2c1bbd62009-11-07 15:15:03 +00008984
Richard Low36e447c2008-01-20 15:24:55 +00008985 return 0;
8986}
8987
8988/**
Philip Langdale99351a42012-08-18 15:32:12 -07008989 * Retrieve the thumbnail for a file.
8990 * @param device a pointer to the device to get the thumbnail from.
8991 * @param id the object ID of the file to retrieve the thumbnail for.
8992 * @return 0 on success, any other value means failure.
8993 */
8994int LIBMTP_Get_Thumbnail(LIBMTP_mtpdevice_t *device, uint32_t const id,
8995 unsigned char **data, unsigned int *size)
8996{
8997 PTPParams *params = (PTPParams *) device->params;
8998 uint16_t ret;
8999
9000 ret = ptp_getthumb(params, id, data, size);
9001 if (ret == PTP_RC_OK)
9002 return 0;
9003 return -1;
9004}
9005
Philip Langdale31e831c2013-03-04 20:58:19 -08009006
9007int LIBMTP_GetPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9008 uint64_t offset, uint32_t maxbytes,
9009 unsigned char **data, unsigned int *size)
9010{
9011 PTPParams *params = (PTPParams *) device->params;
9012 uint16_t ret;
9013
9014 if (!ptp_operation_issupported(params, PTP_OC_ANDROID_GetPartialObject64)) {
9015 if (!ptp_operation_issupported(params, PTP_OC_GetPartialObject)) {
9016 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9017 "LIBMTP_GetPartialObject: PTP_OC_GetPartialObject not supported");
9018 return -1;
9019 }
9020
9021 if (offset >> 32 != 0) {
9022 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9023 "LIBMTP_GetPartialObject: PTP_OC_GetPartialObject only supports 32bit offsets");
9024 return -1;
9025 }
9026
9027 ret = ptp_getpartialobject(params, id, (uint32_t)offset, maxbytes, data, size);
9028 } else {
9029 ret = ptp_android_getpartialobject64(params, id, offset, maxbytes, data, size);
9030 }
9031 if (ret == PTP_RC_OK)
9032 return 0;
9033 return -1;
9034}
9035
9036
9037int LIBMTP_SendPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9038 uint64_t offset, unsigned char *data, unsigned int size)
9039{
9040 PTPParams *params = (PTPParams *) device->params;
9041 uint16_t ret;
9042
9043 if (!ptp_operation_issupported(params, PTP_OC_ANDROID_SendPartialObject)) {
9044 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9045 "LIBMTP_SendPartialObject: PTP_OC_ANDROID_SendPartialObject not supported");
9046 return -1;
9047 }
9048
9049 ret = ptp_android_sendpartialobject(params, id, offset, data, size);
9050 if (ret == PTP_RC_OK)
9051 return 0;
9052 return -1;
9053}
9054
9055
9056int LIBMTP_BeginEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
9057{
9058 PTPParams *params = (PTPParams *) device->params;
9059 uint16_t ret;
9060
9061 if (!ptp_operation_issupported(params, PTP_OC_ANDROID_BeginEditObject)) {
9062 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9063 "LIBMTP_BeginEditObject: PTP_OC_ANDROID_BeginEditObject not supported");
9064 return -1;
9065 }
9066
9067 ret = ptp_android_begineditobject(params, id);
9068 if (ret == PTP_RC_OK)
9069 return 0;
9070 return -1;
9071}
9072
9073
9074int LIBMTP_EndEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
9075{
9076 PTPParams *params = (PTPParams *) device->params;
9077 uint16_t ret;
9078
9079 if (!ptp_operation_issupported(params, PTP_OC_ANDROID_EndEditObject)) {
9080 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9081 "LIBMTP_EndEditObject: PTP_OC_ANDROID_EndEditObject not supported");
9082 return -1;
9083 }
9084
9085 ret = ptp_android_endeditobject(params, id);
9086 if (ret == PTP_RC_OK) {
9087 // update cached object properties if metadata cache exists
9088 update_metadata_cache(device, id);
9089 return 0;
9090 }
9091 return -1;
9092}
9093
9094
9095int LIBMTP_TruncateObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
9096 uint64_t offset)
9097{
9098 PTPParams *params = (PTPParams *) device->params;
9099 uint16_t ret;
9100
9101 if (!ptp_operation_issupported(params, PTP_OC_ANDROID_TruncateObject)) {
9102 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
9103 "LIBMTP_TruncateObject: PTP_OC_ANDROID_TruncateObject not supported");
9104 return -1;
9105 }
9106
9107 ret = ptp_android_truncate(params, id, offset);
9108 if (ret == PTP_RC_OK)
9109 return 0;
9110 return -1;
9111}
9112
9113
Philip Langdale99351a42012-08-18 15:32:12 -07009114/**
Linus Walleij0c33ec02006-10-27 10:15:40 +00009115 * This routine updates an album based on the metadata
9116 * supplied. If the <code>tracks</code> field of the metadata
9117 * contains a track listing, these tracks will be added to the
9118 * album in place of those already present, i.e. the
9119 * previous track listing will be deleted.
9120 * @param device a pointer to the device to create the new album on.
9121 * @param metadata the metadata for the album to be updated.
9122 * notice that the field <code>album_id</code>
9123 * must contain the apropriate album ID.
9124 * @return 0 on success, any other value means failure.
9125 * @see LIBMTP_Create_New_Album()
9126 * @see LIBMTP_Delete_Object()
9127 */
9128int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device,
9129 LIBMTP_album_t const * const metadata)
9130{
Linus Walleij7783b9b2007-09-22 22:10:44 +00009131 return update_abstract_list(device,
9132 metadata->name,
9133 metadata->artist,
Linus Walleij31b74292008-05-02 23:29:06 +00009134 metadata->composer,
Linus Walleij7783b9b2007-09-22 22:10:44 +00009135 metadata->genre,
9136 metadata->album_id,
9137 PTP_OFC_MTP_AbstractAudioAlbum,
9138 metadata->tracks,
9139 metadata->no_tracks);
Linus Walleij0c33ec02006-10-27 10:15:40 +00009140}
Linus Walleijaa4b0752006-07-26 22:21:04 +00009141
9142/**
9143 * Dummy function needed to interface to upstream
9144 * ptp.c/ptp.h files.
9145 */
9146void ptp_nikon_getptpipguid (unsigned char* guid) {
9147 return;
9148}
tsaarnia3eb60a2007-07-06 17:41:30 +00009149
9150/**
Linus Walleij7752b952007-10-19 20:11:46 +00009151 * Add an object to cache.
9152 * @param device the device which may have a cache to which the object should be added.
9153 * @param object_id the object to add to the cache.
Linus Walleij2c1bbd62009-11-07 15:15:03 +00009154 */
Linus Walleija6d0d482007-10-31 08:54:56 +00009155static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
tsaarnia3eb60a2007-07-06 17:41:30 +00009156{
9157 PTPParams *params = (PTPParams *)device->params;
tsaarnia3eb60a2007-07-06 17:41:30 +00009158 uint16_t ret;
tsaarnia3eb60a2007-07-06 17:41:30 +00009159
Linus Walleija6d0d482007-10-31 08:54:56 +00009160 ret = ptp_add_object_to_cache(params, object_id);
9161 if (ret != PTP_RC_OK) {
9162 add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): couldn't add object to cache");
tsaarnia3eb60a2007-07-06 17:41:30 +00009163 }
9164}
9165
9166
9167/**
9168 * Update cache after object has been modified
Linus Walleija6d0d482007-10-31 08:54:56 +00009169 * @param device the device which may have a cache to which the object should be updated.
9170 * @param object_id the object to update.
tsaarnia3eb60a2007-07-06 17:41:30 +00009171 */
Linus Walleij7752b952007-10-19 20:11:46 +00009172static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
tsaarnia3eb60a2007-07-06 17:41:30 +00009173{
Linus Walleija3d0eaa2007-11-18 22:00:48 +00009174 PTPParams *params = (PTPParams *)device->params;
9175
9176 ptp_remove_object_from_cache(params, object_id);
Linus Walleija6d0d482007-10-31 08:54:56 +00009177 add_object_to_cache(device, object_id);
tsaarnia3eb60a2007-07-06 17:41:30 +00009178}
Marcin Niestroj718b2902017-06-10 16:36:18 +02009179
9180
9181/**
9182 * Issue custom (e.g. vendor specific) operation (without data phase)
9183 * @param device a pointer to the device to send custom operation to.
9184 * @param code operation code to send.
9185 * @param n_param number of parameters passed.
9186 * @param ... uint32_t operation specific parameters.
9187 */
9188int LIBMTP_Custom_Operation(LIBMTP_mtpdevice_t *device, uint16_t code, int n_param, ...)
9189{
9190 PTPParams *params = (PTPParams *) device->params;
9191 PTPContainer ptp;
9192 va_list args;
9193 uint16_t ret;
9194 int i;
9195
9196 ptp.Code = code;
9197 ptp.Nparam = n_param;
9198 va_start(args, n_param);
9199 for (i = 0; i < n_param; i++)
9200 (&ptp.Param1)[i] = va_arg(args, uint32_t);
9201 va_end(args);
9202
9203 ret = ptp_transaction_new(params, &ptp, PTP_DP_NODATA, 0, NULL);
9204
9205 if (ret != PTP_RC_OK) {
9206 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Custom_Operation(): failed to execute operation.");
9207 return -1;
9208 }
9209
9210 return 0;
9211}