blob: bb44ab622aec5402dd0d4c36a9e483ab7f6dd7fc [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "MtpDatabase.h"
18#include "MtpDataPacket.h"
19#include "SqliteDatabase.h"
20#include "SqliteStatement.h"
21
22#include <stdio.h>
23#include <sqlite3.h>
24
25#define ID_COLUMN 1
26#define PATH_COLUMN 2
27#define FORMAT_COLUMN 3
28#define PARENT_COLUMN 4
29#define STORAGE_COLUMN 5
30#define SIZE_COLUMN 6
31#define CREATED_COLUMN 7
32#define MODIFIED_COLUMN 8
33
34#define TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \
35 "_id INTEGER PRIMARY KEY," \
36 "path TEXT," \
37 "format INTEGER," \
38 "parent INTEGER," \
39 "storage INTEGER," \
40 "size INTEGER," \
41 "date_created INTEGER," \
42 "date_modified INTEGER" \
43 ");"
44
45#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);"
46
47#define FILE_ID_QUERY "SELECT _id FROM files WHERE path = ?;"
48#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?"
49
50#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_created,date_modified FROM files WHERE _id = ?;"
51#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?,?);"
52#define FILE_DELETE "DELETE FROM files WHERE path = ?;"
53
54
55struct PropertyTableEntry {
56 MtpObjectProperty property;
57 int type;
58 const char* columnName;
59};
60
61static const PropertyTableEntry kPropertyTable[] = {
62 { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" },
63 { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" },
64 { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" },
65 { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" },
66 { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" },
67 { MTP_PROPERTY_DATE_CREATED, MTP_TYPE_STR, "date_created" },
68 { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" },
69};
70
71static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) {
72 int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
73 const PropertyTableEntry* entry = kPropertyTable;
74 for (int i = 0; i < count; i++, entry++) {
75 if (entry->property == property) {
76 type = entry->type;
77 columnName = entry->columnName;
78 return true;
79 }
80 }
81 return false;
82}
83
84
85MtpDatabase::MtpDatabase()
86 : mFileIdQuery(NULL),
87 mObjectInfoQuery(NULL),
88 mFileInserter(NULL),
89 mFileDeleter(NULL)
90{
91}
92
93MtpDatabase::~MtpDatabase() {
94}
95
96bool MtpDatabase::open(const char* path, bool create) {
97 if (!SqliteDatabase::open(path, create))
98 return false;
99
100 // create the table if necessary
101 if (!exec(TABLE_CREATE)) {
102 fprintf(stderr, "could not create table\n");
103 return false;
104 }
105 if (!exec(PATH_INDEX_CREATE)) {
106 fprintf(stderr, "could not path index\n");
107 return false;
108 }
109 return true;
110}
111
112MtpObjectHandle MtpDatabase::addFile(const char* path,
113 MtpObjectFormat format,
114 MtpObjectHandle parent,
115 MtpStorageID storage,
116 uint64_t size,
117 time_t created,
118 time_t modified) {
119
120 // first check to see if the file exists
121 if (mFileIdQuery)
122 mFileIdQuery->reset();
123 else {
124 mFileIdQuery = new SqliteStatement(this);
125 if (!mFileIdQuery->prepare(FILE_ID_QUERY)) {
126 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
127 delete mFileIdQuery;
128 mFileIdQuery = NULL;
129 return kInvalidObjectHandle;
130 }
131 }
132
133 mFileIdQuery->bind(1, path);
134 if (mFileIdQuery->step()) {
135 int row = mFileIdQuery->getColumnInt(0);
136 if (row > 0)
137 return row;
138 }
139
140 if (!mFileInserter) {
141 mFileInserter = new SqliteStatement(this);
142 if (!mFileInserter->prepare(FILE_INSERT)) {
143 fprintf(stderr, "could not compile FILE_INSERT\n");
144 delete mFileInserter;
145 mFileInserter = NULL;
146 return kInvalidObjectHandle;
147 }
148 }
149 mFileInserter->bind(PATH_COLUMN, path);
150 mFileInserter->bind(FORMAT_COLUMN, format);
151 mFileInserter->bind(PARENT_COLUMN, parent);
152 mFileInserter->bind(STORAGE_COLUMN, storage);
153 mFileInserter->bind(SIZE_COLUMN, size);
154 mFileInserter->bind(CREATED_COLUMN, created);
155 mFileInserter->bind(MODIFIED_COLUMN, modified);
156 mFileInserter->step();
157 mFileInserter->reset();
158 int row = lastInsertedRow();
159 return (row > 0 ? row : kInvalidObjectHandle);
160}
161
162MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
163 MtpObjectFormat format,
164 MtpObjectHandle parent) {
165 bool whereStorage = (storageID != 0xFFFFFFFF);
166 bool whereFormat = (format != 0);
167 bool whereParent = (parent != 0);
168 char intBuffer[20];
169
170 MtpString query("SELECT _id FROM files");
171 if (whereStorage || whereFormat || whereParent)
172 query += " WHERE";
173 if (whereStorage) {
174 snprintf(intBuffer, sizeof(intBuffer), "%d", storageID);
175 query += " storage = ";
176 query += intBuffer;
177 }
178 if (whereFormat) {
179 snprintf(intBuffer, sizeof(intBuffer), "%d", format);
180 if (whereStorage)
181 query += " AND";
182 query += " format = ";
183 query += intBuffer;
184 }
185 if (whereParent) {
186 snprintf(intBuffer, sizeof(intBuffer), "%d", parent);
187 if (whereStorage || whereFormat)
188 query += " AND";
189 query += " parent = ";
190 query += intBuffer;
191 }
192 query += ";";
193
194 SqliteStatement stmt(this);
195 printf("%s\n", (const char *)query);
196 stmt.prepare(query);
197
198 MtpObjectHandleList* list = new MtpObjectHandleList();
199 while (!stmt.isDone()) {
200 if (stmt.step()) {
201 int index = stmt.getColumnInt(0);
202 printf("stmt.getColumnInt returned %d\n", index);
203 if (index > 0)
204 list->push(index);
205 }
206 }
207 printf("list size: %d\n", list->size());
208 return list;
209}
210
211MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle,
212 MtpObjectProperty property,
213 MtpDataPacket& packet) {
214 int type;
215 const char* columnName;
216 char intBuffer[20];
217
218 if (!getPropertyInfo(property, type, columnName))
219 return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
220 snprintf(intBuffer, sizeof(intBuffer), "%d", handle);
221
222 MtpString query("SELECT ");
223 query += columnName;
224 query += " FROM files WHERE _id = ";
225 query += intBuffer;
226 query += ";";
227
228 SqliteStatement stmt(this);
229 printf("%s\n", (const char *)query);
230 stmt.prepare(query);
231
232 if (!stmt.step())
233 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
234
235 switch (type) {
236 case MTP_TYPE_INT8:
237 packet.putInt8(stmt.getColumnInt(0));
238 break;
239 case MTP_TYPE_UINT8:
240 packet.putUInt8(stmt.getColumnInt(0));
241 break;
242 case MTP_TYPE_INT16:
243 packet.putInt16(stmt.getColumnInt(0));
244 break;
245 case MTP_TYPE_UINT16:
246 packet.putUInt16(stmt.getColumnInt(0));
247 break;
248 case MTP_TYPE_INT32:
249 packet.putInt32(stmt.getColumnInt(0));
250 break;
251 case MTP_TYPE_UINT32:
252 packet.putUInt32(stmt.getColumnInt(0));
253 break;
254 case MTP_TYPE_INT64:
255 packet.putInt64(stmt.getColumnInt64(0));
256 break;
257 case MTP_TYPE_UINT64:
258 packet.putUInt64(stmt.getColumnInt64(0));
259 break;
260 case MTP_TYPE_STR:
261 packet.putString(stmt.getColumnString(0));
262 break;
263 default:
264 fprintf(stderr, "unsupported object type\n");
265 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
266 }
267 return MTP_RESPONSE_OK;
268}
269
270MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
271 MtpDataPacket& packet) {
272 char date[20];
273
274 if (mObjectInfoQuery)
275 mObjectInfoQuery->reset();
276 else {
277 mObjectInfoQuery = new SqliteStatement(this);
278 if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
279 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
280 delete mObjectInfoQuery;
281 mObjectInfoQuery = NULL;
282 return MTP_RESPONSE_GENERAL_ERROR;
283 }
284 }
285
286 mObjectInfoQuery->bind(1, handle);
287 if (!mObjectInfoQuery->step())
288 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
289
290 MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0);
291 MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1);
292 MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2);
293 // extract name from path. do we want a separate database entry for this?
294 const char* name = mObjectInfoQuery->getColumnString(3);
295 const char* lastSlash = strrchr(name, '/');
296 if (lastSlash)
297 name = lastSlash + 1;
298 int64_t size = mObjectInfoQuery->getColumnInt64(4);
299 time_t created = mObjectInfoQuery->getColumnInt(5);
300 time_t modified = mObjectInfoQuery->getColumnInt(6);
301 int associationType = (format == MTP_FORMAT_ASSOCIATION ?
302 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
303 MTP_ASSOCIATION_TYPE_UNDEFINED);
304
305 printf("storageID: %d, format: %d, parent: %d\n", storageID, format, parent);
306
307 packet.putUInt32(storageID);
308 packet.putUInt16(format);
309 packet.putUInt16(0); // protection status
310 packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
311 packet.putUInt16(0); // thumb format
312 packet.putUInt32(0); // thumb compressed size
313 packet.putUInt32(0); // thumb pix width
314 packet.putUInt32(0); // thumb pix height
315 packet.putUInt32(0); // image pix width
316 packet.putUInt32(0); // image pix height
317 packet.putUInt32(0); // image bit depth
318 packet.putUInt32(parent);
319 packet.putUInt16(associationType);
320 packet.putUInt32(0); // association desc
321 packet.putUInt32(0); // sequence number
322 packet.putString(name); // file name
323 formatDateTime(created, date, sizeof(date));
324 packet.putString(date); // date created
325 formatDateTime(modified, date, sizeof(date));
326 packet.putString(date); // date modified
327 packet.putEmptyString(); // keywords
328
329 return MTP_RESPONSE_OK;
330}
331
332bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
333 MtpString& filePath,
334 int64_t& fileLength) {
335 if (mFilePathQuery)
336 mFilePathQuery->reset();
337 else {
338 mFilePathQuery = new SqliteStatement(this);
339 if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
340 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
341 delete mFilePathQuery;
342 mFilePathQuery = NULL;
343 return kInvalidObjectHandle;
344 }
345 }
346
347 mFilePathQuery->bind(1, handle);
348 if (!mFilePathQuery->step())
349 return false;
350
351 const char* path = mFilePathQuery->getColumnString(0);
352 if (!path)
353 return false;
354 filePath = path;
355 fileLength = mFilePathQuery->getColumnInt64(1);
356 return true;
357}
358
359bool MtpDatabase::deleteFile(MtpObjectHandle handle) {
360 if (!mFileDeleter) {
361 mFileDeleter = new SqliteStatement(this);
362 if (!mFileDeleter->prepare(FILE_DELETE)) {
363 fprintf(stderr, "could not compile FILE_DELETE\n");
364 delete mFileDeleter;
365 mFileDeleter = NULL;
366 return false;
367 }
368 }
369printf("deleteFile %d\n", handle);
370 mFileDeleter->bind(1, handle);
371 mFileDeleter->step();
372 mFileDeleter->reset();
373 return true;
374}
375
376/*
377 for getObjectPropDesc
378
379 packet.putUInt16(property);
380 packet.putUInt16(dataType);
381 packet.putUInt8(getSet);
382 // default value DTS
383 packet.putUInt32(groupCode);
384 packet.putUInt8(formFlag);
385 // form, variable
386*/