Fix memory leak
[platform/core/multimedia/libmedia-service.git] / src / common / media-svc-media.c
1 /*
2  * libmedia-service
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Hyunjun Ko <zzoon.ko@samsung.com>, Haejeong Kim <backto.kim@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <grp.h>
26 #include <pwd.h>
27 #include <media-util-err.h>
28 #include "media-svc-media.h"
29 #include "media-svc-media-folder.h"
30 #include "media-svc-debug.h"
31 #include "media-svc-util.h"
32 #include "media-svc-db-utils.h"
33 #include "media-svc-noti.h"
34
35 #define GLOBAL_USER    0 /*#define     tzplatform_getenv(TZ_GLOBAL) //TODO */
36
37 typedef struct {
38         char thumbnail_path[MEDIA_SVC_PATHNAME_SIZE];
39 } media_svc_thumbnailpath_s;
40
41 static __thread GList *g_media_svc_item_validity_query_list = NULL;
42 static __thread GList *g_media_svc_insert_item_query_list = NULL;
43 __thread GList *g_media_svc_move_item_query_list = NULL;
44 static __thread GList *g_media_svc_update_item_query_list = NULL;
45
46 static int __media_svc_count_invalid_records_with_thumbnail(sqlite3 *handle, media_svc_storage_type_e storage_type, int *count);
47 static int __media_svc_get_invalid_records_with_thumbnail(sqlite3 *handle, media_svc_storage_type_e storage_type,
48                                                           int count, media_svc_thumbnailpath_s *thumb_path);
49 static int __media_svc_count_invalid_folder_records_with_thumbnail(sqlite3 *handle, const char *folder_path, int *count);
50 static int __media_svc_get_invalid_folder_records_with_thumbnail(sqlite3 *handle, const char *folder_path,
51                                                                  int count, media_svc_thumbnailpath_s *thumb_path);
52
53 static int __media_svc_count_invalid_records_with_thumbnail(sqlite3 *handle, media_svc_storage_type_e storage_type, int *count)
54 {
55         int ret = MS_MEDIA_ERR_NONE;
56         sqlite3_stmt *sql_stmt = NULL;
57         char *sql = sqlite3_mprintf("SELECT count(*) FROM %s WHERE validity=0 AND storage_type=%d AND thumbnail_path IS NOT NULL",
58                                     MEDIA_SVC_DB_TABLE_MEDIA, storage_type);
59
60         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
61
62         if (ret != MS_MEDIA_ERR_NONE) {
63                 media_svc_error("error when __media_svc_count_invalid_records_with_thumbnail. err = [%d]", ret);
64                 return ret;
65         }
66
67         *count = sqlite3_column_int(sql_stmt, 0);
68
69         SQLITE3_FINALIZE(sql_stmt);
70
71         return MS_MEDIA_ERR_NONE;
72
73 }
74
75 static int __media_svc_get_invalid_records_with_thumbnail(sqlite3 *handle, media_svc_storage_type_e storage_type,
76                                                           int count, media_svc_thumbnailpath_s *thumb_path)
77 {
78         int ret = MS_MEDIA_ERR_NONE;
79         sqlite3_stmt *sql_stmt = NULL;
80         int idx = 0;
81
82         char *sql = sqlite3_mprintf("SELECT thumbnail_path from (select thumbnail_path, validity from %s WHERE storage_type=%d AND thumbnail_path IS NOT NULL GROUP BY thumbnail_path HAVING count() = 1) WHERE validity=0",
83                                     MEDIA_SVC_DB_TABLE_MEDIA, storage_type);
84
85         media_svc_debug("[SQL query] : %s", sql);
86
87         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
88         if (ret != MS_MEDIA_ERR_NONE) {
89                 media_svc_error("error when __media_svc_get_invalid_records_with_thumbnail. err = [%d]", ret);
90                 return ret;
91         }
92
93         while (sqlite3_step(sql_stmt) == SQLITE_ROW) {
94                 _strncpy_safe(thumb_path[idx].thumbnail_path, (const char *)sqlite3_column_text(sql_stmt, 0), sizeof(thumb_path[idx]));
95                 /*media_svc_debug("thumb_path[%d]=[%s]", idx, thumb_path[idx].thumbnail_path); */
96                 idx++;
97         }
98
99         SQLITE3_FINALIZE(sql_stmt);
100
101         return MS_MEDIA_ERR_NONE;
102 }
103
104 static int __media_svc_count_invalid_folder_records_with_thumbnail(sqlite3 *handle, const char *folder_path, int *count)
105 {
106         int ret = MS_MEDIA_ERR_NONE;
107         sqlite3_stmt *sql_stmt = NULL;
108         char *sql = sqlite3_mprintf("SELECT count(*) FROM %s WHERE validity=0 AND path LIKE '%q/%%' AND thumbnail_path IS NOT NULL",
109                                     MEDIA_SVC_DB_TABLE_MEDIA, folder_path);
110
111         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
112
113         if (ret != MS_MEDIA_ERR_NONE) {
114                 media_svc_error("error when __media_svc_count_invalid_folder_records_with_thumbnail. err = [%d]", ret);
115                 return ret;
116         }
117
118         *count = sqlite3_column_int(sql_stmt, 0);
119
120         SQLITE3_FINALIZE(sql_stmt);
121
122         return MS_MEDIA_ERR_NONE;
123
124 }
125
126 static int __media_svc_get_invalid_folder_records_with_thumbnail(sqlite3 *handle, const char *folder_path,
127                                                                  int count, media_svc_thumbnailpath_s *thumb_path)
128 {
129         int ret = MS_MEDIA_ERR_NONE;
130         sqlite3_stmt *sql_stmt = NULL;
131         int idx = 0;
132
133         char *sql = sqlite3_mprintf("SELECT thumbnail_path from (select thumbnail_path, validity from %s WHERE path LIKE '%q/%%' AND thumbnail_path IS NOT NULL GROUP BY thumbnail_path HAVING count() = 1) WHERE validity=0",
134                                     MEDIA_SVC_DB_TABLE_MEDIA, folder_path);
135
136         media_svc_debug("[SQL query] : %s", sql);
137
138         ret = _media_svc_sql_prepare_to_step_simple(handle, sql, &sql_stmt);
139         if (ret != MS_MEDIA_ERR_NONE) {
140                 media_svc_error("error when __media_svc_get_invalid_folder_records_with_thumbnail. err = [%d]", ret);
141                 return ret;
142         }
143
144         while (sqlite3_step(sql_stmt) == SQLITE_ROW) {
145                 _strncpy_safe(thumb_path[idx].thumbnail_path, (const char *)sqlite3_column_text(sql_stmt, 0), sizeof(thumb_path[idx]));
146                 idx++;
147         }
148
149         SQLITE3_FINALIZE(sql_stmt);
150
151         return MS_MEDIA_ERR_NONE;
152 }
153
154 int _media_svc_count_record_with_path(sqlite3 *handle, const char *path, int *count)
155 {
156         int ret = MS_MEDIA_ERR_NONE;
157         sqlite3_stmt *sql_stmt = NULL;
158
159         char *sql = sqlite3_mprintf("SELECT count(*) FROM %s WHERE path='%q'", MEDIA_SVC_DB_TABLE_MEDIA, path);
160
161         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
162
163         media_svc_retv_if(ret != MS_MEDIA_ERR_NONE, ret);
164
165         *count = sqlite3_column_int(sql_stmt, 0);
166
167         SQLITE3_FINALIZE(sql_stmt);
168
169         return MS_MEDIA_ERR_NONE;
170 }
171
172 char *_media_svc_get_thumb_default_path(uid_t uid)
173 {
174         char *result_psswd = NULL;
175         struct group *grpinfo = NULL;
176         if (uid == getuid()) {
177                 result_psswd = strdup(MEDIA_SVC_THUMB_DEFAULT_PATH);
178                 grpinfo = getgrnam("users");
179                 if (grpinfo == NULL) {
180                         media_svc_error("getgrnam(users) returns NULL !");
181                         if(result_psswd)
182                                 free(result_psswd);
183                         return NULL;
184                 }
185         } else {
186                 struct passwd *userinfo = getpwuid(uid);
187                 if (userinfo == NULL) {
188                         media_svc_error("getpwuid(%d) returns NULL !", uid);
189                         return NULL;
190                 }
191                 grpinfo = getgrnam("users");
192                 if (grpinfo == NULL) {
193                         media_svc_error("getgrnam(users) returns NULL !");
194                         return NULL;
195                 }
196                 /* Compare git_t type and not group name */
197                 if (grpinfo->gr_gid != userinfo->pw_gid) {
198                         media_svc_error("UID [%d] does not belong to 'users' group!", uid);
199                         return NULL;
200                 }
201                 asprintf(&result_psswd, "%s/data/file-manager-service/.thumb/thumb_default.png", userinfo->pw_dir);
202         }
203
204         return result_psswd;
205 }
206
207 int _media_svc_insert_item_with_data(sqlite3 *handle, media_svc_content_info_s *content_info, int is_burst, bool stack_query, uid_t uid)
208 {
209         int ret = MS_MEDIA_ERR_NONE;
210         char *burst_id = NULL;
211
212         char *db_fields = "media_uuid, path, file_name, media_type, mime_type, size, added_time, modified_time, folder_uuid, \
213                                         thumbnail_path, title, album_id, album, artist, album_artist, genre, composer, year, recorded_date, copyright, track_num, description, \
214                                         bitrate, bitpersample, samplerate, channel, duration, longitude, latitude, altitude, exposure_time, fnumber, iso, model, width, height, datetaken, orientation, \
215                                         rating, is_drm, storage_type, burst_id, timeline, weather, sync_status, \
216                                         file_name_pinyin, title_pinyin, album_pinyin, artist_pinyin, album_artist_pinyin, genre_pinyin, composer_pinyin, copyright_pinyin, description_pinyin, storage_uuid";
217
218         /* This sql is due to sqlite3_mprintf's wrong operation when using floating point in the text format */
219         /* This code will be removed when sqlite3_mprintf works clearly */
220         char *test_sql = sqlite3_mprintf("%f, %f, %f", content_info->media_meta.longitude, content_info->media_meta.latitude, content_info->media_meta.altitude);
221         sqlite3_free(test_sql);
222
223         if (is_burst) {
224                 int burst_id_int = 0;
225                 ret = _media_svc_get_burst_id(handle, &burst_id_int);
226                 if (ret != MS_MEDIA_ERR_NONE) {
227                         burst_id = NULL;
228                 }
229
230                 if (burst_id_int > 0) {
231                         media_svc_debug("Burst id : %d", burst_id_int);
232                         burst_id = sqlite3_mprintf("%d", burst_id_int);
233                 }
234
235                 /* Get thumbnail for burst shot */
236                 char thumb_path[MEDIA_SVC_PATHNAME_SIZE + 1] = {0, };
237                 int width = 0;
238                 int height = 0;
239
240                 ret = _media_svc_request_thumbnail_with_origin_size(content_info->path, thumb_path, sizeof(thumb_path), &width, &height, uid);
241                 if (ret == MS_MEDIA_ERR_NONE) {
242
243                         ret = __media_svc_malloc_and_strncpy(&(content_info->thumbnail_path), thumb_path);
244                         if (ret != MS_MEDIA_ERR_NONE) {
245                                 content_info->thumbnail_path = NULL;
246                         }
247                 }
248
249                 if (content_info->media_meta.width <= 0)
250                         content_info->media_meta.width = width;
251
252                 if (content_info->media_meta.height <= 0)
253                         content_info->media_meta.height = height;
254         }
255
256         /*Update Pinyin If Support Pinyin*/
257         if (_media_svc_check_pinyin_support()) {
258                 if (STRING_VALID(content_info->file_name))
259                         _media_svc_get_pinyin_str(content_info->file_name, &content_info->file_name_pinyin);
260                 if (STRING_VALID(content_info->media_meta.title))
261                         _media_svc_get_pinyin_str(content_info->media_meta.title, &content_info->media_meta.title_pinyin);
262                 if (STRING_VALID(content_info->media_meta.album))
263                         _media_svc_get_pinyin_str(content_info->media_meta.album, &content_info->media_meta.album_pinyin);
264                 if (STRING_VALID(content_info->media_meta.artist))
265                         _media_svc_get_pinyin_str(content_info->media_meta.artist, &content_info->media_meta.artist_pinyin);
266                 if (STRING_VALID(content_info->media_meta.album_artist))
267                         _media_svc_get_pinyin_str(content_info->media_meta.album_artist, &content_info->media_meta.album_artist_pinyin);
268                 if (STRING_VALID(content_info->media_meta.genre))
269                         _media_svc_get_pinyin_str(content_info->media_meta.genre, &content_info->media_meta.genre_pinyin);
270                 if (STRING_VALID(content_info->media_meta.composer))
271                         _media_svc_get_pinyin_str(content_info->media_meta.composer, &content_info->media_meta.composer_pinyin);
272                 if (STRING_VALID(content_info->media_meta.copyright))
273                         _media_svc_get_pinyin_str(content_info->media_meta.copyright, &content_info->media_meta.copyright_pinyin);
274                 if (STRING_VALID(content_info->media_meta.description))
275                         _media_svc_get_pinyin_str(content_info->media_meta.description, &content_info->media_meta.description_pinyin);
276         }
277
278         char *sql = sqlite3_mprintf("INSERT INTO '%s' (%s) VALUES (%Q, %Q, %Q, %d, %Q, %lld, %d, %d, %Q, \
279                                                                                                         %Q, %Q, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, \
280                                                                                                         %d, %d, %d, %d, %d, %.6f, %.6f, %.6f, %Q, %.6f, %d, %Q, %d, %d, %Q, %d, \
281                                                                                                         %d, %d, %d, %Q, %d, %Q, %d, \
282                                                                                                         %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q);",
283                                                                 content_info->storage_uuid, db_fields,
284                                                                 content_info->media_uuid,
285                                                                 content_info->path,
286                                                                 content_info->file_name,
287                                                                 content_info->media_type,
288                                                                 content_info->mime_type,
289                                                                 content_info->size,
290                                                                 content_info->added_time,
291                                                                 content_info->modified_time,
292                                                                 content_info->folder_uuid,
293                                                                 content_info->thumbnail_path,
294                                                                 content_info->media_meta.title,
295                                                                 content_info->album_id,
296                                                                 content_info->media_meta.album,
297                                                                 content_info->media_meta.artist,
298                                                                 content_info->media_meta.album_artist,
299                                                                 content_info->media_meta.genre,
300                                                                 content_info->media_meta.composer,
301                                                                 content_info->media_meta.year,
302                                                                 content_info->media_meta.recorded_date,
303                                                                 content_info->media_meta.copyright,
304                                                                 content_info->media_meta.track_num,
305                                                                 content_info->media_meta.description,
306                                                                 content_info->media_meta.bitrate,
307                                                                 content_info->media_meta.bitpersample,
308                                                                 content_info->media_meta.samplerate,
309                                                                 content_info->media_meta.channel,
310                                                                 content_info->media_meta.duration,
311                                                                 content_info->media_meta.longitude,
312                                                                 content_info->media_meta.latitude,
313                                                                 content_info->media_meta.altitude,
314                                                                 content_info->media_meta.exposure_time,
315                                                                 content_info->media_meta.fnumber,
316                                                                 content_info->media_meta.iso,
317                                                                 content_info->media_meta.model,
318                                                                 content_info->media_meta.width,
319                                                                 content_info->media_meta.height,
320                                                                 content_info->media_meta.datetaken,
321                                                                 content_info->media_meta.orientation,
322                                                                 content_info->media_meta.rating,
323                                                                 content_info->is_drm,
324                                                                 content_info->storage_type,
325                                                                 burst_id,
326                                                                 content_info->timeline,
327                                                                 content_info->media_meta.weather,
328                                                                 content_info->sync_status,
329                                                                 content_info->file_name_pinyin,
330                                                                 content_info->media_meta.title_pinyin,
331                                                                 content_info->media_meta.album_pinyin,
332                                                                 content_info->media_meta.artist_pinyin,
333                                                                 content_info->media_meta.album_artist_pinyin,
334                                                                 content_info->media_meta.genre_pinyin,
335                                                                 content_info->media_meta.composer_pinyin,
336                                                                 content_info->media_meta.copyright_pinyin,
337                                                                 content_info->media_meta.description_pinyin,
338                                                                 content_info->storage_uuid
339                                    );
340
341         if (burst_id) {
342                 sqlite3_free(burst_id);
343                 burst_id = NULL;
344         }
345
346         if (!stack_query) {
347                 ret = _media_svc_sql_query(handle, sql, uid);
348                 sqlite3_free(sql);
349                 if (ret != MS_MEDIA_ERR_NONE) {
350                         media_svc_error("failed to insert item");
351                         return ret;
352                 }
353         } else {
354                 media_svc_debug("query : %s", sql);
355                 _media_svc_sql_query_add(&g_media_svc_insert_item_query_list, &sql);
356         }
357
358         return MS_MEDIA_ERR_NONE;
359 }
360
361 int _media_svc_update_meta_with_data(sqlite3 *handle, media_svc_content_info_s *content_info)
362 {
363         int ret = MS_MEDIA_ERR_NONE;
364
365         /* This sql is due to sqlite3_mprintf's wrong operation when using floating point in the text format */
366         /* This code will be removed when sqlite3_mprintf works clearly */
367         char *test_sql = sqlite3_mprintf("%f, %f, %f", content_info->media_meta.longitude, content_info->media_meta.latitude, content_info->media_meta.altitude);
368         sqlite3_free(test_sql);
369
370         /*Update Pinyin If Support Pinyin*/
371         if (_media_svc_check_pinyin_support()) {
372                 if (STRING_VALID(content_info->file_name))
373                         _media_svc_get_pinyin_str(content_info->file_name, &content_info->file_name_pinyin);
374                 if (STRING_VALID(content_info->media_meta.title))
375                         _media_svc_get_pinyin_str(content_info->media_meta.title, &content_info->media_meta.title_pinyin);
376                 if (STRING_VALID(content_info->media_meta.album))
377                         _media_svc_get_pinyin_str(content_info->media_meta.album, &content_info->media_meta.album_pinyin);
378                 if (STRING_VALID(content_info->media_meta.artist))
379                         _media_svc_get_pinyin_str(content_info->media_meta.artist, &content_info->media_meta.artist_pinyin);
380                 if (STRING_VALID(content_info->media_meta.album_artist))
381                         _media_svc_get_pinyin_str(content_info->media_meta.album_artist, &content_info->media_meta.album_artist_pinyin);
382                 if (STRING_VALID(content_info->media_meta.genre))
383                         _media_svc_get_pinyin_str(content_info->media_meta.genre, &content_info->media_meta.genre_pinyin);
384                 if (STRING_VALID(content_info->media_meta.composer))
385                         _media_svc_get_pinyin_str(content_info->media_meta.composer, &content_info->media_meta.composer_pinyin);
386                 if (STRING_VALID(content_info->media_meta.copyright))
387                         _media_svc_get_pinyin_str(content_info->media_meta.copyright, &content_info->media_meta.copyright_pinyin);
388                 if (STRING_VALID(content_info->media_meta.description))
389                         _media_svc_get_pinyin_str(content_info->media_meta.description, &content_info->media_meta.description_pinyin);
390         }
391
392         char *sql = sqlite3_mprintf("UPDATE %s SET title=%Q, album=%Q, artist=%Q, album_artist=%Q, genre=%Q, composer=%Q, copyright=%Q, description=%Q, \
393                 file_name_pinyin=%Q, title_pinyin=%Q, album_pinyin=%Q, artist_pinyin=%Q, album_artist_pinyin=%Q, genre_pinyin=%Q, composer_pinyin=%Q, copyright_pinyin=%Q, description_pinyin=%Q \
394                 WHERE path=%Q",
395                                                                 MEDIA_SVC_DB_TABLE_MEDIA,
396                                                                 content_info->media_meta.title,
397                                                                 content_info->media_meta.album,
398                                                                 content_info->media_meta.artist,
399                                                                 content_info->media_meta.album_artist,
400                                                                 content_info->media_meta.genre,
401                                                                 content_info->media_meta.composer,
402                                                                 content_info->media_meta.copyright,
403                                                                 content_info->media_meta.description,
404                                                                 content_info->file_name_pinyin,
405                                                                 content_info->media_meta.title_pinyin,
406                                                                 content_info->media_meta.album_pinyin,
407                                                                 content_info->media_meta.artist_pinyin,
408                                                                 content_info->media_meta.album_artist_pinyin,
409                                                                 content_info->media_meta.genre_pinyin,
410                                                                 content_info->media_meta.composer_pinyin,
411                                                                 content_info->media_meta.copyright_pinyin,
412                                                                 content_info->media_meta.description_pinyin,
413                                                                 content_info->path
414                                    );
415
416         media_svc_error("");
417         if (sql != NULL) {
418                 media_svc_debug("query : %s", sql);
419                 _media_svc_sql_query_add(&g_media_svc_update_item_query_list, &sql);
420         } else {
421                 media_svc_error("sqlite3_mprintf failed");
422                 ret = MS_MEDIA_ERR_OUT_OF_MEMORY;
423         }
424
425         media_svc_error("");
426
427         return ret;
428 }
429
430 int _media_svc_update_item_with_data(sqlite3 *handle, const char *storage_id, media_svc_content_info_s *content_info, uid_t uid)
431 {
432         int ret = MS_MEDIA_ERR_NONE;
433
434         /* This sql is due to sqlite3_mprintf's wrong operation when using floating point in the text format */
435         /* This code will be removed when sqlite3_mprintf works clearly */
436         char *test_sql = sqlite3_mprintf("%f, %f, %f", content_info->media_meta.longitude, content_info->media_meta.latitude, content_info->media_meta.altitude);
437         sqlite3_free(test_sql);
438
439         /*Update Pinyin If Support Pinyin*/
440         if (_media_svc_check_pinyin_support()) {
441                 if (STRING_VALID(content_info->file_name))
442                         _media_svc_get_pinyin_str(content_info->file_name, &content_info->file_name_pinyin);
443                 if (STRING_VALID(content_info->media_meta.title))
444                         _media_svc_get_pinyin_str(content_info->media_meta.title, &content_info->media_meta.title_pinyin);
445                 if (STRING_VALID(content_info->media_meta.album))
446                         _media_svc_get_pinyin_str(content_info->media_meta.album, &content_info->media_meta.album_pinyin);
447                 if (STRING_VALID(content_info->media_meta.artist))
448                         _media_svc_get_pinyin_str(content_info->media_meta.artist, &content_info->media_meta.artist_pinyin);
449                 if (STRING_VALID(content_info->media_meta.album_artist))
450                         _media_svc_get_pinyin_str(content_info->media_meta.album_artist, &content_info->media_meta.album_artist_pinyin);
451                 if (STRING_VALID(content_info->media_meta.genre))
452                         _media_svc_get_pinyin_str(content_info->media_meta.genre, &content_info->media_meta.genre_pinyin);
453                 if (STRING_VALID(content_info->media_meta.composer))
454                         _media_svc_get_pinyin_str(content_info->media_meta.composer, &content_info->media_meta.composer_pinyin);
455                 if (STRING_VALID(content_info->media_meta.copyright))
456                         _media_svc_get_pinyin_str(content_info->media_meta.copyright, &content_info->media_meta.copyright_pinyin);
457                 if (STRING_VALID(content_info->media_meta.description))
458                         _media_svc_get_pinyin_str(content_info->media_meta.description, &content_info->media_meta.description_pinyin);
459         }
460
461         char *sql = sqlite3_mprintf("UPDATE '%s' SET \
462                 size=%lld, modified_time=%d, thumbnail_path=%Q, title=%Q, album_id=%d, album=%Q, artist=%Q, album_artist=%Q, genre=%Q, \
463                 composer=%Q, year=%Q, recorded_date=%Q, copyright=%Q, track_num=%Q, description=%Q, \
464                 bitrate=%d, bitpersample=%d, samplerate=%d, channel=%d, duration=%d, longitude=%f, latitude=%f, altitude=%f, exposure_time=%Q, fnumber=%f, iso=%d, model=%Q, width=%d, height=%d, datetaken=%Q, \
465                                                                                                         orientation=%d WHERE path=%Q",
466                                                                 storage_id,
467                                                                 content_info->size,
468                                                                 content_info->modified_time,
469                                                                 content_info->thumbnail_path,
470                                                                 content_info->media_meta.title,
471                                                                 content_info->album_id,
472                                                                 content_info->media_meta.album,
473                                                                 content_info->media_meta.artist,
474                                                                 content_info->media_meta.album_artist,
475                                                                 content_info->media_meta.genre,
476                                                                 content_info->media_meta.composer,
477                                                                 content_info->media_meta.year,
478                                                                 content_info->media_meta.recorded_date,
479                                                                 content_info->media_meta.copyright,
480                                                                 content_info->media_meta.track_num,
481                                                                 content_info->media_meta.description,
482                                                                 content_info->media_meta.bitrate,
483                                                                 content_info->media_meta.bitpersample,
484                                                                 content_info->media_meta.samplerate,
485                                                                 content_info->media_meta.channel,
486                                                                 content_info->media_meta.duration,
487                                                                 content_info->media_meta.longitude,
488                                                                 content_info->media_meta.latitude,
489                                                                 content_info->media_meta.altitude,
490                                                                 content_info->media_meta.exposure_time,
491                                                                 content_info->media_meta.fnumber,
492                                                                 content_info->media_meta.iso,
493                                                                 content_info->media_meta.model,
494                                                                 content_info->media_meta.width,
495                                                                 content_info->media_meta.height,
496                                                                 content_info->media_meta.datetaken,
497                                                                 content_info->media_meta.orientation,
498                                                                 content_info->path
499                                    );
500
501         ret = _media_svc_sql_query(handle, sql, uid);
502         sqlite3_free(sql);
503
504         return ret;
505 }
506 int _media_svc_get_thumbnail_path_by_path(sqlite3 *handle, const char *storage_id, const char *path, char *thumbnail_path)
507 {
508         int ret = MS_MEDIA_ERR_NONE;
509         sqlite3_stmt *sql_stmt = NULL;
510
511         char *sql = sqlite3_mprintf("SELECT thumbnail_path FROM '%s' WHERE path='%q'", storage_id, path);
512
513         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
514
515         if (ret != MS_MEDIA_ERR_NONE) {
516                 if (ret == MS_MEDIA_ERR_DB_NO_RECORD) {
517                         media_svc_debug("there is no thumbnail.");
518                 } else {
519                         media_svc_error("error when _media_svc_get_thumbnail_path_by_path. err = [%d]", ret);
520                 }
521                 return ret;
522         }
523
524         _strncpy_safe(thumbnail_path, (const char *)sqlite3_column_text(sql_stmt, 0), MEDIA_SVC_PATHNAME_SIZE);
525
526         SQLITE3_FINALIZE(sql_stmt);
527
528         return MS_MEDIA_ERR_NONE;
529 }
530
531 int _media_svc_get_media_type_by_path(sqlite3 *handle, const char *storage_id, const char *path, int *media_type)
532 {
533         int ret = MS_MEDIA_ERR_NONE;
534         sqlite3_stmt *sql_stmt = NULL;
535
536         char *sql = sqlite3_mprintf("SELECT media_type FROM '%s' WHERE path='%q'", storage_id, path);
537
538         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
539
540         if (ret != MS_MEDIA_ERR_NONE) {
541                 media_svc_error("error when _media_svc_get_media_type_by_path. err = [%d]", ret);
542                 return ret;
543         }
544
545         *media_type = sqlite3_column_int(sql_stmt, 0);
546
547         SQLITE3_FINALIZE(sql_stmt);
548
549         return MS_MEDIA_ERR_NONE;
550 }
551
552 int _media_svc_delete_item_by_path(sqlite3 *handle, const char *storage_id, const char *path, bool stack_query, uid_t uid)
553 {
554         int ret = MS_MEDIA_ERR_NONE;
555         char *sql = sqlite3_mprintf("DELETE FROM '%s' WHERE path='%q'", storage_id, path);
556
557         if (!stack_query) {
558                 ret = _media_svc_sql_query(handle, sql, uid);
559                 sqlite3_free(sql);
560                 if (ret != MS_MEDIA_ERR_NONE) {
561                         media_svc_error("failed to delete item");
562                         return ret;
563                 }
564         } else {
565                 media_svc_debug("query : %s", sql);
566                 _media_svc_sql_query_add(&g_media_svc_insert_item_query_list, &sql);
567         }
568
569         return ret;
570 }
571
572 int _media_svc_truncate_table(sqlite3 *handle, media_svc_storage_type_e storage_type, uid_t uid)
573 {
574         int ret = MS_MEDIA_ERR_NONE;
575         char *sql = sqlite3_mprintf("DELETE FROM %s WHERE storage_type=%d", MEDIA_SVC_DB_TABLE_MEDIA, storage_type);
576
577         ret = _media_svc_sql_query(handle, sql, uid);
578         sqlite3_free(sql);
579
580         return ret;
581 }
582
583 int _media_svc_delete_invalid_items(sqlite3 *handle, media_svc_storage_type_e storage_type, uid_t uid)
584 {
585         int idx = 0;
586         media_svc_thumbnailpath_s *thumbpath_record = NULL;
587         int invalid_count = 0;
588         int ret = MS_MEDIA_ERR_NONE;
589
590         ret = __media_svc_count_invalid_records_with_thumbnail(handle, storage_type, &invalid_count);
591         media_svc_retv_if(ret != MS_MEDIA_ERR_NONE, ret);
592
593         media_svc_debug("invalid count: %d", invalid_count);
594
595         if (invalid_count > 0) {
596                 thumbpath_record = (media_svc_thumbnailpath_s *)calloc(invalid_count, sizeof(media_svc_thumbnailpath_s));
597                 if (thumbpath_record == NULL) {
598                         media_svc_error("fail to memory allocation");
599                         return MS_MEDIA_ERR_OUT_OF_MEMORY;
600                 }
601
602                 ret = __media_svc_get_invalid_records_with_thumbnail(handle, storage_type, invalid_count, thumbpath_record);
603                 if (ret != MS_MEDIA_ERR_NONE) {
604                         media_svc_error("error when get thumbnail record");
605                         SAFE_FREE(thumbpath_record);
606                         return ret;
607                 }
608         } else {
609                 media_svc_debug("There is no item with thumbnail");
610         }
611
612         char *sql = sqlite3_mprintf("DELETE FROM %s WHERE validity = 0 AND storage_type=%d", MEDIA_SVC_DB_TABLE_MEDIA, storage_type);
613         ret = _media_svc_sql_query(handle, sql, uid);
614         sqlite3_free(sql);
615         if (ret != MS_MEDIA_ERR_NONE) {
616                 SAFE_FREE(thumbpath_record);
617                 return ret;
618         }
619
620         /*Delete thumbnails*/
621         for (idx = 0; idx < invalid_count; idx++) {
622                 if ((strlen(thumbpath_record[idx].thumbnail_path) > 0) && (strncmp(thumbpath_record[idx].thumbnail_path, _media_svc_get_thumb_default_path(uid), sizeof(_media_svc_get_thumb_default_path(uid))) != 0)) {
623                         ret = _media_svc_remove_file(thumbpath_record[idx].thumbnail_path);
624                         if (ret != MS_MEDIA_ERR_NONE) {
625                                 media_svc_error("fail to remove thumbnail file.");
626                         }
627                 }
628         }
629
630         SAFE_FREE(thumbpath_record);
631
632         return MS_MEDIA_ERR_NONE;
633 }
634
635 int _media_svc_delete_invalid_folder_items(sqlite3 *handle, const char *folder_path, uid_t uid)
636 {
637         int idx = 0;
638         media_svc_thumbnailpath_s *thumbpath_record = NULL;
639         int invalid_count = 0;
640         int ret = MS_MEDIA_ERR_NONE;
641
642         ret = __media_svc_count_invalid_folder_records_with_thumbnail(handle, folder_path, &invalid_count);
643         media_svc_retv_if(ret != MS_MEDIA_ERR_NONE, ret);
644
645         media_svc_debug("invalid count: %d", invalid_count);
646
647         if (invalid_count > 0) {
648                 thumbpath_record = (media_svc_thumbnailpath_s *)calloc(invalid_count, sizeof(media_svc_thumbnailpath_s));
649                 if (thumbpath_record == NULL) {
650                         media_svc_error("fail to memory allocation");
651                         return MS_MEDIA_ERR_OUT_OF_MEMORY;
652                 }
653
654                 ret = __media_svc_get_invalid_folder_records_with_thumbnail(handle, folder_path, invalid_count, thumbpath_record);
655                 if (ret != MS_MEDIA_ERR_NONE) {
656                         media_svc_error("error when get thumbnail record");
657                         SAFE_FREE(thumbpath_record);
658                         return ret;
659                 }
660         } else {
661                 media_svc_debug("There is no item with thumbnail");
662         }
663
664         char *sql = sqlite3_mprintf("DELETE FROM %s WHERE validity = 0 AND path LIKE '%q/%%'", MEDIA_SVC_DB_TABLE_MEDIA, folder_path);
665         ret = _media_svc_sql_query(handle, sql, uid);
666         sqlite3_free(sql);
667         if (ret != MS_MEDIA_ERR_NONE) {
668                 SAFE_FREE(thumbpath_record);
669                 return ret;
670         }
671
672         /*Delete thumbnails*/
673         for (idx = 0; idx < invalid_count; idx++) {
674                 if ((strlen(thumbpath_record[idx].thumbnail_path) > 0) && (strncmp(thumbpath_record[idx].thumbnail_path, _media_svc_get_thumb_default_path(uid), sizeof(_media_svc_get_thumb_default_path(uid))) != 0)) {
675                         ret = _media_svc_remove_file(thumbpath_record[idx].thumbnail_path);
676                         if (ret != MS_MEDIA_ERR_NONE) {
677                                 media_svc_error("fail to remove thumbnail file.");
678                         }
679                 }
680         }
681
682         SAFE_FREE(thumbpath_record);
683
684         return MS_MEDIA_ERR_NONE;
685 }
686
687 int _media_svc_update_item_validity(sqlite3 *handle, const char *path, int validity, bool stack_query, uid_t uid)
688 {
689         int ret = MS_MEDIA_ERR_NONE;
690
691         char *sql = sqlite3_mprintf("UPDATE %s SET validity=%d WHERE path= '%q'", MEDIA_SVC_DB_TABLE_MEDIA, validity, path);
692
693         if (!stack_query) {
694                 ret = _media_svc_sql_query(handle, sql, uid);
695                 sqlite3_free(sql);
696         } else {
697                 _media_svc_sql_query_add(&g_media_svc_item_validity_query_list, &sql);
698         }
699
700         return ret;
701 }
702
703 int _media_svc_update_thumbnail_path(sqlite3 *handle,  const char *storage_id, const char *path, const char *thumb_path, uid_t uid)
704 {
705         int ret = MS_MEDIA_ERR_NONE;
706
707         char *sql = sqlite3_mprintf("UPDATE '%s' SET thumbnail_path=%Q WHERE path= %Q", storage_id, thumb_path, path);
708
709         ret = _media_svc_sql_query(handle, sql, uid);
710         sqlite3_free(sql);
711
712         return ret;
713 }
714
715 int _media_svc_update_storage_item_validity(sqlite3 *handle, media_svc_storage_type_e storage_type, int validity, uid_t uid)
716 {
717         int ret = MS_MEDIA_ERR_NONE;
718
719         char *sql = sqlite3_mprintf("UPDATE %s SET validity=%d WHERE storage_type=%d", MEDIA_SVC_DB_TABLE_MEDIA, validity, storage_type);
720
721         ret = _media_svc_sql_query(handle, sql, uid);
722         sqlite3_free(sql);
723
724         return ret;
725 }
726
727 int _media_svc_update_folder_item_validity(sqlite3 *handle, const char *folder_path, int validity, uid_t uid)
728 {
729         int ret = MS_MEDIA_ERR_NONE;
730         char *sql = NULL;
731         char folder_uuid[MEDIA_SVC_UUID_SIZE + 1] = {0, };
732         sqlite3_stmt *sql_stmt = NULL;
733
734         /*Get folder ID*/
735         sql = sqlite3_mprintf("SELECT folder_uuid FROM %s WHERE path='%q'", MEDIA_SVC_DB_TABLE_FOLDER, folder_path);
736         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
737         if (ret != MS_MEDIA_ERR_NONE) {
738                 if (ret == MS_MEDIA_ERR_DB_NO_RECORD)
739                         media_svc_debug("folder not exist");
740                 else
741                         media_svc_error("error when get folder_id. err = [%d]", ret);
742
743                 return ret;
744         }
745
746         _strncpy_safe(folder_uuid, (const char *)sqlite3_column_text(sql_stmt, 0), MEDIA_SVC_UUID_SIZE + 1);
747         SQLITE3_FINALIZE(sql_stmt);
748
749         /*Update folder item validity*/
750         sql = sqlite3_mprintf("UPDATE %s SET validity=%d WHERE (storage_type = 0 OR storage_type = 1) AND folder_uuid='%q'", MEDIA_SVC_DB_TABLE_MEDIA, validity, folder_uuid);
751         ret = _media_svc_sql_query(handle, sql, uid);
752         sqlite3_free(sql);
753
754         return ret;
755 }
756
757 int _media_svc_update_recursive_folder_item_validity(sqlite3 *handle, const char *folder_path, int validity, uid_t uid)
758 {
759         int ret = MS_MEDIA_ERR_NONE;
760
761         /*Update folder item validity*/
762         char *sql = sqlite3_mprintf("UPDATE %s SET validity=%d WHERE (storage_type = 0 OR storage_type = 1) AND path LIKE '%q/%%'", MEDIA_SVC_DB_TABLE_MEDIA, validity, folder_path);
763
764         ret = _media_svc_sql_query(handle, sql, uid);
765         sqlite3_free(sql);
766
767         return ret;
768 }
769
770 int _media_svc_update_item_by_path(sqlite3 *handle, const char *src_path, media_svc_storage_type_e dest_storage, const char *dest_path,
771                                    const char *file_name, int modified_time, const char *folder_uuid, const char *thumb_path, bool stack_query, uid_t uid)
772 {
773         /* update path, filename, modified_time, folder_uuid, thumbnail_path, */
774         /* played_count, last_played_time, last_played_position, favourite, storaget_type*/
775
776         int ret = MS_MEDIA_ERR_NONE;
777         char *sql = NULL;
778
779         if (thumb_path != NULL) {
780                 sql = sqlite3_mprintf("UPDATE %s SET \
781                                         path=%Q, file_name=%Q, modified_time=%d, folder_uuid=%Q, thumbnail_path=%Q, storage_type=%d, \
782                                         played_count=0, last_played_time=0, last_played_position=0 \
783                                         WHERE path=%Q",
784                                       MEDIA_SVC_DB_TABLE_MEDIA, dest_path, file_name, modified_time, folder_uuid, thumb_path, dest_storage, src_path);
785         } else {
786                 sql = sqlite3_mprintf("UPDATE %s SET \
787                                         path=%Q, file_name=%Q, modified_time=%d, folder_uuid=%Q, storage_type=%d, \
788                                         played_count=0, last_played_time=0, last_played_position=0 \
789                                         WHERE path=%Q",
790                                       MEDIA_SVC_DB_TABLE_MEDIA, dest_path, file_name, modified_time, folder_uuid, dest_storage, src_path);
791         }
792
793         if (!stack_query) {
794                 ret = _media_svc_sql_query(handle, sql, uid);
795                 sqlite3_free(sql);
796         } else {
797                 _media_svc_sql_query_add(&g_media_svc_move_item_query_list, &sql);
798         }
799
800         return ret;
801 }
802
803 int _media_svc_list_query_do(sqlite3 *handle, media_svc_query_type_e query_type, uid_t uid)
804 {
805         int ret = MS_MEDIA_ERR_NONE;
806
807         ret = _media_svc_sql_begin_trans(handle, uid);
808         media_svc_retv_if(ret != MS_MEDIA_ERR_NONE, ret);
809
810         if (query_type == MEDIA_SVC_QUERY_SET_ITEM_VALIDITY)
811                 ret = _media_svc_sql_query_list(handle, &g_media_svc_item_validity_query_list, uid);
812         else if (query_type == MEDIA_SVC_QUERY_MOVE_ITEM)
813                 ret = _media_svc_sql_query_list(handle, &g_media_svc_move_item_query_list, uid);
814         else if (query_type == MEDIA_SVC_QUERY_INSERT_ITEM)
815                 ret = _media_svc_sql_query_list(handle, &g_media_svc_insert_item_query_list, uid);
816         else
817                 ret = MS_MEDIA_ERR_INVALID_PARAMETER;
818
819         if (ret != MS_MEDIA_ERR_NONE) {
820                 media_svc_error("_media_svc_list_query_do failed. start rollback");
821                 _media_svc_sql_rollback_trans(handle, uid);
822                 return ret;
823         }
824
825         ret = _media_svc_sql_end_trans(handle, uid);
826         if (ret != MS_MEDIA_ERR_NONE) {
827                 media_svc_error("mb_svc_sqlite3_commit_trans failed.. Now start to rollback");
828                 _media_svc_sql_rollback_trans(handle, uid);
829                 return ret;
830         }
831
832         return MS_MEDIA_ERR_NONE;
833 }
834
835 int _media_svc_get_burst_id(sqlite3 *handle, int *id)
836 {
837         int ret = MS_MEDIA_ERR_NONE;
838         int cur_id = -1;
839         sqlite3_stmt *sql_stmt = NULL;
840         char *sql = sqlite3_mprintf("SELECT max(CAST(burst_id AS INTEGER)) FROM %s", MEDIA_SVC_DB_TABLE_MEDIA);
841
842         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
843
844         if (ret != MS_MEDIA_ERR_NONE) {
845                 media_svc_error("error when _media_svc_get_burst_id. err = [%d]", ret);
846                 return ret;
847         }
848
849         cur_id = sqlite3_column_int(sql_stmt, 0);
850         *id = ++cur_id;
851         SQLITE3_FINALIZE(sql_stmt);
852
853         return MS_MEDIA_ERR_NONE;
854 }
855
856 int _media_svc_get_noti_info(sqlite3 *handle,  const char *storage_id, const char *path, int update_item, media_svc_noti_item **item)
857 {
858         int ret = MS_MEDIA_ERR_NONE;
859         sqlite3_stmt *sql_stmt = NULL;
860         char *sql = NULL;
861         int is_root_dir = FALSE;
862
863         if (item == NULL) {
864                 media_svc_error("invalid parameter");
865                 return MS_MEDIA_ERR_INVALID_PARAMETER;
866         }
867
868         if (update_item == MS_MEDIA_ITEM_FILE) {
869                 sql = sqlite3_mprintf("SELECT media_uuid, media_type, mime_type FROM '%s' WHERE path=%Q", storage_id, path);
870         } else if (update_item == MS_MEDIA_ITEM_DIRECTORY) {
871                 sql = sqlite3_mprintf("SELECT folder_uuid FROM '%s' WHERE path=%Q AND storage_uuid='%s'", MEDIA_SVC_DB_TABLE_FOLDER, path, storage_id);
872         } else {
873                 media_svc_error("_media_svc_get_noti_info failed : update item");
874                 return MS_MEDIA_ERR_INVALID_PARAMETER;
875         }
876
877         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
878
879         if (ret != MS_MEDIA_ERR_NONE) {
880                 if (ret == MS_MEDIA_ERR_DB_NO_RECORD && update_item == MS_MEDIA_ITEM_DIRECTORY) {
881                         media_svc_debug("This is root directory of media");
882                         sql_stmt = NULL;
883                         is_root_dir = TRUE;
884                 } else {
885                         media_svc_error("error when _media_svc_get_noti_info. err = [%d]", ret);
886                         return ret;
887                 }
888         }
889
890         *item = calloc(1, sizeof(media_svc_noti_item));
891         if (*item == NULL) {
892                 media_svc_error("_media_svc_get_noti_info failed : calloc");
893                 return MS_MEDIA_ERR_OUT_OF_MEMORY;
894         }
895
896         if (update_item == MS_MEDIA_ITEM_FILE) {
897                 if (STRING_VALID((const char *)sqlite3_column_text(sql_stmt, 0)))
898                         (*item)->media_uuid = strdup((const char *)sqlite3_column_text(sql_stmt, 0));
899
900                 (*item)->media_type = sqlite3_column_int(sql_stmt, 1);
901
902                 if (STRING_VALID((const char *)sqlite3_column_text(sql_stmt, 2)))
903                         (*item)->mime_type = strdup((const char *)sqlite3_column_text(sql_stmt, 2));
904         } else if (update_item == MS_MEDIA_ITEM_DIRECTORY) {
905                 if (is_root_dir) {
906                         (*item)->media_uuid = NULL;
907                 } else {
908                         if (STRING_VALID((const char *)sqlite3_column_text(sql_stmt, 0)))
909                                 (*item)->media_uuid = strdup((const char *)sqlite3_column_text(sql_stmt, 0));
910                 }
911         }
912
913         SQLITE3_FINALIZE(sql_stmt);
914
915         return MS_MEDIA_ERR_NONE;
916 }
917
918 int _media_svc_count_invalid_folder_items(sqlite3 *handle, const char *folder_path, int *count)
919 {
920         int ret = MS_MEDIA_ERR_NONE;
921         sqlite3_stmt *sql_stmt = NULL;
922         char *sql = sqlite3_mprintf("SELECT count(*) FROM %s WHERE validity=0 AND path LIKE '%q/%%'",
923                                     MEDIA_SVC_DB_TABLE_MEDIA, folder_path);
924
925         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
926
927         if (ret != MS_MEDIA_ERR_NONE) {
928                 media_svc_error("error when _media_svc_count_invalid_folder_items. err = [%d]", ret);
929                 return ret;
930         }
931
932         *count = sqlite3_column_int(sql_stmt, 0);
933
934         SQLITE3_FINALIZE(sql_stmt);
935
936         return MS_MEDIA_ERR_NONE;
937 }
938
939 int _media_svc_get_thumbnail_count(sqlite3 *handle, const char *thumb_path, int *count)
940 {
941         int ret = MS_MEDIA_ERR_NONE;
942         sqlite3_stmt *sql_stmt = NULL;
943         char *sql = sqlite3_mprintf("SELECT count(*) FROM %s WHERE thumbnail_path=%Q", MEDIA_SVC_DB_TABLE_MEDIA, thumb_path);
944
945         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
946
947         if (ret != MS_MEDIA_ERR_NONE) {
948                 media_svc_error("error when _media_svc_get_thumbnail_count. err = [%d]", ret);
949                 return ret;
950         }
951
952         *count = sqlite3_column_int(sql_stmt, 0);
953
954         SQLITE3_FINALIZE(sql_stmt);
955
956         return MS_MEDIA_ERR_NONE;
957 }
958
959 int _media_svc_get_fileinfo_by_path(sqlite3 *handle, const char *path, time_t *modified_time, unsigned long long *size)
960 {
961         int ret = MS_MEDIA_ERR_NONE;
962         sqlite3_stmt *sql_stmt = NULL;
963         char *sql = sqlite3_mprintf("SELECT modified_time, size FROM %s WHERE path='%q'",
964                                     MEDIA_SVC_DB_TABLE_MEDIA, path);
965
966         ret = _media_svc_sql_prepare_to_step(handle, sql, &sql_stmt);
967
968         if (ret != MS_MEDIA_ERR_NONE) {
969                 media_svc_error("error when _media_svc_get_fileinfo_by_path. err = [%d]", ret);
970                 return ret;
971         }
972
973         *modified_time = (int)sqlite3_column_int(sql_stmt, 0);
974         *size = (unsigned long long)sqlite3_column_int64(sql_stmt, 1);
975
976         SQLITE3_FINALIZE(sql_stmt);
977
978         return MS_MEDIA_ERR_NONE;
979 }