* Fix some possible memory leaks.

* Pay attention to the specified port number.
* Add support for multiple media directories, and allow content filtering on the directories.
This commit is contained in:
Justin Maggard 2009-02-03 03:36:59 +00:00
parent 3f454a5762
commit 0324818d86
11 changed files with 281 additions and 137 deletions

View File

@ -222,6 +222,7 @@ find_album_art(const char * path)
} }
else else
{ {
sqlite3_free(sql);
sql = sqlite3_mprintf( "INSERT into ALBUM_ART" sql = sqlite3_mprintf( "INSERT into ALBUM_ART"
" (PATH, EMBEDDED) " " (PATH, EMBEDDED) "
"VALUES" "VALUES"
@ -231,6 +232,7 @@ find_album_art(const char * path)
if( sql_exec(db, sql) == SQLITE_OK ) if( sql_exec(db, sql) == SQLITE_OK )
ret = sqlite3_last_insert_rowid(db); ret = sqlite3_last_insert_rowid(db);
} }
sqlite3_free_table(result);
sqlite3_free(sql); sqlite3_free(sql);
} }
if( album_art ) if( album_art )

View File

@ -59,17 +59,34 @@ getifaddr(const char * ifname, char * buf, int len)
int int
getsysaddr(char * buf, int len) getsysaddr(char * buf, int len)
{ {
char hn[256]; int i;
struct in_addr *addr; int s = socket(PF_INET, SOCK_STREAM, 0);
struct hostent *host;
memset(buf, '\0', len); for (i=1; i > 0; i++)
gethostname(hn, sizeof(hn)); {
host = gethostbyname(hn); struct ifreq ifr;
if( !host ) struct sockaddr_in *addr = (struct sockaddr_in *) &ifr.ifr_addr;
return -1;
addr = (struct in_addr*)host->h_addr; ifr.ifr_ifindex = i;
strncpy(buf, inet_ntoa(*addr), len); if( ioctl(s, SIOCGIFNAME, &ifr) < 0 )
break;
if(ioctl(s, SIOCGIFADDR, &ifr, sizeof(struct ifreq)) < 0)
{
syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
close(s);
return -1;
}
if(strncmp(inet_ntoa(addr->sin_addr), "127.", 4) == 0)
continue;
if(!inet_ntop(AF_INET, &addr->sin_addr, buf, len))
{
syslog(LOG_ERR, "inet_ntop(): %m");
close(s);
return -1;
}
break;
}
close(s);
return 0; return 0;
} }

View File

