00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00031 #include "stdinc.h"
00032
00033 #include "config.h"
00034 #include "vfs.h"
00035 #include "vfs_modules.h"
00036
00041 static struct vfsmodule modules[] = {
00042 #ifdef BUILD_HTTP
00043 { vfs_http_match, NULL, vfs_http_open, 1, 1, '^' },
00044 #endif
00045 { vfs_m3u_match, vfs_m3u_populate, NULL, 0, 1, '@' },
00046 { vfs_pls_match, vfs_pls_populate, NULL, 0, 1, '@' },
00047 #ifdef BUILD_XSPF
00048 { vfs_xspf_match, vfs_xspf_populate, NULL, 0, 1, '@' },
00049 #endif
00050
00051
00052
00053
00054 { vfs_dir_match, vfs_dir_populate, NULL, 0, 0, G_DIR_SEPARATOR },
00055 { vfs_file_match, NULL, vfs_file_open, 0, 1, '\0' },
00056 };
00061 #define NUM_MODULES (sizeof modules / sizeof(struct vfsmodule))
00062
00067 struct vfswriter {
00071 int (*write)(const struct vfslist *vl, const char *filename);
00075 char *ext;
00076 };
00077
00083 static struct vfswriter writers[] = {
00084 #ifdef BUILD_XSPF
00085 { vfs_xspf_write, ".xspf" },
00086 #endif
00087 { vfs_pls_write, ".pls" },
00088 { vfs_m3u_write, ".m3u" },
00089 };
00094 #define NUM_WRITERS (sizeof writers / sizeof(struct vfswriter))
00095
00100 static char *
00101 vfs_path_concat(const char *dir, const char *file, int strict)
00102 {
00103 GString *npath;
00104 char *tmp, *off;
00105 #ifdef G_OS_UNIX
00106 const char *uend;
00107 struct passwd *pw;
00108 #endif
00109
00110 if (!strict && file[0] == '~' &&
00111 (file[1] == '\0' || file[1] == G_DIR_SEPARATOR)) {
00112
00113 npath = g_string_new(g_get_home_dir());
00114 g_string_append(npath, file + 1);
00115 #ifdef G_OS_UNIX
00116 } else if (!strict && file[0] == '~') {
00117
00118 uend = strchr(file + 1, G_DIR_SEPARATOR);
00119
00120
00121 if (uend != NULL) {
00122 tmp = g_strndup(file + 1, uend - (file + 1));
00123 pw = getpwnam(tmp);
00124 g_free(tmp);
00125 } else {
00126 pw = getpwnam(file + 1);
00127 }
00128 if (pw == NULL)
00129 return (NULL);
00130
00131
00132 npath = g_string_new(pw->pw_dir);
00133 if (uend != NULL)
00134 g_string_append(npath, uend);
00135 #endif
00136 } else if (g_path_is_absolute(file)) {
00137
00138 npath = g_string_new(file);
00139 } else if (dir != NULL) {
00140
00141 if (!g_path_is_absolute(dir))
00142 return (NULL);
00143
00144
00145 tmp = g_build_filename(dir, file, NULL);
00146 npath = g_string_new(tmp);
00147 g_free(tmp);
00148 } else {
00149
00150 return (NULL);
00151 }
00152
00153
00154 for (off = npath->str;
00155 (off = strchr(off, G_DIR_SEPARATOR)) != NULL;) {
00156 if (off[1] == '\0' || off[1] == G_DIR_SEPARATOR) {
00157
00158 g_string_erase(npath, off - npath->str, 1);
00159 } else if (off[1] == '.' &&
00160 (off[2] == '\0' || off[2] == G_DIR_SEPARATOR)) {
00161
00162 g_string_erase(npath, off - npath->str, 2);
00163 } else if (off[1] == '.' && off[2] == '.' &&
00164 (off[3] == '\0' || off[3] == G_DIR_SEPARATOR)) {
00165
00166
00167
00168 *off = '\0';
00169 tmp = strrchr(npath->str, G_DIR_SEPARATOR);
00170 if (tmp != NULL) {
00171 g_string_erase(npath, tmp - npath->str,
00172 (off - tmp) + 3);
00173 off = tmp;
00174 } else {
00175
00176 g_string_free(npath, TRUE);
00177 return (NULL);
00178 }
00179 } else {
00180
00181 off++;
00182 }
00183 }
00184
00185 if (npath->len == 0)
00186 g_string_assign(npath, G_DIR_SEPARATOR_S);
00187
00188 return g_string_free(npath, FALSE);
00189 }
00190
00191 const char *
00192 vfs_lockup(void)
00193 {
00194 #ifdef G_OS_UNIX
00195 const char *root;
00196 const char *user;
00197 char *rootpath;
00198 struct passwd *pw = NULL;
00199
00200 user = config_getopt("vfs.lockup.user");
00201 if (user[0] != '\0') {
00202 pw = getpwnam(user);
00203 if (pw == NULL)
00204 return g_strdup_printf(
00205 _("Unknown user: %s\n"), user);
00206 }
00207
00208 root = config_getopt("vfs.lockup.chroot");
00209 if (root[0] != '\0') {
00210 #ifdef BUILD_RES_INIT
00211
00212 res_init();
00213 #endif
00214
00215
00216 rootpath = vfs_path_concat(NULL, root, 0);
00217 if (rootpath == NULL || chroot(rootpath) != 0)
00218 return g_strdup_printf(
00219 _("Unable to chroot in %s\n"),
00220 rootpath != NULL ? rootpath : root);
00221
00222 chdir("/");
00223 }
00224
00225 if (pw != NULL) {
00226 if (setgid(pw->pw_gid) != 0)
00227 return g_strdup_printf(
00228 _("Unable to change to group %d\n"),
00229 (int)pw->pw_gid);
00230 if (setuid(pw->pw_uid) != 0)
00231 return g_strdup_printf(
00232 _("Unable to change to user %d\n"),
00233 (int)pw->pw_uid);
00234 }
00235 #endif
00236
00237 return (NULL);
00238 }
00239
00243 static void
00244 vfs_dealloc(struct vfsent *ve)
00245 {
00246 g_free(ve->name);
00247 g_free(ve->filename);
00248 g_slice_free(struct vfsent, ve);
00249 }
00250
00251 struct vfsref *
00252 vfs_lookup(const char *filename, const char *name, const char *basepath,
00253 int strict)
00254 {
00255 char *fn;
00256 struct vfsent *ve;
00257 struct vfsref *vr;
00258 struct stat fs;
00259 unsigned int i;
00260 int pseudo = 0;
00261
00262 fn = vfs_path_concat(basepath, filename, strict);
00263
00264
00265 if (fn == NULL || stat(fn, &fs) != 0) {
00266
00267 pseudo = 1;
00268
00269 g_free(fn);
00270 fn = g_strdup(filename);
00271 } else if (!S_ISREG(fs.st_mode) && !S_ISDIR(fs.st_mode)) {
00272
00273 g_free(fn);
00274 return (NULL);
00275 }
00276
00277
00278 ve = g_slice_new0(struct vfsent);
00279 ve->filename = fn;
00280 ve->recurse = 1;
00281 vfs_list_init(&ve->population);
00282
00283
00284 if (name != NULL) {
00285
00286 ve->name = g_strdup(name);
00287 } else if (pseudo) {
00288
00289 ve->name = g_strdup(ve->filename);
00290 } else {
00291
00292 ve->name = g_path_get_basename(ve->filename);
00293 }
00294
00295
00296 for (i = 0; i < NUM_MODULES; i++) {
00297
00298 if (pseudo && !modules[i].pseudo)
00299 continue;
00300
00301
00302 ve->vmod = &modules[i];
00303 if (ve->vmod->match(ve, S_ISDIR(fs.st_mode)) == 0)
00304 goto found;
00305 }
00306
00307
00308 vfs_dealloc(ve);
00309 return (NULL);
00310
00311 found:
00312
00313 ve->refcount = 1;
00314 vr = g_slice_new0(struct vfsref);
00315 vr->ent = ve;
00316 return (vr);
00317 }
00318
00319 struct vfsref *
00320 vfs_dup(const struct vfsref *vr)
00321 {
00322 struct vfsent *ve;
00323 struct vfsref *rvr;
00324
00325 ve = vr->ent;
00326 g_atomic_int_inc(&ve->refcount);
00327
00328 rvr = g_slice_new(struct vfsref);
00329 rvr->ent = ve;
00330 rvr->marked = 0;
00331
00332 return (rvr);
00333 }
00334
00335 void
00336 vfs_close(struct vfsref *vr)
00337 {
00338 struct vfsref *cur;
00339
00340 if (g_atomic_int_dec_and_test(&vr->ent->refcount)) {
00341
00342 while ((cur = vfs_list_first(&vr->ent->population)) != NULL) {
00343 vfs_list_remove(&vr->ent->population, cur);
00344 vfs_close(cur);
00345 }
00346
00347 vfs_dealloc(vr->ent);
00348 }
00349
00350
00351 g_slice_free(struct vfsref, vr);
00352 }
00353
00354 int
00355 vfs_populate(const struct vfsref *vr)
00356 {
00357
00358 if (!vfs_populatable(vr))
00359 return (-1);
00360
00361
00362 if (!vfs_list_empty(vfs_population(vr)))
00363 return (0);
00364
00365 return vr->ent->vmod->populate(vr->ent);
00366 }
00367
00368 void
00369 vfs_unfold(struct vfslist *vl, const struct vfsref *vr)
00370 {
00371 struct vfsref *cvr;
00372
00373 if (vfs_playable(vr)) {
00374
00375 vfs_list_insert_tail(vl, vfs_dup(vr));
00376 } else {
00377
00378 vfs_populate(vr);
00379 VFS_LIST_FOREACH(&vr->ent->population, cvr) {
00380 if (cvr->ent->recurse)
00381 vfs_unfold(vl, cvr);
00382 }
00383 }
00384 }
00385
00386 void
00387 vfs_locate(struct vfslist *vl, const struct vfsref *vr,
00388 const struct vfsmatch *vm)
00389 {
00390 struct vfsref *cvr;
00391
00392 vfs_populate(vr);
00393 VFS_LIST_FOREACH(&vr->ent->population, cvr) {
00394
00395 if (vfs_playable(cvr) &&
00396 vfs_match_compare(vm, vfs_filename(cvr)))
00397 vfs_list_insert_tail(vl, vfs_dup(cvr));
00398
00399 if (cvr->ent->recurse)
00400 vfs_locate(vl, cvr, vm);
00401 }
00402 }
00403
00404 struct vfsref *
00405 vfs_write_playlist(const struct vfslist *vl, const struct vfsref *vr,
00406 const char *filename)
00407 {
00408 const char *base = NULL;
00409 char *fn, *nfn;
00410 struct vfsref *rvr = NULL;
00411 struct vfswriter *wr;
00412 unsigned int i;
00413
00414 if (vr != NULL)
00415 base = vfs_filename(vr);
00416 fn = vfs_path_concat(base, filename, 0);
00417 if (fn == NULL)
00418 return (NULL);
00419
00420
00421 for (i = 0; i < NUM_WRITERS; i++) {
00422 if (g_str_has_suffix(fn, writers[i].ext)) {
00423 wr = &writers[i];
00424 goto match;
00425 }
00426 }
00427
00428
00429 wr = &writers[0];
00430 nfn = g_strdup_printf("%s%s", fn, wr->ext);
00431 g_free(fn);
00432 fn = nfn;
00433 match:
00434
00435 if (wr->write(vl, fn) == 0)
00436 rvr = vfs_lookup(fn, NULL, NULL, 0);
00437
00438 g_free(fn);
00439 return (rvr);
00440 }
00441
00442 int
00443 vfs_delete(const char *filename)
00444 {
00445 char *fn;
00446 int ret;
00447
00448 fn = vfs_path_concat(NULL, filename, 0);
00449 if (fn == NULL)
00450 return (-1);
00451
00452 ret = unlink(fn);
00453 g_free(fn);
00454
00455 return (ret);
00456 }
00457
00458 FILE *
00459 vfs_fopen(const char *filename, const char *mode)
00460 {
00461 char *fn;
00462 FILE *ret;
00463
00464 fn = vfs_path_concat(NULL, filename, 0);
00465 if (fn == NULL)
00466 return (NULL);
00467
00468 ret = fopen(fn, mode);
00469 g_free(fn);
00470
00471 return (ret);
00472 }
00473
00474 int
00475 vfs_fgets(char *str, size_t size, FILE *fp)
00476 {
00477 char *eol;
00478
00479 if (fgets(str, size, fp) == NULL)
00480 return (-1);
00481
00482 eol = strchr(str, '\0');
00483 g_assert(eol != NULL);
00484
00485
00486 if (--eol >= str && *eol == '\n') {
00487 *eol = '\0';
00488
00489 if (--eol >= str && *eol == '\r')
00490 *eol = '\0';
00491 }
00492
00493 return (0);
00494 }
00495
00496 struct vfsmatch *
00497 vfs_match_new(const char *str)
00498 {
00499 struct vfsmatch *vm;
00500
00501 vm = g_slice_new0(struct vfsmatch);
00502
00503 if (regcomp(&vm->regex, str, REG_EXTENDED|REG_ICASE) != 0) {
00504 g_slice_free(struct vfsmatch, vm);
00505 return (NULL);
00506 }
00507 vm->string = g_strdup(str);
00508
00509 return (vm);
00510 }
00511
00512 void
00513 vfs_match_free(struct vfsmatch *vm)
00514 {
00515 regfree(&vm->regex);
00516 g_free(vm->string);
00517 g_slice_free(struct vfsmatch, vm);
00518 }