1/*
2 * Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org>
3 *
4 * Rewritten from mt-daapd code:
5 * Copyright (C) 2003 Ron Pedde (ron@pedde.com)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include <stdio.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <string.h>
30#include <ctype.h>
31#include <limits.h>
32#include <sys/param.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <errno.h>
36
37#include "logger.h"
38#include "db.h"
39#include "filescanner.h"
40#include "misc.h"
41
42
43void
44scan_m3u_playlist(char *file)
45{
46  FILE *fp;
47  struct playlist_info *pli;
48  struct stat sb;
49  char buf[PATH_MAX];
50  char rel_entry[PATH_MAX];
51  char *pl_base;
52  char *entry;
53  char *filename;
54  char *ptr;
55  size_t len;
56  int pl_id;
57  int ret;
58
59  DPRINTF(E_INFO, L_SCAN, "Processing static playlist: %s\n", file);
60
61  ret = stat(file, &sb);
62  if (ret < 0)
63    {
64      DPRINTF(E_LOG, L_SCAN, "Could not stat() '%s': %s\n", file, strerror(errno));
65
66      return;
67    }
68
69  filename = strrchr(file, '/');
70  if (!filename)
71    filename = file;
72  else
73    filename++;
74
75  pli = db_pl_fetch_bypath(file);
76
77  if (pli)
78    {
79      DPRINTF(E_DBG, L_SCAN, "Playlist found, updating\n");
80
81      pl_id = pli->id;
82
83      free_pli(pli, 0);
84
85      db_pl_ping(pl_id);
86      db_pl_clear_items(pl_id);
87    }
88  else
89    pl_id = 0;
90
91  fp = fopen(file, "r");
92  if (!fp)
93    {
94      DPRINTF(E_WARN, L_SCAN, "Could not open playlist '%s': %s\n", file, strerror(errno));
95
96      return;
97    }
98
99  if (pl_id == 0)
100    {
101      /* Get only the basename, to be used as the playlist name */
102      ptr = strrchr(filename, '.');
103      if (ptr)
104	*ptr = '\0';
105
106      /* Safe: filename is a subset of file which is <= PATH_MAX already */
107      strncpy(buf, filename, sizeof(buf));
108
109      /* Restore the full filename */
110      if (ptr)
111	*ptr = '.';
112
113      ret = db_pl_add(buf, file, &pl_id);
114      if (ret < 0)
115	{
116	  DPRINTF(E_LOG, L_SCAN, "Error adding m3u playlist '%s'\n", file);
117
118	  return;
119	}
120
121      DPRINTF(E_INFO, L_SCAN, "Added playlist as id %d\n", pl_id);
122    }
123
124  ptr = strrchr(file, '/');
125  if (!ptr)
126    {
127      DPRINTF(E_WARN, L_SCAN, "Could not determine playlist base path\n");
128
129      return;
130    }
131
132  *ptr = '\0';
133  pl_base = strdup(file);
134  *ptr = '/';
135
136  if (!pl_base)
137    {
138      DPRINTF(E_WARN, L_SCAN, "Out of memory\n");
139
140      return;
141    }
142
143  while (fgets(buf, sizeof(buf), fp) != NULL)
144    {
145      len = strlen(buf);
146      if (buf[len - 1] != '\n')
147	{
148	  DPRINTF(E_WARN, L_SCAN, "Entry exceeds PATH_MAX, discarding\n");
149
150	  while (fgets(buf, sizeof(buf), fp) != NULL)
151	    {
152	      if (buf[strlen(buf) - 1] == '\n')
153		break;
154	    }
155
156	  continue;
157	}
158
159      if ((buf[0] == ';') || (buf[0] == '#') || (buf[0] == '\n'))
160	continue;
161
162      while (isspace(buf[len - 1]))
163	{
164	  len--;
165	  buf[len] = '\0';
166	}
167
168      /* Absolute vs. relative path */
169      if (buf[0] == '/')
170	{
171	  entry = buf;
172	}
173      else
174	{
175	  ret = snprintf(rel_entry, sizeof(rel_entry),"%s/%s", pl_base, buf);
176	  if ((ret < 0) || (ret >= sizeof(rel_entry)))
177	    {
178	      DPRINTF(E_WARN, L_SCAN, "Skipping entry, PATH_MAX exceeded\n");
179
180	      continue;
181	    }
182
183	  entry = rel_entry;
184	}
185
186	filename = m_realpath(entry);
187	if (!filename)
188	  {
189	    DPRINTF(E_WARN, L_SCAN, "Could not determine real path for '%s': %s\n", entry, strerror(errno));
190
191	    continue;
192	  }
193
194	ret = db_pl_add_item_bypath(pl_id, filename);
195	if (ret < 0)
196	  DPRINTF(E_WARN, L_SCAN, "Could not add %s to playlist\n", filename);
197
198	free(filename);
199    }
200
201  free(pl_base);
202
203  if (!feof(fp))
204    {
205      DPRINTF(E_LOG, L_SCAN, "Error reading playlist '%s': %s\n", file, strerror(errno));
206
207      fclose(fp);
208      return;
209    }
210
211  fclose(fp);
212
213  DPRINTF(E_INFO, L_SCAN, "Done processing playlist\n");
214}
215