@ -139,17 +139,6 @@ set_startup_time(int sysuptime)
} }
} }
/* structure containing variables used during "main loop"
* that are filled during the init */
struct runtime_vars {
/* LAN IP addresses for SSDP traffic and HTTP */
/* moved to global vars */
/*int n_lan_addr;*/
/*struct lan_addr_s lan_addr[MAX_LAN_ADDR];*/
int port; /* HTTP Port */
int notify_interval; /* seconds between SSDP announces */
};
/* parselanaddr() /* parselanaddr()
* parse address with mask * parse address with mask
* ex: 192.168.1.1/24 * ex: 192.168.1.1/24
@ -224,7 +213,7 @@ getfriendlyname(char * buf, int len)
* 7) compute presentation URL * 7) compute presentation URL
* 8) set signal handlers */ * 8) set signal handlers */
static int static int
init(int argc, char * * argv, struct runtime_vars * v) init(int argc, char * * argv)
{ {
int i; int i;
int pid; int pid;
@ -261,7 +250,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN); getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
/*v->n_lan_addr = 0;*/
char ext_ip_addr[INET_ADDRSTRLEN]; char ext_ip_addr[INET_ADDRSTRLEN];
if( (getsysaddr(ext_ip_addr, INET_ADDRSTRLEN) < 0) && if( (getsysaddr(ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
(getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) && (getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
@ -272,8 +260,8 @@ init(int argc, char * * argv, struct runtime_vars * v)
} }
if( parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 ) if( parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 )
n_lan_addr++; n_lan_addr++;
v->port = -1; runtime_vars.port = -1;
v->notify_interval = 30; /* seconds between SSDP announces */ runtime_vars.notify_interval = 30; /* seconds between SSDP announces */
/* read options file first since /* read options file first since
* command line arguments have final say */ * command line arguments have final say */
@ -290,12 +278,11 @@ init(int argc, char * * argv, struct runtime_vars * v)
switch(ary_options[i].id) switch(ary_options[i].id)
{ {
case UPNPLISTENING_IP: case UPNPLISTENING_IP:
if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/ if(n_lan_addr < MAX_LAN_ADDR)
{ {
/*if(parselanaddr(&v->lan_addr[v->n_lan_addr],*/
if(parselanaddr(&lan_addr[n_lan_addr], if(parselanaddr(&lan_addr[n_lan_addr],
ary_options[i].value) == 0) ary_options[i].value) == 0)
n_lan_addr++; /*v->n_lan_addr++; */ n_lan_addr++;
} }
else else
{ {
@ -304,13 +291,13 @@ init(int argc, char * * argv, struct runtime_vars * v)
} }
break; break;
case UPNPPORT: case UPNPPORT:
v->port = atoi(ary_options[i].value); runtime_vars.port = atoi(ary_options[i].value);
break; break;
case UPNPPRESENTATIONURL: case UPNPPRESENTATIONURL:
presurl = ary_options[i].value; presurl = ary_options[i].value;
break; break;
case UPNPNOTIFY_INTERVAL: case UPNPNOTIFY_INTERVAL:
v->notify_interval = atoi(ary_options[i].value); runtime_vars.notify_interval = atoi(ary_options[i].value);
break; break;
case UPNPSYSTEM_UPTIME: case UPNPSYSTEM_UPTIME:
if(strcmp(ary_options[i].value, "yes") == 0) if(strcmp(ary_options[i].value, "yes") == 0)
@ -337,8 +324,54 @@ init(int argc, char * * argv, struct runtime_vars * v)
friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0'; friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
break; break;
case UPNPMEDIADIR: case UPNPMEDIADIR:
strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN); usleep(1);
media_dir[MEDIADIR_MAX_LEN-1] = '\0'; enum media_types type = ALL_MEDIA;
char * myval = NULL;
switch( ary_options[i].value[0] )
{
case 'A':
case 'a':
if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' )
type = AUDIO_ONLY;
case 'V':
case 'v':
if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' )
type = VIDEO_ONLY;
case 'P':
case 'p':
if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' )
type = IMAGES_ONLY;
myval = index(ary_options[i].value, '/');
case '/':
usleep(1);
char * path = realpath(myval ? myval:ary_options[i].value, NULL);
if( access(path, F_OK) != 0 )
{
fprintf(stderr, "Media directory not accessible! [%s]\n",
path);
free(path);
break;
}
struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s));
this_dir->path = path;
this_dir->type = type;
if( !media_dirs )
{
media_dirs = this_dir;
}
else
{
struct media_dir_s * all_dirs = media_dirs;
while( all_dirs->next )
all_dirs = all_dirs->next;
all_dirs->next = this_dir;
}
break;
default:
fprintf(stderr, "Media directory entry not understood! [%s]\n",
ary_options[i].value);
break;
}
break; break;
default: default:
fprintf(stderr, "Unknown option in file %s\n", fprintf(stderr, "Unknown option in file %s\n",
@ -358,7 +391,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
{ {
case 't': case 't':
if(i+1 < argc) if(i+1 < argc)
v->notify_interval = atoi(argv[++i]); runtime_vars.notify_interval = atoi(argv[++i]);
else else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break; break;
@ -392,7 +425,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
break; break;
case 'p': case 'p':
if(i+1 < argc) if(i+1 < argc)
v->port = atoi(argv[++i]); runtime_vars.port = atoi(argv[++i]);
else else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break; break;
@ -417,22 +450,19 @@ init(int argc, char * * argv, struct runtime_vars * v)
int address_already_there = 0; int address_already_there = 0;
int j; int j;
i++; i++;
for(j=0; j<n_lan_addr; j++)/* for(j=0; j<v->n_lan_addr; j++)*/ for(j=0; j<n_lan_addr; j++)
{ {
struct lan_addr_s tmpaddr; struct lan_addr_s tmpaddr;
parselanaddr(&tmpaddr, argv[i]); parselanaddr(&tmpaddr, argv[i]);
/*if(0 == strcmp(v->lan_addr[j].str, tmpaddr.str))*/
if(0 == strcmp(lan_addr[j].str, tmpaddr.str)) if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
address_already_there = 1; address_already_there = 1;
} }
if(address_already_there) if(address_already_there)
break; break;
if(n_lan_addr < MAX_LAN_ADDR) /*if(v->n_lan_addr < MAX_LAN_ADDR)*/ if(n_lan_addr < MAX_LAN_ADDR)
{ {
/*v->lan_addr[v->n_lan_addr++] = argv[i];*/
/*if(parselanaddr(&v->lan_addr[v->n_lan_addr], argv[i]) == 0)*/
if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0) if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0)
n_lan_addr++; /*v->n_lan_addr++;*/ n_lan_addr++;
} }
else else
{ {
@ -450,7 +480,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
fprintf(stderr, "Unknown option: %s\n", argv[i]); fprintf(stderr, "Unknown option: %s\n", argv[i]);
} }
} }
if( (/*v->*/n_lan_addr==0) || (v->port<=0) ) if( (n_lan_addr==0) || (runtime_vars.port<=0) )
{ {
fprintf(stderr, "Usage:\n\t" fprintf(stderr, "Usage:\n\t"
"%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n" "%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
@ -521,8 +551,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
{ {
snprintf(presentationurl, PRESENTATIONURL_MAX_LEN, snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
"http://%s/", lan_addr[0].str); "http://%s/", lan_addr[0].str);
/*"http://%s:%d/", lan_addr[0].str, 80);*/
/*"http://%s:%d/", v->lan_addr[0].str, 80);*/
} }
/* set signal handler */ /* set signal handler */
@ -566,19 +594,36 @@ main(int argc, char * * argv)
#endif #endif
struct timeval timeout, timeofday, lasttimeofday = {0, 0}; struct timeval timeout, timeofday, lasttimeofday = {0, 0};
int max_fd = -1; int max_fd = -1;
struct runtime_vars v;
if(init(argc, argv, &v) != 0) if(init(argc, argv) != 0)
return 1; return 1;
LIST_INIT(&upnphttphead); LIST_INIT(&upnphttphead);
if( access(DB_PATH, F_OK) ) if( access(DB_PATH, F_OK) )
{ {
struct media_dir_s * media_path = media_dirs;
sqlite3_open(DB_PATH, &db); sqlite3_open(DB_PATH, &db);
freopen("/dev/null", "a", stderr); freopen("/dev/null", "a", stderr);
ScanDirectory(media_dir, NULL); if( CreateDatabase() != 0 )
{
fprintf(stderr, "Error creating database!\n");
return -1;
}
#if USE_FORK
pid_t newpid = fork();
if( newpid )
goto fork_done;
#endif
while( media_path )
{
ScanDirectory(media_path->path, NULL, media_path->type);
media_path = media_path->next;
}
freopen("/proc/self/fd/2", "a", stderr); freopen("/proc/self/fd/2", "a", stderr);
#if USE_FORK
_exit(0);
#endif
} }
else else
{ {
@ -588,21 +633,40 @@ main(int argc, char * * argv)
if( sqlite3_get_table(db, "pragma user_version", &result, &rows, 0, 0) == SQLITE_OK ) if( sqlite3_get_table(db, "pragma user_version", &result, &rows, 0, 0) == SQLITE_OK )
{ {
if( atoi(result[1]) != DB_VERSION ) { if( atoi(result[1]) != DB_VERSION ) {
struct media_dir_s * media_path = media_dirs;
printf("Database version mismatch; need to recreate...\n"); printf("Database version mismatch; need to recreate...\n");
sqlite3_close(db); sqlite3_close(db);
unlink(DB_PATH); unlink(DB_PATH);
sqlite3_open(DB_PATH, &db); sqlite3_open(DB_PATH, &db);
freopen("/dev/null", "a", stderr); freopen("/dev/null", "a", stderr);
ScanDirectory(media_dir, NULL); if( CreateDatabase() != 0 )
{
fprintf(stderr, "Error creating database!\n");
return -1;
}
#if USE_FORK
pid_t newpid = fork();
if( newpid )
goto fork_done;
#endif
while( media_path )
{
ScanDirectory(media_path->path, NULL, media_path->type);
media_path = media_path->next;
}
ScanDirectory(media_dirs->path, NULL, media_dirs->type);
freopen("/proc/self/fd/2", "a", stderr); freopen("/proc/self/fd/2", "a", stderr);
#if USE_FORK
_exit(0);
#endif
} }
sqlite3_free_table(result); sqlite3_free_table(result);
} }
} }
#if USE_FORK
fork_done:
#endif
/* open socket for SSDP connections */
/*sudp = OpenAndConfSSDPReceiveSocket(v.n_lan_addr, v.lan_addr);*/
sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr);
if(sudp < 0) if(sudp < 0)
{ {
@ -610,13 +674,13 @@ main(int argc, char * * argv)
return 1; return 1;
} }
/* open socket for HTTP connections. Listen on the 1st LAN address */ /* open socket for HTTP connections. Listen on the 1st LAN address */
shttpl = OpenAndConfHTTPSocket(v.port); shttpl = OpenAndConfHTTPSocket(runtime_vars.port);
if(shttpl < 0) if(shttpl < 0)
{ {
syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
return 1; return 1;
} }
syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); syslog(LOG_NOTICE, "HTTP listening on port %d", runtime_vars.port);
/* open socket for sending notifications */ /* open socket for sending notifications */
if(OpenAndConfSSDPNotifySockets(snotify) < 0) if(OpenAndConfSSDPNotifySockets(snotify) < 0)
@ -636,24 +700,24 @@ main(int argc, char * * argv)
if(gettimeofday(&timeofday, 0) < 0) if(gettimeofday(&timeofday, 0) < 0)
{ {
syslog(LOG_ERR, "gettimeofday(): %m"); syslog(LOG_ERR, "gettimeofday(): %m");
timeout.tv_sec = v.notify_interval; timeout.tv_sec = runtime_vars.notify_interval;
timeout.tv_usec = 0; timeout.tv_usec = 0;
} }
else else
{ {
/* the comparaison is not very precise but who cares ? */ /* the comparaison is not very precise but who cares ? */
if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval)) if(timeofday.tv_sec >= (lasttimeofday.tv_sec + runtime_vars.notify_interval))
{ {
SendSSDPNotifies2(snotify, SendSSDPNotifies2(snotify,
(unsigned short)v.port, (unsigned short)runtime_vars.port,
(v.notify_interval << 1)+10); (runtime_vars.notify_interval << 1)+10);
memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval)); memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval));
timeout.tv_sec = v.notify_interval; timeout.tv_sec = runtime_vars.notify_interval;
timeout.tv_usec = 0; timeout.tv_usec = 0;
} }
else else
{ {
timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval timeout.tv_sec = lasttimeofday.tv_sec + runtime_vars.notify_interval
- timeofday.tv_sec; - timeofday.tv_sec;
if(timeofday.tv_usec > lasttimeofday.tv_usec) if(timeofday.tv_usec > lasttimeofday.tv_usec)
{ {
@ -724,8 +788,7 @@ main(int argc, char * * argv)
if(sudp >= 0 && FD_ISSET(sudp, &readset)) if(sudp >= 0 && FD_ISSET(sudp, &readset))
{ {
/*syslog(LOG_INFO, "Received UDP Packet");*/ /*syslog(LOG_INFO, "Received UDP Packet");*/
/*ProcessSSDPRequest(sudp, v.lan_addr, v.n_lan_addr,*/ ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port);
ProcessSSDPRequest(sudp, (unsigned short)v.port);
} }
/* process active HTTP connections */ /* process active HTTP connections */
/* LIST_FOREACH macro is not available under linux */ /* LIST_FOREACH macro is not available under linux */
@ -802,7 +865,7 @@ shutdown:
{ {
syslog(LOG_ERR, "Failed to broadcast good-bye notifications"); syslog(LOG_ERR, "Failed to broadcast good-bye notifications");
} }
for(i=0; i<n_lan_addr; i++)/* for(i=0; i<v.n_lan_addr; i++)*/ for(i=0; i<n_lan_addr; i++)
close(snotify[i]); close(snotify[i]);
sqlite3_close(db); sqlite3_close(db);

View File

@ -4,6 +4,13 @@ port=5555
# enable UPNP support (default is yes) # enable UPNP support (default is yes)
enable_upnp=yes enable_upnp=yes
# set this to the directory you want scanned.
# * if have multiple directories, you can have multiple media_dir= lines
# * if you want to restrict a media_dir to a specific content type, you
# can prepend the type, followed by a comma, to the directory:
# + "A" for audio (eg. media_dir=A,/home/jmaggard/Music)
# + "V" for video (eg. media_dir=V,/home/jmaggard/Videos)
# + "P" for images (eg. media_dir=P,/home/jmaggard/Pictures)
media_dir=/opt media_dir=/opt
# set this if you want to customize the name that shows up on your clients # set this if you want to customize the name that shows up on your clients

View File

@ -16,4 +16,22 @@ struct lan_addr_s {
struct in_addr addr, mask; /* ip/mask */ struct in_addr addr, mask; /* ip/mask */
}; };
struct runtime_vars_s {
int port; /* HTTP Port */
int notify_interval; /* seconds between SSDP announces */
};
enum media_types {
ALL_MEDIA,
AUDIO_ONLY,
VIDEO_ONLY,
IMAGES_ONLY
};
struct media_dir_s {
char * path; /* Base path */
enum media_types type; /* type of files to scan */
struct media_dir_s * next;
};
#endif #endif

150
scanner.c
View File

@ -57,6 +57,26 @@ is_image(const char * file)
return (ends_with(file, ".jpg") || ends_with(file, ".jpeg")); return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
} }
sqlite_int64
get_next_available_id(const char * table, const char * parentID)
{
char * sql;
char **result;
int ret;
sqlite_int64 objectID = 0;
asprintf(&sql, "SELECT OBJECT_ID, max(ID) from %s where PARENT_ID = '%s'", table, parentID);
ret = sql_get_table(db, sql, &result, NULL, NULL);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%llX", &objectID) == 1) )
{
objectID++;
}
sqlite3_free_table(result);
free(sql);
return objectID;
}
long long int long long int
insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent, insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent,
const char *class, const char *artist, const char *genre, const char *album_art) const char *class, const char *artist, const char *genre, const char *album_art)
@ -73,29 +93,13 @@ insert_container(const char * tmpTable, const char * item, const char * rootPare
if( cols ) if( cols )
{ {
sscanf(result[4], "%X", &parentID); sscanf(result[4], "%X", &parentID);
sqlite3_free_table(result); asprintf(&sql, "%s$%X", rootParent, parentID);
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID); objectID = get_next_available_id("OBJECTS", sql);
ret = sql_get_table(db, sql, &result, 0, &cols); free(sql);
sqlite3_free(sql);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
{
objectID++;
}
} }
else else
{ {
sqlite3_free_table(result); parentID = get_next_available_id("OBJECTS", rootParent);
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s'", rootParent);
sql_get_table(db, sql, &result, &rows, &cols);
sqlite3_free(sql);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &parentID) == 1) )
{
parentID++;
}
else
{
parentID = 0;
}
detailID = GetFolderMetadata(item, artist, genre, album_art); detailID = GetFolderMetadata(item, artist, genre, album_art);
sql = sqlite3_mprintf( "INSERT into OBJECTS" sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) " " (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) "
@ -301,13 +305,14 @@ insert_directory(const char * name, const char * path, const char * base, const
char * id_buf = NULL; char * id_buf = NULL;
char * parent_buf = NULL; char * parent_buf = NULL;
char **result; char **result;
char *dir = strdup(path); char *dir = NULL;
if( strcmp(base, BROWSEDIR_ID) != 0 ) if( strcmp(base, BROWSEDIR_ID) != 0 )
asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID); asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
if( refID ) if( refID )
{ {
dir = strdup(path);
dir = dirname(dir); dir = dirname(dir);
asprintf(&id_buf, "%s%s$%X", base, parentID, objectID); asprintf(&id_buf, "%s%s$%X", base, parentID, objectID);
asprintf(&parent_buf, "%s%s", base, parentID); asprintf(&parent_buf, "%s%s", base, parentID);
@ -315,19 +320,28 @@ insert_directory(const char * name, const char * path, const char * base, const
{ {
sql = sqlite3_mprintf("SELECT count(OBJECT_ID) from OBJECTS where OBJECT_ID = '%s'", id_buf); sql = sqlite3_mprintf("SELECT count(OBJECT_ID) from OBJECTS where OBJECT_ID = '%s'", id_buf);
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) ) if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
{
sqlite3_free_table(result);
sqlite3_free(sql);
break; break;
}
sqlite3_free_table(result);
sqlite3_free(sql);
/* Does not exist. Need to create, and may need to create parents also */ /* Does not exist. Need to create, and may need to create parents also */
sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID); sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) ) if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
{ {
detailID = atoi(result[1]); detailID = atoi(result[1]);
} }
sqlite3_free_table(result);
sqlite3_free(sql);
sql = sqlite3_mprintf( "INSERT into OBJECTS" sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
"VALUES" "VALUES"
" ('%s', '%s', %Q, '%lld', '%s', '%q')", " ('%s', '%s', %Q, '%lld', '%s', '%q')",
id_buf, parent_buf, refID, detailID, class, rindex(dir, '/')+1); id_buf, parent_buf, refID, detailID, class, rindex(dir, '/')+1);
sql_exec(db, sql); sql_exec(db, sql);
sqlite3_free(sql);
if( rindex(id_buf, '$') ) if( rindex(id_buf, '$') )
*rindex(id_buf, '$') = '\0'; *rindex(id_buf, '$') = '\0';
if( rindex(parent_buf, '$') ) if( rindex(parent_buf, '$') )
@ -336,8 +350,10 @@ insert_directory(const char * name, const char * path, const char * base, const
*rindex(refID, '$') = '\0'; *rindex(refID, '$') = '\0';
dir = dirname(dir); dir = dirname(dir);
} }
sqlite3_free(sql);
free(refID); free(refID);
free(parent_buf);
free(id_buf);
free(dir);
return 1; return 1;
} }
@ -365,16 +381,12 @@ insert_file(char * name, const char * path, const char * parentID, int object)
unsigned long int detailID = 0; unsigned long int detailID = 0;
char base[8]; char base[8];
char * typedir_parentID; char * typedir_parentID;
int typedir_objectID; int typedir_objectID = 0;
char * baseid;
static long unsigned int fileno = 0; static long unsigned int fileno = 0;
printf("Scanned %lu files...\r", fileno++); fflush(stdout); printf("Scanned %lu files...\r", fileno++); fflush(stdout);
sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
typedir_parentID = strdup(parentID);
sscanf(rindex(typedir_parentID, '$')+1, "%X", &typedir_objectID);
*rindex(typedir_parentID, '$') = '\0';
if( is_image(name) ) if( is_image(name) )
{ {
strcpy(base, IMAGE_DIR_ID); strcpy(base, IMAGE_DIR_ID);
@ -397,6 +409,15 @@ insert_file(char * name, const char * path, const char * parentID, int object)
if( !detailID ) if( !detailID )
return -1; return -1;
sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
typedir_parentID = strdup(parentID);
baseid = rindex(typedir_parentID, '$');
if( baseid )
{
sscanf(baseid+1, "%X", &typedir_objectID);
*baseid = '\0';
}
insert_directory(name, path, base, typedir_parentID, typedir_objectID); insert_directory(name, path, base, typedir_parentID, typedir_objectID);
sql = sqlite3_mprintf( "INSERT into OBJECTS" sql = sqlite3_mprintf( "INSERT into OBJECTS"
@ -406,6 +427,7 @@ insert_file(char * name, const char * path, const char * parentID, int object)
objectID, BROWSEDIR_ID, parentID, class, detailID, name); objectID, BROWSEDIR_ID, parentID, class, detailID, name);
//DEBUG printf("SQL: %s\n", sql); //DEBUG printf("SQL: %s\n", sql);
sql_exec(db, sql); sql_exec(db, sql);
sqlite3_free(sql);
sql = sqlite3_mprintf( "INSERT into OBJECTS" sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
@ -416,12 +438,14 @@ insert_file(char * name, const char * path, const char * parentID, int object)
sql_exec(db, sql); sql_exec(db, sql);
sqlite3_free(sql); sqlite3_free(sql);
free(typedir_parentID);
insert_containers(name, path, objectID, class, detailID); insert_containers(name, path, objectID, class, detailID);
return 0; return 0;
} }
int int
create_database(void) CreateDatabase(void)
{ {
int ret, i; int ret, i;
char sql_buf[512]; char sql_buf[512];
@ -569,42 +593,52 @@ filter_media(const struct dirent *d)
} }
void void
ScanDirectory(const char * dir, const char * parent) ScanDirectory(const char * dir, const char * parent, enum media_types type)
{ {
struct dirent **namelist; struct dirent **namelist;
int n, i; int n, i, startID = 0;
char parent_id[PATH_MAX]; char parent_id[PATH_MAX];
char full_path[PATH_MAX]; char full_path[PATH_MAX];
char * name; char * name = NULL;
#if USE_FORK
pid_t newpid;
#endif
if( !parent )
{
if( create_database() != 0 )
{
fprintf(stderr, "Error creating database!\n");
return;
}
#if USE_FORK
newpid = fork();
if( newpid )
return;
#endif
}
setlocale(LC_COLLATE, ""); setlocale(LC_COLLATE, "");
if( chdir(dir) != 0 ) if( chdir(dir) != 0 )
return; return;
printf("\nScanning %s\n", dir); printf("\nScanning %s\n", dir);
n = scandir(".", &namelist, filter_media, alphasort); switch( type )
{
case ALL_MEDIA:
n = scandir(".", &namelist, filter_media, alphasort);
break;
case AUDIO_ONLY:
n = scandir(".", &namelist, filter_audio, alphasort);
break;
case VIDEO_ONLY:
n = scandir(".", &namelist, filter_video, alphasort);
break;
case IMAGES_ONLY:
n = scandir(".", &namelist, filter_images, alphasort);
break;
default:
break;
}
if (n < 0) { if (n < 0) {
fprintf(stderr, "Error scanning %s [scandir]\n", dir); fprintf(stderr, "Error scanning %s [scandir]\n", dir);
return; return;
} }
/* sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID);
ret = sql_get_table(db, sql, &result, 0, &cols);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
{
objectID++;
}
*/
if( !parent )
startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
for (i=0; i < n; i++) { for (i=0; i < n; i++) {
name = NULL;
sprintf(full_path, "%s/%s", dir, namelist[i]->d_name); sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
if( index(namelist[i]->d_name, '&') ) if( index(namelist[i]->d_name, '&') )
{ {
@ -612,16 +646,19 @@ ScanDirectory(const char * dir, const char * parent)
} }
if( namelist[i]->d_type == DT_DIR ) if( namelist[i]->d_type == DT_DIR )
{ {
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i); insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
sprintf(parent_id, "%s$%X", (parent ? parent:""), i); sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID);
ScanDirectory(full_path, parent_id); ScanDirectory(full_path, parent_id, type);
} }
else else
{ {
insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i); insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i+startID);
} }
if( name ) if( name )
{
free(name); free(name);
name = NULL;
}
free(namelist[i]); free(namelist[i]);
} }
free(namelist); free(namelist);
@ -632,8 +669,5 @@ ScanDirectory(const char * dir, const char * parent)
else else
{ {
printf("Scanning \"%s\" finished!\n", dir); printf("Scanning \"%s\" finished!\n", dir);
#if USE_FORK
_exit(0);
#endif
} }
} }

