blob: eeb159c679fe8edf66f9d9ac18981e1e658fbc92 [file] [log] [blame]
Linus Walleij10c58422006-06-02 08:51:53 +00001/**
2 * \file libmtp.c
Linus Walleij2f45d222007-02-02 22:47:39 +00003 *
4 * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
5 * Copyright (C) 2005-2007 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 Walleij2f45d222007-02-02 22:47:39 +00008 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 *
Linus Walleij10c58422006-06-02 08:51:53 +000024 * This file provides an interface "glue" to the underlying
25 * PTP implementation from libgphoto2. It uses some local
26 * code to convert from/to UTF-8 (stored in unicode.c/.h)
27 * and some small utility functions, mainly for debugging
28 * (stored in util.c/.h).
29 *
mopoke96143402006-10-30 04:37:26 +000030 * The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
Linus Walleij10c58422006-06-02 08:51:53 +000031 * plain copied from the libhphoto2 codebase.
32 *
33 * The files libusb-glue.c/.h are just what they say: an
34 * interface to libusb for the actual, physical USB traffic.
35 */
Linus Walleij2f45d222007-02-02 22:47:39 +000036
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000037#include "libmtp.h"
Linus Walleijb9256fd2006-02-15 09:40:43 +000038#include "unicode.h"
Linus Walleij394bbbe2006-02-22 16:10:53 +000039#include "ptp.h"
Linus Walleij15e344f2006-03-06 15:15:00 +000040#include "libusb-glue.h"
Linus Walleijeb8c6fe2006-02-03 09:46:22 +000041
Linus Walleij7beba572006-11-29 08:56:12 +000042#include <string.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <fcntl.h>
46#ifdef _MSC_VER // For MSVC++
47#define USE_WINDOWS_IO_H
48#include <io.h>
49#endif
50
Linus Walleijc86afbd2006-05-04 19:05:24 +000051/*
52 * This is a mapping between libmtp internal MTP filetypes and
53 * the libgphoto2/PTP equivalent defines. We need this because
54 * otherwise the libmtp.h device has to be dependent on ptp.h
55 * to be installed too, and we don't want that.
56 */
Linus Walleijcf42f452006-11-28 08:32:49 +000057//typedef struct filemap_struct filemap_t;
58typedef struct filemap_struct {
raveloxd9a28642006-05-26 23:42:22 +000059 char *description; /**< Text description for the file type */
60 LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
61 uint16_t ptp_id; /**< PTP ID for the filetype */
Linus Walleijcf42f452006-11-28 08:32:49 +000062 struct filemap_struct *next;
63} filemap_t;
Linus Walleijc86afbd2006-05-04 19:05:24 +000064
Linus Walleijf67bca92006-05-29 09:33:39 +000065// Global variables
Linus Walleij438bd7f2006-06-08 11:35:44 +000066// This holds the global filetype mapping table
Linus Walleijcf42f452006-11-28 08:32:49 +000067static filemap_t *filemap = NULL;
Linus Walleijf67bca92006-05-29 09:33:39 +000068
Linus Walleijcf42f452006-11-28 08:32:49 +000069/*
70 * Forward declarations of local (static) functions.
71 */
72static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
73 uint16_t const ptp_id);
Linus Walleij7beba572006-11-29 08:56:12 +000074static void init_filemap();
Linus Walleij2715c442007-01-20 22:35:29 +000075static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
76 LIBMTP_error_number_t errornumber,
77 char const * const error_text);
Linus Walleij070e9b42007-01-22 08:49:28 +000078static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
79 uint16_t ptp_error,
80 char const * const error_text);
Linus Walleij438bd7f2006-06-08 11:35:44 +000081static void flush_handles(LIBMTP_mtpdevice_t *device);
Richard Low6711f442007-05-05 19:00:59 +000082static void get_handles_recursively(LIBMTP_mtpdevice_t *device, PTPParams *params, PTPObjectHandles *handles, uint32_t parent);
Linus Walleij9e1b0812006-12-12 19:22:02 +000083static void free_storage_list(LIBMTP_mtpdevice_t *device);
84static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
85static uint32_t get_first_storageid(LIBMTP_mtpdevice_t *device);
86static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device,uint64_t *freespace);
Linus Walleijf5fcda32006-12-03 22:31:02 +000087static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize);
Linus Walleijf67bca92006-05-29 09:33:39 +000088static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
89static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
mopoke96143402006-10-30 04:37:26 +000090static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +000091 char **unicstring, uint16_t property);
Linus Walleij9901e222006-11-30 12:28:19 +000092static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
93 uint16_t const attribute_id);
94static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
95 uint16_t const attribute_id, uint32_t const value_default);
96static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
97 uint16_t const attribute_id, uint16_t const value_default);
98static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
99 uint16_t const attribute_id, uint8_t const value_default);
100static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
101 uint16_t const attribute_id, char const * const string);
102static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
103 uint16_t const attribute_id, uint32_t const value);
104static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
105 uint16_t const attribute_id, uint16_t const value);
106static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
107 uint16_t const attribute_id, uint8_t const value);
Linus Walleij8ab54262006-06-21 07:12:28 +0000108static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
109 LIBMTP_track_t *track);
Linus Walleij7beba572006-11-29 08:56:12 +0000110static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
111 char const * const name,
Linus Walleijadce4a52007-04-23 07:28:11 +0000112 char const * const artist,
113 char const * const genre,
Linus Walleij7beba572006-11-29 08:56:12 +0000114 uint32_t const parenthandle,
115 uint16_t const objectformat,
116 char const * const suffix,
117 uint32_t * const newid,
118 uint32_t const * const tracks,
119 uint32_t const no_tracks);
Linus Walleije7df6532007-03-23 08:20:06 +0000120static MTPPropList *new_mtp_prop_entry();
121static MTPPropList *add_mtp_prop_to_proplist(MTPPropList *proplist, MTPPropList *prop);
122static void destroy_mtp_prop_list(MTPPropList *proplist);
tsaarnia3eb60a2007-07-06 17:41:30 +0000123static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t handle);
124static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t handle);
raveloxd9a28642006-05-26 23:42:22 +0000125
Linus Walleijcf42f452006-11-28 08:32:49 +0000126/**
127 * Create a new file mapping entry
128 * @return a newly allocated filemapping entry.
129 */
130static filemap_t *new_filemap_entry()
raveloxd9a28642006-05-26 23:42:22 +0000131{
Linus Walleijcf42f452006-11-28 08:32:49 +0000132 filemap_t *filemap;
mopoke96143402006-10-30 04:37:26 +0000133
Linus Walleijcf42f452006-11-28 08:32:49 +0000134 filemap = (filemap_t *)malloc(sizeof(filemap_t));
mopoke96143402006-10-30 04:37:26 +0000135
Linus Walleijf67bca92006-05-29 09:33:39 +0000136 if( filemap != NULL ) {
137 filemap->description = NULL;
138 filemap->id = LIBMTP_FILETYPE_UNKNOWN;
139 filemap->ptp_id = PTP_OFC_Undefined;
Linus Walleijf67bca92006-05-29 09:33:39 +0000140 filemap->next = NULL;
141 }
mopoke96143402006-10-30 04:37:26 +0000142
Linus Walleijf67bca92006-05-29 09:33:39 +0000143 return filemap;
raveloxd9a28642006-05-26 23:42:22 +0000144}
145
raveloxd9a28642006-05-26 23:42:22 +0000146/**
147 * Register an MTP or PTP filetype for data retrieval
148 *
149 * @param description Text description of filetype
Linus Walleijf0f3d482006-05-29 14:10:21 +0000150 * @param id libmtp internal filetype id
raveloxd9a28642006-05-26 23:42:22 +0000151 * @param ptp_id PTP filetype id
Linus Walleijf0f3d482006-05-29 14:10:21 +0000152 * @return 0 for success any other value means error.
raveloxd9a28642006-05-26 23:42:22 +0000153*/
Linus Walleijcf42f452006-11-28 08:32:49 +0000154static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
155 uint16_t const ptp_id)
raveloxd9a28642006-05-26 23:42:22 +0000156{
Linus Walleijcf42f452006-11-28 08:32:49 +0000157 filemap_t *new = NULL, *current;
raveloxd9a28642006-05-26 23:42:22 +0000158
159 // Has this LIBMTP filetype been registered before ?
160 current = filemap;
161 while (current != NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000162 if(current->id == id) {
163 break;
164 }
Linus Walleijf0f3d482006-05-29 14:10:21 +0000165 current = current->next;
raveloxd9a28642006-05-26 23:42:22 +0000166 }
mopoke96143402006-10-30 04:37:26 +0000167
raveloxd9a28642006-05-26 23:42:22 +0000168 // Create the entry
169 if(current == NULL) {
Linus Walleijf67bca92006-05-29 09:33:39 +0000170 new = new_filemap_entry();
Linus Walleijf0f3d482006-05-29 14:10:21 +0000171 if(new == NULL) {
172 return 1;
173 }
mopoke96143402006-10-30 04:37:26 +0000174
Linus Walleijf67bca92006-05-29 09:33:39 +0000175 new->id = id;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000176 if(description != NULL) {
177 new->description = strdup(description);
178 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000179 new->ptp_id = ptp_id;
mopoke96143402006-10-30 04:37:26 +0000180
Linus Walleijf67bca92006-05-29 09:33:39 +0000181 // Add the entry to the list
182 if(filemap == NULL) {
183 filemap = new;
184 } else {
185 current = filemap;
186 while (current->next != NULL ) current=current->next;
187 current->next = new;
188 }
189 // Update the existing entry
raveloxd9a28642006-05-26 23:42:22 +0000190 } else {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000191 if (current->description != NULL) {
192 free(current->description);
193 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000194 current->description = NULL;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000195 if(description != NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000196 current->description = strdup(description);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000197 }
Linus Walleijf67bca92006-05-29 09:33:39 +0000198 current->ptp_id = ptp_id;
raveloxd9a28642006-05-26 23:42:22 +0000199 }
200
Linus Walleijf0f3d482006-05-29 14:10:21 +0000201 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000202}
203
raveloxd9a28642006-05-26 23:42:22 +0000204static void init_filemap()
205{
Linus Walleij5fb47132006-12-30 15:35:48 +0000206 register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
Linus Walleijcf42f452006-11-28 08:32:49 +0000207 register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
Linus Walleij5fb47132006-12-30 15:35:48 +0000208 register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
209 register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
Linus Walleijcf42f452006-11-28 08:32:49 +0000210 register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
211 register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000212 register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
213 register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
214 register_filetype("MPEG-4 Part 14 Container Format (Audio Empahsis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
215 register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Empahsis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
Linus Walleijcf42f452006-11-28 08:32:49 +0000216 register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
Linus Walleijcf42f452006-11-28 08:32:49 +0000217 register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
218 register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
219 register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
220 register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
221 register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
222 register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
223 register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
224 register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
Linus Walleij5fb47132006-12-30 15:35:48 +0000225 register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
226 register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
Linus Walleijcf42f452006-11-28 08:32:49 +0000227 register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
228 register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
229 register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
230 register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
231 register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
232 register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
233 register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
234 register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
235 register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
236 register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
237 register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
238 register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
239 register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
240 register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
Linus Walleij5fb47132006-12-30 15:35:48 +0000241 register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
242 register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
243 register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
244 register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
245 register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
Linus Walleija05b9802006-12-08 08:50:30 +0000246 register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
Linus Walleijcf42f452006-11-28 08:32:49 +0000247 register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
raveloxd9a28642006-05-26 23:42:22 +0000248}
Linus Walleij16c51f02006-05-04 13:20:22 +0000249
Linus Walleij16c51f02006-05-04 13:20:22 +0000250/**
251 * Returns the PTP filetype that maps to a certain libmtp internal file type.
252 * @param intype the MTP library interface type
253 * @return the PTP (libgphoto2) interface type
254 */
255static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
256{
Linus Walleijcf42f452006-11-28 08:32:49 +0000257 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000258
raveloxd9a28642006-05-26 23:42:22 +0000259 current = filemap;
260
261 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000262 if(current->id == intype) {
263 return current->ptp_id;
264 }
265 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000266 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000267 // printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000268 return PTP_OFC_Undefined;
269}
270
271
272/**
Linus Walleij438bd7f2006-06-08 11:35:44 +0000273 * Returns the PTP internal filetype that maps to a certain libmtp
274 * interface file type.
mopoke96143402006-10-30 04:37:26 +0000275 * @param intype the PTP (libgphoto2) interface type
Linus Walleij8ab54262006-06-21 07:12:28 +0000276 * @return the MTP library interface type
Linus Walleij16c51f02006-05-04 13:20:22 +0000277 */
278static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
279{
Linus Walleijcf42f452006-11-28 08:32:49 +0000280 filemap_t *current;
Linus Walleij16c51f02006-05-04 13:20:22 +0000281
raveloxd9a28642006-05-26 23:42:22 +0000282 current = filemap;
283
284 while (current != NULL) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000285 if(current->ptp_id == intype) {
286 return current->id;
287 }
288 current = current->next;
Linus Walleij16c51f02006-05-04 13:20:22 +0000289 }
Linus Walleij1fd2d272006-05-08 09:22:01 +0000290 // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
Linus Walleij16c51f02006-05-04 13:20:22 +0000291 return LIBMTP_FILETYPE_UNKNOWN;
292}
293
Linus Walleijfa1374c2006-02-27 07:41:46 +0000294
Linus Walleij6946ac52006-03-21 06:51:22 +0000295/**
Linus Walleijf0f3d482006-05-29 14:10:21 +0000296 * Initialize the library. You are only supposed to call this
297 * one, before using the library for the first time in a program.
298 * Never re-initialize libmtp!
299 *
raveloxd9a28642006-05-26 23:42:22 +0000300 * The only thing this does at the moment is to initialise the
301 * filetype mapping table.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000302 */
303void LIBMTP_Init(void)
304{
raveloxd9a28642006-05-26 23:42:22 +0000305 init_filemap();
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000306 return;
307}
308
Linus Walleijcf42f452006-11-28 08:32:49 +0000309
310/**
311 * This helper function returns a textual description for a libmtp
312 * file type to be used in dialog boxes etc.
313 * @param intype the libmtp internal filetype to get a description for.
314 * @return a string representing the filetype, this must <b>NOT</b>
315 * be free():ed by the caller!
316 */
317char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
318{
319 filemap_t *current;
320
321 current = filemap;
322
323 while (current != NULL) {
324 if(current->id == intype) {
325 return current->description;
326 }
327 current = current->next;
328 }
329
330 return "Unknown filetype";
331}
332
333
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000334/**
raveloxd9a28642006-05-26 23:42:22 +0000335 * Retrieves a string from an object
336 *
337 * @param device a pointer to an MTP device.
338 * @param object_id Object reference
339 * @param attribute_id PTP attribute ID
Linus Walleij438bd7f2006-06-08 11:35:44 +0000340 * @return valid string or NULL on failure. The returned string
341 * must bee <code>free()</code>:ed by the caller after
342 * use.
raveloxd9a28642006-05-26 23:42:22 +0000343 */
Linus Walleij9901e222006-11-30 12:28:19 +0000344static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000345 uint16_t const attribute_id)
raveloxd9a28642006-05-26 23:42:22 +0000346{
347 PTPPropertyValue propval;
348 char *retstring = NULL;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000349 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000350 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +0000351
Linus Walleij438bd7f2006-06-08 11:35:44 +0000352 if ( device == NULL || object_id == 0) {
Linus Walleijf0f3d482006-05-29 14:10:21 +0000353 return NULL;
354 }
Linus Walleij338ade42007-07-03 20:44:08 +0000355
356 // This O(n) search should not be used so often, since code
357 // using the cached properties don't usually call this function.
358 if (params->proplist) {
359 MTPPropList *prop = params->proplist;
360
361 while (prop) {
362 if (object_id == prop->ObjectHandle && attribute_id == prop->property) {
Richard Low0fa83522007-07-17 20:05:08 +0000363 if (prop->propval.str != NULL)
364 return strdup(prop->propval.str);
365 else
366 return NULL;
Linus Walleij338ade42007-07-03 20:44:08 +0000367 }
368 prop = prop->next;
369 }
370 }
mopoke96143402006-10-30 04:37:26 +0000371
Linus Walleija823a702006-08-27 21:27:46 +0000372 ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
raveloxd9a28642006-05-26 23:42:22 +0000373 if (ret == PTP_RC_OK) {
Linus Walleija823a702006-08-27 21:27:46 +0000374 if (propval.str != NULL) {
375 retstring = (char *) strdup(propval.str);
376 free(propval.str);
raveloxd9a28642006-05-26 23:42:22 +0000377 }
Linus Walleij070e9b42007-01-22 08:49:28 +0000378 } else {
379 add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
raveloxd9a28642006-05-26 23:42:22 +0000380 }
mopoke96143402006-10-30 04:37:26 +0000381
raveloxd9a28642006-05-26 23:42:22 +0000382 return retstring;
383}
384
385/**
386 * Retrieves an unsigned 32-bit integer from an object attribute
387 *
388 * @param device a pointer to an MTP device.
389 * @param object_id Object reference
390 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +0000391 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +0000392 * @return the value
raveloxd9a28642006-05-26 23:42:22 +0000393 */
Linus Walleij9901e222006-11-30 12:28:19 +0000394static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000395 uint16_t const attribute_id, uint32_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +0000396{
397 PTPPropertyValue propval;
398 uint32_t retval = value_default;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000399 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000400 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +0000401
Linus Walleijf0f3d482006-05-29 14:10:21 +0000402 if ( device == NULL ) {
403 return value_default;
404 }
raveloxd9a28642006-05-26 23:42:22 +0000405
Linus Walleij338ade42007-07-03 20:44:08 +0000406 // This O(n) search should not be used so often, since code
407 // using the cached properties don't usually call this function.
408 if (params->proplist) {
409 MTPPropList *prop = params->proplist;
410
411 while (prop) {
412 if (object_id == prop->ObjectHandle && attribute_id == prop->property) {
413 return prop->propval.u32;
414 }
415 prop = prop->next;
416 }
417 }
418
raveloxd9a28642006-05-26 23:42:22 +0000419 ret = ptp_mtp_getobjectpropvalue(params, object_id,
420 attribute_id,
421 &propval,
422 PTP_DTC_UINT32);
423 if (ret == PTP_RC_OK) {
424 retval = propval.u32;
Linus Walleij070e9b42007-01-22 08:49:28 +0000425 } else {
426 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 +0000427 }
428
429 return retval;
430}
431
432/**
433 * Retrieves an unsigned 16-bit integer from an object attribute
434 *
435 * @param device a pointer to an MTP device.
436 * @param object_id Object reference
437 * @param attribute_id PTP attribute ID
Linus Walleij5acfa742006-05-29 14:51:59 +0000438 * @param value_default Default value to return on failure
Linus Walleijf0f3d482006-05-29 14:10:21 +0000439 * @return a value
raveloxd9a28642006-05-26 23:42:22 +0000440 */
Linus Walleij9901e222006-11-30 12:28:19 +0000441static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000442 uint16_t const attribute_id, uint16_t const value_default)
raveloxd9a28642006-05-26 23:42:22 +0000443{
444 PTPPropertyValue propval;
445 uint16_t retval = value_default;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000446 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000447 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000448
Linus Walleijf0f3d482006-05-29 14:10:21 +0000449 if ( device == NULL ) {
450 return value_default;
451 }
raveloxd9a28642006-05-26 23:42:22 +0000452
Linus Walleij338ade42007-07-03 20:44:08 +0000453 // This O(n) search should not be used so often, since code
454 // using the cached properties don't usually call this function.
455 if (params->proplist) {
456 MTPPropList *prop = params->proplist;
457
458 while (prop) {
459 if (object_id == prop->ObjectHandle && attribute_id == prop->property) {
460 return prop->propval.u16;
461 }
462 prop = prop->next;
463 }
464 }
465
raveloxd9a28642006-05-26 23:42:22 +0000466 ret = ptp_mtp_getobjectpropvalue(params, object_id,
467 attribute_id,
468 &propval,
469 PTP_DTC_UINT16);
470 if (ret == PTP_RC_OK) {
471 retval = propval.u16;
Linus Walleij070e9b42007-01-22 08:49:28 +0000472 } else {
473 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 +0000474 }
mopoke96143402006-10-30 04:37:26 +0000475
raveloxd9a28642006-05-26 23:42:22 +0000476 return retval;
477}
478
479/**
Linus Walleij99310d42006-11-01 08:29:39 +0000480 * Retrieves an unsigned 8-bit integer from an object attribute
481 *
482 * @param device a pointer to an MTP device.
483 * @param object_id Object reference
484 * @param attribute_id PTP attribute ID
485 * @param value_default Default value to return on failure
486 * @return a value
487 */
Linus Walleij9901e222006-11-30 12:28:19 +0000488static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij99310d42006-11-01 08:29:39 +0000489 uint16_t const attribute_id, uint8_t const value_default)
490{
491 PTPPropertyValue propval;
492 uint8_t retval = value_default;
493 PTPParams *params = (PTPParams *) device->params;
494 uint16_t ret;
495
496 if ( device == NULL ) {
497 return value_default;
498 }
499
Linus Walleij338ade42007-07-03 20:44:08 +0000500 // This O(n) search should not be used so often, since code
501 // using the cached properties don't usually call this function.
502 if (params->proplist) {
503 MTPPropList *prop = params->proplist;
504
505 while (prop) {
506 if (object_id == prop->ObjectHandle && attribute_id == prop->property) {
507 return prop->propval.u8;
508 }
509 prop = prop->next;
510 }
511 }
512
Linus Walleij99310d42006-11-01 08:29:39 +0000513 ret = ptp_mtp_getobjectpropvalue(params, object_id,
514 attribute_id,
515 &propval,
516 PTP_DTC_UINT8);
517 if (ret == PTP_RC_OK) {
518 retval = propval.u8;
Linus Walleij070e9b42007-01-22 08:49:28 +0000519 } else {
520 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 +0000521 }
522
523 return retval;
524}
525
526/**
raveloxd9a28642006-05-26 23:42:22 +0000527 * Sets an object attribute from a string
528 *
529 * @param device a pointer to an MTP device.
530 * @param object_id Object reference
531 * @param attribute_id PTP attribute ID
532 * @param string string value to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000533 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000534 */
Linus Walleij9901e222006-11-30 12:28:19 +0000535static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000536 uint16_t const attribute_id, char const * const string)
raveloxd9a28642006-05-26 23:42:22 +0000537{
538 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000539 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000540 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000541
Linus Walleijf0f3d482006-05-29 14:10:21 +0000542 if (device == NULL || string == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000543 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000544 }
Linus Walleijb2753182007-02-26 08:11:38 +0000545
546 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
547 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
548 "PTP_OC_MTP_SetObjectPropValue not supported.");
549 return -1;
550 }
Linus Walleija823a702006-08-27 21:27:46 +0000551 propval.str = (char *) string;
552 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000553 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000554 add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
Linus Walleij438bd7f2006-06-08 11:35:44 +0000555 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000556 }
mopoke96143402006-10-30 04:37:26 +0000557
Linus Walleijf0f3d482006-05-29 14:10:21 +0000558 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000559}
560
561/**
562 * Sets an object attribute from an unsigned 32-bit integer
563 *
564 * @param device a pointer to an MTP device.
565 * @param object_id Object reference
566 * @param attribute_id PTP attribute ID
567 * @param value 32-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000568 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000569 */
Linus Walleij9901e222006-11-30 12:28:19 +0000570static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000571 uint16_t const attribute_id, uint32_t const value)
raveloxd9a28642006-05-26 23:42:22 +0000572{
573 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000574 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000575 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000576
Linus Walleijf0f3d482006-05-29 14:10:21 +0000577 if (device == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +0000578 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000579 }
raveloxd9a28642006-05-26 23:42:22 +0000580
Linus Walleijb2753182007-02-26 08:11:38 +0000581 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
582 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
583 "PTP_OC_MTP_SetObjectPropValue not supported.");
584 return -1;
585 }
raveloxd9a28642006-05-26 23:42:22 +0000586 propval.u32 = value;
587 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000588 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000589 add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
Linus Walleij438bd7f2006-06-08 11:35:44 +0000590 return -1;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000591 }
mopoke96143402006-10-30 04:37:26 +0000592
Linus Walleijf0f3d482006-05-29 14:10:21 +0000593 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000594}
595
596/**
597 * Sets an object attribute from an unsigned 16-bit integer
598 *
599 * @param device a pointer to an MTP device.
600 * @param object_id Object reference
601 * @param attribute_id PTP attribute ID
602 * @param value 16-bit unsigned integer to set
Linus Walleijf0f3d482006-05-29 14:10:21 +0000603 * @return 0 on success, any other value means failure
raveloxd9a28642006-05-26 23:42:22 +0000604 */
Linus Walleij9901e222006-11-30 12:28:19 +0000605static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
Linus Walleij4ef39e62006-09-19 14:11:52 +0000606 uint16_t const attribute_id, uint16_t const value)
raveloxd9a28642006-05-26 23:42:22 +0000607{
608 PTPPropertyValue propval;
Linus Walleijf0f3d482006-05-29 14:10:21 +0000609 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +0000610 uint16_t ret;
raveloxd9a28642006-05-26 23:42:22 +0000611
Linus Walleijf0f3d482006-05-29 14:10:21 +0000612 if (device == NULL) {
613 return 1;
614 }
raveloxd9a28642006-05-26 23:42:22 +0000615
Linus Walleijb2753182007-02-26 08:11:38 +0000616 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
617 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
618 "PTP_OC_MTP_SetObjectPropValue not supported.");
619 return -1;
620 }
raveloxd9a28642006-05-26 23:42:22 +0000621 propval.u16 = value;
622 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
Linus Walleijf0f3d482006-05-29 14:10:21 +0000623 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000624 add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +0000625 return 1;
626 }
raveloxd9a28642006-05-26 23:42:22 +0000627
Linus Walleijf0f3d482006-05-29 14:10:21 +0000628 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000629}
630
631/**
Linus Walleij99310d42006-11-01 08:29:39 +0000632 * Sets an object attribute from an unsigned 8-bit integer
633 *
634 * @param device a pointer to an MTP device.
635 * @param object_id Object reference
636 * @param attribute_id PTP attribute ID
637 * @param value 8-bit unsigned integer to set
638 * @return 0 on success, any other value means failure
639 */
Linus Walleij9901e222006-11-30 12:28:19 +0000640static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
641 uint16_t const attribute_id, uint8_t const value)
Linus Walleij99310d42006-11-01 08:29:39 +0000642{
643 PTPPropertyValue propval;
644 PTPParams *params = (PTPParams *) device->params;
645 uint16_t ret;
646
647 if (device == NULL) {
648 return 1;
649 }
650
Linus Walleijb2753182007-02-26 08:11:38 +0000651 if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
652 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
653 "PTP_OC_MTP_SetObjectPropValue not supported.");
654 return -1;
655 }
Linus Walleij99310d42006-11-01 08:29:39 +0000656 propval.u8 = value;
657 ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
658 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +0000659 add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
Linus Walleijf0f3d482006-05-29 14:10:21 +0000660 return 1;
raveloxd9a28642006-05-26 23:42:22 +0000661 }
mopoke96143402006-10-30 04:37:26 +0000662
Linus Walleijf0f3d482006-05-29 14:10:21 +0000663 return 0;
raveloxd9a28642006-05-26 23:42:22 +0000664}
665
raveloxd9a28642006-05-26 23:42:22 +0000666/**
Linus Walleij039d1dd2007-02-23 22:34:07 +0000667 * Get the first (as in "first in the list of") connected MTP device.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000668 * @return a device pointer.
Linus Walleij039d1dd2007-02-23 22:34:07 +0000669 * @see LIBMTP_Get_Connected_Devices()
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000670 */
Linus Walleijb9256fd2006-02-15 09:40:43 +0000671LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000672{
tedbullocke7713642007-02-18 23:10:09 +0000673 LIBMTP_mtpdevice_t *first_device = NULL;
674
tedbullocke7713642007-02-18 23:10:09 +0000675 switch(LIBMTP_Get_Connected_Devices(&first_device))
676 {
677 /* Specific Errors or Messages that connect_mtp_devices should return */
tedbullock433d2172007-02-23 22:39:12 +0000678 case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
tedbullocke7713642007-02-18 23:10:09 +0000679 fprintf(stderr, "LIBMTP_Get_First_Device: No Devices Attached\n");
Richard Low0f794762007-02-23 22:06:27 +0000680 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000681
682 case LIBMTP_ERROR_CONNECTING:
683 fprintf(stderr, "LIBMTP_Get_First_Device: Error Connecting\n");
Richard Low0f794762007-02-23 22:06:27 +0000684 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000685
686 case LIBMTP_ERROR_MEMORY_ALLOCATION:
687 fprintf(stderr, "LIBMTP_Get_First_Device: Memory Alloc Error\n");
Richard Low0f794762007-02-23 22:06:27 +0000688 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000689
690 /* Unknown general errors - This should never execute */
691 case LIBMTP_ERROR_GENERAL:
692 default:
693 fprintf(stderr, "LIBMTP_Get_First_Device: Unknown Connection Error\n");
Richard Low0f794762007-02-23 22:06:27 +0000694 return NULL;
tedbullocke7713642007-02-18 23:10:09 +0000695
696 /* Successfully connect at least one device, so continue */
697 case LIBMTP_ERROR_NONE:
tedbullocke7713642007-02-18 23:10:09 +0000698 break;
Linus Walleija8a19cc2007-02-02 22:13:17 +0000699 }
tedbullock44b0ff72007-02-22 22:20:28 +0000700
701 /* Only return the first device, release the rest */
702 if(first_device->next != NULL)
703 {
704 LIBMTP_Release_Device_List(first_device->next);
705 first_device->next = NULL;
706 }
tedbullocke7713642007-02-18 23:10:09 +0000707
708 return first_device;
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000709}
710
711/**
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000712 * Recursive function that adds MTP devices to a linked list
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000713 * @param numdevices The number of detected USB devices
714 * @param interface_number Dynamic array of interface numbers
715 * @param params Dynamic array of PTP parameters
716 * @param ptp_usb Dynamic array of USB PTP devices
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000717 * @return a device pointer to a newly created mtpdevice (used in linked
718 * list creation
719 */
Linus Walleij45a86372007-03-07 09:36:19 +0000720static LIBMTP_mtpdevice_t * create_usb_mtp_devices(mtpdevice_list_t *devices)
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000721{
Linus Walleij45a86372007-03-07 09:36:19 +0000722 uint8_t i = 1;
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000723 LIBMTP_mtpdevice_t *mtp_device_list = NULL;
724 LIBMTP_mtpdevice_t *current_device = NULL;
725 PTPParams *current_params;
Linus Walleij45a86372007-03-07 09:36:19 +0000726 mtpdevice_list_t *tmplist = devices;
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000727
Linus Walleij45a86372007-03-07 09:36:19 +0000728 while (tmplist != NULL) {
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000729 LIBMTP_mtpdevice_t *mtp_device;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000730
731 /* Clear any handlers */
Linus Walleij45a86372007-03-07 09:36:19 +0000732 tmplist->params->handles.Handler = NULL;
Linus Walleijf0bf4372007-07-01 21:47:38 +0000733 tmplist->params->objectinfo = NULL;
Linus Walleij338ade42007-07-03 20:44:08 +0000734 tmplist->params->proplist = NULL;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000735
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000736 /* Allocate dynamic space for our device */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000737 mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000738
739 /* Check if there was a memory allocation error */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000740 if(mtp_device == NULL) {
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000741 /* There has been an memory allocation error. We are going to ignore this
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000742 device and attempt to continue */
743
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000744 /* TODO: This error statement could probably be a bit more robust */
Linus Walleij68b19c02007-02-15 11:50:37 +0000745 fprintf(stderr, "LIBMTP PANIC: connect_usb_devices encountered a memory "
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000746 "allocation error with device %u, trying to continue",
747 i);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000748
749 /* Prevent memory leaks for this device */
Linus Walleij45a86372007-03-07 09:36:19 +0000750 free(tmplist->ptp_usb);
751 tmplist->ptp_usb = NULL;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000752
Linus Walleij45a86372007-03-07 09:36:19 +0000753 free(tmplist->params);
754 tmplist->params = NULL;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000755
756 /* We have freed a bit of memory so try again with the next device */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000757 continue;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000758 }
759
tedbullock69a445b2007-02-15 07:41:04 +0000760 /* Copy device information to mtp_device structure */
Linus Walleij45a86372007-03-07 09:36:19 +0000761 mtp_device->interface_number = tmplist->interface_number;
762 mtp_device->params = tmplist->params;
763 mtp_device->usbinfo = tmplist->ptp_usb;
764 current_params = tmplist->params;
tedbullock69a445b2007-02-15 07:41:04 +0000765
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000766 /* Cache the device information for later use */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000767 if (ptp_getdeviceinfo(current_params,
768 &current_params->deviceinfo) != PTP_RC_OK) {
Linus Walleij68b19c02007-02-15 11:50:37 +0000769 fprintf(stderr, "LIBMTP PANIC: Unable to read device information on device "
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000770 "number %u, trying to continue", i);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000771
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000772 /* Prevent memory leaks for this device */
Linus Walleij45a86372007-03-07 09:36:19 +0000773 free(tmplist->ptp_usb);
774 tmplist->ptp_usb = NULL;
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000775
776 free(current_params);
777 current_params = NULL;
tedbullock69a445b2007-02-15 07:41:04 +0000778 free(mtp_device);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000779
780 /* try again with the next device */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000781 continue;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000782 }
783
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000784 /* No Errors yet for this device */
785 mtp_device->errorstack = NULL;
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000786
tedbullock0f033cb2007-02-14 20:56:54 +0000787 /* Cache the device information */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000788 if (ptp_getdeviceinfo(current_params,
789 &current_params->deviceinfo) != PTP_RC_OK) {
tedbullock0f033cb2007-02-14 20:56:54 +0000790 add_error_to_errorstack(mtp_device,
Linus Walleij68b19c02007-02-15 11:50:37 +0000791 LIBMTP_ERROR_CONNECTING,
792 "Unable to read device information. Recommend "
793 "disconnecting this device\n");
tedbullock0f033cb2007-02-14 20:56:54 +0000794 }
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000795
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000796 /* Default Max Battery Level, we will adjust this if possible */
797 mtp_device->maximum_battery_level = 100;
798
799 /* Check if device supports reading maximum battery level */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000800 if(ptp_property_issupported( current_params,
801 PTP_DPC_BatteryLevel)) {
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000802 PTPDevicePropDesc dpd;
803
804 /* Try to read maximum battery level */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000805 if(ptp_getdevicepropdesc(current_params,
806 PTP_DPC_BatteryLevel,
807 &dpd) != PTP_RC_OK) {
808 add_error_to_errorstack(mtp_device,
809 LIBMTP_ERROR_CONNECTING,
810 "Unable to read Maximum Battery Level for this "
811 "device even though the device supposedly "
812 "supports this functionality");
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000813 }
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000814
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000815 /* TODO: is this appropriate? */
816 /* If max battery level is 0 then leave the default, otherwise assign */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000817 if (dpd.FORM.Range.MaximumValue.u8 != 0) {
818 mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000819 }
820
821 ptp_free_devicepropdesc(&dpd);
822 }
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000823
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000824 /* Set all default folders to 0 (root directory) */
825 mtp_device->default_music_folder = 0;
826 mtp_device->default_playlist_folder = 0;
827 mtp_device->default_picture_folder = 0;
828 mtp_device->default_video_folder = 0;
829 mtp_device->default_organizer_folder = 0;
830 mtp_device->default_zencast_folder = 0;
831 mtp_device->default_album_folder = 0;
832 mtp_device->default_text_folder = 0;
833
834 /*
835 * Then get the handles and try to locate the default folders.
836 * This has the desired side effect of caching all handles from
837 * the device which speeds up later operations.
838 */
839 flush_handles(mtp_device);
840
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000841 /* Set initial storage information */
842 mtp_device->storage = NULL;
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000843 if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) {
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000844 add_error_to_errorstack(mtp_device,
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000845 LIBMTP_ERROR_GENERAL,
846 "Get Storage information failed.");
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000847 }
848
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000849 /* Add the device to the list */
850 mtp_device->next = NULL;
851 if (mtp_device_list == NULL) {
852 mtp_device_list = current_device = mtp_device;
853 } else {
854 current_device->next = mtp_device;
855 current_device = mtp_device;
856 }
Linus Walleij45a86372007-03-07 09:36:19 +0000857
858 tmplist = tmplist->next;
859 i++;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000860 }
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000861 return mtp_device_list;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000862}
863
864/**
tedbullock848009b2007-03-03 23:20:12 +0000865 * Get the number of devices that are available in the listed device list
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000866 * @param device_list Pointer to a linked list of devices
867 * @return Number of devices in the device list device_list
868 * @see LIBMTP_Get_Connected_Devices()
tedbullock848009b2007-03-03 23:20:12 +0000869 */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000870uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *device_list)
tedbullock848009b2007-03-03 23:20:12 +0000871{
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000872 uint32_t numdevices = 0;
873 LIBMTP_mtpdevice_t *iter;
874 for(iter = device_list; iter != NULL; iter = iter->next)
875 numdevices++;
876
877 return numdevices;
tedbullock848009b2007-03-03 23:20:12 +0000878}
879
880/**
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000881 * Get the first connected MTP device node in the linked list of devices.
882 * Currently this only provides access to USB devices
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000883 * @param device_list A list of devices ready to be used by the caller. You
884 * need to know how many there are.
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000885 * @return Any error information gathered from device connections
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000886 * @see LIBMTP_Number_Devices_In_List()
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000887 */
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000888LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_list)
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000889{
Linus Walleij45a86372007-03-07 09:36:19 +0000890 mtpdevice_list_t *devices;
Linus Walleij5fbb77d2007-03-07 08:40:36 +0000891 LIBMTP_error_number_t ret;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000892
Linus Walleij45a86372007-03-07 09:36:19 +0000893 ret = find_usb_devices(&devices);
Linus Walleij5fbb77d2007-03-07 08:40:36 +0000894 if (ret != LIBMTP_ERROR_NONE) {
Linus Walleijf27d1cd2007-03-05 14:23:00 +0000895 *device_list = NULL;
Linus Walleij5fbb77d2007-03-07 08:40:36 +0000896 return ret;
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000897 }
898
899 /* Assign linked list of devices */
Linus Walleij45a86372007-03-07 09:36:19 +0000900 *device_list = create_usb_mtp_devices(devices);
901
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000902 /* TODO: Add wifi device access here */
Linus Walleij45a86372007-03-07 09:36:19 +0000903 free_mtpdevice_list(devices);
Linus Walleij2d3f7b82007-02-14 09:24:20 +0000904
905 return LIBMTP_ERROR_NONE;
906}
907
908/**
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000909 * This closes and releases an allocated MTP device.
Linus Walleijb9256fd2006-02-15 09:40:43 +0000910 * @param device a pointer to the MTP device to release.
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000911 */
tedbullock0f033cb2007-02-14 20:56:54 +0000912void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
913{
914 if(device != NULL)
915 {
916 if(device->next != NULL)
917 {
918 LIBMTP_Release_Device_List(device->next);
919 }
920
921 LIBMTP_Release_Device(device);
922 }
923}
924
925/**
926 * This closes and releases an allocated MTP device.
927 * @param device a pointer to the MTP device to release.
928 */
Linus Walleijb9256fd2006-02-15 09:40:43 +0000929void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000930{
Linus Walleij9b28da32006-03-16 13:47:58 +0000931 PTPParams *params = (PTPParams *) device->params;
Linus Walleij2d411db2006-03-22 12:13:09 +0000932 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij9b28da32006-03-16 13:47:58 +0000933
Linus Walleij2d411db2006-03-22 12:13:09 +0000934 close_device(ptp_usb, params, device->interface_number);
Linus Walleij2715c442007-01-20 22:35:29 +0000935 // Clear error stack
936 LIBMTP_Clear_Errorstack(device);
Linus Walleij3ec86312006-08-21 13:25:24 +0000937 // Free iconv() converters...
Linus Walleija823a702006-08-27 21:27:46 +0000938 iconv_close(params->cd_locale_to_ucs2);
939 iconv_close(params->cd_ucs2_to_locale);
tedbullock69a445b2007-02-15 07:41:04 +0000940 free(ptp_usb);
Linus Walleij073c4172007-02-02 22:26:33 +0000941 ptp_free_params(params);
Linus Walleij9e1b0812006-12-12 19:22:02 +0000942 free_storage_list(device);
Linus Walleijeb8c6fe2006-02-03 09:46:22 +0000943 free(device);
944}
Linus Walleijb9256fd2006-02-15 09:40:43 +0000945
946/**
Linus Walleij2715c442007-01-20 22:35:29 +0000947 * This can be used by any libmtp-intrinsic code that
Linus Walleij68b19c02007-02-15 11:50:37 +0000948 * need to stack up an error on the stack. You are only
949 * supposed to add errors to the error stack using this
950 * function, do not create and reference error entries
951 * directly.
Linus Walleij2715c442007-01-20 22:35:29 +0000952 */
953static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
954 LIBMTP_error_number_t errornumber,
955 char const * const error_text)
956{
957 LIBMTP_error_t *newerror;
958
Linus Walleij68b19c02007-02-15 11:50:37 +0000959 if (device == NULL) {
960 fprintf(stderr, "LIBMTP PANIC: Trying to add error to a NULL device!\n");
961 return;
962 }
Linus Walleij2715c442007-01-20 22:35:29 +0000963 newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
964 newerror->errornumber = errornumber;
965 newerror->error_text = strdup(error_text);
rreardon774503c2007-02-15 15:52:49 +0000966 newerror->next = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +0000967 if (device->errorstack == NULL) {
968 device->errorstack = newerror;
969 } else {
970 LIBMTP_error_t *tmp = device->errorstack;
971
972 while (tmp->next != NULL) {
973 tmp = tmp->next;
974 }
975 tmp->next = newerror;
976 }
977}
978
979/**
Linus Walleij070e9b42007-01-22 08:49:28 +0000980 * Adds an error from the PTP layer to the error stack.
981 */
982static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
983 uint16_t ptp_error,
984 char const * const error_text)
985{
Linus Walleij68b19c02007-02-15 11:50:37 +0000986 if (device == NULL) {
987 fprintf(stderr, "LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
988 return;
989 } else {
990 char outstr[256];
991 snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
992 outstr[sizeof(outstr)-1] = '\0';
993 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
994 add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, "(Look this up in ptp.h for an explanation.)");
995 }
Linus Walleij070e9b42007-01-22 08:49:28 +0000996}
997
998/**
Linus Walleij2715c442007-01-20 22:35:29 +0000999 * This returns the error stack for a device in case you
1000 * need to either reference the error numbers (e.g. when
1001 * creating multilingual apps with multiple-language text
1002 * representations for each error number) or when you need
1003 * to build a multi-line error text widget or something like
1004 * that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
1005 * to clear it when you're finished with it.
1006 * @param device a pointer to the MTP device to get the error
1007 * stack for.
1008 * @return the error stack or NULL if there are no errors
1009 * on the stack.
1010 * @see LIBMTP_Clear_Errorstack()
1011 * @see LIBMTP_Dump_Errorstack()
1012 */
1013LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
1014{
Linus Walleij68b19c02007-02-15 11:50:37 +00001015 if (device == NULL) {
1016 fprintf(stderr, "LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
1017 }
Linus Walleij2715c442007-01-20 22:35:29 +00001018 return device->errorstack;
1019}
1020
1021/**
1022 * This function clears the error stack of a device and frees
1023 * any memory used by it. Call this when you're finished with
1024 * using the errors.
1025 * @param device a pointer to the MTP device to clear the error
1026 * stack for.
1027 */
1028void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
1029{
Linus Walleij68b19c02007-02-15 11:50:37 +00001030 if (device == NULL) {
1031 fprintf(stderr, "LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
1032 } else {
1033 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2715c442007-01-20 22:35:29 +00001034
Linus Walleij68b19c02007-02-15 11:50:37 +00001035 while (tmp != NULL) {
1036 LIBMTP_error_t *tmp2;
1037
1038 if (tmp->error_text != NULL) {
1039 free(tmp->error_text);
1040 }
1041 tmp2 = tmp;
1042 tmp = tmp->next;
1043 free(tmp2);
Linus Walleij2715c442007-01-20 22:35:29 +00001044 }
Linus Walleij68b19c02007-02-15 11:50:37 +00001045 device->errorstack = NULL;
Linus Walleij2715c442007-01-20 22:35:29 +00001046 }
Linus Walleij2715c442007-01-20 22:35:29 +00001047}
1048
1049/**
1050 * This function dumps the error stack to <code>stderr</code>.
1051 * (You still have to clear the stack though.)
1052 * @param device a pointer to the MTP device to dump the error
1053 * stack for.
1054 */
1055void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
1056{
Linus Walleij68b19c02007-02-15 11:50:37 +00001057 if (device == NULL) {
1058 fprintf(stderr, "LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
1059 } else {
1060 LIBMTP_error_t *tmp = device->errorstack;
Linus Walleij2715c442007-01-20 22:35:29 +00001061
Linus Walleij68b19c02007-02-15 11:50:37 +00001062 while (tmp != NULL) {
1063 if (tmp->error_text != NULL) {
1064 fprintf(stderr, "Error %d: %s\n", tmp->errornumber, tmp->error_text);
1065 } else {
1066 fprintf(stderr, "Error %d: (unknown)\n", tmp->errornumber);
1067 }
1068 tmp = tmp->next;
Linus Walleij2715c442007-01-20 22:35:29 +00001069 }
Linus Walleij2715c442007-01-20 22:35:29 +00001070 }
1071}
1072
1073/**
Linus Walleij338ade42007-07-03 20:44:08 +00001074 * This command gets all handles and stuff by FAST directory retrieveal
1075 * which is available by getting all metadata for object
1076 * <code>0xffffffff</code> which simply means "all metadata for all objects".
1077 * This works on the vast majority of MTP devices (there ARE exceptions!)
1078 * and is quite quick. Check the error stack to see if there were
1079 * problems getting the metadata.
1080 */
1081static void get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
1082{
1083 PTPParams *params = (PTPParams *) device->params;
1084 int cnt = 0;
1085 int i;
1086 uint32_t lasthandle = 0xffffffff;
1087 MTPPropList *proplist = NULL;
1088 MTPPropList *prop;
1089 uint16_t ret;
1090
1091 ret = ptp_mtp_getobjectproplist (params, 0xffffffff, &proplist);
1092 if (ret != PTP_RC_OK) {
1093 add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): could not get all object proplist.");
1094 return;
1095 }
1096 params->proplist = proplist; /* cache it */
1097
1098 /*
1099 * We count the number of objects by counting the ObjectHandle
1100 * references, whenever it changes we get a new objects, when it's
1101 * the same, it is just different properties of the same object.
1102 */
1103 prop = proplist;
1104 while (prop) {
1105 if (lasthandle != prop->ObjectHandle) {
1106 cnt++;
1107 lasthandle = prop->ObjectHandle;
1108 }
1109 prop = prop->next;
1110 }
Linus Walleij338ade42007-07-03 20:44:08 +00001111 lasthandle = 0xffffffff;
1112 params->objectinfo = malloc (sizeof (PTPObjectInfo) * cnt);
1113 memset (params->objectinfo, 0, sizeof(PTPObjectInfo) * cnt);
1114 params->handles.Handler = malloc (sizeof (uint32_t) * cnt);
1115 params->handles.n = cnt;
1116
1117 prop = proplist;
1118 i = -1;
1119 while (prop) {
1120 if (lasthandle != prop->ObjectHandle) {
1121 if (i >= 0) {
1122 if (!params->objectinfo[i].Filename) {
1123 /* I have one such file on my Creative */
1124 params->objectinfo[i].Filename = strdup("<null>");
1125 }
1126 }
1127 i++;
1128 lasthandle = prop->ObjectHandle;
1129 params->handles.Handler[i] = prop->ObjectHandle;
1130 }
1131 switch (prop->property) {
1132 case PTP_OPC_ParentObject:
1133 params->objectinfo[i].ParentObject = prop->propval.u32;
1134 break;
1135 case PTP_OPC_ObjectFormat:
1136 params->objectinfo[i].ObjectFormat = prop->propval.u16;
1137 break;
1138 case PTP_OPC_ObjectSize:
1139 params->objectinfo[i].ObjectCompressedSize = prop->propval.u32;
1140 break;
1141 case PTP_OPC_StorageID:
1142 params->objectinfo[i].StorageID = prop->propval.u32;
1143 break;
1144 case PTP_OPC_ObjectFileName:
Richard Low8e8d9d42007-07-14 10:36:50 +00001145 if (prop->propval.str != NULL)
1146 params->objectinfo[i].Filename = strdup(prop->propval.str);
Linus Walleij338ade42007-07-03 20:44:08 +00001147 break;
1148 default:
1149 /*
1150 * This was in libgphoto2 no idea what it tests for...
1151 * if ((prop->property & 0xfff0) == 0xdc00)
1152 * gp_log (GP_LOG_DEBUG, "ptp2/mtpfast", "case %x type %x unhandled.\n", prop->property, prop->datatype);
1153 */
1154 break;
1155 // FIXME: the rest of the metadata is readily available right here!
1156 }
1157 prop = prop->next;
1158 }
1159}
1160
1161/**
1162 * This function will recurse through all the directories on the device,
1163 * starting at the root directory, gathering metadata as it moves along.
1164 * It works better on some devices that will only return data for a
1165 * certain directory and does not respect the option to get all metadata
1166 * for all objects.
1167 */
1168static void get_handles_recursively(LIBMTP_mtpdevice_t *device, PTPParams *params, PTPObjectHandles *handles, uint32_t parent)
1169{
1170 PTPObjectHandles currentHandles;
1171 int i = 0;
1172 uint32_t old_handles;
1173
1174 uint16_t ret = ptp_getobjecthandles(params,
1175 PTP_GOH_ALL_STORAGE,
1176 PTP_GOH_ALL_FORMATS,
1177 parent,
1178 &currentHandles);
1179
1180 if (ret != PTP_RC_OK) {
1181 add_ptp_error_to_errorstack(device, ret, "get_handles_recursively(): could not get object handles.");
1182 return;
1183 }
1184
1185 if (currentHandles.Handler == NULL || currentHandles.n == 0)
1186 return;
1187
1188 old_handles = handles->n;
1189
1190 // Realloc main space
1191 handles->Handler = (uint32_t *) realloc(handles->Handler,
1192 (old_handles + currentHandles.n) * sizeof(uint32_t));
1193 // Realloc object info cache
1194 params->objectinfo = (PTPObjectInfo*) realloc(params->objectinfo,
1195 (old_handles + currentHandles.n) * sizeof(PTPObjectInfo));
1196 memset(&params->objectinfo[old_handles], 0, currentHandles.n * sizeof(PTPObjectInfo));
1197
1198 // Copy new handles
1199 memcpy(&(handles->Handler[old_handles]), currentHandles.Handler, currentHandles.n * sizeof(uint32_t));
1200 handles->n = old_handles + currentHandles.n;
1201
1202 // Now descend into any subdirectories found
1203 for (i = 0; i < currentHandles.n; i++) {
1204 ret = ptp_getobjectinfo(params,
1205 currentHandles.Handler[i],
1206 &params->objectinfo[old_handles + i]);
1207
1208 if (ret == PTP_RC_OK) {
1209 PTPObjectInfo *oi;
1210
1211 oi = &params->objectinfo[old_handles + i];
1212 if (oi->ObjectFormat == PTP_OFC_Association) {
1213 get_handles_recursively(device, params, handles, currentHandles.Handler[i]);
1214 }
1215 } else {
1216 add_error_to_errorstack(device,
1217 LIBMTP_ERROR_CONNECTING,
1218 "Found a bad handle, trying to ignore it.");
1219 }
1220 }
1221
1222 free(currentHandles.Handler);
1223}
1224
1225/**
Linus Walleij438bd7f2006-06-08 11:35:44 +00001226 * This function refresh the internal handle list whenever
1227 * the items stored inside the device is altered. On operations
1228 * that do not add or remove objects, this is typically not
1229 * called.
1230 * @param device a pointer to the MTP device to flush handles for.
1231 */
1232static void flush_handles(LIBMTP_mtpdevice_t *device)
1233{
1234 PTPParams *params = (PTPParams *) device->params;
Linus Walleij338ade42007-07-03 20:44:08 +00001235 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijf0bf4372007-07-01 21:47:38 +00001236 uint32_t i;
1237
Linus Walleij438bd7f2006-06-08 11:35:44 +00001238 if (params->handles.Handler != NULL) {
1239 free(params->handles.Handler);
1240 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00001241 if (params->objectinfo != NULL) {
1242 for (i=0;i<params->handles.n;i++)
1243 ptp_free_objectinfo (&params->objectinfo[i]);
1244 free(params->objectinfo);
1245 }
Linus Walleij338ade42007-07-03 20:44:08 +00001246 if (params->proplist != NULL) {
1247 destroy_mtp_prop_list(params->proplist);
1248 }
Richard Low6711f442007-05-05 19:00:59 +00001249
Linus Walleijf0bf4372007-07-01 21:47:38 +00001250 params->handles.n = 0;
Richard Low6711f442007-05-05 19:00:59 +00001251 params->handles.Handler = NULL;
Linus Walleijf0bf4372007-07-01 21:47:38 +00001252 params->objectinfo = NULL;
Linus Walleij338ade42007-07-03 20:44:08 +00001253 params->proplist = NULL;
Linus Walleijf0bf4372007-07-01 21:47:38 +00001254
Linus Walleij338ade42007-07-03 20:44:08 +00001255 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
Linus Walleij2b218852007-07-07 20:16:36 +00001256 && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)
1257 && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL)) {
Linus Walleij338ade42007-07-03 20:44:08 +00001258 // Use the fast method.
1259 get_all_metadata_fast(device);
1260 } else {
1261 // Get all the handles using just standard commands.
1262 get_handles_recursively(device, params,
1263 &params->handles,
1264 PTP_GOH_ROOT_PARENT);
1265 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00001266
Linus Walleijf0bf4372007-07-01 21:47:38 +00001267 for(i = 0; i < params->handles.n; i++) {
1268 PTPObjectInfo *oi;
1269
1270 oi = &params->objectinfo[i];
1271 if (oi->Filename == NULL) {
1272 oi->Filename = strdup("<null>");
1273 }
1274 if (oi->Keywords == NULL) {
1275 oi->Keywords = strdup("<null>");
1276 }
1277
1278 /* Ignore handles that point to non-folders */
1279 if(oi->ObjectFormat != PTP_OFC_Association)
1280 continue;
Linus Walleij338ade42007-07-03 20:44:08 +00001281 if (oi->Filename == NULL)
Linus Walleijf0bf4372007-07-01 21:47:38 +00001282 continue;
1283
1284 /* Is this the Music Folder */
1285 if (!strcasecmp(oi->Filename, "My Music") ||
1286 !strcasecmp(oi->Filename, "Music")) {
1287 device->default_music_folder =
1288 params->handles.Handler[i];
1289 }
1290 else if (!strcasecmp(oi->Filename, "My Playlists") ||
1291 !strcasecmp(oi->Filename, "Playlists")) {
1292 device->default_playlist_folder =
1293 params->handles.Handler[i];
1294 }
1295 else if (!strcasecmp(oi->Filename, "My Pictures") ||
1296 !strcasecmp(oi->Filename, "Pictures")) {
1297 device->default_picture_folder =
1298 params->handles.Handler[i];
1299 }
1300 else if (!strcasecmp(oi->Filename, "My Video") ||
1301 !strcasecmp(oi->Filename, "Video")) {
1302 device->default_video_folder =
1303 params->handles.Handler[i];
1304 }
1305 else if (!strcasecmp(oi->Filename, "My Organizer")) {
1306 device->default_organizer_folder =
1307 params->handles.Handler[i];
1308 }
1309 else if (!strcasecmp(oi->Filename, "ZENcast")) {
1310 device->default_zencast_folder =
1311 params->handles.Handler[i];
1312 }
1313 else if (!strcasecmp(oi->Filename, "My Albums") ||
1314 !strcasecmp(oi->Filename, "Albums")) {
1315 device->default_album_folder =
1316 params->handles.Handler[i];
1317 }
1318 else if (!strcasecmp(oi->Filename, "Text")) {
1319 device->default_text_folder =
1320 params->handles.Handler[i];
1321 }
1322 }
Richard Low6711f442007-05-05 19:00:59 +00001323}
Linus Walleij438bd7f2006-06-08 11:35:44 +00001324
Linus Walleij438bd7f2006-06-08 11:35:44 +00001325/**
Linus Walleij9e1b0812006-12-12 19:22:02 +00001326 * This function traverses a devices storage list freeing up the
1327 * strings and the structs.
1328 * @param device a pointer to the MTP device to free the storage
1329 * list for.
1330 */
1331static void free_storage_list(LIBMTP_mtpdevice_t *device)
1332{
Linus Walleije1ac07e2006-12-14 19:38:59 +00001333 LIBMTP_devicestorage_t *storage;
1334 LIBMTP_devicestorage_t *tmp;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001335
Linus Walleije1ac07e2006-12-14 19:38:59 +00001336 storage = device->storage;
1337 while(storage != NULL) {
1338 if (storage->StorageDescription != NULL) {
1339 free(storage->StorageDescription);
1340 }
1341 if (storage->VolumeIdentifier != NULL) {
1342 free(storage->VolumeIdentifier);
1343 }
1344 tmp = storage;
1345 storage = storage->next;
1346 free(tmp);
Linus Walleij9e1b0812006-12-12 19:22:02 +00001347 }
1348 device->storage = NULL;
1349
1350 return;
1351}
1352
1353/**
1354 * This function traverses a devices storage list freeing up the
1355 * strings and the structs.
1356 * @param device a pointer to the MTP device to free the storage
1357 * list for.
1358 */
1359static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
1360{
1361 LIBMTP_devicestorage_t *oldhead, *ptr1, *ptr2, *newlist;
1362
1363 if (device->storage == NULL)
1364 return -1;
1365 if (sortby == LIBMTP_STORAGE_SORTBY_NOTSORTED)
1366 return 0;
1367
1368 oldhead = ptr1 = ptr2 = device->storage;
1369
1370 newlist = NULL;
1371
1372 while(oldhead != NULL) {
1373 ptr1 = ptr2 = oldhead;
1374 while(ptr1 != NULL) {
1375
1376 if (sortby == LIBMTP_STORAGE_SORTBY_FREESPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
1377 ptr2 = ptr1;
1378 if (sortby == LIBMTP_STORAGE_SORTBY_MAXSPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes)
1379 ptr2 = ptr1;
1380
1381 ptr1 = ptr1->next;
1382 }
1383
1384 // Make our previous entries next point to our next
1385 if(ptr2->prev != NULL) {
1386 ptr1 = ptr2->prev;
1387 ptr1->next = ptr2->next;
1388 } else {
1389 oldhead = ptr2->next;
1390 if(oldhead != NULL)
1391 oldhead->prev = NULL;
1392 }
1393
1394 // Make our next entries previous point to our previous
1395 ptr1 = ptr2->next;
1396 if(ptr1 != NULL) {
1397 ptr1->prev = ptr2->prev;
1398 } else {
1399 ptr1 = ptr2->prev;
1400 if(ptr1 != NULL)
1401 ptr1->next = NULL;
1402 }
1403
1404 if(newlist == NULL) {
1405 newlist = ptr2;
1406 newlist->prev = NULL;
1407 } else {
1408 ptr2->prev = newlist;
1409 newlist->next = ptr2;
1410 newlist = newlist->next;
1411 }
1412 }
1413
1414 newlist->next = NULL;
1415 while(newlist->prev != NULL)
1416 newlist = newlist->prev;
1417
1418 device->storage = newlist;
1419
1420 return 0;
1421}
1422
1423/**
1424 * This function grabs the first storageid from the device storage
1425 * list.
1426 * @param device a pointer to the MTP device to free the storage
1427 * list for.
1428 */
1429static uint32_t get_first_storageid(LIBMTP_mtpdevice_t *device)
1430{
1431 LIBMTP_devicestorage_t *storage = device->storage;
1432 uint32_t store = 0;
1433
1434 if(storage != NULL)
1435 store = storage->id;
1436
1437 return store;
1438}
1439
1440/**
1441 * This function grabs the freespace from the first storage in
1442 * device storage list.
1443 * @param device a pointer to the MTP device to free the storage
1444 * list for.
1445 */
Linus Walleij070e9b42007-01-22 08:49:28 +00001446static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device, uint64_t *freespace)
Linus Walleij9e1b0812006-12-12 19:22:02 +00001447{
1448 LIBMTP_devicestorage_t *storage = device->storage;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001449 PTPParams *params = (PTPParams *) device->params;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001450
Linus Walleije1ac07e2006-12-14 19:38:59 +00001451 if(storage == NULL) {
Linus Walleij9e1b0812006-12-12 19:22:02 +00001452 return -1;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001453 }
1454 // Always query the device about this, since some models explicitly
1455 // needs that.
1456 if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
1457 PTPStorageInfo storageInfo;
Linus Walleij070e9b42007-01-22 08:49:28 +00001458 uint16_t ret;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001459
Linus Walleij070e9b42007-01-22 08:49:28 +00001460 ret = ptp_getstorageinfo(params, storage->id, &storageInfo);
1461 if (ret != PTP_RC_OK) {
1462 add_ptp_error_to_errorstack(device, ret, "get_first_storage_freespace(): could not get storage info.");
Linus Walleije1ac07e2006-12-14 19:38:59 +00001463 return -1;
1464 }
1465 if (storage->StorageDescription != NULL) {
1466 free(storage->StorageDescription);
1467 }
1468 if (storage->VolumeIdentifier != NULL) {
1469 free(storage->VolumeIdentifier);
1470 }
1471 storage->StorageType = storageInfo.StorageType;
1472 storage->FilesystemType = storageInfo.FilesystemType;
1473 storage->AccessCapability = storageInfo.AccessCapability;
1474 storage->MaxCapacity = storageInfo.MaxCapability;
1475 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
1476 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
1477 storage->StorageDescription = storageInfo.StorageDescription;
1478 storage->VolumeIdentifier = storageInfo.VolumeLabel;
1479 }
1480 if(storage->FreeSpaceInBytes == (uint64_t) -1)
Linus Walleij9e1b0812006-12-12 19:22:02 +00001481 return -1;
1482 *freespace = storage->FreeSpaceInBytes;
1483 return 0;
1484}
1485
1486/**
Linus Walleij8c45b292006-04-26 14:12:44 +00001487 * This function dumps out a large chunk of textual information
1488 * provided from the PTP protocol and additionally some extra
1489 * MTP-specific information where applicable.
1490 * @param device a pointer to the MTP device to report info from.
1491 */
1492void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
1493{
1494 int i;
1495 PTPParams *params = (PTPParams *) device->params;
Linus Walleijc6210fb2006-05-08 11:11:41 +00001496 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleije1ac07e2006-12-14 19:38:59 +00001497 LIBMTP_devicestorage_t *storage = device->storage;
mopoke96143402006-10-30 04:37:26 +00001498
Linus Walleijc6210fb2006-05-08 11:11:41 +00001499 printf("USB low-level info:\n");
1500 dump_usbinfo(ptp_usb);
Linus Walleij8c45b292006-04-26 14:12:44 +00001501 /* Print out some verbose information */
1502 printf("Device info:\n");
1503 printf(" Manufacturer: %s\n", params->deviceinfo.Manufacturer);
1504 printf(" Model: %s\n", params->deviceinfo.Model);
1505 printf(" Device version: %s\n", params->deviceinfo.DeviceVersion);
1506 printf(" Serial number: %s\n", params->deviceinfo.SerialNumber);
1507 printf(" Vendor extension ID: 0x%08x\n", params->deviceinfo.VendorExtensionID);
1508 printf(" Vendor extension description: %s\n", params->deviceinfo.VendorExtensionDesc);
1509 printf("Supported operations:\n");
1510 for (i=0;i<params->deviceinfo.OperationsSupported_len;i++) {
Linus Walleij4f40d112006-09-21 07:44:53 +00001511 char txt[256];
1512
1513 (void) ptp_render_opcode (params, params->deviceinfo.OperationsSupported[i], sizeof(txt), txt);
1514 printf(" %04x: %s\n", params->deviceinfo.OperationsSupported[i], txt);
Linus Walleij8c45b292006-04-26 14:12:44 +00001515 }
1516 printf("Events supported:\n");
1517 if (params->deviceinfo.EventsSupported_len == 0) {
1518 printf(" None.\n");
1519 } else {
1520 for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
1521 printf(" 0x%04x\n", params->deviceinfo.EventsSupported[i]);
1522 }
1523 }
1524 printf("Device Properties Supported:\n");
1525 for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
Linus Walleij16c51f02006-05-04 13:20:22 +00001526 char const *propdesc = ptp_get_property_description(params, params->deviceinfo.DevicePropertiesSupported[i]);
mopoke96143402006-10-30 04:37:26 +00001527
Linus Walleij545c7792006-06-13 15:22:30 +00001528 if (propdesc != NULL) {
1529 printf(" 0x%04x: %s\n", params->deviceinfo.DevicePropertiesSupported[i], propdesc);
1530 } else {
1531 uint16_t prop = params->deviceinfo.DevicePropertiesSupported[i];
Linus Walleijcf223e62006-06-19 09:31:53 +00001532 printf(" 0x%04x: Unknown property\n", prop);
Linus Walleij545c7792006-06-13 15:22:30 +00001533 }
Linus Walleij8c45b292006-04-26 14:12:44 +00001534 }
Linus Walleij0af979a2006-06-19 11:49:10 +00001535
1536 if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
1537 printf("Playable File (Object) Types and Object Properties Supported:\n");
1538 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
1539 char txt[256];
1540 uint16_t ret;
1541 uint16_t *props = NULL;
1542 uint32_t propcnt = 0;
1543 int j;
mopoke96143402006-10-30 04:37:26 +00001544
Linus Walleij0af979a2006-06-19 11:49:10 +00001545 (void) ptp_render_ofc (params, params->deviceinfo.ImageFormats[i], sizeof(txt), txt);
1546 printf(" %04x: %s\n", params->deviceinfo.ImageFormats[i], txt);
mopoke96143402006-10-30 04:37:26 +00001547
Linus Walleij0af979a2006-06-19 11:49:10 +00001548 ret = ptp_mtp_getobjectpropssupported (params, params->deviceinfo.ImageFormats[i], &propcnt, &props);
1549 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001550 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Dump_Device_Info(): error on query for object properties.");
Linus Walleij0af979a2006-06-19 11:49:10 +00001551 } else {
1552 for (j=0;j<propcnt;j++) {
Linus Walleij14830342007-03-23 13:32:00 +00001553 PTPObjectPropDesc opd;
1554 int k;
1555
Linus Walleij0af979a2006-06-19 11:49:10 +00001556 (void) ptp_render_mtp_propname(props[j],sizeof(txt),txt);
Linus Walleij14830342007-03-23 13:32:00 +00001557 printf(" %04x: %s", props[j], txt);
1558 // Get a more verbose description
1559 ret = ptp_mtp_getobjectpropdesc(params, props[j], params->deviceinfo.ImageFormats[i], &opd);
1560 if (ret != PTP_RC_OK) {
1561 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Dump_Device_Info(): "
1562 "could not get property description.");
1563 break;
1564 }
1565
1566 if (opd.DataType == PTP_DTC_STR) {
1567 printf(" STRING data type");
1568 } else {
1569 if (opd.DataType & PTP_DTC_ARRAY_MASK) {
1570 printf(" array of");
1571 }
1572
1573 switch (opd.DataType & (~PTP_DTC_ARRAY_MASK)) {
1574
1575 case PTP_DTC_UNDEF:
1576 printf(" UNDEFINED data type");
1577 break;
1578
1579 case PTP_DTC_INT8:
1580 printf(" INT8 data type");
1581 switch (opd.FormFlag) {
1582 case PTP_DPFF_Range:
1583 printf(" range: MIN %d, MAX %d, STEP %d",
1584 opd.FORM.Range.MinimumValue.i8,
1585 opd.FORM.Range.MaximumValue.i8,
1586 opd.FORM.Range.StepSize.i8);
1587 break;
1588 case PTP_DPFF_Enumeration:
1589 printf(" enumeration: ");
1590 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1591 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i8);
1592 }
1593 break;
1594 default:
1595 printf(" ANY 8BIT VALUE form");
1596 break;
1597 }
1598 break;
1599
1600 case PTP_DTC_UINT8:
1601 printf(" UINT8 data type");
1602 switch (opd.FormFlag) {
1603 case PTP_DPFF_Range:
1604 printf(" range: MIN %d, MAX %d, STEP %d",
1605 opd.FORM.Range.MinimumValue.u8,
1606 opd.FORM.Range.MaximumValue.u8,
1607 opd.FORM.Range.StepSize.u8);
1608 break;
1609 case PTP_DPFF_Enumeration:
1610 printf(" enumeration: ");
1611 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1612 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u8);
1613 }
1614 break;
1615 default:
1616 printf(" ANY 8BIT VALUE form");
1617 break;
1618 }
1619 break;
1620
1621 case PTP_DTC_INT16:
1622 printf(" INT16 data type");
1623 switch (opd.FormFlag) {
1624 case PTP_DPFF_Range:
1625 printf(" range: MIN %d, MAX %d, STEP %d",
1626 opd.FORM.Range.MinimumValue.i16,
1627 opd.FORM.Range.MaximumValue.i16,
1628 opd.FORM.Range.StepSize.i16);
1629 break;
1630 case PTP_DPFF_Enumeration:
1631 printf(" enumeration: ");
1632 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1633 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i16);
1634 }
1635 break;
1636 default:
1637 printf(" ANY 16BIT VALUE form");
1638 break;
1639 }
1640 break;
1641
1642 case PTP_DTC_UINT16:
1643 printf(" UINT16 data type");
1644 switch (opd.FormFlag) {
1645 case PTP_DPFF_Range:
1646 printf(" range: MIN %d, MAX %d, STEP %d",
1647 opd.FORM.Range.MinimumValue.u16,
1648 opd.FORM.Range.MaximumValue.u16,
1649 opd.FORM.Range.StepSize.u16);
1650 break;
1651 case PTP_DPFF_Enumeration:
1652 printf(" enumeration: ");
1653 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1654 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u16);
1655 }
1656 break;
1657 default:
1658 printf(" ANY 16BIT VALUE form");
1659 break;
1660 }
1661 break;
1662
1663 case PTP_DTC_INT32:
1664 printf(" INT32 data type");
1665 switch (opd.FormFlag) {
1666 case PTP_DPFF_Range:
1667 printf(" range: MIN %d, MAX %d, STEP %d",
1668 opd.FORM.Range.MinimumValue.i32,
1669 opd.FORM.Range.MaximumValue.i32,
1670 opd.FORM.Range.StepSize.i32);
1671 break;
1672 case PTP_DPFF_Enumeration:
1673 printf(" enumeration: ");
1674 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1675 printf("%d, ", opd.FORM.Enum.SupportedValue[k].i32);
1676 }
1677 break;
1678 default:
1679 printf(" ANY 32BIT VALUE form");
1680 break;
1681 }
1682 break;
1683
1684 case PTP_DTC_UINT32:
1685 printf(" UINT32 data type");
1686 switch (opd.FormFlag) {
1687 case PTP_DPFF_Range:
1688 printf(" range: MIN %d, MAX %d, STEP %d",
1689 opd.FORM.Range.MinimumValue.u32,
1690 opd.FORM.Range.MaximumValue.u32,
1691 opd.FORM.Range.StepSize.u32);
1692 break;
1693 case PTP_DPFF_Enumeration:
1694 printf(" enumeration: ");
1695 for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
1696 printf("%d, ", opd.FORM.Enum.SupportedValue[k].u32);
1697 }
1698 break;
1699 default:
1700 printf(" ANY 32BIT VALUE form");
1701 break;
1702 }
1703 break;
1704
1705 case PTP_DTC_INT64:
1706 printf(" INT64 data type");
1707 break;
1708
1709 case PTP_DTC_UINT64:
1710 printf(" UINT64 data type");
1711 break;
1712
1713 case PTP_DTC_INT128:
1714 printf(" INT128 data type");
1715 break;
1716
1717 case PTP_DTC_UINT128:
1718 printf(" UINT128 data type");
1719 break;
1720
1721 default:
1722 printf(" UNKNOWN data type");
1723 break;
1724 }
1725 }
1726 if (opd.GetSet) {
1727 printf(" GET/SET");
1728 } else {
1729 printf(" READ ONLY");
1730 }
1731 printf("\n");
Linus Walleij0af979a2006-06-19 11:49:10 +00001732 }
1733 free(props);
1734 }
1735 }
1736 }
mopoke96143402006-10-30 04:37:26 +00001737
Linus Walleije1ac07e2006-12-14 19:38:59 +00001738 if(storage != NULL && ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
Linus Walleij9e1b0812006-12-12 19:22:02 +00001739 printf("Storage Devices:\n");
Linus Walleije1ac07e2006-12-14 19:38:59 +00001740 while(storage != NULL) {
1741 printf(" StorageID: 0x%08x\n",storage->id);
1742 printf(" StorageType: 0x%04x\n",storage->StorageType);
1743 printf(" FilesystemType: 0x%04x\n",storage->FilesystemType);
1744 printf(" AccessCapability: 0x%04x\n",storage->AccessCapability);
1745 printf(" MaxCapacity: %lld\n",storage->MaxCapacity);
1746 printf(" FreeSpaceInBytes: %lld\n",storage->FreeSpaceInBytes);
1747 printf(" FreeSpaceInObjects: %lld\n",storage->FreeSpaceInObjects);
1748 printf(" StorageDescription: %s\n",storage->StorageDescription);
1749 printf(" VolumeIdentifier: %s\n",storage->VolumeIdentifier);
1750 storage = storage->next;
Linus Walleij9e1b0812006-12-12 19:22:02 +00001751 }
1752 }
1753
Linus Walleij545c7792006-06-13 15:22:30 +00001754 printf("Special directories:\n");
1755 printf(" Default music folder: 0x%08x\n", device->default_music_folder);
1756 printf(" Default playlist folder: 0x%08x\n", device->default_playlist_folder);
1757 printf(" Default picture folder: 0x%08x\n", device->default_picture_folder);
1758 printf(" Default video folder: 0x%08x\n", device->default_video_folder);
1759 printf(" Default organizer folder: 0x%08x\n", device->default_organizer_folder);
1760 printf(" Default zencast folder: 0x%08x\n", device->default_zencast_folder);
Linus Walleijccf28ce2006-11-16 16:06:38 +00001761 printf(" Default album folder: 0x%08x\n", device->default_album_folder);
Linus Walleij9316e652006-12-07 09:55:21 +00001762 printf(" Default text folder: 0x%08x\n", device->default_text_folder);
Linus Walleij8c45b292006-04-26 14:12:44 +00001763}
1764
1765/**
Linus Walleij5d533bb2007-07-17 21:48:57 +00001766 * This resets a device in case it supports the <code>PTP_OC_ResetDevice</code>
1767 * operation code (0x1010).
1768 * @param device a pointer to the device to reset.
1769 * @return 0 on success, any other value means failure.
1770 */
1771int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t *device)
1772{
1773 PTPParams *params = (PTPParams *) device->params;
1774 uint16_t ret;
1775
1776 if (!ptp_operation_issupported(params,PTP_OC_ResetDevice)) {
1777 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
1778 "LIBMTP_Reset_Device(): device does not support resetting.");
1779 return -1;
1780 }
1781 ret = ptp_resetdevice(params);
1782 if (ret != PTP_RC_OK) {
1783 add_ptp_error_to_errorstack(device, ret, "Error resetting.");
1784 return -1;
1785 }
1786 return 0;
1787}
1788
1789/**
mopoke96143402006-10-30 04:37:26 +00001790 * This retrieves the model name (often equal to product name)
Linus Walleij80124062006-03-15 10:26:09 +00001791 * of an MTP device.
1792 * @param device a pointer to the device to get the model name for.
1793 * @return a newly allocated UTF-8 string representing the model name.
1794 * The string must be freed by the caller after use. If the call
1795 * was unsuccessful this will contain NULL.
1796 */
1797char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t *device)
1798{
1799 char *retmodel = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001800 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001801
Linus Walleij9b28da32006-03-16 13:47:58 +00001802 if (params->deviceinfo.Model != NULL) {
1803 retmodel = strdup(params->deviceinfo.Model);
Linus Walleij80124062006-03-15 10:26:09 +00001804 }
1805 return retmodel;
1806}
1807
1808/**
1809 * This retrieves the serial number of an MTP device.
1810 * @param device a pointer to the device to get the serial number for.
1811 * @return a newly allocated UTF-8 string representing the serial number.
1812 * The string must be freed by the caller after use. If the call
1813 * was unsuccessful this will contain NULL.
1814 */
1815char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t *device)
1816{
1817 char *retnumber = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001818 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001819
Linus Walleij9b28da32006-03-16 13:47:58 +00001820 if (params->deviceinfo.SerialNumber != NULL) {
1821 retnumber = strdup(params->deviceinfo.SerialNumber);
Linus Walleij80124062006-03-15 10:26:09 +00001822 }
1823 return retnumber;
1824}
1825
1826/**
mopoke96143402006-10-30 04:37:26 +00001827 * This retrieves the device version (hardware and firmware version) of an
Linus Walleij80124062006-03-15 10:26:09 +00001828 * MTP device.
1829 * @param device a pointer to the device to get the device version for.
1830 * @return a newly allocated UTF-8 string representing the device version.
1831 * The string must be freed by the caller after use. If the call
1832 * was unsuccessful this will contain NULL.
1833 */
1834char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t *device)
1835{
1836 char *retversion = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001837 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00001838
Linus Walleij9b28da32006-03-16 13:47:58 +00001839 if (params->deviceinfo.DeviceVersion != NULL) {
1840 retversion = strdup(params->deviceinfo.DeviceVersion);
Linus Walleij80124062006-03-15 10:26:09 +00001841 }
1842 return retversion;
1843}
1844
1845
1846/**
Linus Walleijfae27482006-08-19 20:13:25 +00001847 * This retrieves the "friendly name" of an MTP device. Usually
1848 * this is simply the name of the owner or something like
Linus Walleij30658792006-08-19 22:18:55 +00001849 * "John Doe's Digital Audio Player". This property should be supported
Linus Walleijfae27482006-08-19 20:13:25 +00001850 * by all MTP devices.
1851 * @param device a pointer to the device to get the friendly name for.
mopoke96143402006-10-30 04:37:26 +00001852 * @return a newly allocated UTF-8 string representing the friendly name.
Linus Walleijb9256fd2006-02-15 09:40:43 +00001853 * The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00001854 * @see LIBMTP_Set_Friendlyname()
Linus Walleijb9256fd2006-02-15 09:40:43 +00001855 */
Linus Walleij30658792006-08-19 22:18:55 +00001856char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t *device)
Linus Walleijb9256fd2006-02-15 09:40:43 +00001857{
Linus Walleijb02a0662006-04-25 08:05:09 +00001858 PTPPropertyValue propval;
Linus Walleijb9256fd2006-02-15 09:40:43 +00001859 char *retstring = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00001860 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001861 uint16_t ret;
Linus Walleijb9256fd2006-02-15 09:40:43 +00001862
Linus Walleijcf223e62006-06-19 09:31:53 +00001863 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
1864 return NULL;
1865 }
1866
Linus Walleij070e9b42007-01-22 08:49:28 +00001867 ret = ptp_getdevicepropvalue(params,
1868 PTP_DPC_MTP_DeviceFriendlyName,
1869 &propval,
1870 PTP_DTC_STR);
1871 if (ret != PTP_RC_OK) {
1872 add_ptp_error_to_errorstack(device, ret, "Error getting friendlyname.");
Linus Walleijb9256fd2006-02-15 09:40:43 +00001873 return NULL;
1874 }
Linus Walleija823a702006-08-27 21:27:46 +00001875 if (propval.str != NULL) {
1876 retstring = strdup(propval.str);
1877 free(propval.str);
1878 }
Linus Walleijfae27482006-08-19 20:13:25 +00001879 return retstring;
1880}
1881
1882/**
Linus Walleij30658792006-08-19 22:18:55 +00001883 * Sets the "friendly name" of an MTP device.
1884 * @param device a pointer to the device to set the friendly name for.
1885 * @param friendlyname the new friendly name for the device.
1886 * @return 0 on success, any other value means failure.
1887 * @see LIBMTP_Get_Ownername()
1888 */
1889int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t *device,
1890 char const * const friendlyname)
1891{
1892 PTPPropertyValue propval;
1893 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001894 uint16_t ret;
Linus Walleij30658792006-08-19 22:18:55 +00001895
1896 if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
1897 return -1;
1898 }
Linus Walleija823a702006-08-27 21:27:46 +00001899 propval.str = (char *) friendlyname;
Linus Walleij070e9b42007-01-22 08:49:28 +00001900 ret = ptp_setdevicepropvalue(params,
1901 PTP_DPC_MTP_DeviceFriendlyName,
1902 &propval,
1903 PTP_DTC_STR);
1904 if (ret != PTP_RC_OK) {
1905 add_ptp_error_to_errorstack(device, ret, "Error setting friendlyname.");
Linus Walleij30658792006-08-19 22:18:55 +00001906 return -1;
1907 }
Linus Walleij30658792006-08-19 22:18:55 +00001908 return 0;
1909}
1910
1911/**
Linus Walleijfae27482006-08-19 20:13:25 +00001912 * This retrieves the syncronization partner of an MTP device. This
1913 * property should be supported by all MTP devices.
1914 * @param device a pointer to the device to get the sync partner for.
1915 * @return a newly allocated UTF-8 string representing the synchronization
1916 * partner. The string must be freed by the caller after use.
Linus Walleij30658792006-08-19 22:18:55 +00001917 * @see LIBMTP_Set_Syncpartner()
Linus Walleijfae27482006-08-19 20:13:25 +00001918 */
1919char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t *device)
1920{
1921 PTPPropertyValue propval;
1922 char *retstring = NULL;
1923 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001924 uint16_t ret;
Linus Walleijfae27482006-08-19 20:13:25 +00001925
1926 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
1927 return NULL;
1928 }
1929
Linus Walleij070e9b42007-01-22 08:49:28 +00001930 ret = ptp_getdevicepropvalue(params,
1931 PTP_DPC_MTP_SynchronizationPartner,
1932 &propval,
1933 PTP_DTC_STR);
1934 if (ret != PTP_RC_OK) {
1935 add_ptp_error_to_errorstack(device, ret, "Error getting syncpartner.");
Linus Walleijfae27482006-08-19 20:13:25 +00001936 return NULL;
1937 }
Linus Walleija823a702006-08-27 21:27:46 +00001938 if (propval.str != NULL) {
1939 retstring = strdup(propval.str);
1940 free(propval.str);
1941 }
Linus Walleijb9256fd2006-02-15 09:40:43 +00001942 return retstring;
1943}
1944
Linus Walleij30658792006-08-19 22:18:55 +00001945
1946/**
1947 * Sets the synchronization partner of an MTP device. Note that
1948 * we have no idea what the effect of setting this to "foobar"
1949 * may be. But the general idea seems to be to tell which program
1950 * shall synchronize with this device and tell others to leave
1951 * it alone.
1952 * @param device a pointer to the device to set the sync partner for.
1953 * @param syncpartner the new synchronization partner for the device.
1954 * @return 0 on success, any other value means failure.
1955 * @see LIBMTP_Get_Syncpartner()
1956 */
1957int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t *device,
1958 char const * const syncpartner)
1959{
1960 PTPPropertyValue propval;
1961 PTPParams *params = (PTPParams *) device->params;
Linus Walleij070e9b42007-01-22 08:49:28 +00001962 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00001963
Linus Walleij30658792006-08-19 22:18:55 +00001964 if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
1965 return -1;
1966 }
Linus Walleija823a702006-08-27 21:27:46 +00001967 propval.str = (char *) syncpartner;
Linus Walleij070e9b42007-01-22 08:49:28 +00001968 ret = ptp_setdevicepropvalue(params,
1969 PTP_DPC_MTP_SynchronizationPartner,
1970 &propval,
1971 PTP_DTC_STR);
1972 if (ret != PTP_RC_OK) {
1973 add_ptp_error_to_errorstack(device, ret, "Error setting syncpartner.");
Linus Walleij30658792006-08-19 22:18:55 +00001974 return -1;
1975 }
Linus Walleij30658792006-08-19 22:18:55 +00001976 return 0;
1977}
1978
Linus Walleij394bbbe2006-02-22 16:10:53 +00001979/**
Linus Walleijf5fcda32006-12-03 22:31:02 +00001980 * Checks if the device can stora a file of this size or
1981 * if it's too big.
1982 * @param device a pointer to the device.
1983 * @param filesize the size of the file to check whether it will fit.
1984 * @return 0 if the file fits, any other value means failure.
1985 */
1986static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize) {
1987 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf5fcda32006-12-03 22:31:02 +00001988 uint64_t freebytes;
Linus Walleijf5fcda32006-12-03 22:31:02 +00001989 int ret;
1990
1991 // If we cannot check the storage, no big deal.
1992 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
1993 return 0;
1994 }
1995
Linus Walleij9e1b0812006-12-12 19:22:02 +00001996 ret = get_first_storage_freespace(device,&freebytes);
Linus Walleijf5fcda32006-12-03 22:31:02 +00001997 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00001998 add_ptp_error_to_errorstack(device, ret, "check_if_file_fits(): error checking free storage.");
Linus Walleijf5fcda32006-12-03 22:31:02 +00001999 return -1;
2000 } else {
2001 if (filesize > freebytes) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002002 add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL, "check_if_file_fits(): device storage is full.");
Linus Walleijf5fcda32006-12-03 22:31:02 +00002003 return -1;
2004 }
2005 }
2006 return 0;
2007}
2008
2009
Linus Walleijf5fcda32006-12-03 22:31:02 +00002010/**
Linus Walleijfa1374c2006-02-27 07:41:46 +00002011 * This function retrieves the current battery level on the device.
2012 * @param device a pointer to the device to get the battery level for.
mopoke96143402006-10-30 04:37:26 +00002013 * @param maximum_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00002014 * maximum level of the battery if the call was successful.
mopoke96143402006-10-30 04:37:26 +00002015 * @param current_level a pointer to a variable that will hold the
Linus Walleijfa1374c2006-02-27 07:41:46 +00002016 * current level of the battery if the call was successful.
Linus Walleij545c7792006-06-13 15:22:30 +00002017 * A value of 0 means that the device is on external power.
Linus Walleijfa1374c2006-02-27 07:41:46 +00002018 * @return 0 if the storage info was successfully retrieved, any other
Linus Walleij80439342006-09-12 10:42:26 +00002019 * means failure. A typical cause of failure is that
Linus Walleij545c7792006-06-13 15:22:30 +00002020 * the device does not support the battery level property.
Linus Walleijfa1374c2006-02-27 07:41:46 +00002021 */
mopoke96143402006-10-30 04:37:26 +00002022int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *device,
2023 uint8_t * const maximum_level,
Linus Walleijfa1374c2006-02-27 07:41:46 +00002024 uint8_t * const current_level)
2025{
Linus Walleijb02a0662006-04-25 08:05:09 +00002026 PTPPropertyValue propval;
Linus Walleijfa1374c2006-02-27 07:41:46 +00002027 uint16_t ret;
Linus Walleij9b28da32006-03-16 13:47:58 +00002028 PTPParams *params = (PTPParams *) device->params;
Linus Walleijfa1374c2006-02-27 07:41:46 +00002029
Linus Walleij545c7792006-06-13 15:22:30 +00002030 *maximum_level = 0;
2031 *current_level = 0;
2032
2033 if (!ptp_property_issupported(params, PTP_DPC_BatteryLevel)) {
2034 return -1;
2035 }
mopoke96143402006-10-30 04:37:26 +00002036
Linus Walleijb02a0662006-04-25 08:05:09 +00002037 ret = ptp_getdevicepropvalue(params, PTP_DPC_BatteryLevel, &propval, PTP_DTC_UINT8);
2038 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002039 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Batterylevel(): could not get device property value.");
Linus Walleijfa1374c2006-02-27 07:41:46 +00002040 return -1;
2041 }
mopoke96143402006-10-30 04:37:26 +00002042
Linus Walleijfa1374c2006-02-27 07:41:46 +00002043 *maximum_level = device->maximum_battery_level;
Linus Walleijb02a0662006-04-25 08:05:09 +00002044 *current_level = propval.u8;
mopoke96143402006-10-30 04:37:26 +00002045
Linus Walleijfa1374c2006-02-27 07:41:46 +00002046 return 0;
2047}
2048
Linus Walleij13374a42006-09-13 11:55:30 +00002049
2050/**
2051 * Formats device storage (if the device supports the operation).
2052 * WARNING: This WILL delete all data from the device. Make sure you've
2053 * got confirmation from the user BEFORE you call this function.
2054 *
Linus Walleijf8491912006-12-15 10:23:30 +00002055 * @param device a pointer to the device containing the storage to format.
2056 * @param storage the actual storage to format.
Linus Walleij13374a42006-09-13 11:55:30 +00002057 * @return 0 on success, any other value means failure.
2058 */
Linus Walleijf8491912006-12-15 10:23:30 +00002059int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *device, LIBMTP_devicestorage_t *storage)
Linus Walleij13374a42006-09-13 11:55:30 +00002060{
2061 uint16_t ret;
2062 PTPParams *params = (PTPParams *) device->params;
mopoke96143402006-10-30 04:37:26 +00002063
Linus Walleij13374a42006-09-13 11:55:30 +00002064 if (!ptp_operation_issupported(params,PTP_OC_FormatStore)) {
Linus Walleij5d533bb2007-07-17 21:48:57 +00002065 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
2066 "LIBMTP_Format_Storage(): device does not support formatting storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00002067 return -1;
2068 }
Linus Walleijf8491912006-12-15 10:23:30 +00002069 ret = ptp_formatstore(params, storage->id);
Linus Walleij13374a42006-09-13 11:55:30 +00002070 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002071 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Format_Storage(): failed to format storage.");
Linus Walleij13374a42006-09-13 11:55:30 +00002072 return -1;
2073 }
2074 return 0;
2075}
2076
Linus Walleijfa1374c2006-02-27 07:41:46 +00002077/**
Linus Walleij545c7792006-06-13 15:22:30 +00002078 * Helper function to extract a unicode property off a device.
Linus Walleije46f12e2006-06-22 17:53:25 +00002079 * This is the standard way of retrieveing unicode device
2080 * properties as described by the PTP spec.
Linus Walleijcf223e62006-06-19 09:31:53 +00002081 * @param device a pointer to the device to get the property from.
mopoke96143402006-10-30 04:37:26 +00002082 * @param unicstring a pointer to a pointer that will hold the
Linus Walleijcf223e62006-06-19 09:31:53 +00002083 * property after this call is completed.
2084 * @param property the property to retrieve.
2085 * @return 0 on success, any other value means failure.
Linus Walleij545c7792006-06-13 15:22:30 +00002086 */
mopoke96143402006-10-30 04:37:26 +00002087static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
Linus Walleijcf223e62006-06-19 09:31:53 +00002088 char **unicstring, uint16_t property)
Linus Walleij545c7792006-06-13 15:22:30 +00002089{
2090 PTPPropertyValue propval;
2091 PTPParams *params = (PTPParams *) device->params;
Linus Walleij16571dc2006-08-17 20:27:46 +00002092 uint16_t *tmp;
Linus Walleij070e9b42007-01-22 08:49:28 +00002093 uint16_t ret;
Linus Walleij545c7792006-06-13 15:22:30 +00002094 int i;
2095
2096 if (!ptp_property_issupported(params, property)) {
2097 return -1;
2098 }
2099
Linus Walleijcf223e62006-06-19 09:31:53 +00002100 // Unicode strings are 16bit unsigned integer arrays.
Linus Walleij070e9b42007-01-22 08:49:28 +00002101 ret = ptp_getdevicepropvalue(params,
2102 property,
2103 &propval,
2104 PTP_DTC_AUINT16);
2105 if (ret != PTP_RC_OK) {
2106 // TODO: add a note on WHICH property that we failed to get.
tedbullock4e51cb92007-02-15 11:48:34 +00002107 *unicstring = NULL;
Linus Walleij070e9b42007-01-22 08:49:28 +00002108 add_ptp_error_to_errorstack(device, ret, "get_device_unicode_property(): failed to get unicode property.");
Linus Walleij545c7792006-06-13 15:22:30 +00002109 return -1;
2110 }
2111
2112 // Extract the actual array.
Linus Walleij16571dc2006-08-17 20:27:46 +00002113 // printf("Array of %d elements\n", propval.a.count);
2114 tmp = malloc((propval.a.count + 1)*sizeof(uint16_t));
Linus Walleij545c7792006-06-13 15:22:30 +00002115 for (i = 0; i < propval.a.count; i++) {
Linus Walleij16571dc2006-08-17 20:27:46 +00002116 tmp[i] = propval.a.v[i].u16;
2117 // printf("%04x ", tmp[i]);
Linus Walleij545c7792006-06-13 15:22:30 +00002118 }
Linus Walleij16571dc2006-08-17 20:27:46 +00002119 tmp[propval.a.count] = 0x0000U;
Linus Walleij545c7792006-06-13 15:22:30 +00002120 free(propval.a.v);
2121
Linus Walleij3ec86312006-08-21 13:25:24 +00002122 *unicstring = utf16_to_utf8(device, tmp);
Linus Walleij16571dc2006-08-17 20:27:46 +00002123
Linus Walleij545c7792006-06-13 15:22:30 +00002124 free(tmp);
2125
2126 return 0;
2127}
2128
2129/**
2130 * This function returns the secure time as an XML document string from
2131 * the device.
2132 * @param device a pointer to the device to get the secure time for.
2133 * @param sectime the secure time string as an XML document or NULL if the call
2134 * failed or the secure time property is not supported. This string
2135 * must be <code>free()</code>:ed by the caller after use.
2136 * @return 0 on success, any other value means failure.
2137 */
Linus Walleij8ab54262006-06-21 07:12:28 +00002138int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *device, char ** const sectime)
Linus Walleij545c7792006-06-13 15:22:30 +00002139{
2140 return get_device_unicode_property(device, sectime, PTP_DPC_MTP_SecureTime);
2141}
2142
2143/**
mopoke96143402006-10-30 04:37:26 +00002144 * This function returns the device (public key) certificate as an
Linus Walleij545c7792006-06-13 15:22:30 +00002145 * XML document string from the device.
2146 * @param device a pointer to the device to get the device certificate for.
2147 * @param devcert the device certificate as an XML string or NULL if the call
2148 * failed or the device certificate property is not supported. This
2149 * string must be <code>free()</code>:ed by the caller after use.
2150 * @return 0 on success, any other value means failure.
2151 */
Linus Walleij8ab54262006-06-21 07:12:28 +00002152int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *device, char ** const devcert)
Linus Walleij545c7792006-06-13 15:22:30 +00002153{
2154 return get_device_unicode_property(device, devcert, PTP_DPC_MTP_DeviceCertificate);
2155}
2156
2157/**
Linus Walleij8ab54262006-06-21 07:12:28 +00002158 * This function retrieves a list of supported file types, i.e. the file
2159 * types that this device claims it supports, e.g. audio file types that
2160 * the device can play etc. This list is mitigated to
2161 * inlcude the file types that libmtp can handle, i.e. it will not list
2162 * filetypes that libmtp will handle internally like playlists and folders.
2163 * @param device a pointer to the device to get the filetype capabilities for.
2164 * @param filetypes a pointer to a pointer that will hold the list of
2165 * supported filetypes if the call was successful. This list must
2166 * be <code>free()</code>:ed by the caller after use.
2167 * @param length a pointer to a variable that will hold the length of the
2168 * list of supported filetypes if the call was successful.
2169 * @return 0 on success, any other value means failure.
2170 * @see LIBMTP_Get_Filetype_Description()
2171 */
mopoke96143402006-10-30 04:37:26 +00002172int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const filetypes,
Linus Walleij8ab54262006-06-21 07:12:28 +00002173 uint16_t * const length)
2174{
2175 PTPParams *params = (PTPParams *) device->params;
2176 uint16_t *localtypes;
2177 uint16_t localtypelen;
2178 uint32_t i;
mopoke96143402006-10-30 04:37:26 +00002179
Linus Walleij8ab54262006-06-21 07:12:28 +00002180 // This is more memory than needed if there are unknown types, but what the heck.
2181 localtypes = (uint16_t *) malloc(params->deviceinfo.ImageFormats_len * sizeof(uint16_t));
2182 localtypelen = 0;
mopoke96143402006-10-30 04:37:26 +00002183
Linus Walleij8ab54262006-06-21 07:12:28 +00002184 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
2185 uint16_t localtype = map_ptp_type_to_libmtp_type(params->deviceinfo.ImageFormats[i]);
2186 if (localtype != LIBMTP_FILETYPE_UNKNOWN) {
2187 localtypes[localtypelen] = localtype;
2188 localtypelen++;
2189 }
2190 }
2191
2192 *filetypes = localtypes;
2193 *length = localtypelen;
2194
2195 return 0;
2196}
2197
raveloxd9a28642006-05-26 23:42:22 +00002198/**
Linus Walleij9e1b0812006-12-12 19:22:02 +00002199 * This function retrieves all the storage id's of a device and there
2200 * properties. Then creates a linked list and puts the list head into
Linus Walleijf8491912006-12-15 10:23:30 +00002201 * the device struct. It also optionally sorts this list. If you want
2202 * to display storage information in your application you should call
2203 * this function, then dereference the device struct
2204 * (<code>device->storage</code>) to get out information on the storage.
2205 *
Linus Walleij9e1b0812006-12-12 19:22:02 +00002206 * @param device a pointer to the device to get the filetype capabilities for.
2207 * @param sortby an integer that determines the sorting of the storage list.
2208 * Valid sort methods are defined in libmtp.h with beginning with
2209 * LIBMTP_STORAGE_SORTBY_. 0 or LIBMTP_STORAGE_SORTBY_NOTSORTED to not
2210 * sort.
2211 * @return 0 on success, 1 success but only with storage id's, storage
2212 * properities could not be retrieved and -1 means failure.
2213 * @see LIBMTP_Get_Filetype_Description()
2214 */
2215int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *device, int const sortby)
2216{
2217 uint32_t i = 0;
2218 PTPStorageInfo storageInfo;
2219 PTPParams *params = (PTPParams *) device->params;
2220 PTPStorageIDs storageIDs;
Linus Walleije1ac07e2006-12-14 19:38:59 +00002221 LIBMTP_devicestorage_t *storage = NULL;
2222 LIBMTP_devicestorage_t *storageprev = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002223
2224 if (device->storage != NULL)
2225 free_storage_list(device);
2226
2227 // if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs))
2228 // return -1;
Richard Lowbd14bf42007-07-21 11:25:11 +00002229 if (ptp_getstorageids (params, &storageIDs) != PTP_RC_OK)
Linus Walleij9e1b0812006-12-12 19:22:02 +00002230 return -1;
2231 if (storageIDs.n < 1)
2232 return -1;
2233
2234 if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
2235 for (i = 0; i < storageIDs.n; i++) {
2236
Linus Walleije1ac07e2006-12-14 19:38:59 +00002237 storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
2238 storage->prev = storageprev;
2239 if (storageprev != NULL)
2240 storageprev->next = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002241 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00002242 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002243
Linus Walleije1ac07e2006-12-14 19:38:59 +00002244 storage->id = storageIDs.Storage[i];
2245 storage->StorageType = PTP_ST_Undefined;
2246 storage->FilesystemType = PTP_FST_Undefined;
2247 storage->AccessCapability = PTP_AC_ReadWrite;
2248 storage->MaxCapacity = (uint64_t) -1;
2249 storage->FreeSpaceInBytes = (uint64_t) -1;
2250 storage->FreeSpaceInObjects = (uint64_t) -1;
2251 storage->StorageDescription = strdup("Unknown storage");
2252 storage->VolumeIdentifier = strdup("Unknown volume");
2253 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002254
Linus Walleije1ac07e2006-12-14 19:38:59 +00002255 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002256 }
2257 free(storageIDs.Storage);
2258 return 1;
2259 } else {
2260 for (i = 0; i < storageIDs.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002261 uint16_t ret;
2262 ret = ptp_getstorageinfo(params, storageIDs.Storage[i], &storageInfo);
2263 if (ret != PTP_RC_OK) {
2264 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Storage(): Could not get storage info.");
Linus Walleij9e1b0812006-12-12 19:22:02 +00002265 if (device->storage != NULL) {
2266 free_storage_list(device);
2267 }
2268 return -1;
2269 }
2270
Linus Walleije1ac07e2006-12-14 19:38:59 +00002271 storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
2272 storage->prev = storageprev;
2273 if (storageprev != NULL)
2274 storageprev->next = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002275 if (device->storage == NULL)
Linus Walleije1ac07e2006-12-14 19:38:59 +00002276 device->storage = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002277
Linus Walleije1ac07e2006-12-14 19:38:59 +00002278 storage->id = storageIDs.Storage[i];
2279 storage->StorageType = storageInfo.StorageType;
2280 storage->FilesystemType = storageInfo.FilesystemType;
2281 storage->AccessCapability = storageInfo.AccessCapability;
2282 storage->MaxCapacity = storageInfo.MaxCapability;
2283 storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
2284 storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
2285 storage->StorageDescription = storageInfo.StorageDescription;
2286 storage->VolumeIdentifier = storageInfo.VolumeLabel;
2287 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002288
Linus Walleije1ac07e2006-12-14 19:38:59 +00002289 storageprev = storage;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002290 }
2291
Linus Walleije1ac07e2006-12-14 19:38:59 +00002292 storage->next = NULL;
Linus Walleij9e1b0812006-12-12 19:22:02 +00002293
2294 sort_storage_by(device,sortby);
2295 free(storageIDs.Storage);
2296 return 0;
2297 }
2298}
2299
2300/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00002301 * This creates a new file metadata structure and allocates memory
2302 * for it. Notice that if you add strings to this structure they
2303 * will be freed by the corresponding <code>LIBMTP_destroy_file_t</code>
mopoke96143402006-10-30 04:37:26 +00002304 * operation later, so be careful of using strdup() when assigning
Linus Walleijf6bc1782006-03-24 15:12:47 +00002305 * strings, e.g.:
2306 *
2307 * <pre>
2308 * LIBMTP_file_t *file = LIBMTP_new_file_t();
2309 * file->filename = strdup(namestr);
2310 * ....
2311 * LIBMTP_destroy_file_t(file);
2312 * </pre>
2313 *
2314 * @return a pointer to the newly allocated metadata structure.
2315 * @see LIBMTP_destroy_file_t()
2316 */
2317LIBMTP_file_t *LIBMTP_new_file_t(void)
2318{
2319 LIBMTP_file_t *new = (LIBMTP_file_t *) malloc(sizeof(LIBMTP_file_t));
2320 if (new == NULL) {
2321 return NULL;
2322 }
2323 new->filename = NULL;
2324 new->filesize = 0;
2325 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
2326 new->next = NULL;
2327 return new;
2328}
2329
2330/**
2331 * This destroys a file metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00002332 * used by it, including any strings. Never use a file metadata
Linus Walleijf6bc1782006-03-24 15:12:47 +00002333 * structure again after calling this function on it.
2334 * @param file the file metadata to destroy.
2335 * @see LIBMTP_new_file_t()
2336 */
2337void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
2338{
2339 if (file == NULL) {
2340 return;
2341 }
2342 if (file->filename != NULL)
2343 free(file->filename);
2344 free(file);
2345 return;
2346}
2347
2348/**
mopoke31364442006-11-20 04:53:04 +00002349* THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Richard Lowdc0b6c72006-11-13 09:22:23 +00002350 * NOT TO USE IT.
2351 * @see LIBMTP_Get_Filelisting_With_Callback()
2352 */
2353LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *device)
2354{
2355 printf("WARNING: LIBMTP_Get_Filelisting() is deprecated.\n");
2356 printf("WARNING: please update your code to use LIBMTP_Get_Filelisting_With_Callback()\n");
2357 return LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
2358}
2359
2360/**
Linus Walleijf6bc1782006-03-24 15:12:47 +00002361 * This returns a long list of all files available
2362 * on the current MTP device. Typical usage:
2363 *
2364 * <pre>
2365 * LIBMTP_file_t *filelist;
2366 *
Richard Lowdc0b6c72006-11-13 09:22:23 +00002367 * filelist = LIBMTP_Get_Filelisting_With_Callback(device, callback, data);
Linus Walleijf6bc1782006-03-24 15:12:47 +00002368 * while (filelist != NULL) {
2369 * LIBMTP_file_t *tmp;
2370 *
2371 * // Do something on each element in the list here...
2372 * tmp = filelist;
2373 * filelist = filelist->next;
2374 * LIBMTP_destroy_file_t(tmp);
2375 * }
2376 * </pre>
2377 *
2378 * @param device a pointer to the device to get the file listing for.
Richard Lowdc0b6c72006-11-13 09:22:23 +00002379 * @param callback a function to be called during the tracklisting retrieveal
mopoke31364442006-11-20 04:53:04 +00002380 * for displaying progress bars etc, or NULL if you don't want
Richard Lowdc0b6c72006-11-13 09:22:23 +00002381 * any callbacks.
2382 * @param data a user-defined pointer that is passed along to
2383 * the <code>progress</code> function in order to
2384 * pass along some user defined data to the progress
2385 * updates. If not used, set this to NULL.
Linus Walleijf6bc1782006-03-24 15:12:47 +00002386 * @return a list of files that can be followed using the <code>next</code>
2387 * field of the <code>LIBMTP_file_t</code> data structure.
2388 * Each of the metadata tags must be freed after use, and may
2389 * contain only partial metadata information, i.e. one or several
2390 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002391 * @see LIBMTP_Get_Filemetadata()
Linus Walleijf6bc1782006-03-24 15:12:47 +00002392 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00002393LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
2394 LIBMTP_progressfunc_t const callback,
2395 void const * const data)
Linus Walleijf6bc1782006-03-24 15:12:47 +00002396{
2397 uint32_t i = 0;
2398 LIBMTP_file_t *retfiles = NULL;
2399 LIBMTP_file_t *curfile = NULL;
2400 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00002401
mopoke96143402006-10-30 04:37:26 +00002402 // Get all the handles if we haven't already done that
Linus Walleijf6bc1782006-03-24 15:12:47 +00002403 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00002404 flush_handles(device);
Linus Walleijf6bc1782006-03-24 15:12:47 +00002405 }
mopoke96143402006-10-30 04:37:26 +00002406
Linus Walleijf6bc1782006-03-24 15:12:47 +00002407 for (i = 0; i < params->handles.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002408 LIBMTP_file_t *file;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002409 PTPObjectInfo *oi;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002410
Richard Lowdc0b6c72006-11-13 09:22:23 +00002411 if (callback != NULL)
2412 callback(i, params->handles.n, data);
mopoke31364442006-11-20 04:53:04 +00002413
Linus Walleijf0bf4372007-07-01 21:47:38 +00002414 oi = &params->objectinfo[i];
Linus Walleijf6bc1782006-03-24 15:12:47 +00002415
Linus Walleijf0bf4372007-07-01 21:47:38 +00002416 if (oi->ObjectFormat == PTP_OFC_Association) {
2417 // MTP use thesis object format for folders which means
2418 // these "files" will turn up on a folder listing instead.
2419 continue;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002420 }
2421
Linus Walleijf0bf4372007-07-01 21:47:38 +00002422 // Allocate a new file type
2423 file = LIBMTP_new_file_t();
2424
2425 file->parent_id = oi->ParentObject;
2426
2427 // Set the filetype
2428 file->filetype = map_ptp_type_to_libmtp_type(oi->ObjectFormat);
2429
2430 // Original file-specific properties
2431 file->filesize = oi->ObjectCompressedSize;
2432 if (oi->Filename != NULL) {
2433 file->filename = strdup(oi->Filename);
2434 }
2435
2436 // This is some sort of unique ID so we can keep track of the track.
2437 file->item_id = params->handles.Handler[i];
2438
2439 // Add track to a list that will be returned afterwards.
2440 if (retfiles == NULL) {
2441 retfiles = file;
2442 curfile = file;
2443 } else {
2444 curfile->next = file;
2445 curfile = file;
2446 }
2447
2448 // Call listing callback
2449 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
2450
Linus Walleijf6bc1782006-03-24 15:12:47 +00002451 } // Handle counting loop
2452 return retfiles;
2453}
2454
2455/**
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002456 * This function retrieves the metadata for a single file off
2457 * the device.
2458 *
2459 * Do not call this function repeatedly! The file handles are linearly
2460 * searched O(n) and the call may involve (slow) USB traffic, so use
2461 * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
2462 * as an efficient data structure such as a hash list.
2463 *
2464 * @param device a pointer to the device to get the file metadata from.
2465 * @param fileid the object ID of the file that you want the metadata for.
2466 * @return a metadata entry on success or NULL on failure.
2467 * @see LIBMTP_Get_Filelisting()
2468 */
2469LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
2470{
2471 uint32_t i = 0;
2472 PTPParams *params = (PTPParams *) device->params;
2473
mopoke96143402006-10-30 04:37:26 +00002474 // Get all the handles if we haven't already done that
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002475 if (params->handles.Handler == NULL) {
2476 flush_handles(device);
2477 }
2478
2479 for (i = 0; i < params->handles.n; i++) {
2480 LIBMTP_file_t *file;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002481 PTPObjectInfo *oi;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002482
2483 // Is this the file we're looking for?
2484 if (params->handles.Handler[i] != fileid) {
2485 continue;
2486 }
2487
Linus Walleijf0bf4372007-07-01 21:47:38 +00002488 oi = &params->objectinfo[i];
Linus Walleij070e9b42007-01-22 08:49:28 +00002489
Linus Walleijf0bf4372007-07-01 21:47:38 +00002490 if (oi->ObjectFormat == PTP_OFC_Association) {
2491 // MTP use thesis object format for folders which means
2492 // these "files" will turn up on a folder listing instead.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002493 return NULL;
2494 }
2495
Linus Walleijf0bf4372007-07-01 21:47:38 +00002496 // Allocate a new file type
2497 file = LIBMTP_new_file_t();
2498
2499 file->parent_id = oi->ParentObject;
2500
2501 // Set the filetype
2502 file->filetype = map_ptp_type_to_libmtp_type(oi->ObjectFormat);
2503
2504 // Original file-specific properties
2505 file->filesize = oi->ObjectCompressedSize;
2506 if (oi->Filename != NULL) {
2507 file->filename = strdup(oi->Filename);
2508 }
2509
2510 // This is some sort of unique ID so we can keep track of the track.
2511 file->item_id = params->handles.Handler[i];
2512
2513 return file;
2514
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002515 }
2516 return NULL;
2517}
2518
2519/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00002520 * This creates a new track metadata structure and allocates memory
2521 * for it. Notice that if you add strings to this structure they
2522 * will be freed by the corresponding <code>LIBMTP_destroy_track_t</code>
mopoke96143402006-10-30 04:37:26 +00002523 * operation later, so be careful of using strdup() when assigning
Linus Walleij394bbbe2006-02-22 16:10:53 +00002524 * strings, e.g.:
2525 *
Linus Walleij17e39f72006-02-23 15:54:28 +00002526 * <pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00002527 * LIBMTP_track_t *track = LIBMTP_new_track_t();
2528 * track->title = strdup(titlestr);
2529 * ....
2530 * LIBMTP_destroy_track_t(track);
Linus Walleij17e39f72006-02-23 15:54:28 +00002531 * </pre>
Linus Walleij394bbbe2006-02-22 16:10:53 +00002532 *
2533 * @return a pointer to the newly allocated metadata structure.
2534 * @see LIBMTP_destroy_track_t()
2535 */
2536LIBMTP_track_t *LIBMTP_new_track_t(void)
Linus Walleijb9256fd2006-02-15 09:40:43 +00002537{
2538 LIBMTP_track_t *new = (LIBMTP_track_t *) malloc(sizeof(LIBMTP_track_t));
2539 if (new == NULL) {
2540 return NULL;
2541 }
2542 new->title = NULL;
2543 new->artist = NULL;
2544 new->album = NULL;
2545 new->genre = NULL;
2546 new->date = NULL;
2547 new->filename = NULL;
2548 new->duration = 0;
2549 new->tracknumber = 0;
2550 new->filesize = 0;
Linus Walleijf6bc1782006-03-24 15:12:47 +00002551 new->filetype = LIBMTP_FILETYPE_UNKNOWN;
Linus Walleijcf223e62006-06-19 09:31:53 +00002552 new->samplerate = 0;
2553 new->nochannels = 0;
2554 new->wavecodec = 0;
2555 new->bitrate = 0;
2556 new->bitratetype = 0;
2557 new->rating = 0;
2558 new->usecount = 0;
Linus Walleijb9256fd2006-02-15 09:40:43 +00002559 new->next = NULL;
2560 return new;
2561}
2562
Linus Walleij394bbbe2006-02-22 16:10:53 +00002563/**
2564 * This destroys a track metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00002565 * used by it, including any strings. Never use a track metadata
Linus Walleij394bbbe2006-02-22 16:10:53 +00002566 * structure again after calling this function on it.
2567 * @param track the track metadata to destroy.
2568 * @see LIBMTP_new_track_t()
2569 */
Linus Walleijb9256fd2006-02-15 09:40:43 +00002570void LIBMTP_destroy_track_t(LIBMTP_track_t *track)
2571{
2572 if (track == NULL) {
2573 return;
2574 }
2575 if (track->title != NULL)
2576 free(track->title);
2577 if (track->artist != NULL)
2578 free(track->artist);
2579 if (track->album != NULL)
2580 free(track->album);
2581 if (track->genre != NULL)
2582 free(track->genre);
2583 if (track->date != NULL)
2584 free(track->date);
2585 if (track->filename != NULL)
2586 free(track->filename);
2587 free(track);
2588 return;
2589}
2590
2591/**
Linus Walleij338ade42007-07-03 20:44:08 +00002592 * This function maps and copies a property onto the track metadata if applicable.
2593 */
2594static void pick_property_to_track_metadata(MTPPropList *prop, LIBMTP_track_t *track)
2595{
2596 switch (prop->property) {
2597 case PTP_OPC_Name:
2598 if (prop->propval.str != NULL)
2599 track->title = strdup(prop->propval.str);
2600 else
2601 track->title = NULL;
2602 break;
2603 case PTP_OPC_Artist:
2604 if (prop->propval.str != NULL)
2605 track->artist = strdup(prop->propval.str);
2606 else
2607 track->artist = NULL;
2608 break;
2609 case PTP_OPC_Duration:
2610 track->duration = prop->propval.u32;
2611 break;
2612 case PTP_OPC_Track:
2613 track->tracknumber = prop->propval.u16;
2614 break;
2615 case PTP_OPC_Genre:
2616 if (prop->propval.str != NULL)
2617 track->genre = strdup(prop->propval.str);
2618 else
2619 track->genre = NULL;
2620 break;
2621 case PTP_OPC_AlbumName:
2622 if (prop->propval.str != NULL)
2623 track->album = strdup(prop->propval.str);
2624 else
2625 track->album = NULL;
2626 break;
2627 case PTP_OPC_OriginalReleaseDate:
2628 if (prop->propval.str != NULL)
2629 track->date = strdup(prop->propval.str);
2630 else
2631 track->date = NULL;
2632 break;
2633 // These are, well not so important.
2634 case PTP_OPC_SampleRate:
2635 track->samplerate = prop->propval.u32;
2636 break;
2637 case PTP_OPC_NumberOfChannels:
2638 track->nochannels = prop->propval.u16;
2639 break;
2640 case PTP_OPC_AudioWAVECodec:
2641 track->wavecodec = prop->propval.u32;
2642 break;
2643 case PTP_OPC_AudioBitRate:
2644 track->bitrate = prop->propval.u32;
2645 break;
2646 case PTP_OPC_BitRateType:
2647 track->bitratetype = prop->propval.u16;
2648 break;
2649 case PTP_OPC_Rating:
2650 track->rating = prop->propval.u16;
2651 break;
2652 case PTP_OPC_UseCount:
2653 track->usecount = prop->propval.u32;
2654 break;
2655 }
2656}
2657
2658/**
Linus Walleij8ab54262006-06-21 07:12:28 +00002659 * This function retrieves the track metadata for a track
2660 * given by a unique ID.
2661 * @param device a pointer to the device to get the track metadata off.
2662 * @param trackid the unique ID of the track.
2663 * @param objectformat the object format of this track, so we know what it supports.
2664 * @param track a metadata set to fill in.
2665 */
2666static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
2667 LIBMTP_track_t *track)
2668{
Linus Walleij00cf0642006-07-26 20:40:59 +00002669 uint16_t ret;
2670 PTPParams *params = (PTPParams *) device->params;
Linus Walleij1a1b2d12006-11-23 13:32:17 +00002671 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij00cf0642006-07-26 20:40:59 +00002672 uint32_t i;
Linus Walleij00cf0642006-07-26 20:40:59 +00002673
Linus Walleij338ade42007-07-03 20:44:08 +00002674 /*
2675 * If we have a cached, large set of metadata, then use it!
2676 */
2677 if (params->proplist) {
2678 MTPPropList *prop = params->proplist;
2679
2680 while (prop != NULL && prop->ObjectHandle != track->item_id) {
2681 prop = prop->next;
2682 }
2683 while (prop != NULL && prop->ObjectHandle == track->item_id) {
2684 pick_property_to_track_metadata(prop, track);
2685 prop = prop->next;
2686 }
2687 } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
2688 && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)) {
Linus Walleij3fcfea52006-11-13 07:07:36 +00002689 MTPPropList *proplist = NULL;
2690 MTPPropList *prop;
mopoke31364442006-11-20 04:53:04 +00002691
Linus Walleij1a1b2d12006-11-23 13:32:17 +00002692 /*
2693 * This should retrieve all properties for an object, but on devices
2694 * which are inherently broken it will not, so these need the
2695 * special flag DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST.
2696 */
Linus Walleij3fcfea52006-11-13 07:07:36 +00002697 ret = ptp_mtp_getobjectproplist(params, track->item_id, &proplist);
Linus Walleij070e9b42007-01-22 08:49:28 +00002698 if (ret != PTP_RC_OK) {
2699 add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectproplist() failed.");
2700 return;
2701 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00002702 prop = proplist;
Linus Walleij338ade42007-07-03 20:44:08 +00002703 while (prop != NULL && prop->ObjectHandle == track->item_id) {
2704 pick_property_to_track_metadata(prop, track);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002705 prop = prop->next;
Linus Walleij00cf0642006-07-26 20:40:59 +00002706 }
Linus Walleije7df6532007-03-23 08:20:06 +00002707 destroy_mtp_prop_list(proplist);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002708 } else {
Linus Walleij3fcfea52006-11-13 07:07:36 +00002709 uint16_t *props = NULL;
2710 uint32_t propcnt = 0;
2711
2712 // First see which properties can be retrieved for this object format
Linus Walleij070e9b42007-01-22 08:49:28 +00002713 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(track->filetype), &propcnt, &props);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002714 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002715 add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
Linus Walleij3fcfea52006-11-13 07:07:36 +00002716 // Just bail out for now, nothing is ever set.
2717 return;
2718 } else {
2719 for (i=0;i<propcnt;i++) {
2720 switch (props[i]) {
2721 case PTP_OPC_Name:
Linus Walleij9901e222006-11-30 12:28:19 +00002722 track->title = get_string_from_object(device, track->item_id, PTP_OPC_Name);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002723 break;
2724 case PTP_OPC_Artist:
Linus Walleij9901e222006-11-30 12:28:19 +00002725 track->artist = get_string_from_object(device, track->item_id, PTP_OPC_Artist);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002726 break;
2727 case PTP_OPC_Duration:
Linus Walleij9901e222006-11-30 12:28:19 +00002728 track->duration = get_u32_from_object(device, track->item_id, PTP_OPC_Duration, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002729 break;
2730 case PTP_OPC_Track:
Linus Walleij9901e222006-11-30 12:28:19 +00002731 track->tracknumber = get_u16_from_object(device, track->item_id, PTP_OPC_Track, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002732 break;
2733 case PTP_OPC_Genre:
Linus Walleij9901e222006-11-30 12:28:19 +00002734 track->genre = get_string_from_object(device, track->item_id, PTP_OPC_Genre);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002735 break;
2736 case PTP_OPC_AlbumName:
Linus Walleij9901e222006-11-30 12:28:19 +00002737 track->album = get_string_from_object(device, track->item_id, PTP_OPC_AlbumName);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002738 break;
2739 case PTP_OPC_OriginalReleaseDate:
Linus Walleij9901e222006-11-30 12:28:19 +00002740 track->date = get_string_from_object(device, track->item_id, PTP_OPC_OriginalReleaseDate);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002741 break;
2742 // These are, well not so important.
2743 case PTP_OPC_SampleRate:
Linus Walleij9901e222006-11-30 12:28:19 +00002744 track->samplerate = get_u32_from_object(device, track->item_id, PTP_OPC_SampleRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002745 break;
2746 case PTP_OPC_NumberOfChannels:
Linus Walleij9901e222006-11-30 12:28:19 +00002747 track->nochannels = get_u16_from_object(device, track->item_id, PTP_OPC_NumberOfChannels, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002748 break;
2749 case PTP_OPC_AudioWAVECodec:
Linus Walleij9901e222006-11-30 12:28:19 +00002750 track->wavecodec = get_u32_from_object(device, track->item_id, PTP_OPC_AudioWAVECodec, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002751 break;
2752 case PTP_OPC_AudioBitRate:
Linus Walleij9901e222006-11-30 12:28:19 +00002753 track->bitrate = get_u32_from_object(device, track->item_id, PTP_OPC_AudioBitRate, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002754 break;
2755 case PTP_OPC_BitRateType:
Linus Walleij9901e222006-11-30 12:28:19 +00002756 track->bitratetype = get_u16_from_object(device, track->item_id, PTP_OPC_BitRateType, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002757 break;
2758 case PTP_OPC_Rating:
Linus Walleij9901e222006-11-30 12:28:19 +00002759 track->rating = get_u16_from_object(device, track->item_id, PTP_OPC_Rating, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002760 break;
2761 case PTP_OPC_UseCount:
Linus Walleij9901e222006-11-30 12:28:19 +00002762 track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002763 break;
2764 }
2765 }
2766 free(props);
2767 }
Linus Walleij00cf0642006-07-26 20:40:59 +00002768 }
Linus Walleij8ab54262006-06-21 07:12:28 +00002769}
2770
2771/**
mopoke31364442006-11-20 04:53:04 +00002772 * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
Linus Walleij3fcfea52006-11-13 07:07:36 +00002773 * NOT TO USE IT.
2774 * @see LIBMTP_Get_Tracklisting_With_Callback()
2775 */
2776LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t *device)
2777{
2778 printf("WARNING: LIBMTP_Get_Tracklisting() is deprecated.\n");
2779 printf("WARNING: please update your code to use LIBMTP_Get_Tracklisting_With_Callback()\n");
Richard Lowdc0b6c72006-11-13 09:22:23 +00002780 return LIBMTP_Get_Tracklisting_With_Callback(device, NULL, NULL);
Linus Walleij3fcfea52006-11-13 07:07:36 +00002781}
2782
2783/**
Linus Walleijb9256fd2006-02-15 09:40:43 +00002784 * This returns a long list of all tracks available
Linus Walleija4982732006-02-24 15:46:02 +00002785 * on the current MTP device. Typical usage:
2786 *
2787 * <pre>
2788 * LIBMTP_track_t *tracklist;
2789 *
Richard Low6711f442007-05-05 19:00:59 +00002790 * tracklist = LIBMTP_Get_Tracklisting_With_Callback(device, callback, data);
Linus Walleija4982732006-02-24 15:46:02 +00002791 * while (tracklist != NULL) {
2792 * LIBMTP_track_t *tmp;
2793 *
2794 * // Do something on each element in the list here...
2795 * tmp = tracklist;
2796 * tracklist = tracklist->next;
2797 * LIBMTP_destroy_track_t(tmp);
2798 * }
2799 * </pre>
2800 *
Linus Walleijb9256fd2006-02-15 09:40:43 +00002801 * @param device a pointer to the device to get the track listing for.
Linus Walleij3fcfea52006-11-13 07:07:36 +00002802 * @param callback a function to be called during the tracklisting retrieveal
mopoke31364442006-11-20 04:53:04 +00002803 * for displaying progress bars etc, or NULL if you don't want
Linus Walleij3fcfea52006-11-13 07:07:36 +00002804 * any callbacks.
Richard Lowdc0b6c72006-11-13 09:22:23 +00002805 * @param data a user-defined pointer that is passed along to
2806 * the <code>progress</code> function in order to
2807 * pass along some user defined data to the progress
2808 * updates. If not used, set this to NULL.
Linus Walleija4982732006-02-24 15:46:02 +00002809 * @return a list of tracks that can be followed using the <code>next</code>
2810 * field of the <code>LIBMTP_track_t</code> data structure.
2811 * Each of the metadata tags must be freed after use, and may
2812 * contain only partial metadata information, i.e. one or several
2813 * fields may be NULL or 0.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002814 * @see LIBMTP_Get_Trackmetadata()
Linus Walleijb9256fd2006-02-15 09:40:43 +00002815 */
Richard Lowdc0b6c72006-11-13 09:22:23 +00002816LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t *device,
2817 LIBMTP_progressfunc_t const callback,
2818 void const * const data)
Linus Walleijb9256fd2006-02-15 09:40:43 +00002819{
2820 uint32_t i = 0;
2821 LIBMTP_track_t *retracks = NULL;
2822 LIBMTP_track_t *curtrack = NULL;
Linus Walleij9b28da32006-03-16 13:47:58 +00002823 PTPParams *params = (PTPParams *) device->params;
Linus Walleij2be40c72007-03-16 15:19:44 +00002824 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij438bd7f2006-06-08 11:35:44 +00002825
mopoke96143402006-10-30 04:37:26 +00002826 // Get all the handles if we haven't already done that
Linus Walleij9b28da32006-03-16 13:47:58 +00002827 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00002828 flush_handles(device);
Linus Walleijb9256fd2006-02-15 09:40:43 +00002829 }
mopoke96143402006-10-30 04:37:26 +00002830
Linus Walleij9b28da32006-03-16 13:47:58 +00002831 for (i = 0; i < params->handles.n; i++) {
Linus Walleij070e9b42007-01-22 08:49:28 +00002832 LIBMTP_track_t *track;
Linus Walleijf0bf4372007-07-01 21:47:38 +00002833 PTPObjectInfo *oi;
mopoke31364442006-11-20 04:53:04 +00002834
Richard Lowdc0b6c72006-11-13 09:22:23 +00002835 if (callback != NULL)
2836 callback(i, params->handles.n, data);
mopoke31364442006-11-20 04:53:04 +00002837
Linus Walleijf0bf4372007-07-01 21:47:38 +00002838 oi = &params->objectinfo[i];
mopoke96143402006-10-30 04:37:26 +00002839
Linus Walleijf0bf4372007-07-01 21:47:38 +00002840 // Ignore stuff we don't know how to handle...
2841 // TODO: get this list as an intersection of the sets
2842 // supported by the device and the from the device and
2843 // all known audio track files?
2844 if ( oi->ObjectFormat != PTP_OFC_WAV &&
2845 oi->ObjectFormat != PTP_OFC_MP3 &&
2846 oi->ObjectFormat != PTP_OFC_MTP_MP2 &&
2847 oi->ObjectFormat != PTP_OFC_MTP_WMA &&
2848 oi->ObjectFormat != PTP_OFC_MTP_OGG &&
2849 oi->ObjectFormat != PTP_OFC_MTP_FLAC &&
2850 oi->ObjectFormat != PTP_OFC_MTP_AAC &&
2851 oi->ObjectFormat != PTP_OFC_MTP_M4A &&
2852 oi->ObjectFormat != PTP_OFC_MTP_MP4 &&
2853 oi->ObjectFormat != PTP_OFC_MTP_UndefinedAudio &&
2854 // This row lets through undefined files for examination since they may be forgotten OGG files.
2855 (oi->ObjectFormat != PTP_OFC_Undefined || !(ptp_usb->device_flags & DEVICE_FLAG_IRIVER_OGG_ALZHEIMER))
2856 ) {
2857 // printf("Not a music track (format: %d), skipping...\n",oi.ObjectFormat);
2858 continue;
Linus Walleijb9256fd2006-02-15 09:40:43 +00002859 }
2860
Linus Walleijf0bf4372007-07-01 21:47:38 +00002861 // Allocate a new track type
2862 track = LIBMTP_new_track_t();
2863
2864 // This is some sort of unique ID so we can keep track of the track.
2865 track->item_id = params->handles.Handler[i];
2866 track->parent_id = oi->ParentObject;
2867
2868 track->filetype = map_ptp_type_to_libmtp_type(oi->ObjectFormat);
2869
2870 // Original file-specific properties
2871 track->filesize = oi->ObjectCompressedSize;
2872 if (oi->Filename != NULL) {
2873 track->filename = strdup(oi->Filename);
2874 }
2875
2876 get_track_metadata(device, oi->ObjectFormat, track);
2877
2878 /*
2879 * A special quirk for iriver devices that doesn't quite
2880 * remember that some files marked as "unknown" type are
2881 * actually OGG files. We look at the filename extension
2882 * and see if it happens that this was atleast named "ogg"
2883 * and fall back on this heuristic approach in that case,
2884 * for these bugged devices only.
2885 */
2886 if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
2887 ptp_usb->device_flags & DEVICE_FLAG_IRIVER_OGG_ALZHEIMER) {
2888 // Repair forgotten OGG filetype
2889 char *ptype;
2890
2891 ptype = rindex(track->filename,'.')+1;
2892 if (ptype != NULL && !strcasecmp (ptype, "ogg")) {
2893 // Fix it.
2894 track->filetype = LIBMTP_FILETYPE_OGG;
2895 } else {
2896 // This was not an OGG file so discard it and continue
2897 LIBMTP_destroy_track_t(track);
2898 continue;
2899 }
2900 }
2901
2902 // Add track to a list that will be returned afterwards.
2903 if (retracks == NULL) {
2904 retracks = track;
2905 curtrack = track;
2906 } else {
2907 curtrack->next = track;
2908 curtrack = track;
2909 }
2910
2911 // Call listing callback
2912 // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
2913
2914
Linus Walleijb9256fd2006-02-15 09:40:43 +00002915 } // Handle counting loop
2916 return retracks;
2917}
Linus Walleijdcde6082006-02-17 16:16:34 +00002918
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002919/**
2920 * This function retrieves the metadata for a single track off
2921 * the device.
2922 *
2923 * Do not call this function repeatedly! The track handles are linearly
2924 * searched O(n) and the call may involve (slow) USB traffic, so use
2925 * <code>LIBMTP_Get_Tracklisting()</code> and cache the tracks, preferably
2926 * as an efficient data structure such as a hash list.
2927 *
2928 * @param device a pointer to the device to get the track metadata from.
2929 * @param trackid the object ID of the track that you want the metadata for.
2930 * @return a track metadata entry on success or NULL on failure.
2931 * @see LIBMTP_Get_Tracklisting()
2932 */
2933LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t *device, uint32_t const trackid)
2934{
2935 uint32_t i = 0;
2936 PTPParams *params = (PTPParams *) device->params;
2937
mopoke96143402006-10-30 04:37:26 +00002938 // Get all the handles if we haven't already done that
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002939 if (params->handles.Handler == NULL) {
2940 flush_handles(device);
2941 }
2942
2943 for (i = 0; i < params->handles.n; i++) {
Linus Walleijf0bf4372007-07-01 21:47:38 +00002944 PTPObjectInfo *oi;
2945 LIBMTP_track_t *track;
mopoke96143402006-10-30 04:37:26 +00002946
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002947 // Skip if this is not the track we want.
2948 if (params->handles.Handler[i] != trackid) {
2949 continue;
2950 }
2951
Linus Walleijf0bf4372007-07-01 21:47:38 +00002952 oi = &params->objectinfo[i];
mopoke96143402006-10-30 04:37:26 +00002953
Linus Walleijf0bf4372007-07-01 21:47:38 +00002954 // Ignore stuff we don't know how to handle...
2955 if ( oi->ObjectFormat != PTP_OFC_WAV &&
2956 oi->ObjectFormat != PTP_OFC_MP3 &&
2957 oi->ObjectFormat != PTP_OFC_MTP_MP2 &&
2958 oi->ObjectFormat != PTP_OFC_MTP_WMA &&
2959 oi->ObjectFormat != PTP_OFC_MTP_OGG &&
2960 oi->ObjectFormat != PTP_OFC_MTP_FLAC &&
2961 oi->ObjectFormat != PTP_OFC_MTP_AAC &&
2962 oi->ObjectFormat != PTP_OFC_MTP_M4A &&
2963 oi->ObjectFormat != PTP_OFC_MTP_MP4 &&
2964 oi->ObjectFormat != PTP_OFC_MTP_UndefinedAudio ) {
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002965 return NULL;
2966 }
2967
Linus Walleijf0bf4372007-07-01 21:47:38 +00002968 // Allocate a new track type
2969 track = LIBMTP_new_track_t();
2970
2971 // This is some sort of unique ID so we can keep track of the track.
2972 track->item_id = params->handles.Handler[i];
2973 track->parent_id = oi->ParentObject;
2974
2975 track->filetype = map_ptp_type_to_libmtp_type(oi->ObjectFormat);
2976
2977 // Original file-specific properties
2978 track->filesize = oi->ObjectCompressedSize;
2979 if (oi->Filename != NULL) {
2980 track->filename = strdup(oi->Filename);
2981 }
2982
2983 get_track_metadata(device, oi->ObjectFormat, track);
2984
2985 return track;
2986
Linus Walleij2e4b5f92006-06-16 14:00:49 +00002987 }
2988 return NULL;
2989}
2990
Linus Walleijf6bc1782006-03-24 15:12:47 +00002991
2992/**
2993 * This gets a file off the device to a local file identified
2994 * by a filename.
2995 * @param device a pointer to the device to get the track from.
2996 * @param id the file ID of the file to retrieve.
2997 * @param path a filename to use for the retrieved file.
2998 * @param callback a progress indicator function or NULL to ignore.
2999 * @param data a user-defined pointer that is passed along to
3000 * the <code>progress</code> function in order to
3001 * pass along some user defined data to the progress
3002 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00003003 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00003004 * failure.
3005 * @see LIBMTP_Get_File_To_File_Descriptor()
3006 */
mopoke96143402006-10-30 04:37:26 +00003007int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00003008 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00003009 void const * const data)
3010{
3011 int fd = -1;
3012 int ret;
3013
3014 // Sanity check
3015 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003016 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 +00003017 return -1;
3018 }
3019
3020 // Open file
3021#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00003022#ifdef USE_WINDOWS_IO_H
3023 if ( (fd = _open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,_S_IREAD)) == -1 ) {
3024#else
flavienbfd80eb2006-06-01 22:41:49 +00003025 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IRWXU|S_IRGRP)) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00003026#endif
Linus Walleijf6bc1782006-03-24 15:12:47 +00003027#else
3028 if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP)) == -1) {
3029#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00003030 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Could not create file.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00003031 return -1;
3032 }
mopoke96143402006-10-30 04:37:26 +00003033
Linus Walleijf6bc1782006-03-24 15:12:47 +00003034 ret = LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
3035
3036 // Close file
3037 close(fd);
mopoke96143402006-10-30 04:37:26 +00003038
Linus Walleijf6bc1782006-03-24 15:12:47 +00003039 return ret;
3040}
3041
3042/**
3043 * This gets a file off the device to a file identified
3044 * by a file descriptor.
Linus Walleij0558ac52006-09-07 06:55:03 +00003045 *
mopoke96143402006-10-30 04:37:26 +00003046 * This function can potentially be used for streaming
3047 * files off the device for playback or broadcast for example,
Linus Walleij0558ac52006-09-07 06:55:03 +00003048 * by downloading the file into a stream sink e.g. a socket.
3049 *
Linus Walleijf6bc1782006-03-24 15:12:47 +00003050 * @param device a pointer to the device to get the file from.
3051 * @param id the file ID of the file to retrieve.
3052 * @param fd a local file descriptor to write the file to.
3053 * @param callback a progress indicator function or NULL to ignore.
3054 * @param data a user-defined pointer that is passed along to
3055 * the <code>progress</code> function in order to
3056 * pass along some user defined data to the progress
3057 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00003058 * @return 0 if the transfer was successful, any other value means
Linus Walleijf6bc1782006-03-24 15:12:47 +00003059 * failure.
3060 * @see LIBMTP_Get_File_To_File()
3061 */
mopoke96143402006-10-30 04:37:26 +00003062int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
3063 uint32_t const id,
3064 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00003065 LIBMTP_progressfunc_t const callback,
Linus Walleijf6bc1782006-03-24 15:12:47 +00003066 void const * const data)
3067{
Linus Walleijf0bf4372007-07-01 21:47:38 +00003068 PTPObjectInfo *oi;
3069 uint32_t i;
Linus Walleij438bd7f2006-06-08 11:35:44 +00003070 uint16_t ret;
Linus Walleijf6bc1782006-03-24 15:12:47 +00003071 PTPParams *params = (PTPParams *) device->params;
Linus Walleijee73ef22006-08-27 19:56:00 +00003072 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleijf6bc1782006-03-24 15:12:47 +00003073
Linus Walleijf0bf4372007-07-01 21:47:38 +00003074 oi = NULL;
3075 for (i = 0; i < params->handles.n; i++) {
3076 if (params->handles.Handler[i] == id) {
3077 oi = &params->objectinfo[i];
3078 break;
3079 }
3080 }
3081 if (oi == NULL) {
3082 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 +00003083 return -1;
3084 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00003085 if (oi->ObjectFormat == PTP_OFC_Association) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003086 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
Linus Walleijf6bc1782006-03-24 15:12:47 +00003087 return -1;
3088 }
Linus Walleijf6bc1782006-03-24 15:12:47 +00003089
Linus Walleijee73ef22006-08-27 19:56:00 +00003090 // Callbacks
3091 ptp_usb->callback_active = 1;
Linus Walleijf0bf4372007-07-01 21:47:38 +00003092 ptp_usb->current_transfer_total = oi->ObjectCompressedSize+
Linus Walleij7f0c72e2006-09-06 13:01:58 +00003093 PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
Linus Walleijee73ef22006-08-27 19:56:00 +00003094 ptp_usb->current_transfer_complete = 0;
3095 ptp_usb->current_transfer_callback = callback;
3096 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00003097
Linus Walleij96c62432006-08-21 10:04:02 +00003098 // This now exist in upstream
3099 ret = ptp_getobject_tofd(params, id, fd);
Linus Walleijee73ef22006-08-27 19:56:00 +00003100
3101 ptp_usb->callback_active = 0;
3102 ptp_usb->current_transfer_callback = NULL;
3103 ptp_usb->current_transfer_callback_data = NULL;
mopoke96143402006-10-30 04:37:26 +00003104
Linus Walleijb02a0662006-04-25 08:05:09 +00003105 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003106 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 +00003107 return -1;
3108 }
mopoke96143402006-10-30 04:37:26 +00003109
Linus Walleijf6bc1782006-03-24 15:12:47 +00003110 return 0;
3111}
3112
Linus Walleijdcde6082006-02-17 16:16:34 +00003113/**
3114 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00003115 * by a filename. This is actually just a wrapper for the
3116 * \c LIBMTP_Get_Track_To_File() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00003117 * @param device a pointer to the device to get the track from.
3118 * @param id the track ID of the track to retrieve.
3119 * @param path a filename to use for the retrieved track.
3120 * @param callback a progress indicator function or NULL to ignore.
3121 * @param data a user-defined pointer that is passed along to
3122 * the <code>progress</code> function in order to
3123 * pass along some user defined data to the progress
3124 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00003125 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00003126 * failure.
3127 * @see LIBMTP_Get_Track_To_File_Descriptor()
3128 */
mopoke96143402006-10-30 04:37:26 +00003129int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
Linus Walleijee73ef22006-08-27 19:56:00 +00003130 char const * const path, LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00003131 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00003132{
Linus Walleijf6bc1782006-03-24 15:12:47 +00003133 // This is just a wrapper
3134 return LIBMTP_Get_File_To_File(device, id, path, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00003135}
3136
3137/**
3138 * This gets a track off the device to a file identified
Linus Walleijf6bc1782006-03-24 15:12:47 +00003139 * by a file descriptor. This is actually just a wrapper for
3140 * the \c LIBMTP_Get_File_To_File_Descriptor() function.
Linus Walleijdcde6082006-02-17 16:16:34 +00003141 * @param device a pointer to the device to get the track from.
3142 * @param id the track ID of the track to retrieve.
3143 * @param fd a file descriptor to write the track to.
3144 * @param callback a progress indicator function or NULL to ignore.
3145 * @param data a user-defined pointer that is passed along to
3146 * the <code>progress</code> function in order to
3147 * pass along some user defined data to the progress
3148 * updates. If not used, set this to NULL.
mopoke96143402006-10-30 04:37:26 +00003149 * @return 0 if the transfer was successful, any other value means
Linus Walleijdcde6082006-02-17 16:16:34 +00003150 * failure.
3151 * @see LIBMTP_Get_Track_To_File()
3152 */
mopoke96143402006-10-30 04:37:26 +00003153int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
3154 uint32_t const id,
3155 int const fd,
Linus Walleijee73ef22006-08-27 19:56:00 +00003156 LIBMTP_progressfunc_t const callback,
Linus Walleij0cd85432006-02-20 14:37:50 +00003157 void const * const data)
Linus Walleijdcde6082006-02-17 16:16:34 +00003158{
Linus Walleijf6bc1782006-03-24 15:12:47 +00003159 // This is just a wrapper
3160 return LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
Linus Walleijdcde6082006-02-17 16:16:34 +00003161}
Linus Walleij394bbbe2006-02-22 16:10:53 +00003162
3163/**
3164 * This function sends a track from a local file to an
3165 * MTP device. A filename and a set of metadata must be
3166 * given as input.
3167 * @param device a pointer to the device to send the track to.
3168 * @param path the filename of a local file which will be sent.
3169 * @param metadata a track metadata set to be written along with the file.
3170 * @param callback a progress indicator function or NULL to ignore.
3171 * @param data a user-defined pointer that is passed along to
3172 * the <code>progress</code> function in order to
3173 * pass along some user defined data to the progress
3174 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00003175 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003176 * in. Since some devices are a bit picky about where files
3177 * are placed, a default folder will be chosen if libmtp
3178 * has detected one for the current filetype and this
3179 * parameter is set to 0. If this is 0 and no default folder
3180 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003181 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00003182 * failure.
3183 * @see LIBMTP_Send_Track_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003184 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00003185 */
mopoke96143402006-10-30 04:37:26 +00003186int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00003187 char const * const path, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003188 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003189 void const * const data, uint32_t const parenthandle)
Linus Walleij394bbbe2006-02-22 16:10:53 +00003190{
3191 int fd;
3192 int ret;
3193
3194 // Sanity check
3195 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003196 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 +00003197 return -1;
3198 }
3199
3200 // Open file
3201#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00003202#ifdef USE_WINDOWS_IO_H
3203 if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1 ) {
3204#else
Linus Walleij394bbbe2006-02-22 16:10:53 +00003205 if ( (fd = open(path, O_RDONLY|O_BINARY) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00003206#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00003207#else
3208 if ( (fd = open(path, O_RDONLY)) == -1) {
3209#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00003210 printf("LIBMTP_Send_Track_From_File(): Could not open source file \"%s\"\n", path);
Linus Walleij394bbbe2006-02-22 16:10:53 +00003211 return -1;
3212 }
3213
Linus Walleijd6a49972006-05-02 08:24:54 +00003214 ret = LIBMTP_Send_Track_From_File_Descriptor(device, fd, metadata, callback, data, parenthandle);
mopoke96143402006-10-30 04:37:26 +00003215
Linus Walleij394bbbe2006-02-22 16:10:53 +00003216 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00003217#ifdef USE_WINDOWS_IO_H
3218 _close(fd);
3219#else
Linus Walleij394bbbe2006-02-22 16:10:53 +00003220 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00003221#endif
Linus Walleij394bbbe2006-02-22 16:10:53 +00003222
3223 return ret;
3224}
3225
Linus Walleijfa1374c2006-02-27 07:41:46 +00003226
Linus Walleije7df6532007-03-23 08:20:06 +00003227static MTPPropList *new_mtp_prop_entry()
Linus Walleij99310d42006-11-01 08:29:39 +00003228{
3229 MTPPropList *prop;
3230 prop = (MTPPropList *) malloc(sizeof(MTPPropList));
3231 prop->property = PTP_OPC_StorageID; /* Should be "unknown" */
3232 prop->datatype = PTP_DTC_UNDEF;
Richard Low6c0a6ce2006-11-26 10:42:08 +00003233 prop->ObjectHandle = 0x00000000U;
Linus Walleij99310d42006-11-01 08:29:39 +00003234 prop->next = NULL;
3235 return prop;
3236}
3237
Linus Walleije7df6532007-03-23 08:20:06 +00003238static MTPPropList *add_mtp_prop_to_proplist(MTPPropList *proplist, MTPPropList *prop)
Linus Walleij99310d42006-11-01 08:29:39 +00003239{
Linus Walleije7df6532007-03-23 08:20:06 +00003240 if (proplist == NULL) {
3241 return prop;
3242 } else {
3243 MTPPropList *tmp = proplist;
3244 while (tmp->next != NULL) {
3245 tmp = tmp->next;
3246 }
3247 tmp->next = prop;
Linus Walleij99310d42006-11-01 08:29:39 +00003248 }
Linus Walleije7df6532007-03-23 08:20:06 +00003249 return proplist;
3250}
3251
3252static void destroy_mtp_prop_list(MTPPropList *proplist)
3253{
3254 MTPPropList *tmp = proplist;
3255 while (tmp != NULL) {
3256 MTPPropList *prop = tmp;
3257
3258 tmp = tmp->next;
3259 if (prop->datatype == PTP_DTC_STR) {
3260 free(prop->propval.str);
3261 }
3262 free(prop);
3263 }
Linus Walleij99310d42006-11-01 08:29:39 +00003264}
3265
Linus Walleijfa1374c2006-02-27 07:41:46 +00003266/**
Linus Walleij394bbbe2006-02-22 16:10:53 +00003267 * This function sends a track from a file descriptor to an
3268 * MTP device. A filename and a set of metadata must be
3269 * given as input.
3270 * @param device a pointer to the device to send the track to.
3271 * @param fd the filedescriptor for a local file which will be sent.
3272 * @param metadata a track metadata set to be written along with the file.
3273 * After this call the field <code>item_id</code>
3274 * will contain the new track ID.
3275 * @param callback a progress indicator function or NULL to ignore.
3276 * @param data a user-defined pointer that is passed along to
3277 * the <code>progress</code> function in order to
3278 * pass along some user defined data to the progress
3279 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00003280 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003281 * in. Since some devices are a bit picky about where files
3282 * are placed, a default folder will be chosen if libmtp
3283 * has detected one for the current filetype and this
3284 * parameter is set to 0. If this is 0 and no default folder
3285 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003286 * @return 0 if the transfer was successful, any other value means
Linus Walleij394bbbe2006-02-22 16:10:53 +00003287 * failure.
3288 * @see LIBMTP_Send_Track_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003289 * @see LIBMTP_Delete_Object()
Linus Walleij394bbbe2006-02-22 16:10:53 +00003290 */
mopoke96143402006-10-30 04:37:26 +00003291int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleij394bbbe2006-02-22 16:10:53 +00003292 int const fd, LIBMTP_track_t * const metadata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003293 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003294 void const * const data, uint32_t const parenthandle)
Linus Walleij394bbbe2006-02-22 16:10:53 +00003295{
Linus Walleij394bbbe2006-02-22 16:10:53 +00003296 uint16_t ret;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003297 uint32_t store = get_first_storageid(device);
Linus Walleij17e39f72006-02-23 15:54:28 +00003298 int subcall_ret;
Linus Walleij9b28da32006-03-16 13:47:58 +00003299 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd6a49972006-05-02 08:24:54 +00003300 uint32_t localph = parenthandle;
Linus Walleijd214b9b2006-08-26 22:08:37 +00003301 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
Linus Walleij99310d42006-11-01 08:29:39 +00003302 uint8_t nonconsumable = 0x00U; /* By default it is consumable */
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003303 uint32_t i = 0;
mopoke31364442006-11-20 04:53:04 +00003304
Linus Walleijf5fcda32006-12-03 22:31:02 +00003305 subcall_ret = check_if_file_fits(device, metadata->filesize);
3306 if (subcall_ret != 0) {
3307 return -1;
3308 }
3309
Linus Walleij05ccbe72006-06-13 07:46:58 +00003310 if (localph == 0) {
3311 localph = device->default_music_folder;
3312 }
3313
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003314 // Sanity check: no zerolength files
3315 if (metadata->filesize == 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003316 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File_Descriptor(): File of zero size.");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003317 return -1;
3318 }
3319
Linus Walleij16c51f02006-05-04 13:20:22 +00003320 // Sanity check, is this really a track?
3321 if (metadata->filetype != LIBMTP_FILETYPE_WAV &&
3322 metadata->filetype != LIBMTP_FILETYPE_MP3 &&
Linus Walleij5fb47132006-12-30 15:35:48 +00003323 metadata->filetype != LIBMTP_FILETYPE_MP2 &&
Linus Walleij16c51f02006-05-04 13:20:22 +00003324 metadata->filetype != LIBMTP_FILETYPE_WMA &&
3325 metadata->filetype != LIBMTP_FILETYPE_OGG &&
Linus Walleij5fb47132006-12-30 15:35:48 +00003326 metadata->filetype != LIBMTP_FILETYPE_FLAC &&
3327 metadata->filetype != LIBMTP_FILETYPE_AAC &&
3328 metadata->filetype != LIBMTP_FILETYPE_M4A &&
Linus Walleij16c51f02006-05-04 13:20:22 +00003329 metadata->filetype != LIBMTP_FILETYPE_MP4 &&
3330 metadata->filetype != LIBMTP_FILETYPE_UNDEF_AUDIO) {
3331 printf("LIBMTP_Send_Track_From_File_Descriptor: I don't think this is actually a track, strange filetype...\n");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003332 nonconsumable = 0x01U; /* Not suitable for consumption, atleast it's no track! */
Linus Walleij20698482006-11-24 20:54:21 +00003333 } else if (metadata->filetype == LIBMTP_FILETYPE_UNDEF_AUDIO) {
Linus Walleij99310d42006-11-01 08:29:39 +00003334 nonconsumable = 0x01U; /* Not suitable for consumption */
3335 }
Linus Walleij37253292006-10-11 08:38:14 +00003336
Linus Walleij99310d42006-11-01 08:29:39 +00003337 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
3338 /*
3339 * MTP enhanched does it this way (from a sniff):
3340 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
3341 * 20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
3342 * FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
3343 * Length: 0x00000020
3344 * Type: 0x0001 PTP_USB_CONTAINER_COMMAND
3345 * Code: 0x9808
3346 * Transaction ID: 0x0000001B
3347 * Param1: 0x00010001 <- store
3348 * Param2: 0xffffffff <- parent handle (-1 ?)
3349 * Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
3350 * Param4: 0x00000000 <- file length MSB (-0x0c header len)
3351 * Param5: 0x00005e12 <- file length LSB (-0x0c header len)
3352 *
3353 * -> PTP_OC_MTP_SendObjectPropList (0x9808):
3354 * 46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
3355 * 00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
3356 * 00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
3357 * 00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
3358 * 00 4F DC 02 00 01 - dc4f = non consumable
3359 * Length: 0x00000046
3360 * Type: 0x0002 PTP_USB_CONTAINER_DATA
3361 * Code: 0x9808
3362 * Transaction ID: 0x0000001B
3363 * Metadata....
3364 * 0x00000003 <- Number of metadata items
3365 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
3366 * 0xdc07 <- metadata type: file name
3367 * 0xffff <- metadata type: string
3368 * 0x0d <- number of (uint16_t) characters
3369 * 4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
3370 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
3371 * 0xdc03 <- metadata type: protection status
3372 * 0x0004 <- metadata type: uint16_t
3373 * 0x0000 <- not protected
3374 * 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
3375 * 0xdc4f <- non consumable
3376 * 0x0002 <- metadata type: uint8_t
3377 * 0x01 <- non-consumable (this device cannot display PDF)
3378 *
3379 * <- Read 0x18 bytes back
3380 * 18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
3381 * 00 00 00 00 01 40 00 00
3382 * Length: 0x000000018
3383 * Type: 0x0003 PTP_USB_CONTAINER_RESPONSE
3384 * Code: 0x2001 PTP_OK
3385 * Transaction ID: 0x0000001B
3386 * Param1: 0x00010001 <- store
3387 * Param2: 0x00000000 <- parent handle
3388 * Param3: 0x00004001 <- new file/object ID
3389 *
3390 * -> PTP_OC_SendObject (0x100d)
3391 * 0C 00 00 00 01 00 0D 10 1C 00 00 00
3392 * -> ... all the bytes ...
3393 * <- Read 0x0c bytes back
3394 * 0C 00 00 00 03 00 01 20 1C 00 00 00
3395 * ... Then update metadata one-by one, actually (instead of sending it first!) ...
3396 */
Richard Low4c60f6e2006-11-07 20:36:42 +00003397 MTPPropList *proplist = NULL;
3398 MTPPropList *prop = NULL;
Linus Walleijf8574f02006-12-04 19:42:28 +00003399 uint16_t *props = NULL;
3400 uint32_t propcnt = 0;
Linus Walleij37253292006-10-11 08:38:14 +00003401
rreardon5332f9c2006-12-05 10:18:23 +00003402 /* Send an object property list of that is supported */
mopoke31364442006-11-20 04:53:04 +00003403
Richard Low91354812006-11-17 22:44:05 +00003404 // default handle
3405 if (localph == 0)
3406 localph = 0xFFFFFFFFU; // Set to -1
mopoke31364442006-11-20 04:53:04 +00003407
Linus Walleij99310d42006-11-01 08:29:39 +00003408 metadata->item_id = 0x00000000U;
mopoke31364442006-11-20 04:53:04 +00003409
Richard Low4c60f6e2006-11-07 20:36:42 +00003410 ret = ptp_mtp_getobjectpropssupported (params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00003411
Richard Low4c60f6e2006-11-07 20:36:42 +00003412 if (ret == PTP_RC_OK)
3413 {
3414 for (i=0;i<propcnt;i++) {
3415 switch (props[i]) {
3416 case PTP_OPC_ObjectFileName:
Linus Walleije7df6532007-03-23 08:20:06 +00003417 prop = new_mtp_prop_entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00003418 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00003419 prop->property = PTP_OPC_ObjectFileName;
3420 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00003421 if (metadata->filename != NULL)
3422 prop->propval.str = strdup(metadata->filename);
Linus Walleije7df6532007-03-23 08:20:06 +00003423 proplist = add_mtp_prop_to_proplist(proplist, prop);
Richard Low4c60f6e2006-11-07 20:36:42 +00003424 break;
3425 case PTP_OPC_ProtectionStatus:
Linus Walleije7df6532007-03-23 08:20:06 +00003426 prop = new_mtp_prop_entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00003427 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00003428 prop->property = PTP_OPC_ProtectionStatus;
3429 prop->datatype = PTP_DTC_UINT16;
3430 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleije7df6532007-03-23 08:20:06 +00003431 proplist = add_mtp_prop_to_proplist(proplist, prop);
Richard Low4c60f6e2006-11-07 20:36:42 +00003432 break;
3433 case PTP_OPC_NonConsumable:
Linus Walleije7df6532007-03-23 08:20:06 +00003434 prop = new_mtp_prop_entry();
Richard Low6c0a6ce2006-11-26 10:42:08 +00003435 prop->ObjectHandle = metadata->item_id;
Richard Low4c60f6e2006-11-07 20:36:42 +00003436 prop->property = PTP_OPC_NonConsumable;
3437 prop->datatype = PTP_DTC_UINT8;
3438 prop->propval.u8 = nonconsumable;
Linus Walleije7df6532007-03-23 08:20:06 +00003439 proplist = add_mtp_prop_to_proplist(proplist, prop);
Richard Low4c60f6e2006-11-07 20:36:42 +00003440 break;
3441 }
3442 }
Linus Walleij3fcfea52006-11-13 07:07:36 +00003443 free(props);
Richard Low4c60f6e2006-11-07 20:36:42 +00003444 }
3445
mopoke31364442006-11-20 04:53:04 +00003446
Linus Walleij99310d42006-11-01 08:29:39 +00003447 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &metadata->item_id,
3448 map_libmtp_type_to_ptp_type(metadata->filetype),
3449 metadata->filesize, proplist);
3450
3451 /* Free property list */
Linus Walleije7df6532007-03-23 08:20:06 +00003452 destroy_mtp_prop_list(proplist);
Linus Walleij99310d42006-11-01 08:29:39 +00003453
3454 if (ret != PTP_RC_OK) {
Linus Walleije7df6532007-03-23 08:20:06 +00003455 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: "
3456 "Could not send object property list.");
Linus Walleij99310d42006-11-01 08:29:39 +00003457 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003458 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00003459 }
3460 return -1;
3461 }
3462 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
Linus Walleij99310d42006-11-01 08:29:39 +00003463 PTPObjectInfo new_track;
Richard Lowab0d22d2006-11-30 22:17:49 +00003464
3465 memset(&new_track, 0, sizeof(PTPObjectInfo));
Linus Walleij99310d42006-11-01 08:29:39 +00003466
3467 /* Else use the fallback compatibility mode */
3468 new_track.Filename = metadata->filename;
3469 new_track.ObjectCompressedSize = metadata->filesize;
3470 new_track.ObjectFormat = map_libmtp_type_to_ptp_type(metadata->filetype);
Richard Lowaf20b5d2006-12-17 18:00:59 +00003471 new_track.StorageID = store;
3472 new_track.ParentObject = parenthandle;
Richard Low021421e2007-01-01 18:08:57 +00003473
Linus Walleij99310d42006-11-01 08:29:39 +00003474 // Create the object
3475 ret = ptp_sendobjectinfo(params, &store, &localph, &metadata->item_id, &new_track);
Richard Low021421e2007-01-01 18:08:57 +00003476
Linus Walleij99310d42006-11-01 08:29:39 +00003477 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003478 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: Could not send object info.");
Linus Walleij99310d42006-11-01 08:29:39 +00003479 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003480 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00003481 }
3482 return -1;
3483 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00003484
3485 // NOTE: The char* pointers inside the new_track object info are not copies
3486 // so don't try to destroy this object info!
Linus Walleij394bbbe2006-02-22 16:10:53 +00003487 }
3488
Linus Walleijee73ef22006-08-27 19:56:00 +00003489 // Callbacks
Linus Walleijd214b9b2006-08-26 22:08:37 +00003490 ptp_usb->callback_active = 1;
Linus Walleij7f0c72e2006-09-06 13:01:58 +00003491 // The callback will deactivate itself after this amount of data has been sent
3492 // One BULK header for the request, one for the data phase. No parameters to the request.
3493 ptp_usb->current_transfer_total = metadata->filesize+PTP_USB_BULK_HDR_LEN*2;
Linus Walleijd214b9b2006-08-26 22:08:37 +00003494 ptp_usb->current_transfer_complete = 0;
3495 ptp_usb->current_transfer_callback = callback;
3496 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00003497
Linus Walleije7f44be2006-08-25 19:32:29 +00003498 ret = ptp_sendobject_fromfd(params, fd, metadata->filesize);
mopoke96143402006-10-30 04:37:26 +00003499
Linus Walleijee73ef22006-08-27 19:56:00 +00003500 ptp_usb->callback_active = 0;
3501 ptp_usb->current_transfer_callback = NULL;
3502 ptp_usb->current_transfer_callback_data = NULL;
3503
Linus Walleije7f44be2006-08-25 19:32:29 +00003504 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003505 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Track_From_File_Descriptor: Could not send object.");
Linus Walleij394bbbe2006-02-22 16:10:53 +00003506 return -1;
3507 }
mopoke96143402006-10-30 04:37:26 +00003508
Linus Walleij17e39f72006-02-23 15:54:28 +00003509 // Set track metadata for the new fine track
3510 subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
3511 if (subcall_ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003512 // Subcall will add error to errorstack
Linus Walleijb2753182007-02-26 08:11:38 +00003513 // We used to delete the file here, but don't... It might be OK after all.
3514 // (void) LIBMTP_Delete_Object(device, metadata->item_id);
Linus Walleij17e39f72006-02-23 15:54:28 +00003515 return -1;
3516 }
Linus Walleij99310d42006-11-01 08:29:39 +00003517 if (nonconsumable != 0x00U) {
3518 /* Flag it as non-consumable if it is */
Linus Walleij9901e222006-11-30 12:28:19 +00003519 subcall_ret = set_object_u8(device, metadata->item_id, PTP_OPC_NonConsumable, nonconsumable);
Linus Walleij99310d42006-11-01 08:29:39 +00003520 if (subcall_ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003521 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): could not set non-consumable status.");
Linus Walleij99310d42006-11-01 08:29:39 +00003522 return -1;
mopoke31364442006-11-20 04:53:04 +00003523 }
Linus Walleij99310d42006-11-01 08:29:39 +00003524 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00003525
tsaarnia3eb60a2007-07-06 17:41:30 +00003526 add_object_to_cache(device, metadata->item_id);
mopoke96143402006-10-30 04:37:26 +00003527
Linus Walleij17e39f72006-02-23 15:54:28 +00003528 return 0;
3529}
3530
Linus Walleijd6a49972006-05-02 08:24:54 +00003531/**
mopoke96143402006-10-30 04:37:26 +00003532 * This function sends a local file to an MTP device.
Linus Walleijd6a49972006-05-02 08:24:54 +00003533 * A filename and a set of metadata must be
3534 * given as input.
3535 * @param device a pointer to the device to send the track to.
3536 * @param path the filename of a local file which will be sent.
3537 * @param filedata a file strtuct to pass in info about the file.
3538 * After this call the field <code>item_id</code>
3539 * will contain the new file ID.
3540 * @param callback a progress indicator function or NULL to ignore.
3541 * @param data a user-defined pointer that is passed along to
3542 * the <code>progress</code> function in order to
3543 * pass along some user defined data to the progress
3544 * updates. If not used, set this to NULL.
3545 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003546 * in. Since some devices are a bit picky about where files
3547 * are placed, a default folder will be chosen if libmtp
3548 * has detected one for the current filetype and this
3549 * parameter is set to 0. If this is 0 and no default folder
3550 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003551 * @return 0 if the transfer was successful, any other value means
Linus Walleijd6a49972006-05-02 08:24:54 +00003552 * failure.
3553 * @see LIBMTP_Send_File_From_File_Descriptor()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003554 * @see LIBMTP_Delete_Object()
Linus Walleijd6a49972006-05-02 08:24:54 +00003555 */
mopoke96143402006-10-30 04:37:26 +00003556int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
Linus Walleijd6a49972006-05-02 08:24:54 +00003557 char const * const path, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003558 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003559 void const * const data, uint32_t const parenthandle)
3560{
3561 int fd;
3562 int ret;
3563
3564 // Sanity check
3565 if (path == NULL) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003566 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 +00003567 return -1;
3568 }
3569
3570 // Open file
3571#ifdef __WIN32__
Linus Walleij7beba572006-11-29 08:56:12 +00003572#ifdef USE_WINDOWS_IO_H
3573 if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1 ) {
3574#else
Linus Walleijd6a49972006-05-02 08:24:54 +00003575 if ( (fd = open(path, O_RDONLY|O_BINARY) == -1 ) {
Linus Walleij7beba572006-11-29 08:56:12 +00003576#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00003577#else
3578 if ( (fd = open(path, O_RDONLY)) == -1) {
3579#endif
Linus Walleij070e9b42007-01-22 08:49:28 +00003580 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 +00003581 return -1;
3582 }
3583
3584 ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data, parenthandle);
mopoke96143402006-10-30 04:37:26 +00003585
Linus Walleijd6a49972006-05-02 08:24:54 +00003586 // Close file.
Linus Walleij7beba572006-11-29 08:56:12 +00003587#ifdef USE_WINDOWS_IO_H
3588 _close(fd);
3589#else
Linus Walleijd6a49972006-05-02 08:24:54 +00003590 close(fd);
Linus Walleij7beba572006-11-29 08:56:12 +00003591#endif
Linus Walleijd6a49972006-05-02 08:24:54 +00003592
3593 return ret;
3594}
3595
Linus Walleijd208f9c2006-04-27 14:16:06 +00003596/**
3597 * This function sends a generic file from a file descriptor to an
3598 * MTP device. A filename and a set of metadata must be
3599 * given as input.
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003600 *
3601 * This can potentially be used for sending in a stream of unknown
3602 * length. Set <code>filedata->filesize = (uint64_t) -1</code> to
3603 * make libmtp send some dummy length to the device and just
3604 * accept a stream up to some device-determined max length. There
3605 * is not guarantee this will work on all devices... Remember to
3606 * set correct metadata for the track with
3607 * <code>LIBMTP_Update_Track_Metadata()</code> afterwards if it's
3608 * a music file. (This doesn't seem to work very well right now.)
3609 *
Linus Walleijd208f9c2006-04-27 14:16:06 +00003610 * @param device a pointer to the device to send the file to.
3611 * @param fd the filedescriptor for a local file which will be sent.
Linus Walleijd6a49972006-05-02 08:24:54 +00003612 * @param filedata a file strtuct to pass in info about the file.
Linus Walleijd208f9c2006-04-27 14:16:06 +00003613 * After this call the field <code>item_id</code>
3614 * will contain the new track ID.
3615 * @param callback a progress indicator function or NULL to ignore.
3616 * @param data a user-defined pointer that is passed along to
3617 * the <code>progress</code> function in order to
3618 * pass along some user defined data to the progress
3619 * updates. If not used, set this to NULL.
Linus Walleijd6a49972006-05-02 08:24:54 +00003620 * @param parenthandle the parent (e.g. folder) to store this file
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003621 * in. Since some devices are a bit picky about where files
3622 * are placed, a default folder will be chosen if libmtp
3623 * has detected one for the current filetype and this
3624 * parameter is set to 0. If this is 0 and no default folder
3625 * can be found, the file will be stored in the root folder.
mopoke96143402006-10-30 04:37:26 +00003626 * @return 0 if the transfer was successful, any other value means
Linus Walleijd208f9c2006-04-27 14:16:06 +00003627 * failure.
Linus Walleijd6a49972006-05-02 08:24:54 +00003628 * @see LIBMTP_Send_File_From_File()
Linus Walleij438bd7f2006-06-08 11:35:44 +00003629 * @see LIBMTP_Delete_Object()
Linus Walleijd208f9c2006-04-27 14:16:06 +00003630 */
mopoke96143402006-10-30 04:37:26 +00003631int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
Linus Walleijd208f9c2006-04-27 14:16:06 +00003632 int const fd, LIBMTP_file_t * const filedata,
Linus Walleijee73ef22006-08-27 19:56:00 +00003633 LIBMTP_progressfunc_t const callback,
Linus Walleijd6a49972006-05-02 08:24:54 +00003634 void const * const data, uint32_t const parenthandle)
Linus Walleijd208f9c2006-04-27 14:16:06 +00003635{
3636 uint16_t ret;
Linus Walleij9e1b0812006-12-12 19:22:02 +00003637 uint32_t store = get_first_storageid(device);
Linus Walleijd6a49972006-05-02 08:24:54 +00003638 uint32_t localph = parenthandle;
Linus Walleijd208f9c2006-04-27 14:16:06 +00003639 PTPObjectInfo new_file;
3640 PTPParams *params = (PTPParams *) device->params;
Linus Walleijd214b9b2006-08-26 22:08:37 +00003641 PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
rreardonbbb4e562006-11-19 22:16:11 +00003642 int i;
Linus Walleijf5fcda32006-12-03 22:31:02 +00003643 int subcall_ret;
rreardonbbb4e562006-11-19 22:16:11 +00003644 uint16_t *props = NULL;
3645 uint32_t propcnt = 0;
Linus Walleij7430b1b2006-11-21 09:25:55 +00003646 uint8_t nonconsumable = 0x01U; /* By default it is non-consumable */
Richard Lowab0d22d2006-11-30 22:17:49 +00003647
Linus Walleijf5fcda32006-12-03 22:31:02 +00003648 subcall_ret = check_if_file_fits(device, filedata->filesize);
3649 if (subcall_ret != 0) {
3650 return -1;
3651 }
3652
Richard Lowab0d22d2006-11-30 22:17:49 +00003653 memset(&new_file, 0, sizeof(PTPObjectInfo));
Linus Walleij05ccbe72006-06-13 07:46:58 +00003654
Linus Walleijd208f9c2006-04-27 14:16:06 +00003655 new_file.Filename = filedata->filename;
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003656 if (filedata->filesize == (uint64_t) -1) {
3657 // This is a stream. Set a dummy length...
3658 new_file.ObjectCompressedSize = 1;
3659 } else {
Linus Walleij20698482006-11-24 20:54:21 +00003660 // Sanity check: no zerolength files
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003661 if (filedata->filesize == 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003662 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File_Descriptor(): File of zero size.");
Linus Walleij8ae78bb2006-11-20 21:45:52 +00003663 return -1;
3664 }
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003665 new_file.ObjectCompressedSize = filedata->filesize;
3666 }
Linus Walleij16c51f02006-05-04 13:20:22 +00003667 new_file.ObjectFormat = map_libmtp_type_to_ptp_type(filedata->filetype);
Linus Walleijd208f9c2006-04-27 14:16:06 +00003668
mopoke96143402006-10-30 04:37:26 +00003669 /*
Linus Walleij7430b1b2006-11-21 09:25:55 +00003670 * If this file is among the supported filetypes for this device,
3671 * then it is indeed consumable.
3672 */
3673 for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
3674 if (params->deviceinfo.ImageFormats[i] == new_file.ObjectFormat) {
3675 nonconsumable = 0x00U;
3676 break;
3677 }
3678 }
3679
3680 /*
Linus Walleij05ccbe72006-06-13 07:46:58 +00003681 * If no destination folder was given, look up a default
3682 * folder if possible. Perhaps there is some way of retrieveing
3683 * the default folder for different forms of content, what
3684 * do I know, we use a fixed list in lack of any better method.
3685 * Some devices obviously need to have their files in certain
mopoke96143402006-10-30 04:37:26 +00003686 * folders in order to find/display them at all (hello Creative),
Linus Walleij05ccbe72006-06-13 07:46:58 +00003687 * so we have to have a method for this.
3688 */
3689
3690 if (localph == 0) {
3691 uint16_t of = new_file.ObjectFormat;
3692 if (of == PTP_OFC_WAV ||
3693 of == PTP_OFC_MP3 ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003694 of == PTP_OFC_MTP_MP2 ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003695 of == PTP_OFC_MTP_WMA ||
3696 of == PTP_OFC_MTP_OGG ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003697 of == PTP_OFC_MTP_FLAC ||
3698 of == PTP_OFC_MTP_AAC ||
3699 of == PTP_OFC_MTP_M4A ||
3700 of == PTP_OFC_AIFF ||
3701 //of == PTP_OFC_MTP_MP4 || /* ambiguous mp4 can contain video */
3702 of == PTP_OFC_MTP_AudibleCodec ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003703 of == PTP_OFC_MTP_UndefinedAudio) {
3704 localph = device->default_music_folder;
3705 } else if (of == PTP_OFC_MTP_WMV ||
3706 of == PTP_OFC_AVI ||
3707 of == PTP_OFC_MPEG ||
3708 of == PTP_OFC_ASF ||
3709 of == PTP_OFC_QT ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003710 of == PTP_OFC_MTP_3GP ||
3711 of == PTP_OFC_MTP_MP4 || /* ambiguous mp4 can also contain only audio */
Linus Walleij05ccbe72006-06-13 07:46:58 +00003712 of == PTP_OFC_MTP_UndefinedVideo) {
3713 localph = device->default_video_folder;
3714 } else if (of == PTP_OFC_EXIF_JPEG ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003715 of == PTP_OFC_JP2 ||
3716 of == PTP_OFC_JPX ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003717 of == PTP_OFC_JFIF ||
3718 of == PTP_OFC_TIFF ||
Linus Walleij5fb47132006-12-30 15:35:48 +00003719 of == PTP_OFC_TIFF_IT ||
Linus Walleij05ccbe72006-06-13 07:46:58 +00003720 of == PTP_OFC_BMP ||
3721 of == PTP_OFC_GIF ||
3722 of == PTP_OFC_PICT ||
3723 of == PTP_OFC_PNG ||
3724 of == PTP_OFC_MTP_WindowsImageFormat) {
3725 localph = device->default_picture_folder;
3726 } else if (of == PTP_OFC_MTP_vCalendar1 ||
Linus Walleijd7aa5b22006-09-02 11:52:31 +00003727 of == PTP_OFC_MTP_vCalendar2 ||
3728 of == PTP_OFC_MTP_UndefinedContact ||
3729 of == PTP_OFC_MTP_vCard2 ||
3730 of == PTP_OFC_MTP_vCard3 ||
3731 of == PTP_OFC_MTP_UndefinedCalendarItem) {
Linus Walleij05ccbe72006-06-13 07:46:58 +00003732 localph = device->default_organizer_folder;
Linus Walleij5fb47132006-12-30 15:35:48 +00003733 } else if (of == PTP_OFC_Text
3734 ) {
Linus Walleij9316e652006-12-07 09:55:21 +00003735 localph = device->default_text_folder;
Linus Walleij05ccbe72006-06-13 07:46:58 +00003736 }
3737 }
mopoke31364442006-11-20 04:53:04 +00003738
rreardonbbb4e562006-11-19 22:16:11 +00003739 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
mopoke31364442006-11-20 04:53:04 +00003740
rreardonbbb4e562006-11-19 22:16:11 +00003741 MTPPropList *proplist = NULL;
3742 MTPPropList *prop = NULL;
Richard Low6c0a6ce2006-11-26 10:42:08 +00003743
3744 // Must be 0x00000000U for new objects
3745 filedata->item_id = 0x00000000U;
Linus Walleij05ccbe72006-06-13 07:46:58 +00003746
rreardonbbb4e562006-11-19 22:16:11 +00003747 ret = ptp_mtp_getobjectpropssupported(params, new_file.ObjectFormat, &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00003748
rreardonbbb4e562006-11-19 22:16:11 +00003749 for (i=0;i<propcnt;i++) {
3750 switch (props[i]) {
3751 case PTP_OPC_ObjectFileName:
Linus Walleije7df6532007-03-23 08:20:06 +00003752 prop = new_mtp_prop_entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003753 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003754 prop->property = PTP_OPC_ObjectFileName;
3755 prop->datatype = PTP_DTC_STR;
3756 prop->propval.str = strdup(new_file.Filename);
Linus Walleije7df6532007-03-23 08:20:06 +00003757 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardonbbb4e562006-11-19 22:16:11 +00003758 break;
3759 case PTP_OPC_ProtectionStatus:
Linus Walleije7df6532007-03-23 08:20:06 +00003760 prop = new_mtp_prop_entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003761 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003762 prop->property = PTP_OPC_ProtectionStatus;
3763 prop->datatype = PTP_DTC_UINT16;
3764 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleije7df6532007-03-23 08:20:06 +00003765 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardonbbb4e562006-11-19 22:16:11 +00003766 break;
3767 case PTP_OPC_NonConsumable:
Linus Walleije7df6532007-03-23 08:20:06 +00003768 prop = new_mtp_prop_entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003769 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003770 prop->property = PTP_OPC_NonConsumable;
3771 prop->datatype = PTP_DTC_UINT8;
3772 prop->propval.u8 = nonconsumable;
Linus Walleije7df6532007-03-23 08:20:06 +00003773 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardonbbb4e562006-11-19 22:16:11 +00003774 break;
3775 case PTP_OPC_Name:
Linus Walleije7df6532007-03-23 08:20:06 +00003776 prop = new_mtp_prop_entry();
Linus Walleij070e9b42007-01-22 08:49:28 +00003777 prop->ObjectHandle = filedata->item_id;
rreardonbbb4e562006-11-19 22:16:11 +00003778 prop->property = PTP_OPC_Name;
3779 prop->datatype = PTP_DTC_STR;
3780 prop->propval.str = strdup(filedata->filename);
Linus Walleije7df6532007-03-23 08:20:06 +00003781 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardonbbb4e562006-11-19 22:16:11 +00003782 break;
3783 }
Linus Walleij99310d42006-11-01 08:29:39 +00003784 }
rreardonbbb4e562006-11-19 22:16:11 +00003785 free(props);
mopoke31364442006-11-20 04:53:04 +00003786
Linus Walleij9be685b2006-11-21 09:44:53 +00003787 // default handle
3788 if (localph == 0)
3789 localph = 0xFFFFFFFFU; // Set to -1
3790
rreardonbbb4e562006-11-19 22:16:11 +00003791 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id,
3792 new_file.ObjectFormat,
3793 new_file.ObjectCompressedSize, proplist);
mopoke31364442006-11-20 04:53:04 +00003794
rreardonbbb4e562006-11-19 22:16:11 +00003795 /* Free property list */
Linus Walleije7df6532007-03-23 08:20:06 +00003796 destroy_mtp_prop_list(proplist);
mopoke31364442006-11-20 04:53:04 +00003797
rreardonbbb4e562006-11-19 22:16:11 +00003798 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003799 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File(): Could not send object property list.");
rreardonbbb4e562006-11-19 22:16:11 +00003800 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003801 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardonbbb4e562006-11-19 22:16:11 +00003802 }
3803 return -1;
3804 }
3805 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
Linus Walleij9be685b2006-11-21 09:44:53 +00003806 // Create the object
3807 ret = ptp_sendobjectinfo(params, &store, &localph, &filedata->item_id, &new_file);
3808 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003809 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Could not send object info.");
Linus Walleij9be685b2006-11-21 09:44:53 +00003810 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003811 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij9be685b2006-11-21 09:44:53 +00003812 }
3813 return -1;
3814 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00003815 // NOTE: the char* pointers inside new_file are not copies so don't
3816 // try to destroy this objectinfo!
rreardonbbb4e562006-11-19 22:16:11 +00003817 }
Richard Low021421e2007-01-01 18:08:57 +00003818
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003819 if (filedata->filesize != (uint64_t) -1) {
3820 // Callbacks
3821 ptp_usb->callback_active = 1;
Linus Walleij7f0c72e2006-09-06 13:01:58 +00003822 // The callback will deactivate itself after this amount of data has been sent
3823 // One BULK header for the request, one for the data phase. No parameters to the request.
3824 ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003825 ptp_usb->current_transfer_complete = 0;
3826 ptp_usb->current_transfer_callback = callback;
3827 ptp_usb->current_transfer_callback_data = data;
mopoke96143402006-10-30 04:37:26 +00003828
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003829 ret = ptp_sendobject_fromfd(params, fd, filedata->filesize);
mopoke96143402006-10-30 04:37:26 +00003830
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003831 ptp_usb->callback_active = 0;
3832 ptp_usb->current_transfer_callback = NULL;
3833 ptp_usb->current_transfer_callback_data = NULL;
3834 } else {
3835 // This is a stream..
Linus Walleija9310fa2006-09-04 06:47:42 +00003836 ret = ptp_sendobject_fromfd(params, fd, 0xFFFFFFFFU-PTP_USB_BULK_HDR_LEN);
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003837 if (ret == PTP_ERROR_IO) {
3838 // That's expected. The stream ends, simply...
3839 ret = PTP_RC_OK;
3840 } else {
Linus Walleij070e9b42007-01-22 08:49:28 +00003841 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Error while sending stream.");
Linus Walleijcd3eb3d2006-09-02 21:33:22 +00003842 }
3843 }
Linus Walleijee73ef22006-08-27 19:56:00 +00003844
3845 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00003846 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor: Could not send object.");
Linus Walleijee73ef22006-08-27 19:56:00 +00003847 return -1;
3848 }
mopoke96143402006-10-30 04:37:26 +00003849
tsaarnia3eb60a2007-07-06 17:41:30 +00003850 add_object_to_cache(device, filedata->item_id);
3851
Linus Walleijd208f9c2006-04-27 14:16:06 +00003852 return 0;
3853}
3854
Linus Walleij17e39f72006-02-23 15:54:28 +00003855/**
3856 * This function updates the MTP object metadata on a single file
3857 * identified by an object ID.
mopoke96143402006-10-30 04:37:26 +00003858 * @param device a pointer to the device to update the track
Linus Walleij95698cd2006-02-24 10:40:40 +00003859 * metadata on.
Linus Walleij17e39f72006-02-23 15:54:28 +00003860 * @param metadata a track metadata set to be written to the file.
3861 * notice that the <code>track_id</code> field of the
3862 * metadata structure must be correct so that the
Linus Walleija4982732006-02-24 15:46:02 +00003863 * function can update the right file. If some properties
3864 * of this metadata are set to NULL (strings) or 0
3865 * (numerical values) they will be discarded and the
3866 * track will not be tagged with these blank values.
Richard Lowaf20b5d2006-12-17 18:00:59 +00003867 * @return 0 on success, any other value means failure. If some
3868 * or all of the properties fail to update we will still
3869 * return success. On some devices (notably iRiver T30)
3870 * properties that exist cannot be updated.
Linus Walleij17e39f72006-02-23 15:54:28 +00003871 */
mopoke96143402006-10-30 04:37:26 +00003872int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *device,
Linus Walleij17e39f72006-02-23 15:54:28 +00003873 LIBMTP_track_t const * const metadata)
3874{
Linus Walleij17e39f72006-02-23 15:54:28 +00003875 uint16_t ret;
Linus Walleij00cf0642006-07-26 20:40:59 +00003876 PTPParams *params = (PTPParams *) device->params;
3877 uint32_t i;
3878 uint16_t *props = NULL;
3879 uint32_t propcnt = 0;
Linus Walleij17e39f72006-02-23 15:54:28 +00003880
mopoke96143402006-10-30 04:37:26 +00003881 // First see which properties can be set on this file format and apply accordingly
Linus Walleij00cf0642006-07-26 20:40:59 +00003882 // i.e only try to update this metadata for object tags that exist on the current player.
Linus Walleij304433d2007-02-26 08:02:47 +00003883 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &props);
Linus Walleij00cf0642006-07-26 20:40:59 +00003884 if (ret != PTP_RC_OK) {
3885 // Just bail out for now, nothing is ever set.
Linus Walleije7df6532007-03-23 08:20:06 +00003886 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3887 "could not retrieve supported object properties.");
raveloxd9a28642006-05-26 23:42:22 +00003888 return -1;
Richard Low15731fe2007-03-22 20:27:20 +00003889 }
3890 if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList)) {
3891 MTPPropList *proplist = NULL;
3892 MTPPropList *prop = NULL;
Richard Low15731fe2007-03-22 20:27:20 +00003893 PTPObjectPropDesc opd;
3894
3895 for (i=0;i<propcnt;i++) {
3896 switch (props[i]) {
Linus Walleije7df6532007-03-23 08:20:06 +00003897 case PTP_OPC_Name:
3898 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Name, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3899 if (ret != PTP_RC_OK) {
3900 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3901 "could not get property description for PTP_OPC_Name.");
3902 break;
3903 }
3904
3905 if (opd.GetSet) {
3906 prop = new_mtp_prop_entry();
3907 prop->ObjectHandle = metadata->item_id;
3908 prop->property = PTP_OPC_Name;
3909 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00003910 if (metadata->title != NULL)
3911 prop->propval.str = strdup(metadata->title);
Linus Walleije7df6532007-03-23 08:20:06 +00003912 proplist = add_mtp_prop_to_proplist(proplist, prop);
3913 }
3914 ptp_free_objectpropdesc(&opd);
3915
3916 break;
3917 case PTP_OPC_AlbumName:
3918 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_AlbumName, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3919 if (ret != PTP_RC_OK) {
3920 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3921 "could not get property description for PTP_OPC_AlbumName.");
3922 break;
3923 }
3924
3925 if (opd.GetSet) {
3926 prop = new_mtp_prop_entry();
3927 prop->ObjectHandle = metadata->item_id;
3928 prop->property = PTP_OPC_AlbumName;
3929 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00003930 if (metadata->album != NULL)
3931 prop->propval.str = strdup(metadata->album);
Linus Walleije7df6532007-03-23 08:20:06 +00003932 proplist = add_mtp_prop_to_proplist(proplist, prop);
3933 }
3934 ptp_free_objectpropdesc(&opd);
3935
3936 break;
3937 case PTP_OPC_Artist:
3938 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Artist, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3939 if (ret != PTP_RC_OK) {
3940 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3941 "could not get property description for PTP_OPC_Artist.");
3942 break;
3943 }
3944
3945 if (opd.GetSet) {
3946 prop = new_mtp_prop_entry();
3947 prop->ObjectHandle = metadata->item_id;
3948 prop->property = PTP_OPC_Artist;
3949 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00003950 if (metadata->artist != NULL)
3951 prop->propval.str = strdup(metadata->artist);
Linus Walleije7df6532007-03-23 08:20:06 +00003952 proplist = add_mtp_prop_to_proplist(proplist, prop);
3953 }
3954 ptp_free_objectpropdesc(&opd);
3955
3956 break;
3957 case PTP_OPC_Genre:
3958 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Genre, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3959 if (ret != PTP_RC_OK) {
3960 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3961 "could not get property description for PTP_OPC_Genre.");
3962 break;
3963 }
3964
3965 if (opd.GetSet) {
3966 prop = new_mtp_prop_entry();
3967 prop->ObjectHandle = metadata->item_id;
3968 prop->property = PTP_OPC_Genre;
3969 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00003970 if (metadata->genre != NULL)
3971 prop->propval.str = strdup(metadata->genre);
Linus Walleije7df6532007-03-23 08:20:06 +00003972 proplist = add_mtp_prop_to_proplist(proplist, prop);
3973 }
3974 ptp_free_objectpropdesc(&opd);
3975
3976 break;
3977 case PTP_OPC_Duration:
3978 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Duration, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3979 if (ret != PTP_RC_OK) {
3980 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
3981 "could not get property description for PTP_OPC_Duration.");
3982 break;
3983 }
3984
3985 if (opd.GetSet) {
3986 prop = new_mtp_prop_entry();
3987 prop->ObjectHandle = metadata->item_id;
3988 prop->property = PTP_OPC_Duration;
3989 prop->datatype = PTP_DTC_UINT32;
3990 prop->propval.u32 = metadata->duration;
3991 proplist = add_mtp_prop_to_proplist(proplist, prop);
3992 }
3993 ptp_free_objectpropdesc(&opd);
3994
3995 break;
3996 case PTP_OPC_Track:
3997 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Track, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
3998 if (ret != PTP_RC_OK) {
3999 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4000 "could not get property description for PTP_OPC_Track.");
4001 break;
4002 }
4003
4004 if (opd.GetSet) {
4005 prop = new_mtp_prop_entry();
4006 prop->ObjectHandle = metadata->item_id;
4007 prop->property = PTP_OPC_Track;
4008 prop->datatype = PTP_DTC_UINT16;
4009 prop->propval.u16 = metadata->tracknumber;
4010 proplist = add_mtp_prop_to_proplist(proplist, prop);
4011 }
4012 ptp_free_objectpropdesc(&opd);
4013 break;
4014 case PTP_OPC_OriginalReleaseDate:
4015 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_OriginalReleaseDate, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4016 if (ret != PTP_RC_OK) {
4017 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4018 "could not get property description for PTP_OPC_OriginalReleaseDate.");
4019 break;
4020 }
4021
4022 if (opd.GetSet) {
4023 prop = new_mtp_prop_entry();
4024 prop->ObjectHandle = metadata->item_id;
4025 prop->property = PTP_OPC_OriginalReleaseDate;
4026 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00004027 if (metadata->date != NULL)
4028 prop->propval.str = strdup(metadata->date);
Linus Walleije7df6532007-03-23 08:20:06 +00004029 proplist = add_mtp_prop_to_proplist(proplist, prop);
4030 }
4031 ptp_free_objectpropdesc(&opd);
4032 break;
4033 case PTP_OPC_SampleRate:
4034 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_SampleRate, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4035 if (ret != PTP_RC_OK) {
4036 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4037 "could not get property description for PTP_OPC_SampleRate.");
4038 break;
4039 }
4040
4041 if (opd.GetSet) {
4042 prop = new_mtp_prop_entry();
4043 prop->ObjectHandle = metadata->item_id;
4044 prop->property = PTP_OPC_SampleRate;
4045 prop->datatype = PTP_DTC_UINT32;
4046 prop->propval.u32 = metadata->samplerate;
4047 proplist = add_mtp_prop_to_proplist(proplist, prop);
4048 }
4049 ptp_free_objectpropdesc(&opd);
4050
4051 break;
4052 case PTP_OPC_NumberOfChannels:
4053 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_NumberOfChannels, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4054 if (ret != PTP_RC_OK) {
4055 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4056 "could not get property description for PTP_OPC_NumberOfChannels.");
4057 break;
4058 }
4059
4060 if (opd.GetSet) {
4061 prop = new_mtp_prop_entry();
4062 prop->ObjectHandle = metadata->item_id;
4063 prop->property = PTP_OPC_NumberOfChannels;
4064 prop->datatype = PTP_DTC_UINT16;
4065 prop->propval.u16 = metadata->nochannels;
4066 proplist = add_mtp_prop_to_proplist(proplist, prop);
4067 }
4068 ptp_free_objectpropdesc(&opd);
4069
4070 break;
4071 case PTP_OPC_AudioWAVECodec:
4072 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_AudioWAVECodec, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4073 if (ret != PTP_RC_OK) {
4074 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4075 "could not get property description for PTP_OPC_AudioWAVECodec.");
4076 break;
4077 }
4078
4079 if (opd.GetSet) {
4080 prop = new_mtp_prop_entry();
4081 prop->ObjectHandle = metadata->item_id;
4082 prop->property = PTP_OPC_AudioWAVECodec;
4083 prop->datatype = PTP_DTC_UINT32;
4084 prop->propval.u32 = metadata->wavecodec;
4085 proplist = add_mtp_prop_to_proplist(proplist, prop);
4086 }
4087 ptp_free_objectpropdesc(&opd);
4088
4089 break;
4090 case PTP_OPC_AudioBitRate:
4091 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_AudioBitRate, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4092 if (ret != PTP_RC_OK) {
4093 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4094 "could not get property description for PTP_OPC_AudioBitRate.");
4095 break;
4096 }
4097
4098 if (opd.GetSet) {
4099 prop = new_mtp_prop_entry();
4100 prop->ObjectHandle = metadata->item_id;
4101 prop->property = PTP_OPC_AudioBitRate;
4102 prop->datatype = PTP_DTC_UINT32;
4103 prop->propval.u32 = metadata->bitrate;
4104 proplist = add_mtp_prop_to_proplist(proplist, prop);
4105 }
4106 ptp_free_objectpropdesc(&opd);
4107
4108 break;
4109 case PTP_OPC_BitRateType:
4110 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_BitRateType, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4111 if (ret != PTP_RC_OK) {
4112 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4113 "could not get property description for PTP_OPC_BitRateType.");
4114 break;
4115 }
4116
4117 if (opd.GetSet) {
4118 prop = new_mtp_prop_entry();
4119 prop->ObjectHandle = metadata->item_id;
4120 prop->property = PTP_OPC_BitRateType;
4121 prop->datatype = PTP_DTC_UINT16;
4122 prop->propval.u16 = metadata->bitratetype;
4123 proplist = add_mtp_prop_to_proplist(proplist, prop);
4124 }
4125 ptp_free_objectpropdesc(&opd);
4126
4127 break;
4128 case PTP_OPC_Rating:
4129 // TODO: shall this be set for rating 0?
4130 if (metadata->rating != 0) {
4131 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Rating, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4132 if (ret != PTP_RC_OK) {
4133 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4134 "could not get property description for PTP_OPC_Rating.");
4135 break;
4136 }
Richard Low15731fe2007-03-22 20:27:20 +00004137
Linus Walleije7df6532007-03-23 08:20:06 +00004138 if (opd.GetSet) {
4139 prop = new_mtp_prop_entry();
4140 prop->ObjectHandle = metadata->item_id;
4141 prop->property = PTP_OPC_Rating;
4142 prop->datatype = PTP_DTC_UINT16;
4143 prop->propval.u16 = metadata->rating;
4144 proplist = add_mtp_prop_to_proplist(proplist, prop);
4145 }
4146 ptp_free_objectpropdesc(&opd);
4147 }
4148 break;
4149 case PTP_OPC_UseCount:
4150 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_UseCount, map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
4151 if (ret != PTP_RC_OK) {
4152 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4153 "could not get property description for PTP_OPC_UseCount.");
4154 break;
4155 }
4156
4157 if (opd.GetSet) {
4158 prop = new_mtp_prop_entry();
4159 prop->ObjectHandle = metadata->item_id;
4160 prop->property = PTP_OPC_UseCount;
4161 prop->datatype = PTP_DTC_UINT32;
4162 prop->propval.u32 = metadata->usecount;
4163 proplist = add_mtp_prop_to_proplist(proplist, prop);
4164 }
4165 ptp_free_objectpropdesc(&opd);
4166 break;
Linus Walleij304433d2007-02-26 08:02:47 +00004167 }
Richard Low15731fe2007-03-22 20:27:20 +00004168 }
4169 // NOTE: File size is not updated, this should not change anyway.
4170 // neither will we change the filename.
4171
4172 ret = ptp_mtp_setobjectproplist(params, proplist);
Linus Walleije7df6532007-03-23 08:20:06 +00004173
4174 destroy_mtp_prop_list(proplist);
Richard Low15731fe2007-03-22 20:27:20 +00004175
4176 if (ret != PTP_RC_OK) {
4177 // TODO: return error of which property we couldn't set
Linus Walleije7df6532007-03-23 08:20:06 +00004178 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4179 "could not set object property list.");
Linus Walleij304433d2007-02-26 08:02:47 +00004180 return -1;
4181 }
Richard Low15731fe2007-03-22 20:27:20 +00004182
4183 } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
Linus Walleij00cf0642006-07-26 20:40:59 +00004184 for (i=0;i<propcnt;i++) {
4185 switch (props[i]) {
4186 case PTP_OPC_Name:
Linus Walleije7df6532007-03-23 08:20:06 +00004187 // Update title
4188 ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title);
4189 if (ret != 0) {
4190 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4191 "could not set track title.");
4192 }
4193 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004194 case PTP_OPC_AlbumName:
Linus Walleije7df6532007-03-23 08:20:06 +00004195 // Update album
4196 ret = set_object_string(device, metadata->item_id, PTP_OPC_AlbumName, metadata->album);
4197 if (ret != 0) {
4198 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4199 "could not set track album name.");
4200 }
4201 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004202 case PTP_OPC_Artist:
Linus Walleije7df6532007-03-23 08:20:06 +00004203 // Update artist
4204 ret = set_object_string(device, metadata->item_id, PTP_OPC_Artist, metadata->artist);
4205 if (ret != 0) {
4206 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4207 "could not set track artist name.");
4208 }
4209 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004210 case PTP_OPC_Genre:
Linus Walleije7df6532007-03-23 08:20:06 +00004211 // Update genre
4212 ret = set_object_string(device, metadata->item_id, PTP_OPC_Genre, metadata->genre);
4213 if (ret != 0) {
4214 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4215 "could not set track genre name.");
4216 }
4217 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004218 case PTP_OPC_Duration:
Linus Walleije7df6532007-03-23 08:20:06 +00004219 // Update duration
4220 if (metadata->duration != 0) {
4221 ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, metadata->duration);
4222 if (ret != 0) {
4223 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4224 "could not set track duration.");
4225 }
4226 }
4227 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004228 case PTP_OPC_Track:
Linus Walleije7df6532007-03-23 08:20:06 +00004229 // Update track number.
4230 if (metadata->tracknumber != 0) {
4231 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, metadata->tracknumber);
4232 if (ret != 0) {
4233 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4234 "could not set track tracknumber.");
4235 }
4236 }
4237 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004238 case PTP_OPC_OriginalReleaseDate:
Linus Walleije7df6532007-03-23 08:20:06 +00004239 // Update creation datetime
4240 ret = set_object_string(device, metadata->item_id, PTP_OPC_OriginalReleaseDate, metadata->date);
4241 if (ret != 0) {
4242 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4243 "could not set track release date.");
4244 }
4245 break;
4246 // These are, well not so important.
Linus Walleij00cf0642006-07-26 20:40:59 +00004247 case PTP_OPC_SampleRate:
Linus Walleije7df6532007-03-23 08:20:06 +00004248 // Update sample rate
4249 if (metadata->samplerate != 0) {
4250 ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, metadata->samplerate);
4251 if (ret != 0) {
4252 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4253 "could not set samplerate.");
4254 }
4255 }
4256 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004257 case PTP_OPC_NumberOfChannels:
Linus Walleije7df6532007-03-23 08:20:06 +00004258 // Update number of channels
4259 if (metadata->nochannels != 0) {
4260 ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, metadata->nochannels);
4261 if (ret != 0) {
4262 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4263 "could not set number of channels.");
4264 }
4265 }
4266 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004267 case PTP_OPC_AudioWAVECodec:
Linus Walleije7df6532007-03-23 08:20:06 +00004268 // Update WAVE codec
4269 if (metadata->wavecodec != 0) {
4270 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, metadata->wavecodec);
4271 if (ret != 0) {
4272 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4273 "could not set WAVE codec.");
4274 }
4275 }
4276 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004277 case PTP_OPC_AudioBitRate:
Linus Walleije7df6532007-03-23 08:20:06 +00004278 // Update bitrate
4279 if (metadata->bitrate != 0) {
4280 ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, metadata->bitrate);
4281 if (ret != 0) {
4282 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4283 "could not set bitrate.");
4284 }
4285 }
4286 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004287 case PTP_OPC_BitRateType:
Linus Walleije7df6532007-03-23 08:20:06 +00004288 // Update bitrate type
4289 if (metadata->bitratetype != 0) {
4290 ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, metadata->bitratetype);
4291 if (ret != 0) {
4292 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4293 "could not set bitratetype.");
4294 }
4295 }
4296 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004297 case PTP_OPC_Rating:
Linus Walleije7df6532007-03-23 08:20:06 +00004298 // Update user rating
4299 // TODO: shall this be set for rating 0?
4300 if (metadata->rating != 0) {
4301 ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, metadata->rating);
4302 if (ret != 0) {
4303 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4304 "could not set user rating.");
4305 }
4306 }
4307 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004308 case PTP_OPC_UseCount:
Linus Walleije7df6532007-03-23 08:20:06 +00004309 // Update use count, set even to zero if desired.
4310 ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, metadata->usecount);
4311 if (ret != 0) {
4312 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4313 "could not set use count.");
4314 }
4315 break;
4316
4317 // NOTE: File size is not updated, this should not change anyway.
4318 // neither will we change the filename.
4319 default:
4320 break;
Linus Walleij00cf0642006-07-26 20:40:59 +00004321 }
Linus Walleija4982732006-02-24 15:46:02 +00004322 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00004323 free(props);
Richard Low15731fe2007-03-22 20:27:20 +00004324 } else {
4325 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
4326 "Your device doesn't seem to support any known way of setting metadata.");
4327 return -1;
Linus Walleij17e39f72006-02-23 15:54:28 +00004328 }
Linus Walleije7df6532007-03-23 08:20:06 +00004329
tsaarnia3eb60a2007-07-06 17:41:30 +00004330 // update cached object properties if metadata cache exists
Linus Walleij338ade42007-07-03 20:44:08 +00004331 if (params->proplist != NULL) {
tsaarnia3eb60a2007-07-06 17:41:30 +00004332 update_metadata_cache(device, metadata->item_id);
Linus Walleij338ade42007-07-03 20:44:08 +00004333 }
4334
Linus Walleijf1b02f22006-12-06 09:03:23 +00004335 return 0;
Linus Walleij394bbbe2006-02-22 16:10:53 +00004336}
Linus Walleij95698cd2006-02-24 10:40:40 +00004337
4338/**
Linus Walleij438bd7f2006-06-08 11:35:44 +00004339 * This function deletes a single file, track, playlist or
4340 * any other object off the MTP device,
Linus Walleij95698cd2006-02-24 10:40:40 +00004341 * identified by an object ID.
Linus Walleijf6bc1782006-03-24 15:12:47 +00004342 * @param device a pointer to the device to delete the file or track from.
Linus Walleij95698cd2006-02-24 10:40:40 +00004343 * @param item_id the item to delete.
4344 * @return 0 on success, any other value means failure.
4345 */
mopoke96143402006-10-30 04:37:26 +00004346int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
Linus Walleij438bd7f2006-06-08 11:35:44 +00004347 uint32_t object_id)
Linus Walleij95698cd2006-02-24 10:40:40 +00004348{
Linus Walleij438bd7f2006-06-08 11:35:44 +00004349 uint16_t ret;
4350 PTPParams *params = (PTPParams *) device->params;
tsaarnia3eb60a2007-07-06 17:41:30 +00004351 int i;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004352
4353 ret = ptp_deleteobject(params, object_id, 0);
4354 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004355 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Delete_Object(): could not delete object.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00004356 return -1;
4357 }
tsaarnia3eb60a2007-07-06 17:41:30 +00004358
4359 // remove object from object info cache
4360 for (i = 0; i < params->handles.n; i++) {
4361 if (params->handles.Handler[i] == object_id) {
4362 ptp_free_objectinfo(&params->objectinfo[i]);
4363 memmove(params->handles.Handler+i, params->handles.Handler+i+1,
4364 (params->handles.n-i-1)*sizeof(uint32_t));
4365 memmove(params->objectinfo+i, params->objectinfo+i+1,
4366 (params->handles.n-i-1)*sizeof(PTPObjectInfo));
4367 params->handles.n--;
Linus Walleij2b218852007-07-07 20:16:36 +00004368 params->handles.Handler = realloc(params->handles.Handler, sizeof(uint32_t)*params->handles.n);
4369 params->objectinfo = realloc(params->objectinfo, sizeof(PTPObjectInfo)*params->handles.n);
tsaarnia3eb60a2007-07-06 17:41:30 +00004370 }
4371 }
4372
4373 // delete cached object properties if metadata cache exists
4374 if (params->proplist) {
4375 MTPPropList *prop = params->proplist;
4376 MTPPropList *prev = NULL;
4377 MTPPropList *last_deleted = NULL;
4378 MTPPropList *deleted_props = NULL;
4379
4380 // find the place where properties for this object begin
4381 while (prop != NULL && prop->ObjectHandle != object_id) {
4382 prev = prop;
4383 prop = prop->next;
4384 }
4385
4386 // safeguard in case object is not found
4387 if (!prop) return 0;
4388
4389 // head of properties for the deleted object
4390 deleted_props = prop;
4391
4392 // find the end of properties for this object
4393 while (prop != NULL && prop->ObjectHandle == object_id) {
4394 last_deleted = prop;
4395 prop = prop->next;
4396 }
4397
4398 // the properties to be deleted are split into separate list
4399 prev->next = prop;
4400 last_deleted->next = NULL;
4401
4402 destroy_mtp_prop_list(deleted_props);
4403 }
4404
Linus Walleij438bd7f2006-06-08 11:35:44 +00004405 return 0;
Linus Walleij95698cd2006-02-24 10:40:40 +00004406}
Linus Walleij9c6ca022006-04-21 10:24:15 +00004407
Linus Walleij9c6ca022006-04-21 10:24:15 +00004408/**
4409 * Helper function. This indicates if a track exists on the device
4410 * @param device a pointer to the device to get the track from.
4411 * @param id the track ID of the track to retrieve.
Linus Walleij070e9b42007-01-22 08:49:28 +00004412 * @return TRUE (!=0) if the track exists, FALSE (0) if not
Linus Walleij9c6ca022006-04-21 10:24:15 +00004413 */
4414int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *device,
4415 uint32_t const id)
4416{
Linus Walleij9c6ca022006-04-21 10:24:15 +00004417 PTPParams *params = (PTPParams *) device->params;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004418 uint32_t i;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004419
Linus Walleijf0bf4372007-07-01 21:47:38 +00004420 for (i = 0; i < params->handles.n; i++) {
4421 if (params->handles.Handler[i] == id) {
4422 return -1;
4423 }
Linus Walleij9c6ca022006-04-21 10:24:15 +00004424 }
4425 return 0;
4426}
4427
4428/**
4429 * This creates a new folder structure and allocates memory
4430 * for it. Notice that if you add strings to this structure they
4431 * will be freed by the corresponding <code>LIBMTP_folder_track_t</code>
4432 * operation later, so be careful of using strdup() when assigning
4433 * strings, e.g.:
4434 *
4435 * @return a pointer to the newly allocated folder structure.
4436 * @see LIBMTP_destroy_folder_t()
4437 */
4438LIBMTP_folder_t *LIBMTP_new_folder_t(void)
4439{
4440 LIBMTP_folder_t *new = (LIBMTP_folder_t *) malloc(sizeof(LIBMTP_folder_t));
4441 if (new == NULL) {
4442 return NULL;
4443 }
4444 new->folder_id = 0;
4445 new->parent_id = 0;
4446 new->name = NULL;
4447 new->sibling = NULL;
4448 new->child = NULL;
4449 return new;
4450}
4451
4452/**
Linus Walleij5573def2007-04-23 07:04:18 +00004453 * This recursively deletes the memory for a folder structure.
4454 * This shall typically be called on a top-level folder list to
4455 * detsroy the entire folder tree.
Linus Walleij9c6ca022006-04-21 10:24:15 +00004456 *
4457 * @param folder folder structure to destroy
4458 * @see LIBMTP_new_folder_t()
4459 */
4460void LIBMTP_destroy_folder_t(LIBMTP_folder_t *folder)
4461{
4462
4463 if(folder == NULL) {
4464 return;
4465 }
4466
4467 //Destroy from the bottom up
4468 if(folder->child != NULL) {
4469 LIBMTP_destroy_folder_t(folder->child);
4470 }
4471
4472 if(folder->sibling != NULL) {
4473 LIBMTP_destroy_folder_t(folder->sibling);
4474 }
4475
4476 if(folder->name != NULL) {
4477 free(folder->name);
4478 }
4479
4480 free(folder);
4481}
4482
4483/**
4484 * Helper function. Returns a folder structure for a
4485 * specified id.
4486 *
4487 * @param folderlist list of folders to search
4488 * @id id of folder to look for
4489 * @return a folder or NULL if not found
4490 */
4491LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t *folderlist, uint32_t id)
4492{
4493 LIBMTP_folder_t *ret = NULL;
mopoke96143402006-10-30 04:37:26 +00004494
Linus Walleij9c6ca022006-04-21 10:24:15 +00004495 if(folderlist == NULL) {
4496 return NULL;
4497 }
mopoke96143402006-10-30 04:37:26 +00004498
Linus Walleij9c6ca022006-04-21 10:24:15 +00004499 if(folderlist->folder_id == id) {
4500 return folderlist;
4501 }
mopoke96143402006-10-30 04:37:26 +00004502
Linus Walleij9c6ca022006-04-21 10:24:15 +00004503 if(folderlist->sibling) {
4504 ret = LIBMTP_Find_Folder(folderlist->sibling, id);
4505 }
mopoke96143402006-10-30 04:37:26 +00004506
Linus Walleij9c6ca022006-04-21 10:24:15 +00004507 if(folderlist->child && ret == NULL) {
4508 ret = LIBMTP_Find_Folder(folderlist->child, id);
4509 }
mopoke96143402006-10-30 04:37:26 +00004510
Linus Walleij9c6ca022006-04-21 10:24:15 +00004511 return ret;
4512}
4513
4514/**
4515 * This returns a list of all folders available
4516 * on the current MTP device.
4517 *
4518 * @param device a pointer to the device to get the track listing for.
4519 * @return a list of folders
4520 */
4521LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
4522{
4523 uint32_t i = 0;
4524 LIBMTP_folder_t *retfolders = NULL;
4525 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004526
mopoke96143402006-10-30 04:37:26 +00004527 // Get all the handles if we haven't already done that
Linus Walleij9c6ca022006-04-21 10:24:15 +00004528 if (params->handles.Handler == NULL) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00004529 flush_handles(device);
Linus Walleij9c6ca022006-04-21 10:24:15 +00004530 }
mopoke96143402006-10-30 04:37:26 +00004531
Linus Walleij9c6ca022006-04-21 10:24:15 +00004532 for (i = 0; i < params->handles.n; i++) {
4533 LIBMTP_folder_t *folder;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004534 PTPObjectInfo *oi;
mopoke96143402006-10-30 04:37:26 +00004535
Linus Walleijf0bf4372007-07-01 21:47:38 +00004536 oi = &params->objectinfo[i];
mopoke31364442006-11-20 04:53:04 +00004537
Linus Walleijf0bf4372007-07-01 21:47:38 +00004538 if (oi->ObjectFormat != PTP_OFC_Association) {
4539 continue;
4540 }
4541 folder = LIBMTP_new_folder_t();
4542 folder->folder_id = params->handles.Handler[i];
4543 folder->parent_id = oi->ParentObject;
4544 if (oi->Filename != NULL)
4545 folder->name = (char *)strdup(oi->Filename);
4546 else
4547 folder->name = NULL;
4548
4549 // Work out where to put this new item
4550 if(retfolders == NULL) {
4551 retfolders = folder;
4552 continue;
4553 } else {
4554 LIBMTP_folder_t *parent_folder;
4555 LIBMTP_folder_t *current_folder;
4556
4557 parent_folder = LIBMTP_Find_Folder(retfolders, folder->parent_id);
4558
4559 if(parent_folder == NULL) {
4560 current_folder = retfolders;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004561 } else {
Linus Walleijf0bf4372007-07-01 21:47:38 +00004562 if(parent_folder->child == NULL) {
4563 parent_folder->child = folder;
4564 continue;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004565 } else {
Linus Walleijf0bf4372007-07-01 21:47:38 +00004566 current_folder = parent_folder->child;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004567 }
Linus Walleij9c6ca022006-04-21 10:24:15 +00004568 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004569
4570 while(current_folder->sibling != NULL) {
4571 current_folder=current_folder->sibling;
4572 }
4573
4574 current_folder->sibling = folder;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004575 }
4576 }
4577 return retfolders;
4578}
4579
4580/**
Linus Walleijc86afbd2006-05-04 19:05:24 +00004581 * This create a folder on the current MTP device. The PTP name
4582 * for a folder is "association". The PTP/MTP devices does not
4583 * have an internal "folder" concept really, it contains a flat
4584 * list of all files and some file are "associations" that other
4585 * files and folders may refer to as its "parent".
Linus Walleij9c6ca022006-04-21 10:24:15 +00004586 *
Linus Walleijc86afbd2006-05-04 19:05:24 +00004587 * @param device a pointer to the device to create the folder on.
4588 * @param name the name of the new folder.
4589 * @param parent_id id of parent folder to add the new folder to,
4590 * or 0 to put it in the root directory.
4591 * @return id to new folder or 0 if an error occured
Linus Walleij9c6ca022006-04-21 10:24:15 +00004592 */
4593uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name, uint32_t parent_id)
4594{
4595 PTPParams *params = (PTPParams *) device->params;
4596 uint32_t parenthandle = 0;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004597 uint32_t store = get_first_storageid(device);
Linus Walleij9c6ca022006-04-21 10:24:15 +00004598 PTPObjectInfo new_folder;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004599 uint16_t ret;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004600 uint32_t new_id = 0;
4601
4602 memset(&new_folder, 0, sizeof(new_folder));
4603 new_folder.Filename = name;
4604 new_folder.ObjectCompressedSize = 1;
4605 new_folder.ObjectFormat = PTP_OFC_Association;
4606 new_folder.ParentObject = parent_id;
4607
4608 parenthandle = parent_id;
4609 // Create the object
Linus Walleijf0bf4372007-07-01 21:47:38 +00004610 // FIXME: use send list here if available.
Linus Walleij9c6ca022006-04-21 10:24:15 +00004611 ret = ptp_sendobjectinfo(params, &store, &parenthandle, &new_id, &new_folder);
4612 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004613 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Create_Folder: Could not send object info.");
Linus Walleij99310d42006-11-01 08:29:39 +00004614 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004615 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij99310d42006-11-01 08:29:39 +00004616 }
Linus Walleijc86afbd2006-05-04 19:05:24 +00004617 return 0;
Linus Walleij9c6ca022006-04-21 10:24:15 +00004618 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004619 // NOTE: don't destroy the new_folder objectinfo, because it is statically referencing
4620 // several strings.
tsaarnia3eb60a2007-07-06 17:41:30 +00004621
4622 add_object_to_cache(device, new_id);
4623
Linus Walleij9c6ca022006-04-21 10:24:15 +00004624 return new_id;
4625}
raveloxd9a28642006-05-26 23:42:22 +00004626
Linus Walleij438bd7f2006-06-08 11:35:44 +00004627/**
4628 * This creates a new playlist metadata structure and allocates memory
4629 * for it. Notice that if you add strings to this structure they
4630 * will be freed by the corresponding <code>LIBMTP_destroy_playlist_t</code>
mopoke96143402006-10-30 04:37:26 +00004631 * operation later, so be careful of using strdup() when assigning
Linus Walleij438bd7f2006-06-08 11:35:44 +00004632 * strings, e.g.:
4633 *
4634 * <pre>
4635 * LIBMTP_playlist_t *pl = LIBMTP_new_playlist_t();
4636 * pl->name = strdup(str);
4637 * ....
4638 * LIBMTP_destroy_playlist_t(pl);
4639 * </pre>
4640 *
4641 * @return a pointer to the newly allocated metadata structure.
4642 * @see LIBMTP_destroy_playlist_t()
4643 */
4644LIBMTP_playlist_t *LIBMTP_new_playlist_t(void)
4645{
4646 LIBMTP_playlist_t *new = (LIBMTP_playlist_t *) malloc(sizeof(LIBMTP_playlist_t));
4647 if (new == NULL) {
4648 return NULL;
4649 }
4650 new->playlist_id = 0;
4651 new->name = NULL;
4652 new->tracks = NULL;
4653 new->no_tracks = 0;
4654 new->next = NULL;
4655 return new;
4656}
4657
4658/**
4659 * This destroys a playlist metadata structure and deallocates the memory
mopoke96143402006-10-30 04:37:26 +00004660 * used by it, including any strings. Never use a track metadata
Linus Walleij438bd7f2006-06-08 11:35:44 +00004661 * structure again after calling this function on it.
4662 * @param playlist the playlist metadata to destroy.
4663 * @see LIBMTP_new_playlist_t()
4664 */
4665void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *playlist)
4666{
4667 if (playlist == NULL) {
4668 return;
4669 }
4670 if (playlist->name != NULL)
4671 free(playlist->name);
4672 if (playlist->tracks != NULL)
4673 free(playlist->tracks);
4674 free(playlist);
4675 return;
4676}
4677
4678/**
4679 * This function returns a list of the playlists available on the
4680 * device. Typical usage:
4681 *
4682 * <pre>
4683 * </pre>
4684 *
4685 * @param device a pointer to the device to get the playlist listing from.
4686 * @return a playlist list on success, else NULL. If there are no playlists
4687 * on the device, NULL will be returned as well.
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004688 * @see LIBMTP_Get_Playlist()
Linus Walleij438bd7f2006-06-08 11:35:44 +00004689 */
4690LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *device)
4691{
4692 PTPParams *params = (PTPParams *) device->params;
4693 LIBMTP_playlist_t *retlists = NULL;
4694 LIBMTP_playlist_t *curlist = NULL;
4695 uint32_t i;
4696
4697 // Get all the handles if we haven't already done that
4698 if (params->handles.Handler == NULL) {
4699 flush_handles(device);
4700 }
4701
4702 for (i = 0; i < params->handles.n; i++) {
4703 LIBMTP_playlist_t *pl;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004704 PTPObjectInfo *oi;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004705 uint16_t ret;
mopoke96143402006-10-30 04:37:26 +00004706
Linus Walleijf0bf4372007-07-01 21:47:38 +00004707 oi = &params->objectinfo[i];
mopoke96143402006-10-30 04:37:26 +00004708
Linus Walleijf0bf4372007-07-01 21:47:38 +00004709 // Ignore stuff that isn't playlists
4710 if ( oi->ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
4711 continue;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004712 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004713
4714 // Allocate a new playlist type
4715 pl = LIBMTP_new_playlist_t();
4716
4717 // Ignoring the oi->Filename field.
4718 pl->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
4719
4720 // This is some sort of unique playlist ID so we can keep track of it
4721 pl->playlist_id = params->handles.Handler[i];
4722
4723 // Then get the track listing for this playlist
4724 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
4725 if (ret != PTP_RC_OK) {
4726 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist: Could not get object references.");
4727 pl->tracks = NULL;
4728 pl->no_tracks = 0;
4729 }
4730
4731 // Add playlist to a list that will be returned afterwards.
4732 if (retlists == NULL) {
4733 retlists = pl;
4734 curlist = pl;
4735 } else {
4736 curlist->next = pl;
4737 curlist = pl;
4738 }
4739
4740 // Call callback here if we decide to add that possibility...
mopoke96143402006-10-30 04:37:26 +00004741 }
Linus Walleij438bd7f2006-06-08 11:35:44 +00004742 return retlists;
4743}
4744
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004745
4746/**
4747 * This function retrieves an individual playlist from the device.
4748 * @param device a pointer to the device to get the playlist from.
4749 * @param plid the unique ID of the playlist to retrieve.
4750 * @return a valid playlist metadata post or NULL on failure.
4751 * @see LIBMTP_Get_Playlist_List()
4752 */
4753LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *device, uint32_t const plid)
4754{
4755 PTPParams *params = (PTPParams *) device->params;
4756 uint32_t i;
4757
4758 // Get all the handles if we haven't already done that
4759 if (params->handles.Handler == NULL) {
4760 flush_handles(device);
4761 }
4762
4763 for (i = 0; i < params->handles.n; i++) {
4764 LIBMTP_playlist_t *pl;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004765 PTPObjectInfo *oi;
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004766 uint16_t ret;
Linus Walleijf0bf4372007-07-01 21:47:38 +00004767
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004768 if (params->handles.Handler[i] != plid) {
4769 continue;
4770 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004771
4772 oi = &params->objectinfo[i];
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004773
Linus Walleijf0bf4372007-07-01 21:47:38 +00004774 // Ignore stuff that isn't playlists
4775 if ( oi->ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004776 return NULL;
4777 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004778
4779 // Allocate a new playlist type
4780 pl = LIBMTP_new_playlist_t();
4781
4782 // Ignoring the io.Filename field.
4783 pl->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
4784
4785 // This is some sort of unique playlist ID so we can keep track of it
4786 pl->playlist_id = params->handles.Handler[i];
4787
4788 // Then get the track listing for this playlist
4789 ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
4790 if (ret != PTP_RC_OK) {
4791 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist(): Could not get object references.");
4792 pl->tracks = NULL;
4793 pl->no_tracks = 0;
4794 }
4795
4796 return pl;
mopoke96143402006-10-30 04:37:26 +00004797 }
Linus Walleij2e4b5f92006-06-16 14:00:49 +00004798 return NULL;
4799}
4800
Linus Walleij20698482006-11-24 20:54:21 +00004801/*
Linus Walleijb7426d12006-11-25 23:19:11 +00004802 * This function creates a new abstract list such as a playlist
4803 * or an album.
4804 *
4805 * @param device a pointer to the device to create the new abstract list
4806 * on.
4807 * @param name the name of the new abstract list.
4808 * @param parenthandle the handle of the parent or 0 for no parent
4809 * i.e. the root folder.
4810 * @param objectformat the abstract list type to create.
4811 * @param suffix the ".foo" (4 characters) suffix to use for the virtual
4812 * "file" created by this operation.
4813 * @param newid a pointer to a variable that will hold the new object
4814 * ID if this call is successful.
4815 * @param tracks an array of tracks to associate with this list.
4816 * @param no_tracks the number of tracks in the list.
Linus Walleij438bd7f2006-06-08 11:35:44 +00004817 * @return 0 on success, any other value means failure.
Linus Walleij438bd7f2006-06-08 11:35:44 +00004818 */
Linus Walleijb7426d12006-11-25 23:19:11 +00004819static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
4820 char const * const name,
Linus Walleijadce4a52007-04-23 07:28:11 +00004821 char const * const artist,
4822 char const * const genre,
Linus Walleijb7426d12006-11-25 23:19:11 +00004823 uint32_t const parenthandle,
4824 uint16_t const objectformat,
4825 char const * const suffix,
4826 uint32_t * const newid,
4827 uint32_t const * const tracks,
4828 uint32_t const no_tracks)
4829
Linus Walleij438bd7f2006-06-08 11:35:44 +00004830{
Linus Walleijb7426d12006-11-25 23:19:11 +00004831 int i;
4832 int supported = 0;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004833 uint16_t ret;
rreardon508705f2006-11-19 21:27:22 +00004834 uint16_t *props = NULL;
4835 uint32_t propcnt = 0;
Linus Walleij9e1b0812006-12-12 19:22:02 +00004836 uint32_t store = get_first_storageid(device);
Linus Walleij438bd7f2006-06-08 11:35:44 +00004837 uint32_t localph = parenthandle;
Linus Walleijb7426d12006-11-25 23:19:11 +00004838 uint8_t nonconsumable = 0x00U; /* By default it is consumable */
4839 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004840 char fname[256];
Linus Walleijd14e84f2006-06-16 14:50:59 +00004841 uint8_t data[2];
Linus Walleij438bd7f2006-06-08 11:35:44 +00004842
Linus Walleijb7426d12006-11-25 23:19:11 +00004843 // Check if we can create an object of this type
4844 for ( i=0; i < params->deviceinfo.ImageFormats_len; i++ ) {
4845 if (params->deviceinfo.ImageFormats[i] == objectformat) {
4846 supported = 1;
4847 break;
Linus Walleij438bd7f2006-06-08 11:35:44 +00004848 }
4849 }
Linus Walleijb7426d12006-11-25 23:19:11 +00004850 if (!supported) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004851 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): player does not support this abstract type.");
4852 printf("Unsupported type: %04x\n", objectformat);
Linus Walleijb7426d12006-11-25 23:19:11 +00004853 return -1;
4854 }
4855
Richard Low15731fe2007-03-22 20:27:20 +00004856 // add the new suffix if it isn't there
Linus Walleij629fe402006-12-12 23:39:15 +00004857 fname[0] = '\0';
Linus Walleijb7426d12006-11-25 23:19:11 +00004858 if (strlen(name) > strlen(suffix)) {
4859 char const * const suff = &name[strlen(name)-strlen(suffix)];
4860 if (!strcmp(suff, suffix)) {
4861 // Home free.
Linus Walleij629fe402006-12-12 23:39:15 +00004862 strncpy(fname, name, sizeof(fname));
Linus Walleijb7426d12006-11-25 23:19:11 +00004863 }
4864 }
4865 // If it didn't end with "<suffix>" then add that here.
Linus Walleij629fe402006-12-12 23:39:15 +00004866 if (fname[0] == '\0') {
Linus Walleijb7426d12006-11-25 23:19:11 +00004867 strncpy(fname, name, sizeof(fname)-strlen(suffix)-1);
4868 strcat(fname, suffix);
Linus Walleij438bd7f2006-06-08 11:35:44 +00004869 fname[sizeof(fname)-1] = '\0';
Linus Walleij438bd7f2006-06-08 11:35:44 +00004870 }
4871
rreardon508705f2006-11-19 21:27:22 +00004872 if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) {
mopoke31364442006-11-20 04:53:04 +00004873
rreardon508705f2006-11-19 21:27:22 +00004874 MTPPropList *proplist = NULL;
4875 MTPPropList *prop = NULL;
Richard Low6c0a6ce2006-11-26 10:42:08 +00004876
4877 *newid = 0x00000000U;
mopoke96143402006-10-30 04:37:26 +00004878
Linus Walleijb7426d12006-11-25 23:19:11 +00004879 ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &props);
mopoke31364442006-11-20 04:53:04 +00004880
rreardon508705f2006-11-19 21:27:22 +00004881 for (i=0;i<propcnt;i++) {
4882 switch (props[i]) {
4883 case PTP_OPC_ObjectFileName:
Linus Walleije7df6532007-03-23 08:20:06 +00004884 prop = new_mtp_prop_entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004885 prop->ObjectHandle = *newid;
4886 prop->property = PTP_OPC_ObjectFileName;
rreardon508705f2006-11-19 21:27:22 +00004887 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00004888 prop->propval.str = strdup(fname);
Linus Walleijadce4a52007-04-23 07:28:11 +00004889 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardon508705f2006-11-19 21:27:22 +00004890 break;
4891 case PTP_OPC_ProtectionStatus:
Linus Walleije7df6532007-03-23 08:20:06 +00004892 prop = new_mtp_prop_entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004893 prop->ObjectHandle = *newid;
rreardon508705f2006-11-19 21:27:22 +00004894 prop->property = PTP_OPC_ProtectionStatus;
4895 prop->datatype = PTP_DTC_UINT16;
4896 prop->propval.u16 = 0x0000U; /* Not protected */
Linus Walleijadce4a52007-04-23 07:28:11 +00004897 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardon508705f2006-11-19 21:27:22 +00004898 break;
4899 case PTP_OPC_NonConsumable:
Linus Walleije7df6532007-03-23 08:20:06 +00004900 prop = new_mtp_prop_entry();
Linus Walleijf1b02f22006-12-06 09:03:23 +00004901 prop->ObjectHandle = *newid;
rreardon508705f2006-11-19 21:27:22 +00004902 prop->property = PTP_OPC_NonConsumable;
4903 prop->datatype = PTP_DTC_UINT8;
4904 prop->propval.u8 = nonconsumable;
Linus Walleijadce4a52007-04-23 07:28:11 +00004905 proplist = add_mtp_prop_to_proplist(proplist, prop);
rreardon508705f2006-11-19 21:27:22 +00004906 break;
4907 case PTP_OPC_Name:
Linus Walleijadce4a52007-04-23 07:28:11 +00004908 if (name != NULL) {
4909 prop = new_mtp_prop_entry();
4910 prop->ObjectHandle = *newid;
4911 prop->property = PTP_OPC_Name;
4912 prop->datatype = PTP_DTC_STR;
4913 prop->propval.str = strdup(name);
4914 proplist = add_mtp_prop_to_proplist(proplist, prop);
4915 }
4916 break;
4917 case PTP_OPC_Artist:
4918 if (artist != NULL) {
4919 prop = new_mtp_prop_entry();
4920 prop->ObjectHandle = *newid;
4921 prop->property = PTP_OPC_Artist;
4922 prop->datatype = PTP_DTC_STR;
4923 prop->propval.str = strdup(artist);
4924 proplist = add_mtp_prop_to_proplist(proplist, prop);
4925 }
4926 break;
4927 case PTP_OPC_Genre:
4928 if (genre != NULL) {
4929 prop = new_mtp_prop_entry();
4930 prop->ObjectHandle = *newid;
4931 prop->property = PTP_OPC_Genre;
4932 prop->datatype = PTP_DTC_STR;
4933 prop->propval.str = strdup(genre);
4934 proplist = add_mtp_prop_to_proplist(proplist, prop);
4935 }
rreardon508705f2006-11-19 21:27:22 +00004936 break;
4937 }
4938 }
4939 free(props);
mopoke31364442006-11-20 04:53:04 +00004940
Linus Walleijb7426d12006-11-25 23:19:11 +00004941 ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid,
Richard Lowa6c924c2006-12-29 17:44:28 +00004942 objectformat, 0, proplist);
mopoke31364442006-11-20 04:53:04 +00004943
rreardon508705f2006-11-19 21:27:22 +00004944 /* Free property list */
Linus Walleije7df6532007-03-23 08:20:06 +00004945 destroy_mtp_prop_list(proplist);
mopoke31364442006-11-20 04:53:04 +00004946
rreardon508705f2006-11-19 21:27:22 +00004947 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004948 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list.");
rreardon508705f2006-11-19 21:27:22 +00004949 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004950 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
rreardon508705f2006-11-19 21:27:22 +00004951 }
4952 return -1;
4953 }
Richard Lowa6c924c2006-12-29 17:44:28 +00004954
4955 // now send the blank objet
4956 ret = ptp_sendobject(params, NULL, 0);
4957 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004958 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00004959 return -1;
4960 }
Linus Walleij629fe402006-12-12 23:39:15 +00004961
rreardon508705f2006-11-19 21:27:22 +00004962 } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
Linus Walleij629fe402006-12-12 23:39:15 +00004963 PTPObjectInfo new_object;
4964
4965 new_object.Filename = fname;
4966 new_object.ObjectCompressedSize = 1;
4967 new_object.ObjectFormat = objectformat;
4968
Linus Walleij20698482006-11-24 20:54:21 +00004969 // Create the object
Linus Walleijb7426d12006-11-25 23:19:11 +00004970 ret = ptp_sendobjectinfo(params, &store, &localph, newid, &new_object);
Linus Walleij20698482006-11-24 20:54:21 +00004971 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004972 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 +00004973 if (ret == PTP_RC_AccessDenied) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004974 add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
Linus Walleij20698482006-11-24 20:54:21 +00004975 }
4976 return -1;
4977 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00004978 // NOTE: don't destroy new_object objectinfo afterwards - the strings it contains are
4979 // not copies.
Richard Lowa6c924c2006-12-29 17:44:28 +00004980
4981 /*
4982 * We have to send this one blank data byte.
4983 * If we don't, the handle will not be created and thus there is no playlist.
4984 */
4985 data[0] = '\0';
4986 data[1] = '\0';
4987 ret = ptp_sendobject(params, data, 1);
4988 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00004989 add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
Richard Lowa6c924c2006-12-29 17:44:28 +00004990 return -1;
4991 }
4992
4993 // Update title
Linus Walleijadce4a52007-04-23 07:28:11 +00004994 if (name != NULL) {
4995 ret = set_object_string(device, *newid, PTP_OPC_Name, name);
4996 if (ret != 0) {
4997 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity name.");
4998 return -1;
4999 }
Richard Lowa6c924c2006-12-29 17:44:28 +00005000 }
Linus Walleijadce4a52007-04-23 07:28:11 +00005001
5002 // Update artist
5003 if (artist != NULL) {
5004 ret = set_object_string(device, *newid, PTP_OPC_Artist, artist);
5005 if (ret != 0) {
5006 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity artist.");
5007 return -1;
5008 }
5009 }
5010
5011 // Update genre
5012 if (genre != NULL) {
5013 ret = set_object_string(device, *newid, PTP_OPC_Genre, genre);
5014 if (ret != 0) {
5015 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity genre.");
5016 return -1;
5017 }
5018 }
5019
Linus Walleij438bd7f2006-06-08 11:35:44 +00005020 }
5021
Linus Walleijb7426d12006-11-25 23:19:11 +00005022 if (no_tracks > 0) {
Linus Walleij438bd7f2006-06-08 11:35:44 +00005023 // Add tracks to the new playlist as object references.
Linus Walleijb7426d12006-11-25 23:19:11 +00005024 ret = ptp_mtp_setobjectreferences (params, *newid, (uint32_t *) tracks, no_tracks);
Linus Walleij438bd7f2006-06-08 11:35:44 +00005025 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005026 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 +00005027 return -1;
5028 }
5029 }
mopoke96143402006-10-30 04:37:26 +00005030
tsaarnia3eb60a2007-07-06 17:41:30 +00005031 add_object_to_cache(device, *newid);
Linus Walleij438bd7f2006-06-08 11:35:44 +00005032
5033 return 0;
5034}
5035
Linus Walleijb7426d12006-11-25 23:19:11 +00005036
5037/**
5038 * This routine creates a new playlist based on the metadata
5039 * supplied. If the <code>tracks</code> field of the metadata
5040 * contains a track listing, these tracks will be added to the
5041 * playlist.
5042 * @param device a pointer to the device to create the new playlist on.
5043 * @param metadata the metadata for the new playlist. If the function
5044 * exits with success, the <code>playlist_id</code> field of this
5045 * struct will contain the new playlist ID of the playlist.
5046 * @param parenthandle the parent (e.g. folder) to store this playlist
5047 * in. Pass in 0 to put the playlist in the root directory.
5048 * @return 0 on success, any other value means failure.
5049 * @see LIBMTP_Update_Playlist()
5050 * @see LIBMTP_Delete_Object()
5051 */
5052int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *device,
5053 LIBMTP_playlist_t * const metadata,
5054 uint32_t const parenthandle)
5055{
5056 uint32_t localph = parenthandle;
5057
5058 // Use a default folder if none given
5059 if (localph == 0) {
5060 localph = device->default_playlist_folder;
5061 }
5062
5063 // Just create a new abstract audio/video playlist...
5064 return create_new_abstract_list(device,
5065 metadata->name,
Linus Walleijadce4a52007-04-23 07:28:11 +00005066 NULL,
5067 NULL,
Linus Walleijb7426d12006-11-25 23:19:11 +00005068 localph,
5069 PTP_OFC_MTP_AbstractAudioVideoPlaylist,
5070 ".zpl",
5071 &metadata->playlist_id,
5072 metadata->tracks,
5073 metadata->no_tracks);
5074}
5075
Linus Walleij438bd7f2006-06-08 11:35:44 +00005076/**
5077 * This routine updates a playlist based on the metadata
5078 * supplied. If the <code>tracks</code> field of the metadata
5079 * contains a track listing, these tracks will be added to the
5080 * playlist in place of those already present, i.e. the
5081 * previous track listing will be deleted.
5082 * @param device a pointer to the device to create the new playlist on.
5083 * @param metadata the metadata for the playlist to be updated.
mopoke96143402006-10-30 04:37:26 +00005084 * notice that the field <code>playlist_id</code>
Linus Walleij438bd7f2006-06-08 11:35:44 +00005085 * must contain the apropriate playlist ID.
5086 * @return 0 on success, any other value means failure.
5087 * @see LIBMTP_Create_New_Playlist()
5088 * @see LIBMTP_Delete_Object()
5089 */
mopoke96143402006-10-30 04:37:26 +00005090int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device,
Linus Walleijf5306352006-06-08 12:00:23 +00005091 LIBMTP_playlist_t const * const metadata)
Linus Walleij438bd7f2006-06-08 11:35:44 +00005092{
5093 uint16_t ret;
Linus Walleijf5306352006-06-08 12:00:23 +00005094 PTPParams *params = (PTPParams *) device->params;
Linus Walleij438bd7f2006-06-08 11:35:44 +00005095
5096 // Update title
Linus Walleij9901e222006-11-30 12:28:19 +00005097 ret = set_object_string(device, metadata->playlist_id, PTP_OPC_Name, metadata->name);
Linus Walleijd14e84f2006-06-16 14:50:59 +00005098 if (ret != 0) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005099 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Playlist(): could not set playlist name.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00005100 return -1;
5101 }
5102
5103 if (metadata->no_tracks > 0) {
5104 // Add tracks to the new playlist as object references.
5105 ret = ptp_mtp_setobjectreferences (params, metadata->playlist_id, metadata->tracks, metadata->no_tracks);
5106 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005107 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Playlist(): could not add tracks as object references.");
Linus Walleij438bd7f2006-06-08 11:35:44 +00005108 return -1;
5109 }
5110 }
5111 return 0;
5112}
Linus Walleijaa4b0752006-07-26 22:21:04 +00005113
Linus Walleij0c33ec02006-10-27 10:15:40 +00005114/**
5115 * This creates a new album metadata structure and allocates memory
5116 * for it. Notice that if you add strings to this structure they
5117 * will be freed by the corresponding <code>LIBMTP_destroy_album_t</code>
5118 * operation later, so be careful of using strdup() when assigning
5119 * strings.
5120 *
5121 * @return a pointer to the newly allocated metadata structure.
5122 * @see LIBMTP_destroy_album_t()
5123 */
5124LIBMTP_album_t *LIBMTP_new_album_t(void)
5125{
5126 LIBMTP_album_t *new = (LIBMTP_album_t *) malloc(sizeof(LIBMTP_album_t));
5127 if (new == NULL) {
5128 return NULL;
5129 }
5130 new->album_id = 0;
5131 new->name = NULL;
Linus Walleijadce4a52007-04-23 07:28:11 +00005132 new->artist = NULL;
5133 new->genre = NULL;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005134 new->tracks = NULL;
5135 new->no_tracks = 0;
5136 new->next = NULL;
5137 return new;
5138}
5139
5140/**
5141 * This recursively deletes the memory for an album structure
5142 *
5143 * @param album structure to destroy
5144 * @see LIBMTP_new_album_t()
5145 */
5146void LIBMTP_destroy_album_t(LIBMTP_album_t *album)
5147{
5148 if (album == NULL) {
5149 return;
5150 }
5151 if (album->name != NULL)
5152 free(album->name);
Linus Walleijadce4a52007-04-23 07:28:11 +00005153 if (album->artist != NULL)
5154 free(album->artist);
5155 if (album->genre != NULL)
5156 free(album->genre);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005157 if (album->tracks != NULL)
5158 free(album->tracks);
5159 free(album);
5160 return;
5161}
5162
5163/**
5164 * This function returns a list of the albums available on the
5165 * device.
5166 *
5167 * @param device a pointer to the device to get the album listing from.
5168 * @return an album list on success, else NULL. If there are no albums
5169 * on the device, NULL will be returned as well.
5170 * @see LIBMTP_Get_Album()
5171 */
5172LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *device)
5173{
5174 PTPParams *params = (PTPParams *) device->params;
5175 LIBMTP_album_t *retalbums = NULL;
5176 LIBMTP_album_t *curalbum = NULL;
5177 uint32_t i;
5178
5179 // Get all the handles if we haven't already done that
5180 if (params->handles.Handler == NULL) {
5181 flush_handles(device);
5182 }
5183
5184 for (i = 0; i < params->handles.n; i++) {
5185 LIBMTP_album_t *alb;
Linus Walleijf0bf4372007-07-01 21:47:38 +00005186 PTPObjectInfo *oi;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005187 uint16_t ret;
5188
Linus Walleijf0bf4372007-07-01 21:47:38 +00005189 oi = &params->objectinfo[i];
Linus Walleij0c33ec02006-10-27 10:15:40 +00005190
Linus Walleijf0bf4372007-07-01 21:47:38 +00005191 // Ignore stuff that isn't an album
5192 if ( oi->ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum ) {
5193 continue;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005194 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00005195
5196 // Allocate a new album type
5197 alb = LIBMTP_new_album_t();
5198 // Get metadata for it.
5199 alb->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
5200 alb->artist = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Artist);
5201 alb->genre = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Genre);
5202 alb->album_id = params->handles.Handler[i];
5203
5204 // Then get the track listing for this album
5205 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
5206 if (ret != PTP_RC_OK) {
5207 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Could not get object references.");
5208 alb->tracks = NULL;
5209 alb->no_tracks = 0;
5210 }
5211
5212 // Add album to a list that will be returned afterwards.
5213 if (retalbums == NULL) {
5214 retalbums = alb;
5215 curalbum = alb;
5216 } else {
5217 curalbum->next = alb;
5218 curalbum = alb;
5219 }
5220
Linus Walleij0c33ec02006-10-27 10:15:40 +00005221 }
5222 return retalbums;
5223}
5224
5225/**
5226 * This function retrieves an individual album from the device.
5227 * @param device a pointer to the device to get the album from.
5228 * @param albid the unique ID of the album to retrieve.
5229 * @return a valid album metadata or NULL on failure.
5230 * @see LIBMTP_Get_Album_List()
5231 */
5232LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *device, uint32_t const albid)
5233{
5234 PTPParams *params = (PTPParams *) device->params;
5235 uint32_t i;
5236
5237 // Get all the handles if we haven't already done that
5238 if (params->handles.Handler == NULL) {
5239 flush_handles(device);
5240 }
5241
5242 for (i = 0; i < params->handles.n; i++) {
5243 LIBMTP_album_t *alb;
Linus Walleijf0bf4372007-07-01 21:47:38 +00005244 PTPObjectInfo *oi;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005245 uint16_t ret;
5246
5247 if (params->handles.Handler[i] != albid) {
5248 continue;
5249 }
5250
Linus Walleijf0bf4372007-07-01 21:47:38 +00005251 oi = &params->objectinfo[i];
Linus Walleij0c33ec02006-10-27 10:15:40 +00005252
Linus Walleijf0bf4372007-07-01 21:47:38 +00005253 // Ignore stuff that isn't an album
5254 if ( oi->ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum ) {
Linus Walleij0c33ec02006-10-27 10:15:40 +00005255 return NULL;
5256 }
Linus Walleijf0bf4372007-07-01 21:47:38 +00005257
5258 // Allocate a new album type
5259 alb = LIBMTP_new_album_t();
5260 alb->album_id = params->handles.Handler[i];
5261 alb->name = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Name);
5262 alb->artist = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Artist);
5263 alb->genre = get_string_from_object(device, params->handles.Handler[i], PTP_OPC_Genre);
5264 ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
5265 if (ret != PTP_RC_OK) {
5266 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album: Could not get object references.");
5267 alb->tracks = NULL;
5268 alb->no_tracks = 0;
5269 }
5270
5271 return alb;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005272 }
5273 return NULL;
5274}
5275
5276/**
5277 * This routine creates a new album based on the metadata
5278 * supplied. If the <code>tracks</code> field of the metadata
5279 * contains a track listing, these tracks will be added to the
5280 * album.
5281 * @param device a pointer to the device to create the new album on.
5282 * @param metadata the metadata for the new album. If the function
5283 * exits with success, the <code>album_id</code> field of this
5284 * struct will contain the new ID of the album.
5285 * @param parenthandle the parent (e.g. folder) to store this album
5286 * in. Pass in 0 to put the album in the default music directory.
5287 * @return 0 on success, any other value means failure.
5288 * @see LIBMTP_Update_Album()
5289 * @see LIBMTP_Delete_Object()
5290 */
5291int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *device,
5292 LIBMTP_album_t * const metadata,
5293 uint32_t const parenthandle)
5294{
Linus Walleij0c33ec02006-10-27 10:15:40 +00005295 uint32_t localph = parenthandle;
mopoke96143402006-10-30 04:37:26 +00005296
Linus Walleij0c33ec02006-10-27 10:15:40 +00005297 // Use a default folder if none given
5298 if (localph == 0) {
rreardon508705f2006-11-19 21:27:22 +00005299 localph = device->default_album_folder;
Linus Walleij0c33ec02006-10-27 10:15:40 +00005300 }
5301
Linus Walleijb7426d12006-11-25 23:19:11 +00005302 // Just create a new abstract album...
5303 return create_new_abstract_list(device,
5304 metadata->name,
Linus Walleijadce4a52007-04-23 07:28:11 +00005305 metadata->artist,
5306 metadata->genre,
Linus Walleijb7426d12006-11-25 23:19:11 +00005307 localph,
5308 PTP_OFC_MTP_AbstractAudioAlbum,
5309 ".alb",
5310 &metadata->album_id,
5311 metadata->tracks,
5312 metadata->no_tracks);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005313}
5314
5315/**
rreardon5332f9c2006-12-05 10:18:23 +00005316 * This creates a new sample data metadata structure and allocates memory
5317 * for it. Notice that if you add strings to this structure they
5318 * will be freed by the corresponding <code>LIBMTP_destroy_sampledata_t</code>
5319 * operation later, so be careful of using strdup() when assigning
5320 * strings.
5321 *
5322 * @return a pointer to the newly allocated metadata structure.
5323 * @see LIBMTP_destroy_sampledata_t()
5324 */
5325LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void)
5326{
5327 LIBMTP_filesampledata_t *new = (LIBMTP_filesampledata_t *) malloc(sizeof(LIBMTP_filesampledata_t));
5328 if (new == NULL) {
5329 return NULL;
5330 }
5331 new->height=0;
5332 new->width = 0;
5333 new->data = NULL;
5334 new->duration = 0;
5335 new->size = 0;
5336 return new;
5337}
5338
Linus Walleijf1b02f22006-12-06 09:03:23 +00005339/**
5340 * This destroys a file sample metadata type.
5341 * @param sample the file sample metadata to be destroyed.
5342 */
5343void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t * sample)
5344{
5345 if (sample == NULL) {
5346 return;
5347 }
5348 if (sample->data != NULL) {
5349 free(sample->data);
5350 }
5351 free(sample);
5352}
5353
5354/**
5355 * This routine figures out whether a certain filetype supports
5356 * representative samples (small thumbnail images) or not. This
5357 * typically applies to JPEG files, MP3 files and Album abstract
5358 * playlists, but in theory any filetype could support representative
5359 * samples.
5360 * @param device a pointer to the device which is to be examined.
5361 * @param the filetype to examine, and return the representative sample
5362 * properties for.
5363 * @param sample this will contain a new sample type with the fields
5364 * filled in with suitable default values. For example, the
5365 * supported sample type will be set, the supported height and
5366 * width will be set to max values if it is an image sample,
5367 * and duration will also be given some suitable default value
5368 * which should not be exceeded on audio samples. If the
5369 * device does not support samples for this filetype, this
5370 * pointer will be NULL. If it is not NULL, the user must
5371 * destroy this struct with <code>LIBMTP_destroy_filesampledata_t()</code>
5372 * after use.
5373 * @return 0 on success, any other value means failure.
5374 * @see LIBMTP_Send_Representative_Sample()
5375 * @see LIBMTP_Create_New_Album()
5376 */
5377int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *device,
5378 LIBMTP_filetype_t const filetype,
5379 LIBMTP_filesampledata_t ** sample)
5380{
5381 uint16_t ret;
5382 PTPParams *params = (PTPParams *) device->params;
5383 uint16_t *props = NULL;
5384 uint32_t propcnt = 0;
5385 int i;
5386 // TODO: Get rid of these when we can properly query the device.
5387 int support_data = 0;
5388 int support_format = 0;
5389 int support_height = 0;
5390 int support_width = 0;
5391 int support_duration = 0;
5392
5393 // Default to no type supported.
5394 *sample = NULL;
5395
5396 ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
5397 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005398 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample_Format(): could not get object properties.");
Linus Walleijf1b02f22006-12-06 09:03:23 +00005399 return -1;
5400 }
5401 /*
5402 * TODO: when walking through these object properties, make calls to
5403 * a new function in ptp.h/ptp.c that can send the command
5404 * PTP_OC_MTP_GetObjectPropDesc to get max/min values of the properties
5405 * supported.
5406 */
5407 for (i = 0; i < propcnt; i++) {
5408 switch(props[i]) {
5409 case PTP_OPC_RepresentativeSampleData:
5410 support_data = 1;
5411 break;
5412 case PTP_OPC_RepresentativeSampleFormat:
5413 support_format = 1;
5414 break;
5415 case PTP_OPC_RepresentativeSampleSize:
5416 break;
5417 case PTP_OPC_RepresentativeSampleHeight:
5418 support_height = 1;
5419 break;
5420 case PTP_OPC_RepresentativeSampleWidth:
5421 support_width = 1;
5422 break;
5423 case PTP_OPC_RepresentativeSampleDuration:
5424 support_duration = 1;
5425 break;
5426 default:
5427 break;
5428 }
5429 }
5430 free(props);
5431
5432 /*
5433 * TODO: figure out what format, max height and width, or duration is actually
5434 * supported on this device.
5435 */
5436 if (support_data && support_format && support_height && support_width && !support_duration) {
5437 // Something that supports height and width and not duration is likely to be JPEG
5438 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
5439 retsam->filetype = LIBMTP_FILETYPE_JPEG;
5440 retsam->width = 100;
5441 retsam->height = 100;
5442 *sample = retsam;
5443 } else if (support_data && support_format && !support_height && !support_width && support_duration) {
5444 // Another qualified guess
5445 LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
5446 retsam->filetype = LIBMTP_FILETYPE_MP3;
5447 retsam->duration = 2000; // 2 seconds
5448 *sample = retsam;
5449 }
5450 return 0;
5451}
rreardon5332f9c2006-12-05 10:18:23 +00005452
5453/**
5454 * This routine sends representative sample data for an object.
5455 * This uses the RepresentativeSampleData property of the album,
5456 * if the device supports it. The data should be of a format acceptable
5457 * to the player (for iRiver and Creative, this seems to be JPEG) and
5458 * must not be too large. (for a Creative, max seems to be about 20KB.)
Linus Walleij0c33ec02006-10-27 10:15:40 +00005459 * TODO: there must be a way to find the max size for an ObjectPropertyValue.
Linus Walleij7e3f2762006-12-03 22:52:05 +00005460 * @param device a pointer to the device which the object is on.
5461 * @param id unique id of the object to set artwork for.
5462 * @param data pointer to an array of uint8_t containing the representative
5463 * sample data.
5464 * @param size number of bytes in the sample.
Linus Walleij0c33ec02006-10-27 10:15:40 +00005465 * @return 0 on success, any other value means failure.
Linus Walleijf1b02f22006-12-06 09:03:23 +00005466 * @see LIBMTP_Get_Representative_Sample_Format()
Linus Walleij0c33ec02006-10-27 10:15:40 +00005467 * @see LIBMTP_Create_New_Album()
5468 */
Linus Walleij7e3f2762006-12-03 22:52:05 +00005469int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *device,
rreardon5332f9c2006-12-05 10:18:23 +00005470 uint32_t const id,
5471 LIBMTP_filesampledata_t *sampledata)
Linus Walleij0c33ec02006-10-27 10:15:40 +00005472{
5473 uint16_t ret;
5474 PTPParams *params = (PTPParams *) device->params;
5475 PTPPropertyValue propval;
Linus Walleijf0bf4372007-07-01 21:47:38 +00005476 PTPObjectInfo *oi;
5477 uint32_t i;
Linus Walleijf1b02f22006-12-06 09:03:23 +00005478 uint16_t *props = NULL;
5479 uint32_t propcnt = 0;
5480 int supported = 0;
rreardon5332f9c2006-12-05 10:18:23 +00005481
5482 // get the file format for the object we're going to send representative data for
Linus Walleijf0bf4372007-07-01 21:47:38 +00005483 oi = NULL;
5484 for (i = 0; i < params->handles.n; i++) {
5485 if (params->handles.Handler[i] == id) {
5486 oi = &params->objectinfo[i];
5487 break;
5488 }
5489 }
rreardon5332f9c2006-12-05 10:18:23 +00005490
Linus Walleijf0bf4372007-07-01 21:47:38 +00005491 if (oi == NULL) {
5492 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): could not get object info.");
rreardon5332f9c2006-12-05 10:18:23 +00005493 return -1;
5494 }
5495
5496 // check that we can send representative sample data for this object format
Linus Walleijf0bf4372007-07-01 21:47:38 +00005497 ret = ptp_mtp_getobjectpropssupported(params, oi->ObjectFormat, &propcnt, &props);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005498 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005499 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object properties.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00005500 return -1;
5501 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00005502
Linus Walleij0c33ec02006-10-27 10:15:40 +00005503 for (i = 0; i < propcnt; i++) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00005504 if (props[i] == PTP_OPC_RepresentativeSampleData) {
Linus Walleij0c33ec02006-10-27 10:15:40 +00005505 supported = 1;
Linus Walleijf1b02f22006-12-06 09:03:23 +00005506 break;
5507 }
Linus Walleij0c33ec02006-10-27 10:15:40 +00005508 }
5509 if (!supported) {
Linus Walleijf1b02f22006-12-06 09:03:23 +00005510 free(props);
Linus Walleij070e9b42007-01-22 08:49:28 +00005511 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 +00005512 return -1;
5513 }
Linus Walleijf1b02f22006-12-06 09:03:23 +00005514 free(props);
rreardon5332f9c2006-12-05 10:18:23 +00005515
Linus Walleijf1b02f22006-12-06 09:03:23 +00005516 // Go ahead and send the data
rreardon5332f9c2006-12-05 10:18:23 +00005517 propval.a.count = sampledata->size;
5518 propval.a.v = malloc(sizeof(PTPPropertyValue) * sampledata->size);
5519 for (i = 0; i < sampledata->size; i++) {
5520 propval.a.v[i].u8 = sampledata->data[i];
Linus Walleij7e3f2762006-12-03 22:52:05 +00005521 }
5522
Linus Walleij0c33ec02006-10-27 10:15:40 +00005523 ret = ptp_mtp_setobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
Linus Walleijf1b02f22006-12-06 09:03:23 +00005524 &propval,PTP_DTC_AUINT8);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005525 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005526 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not send sample data.");
Linus Walleij7e3f2762006-12-03 22:52:05 +00005527 free(propval.a.v);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005528 return -1;
5529 }
Linus Walleij7e3f2762006-12-03 22:52:05 +00005530 free(propval.a.v);
Linus Walleijf1b02f22006-12-06 09:03:23 +00005531
5532 /*
5533 * TODO: Send Representative Sample Height, Width and Size here if it is an
5534 * image (typically JPEG) thumbnail, send Duration and Size if it is an audio
5535 * sample (MP3, WAV etc).
5536 */
rreardond2ddb632006-12-12 12:13:21 +00005537
5538 /* Set the height and width if the sample is an image, otherwise just
5539 * set the duration and size */
5540 switch(sampledata->filetype) {
Linus Walleije1ac07e2006-12-14 19:38:59 +00005541 case LIBMTP_FILETYPE_JPEG:
5542 case LIBMTP_FILETYPE_JFIF:
5543 case LIBMTP_FILETYPE_TIFF:
5544 case LIBMTP_FILETYPE_BMP:
5545 case LIBMTP_FILETYPE_GIF:
5546 case LIBMTP_FILETYPE_PICT:
5547 case LIBMTP_FILETYPE_PNG:
5548 // For images, set the height and width
5549 set_object_u32(device, id, PTP_OPC_RepresentativeSampleHeight, sampledata->height);
5550 set_object_u32(device, id, PTP_OPC_RepresentativeSampleWidth, sampledata->width);
5551 break;
5552 default:
5553 // For anything not an image, set the duration and size
5554 set_object_u32(device, id, PTP_OPC_RepresentativeSampleDuration, sampledata->duration);
5555 set_object_u32(device, id, PTP_OPC_RepresentativeSampleSize, sampledata->size);
5556 break;
rreardond2ddb632006-12-12 12:13:21 +00005557 }
rreardond2ddb632006-12-12 12:13:21 +00005558
Linus Walleij0c33ec02006-10-27 10:15:40 +00005559 return 0;
5560}
5561
5562/**
5563 * This routine updates an album based on the metadata
5564 * supplied. If the <code>tracks</code> field of the metadata
5565 * contains a track listing, these tracks will be added to the
5566 * album in place of those already present, i.e. the
5567 * previous track listing will be deleted.
5568 * @param device a pointer to the device to create the new album on.
5569 * @param metadata the metadata for the album to be updated.
5570 * notice that the field <code>album_id</code>
5571 * must contain the apropriate album ID.
5572 * @return 0 on success, any other value means failure.
5573 * @see LIBMTP_Create_New_Album()
5574 * @see LIBMTP_Delete_Object()
5575 */
5576int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device,
5577 LIBMTP_album_t const * const metadata)
5578{
5579 uint16_t ret;
5580 PTPParams *params = (PTPParams *) device->params;
Linus Walleije60d56b2007-05-28 22:13:12 +00005581 uint16_t *props = NULL;
5582 uint32_t propcnt = 0;
5583 int i;
5584
5585 // First see which properties can be set
5586 // i.e only try to update this metadata for object tags that exist on the current player.
5587 ret = ptp_mtp_getobjectpropssupported(params, PTP_OFC_MTP_AbstractAudioAlbum, &propcnt, &props);
5588 if (ret != PTP_RC_OK) {
5589 // Just bail out for now, nothing is ever set.
5590 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5591 "could not retrieve supported object properties.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00005592 return -1;
5593 }
Linus Walleije60d56b2007-05-28 22:13:12 +00005594 if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList)) {
5595 MTPPropList *proplist = NULL;
5596 MTPPropList *prop = NULL;
5597 PTPObjectPropDesc opd;
5598
5599 for (i=0;i<propcnt;i++) {
5600 switch (props[i]) {
5601 case PTP_OPC_Name:
5602 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Name, PTP_OFC_MTP_AbstractAudioAlbum, &opd);
5603 if (ret != PTP_RC_OK) {
5604 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5605 "could not get property description for PTP_OPC_Name.");
5606 break;
5607 }
5608
5609 if (opd.GetSet) {
5610 prop = new_mtp_prop_entry();
5611 prop->ObjectHandle = metadata->album_id;
5612 prop->property = PTP_OPC_Name;
5613 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00005614 if (metadata->name != NULL)
5615 prop->propval.str = strdup(metadata->name);
Linus Walleije60d56b2007-05-28 22:13:12 +00005616 proplist = add_mtp_prop_to_proplist(proplist, prop);
5617 }
5618 ptp_free_objectpropdesc(&opd);
5619
5620 break;
5621 case PTP_OPC_Artist:
5622 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Artist, PTP_OFC_MTP_AbstractAudioAlbum, &opd);
5623 if (ret != PTP_RC_OK) {
5624 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5625 "could not get property description for PTP_OPC_Artist.");
5626 break;
5627 }
5628
5629 if (opd.GetSet) {
5630 prop = new_mtp_prop_entry();
5631 prop->ObjectHandle = metadata->album_id;
5632 prop->property = PTP_OPC_Artist;
5633 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00005634 if (metadata->artist != NULL)
5635 prop->propval.str = strdup(metadata->artist);
Linus Walleije60d56b2007-05-28 22:13:12 +00005636 proplist = add_mtp_prop_to_proplist(proplist, prop);
5637 }
5638 ptp_free_objectpropdesc(&opd);
5639
5640 break;
5641 case PTP_OPC_Genre:
5642 ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_Genre, PTP_OFC_MTP_AbstractAudioAlbum, &opd);
5643 if (ret != PTP_RC_OK) {
5644 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5645 "could not get property description for PTP_OPC_Genre.");
5646 break;
5647 }
5648
5649 if (opd.GetSet) {
5650 prop = new_mtp_prop_entry();
5651 prop->ObjectHandle = metadata->album_id;
5652 prop->property = PTP_OPC_Genre;
5653 prop->datatype = PTP_DTC_STR;
Richard Low0fa83522007-07-17 20:05:08 +00005654 if (metadata->genre != NULL)
5655 prop->propval.str = strdup(metadata->genre);
Linus Walleije60d56b2007-05-28 22:13:12 +00005656 proplist = add_mtp_prop_to_proplist(proplist, prop);
5657 }
5658 ptp_free_objectpropdesc(&opd);
5659
5660 break;
5661 default:
5662 break;
5663 }
5664 }
5665
5666 ret = ptp_mtp_setobjectproplist(params, proplist);
Linus Walleij0c33ec02006-10-27 10:15:40 +00005667
Linus Walleije60d56b2007-05-28 22:13:12 +00005668 destroy_mtp_prop_list(proplist);
5669
5670 if (ret != PTP_RC_OK) {
5671 // TODO: return error of which property we couldn't set
5672 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5673 "could not set object property list.");
5674 return -1;
5675 }
5676
5677 } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
5678 for (i=0;i<propcnt;i++) {
5679 switch (props[i]) {
5680 case PTP_OPC_Name:
5681 // Update title
5682 ret = set_object_string(device, metadata->album_id, PTP_OPC_Name, metadata->name);
5683 if (ret != 0) {
5684 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5685 "could not set album title.");
5686 }
5687 break;
5688 case PTP_OPC_Artist:
5689 // Update artist
5690 ret = set_object_string(device, metadata->album_id, PTP_OPC_Artist, metadata->artist);
5691 if (ret != 0) {
5692 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5693 "could not set album artist name.");
5694 }
5695 break;
5696 case PTP_OPC_Genre:
5697 // Update genre
5698 ret = set_object_string(device, metadata->album_id, PTP_OPC_Genre, metadata->genre);
5699 if (ret != 0) {
5700 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5701 "could not set album genre.");
5702 }
5703 break;
5704 default:
5705 break;
5706 }
5707 }
5708 free(props);
5709 } else {
5710 add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): "
5711 "Your device doesn't seem to support any known way of setting metadata.");
5712 return -1;
5713 }
5714
5715 // Then the object references...
Linus Walleij0c33ec02006-10-27 10:15:40 +00005716 if (metadata->no_tracks > 0) {
5717 // Add tracks to the new album as object references.
5718 ret = ptp_mtp_setobjectreferences (params, metadata->album_id, metadata->tracks, metadata->no_tracks);
5719 if (ret != PTP_RC_OK) {
Linus Walleij070e9b42007-01-22 08:49:28 +00005720 add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Album(): could not add tracks as object references.");
Linus Walleij0c33ec02006-10-27 10:15:40 +00005721 return -1;
5722 }
5723 }
Linus Walleij338ade42007-07-03 20:44:08 +00005724
Linus Walleij338ade42007-07-03 20:44:08 +00005725 if (params->proplist != NULL) {
tsaarnia3eb60a2007-07-06 17:41:30 +00005726 update_metadata_cache(device, metadata->album_id);
Linus Walleij338ade42007-07-03 20:44:08 +00005727 }
Linus Walleij0c33ec02006-10-27 10:15:40 +00005728 return 0;
5729}
Linus Walleijaa4b0752006-07-26 22:21:04 +00005730
5731/**
5732 * Dummy function needed to interface to upstream
5733 * ptp.c/ptp.h files.
5734 */
5735void ptp_nikon_getptpipguid (unsigned char* guid) {
5736 return;
5737}
tsaarnia3eb60a2007-07-06 17:41:30 +00005738
5739/**
5740 * Update cache after new object is added to the device
5741 */
5742void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t handle)
5743{
5744 PTPParams *params = (PTPParams *)device->params;
5745 MTPPropList *prop = NULL;
5746 uint16_t ret;
5747 int n;
5748
5749 n = ++params->handles.n;
5750
5751 params->objectinfo = (PTPObjectInfo*)realloc(params->objectinfo,
5752 sizeof(PTPObjectInfo)*n);
5753 params->handles.Handler = (uint32_t*)realloc(params->handles.Handler,
5754 sizeof(uint32_t)*n);
5755
5756 memset(&params->objectinfo[n-1], 0, sizeof(PTPObjectInfo));
5757 params->handles.Handler[n-1] = handle;
5758
5759 ptp_getobjectinfo(params, handle, &params->objectinfo[n-1]);
5760
5761 if (params->proplist) {
5762
5763 ret = ptp_mtp_getobjectproplist(params, handle, &prop);
5764 if (ret != PTP_RC_OK) {
5765 add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): call to ptp_mtp_getobjectproplist() failed.");
5766 return;
5767 }
5768 add_mtp_prop_to_proplist(params->proplist, prop);
5769 }
5770}
5771
5772
5773/**
5774 * Update cache after object has been modified
5775 */
5776void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t handle)
5777{
5778 PTPParams *params = (PTPParams *)device->params;
5779 MTPPropList *prop = params->proplist;
5780 MTPPropList *prev = NULL;
5781 MTPPropList *deleted_props = NULL;
5782 MTPPropList *updated_props;
5783 uint16_t ret;
5784
5785 // find the place where the properties for this object begin
5786 while (prop != NULL && prop->ObjectHandle != handle) {
5787 prev = prop;
5788 prop = prop->next;
5789 }
5790
5791 // safeguard in case object is not found
5792 if (!prop) return;
5793
5794 // fetch updated properties and add them to the metadata cache in the same
5795 // place where outdated properties were found
5796 ret = ptp_mtp_getobjectproplist(params, handle, &updated_props);
5797 if (ret != PTP_RC_OK) {
5798 add_ptp_error_to_errorstack(device, ret, "update_metadata_cache(): call to ptp_mtp_getobjectproplist() failed.");
5799 return;
5800 }
5801 prev->next = updated_props;
5802
5803 // head of outdated object properties that will be deleted
5804 deleted_props = prop;
5805
5806 // find the end of properties for this object
5807 while (prop != NULL && prop->ObjectHandle == handle) {
5808 prev = prop;
5809 prop = prop->next;
5810 }
5811
5812 // find the end of the fetched properties and link rest of the list to it
5813 while (updated_props->next != NULL) {
5814 updated_props = updated_props->next;
5815 }
5816 updated_props->next = prop;
5817
5818 // split outdated properties into separate list and delete it
5819 prev->next = NULL;
5820 destroy_mtp_prop_list(deleted_props);
5821
5822 return;
5823}