summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorChristian Soffke <christian.soffke@gmail.com>2024-07-29 21:11:27 +0200
committerChristian Soffke <christian.soffke@gmail.com>2024-07-30 16:07:40 -0400
commitcb7ae38fcd051b95fcc6513f91e861902ead76a7 (patch)
tree21b4e33cd6d8bacbb336efa3de329c14b92541ba /apps/plugins
parenta430120b3ff7cadd57ee0367b150a2f828099d9b (diff)
downloadrockbox-cb7ae38fcd051b95fcc6513f91e861902ead76a7.tar.gz
rockbox-cb7ae38fcd051b95fcc6513f91e861902ead76a7.zip
plugins: properties: fix and refactor
- prevent buffer overflow that could theoretically happen if an input file's dirname were larger than sizeof(str_dirname) - remove has_pl_extension, use existing filetype_get_attr - move determination of props type to single function, and eliminate need for redundantly reading the container dir for a file Change-Id: Ice7cfcb9d891d22a8eb549439569dda126435bc2
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/properties.c283
1 files changed, 109 insertions, 174 deletions
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index 4647216390..d3fac59bde 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -33,25 +33,17 @@ enum props_types {
33 PROPS_DIR 33 PROPS_DIR
34}; 34};
35 35
36static int props_type;
37
38static struct gui_synclist properties_lists; 36static struct gui_synclist properties_lists;
39static struct mp3entry id3; 37static struct mp3entry id3;
40static int mul_id3_count; 38static struct tm tm;
41static int skipped_count;
42
43static char str_filename[MAX_PATH];
44static char str_dirname[MAX_PATH];
45static char str_size[64];
46static char str_dircount[64];
47static char str_filecount[64];
48static char str_audio_filecount[64];
49static char str_date[64];
50static char str_time[64];
51
52static unsigned long display_size; 39static unsigned long display_size;
53static int32_t lang_size_unit; 40static int32_t lang_size_unit;
54static struct tm tm; 41static int props_type, mul_id3_count, skipped_count;
42
43static char str_filename[MAX_PATH], str_dirname[MAX_PATH],
44 str_size[64], str_dircount[64], str_filecount[64],
45 str_audio_filecount[64], str_date[64], str_time[64];
46
55 47
56#define NUM_FILE_PROPERTIES 5 48#define NUM_FILE_PROPERTIES 5
57#define NUM_PLAYLIST_PROPERTIES (1 + NUM_FILE_PROPERTIES) 49#define NUM_PLAYLIST_PROPERTIES (1 + NUM_FILE_PROPERTIES)
@@ -78,71 +70,28 @@ static const unsigned char* const props_dir[] =
78 ID2P(LANG_MENU_SHOW_ID3_INFO), str_audio_filecount, 70 ID2P(LANG_MENU_SHOW_ID3_INFO), str_audio_filecount,
79}; 71};
80 72
81static bool file_properties(const char* selected_file) 73static bool dir_properties(const char* selected_file, struct dir_stats *stats)
82{
83 bool found = false;
84 struct dirent* entry;
85 DIR* dir = rb->opendir(str_dirname);
86 if (dir)
87 {
88 while(0 != (entry = rb->readdir(dir)))
89 {
90 struct dirinfo info = rb->dir_get_info(dir, entry);
91 if(!rb->strcmp(entry->d_name, str_filename))
92 {
93 display_size = human_size(info.size, &lang_size_unit);
94 rb->snprintf(str_size, sizeof str_size, "%lu %s",
95 display_size, rb->str(lang_size_unit));
96 rb->gmtime_r(&info.mtime, &tm);
97 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
98 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
99 rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
100 tm.tm_hour, tm.tm_min, tm.tm_sec);
101
102 if (props_type != PROPS_PLAYLIST && rb->get_metadata(&id3, -1, selected_file))
103 props_type = PROPS_ID3;
104 found = true;
105 break;
106 }
107 }
108 rb->closedir(dir);
109 }
110 return found;
111}
112
113
114static bool dir_properties(const char* selected_file, struct dir_stats *stats,
115 bool (*id3_cb)(const char*))
116{ 74{
117 bool success;
118
119 rb->strlcpy(stats->dirname, selected_file, sizeof(stats->dirname)); 75 rb->strlcpy(stats->dirname, selected_file, sizeof(stats->dirname));
120 if (id3_cb)
121 rb->splash_progress_set_delay(HZ / 2); /* hide progress bar for 0.5s */
122 76
123#ifdef HAVE_ADJUSTABLE_CPU_FREQ 77#ifdef HAVE_ADJUSTABLE_CPU_FREQ
124 rb->cpu_boost(true); 78 rb->cpu_boost(true);
125#endif 79#endif
126 success = collect_dir_stats(stats, id3_cb); 80 bool success = collect_dir_stats(stats, NULL);
127
128#ifdef HAVE_ADJUSTABLE_CPU_FREQ 81#ifdef HAVE_ADJUSTABLE_CPU_FREQ
129 rb->cpu_boost(false); 82 rb->cpu_boost(false);
130#endif 83#endif
131
132 if (!success) 84 if (!success)
133 return false; 85 return false;
134 86
135 if (!id3_cb) 87 rb->strlcpy(str_dirname, selected_file, sizeof(str_dirname));
136 { 88 rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count);
137 rb->strlcpy(str_dirname, selected_file, sizeof(str_dirname)); 89 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
138 rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count); 90 rb->snprintf(str_audio_filecount, sizeof str_filecount, "%d",
139 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count); 91 stats->audio_file_count);
140 rb->snprintf(str_audio_filecount, sizeof str_filecount, "%d", 92 display_size = human_size(stats->byte_count, &lang_size_unit);
141 stats->audio_file_count); 93 rb->snprintf(str_size, sizeof str_size, "%lu %s", display_size,
142 display_size = human_size(stats->byte_count, &lang_size_unit); 94 rb->str(lang_size_unit));
143 rb->snprintf(str_size, sizeof str_size, "%lu %s", display_size,
144 rb->str(lang_size_unit));
145 }
146 return true; 95 return true;
147} 96}
148 97
@@ -157,12 +106,11 @@ static const char * get_props(int selected_item, void* data,
157{ 106{
158 (void)data; 107 (void)data;
159 if (PROPS_DIR == props_type) 108 if (PROPS_DIR == props_type)
160 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ? "ERROR" : 109 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ?
161 (char *) p2str(props_dir[selected_item]), buffer_len); 110 "ERROR" : (char *) p2str(props_dir[selected_item]), buffer_len);
162 else 111 else
163 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ? "ERROR" : 112 rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ?
164 (char *) p2str(props_file[selected_item]), buffer_len); 113 "ERROR" : (char *) p2str(props_file[selected_item]), buffer_len);
165
166 return buffer; 114 return buffer;
167} 115}
168 116
@@ -226,15 +174,13 @@ static void setup_properties_list(struct dir_stats *stats)
226 174
227static int browse_file_or_dir(struct dir_stats *stats) 175static int browse_file_or_dir(struct dir_stats *stats)
228{ 176{
229 int button;
230
231 if (props_type == PROPS_DIR && stats->audio_file_count) 177 if (props_type == PROPS_DIR && stats->audio_file_count)
232 rb->gui_synclist_set_nb_items(&properties_lists, NUM_AUDIODIR_PROPERTIES*2); 178 rb->gui_synclist_set_nb_items(&properties_lists, NUM_AUDIODIR_PROPERTIES*2);
233 rb->gui_synclist_draw(&properties_lists); 179 rb->gui_synclist_draw(&properties_lists);
234 rb->gui_synclist_speak_item(&properties_lists); 180 rb->gui_synclist_speak_item(&properties_lists);
235 while(true) 181 while(true)
236 { 182 {
237 button = rb->get_action(CONTEXT_LIST, HZ); 183 int button = rb->get_action(CONTEXT_LIST, HZ);
238 /* HZ so the status bar redraws corectly */ 184 /* HZ so the status bar redraws corectly */
239 if (rb->gui_synclist_do_button(&properties_lists, &button)) 185 if (rb->gui_synclist_do_button(&properties_lists, &button))
240 continue; 186 continue;
@@ -257,24 +203,55 @@ static int browse_file_or_dir(struct dir_stats *stats)
257 } 203 }
258} 204}
259 205
260static bool determine_file_or_dir(void) 206static bool determine_props_type(const char *file)
261{ 207{
262 struct dirent* entry; 208 if (file[0] == PATH_SEPCH)
263 DIR* dir = rb->opendir(str_dirname);
264 if (dir)
265 { 209 {
210 const char* basename = rb->strrchr(file, PATH_SEPCH) + 1;
211 const int dir_len = (basename - file);
212 if ((int) sizeof(str_dirname) <= dir_len)
213 return false;
214 rb->strlcpy(str_dirname, file, dir_len + 1);
215 rb->strlcpy(str_filename, basename, sizeof str_filename);
216 struct dirent* entry;
217 DIR* dir = rb->opendir(str_dirname);
218 if (!dir)
219 return false;
266 while(0 != (entry = rb->readdir(dir))) 220 while(0 != (entry = rb->readdir(dir)))
267 { 221 {
268 if(!rb->strcmp(entry->d_name, str_filename)) 222 if(rb->strcmp(entry->d_name, str_filename))
223 continue;
224
225 struct dirinfo info = rb->dir_get_info(dir, entry);
226 if (info.attribute & ATTR_DIRECTORY)
227 props_type = PROPS_DIR;
228 else
269 { 229 {
270 struct dirinfo info = rb->dir_get_info(dir, entry); 230 display_size = human_size(info.size, &lang_size_unit);
271 props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE; 231 rb->snprintf(str_size, sizeof str_size, "%lu %s",
272 rb->closedir(dir); 232 display_size, rb->str(lang_size_unit));
273 return true; 233 rb->gmtime_r(&info.mtime, &tm);
234 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
235 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
236 rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
237 tm.tm_hour, tm.tm_min, tm.tm_sec);
238
239 if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_M3U)
240 props_type = PROPS_PLAYLIST;
241 else
242 props_type = rb->get_metadata(&id3, -1, file) ?
243 PROPS_ID3 : PROPS_FILE;
274 } 244 }
245 rb->closedir(dir);
246 return true;
275 } 247 }
276 rb->closedir(dir); 248 rb->closedir(dir);
277 } 249 }
250 else if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER)))
251 {
252 props_type = PROPS_MUL_ID3;
253 return true;
254 }
278 return false; 255 return false;
279} 256}
280 257
@@ -287,40 +264,34 @@ bool mul_id3_add(const char *file_name)
287 collect_id3(&id3, mul_id3_count == 0); 264 collect_id3(&id3, mul_id3_count == 0);
288 mul_id3_count++; 265 mul_id3_count++;
289 } 266 }
290
291 return true; 267 return true;
292} 268}
293 269
294static bool has_pl_extension(const char* filename) 270/* Assemble track info from a dir, a playlist, or a database table */
295{
296 char *dot = rb->strrchr(filename, '.');
297 return (dot && (!rb->strcasecmp(dot, ".m3u") || !rb->strcasecmp(dot, ".m3u8")));
298}
299
300/* Assemble track info from a database table, the contents of a playlist file, or a dir */
301static bool assemble_track_info(const char *filename, struct dir_stats *stats) 271static bool assemble_track_info(const char *filename, struct dir_stats *stats)
302{ 272{
303 if (!filename) 273 if (props_type == PROPS_DIR)
304 props_type = PROPS_MUL_ID3; 274 {
305 mul_id3_count = skipped_count = 0; 275#ifdef HAVE_ADJUSTABLE_CPU_FREQ
306 276 rb->cpu_boost(true);
277#endif
278 rb->strlcpy(stats->dirname, filename, sizeof(stats->dirname));
279 rb->splash_progress_set_delay(HZ/2); /* hide progress bar for 0.5s */
280 bool success = collect_dir_stats(stats, &mul_id3_add);
281#ifdef HAVE_ADJUSTABLE_CPU_FREQ
282 rb->cpu_boost(false);
283#endif
284 if (!success)
285 return false;
286 }
287 else if(props_type == PROPS_PLAYLIST &&
288 !rb->playlist_entries_iterate(filename, NULL, &mul_id3_add))
289 return false;
307#ifdef HAVE_TAGCACHE 290#ifdef HAVE_TAGCACHE
308 if (props_type == PROPS_MUL_ID3 && !rb->tagtree_subentries_do_action(&mul_id3_add)) 291 else if (props_type == PROPS_MUL_ID3 &&
292 !rb->tagtree_subentries_do_action(&mul_id3_add))
309 return false; 293 return false;
310 else
311#endif 294#endif
312 {
313 if (props_type == PROPS_DIR)
314 {
315 if (!dir_properties(filename, stats, &mul_id3_add))
316 return false;
317 }
318 else if(props_type == PROPS_PLAYLIST)
319 {
320 if (!rb->playlist_entries_iterate(filename, NULL, &mul_id3_add))
321 return false;
322 }
323 }
324 295
325 if (mul_id3_count == 0) 296 if (mul_id3_count == 0)
326 { 297 {
@@ -338,84 +309,48 @@ static bool assemble_track_info(const char *filename, struct dir_stats *stats)
338 309
339enum plugin_status plugin_start(const void* parameter) 310enum plugin_status plugin_start(const void* parameter)
340{ 311{
341 int ret = 0;
342 static struct dir_stats stats; 312 static struct dir_stats stats;
343 const char *file = parameter; 313 const char *file = parameter;
344 if(!parameter)
345 return PLUGIN_ERROR;
346
347 FOR_NB_SCREENS(i)
348 rb->viewportmanager_theme_enable(i, true, NULL);
349#ifdef HAVE_TOUCHSCREEN 314#ifdef HAVE_TOUCHSCREEN
350 rb->touchscreen_set_mode(rb->global_settings->touch_mode); 315 rb->touchscreen_set_mode(rb->global_settings->touch_mode);
351#endif 316#endif
352 if (file[0] == '/') /* single file or folder selected */ 317 int ret = file && determine_props_type(file);
318 if (!ret)
353 { 319 {
354 const char* file_name = rb->strrchr(file, '/') + 1; 320 rb->splashf(0, "Could not find: %s", file ?: "(NULL)");
355 const int dirlen = (file_name - file); 321 rb->action_userabort(TIMEOUT_BLOCK);
356 rb->strlcpy(str_dirname, file, dirlen + 1); 322 return PLUGIN_OK;
357 rb->snprintf(str_filename, sizeof str_filename, "%s", file + dirlen); 323 }
324 FOR_NB_SCREENS(i)
325 rb->viewportmanager_theme_enable(i, true, NULL);
358 326
359 if(!determine_file_or_dir()) 327 if (props_type == PROPS_MUL_ID3)
328 ret = assemble_track_info(NULL, NULL);
329 else if (props_type != PROPS_ID3)
330 {
331 setup_properties_list(&stats); /* Show title during dir scan */
332 if (props_type == PROPS_DIR)
333 ret = dir_properties(file, &stats);
334 }
335 if (!ret)
336 {
337 if (!stats.canceled)
360 { 338 {
361 rb->splashf(0, "File/Dir not found: %s", file); 339 rb->splash(0, ID2P(LANG_PROPERTIES_FAIL)); /* TODO: describe error */
362 rb->action_userabort(TIMEOUT_BLOCK); 340 rb->action_userabort(TIMEOUT_BLOCK);
363 goto exit;
364 }
365
366 if (props_type == PROPS_FILE)
367 {
368 if (has_pl_extension(file))
369 props_type = PROPS_PLAYLIST;
370
371 ret = !file_properties(file);
372 }
373
374 if (props_type != PROPS_ID3) /* i.e. not handled by browse_id3 */
375 {
376 setup_properties_list(&stats); /* Show title during dir scan */
377 if (props_type == PROPS_DIR)
378 ret = !dir_properties(file, &stats, NULL);
379 }
380 if (ret)
381 {
382 ret = 0;
383 if (!stats.canceled)
384 {
385 /* TODO: describe error */
386 rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
387 rb->action_userabort(TIMEOUT_BLOCK);
388 }
389 goto exit;
390 } 341 }
391 } 342 }
392 /* database table selected */ 343 else if (props_type == PROPS_ID3)
393 else if (rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))
394 || !assemble_track_info(NULL, NULL))
395 {
396 ret = -1;
397 goto exit;
398 }
399
400 if (props_type == PROPS_ID3)
401 ret = rb->browse_id3(&id3, 0, 0, &tm, 1); /* Track Info for single file */ 344 ret = rb->browse_id3(&id3, 0, 0, &tm, 1); /* Track Info for single file */
402 else if (props_type == PROPS_MUL_ID3) 345 else if (props_type == PROPS_MUL_ID3)
403 ret = rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count); /* database tracks */ 346 ret = rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count); /* database tracks */
404 else if ((ret = browse_file_or_dir(&stats)) < 0) 347 else if ((ret = browse_file_or_dir(&stats)) < 0)
405 ret = assemble_track_info(file, &stats) ? /* playlist or folder tracks */ 348 ret = assemble_track_info(file, &stats) ? /* playlist or folder tracks */
406 rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) : 349 rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) :
407 (stats.canceled ? 0 : -1); 350 (stats.canceled ? 0 : -1);
408exit: 351
409 FOR_NB_SCREENS(i) 352 FOR_NB_SCREENS(i)
410 rb->viewportmanager_theme_undo(i, false); 353 rb->viewportmanager_theme_undo(i, false);
411 354
412 switch (ret) 355 return ret == -1 ? PLUGIN_ERROR : ret == 1 ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
413 {
414 case 1:
415 return PLUGIN_USB_CONNECTED;
416 case -1:
417 return PLUGIN_ERROR;
418 default:
419 return PLUGIN_OK;
420 }
421} 356}