View File

@ -15,7 +15,10 @@
#define VIDEO_DIR_ID "2$21" #define VIDEO_DIR_ID "2$21"
#define IMAGE_DIR_ID "3$22" #define IMAGE_DIR_ID "3$22"
int
CreateDatabase(void);
void void
ScanDirectory(const char * dir, const char * parent); ScanDirectory(const char * dir, const char * parent, enum media_types type);
#endif #endif

View File

@ -26,6 +26,7 @@ int logpackets = 0;
#endif #endif
struct runtime_vars_s runtime_vars;
int runtime_flags = 0; int runtime_flags = 0;
const char * pidfilename = "/var/run/minidlna.pid"; const char * pidfilename = "/var/run/minidlna.pid";
@ -44,5 +45,5 @@ struct lan_addr_s lan_addr[MAX_LAN_ADDR];
/* UPnP-A/V [DLNA] */ /* UPnP-A/V [DLNA] */
sqlite3 *db; sqlite3 *db;
char media_dir[MEDIADIR_MAX_LEN]; struct media_dir_s * media_dirs = NULL;
char friendly_name[FRIENDLYNAME_MAX_LEN]; char friendly_name[FRIENDLYNAME_MAX_LEN];

View File

@ -47,6 +47,7 @@
/* statup time */ /* statup time */
extern time_t startup_time; extern time_t startup_time;
extern struct runtime_vars_s runtime_vars;
/* runtime boolean flags */ /* runtime boolean flags */
extern int runtime_flags; extern int runtime_flags;
#define LOGPACKETSMASK 0x0001 #define LOGPACKETSMASK 0x0001
@ -81,7 +82,7 @@ extern struct lan_addr_s lan_addr[];
/* UPnP-A/V [DLNA] */ /* UPnP-A/V [DLNA] */
extern sqlite3 *db; extern sqlite3 *db;
#define MEDIADIR_MAX_LEN (256) #define MEDIADIR_MAX_LEN (256)
extern char media_dir[]; extern struct media_dir_s * media_dirs;
#define FRIENDLYNAME_MAX_LEN (64) #define FRIENDLYNAME_MAX_LEN (64)
extern char friendly_name[]; extern char friendly_name[];

