A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 Antoine Cellerier <dionoea -at- videolan -dot- org>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "plugin.h"
23#include "lib/md5.h"
24
25
26
27#define BUFFERSIZE 16384
28
29static int count = 0;
30static int done = 0;
31static bool quit = false;
32
33static int hash( char *string, const char *path )
34{
35 static char buffer[BUFFERSIZE];
36 ssize_t len;
37 struct md5_s md5;
38 int in = rb->open( path, O_RDONLY );
39 if( in < 0 ) return -1;
40
41 InitMD5( &md5 );
42 while( !quit && ( len = rb->read( in, buffer, sizeof(buffer) ) ) > 0 )
43 {
44 AddMD5( &md5, buffer, len );
45
46 if( rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK) == ACTION_STD_CANCEL )
47 quit = true;
48 }
49
50 EndMD5( &md5 );
51
52 psz_md5_hash( string, &md5 );
53
54 rb->close( in );
55 return 0;
56}
57
58static void hash_file( int out, const char *path )
59{
60 if( out < 0 )
61 count++;
62 else
63 {
64 char string[MD5_STRING_LENGTH+1];
65 int status;
66 done++;
67 rb->splashf( 0, "%d / %d : %s", done, count, path );
68 status = hash( string, path );
69
70 if( quit )
71 return;
72
73 if( status )
74 rb->write( out, "error", 5 );
75 else
76 rb->write( out, string, MD5_STRING_LENGTH );
77 rb->write( out, " ", 2 );
78 rb->write( out, path, rb->strlen( path ) );
79 rb->write( out, "\n", 1 );
80
81 rb->yield();
82 }
83}
84
85static void hash_dir( int out, const char *path )
86{
87 DIR *dir;
88 struct dirent *entry;
89
90 dir = rb->opendir( path );
91 if( dir )
92 {
93 while( !quit && ( entry = rb->readdir( dir ) ) )
94 {
95 char childpath[MAX_PATH];
96 rb->snprintf( childpath, MAX_PATH, "%s/%s",
97 rb->strcmp( path, "/" ) ? path : "", entry->d_name );
98
99 struct dirinfo info = rb->dir_get_info(dir, entry);
100 if (info.attribute & ATTR_DIRECTORY)
101 {
102 if( rb->strcmp( entry->d_name, "." )
103 && rb->strcmp( entry->d_name, ".." ) )
104 {
105 /* Got a sub directory */
106 hash_dir( out, childpath );
107 }
108 }
109 else
110 {
111 /* Got a file */
112 hash_file( out, childpath );
113 }
114 }
115 rb->closedir( dir );
116 }
117}
118
119static void hash_list( int out, const char *path )
120{
121 int list = rb->open( path, O_RDONLY );
122 char newpath[MAX_PATH];
123 if( list < 0 ) return;
124
125 while( !quit && rb->read_line( list, newpath, MAX_PATH ) > 0 )
126 {
127 DIR *dir = rb->opendir( newpath );
128 if( dir )
129 {
130 rb->closedir( dir );
131 hash_dir( out, newpath );
132 }
133 else
134 {
135 hash_file( out, newpath );
136 }
137 }
138
139 rb->close( list );
140}
141
142static void hash_check( int out, const char *path )
143{
144 int list = rb->open( path, O_RDONLY );
145 char line[MD5_STRING_LENGTH+1+MAX_PATH+1];
146 int len;
147 if( list < 0 ) return;
148
149 while( !quit && ( len = rb->read_line( list, line, MD5_STRING_LENGTH+1+MAX_PATH+1 ) ) > 0 )
150 {
151 if( out < 0 )
152 count++;
153 else
154 {
155 const char *filename = rb->strchr( line, ' ' );
156 done++;
157 rb->splashf( 0, "%d / %d : %s", done, count, filename );
158 if( !filename || len < MD5_STRING_LENGTH + 2 )
159 {
160 const char error[] = "Malformed input line ... skipping";
161 rb->write( out, error, rb->strlen( error ) );
162 }
163 else
164 {
165 char string[MD5_STRING_LENGTH+1];
166 while( *filename == ' ' )
167 filename++;
168 rb->write( out, filename, rb->strlen( filename ) );
169 rb->write( out, ": ", 2 );
170 if( hash( string, filename ) )
171 rb->write( out, "FAILED open or read", 19 );
172 else if( rb->strncasecmp( line, string, MD5_STRING_LENGTH ) )
173 rb->write( out, "FAILED", 6 );
174 else
175 rb->write( out, "OK", 2 );
176 }
177 rb->write( out, "\n", 1 );
178 }
179 }
180
181 rb->close( list );
182}
183
184/*
185 * Return the last name from a pathname (ignoring a trailing slash if
186 * it exists). The returned pointer points to a statically allocated
187 * buffer.
188 */
189static char *get_basename(const char *path) {
190 static char temp[MAX_PATH];
191 char *p;
192 int len, isdir = 0;
193
194 rb->strcpy(temp, path);
195
196 len = rb->strlen(temp);
197
198 if (temp[len - 1] == '/')
199 {
200 /* strip trailing slash, and update length accordingly */
201 temp[--len] = '\0';
202 isdir = 1;
203 }
204
205 /* find the last slash, if there is one */
206 p = rb->strrchr(temp, '/');
207
208 /*
209 * re-append trailing slash if we previously removed it (the
210 * original NUL is still present)
211 */
212 if(isdir)
213 temp[len++] = '/';
214
215 return p ? (p + 1) : temp;
216}
217
218enum plugin_status plugin_start(const void* parameter)
219{
220 const char *arg = (const char *)parameter; /* input file path, if any */
221 char *basename;
222 int out = -1; /* output file descriptor */
223 char filename[MAX_PATH]; /* output file name */
224
225 void (*action)( int, const char * ) = NULL;
226
227#ifdef HAVE_ADJUSTABLE_CPU_FREQ
228 rb->cpu_boost( true );
229#endif
230
231 if( arg && *arg )
232 {
233 const char *ext = rb->strrchr( arg, '.' );
234 DIR *dir;
235 rb->snprintf( filename, MAX_PATH, "%s.md5sum", arg );
236
237 if( ext )
238 {
239 if( !rb->strcmp( ext, ".md5" ) || !rb->strcmp( ext, ".md5sum" ) )
240 {
241 rb->snprintf( filename + ( ext - arg ),
242 MAX_PATH + rb->strlen( ext ) - rb->strlen( arg ),
243 ".md5check" );
244 /* Lets check the sums */
245 action = hash_check;
246 }
247 else if( !rb->strcmp( ext, ".md5list" ) ) /* ugly */
248 {
249 /* Hash listed files */
250 action = hash_list;
251 }
252 }
253
254 if( !action )
255 {
256 dir = rb->opendir( arg );
257 if( dir )
258 {
259 rb->closedir( dir );
260
261 /* Hash the directory's content recursively */
262 action = hash_dir;
263 }
264 else
265 {
266 /* Hash the file */
267 action = hash_file;
268 }
269 }
270 }
271 else
272 {
273 rb->snprintf( filename, MAX_PATH, "/everything.md5sum" );
274 /* Hash the whole filesystem */
275 action = hash_dir;
276 arg = "/";
277 }
278
279 basename = get_basename(arg);
280
281 rb->lcd_putsf( 0, 1, "Hashing %s", basename );
282 rb->lcd_puts( 0, 2, rb->str(LANG_ACTION_STD_CANCEL) );
283
284 rb->lcd_puts( 0, 3, "Output file:" );
285 rb->lcd_puts( 0, 4, filename );
286
287 rb->lcd_update();
288 count = 0;
289 done = 0;
290 action( out, arg );
291
292 out = rb->open( filename, O_WRONLY|O_CREAT|O_TRUNC , 0666);
293 if( out < 0 ) return PLUGIN_ERROR;
294 action( out, arg );
295 rb->close( out );
296#ifdef HAVE_ADJUSTABLE_CPU_FREQ
297 rb->cpu_boost( false );
298#endif
299 return PLUGIN_OK;
300}