No issues found
1 /*
2 * Copyright (C) 2009 Jonathan Matthew <jonathan@d14n.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 #include <string.h>
21
22 #include <glib.h>
23 #include <glib-object.h>
24 #include <gio/gunixmounts.h>
25
26 #include "mediaplayerid.h"
27 #include "mpid-private.h"
28
29 /**
30 * SECTION:mediaplayerid
31 * @short_description: Media player device information library
32 *
33 * MPID provides access to device information, such as device and vendor names,
34 * supported formats, and audio folder locations, for USB mass storage media
35 * player devices. It queries the operating system (udev or HAL) and reads
36 * override files from the device filesystem and provides a simple set of
37 * properties.
38 */
39
40 /**
41 * MPIDDevice:
42 *
43 * An #MPIDDevice stores a set of information for a particular attached device,
44 * identified by either a mount point (e.g. /media/device) or a device node
45 * (e.g. /dev/sdb).
46 */
47
48 enum
49 {
50 PROP_0,
51 PROP_INPUT_PATH,
52 PROP_ERROR,
53 PROP_SOURCE,
54 PROP_MODEL,
55 PROP_VENDOR,
56 PROP_FS_UUID,
57 PROP_SERIAL,
58 PROP_DRIVE_TYPE,
59 PROP_REQUIRES_EJECT,
60 PROP_ACCESS_PROTOCOLS,
61 PROP_OUTPUT_FORMATS,
62 PROP_INPUT_FORMATS,
63 PROP_PLAYLIST_FORMATS,
64 PROP_PLAYLIST_PATH,
65 PROP_AUDIO_FOLDERS,
66 PROP_FOLDER_DEPTH
67 };
68
69 static void mpid_device_class_init (MPIDDeviceClass *klass);
70 static void mpid_device_init (MPIDDevice *config);
71
72 G_DEFINE_TYPE (MPIDDevice, mpid_device, G_TYPE_OBJECT)
73
74
75 void
76 mpid_device_debug (MPIDDevice *device, const char *what)
77 {
78 mpid_debug ("device information (%s)\n", what);
79 switch (device->source) {
80 case MPID_SOURCE_NONE:
81 mpid_debug ("no information source\n");
82 break;
83 case MPID_SOURCE_SYSTEM:
84 mpid_debug ("information read from system device database\n");
85 break;
86 case MPID_SOURCE_OVERRIDE:
87 mpid_debug ("information read from device override file\n");
88 break;
89 }
90 mpid_debug_str ("model", device->model);
91 mpid_debug_str ("vendor", device->vendor);
92 mpid_debug_str ("filesystem uuid", device->fs_uuid);
93 mpid_debug_str ("drive type", device->drive_type);
94 mpid_debug ("requires eject: %s\n", device->requires_eject ? "true" : "false");
95 mpid_debug_strv ("access protocols", device->access_protocols);
96 mpid_debug_strv ("output formats", device->output_formats);
97 mpid_debug_strv ("input formats", device->input_formats);
98 mpid_debug_strv ("playlist formats", device->playlist_formats);
99 mpid_debug_str ("playlist path", device->playlist_path);
100 mpid_debug_strv ("audio folders", device->audio_folders);
101 mpid_debug ("folder depth: %d\n", device->folder_depth);
102 }
103
104 char *
105 mpid_device_get_mount_point (MPIDDevice *device)
106 {
107 char *mount_path = NULL;
108 GUnixMountEntry *mount;
109 GList *mounts;
110 GList *i;
111
112 if (device->input_path == NULL) {
113 mpid_debug ("no input path specified, can't find mount point");
114 return NULL;
115 }
116
117 mount = g_unix_mount_at (device->input_path, NULL);
118 if (mount != NULL) {
119 /* path is the mount point */
120 g_unix_mount_free (mount);
121 mpid_debug ("%s is already a mount point\n", device->input_path);
122 return g_strdup (device->input_path);
123 }
124
125 mounts = g_unix_mounts_get (NULL);
126 for (i = mounts; i != NULL; i = i->next) {
127 mount = i->data;
128
129 if (g_str_equal (g_unix_mount_get_device_path (mount), device->input_path)) {
130 mount_path = g_strdup (g_unix_mount_get_mount_path (mount));
131 mpid_debug ("found mount point %s for device path %s\n", mount_path, device->input_path);
132 }
133 g_unix_mount_free (mount);
134 }
135 g_list_free (mounts);
136
137 if (mount_path == NULL) {
138 mpid_debug ("unable to find mount point for device path %s\n", device->input_path);
139 }
140
141 return mount_path;
142 }
143
144 char *
145 mpid_device_get_device_path (MPIDDevice *device)
146 {
147 GUnixMountEntry *mount;
148 char *mount_path;
149 char *device_path = NULL;
150 GList *mounts;
151 GList *i;
152
153 if (device->input_path == NULL) {
154 mpid_debug ("no input path specified, can't find device path\n");
155 return NULL;
156 }
157
158 mount_path = g_strdup (device->input_path);
159 if (mount_path[strlen (mount_path) - 1] == '/') {
160 mount_path[strlen (mount_path) - 1] = '\0';
161 }
162
163 mount = g_unix_mount_at (mount_path, NULL);
164 if (mount != NULL) {
165 device_path = g_strdup (g_unix_mount_get_device_path (mount));
166 g_unix_mount_free (mount);
167 mpid_debug ("found device path %s for mount %s\n", device_path, mount_path);
168 g_free (mount_path);
169 return device_path;
170 }
171
172 /* it's not a mount point, so check if it's the path to a mounted device */
173 mounts = g_unix_mounts_get (NULL);
174 for (i = mounts; i != NULL; i = i->next) {
175 mount = i->data;
176
177 if (g_str_equal (g_unix_mount_get_device_path (mount), mount_path)) {
178 device_path = g_strdup (mount_path);
179 mpid_debug ("%s is already a device path\n", device_path);
180 }
181 g_unix_mount_free (mount);
182 }
183 g_list_free (mounts);
184 g_free (mount_path);
185
186 if (device_path == NULL) {
187 mpid_debug ("unable to find device path for mount point %s\n", device->input_path);
188 }
189
190 return device_path;
191 }
192
193
194
195 static void
196 mpid_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
197 {
198 MPIDDevice *device = MPID_DEVICE (object);
199
200 switch (prop_id) {
201 case PROP_INPUT_PATH:
202 device->input_path = g_value_dup_string (value);
203 break;
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
206 break;
207 }
208 }
209
210 static void
211 mpid_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
212 {
213 MPIDDevice *device = MPID_DEVICE (object);
214
215 switch (prop_id) {
216 case PROP_INPUT_PATH:
217 g_value_set_string (value, device->input_path);
218 break;
219 case PROP_ERROR:
220 g_value_set_enum (value, device->error);
221 break;
222 case PROP_SOURCE:
223 g_value_set_enum (value, device->source);
224 break;
225 case PROP_MODEL:
226 g_value_set_string (value, device->model);
227 break;
228 case PROP_VENDOR:
229 g_value_set_string (value, device->vendor);
230 break;
231 case PROP_FS_UUID:
232 g_value_set_string (value, device->fs_uuid);
233 break;
234 case PROP_SERIAL:
235 g_value_set_string (value, device->serial);
236 break;
237 case PROP_DRIVE_TYPE:
238 g_value_set_string (value, device->drive_type);
239 break;
240 case PROP_REQUIRES_EJECT:
241 g_value_set_boolean (value, device->requires_eject);
242 break;
243 case PROP_ACCESS_PROTOCOLS:
244 g_value_set_boxed (value, device->access_protocols);
245 break;
246 case PROP_OUTPUT_FORMATS:
247 g_value_set_boxed (value, device->output_formats);
248 break;
249 case PROP_INPUT_FORMATS:
250 g_value_set_boxed (value, device->input_formats);
251 break;
252 case PROP_PLAYLIST_FORMATS:
253 g_value_set_boxed (value, device->playlist_formats);
254 break;
255 case PROP_PLAYLIST_PATH:
256 g_value_set_string (value, device->playlist_path);
257 break;
258 case PROP_AUDIO_FOLDERS:
259 g_value_set_boxed (value, device->audio_folders);
260 break;
261 case PROP_FOLDER_DEPTH:
262 g_value_set_int (value, device->folder_depth);
263 break;
264 default:
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266 break;
267 }
268 }
269
270 static void
271 mpid_device_finalize (GObject *object)
272 {
273 MPIDDevice *device = MPID_DEVICE (object);
274
275 g_free (device->model);
276 g_free (device->vendor);
277 g_free (device->fs_uuid);
278 g_free (device->drive_type);
279
280 g_strfreev (device->access_protocols);
281
282 g_strfreev (device->output_formats);
283 g_strfreev (device->input_formats);
284 g_strfreev (device->playlist_formats);
285
286 g_free (device->playlist_path);
287 g_strfreev (device->audio_folders);
288
289 G_OBJECT_CLASS (mpid_device_parent_class)->finalize (object);
290 }
291
292 static void
293 mpid_device_init (MPIDDevice *device)
294 {
295 device->folder_depth = -1;
296 }
297
298 static void
299 mpid_device_constructed (GObject *object)
300 {
301 MPIDDevice *device;
302
303 if (G_OBJECT_CLASS (mpid_device_parent_class)->constructed) {
304 G_OBJECT_CLASS (mpid_device_parent_class)->constructed (object);
305 }
306
307 device = MPID_DEVICE (object);
308
309 mpid_device_db_lookup (device);
310 if (device->source == MPID_SOURCE_SYSTEM) {
311 mpid_device_debug (device, "system database");
312 }
313
314 mpid_device_read_override_file (device);
315 if (device->source == MPID_SOURCE_OVERRIDE) {
316 mpid_device_debug (device, "override file");
317 }
318 }
319
320 static void
321 mpid_device_class_init (MPIDDeviceClass *klass)
322 {
323 GObjectClass *object_class = G_OBJECT_CLASS (klass);
324
325 object_class->constructed = mpid_device_constructed;
326 object_class->finalize = mpid_device_finalize;
327 object_class->get_property = mpid_device_get_property;
328 object_class->set_property = mpid_device_set_property;
329
330 /* install properties */
331 /**
332 * MPIDDevice:input-path:
333 *
334 * Either the device node path or the mount point path for the device.
335 */
336 g_object_class_install_property (object_class,
337 PROP_INPUT_PATH,
338 g_param_spec_string ("input-path",
339 "input path",
340 "Input path (either a device path or a mount point)",
341 NULL,
342 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
343 /**
344 * MPIDDevice:error:
345 *
346 * MPID error code resulting from device detection (see #MPIDError)
347 */
348 g_object_class_install_property (object_class,
349 PROP_ERROR,
350 g_param_spec_enum ("error",
351 "error",
352 "error code",
353 MPID_TYPE_ERROR,
354 MPID_ERROR_NONE,
355 G_PARAM_READABLE));
356 /**
357 * MPIDDevice:source:
358 *
359 * The information source used to provide device information (see #MPIDSource)
360 */
361 g_object_class_install_property (object_class,
362 PROP_SOURCE,
363 g_param_spec_enum ("source",
364 "information source",
365 "information source",
366 MPID_TYPE_SOURCE,
367 MPID_SOURCE_NONE,
368 G_PARAM_READABLE));
369 /**
370 * MPIDDevice:model:
371 *
372 * The device model name
373 */
374 g_object_class_install_property (object_class,
375 PROP_MODEL,
376 g_param_spec_string ("model",
377 "device model",
378 "device model name",
379 NULL,
380 G_PARAM_READABLE));
381 /**
382 * MPIDDevice:vendor:
383 *
384 * The device vendor name
385 */
386 g_object_class_install_property (object_class,
387 PROP_VENDOR,
388 g_param_spec_string ("vendor",
389 "device vendor",
390 "device vendor name",
391 NULL,
392 G_PARAM_READABLE));
393 /**
394 * MPIDDevice:fs-uuid:
395 *
396 * The device filesystem UUID
397 */
398 g_object_class_install_property (object_class,
399 PROP_FS_UUID,
400 g_param_spec_string ("fs-uuid",
401 "device filesystem UUID",
402 "device filesystem UUID",
403 NULL,
404 G_PARAM_READABLE));
405 /**
406 * MPIDDevice:serial:
407 *
408 * The device serial ID
409 */
410 g_object_class_install_property (object_class,
411 PROP_SERIAL,
412 g_param_spec_string ("serial",
413 "device serial ID",
414 "device serial ID",
415 NULL,
416 G_PARAM_READABLE));
417 /**
418 * MPIDDevice:drive-type:
419 *
420 * The device drive type
421 */
422 g_object_class_install_property (object_class,
423 PROP_DRIVE_TYPE,
424 g_param_spec_string ("drive-type",
425 "drive type",
426 "drive type",
427 NULL,
428 G_PARAM_READABLE));
429 /**
430 * MPIDDevice:requires-eject:
431 *
432 * If %TRUE, the device must be ejected rather than unmounted
433 */
434 g_object_class_install_property (object_class,
435 PROP_REQUIRES_EJECT,
436 g_param_spec_boolean ("requires-eject",
437 "requires eject",
438 "flag indicating whether the device requires ejection",
439 FALSE,
440 G_PARAM_READABLE));
441 /**
442 * MPIDDevice:access-protocols:
443 *
444 * Names of access protocols that can be used to access the device contents
445 */
446 g_object_class_install_property (object_class,
447 PROP_ACCESS_PROTOCOLS,
448 g_param_spec_boxed ("access-protocols",
449 "access protocols",
450 "names of protocols supported by the device",
451 G_TYPE_STRV,
452 G_PARAM_READABLE));
453 /**
454 * MPIDDevice:output-formats:
455 *
456 * A set of MIME types that the device can play
457 */
458 g_object_class_install_property (object_class,
459 PROP_OUTPUT_FORMATS,
460 g_param_spec_boxed ("output-formats",
461 "output formats",
462 "MIME types playable by the device",
463 G_TYPE_STRV,
464 G_PARAM_READABLE));
465 /**
466 * MPIDDevice:input-formats:
467 *
468 * A set of MIME types that the device can record
469 */
470 g_object_class_install_property (object_class,
471 PROP_INPUT_FORMATS,
472 g_param_spec_boxed ("input-formats",
473 "input formats",
474 "MIME types recorded by the device",
475 G_TYPE_STRV,
476 G_PARAM_READABLE));
477 /**
478 * MPIDDevice:playlist-formats:
479 *
480 * A set of playlist format MIME types suppored by the device
481 */
482 g_object_class_install_property (object_class,
483 PROP_PLAYLIST_FORMATS,
484 g_param_spec_boxed ("playlist-formats",
485 "playlist formats",
486 "playlist MIME supported by the device",
487 G_TYPE_STRV,
488 G_PARAM_READABLE));
489 /**
490 * MPIDDevice:playlist-path:
491 *
492 * Path to playlist files on the device. May include '%File' to indicate a directory
493 * containing any number of playlist files.
494 */
495 g_object_class_install_property (object_class,
496 PROP_PLAYLIST_PATH,
497 g_param_spec_string ("playlist-path",
498 "playlist path",
499 "playlist path",
500 NULL,
501 G_PARAM_READABLE));
502 /**
503 * MPIDDevice:audio-folders:
504 *
505 * A set of folders (relative to the root of the device) containing audio
506 * folders.
507 */
508 g_object_class_install_property (object_class,
509 PROP_AUDIO_FOLDERS,
510 g_param_spec_boxed ("audio-folders",
511 "audio folders",
512 "names of folders in which audio files are stored on the device",
513 G_TYPE_STRV,
514 G_PARAM_READABLE));
515 /**
516 * MPIDDevice:folder-depth:
517 *
518 * The folder nesting level supported by the device. -1 indicates there is no limit.
519 */
520 g_object_class_install_property (object_class,
521 PROP_FOLDER_DEPTH,
522 g_param_spec_int ("folder-depth",
523 "folder depth",
524 "number of levels of folder nesting supported by the device",
525 -1, G_MAXINT, -1,
526 G_PARAM_READABLE));
527 }
528
529 /**
530 * mpid_device_new:
531 * @path: the input path (either device node path or mount point)
532 *
533 * Creates a new #MPIDDevice and reads device information for the specified
534 * device node path or mount point path.
535 *
536 * Return value: new #MPIDDevice instance
537 */
538 MPIDDevice *
539 mpid_device_new (const char *path)
540 {
541 return g_object_new (MPID_TYPE_DEVICE, "input-path", path, NULL);
542 }