View File

@ -1178,6 +1178,9 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
int sendfh; int sendfh;
#if USE_FORK #if USE_FORK
pid_t newpid = 0; pid_t newpid = 0;
newpid = fork();
if( newpid )
return;
#endif #endif
memset(header, 0, 1500); memset(header, 0, 1500);
@ -1192,15 +1195,10 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
sqlite3_free_table(result); sqlite3_free_table(result);
return; return;
} }
#if USE_FORK
newpid = fork();
if( newpid )
return;
#endif
path = result[3]; path = result[3];
mime = result[4]; mime = result[4];
dlna = result[5]; dlna = result[5];
printf("Serving DetailID: %s [%s]\n", object, path); printf("Serving DetailID: %s [%s]\n", object, path);
if( h->reqflags & FLAG_XFERSTREAMING ) if( h->reqflags & FLAG_XFERSTREAMING )

View File

@ -270,10 +270,10 @@ static int callback(void *args, int argc, char **argv, char **azColName)
} }
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) { if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
sprintf(str_buf, "&lt;upnp:albumArtURI %s" sprintf(str_buf, "&lt;upnp:albumArtURI %s"
"&gt;http://%s:5555/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;", "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
(!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID")) ? (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID")) ?
"dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"" : "", "dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"" : "",
lan_addr[0].str, album_art); lan_addr[0].str, runtime_vars.port, album_art);
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
} }
if( !passed_args->filter || strstr(passed_args->filter, "res") ) { if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
@ -303,26 +303,26 @@ static int callback(void *args, int argc, char **argv, char **azColName)
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
} }
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;" sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:5555/MediaItems/%s.dat" "http://%s:%d/MediaItems/%s.dat"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, detailID); mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
#if 0 //JPEG_RESIZE #if 0 //JPEG_RESIZE
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) { if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
sprintf(str_buf, "&lt;res " sprintf(str_buf, "&lt;res "
"protocolInfo=\"http-get:*:%s:%s\"&gt;" "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:5555/Resized/%s" "http://%s:%d/Resized/%s"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, id); mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, runtime_vars.port, id);
} }
#endif #endif
if( tn && atoi(tn) && dlna_pn ) { if( tn && atoi(tn) && dlna_pn ) {
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
strcat(passed_args->resp, "&lt;res "); strcat(passed_args->resp, "&lt;res ");
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;" sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:5555/Thumbnails/%s.dat" "http://%s:%d/Thumbnails/%s.dat"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, detailID); mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
} }
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
} }
@ -369,7 +369,7 @@ static int callback(void *args, int argc, char **argv, char **azColName)
} }
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) { if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
sprintf(str_buf, "&lt;upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"" sprintf(str_buf, "&lt;upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\""
"&gt;http://%s:5555/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;", lan_addr[0].str, album_art); "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;", lan_addr[0].str, runtime_vars.port, album_art);
strcat(passed_args->resp, str_buf); strcat(passed_args->resp, str_buf);
} }
sprintf(str_buf, "&lt;/container&gt;"); sprintf(str_buf, "&lt;/container&gt;");