* Introduce a new strcatf() function to help simplify some areas of the code.
This commit is contained in:
parent
4daad1291e
commit
23746a68bf
@ -44,6 +44,12 @@ struct runtime_vars_s {
|
|||||||
int notify_interval; /* seconds between SSDP announces */
|
int notify_interval; /* seconds between SSDP announces */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct string_s {
|
||||||
|
char *data; // ptr to start of memory area
|
||||||
|
int off;
|
||||||
|
int size;
|
||||||
|
};
|
||||||
|
|
||||||
enum media_types {
|
enum media_types {
|
||||||
ALL_MEDIA,
|
ALL_MEDIA,
|
||||||
AUDIO_ONLY,
|
AUDIO_ONLY,
|
||||||
|
199
tivo_commands.c
199
tivo_commands.c
@ -20,6 +20,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "tivo_utils.h"
|
#include "tivo_utils.h"
|
||||||
@ -84,7 +85,8 @@ SendRootContainer(struct upnphttp * h)
|
|||||||
"</Content>"
|
"</Content>"
|
||||||
"</Links>"
|
"</Links>"
|
||||||
"</Item>"
|
"</Item>"
|
||||||
"</TiVoContainer>", friendly_name, friendly_name, friendly_name, friendly_name);
|
"</TiVoContainer>",
|
||||||
|
friendly_name, friendly_name, friendly_name, friendly_name);
|
||||||
BuildResp_upnphttp(h, resp, len);
|
BuildResp_upnphttp(h, resp, len);
|
||||||
free(resp);
|
free(resp);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
@ -110,8 +112,8 @@ int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5],
|
char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5],
|
||||||
*bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10],
|
*bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10],
|
||||||
*comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14], *path = argv[15];
|
*comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14], *path = argv[15];
|
||||||
char str_buf[4096];
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
struct string_s *str = passed_args->str;
|
||||||
|
|
||||||
if( strncmp(class, "item", 4) == 0 )
|
if( strncmp(class, "item", 4) == 0 )
|
||||||
{
|
{
|
||||||
@ -120,44 +122,34 @@ int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
if( strncmp(mime, "audio", 5) == 0 )
|
if( strncmp(mime, "audio", 5) == 0 )
|
||||||
{
|
{
|
||||||
flags |= FLAG_NO_PARAMS;
|
flags |= FLAG_NO_PARAMS;
|
||||||
ret = sprintf(str_buf, "<Item><Details>"
|
ret = strcatf(str, "<Item><Details>"
|
||||||
"<ContentType>audio/*</ContentType>"
|
"<ContentType>audio/*</ContentType>"
|
||||||
"<SourceFormat>%s</SourceFormat>"
|
"<SourceFormat>%s</SourceFormat>"
|
||||||
"<SourceSize>%s</SourceSize>"
|
"<SourceSize>%s</SourceSize>"
|
||||||
"<SongTitle>%s</SongTitle>", mime, size, title);
|
"<SongTitle>%s</SongTitle>", mime, size, title);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<AlbumYear>%.*s</AlbumYear>", 4, date);
|
ret = strcatf(str, "<AlbumYear>%.*s</AlbumYear>", 4, date);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( strcmp(mime, "image/jpeg") == 0 )
|
else if( strcmp(mime, "image/jpeg") == 0 )
|
||||||
{
|
{
|
||||||
flags |= FLAG_SEND_RESIZED;
|
flags |= FLAG_SEND_RESIZED;
|
||||||
ret = sprintf(str_buf, "<Item><Details>"
|
ret = strcatf(str, "<Item><Details>"
|
||||||
"<ContentType>image/*</ContentType>"
|
"<ContentType>image/*</ContentType>"
|
||||||
"<SourceFormat>image/jpeg</SourceFormat>"
|
"<SourceFormat>image/jpeg</SourceFormat>"
|
||||||
"<SourceSize>%s</SourceSize>", size);
|
"<SourceSize>%s</SourceSize>", size);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
memset(&tm, 0, sizeof(tm));
|
memset(&tm, 0, sizeof(tm));
|
||||||
tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
|
tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
|
||||||
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
ret = sprintf(str_buf, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
ret = strcatf(str, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( comment )
|
if( comment )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<Caption>%s</Caption>", comment);
|
ret = strcatf(str, "<Caption>%s</Caption>", comment);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( strncmp(mime, "video", 5) == 0 )
|
else if( strncmp(mime, "video", 5) == 0 )
|
||||||
@ -165,111 +157,85 @@ int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
char *episode;
|
char *episode;
|
||||||
flags |= FLAG_NO_PARAMS;
|
flags |= FLAG_NO_PARAMS;
|
||||||
flags |= FLAG_VIDEO;
|
flags |= FLAG_VIDEO;
|
||||||
ret = sprintf(str_buf, "<Item><Details>"
|
ret = strcatf(str, "<Item><Details>"
|
||||||
"<ContentType>video/x-tivo-mpeg</ContentType>"
|
"<ContentType>video/x-tivo-mpeg</ContentType>"
|
||||||
"<SourceFormat>%s</SourceFormat>"
|
"<SourceFormat>%s</SourceFormat>"
|
||||||
"<SourceSize>%s</SourceSize>", mime, size);
|
"<SourceSize>%s</SourceSize>", mime, size);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
episode = strstr(title, " - ");
|
episode = strstr(title, " - ");
|
||||||
if( episode )
|
if( episode )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<Title>%.*s</Title>"
|
ret = strcatf(str, "<Title>%.*s</Title>"
|
||||||
"<EpisodeTitle>%s</EpisodeTitle>",
|
"<EpisodeTitle>%s</EpisodeTitle>",
|
||||||
episode-title, title, episode+3);
|
(int)(episode-title), title, episode+3);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<Title>%s</Title>", title);
|
ret = strcatf(str, "<Title>%s</Title>", title);
|
||||||
}
|
}
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
memset(&tm, 0, sizeof(tm));
|
memset(&tm, 0, sizeof(tm));
|
||||||
tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
|
tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
|
||||||
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
ret = sprintf(str_buf, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
ret = strcatf(str, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( comment )
|
if( comment )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<Description>%s</Description>", comment);
|
ret = strcatf(str, "<Description>%s</Description>", comment);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "<Title>%s</Title>", unescape_tag(title));
|
ret = strcatf(str, "<Title>%s</Title>", unescape_tag(title));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( artist ) {
|
if( artist ) {
|
||||||
ret = sprintf(str_buf, "<ArtistName>%s</ArtistName>", unescape_tag(artist));
|
ret = strcatf(str, "<ArtistName>%s</ArtistName>", unescape_tag(artist));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( album ) {
|
if( album ) {
|
||||||
ret = sprintf(str_buf, "<AlbumTitle>%s</AlbumTitle>", unescape_tag(album));
|
ret = strcatf(str, "<AlbumTitle>%s</AlbumTitle>", unescape_tag(album));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( genre ) {
|
if( genre ) {
|
||||||
ret = sprintf(str_buf, "<MusicGenre>%s</MusicGenre>", unescape_tag(genre));
|
ret = strcatf(str, "<MusicGenre>%s</MusicGenre>", unescape_tag(genre));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( resolution ) {
|
if( resolution ) {
|
||||||
char *width = strsep(&resolution, "x");
|
char *width = strsep(&resolution, "x");
|
||||||
ret = sprintf(str_buf, "<SourceWidth>%s</SourceWidth>"
|
ret = strcatf(str, "<SourceWidth>%s</SourceWidth>"
|
||||||
"<SourceHeight>%s</SourceHeight>",
|
"<SourceHeight>%s</SourceHeight>",
|
||||||
width, resolution);
|
width, resolution);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( duration ) {
|
if( duration ) {
|
||||||
ret = sprintf(str_buf, "<Duration>%d</Duration>",
|
ret = strcatf(str, "<Duration>%d</Duration>",
|
||||||
atoi(strrchr(duration, '.')+1) + (1000*atoi(strrchr(duration, ':')+1))
|
atoi(strrchr(duration, '.')+1) + (1000*atoi(strrchr(duration, ':')+1))
|
||||||
+ (60000*atoi(strrchr(duration, ':')-2)) + (3600000*atoi(duration)));
|
+ (60000*atoi(strrchr(duration, ':')-2)) + (3600000*atoi(duration)));
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( bitrate ) {
|
if( bitrate ) {
|
||||||
ret = sprintf(str_buf, "<SourceBitRate>%s</SourceBitRate>", bitrate);
|
ret = strcatf(str, "<SourceBitRate>%s</SourceBitRate>", bitrate);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( sampleFrequency ) {
|
if( sampleFrequency ) {
|
||||||
ret = sprintf(str_buf, "<SourceSampleRate>%s</SourceSampleRate>", sampleFrequency);
|
ret = strcatf(str, "<SourceSampleRate>%s</SourceSampleRate>", sampleFrequency);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "</Details><Links><Content>"
|
ret = strcatf(str, "</Details><Links><Content>"
|
||||||
"<ContentType>%s</ContentType>"
|
"<ContentType>%s</ContentType>"
|
||||||
"<Url>/%s/%s.dat</Url>%s</Content>",
|
"<Url>/%s/%s.dat</Url>%s</Content>",
|
||||||
mime,
|
mime,
|
||||||
(flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID,
|
(flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID,
|
||||||
(flags & FLAG_NO_PARAMS)?"<AcceptsParams>No</AcceptsParams>":"");
|
(flags & FLAG_NO_PARAMS)?"<AcceptsParams>No</AcceptsParams>":"");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( flags & FLAG_VIDEO )
|
if( flags & FLAG_VIDEO )
|
||||||
{
|
{
|
||||||
char *esc_name = escape_tag(basename(path), 1);
|
char *esc_name = escape_tag(basename(path), 1);
|
||||||
ret = sprintf(str_buf, "<CustomIcon>"
|
ret = strcatf(str, "<CustomIcon>"
|
||||||
"<ContentType>video/*</ContentType>"
|
"<ContentType>video/*</ContentType>"
|
||||||
"<Url>urn:tivo:image:save-until-i-delete-recording</Url>"
|
"<Url>urn:tivo:image:save-until-i-delete-recording</Url>"
|
||||||
"</CustomIcon>"
|
"</CustomIcon>"
|
||||||
"<Push><Container>Videos</Container></Push>"
|
"<Push><Container>Videos</Container></Push>"
|
||||||
"<File>%s</File> </Links>", esc_name);
|
"<File>%s</File> </Links>", esc_name);
|
||||||
free(esc_name);
|
free(esc_name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "</Links>");
|
ret = strcatf(str, "</Links>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( strncmp(class, "container", 9) == 0 )
|
else if( strncmp(class, "container", 9) == 0 )
|
||||||
@ -283,26 +249,22 @@ int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
" (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg')"
|
" (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg')"
|
||||||
" or CLASS glob 'container*')", id);
|
" or CLASS glob 'container*')", id);
|
||||||
#endif
|
#endif
|
||||||
ret = sprintf(str_buf, "<Item>"
|
ret = strcatf(str, "<Item>"
|
||||||
"<Details>"
|
"<Details>"
|
||||||
"<ContentType>x-container/folder</ContentType>"
|
"<ContentType>x-container/folder</ContentType>"
|
||||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||||
"<Title>%s</Title>"
|
"<Title>%s</Title>"
|
||||||
"<TotalItems>%d</TotalItems>"
|
"<TotalItems>%d</TotalItems>"
|
||||||
"</Details>"
|
"</Details>"
|
||||||
"<Links>"
|
"<Links>"
|
||||||
"<Content>"
|
"<Content>"
|
||||||
"<Url>/TiVoConnect?Command=QueryContainer&Container=%s</Url>"
|
"<Url>/TiVoConnect?Command=QueryContainer&Container=%s</Url>"
|
||||||
"<ContentType>x-tivo-container/folder</ContentType>"
|
"<ContentType>x-tivo-container/folder</ContentType>"
|
||||||
"</Content>"
|
"</Content>"
|
||||||
"</Links>",
|
"</Links>",
|
||||||
unescape_tag(title), count, id);
|
unescape_tag(title), count, id);
|
||||||
}
|
}
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
ret = strcatf(str, "</Item>");
|
||||||
passed_args->size += ret;
|
|
||||||
ret = sprintf(str_buf, "</Item>");
|
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
|
|
||||||
passed_args->returned++;
|
passed_args->returned++;
|
||||||
|
|
||||||
@ -320,11 +282,13 @@ SendItemDetails(struct upnphttp * h, sqlite_int64 item)
|
|||||||
char *sql;
|
char *sql;
|
||||||
char *zErrMsg = NULL;
|
char *zErrMsg = NULL;
|
||||||
struct Response args;
|
struct Response args;
|
||||||
|
struct string_s str;
|
||||||
int ret;
|
int ret;
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
|
memset(&str, 0, sizeof(str));
|
||||||
|
|
||||||
args.resp = resp;
|
str.data = resp;
|
||||||
args.size = sprintf(resp, "<?xml version='1.0' encoding='UTF-8' ?>\n<TiVoItem>");
|
ret = strcatf(&str, "<?xml version='1.0' encoding='UTF-8' ?>\n<TiVoItem>");
|
||||||
args.requested = 1;
|
args.requested = 1;
|
||||||
asprintf(&sql, SELECT_COLUMNS
|
asprintf(&sql, SELECT_COLUMNS
|
||||||
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
||||||
@ -337,10 +301,10 @@ SendItemDetails(struct upnphttp * h, sqlite_int64 item)
|
|||||||
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
|
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
|
||||||
sqlite3_free(zErrMsg);
|
sqlite3_free(zErrMsg);
|
||||||
}
|
}
|
||||||
strcat(resp, "</TiVoItem>");
|
strcatf(&str, "</TiVoItem>");
|
||||||
|
|
||||||
BuildResp_upnphttp(h, resp, strlen(resp));
|
BuildResp_upnphttp(h, str.data, str.off);
|
||||||
free(resp);
|
free(str.data);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,12 +322,15 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
char type[8];
|
char type[8];
|
||||||
char groupBy[19] = {0};
|
char groupBy[19] = {0};
|
||||||
struct Response args;
|
struct Response args;
|
||||||
|
struct string_s str;
|
||||||
int totalMatches = 0;
|
int totalMatches = 0;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
|
memset(&str, 0, sizeof(str));
|
||||||
|
|
||||||
args.resp = resp;
|
args.str = &str;
|
||||||
args.size = 1024;
|
str.data = resp+1024;
|
||||||
|
str.size = 262144-1024;
|
||||||
if( itemCount >= 0 )
|
if( itemCount >= 0 )
|
||||||
{
|
{
|
||||||
args.requested = itemCount;
|
args.requested = itemCount;
|
||||||
@ -653,6 +620,7 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
free(resp);
|
free(resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
strcatf(&str, "</TiVoContainer>");
|
||||||
|
|
||||||
ret = sprintf(str_buf, "<?xml version='1.0' encoding='UTF-8' ?>\n"
|
ret = sprintf(str_buf, "<?xml version='1.0' encoding='UTF-8' ?>\n"
|
||||||
"<TiVoContainer>"
|
"<TiVoContainer>"
|
||||||
@ -665,15 +633,12 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
"<ItemStart>%d</ItemStart>"
|
"<ItemStart>%d</ItemStart>"
|
||||||
"<ItemCount>%d</ItemCount>",
|
"<ItemCount>%d</ItemCount>",
|
||||||
type, totalMatches, title, args.start, args.returned);
|
type, totalMatches, title, args.start, args.returned);
|
||||||
args.resp = resp+1024-ret;
|
str.data -= ret;
|
||||||
memcpy(args.resp, &str_buf, ret);
|
memcpy(str.data, &str_buf, ret);
|
||||||
ret = sprintf(str_buf, "</TiVoContainer>");
|
str.size = str.off+ret;
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
args.size -= args.resp-resp;
|
|
||||||
free(title);
|
free(title);
|
||||||
free(which);
|
free(which);
|
||||||
BuildResp_upnphttp(h, args.resp, args.size);
|
BuildResp_upnphttp(h, str.data, str.size);
|
||||||
free(resp);
|
free(resp);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
}
|
}
|
||||||
@ -696,7 +661,7 @@ ProcessTiVoCommand(struct upnphttp * h, const char * orig_path)
|
|||||||
item = strtok_r( path, "&", &saveptr );
|
item = strtok_r( path, "&", &saveptr );
|
||||||
while( item != NULL )
|
while( item != NULL )
|
||||||
{
|
{
|
||||||
if( strlen(item) == 0 )
|
if( *item == '\0' )
|
||||||
{
|
{
|
||||||
item = strtok_r( NULL, "&", &saveptr );
|
item = strtok_r( NULL, "&", &saveptr );
|
||||||
continue;
|
continue;
|
||||||
|
373
upnpsoap.c
373
upnpsoap.c
@ -61,13 +61,12 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "upnphttp.h"
|
#include "upnphttp.h"
|
||||||
#include "upnpsoap.h"
|
#include "upnpsoap.h"
|
||||||
#include "upnpreplyparse.h"
|
#include "upnpreplyparse.h"
|
||||||
#include "getifaddr.h"
|
#include "getifaddr.h"
|
||||||
|
|
||||||
#include "scanner.h"
|
#include "scanner.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "sql.h"
|
#include "sql.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@ -555,17 +554,13 @@ inline static void
|
|||||||
add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn,
|
add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn,
|
||||||
char *detailID, struct Response *passed_args)
|
char *detailID, struct Response *passed_args)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
int dstw = reqw;
|
int dstw = reqw;
|
||||||
int dsth = reqh;
|
int dsth = reqh;
|
||||||
char str_buf[256];
|
|
||||||
|
|
||||||
if( passed_args->flags & FLAG_NO_RESIZE )
|
if( passed_args->flags & FLAG_NO_RESIZE )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = sprintf(str_buf, "<res ");
|
strcatf(passed_args->str, "<res ");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( passed_args->filter & FILTER_RES_RESOLUTION )
|
if( passed_args->filter & FILTER_RES_RESOLUTION )
|
||||||
{
|
{
|
||||||
dstw = reqw;
|
dstw = reqw;
|
||||||
@ -574,17 +569,12 @@ add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn,
|
|||||||
dsth = reqh;
|
dsth = reqh;
|
||||||
dstw = (((reqh<<10)/srch) * srcw>>10);
|
dstw = (((reqh<<10)/srch) * srcw>>10);
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "resolution=\"%dx%d\" ", dstw, dsth);
|
strcatf(passed_args->str, "resolution=\"%dx%d\" ", dstw, dsth);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=%s;DLNA.ORG_CI=1\">"
|
strcatf(passed_args->str, "protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=%s;DLNA.ORG_CI=1\">"
|
||||||
"http://%s:%d/Resized/%s.jpg?width=%d,height=%d"
|
"http://%s:%d/Resized/%s.jpg?width=%d,height=%d"
|
||||||
"</res>",
|
"</res>",
|
||||||
dlna_pn, lan_addr[0].str, runtime_vars.port,
|
dlna_pn, lan_addr[0].str, runtime_vars.port, detailID, dstw, dsth);
|
||||||
detailID, dstw, dsth);
|
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void
|
inline static void
|
||||||
@ -592,48 +582,30 @@ add_res(char *size, char *duration, char *bitrate, char *sampleFrequency,
|
|||||||
char *nrAudioChannels, char *resolution, char *dlna_pn, char *mime,
|
char *nrAudioChannels, char *resolution, char *dlna_pn, char *mime,
|
||||||
char *detailID, char *ext, struct Response *passed_args)
|
char *detailID, char *ext, struct Response *passed_args)
|
||||||
{
|
{
|
||||||
int ret;
|
strcatf(passed_args->str, "<res ");
|
||||||
char str_buf[256];
|
|
||||||
|
|
||||||
ret = sprintf(str_buf, "<res ");
|
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
|
if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
|
||||||
ret = sprintf(str_buf, "size=\"%s\" ", size);
|
strcatf(passed_args->str, "size=\"%s\" ", size);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
|
if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
|
||||||
ret = sprintf(str_buf, "duration=\"%s\" ", duration);
|
strcatf(passed_args->str, "duration=\"%s\" ", duration);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
|
if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
|
||||||
ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
strcatf(passed_args->str, "bitrate=\"%s\" ", bitrate);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
|
if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
|
||||||
ret = sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
|
strcatf(passed_args->str, "sampleFrequency=\"%s\" ", sampleFrequency);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
|
if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
|
||||||
ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
strcatf(passed_args->str, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
|
if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
|
||||||
ret = sprintf(str_buf, "resolution=\"%s\" ", resolution);
|
strcatf(passed_args->str, "resolution=\"%s\" ", resolution);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = snprintf(str_buf, sizeof(str_buf), "protocolInfo=\"http-get:*:%s:%s\">"
|
strcatf(passed_args->str, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
"http://%s:%d/MediaItems/%s.%s"
|
"http://%s:%d/MediaItems/%s.%s"
|
||||||
"</res>",
|
"</res>",
|
||||||
mime, dlna_pn, lan_addr[0].str, runtime_vars.port, detailID, ext);
|
mime, dlna_pn, lan_addr[0].str,
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
runtime_vars.port, detailID, ext);
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS," \
|
#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS," \
|
||||||
@ -651,21 +623,22 @@ callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
*tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22];
|
*tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22];
|
||||||
char dlna_buf[96];
|
char dlna_buf[96];
|
||||||
char ext[5];
|
char ext[5];
|
||||||
char str_buf[512];
|
struct string_s *str = passed_args->str;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Make sure we have at least 4KB left of allocated memory to finish the response. */
|
/* Make sure we have at least 8KB left of allocated memory to finish the response. */
|
||||||
if( passed_args->size > (passed_args->alloced - 4096) )
|
if( str->off > (str->size - 8192) )
|
||||||
{
|
{
|
||||||
#if MAX_RESPONSE_SIZE > 0
|
#if MAX_RESPONSE_SIZE > 0
|
||||||
if( (passed_args->alloced+1048576) <= MAX_RESPONSE_SIZE )
|
if( (str->size+DEFAULT_RESP_SIZE) <= MAX_RESPONSE_SIZE )
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
passed_args->resp = realloc(passed_args->resp, (passed_args->alloced+1048576));
|
str->data = realloc(str->data, (str->off+DEFAULT_RESP_SIZE));
|
||||||
if( passed_args->resp )
|
if( str->data )
|
||||||
{
|
{
|
||||||
passed_args->alloced += 1048576;
|
str->size += DEFAULT_RESP_SIZE;
|
||||||
DPRINTF(E_DEBUG, L_HTTP, "HUGE RESPONSE ALERT: UPnP SOAP response had to be enlarged to %d. [%d results so far]\n", passed_args->alloced, passed_args->returned);
|
DPRINTF(E_DEBUG, L_HTTP, "UPnP SOAP response enlarged to %d. [%d results so far]\n",
|
||||||
|
str->size, passed_args->returned);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -739,120 +712,85 @@ callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snprintf(str_buf, 512, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
|
ret = strcatf(str, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( refID && (passed_args->filter & FILTER_REFID) ) {
|
if( refID && (passed_args->filter & FILTER_REFID) ) {
|
||||||
ret = sprintf(str_buf, " refID=\"%s\"", refID);
|
ret = strcatf(str, " refID=\"%s\"", refID);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = snprintf(str_buf, 512, ">"
|
ret = strcatf(str, ">"
|
||||||
"<dc:title>%s</dc:title>"
|
"<dc:title>%s</dc:title>"
|
||||||
"<upnp:class>object.%s</upnp:class>",
|
"<upnp:class>object.%s</upnp:class>",
|
||||||
title, class);
|
title, class);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
|
if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
|
||||||
ret = snprintf(str_buf, 512, "<dc:description>%.384s</dc:description>", comment);
|
ret = strcatf(str, "<dc:description>%.384s</dc:description>", comment);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
||||||
ret = snprintf(str_buf, 512, "<dc:creator>%s</dc:creator>", creator);
|
ret = strcatf(str, "<dc:creator>%s</dc:creator>", creator);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( date && (passed_args->filter & FILTER_DC_DATE) ) {
|
if( date && (passed_args->filter & FILTER_DC_DATE) ) {
|
||||||
ret = snprintf(str_buf, 512, "<dc:date>%s</dc:date>", date);
|
ret = strcatf(str, "<dc:date>%s</dc:date>", date);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( artist ) {
|
if( artist ) {
|
||||||
if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) {
|
if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:actor>%s</upnp:actor>", artist);
|
ret = strcatf(str, "<upnp:actor>%s</upnp:actor>", artist);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( passed_args->filter & FILTER_UPNP_ARTIST ) {
|
if( passed_args->filter & FILTER_UPNP_ARTIST ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:artist>%s</upnp:artist>", artist);
|
ret = strcatf(str, "<upnp:artist>%s</upnp:artist>", artist);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
|
if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:album>%s</upnp:album>", album);
|
ret = strcatf(str, "<upnp:album>%s</upnp:album>", album);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:genre>%s</upnp:genre>", genre);
|
ret = strcatf(str, "<upnp:genre>%s</upnp:genre>", genre);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( strncmp(id, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) {
|
if( strncmp(id, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) {
|
||||||
track = strrchr(id, '$')+1;
|
track = strrchr(id, '$')+1;
|
||||||
}
|
}
|
||||||
if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
|
if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
|
||||||
ret = sprintf(str_buf, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
ret = strcatf(str, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( album_art && atoi(album_art) )
|
if( album_art && atoi(album_art) )
|
||||||
{
|
{
|
||||||
/* Video and audio album art is handled differently */
|
/* Video and audio album art is handled differently */
|
||||||
if( *mime == 'v' && (passed_args->filter & FILTER_RES) && !(passed_args->flags & FLAG_MS_PFS) ) {
|
if( *mime == 'v' && (passed_args->filter & FILTER_RES) && !(passed_args->flags & FLAG_MS_PFS) ) {
|
||||||
ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\">"
|
ret = strcatf(str, "<res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\">"
|
||||||
"http://%s:%d/AlbumArt/%s-%s.jpg"
|
"http://%s:%d/AlbumArt/%s-%s.jpg"
|
||||||
"</res>",
|
"</res>",
|
||||||
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
} else if( passed_args->filter & FILTER_UPNP_ALBUMARTURI ) {
|
} else if( passed_args->filter & FILTER_UPNP_ALBUMARTURI ) {
|
||||||
ret = sprintf(str_buf, "<upnp:albumArtURI");
|
ret = strcatf(str, "<upnp:albumArtURI");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
||||||
ret = sprintf(str_buf, " dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"", "JPEG_TN");
|
ret = strcatf(str, " dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>",
|
ret = strcatf(str, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>",
|
||||||
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef PFS_HACK
|
#ifdef PFS_HACK
|
||||||
if( (passed_args->flags & FLAG_MS_PFS) && *mime == 'i' ) {
|
if( (passed_args->flags & FLAG_MS_PFS) && *mime == 'i' ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:album>%s</upnp:album>", "[No Keywords]");
|
ret = strcatf(str, "<upnp:album>%s</upnp:album>", "[No Keywords]");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
|
|
||||||
if( tn && atoi(tn) ) {
|
if( tn && atoi(tn) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:albumArtURI>"
|
ret = strcatf(str, "<upnp:albumArtURI>"
|
||||||
"http://%s:%d/Thumbnails/%s.jpg"
|
"http://%s:%d/Thumbnails/%s.jpg"
|
||||||
"</upnp:albumArtURI>",
|
"</upnp:albumArtURI>",
|
||||||
lan_addr[0].str, runtime_vars.port, detailID);
|
lan_addr[0].str, runtime_vars.port, detailID);
|
||||||
} else {
|
} else {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:albumArtURI>"
|
ret = strcatf(str, "<upnp:albumArtURI>"
|
||||||
"http://%s:%d/Resized/%s.jpg?width=160,height=160"
|
"http://%s:%d/Resized/%s.jpg?width=160,height=160"
|
||||||
"</upnp:albumArtURI>",
|
"</upnp:albumArtURI>",
|
||||||
lan_addr[0].str, runtime_vars.port, detailID);
|
lan_addr[0].str, runtime_vars.port, detailID);
|
||||||
}
|
}
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if( passed_args->filter & FILTER_RES ) {
|
if( passed_args->filter & FILTER_RES ) {
|
||||||
mime_to_ext(mime, ext);
|
mime_to_ext(mime, ext);
|
||||||
if( (passed_args->client == EFreeBox) && tn && atoi(tn) ) {
|
if( (passed_args->client == EFreeBox) && tn && atoi(tn) ) {
|
||||||
ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:%s:%s\">"
|
ret = strcatf(str, "<res protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
"http://%s:%d/Thumbnails/%s.jpg"
|
"http://%s:%d/Thumbnails/%s.jpg"
|
||||||
"</res>",
|
"</res>",
|
||||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str,
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
runtime_vars.port, detailID);
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
|
add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
|
||||||
resolution, dlna_buf, mime, detailID, ext, passed_args);
|
resolution, dlna_buf, mime, detailID, ext, passed_args);
|
||||||
@ -868,12 +806,11 @@ callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if( tn && atoi(tn) ) {
|
if( tn && atoi(tn) ) {
|
||||||
ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:%s:%s\">"
|
ret = strcatf(str, "<res protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
"http://%s:%d/Thumbnails/%s.jpg"
|
"http://%s:%d/Thumbnails/%s.jpg"
|
||||||
"</res>",
|
"</res>",
|
||||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str,
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
runtime_vars.port, detailID);
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( *mime == 'v' ) {
|
else if( *mime == 'v' ) {
|
||||||
@ -947,74 +884,52 @@ callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "</item>");
|
ret = strcatf(str, "</item>");
|
||||||
}
|
}
|
||||||
else if( strncmp(class, "container", 9) == 0 )
|
else if( strncmp(class, "container", 9) == 0 )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
|
ret = strcatf(str, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( passed_args->filter & FILTER_CHILDCOUNT )
|
if( passed_args->filter & FILTER_CHILDCOUNT )
|
||||||
{
|
{
|
||||||
int children;
|
int children;
|
||||||
ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", id);
|
ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", id);
|
||||||
children = (ret > 0) ? ret : 0;
|
children = (ret > 0) ? ret : 0;
|
||||||
ret = sprintf(str_buf, "childCount=\"%d\"", children);
|
ret = strcatf(str, "childCount=\"%d\"", children);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
|
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
|
||||||
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
|
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
|
||||||
{
|
{
|
||||||
if( passed_args->filter & FILTER_UPNP_SEARCHCLASS )
|
if( passed_args->filter & FILTER_UPNP_SEARCHCLASS )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, ">"
|
ret = strcatf(str, ">"
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = snprintf(str_buf, 512, ">"
|
ret = strcatf(str, ">"
|
||||||
"<dc:title>%s</dc:title>"
|
"<dc:title>%s</dc:title>"
|
||||||
"<upnp:class>object.%s</upnp:class>",
|
"<upnp:class>object.%s</upnp:class>",
|
||||||
title, class);
|
title, class);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
||||||
ret = snprintf(str_buf, 512, "<dc:creator>%s</dc:creator>", creator);
|
ret = strcatf(str, "<dc:creator>%s</dc:creator>", creator);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:genre>%s</upnp:genre>", genre);
|
ret = strcatf(str, "<upnp:genre>%s</upnp:genre>", genre);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
|
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
|
||||||
ret = snprintf(str_buf, 512, "<upnp:artist>%s</upnp:artist>", artist);
|
ret = strcatf(str, "<upnp:artist>%s</upnp:artist>", artist);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
|
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
|
||||||
ret = sprintf(str_buf, "<upnp:albumArtURI ");
|
ret = strcatf(str, "<upnp:albumArtURI ");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
||||||
ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"", "JPEG_TN");
|
ret = strcatf(str, "dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"");
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>",
|
ret = strcatf(str, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>",
|
||||||
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, "</container>");
|
ret = strcatf(str, "</container>");
|
||||||
}
|
}
|
||||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
|
||||||
passed_args->size += ret;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1028,16 +943,13 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
"<Result>"
|
"<Result>"
|
||||||
"<DIDL-Lite"
|
"<DIDL-Lite"
|
||||||
CONTENT_DIRECTORY_SCHEMAS;
|
CONTENT_DIRECTORY_SCHEMAS;
|
||||||
|
|
||||||
char *resp = malloc(1048576);
|
|
||||||
char str_buf[512];
|
|
||||||
char *zErrMsg = 0;
|
char *zErrMsg = 0;
|
||||||
char *sql, *ptr;
|
char *sql, *ptr;
|
||||||
int ret;
|
int ret;
|
||||||
struct Response args;
|
struct Response args;
|
||||||
|
struct string_s str;
|
||||||
int totalMatches;
|
int totalMatches;
|
||||||
struct NameValueParserData data;
|
struct NameValueParserData data;
|
||||||
*resp = '\0';
|
|
||||||
|
|
||||||
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
|
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
|
||||||
char * ObjectId = GetValueFromNameValueList(&data, "ObjectID");
|
char * ObjectId = GetValueFromNameValueList(&data, "ObjectID");
|
||||||
@ -1068,21 +980,19 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
goto browse_error;
|
goto browse_error;
|
||||||
}
|
}
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
|
memset(&str, 0, sizeof(str));
|
||||||
|
|
||||||
args.alloced = 1048576;
|
str.data = malloc(DEFAULT_RESP_SIZE);
|
||||||
args.resp = resp;
|
str.size = DEFAULT_RESP_SIZE;
|
||||||
args.size = sprintf(resp, "%s", resp0);
|
str.off = sprintf(str.data, "%s", resp0);
|
||||||
|
args.str = &str;
|
||||||
/* See if we need to include DLNA namespace reference */
|
/* See if we need to include DLNA namespace reference */
|
||||||
args.filter = set_filter_flags(Filter, h->req_client);
|
args.filter = set_filter_flags(Filter, h->req_client);
|
||||||
if( args.filter & FILTER_DLNA_NAMESPACE )
|
if( args.filter & FILTER_DLNA_NAMESPACE )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, DLNA_NAMESPACE);
|
ret = strcatf(&str, DLNA_NAMESPACE);
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, ">\n");
|
strcatf(&str, ">\n");
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
|
|
||||||
args.returned = 0;
|
args.returned = 0;
|
||||||
args.requested = RequestedCount;
|
args.requested = RequestedCount;
|
||||||
@ -1188,22 +1098,19 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
goto browse_error;
|
goto browse_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite></Result>\n"
|
ret = strcatf(&str, "</DIDL-Lite></Result>\n"
|
||||||
"<NumberReturned>%u</NumberReturned>\n"
|
"<NumberReturned>%u</NumberReturned>\n"
|
||||||
"<TotalMatches>%u</TotalMatches>\n"
|
"<TotalMatches>%u</TotalMatches>\n"
|
||||||
"<UpdateID>%u</UpdateID>"
|
"<UpdateID>%u</UpdateID>"
|
||||||
"</u:BrowseResponse>",
|
"</u:BrowseResponse>",
|
||||||
args.returned, totalMatches, updateID);
|
args.returned, totalMatches, updateID);
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
BuildSendAndCloseSoapResp(h, str.data, str.off);
|
||||||
args.size += ret;
|
|
||||||
BuildSendAndCloseSoapResp(h, resp, args.size);
|
|
||||||
browse_error:
|
browse_error:
|
||||||
ClearNameValueList(&data);
|
ClearNameValueList(&data);
|
||||||
if( orderBy )
|
|
||||||
free(orderBy);
|
|
||||||
if( args.flags & FLAG_FREE_OBJECT_ID )
|
if( args.flags & FLAG_FREE_OBJECT_ID )
|
||||||
sqlite3_free(ObjectId);
|
sqlite3_free(ObjectId);
|
||||||
free(resp);
|
free(orderBy);
|
||||||
|
free(str.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1215,16 +1122,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
"<Result>"
|
"<Result>"
|
||||||
"<DIDL-Lite"
|
"<DIDL-Lite"
|
||||||
CONTENT_DIRECTORY_SCHEMAS;
|
CONTENT_DIRECTORY_SCHEMAS;
|
||||||
|
|
||||||
char *resp = malloc(1048576);
|
|
||||||
char *zErrMsg = 0;
|
char *zErrMsg = 0;
|
||||||
char *sql, *ptr;
|
char *sql, *ptr;
|
||||||
char **result;
|
char **result;
|
||||||
char str_buf[4096];
|
|
||||||
int ret;
|
|
||||||
struct Response args;
|
struct Response args;
|
||||||
|
struct string_s str;
|
||||||
int totalMatches = 0;
|
int totalMatches = 0;
|
||||||
*resp = '\0';
|
int ret;
|
||||||
|
|
||||||
struct NameValueParserData data;
|
struct NameValueParserData data;
|
||||||
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
|
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
|
||||||
@ -1254,26 +1158,24 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
|
memset(&str, 0, sizeof(str));
|
||||||
|
|
||||||
args.alloced = 1048576;
|
str.data = malloc(DEFAULT_RESP_SIZE);
|
||||||
args.resp = resp;
|
str.size = DEFAULT_RESP_SIZE;
|
||||||
args.size = sprintf(resp, "%s", resp0);
|
str.off = sprintf(str.data, "%s", resp0);
|
||||||
/* See if we need to include DLNA namespace reference */
|
/* See if we need to include DLNA namespace reference */
|
||||||
args.filter = set_filter_flags(Filter, h->req_client);
|
args.filter = set_filter_flags(Filter, h->req_client);
|
||||||
if( args.filter & FILTER_DLNA_NAMESPACE )
|
if( args.filter & FILTER_DLNA_NAMESPACE )
|
||||||
{
|
{
|
||||||
ret = sprintf(str_buf, DLNA_NAMESPACE);
|
ret = strcatf(&str, DLNA_NAMESPACE);
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
}
|
}
|
||||||
ret = sprintf(str_buf, ">\n");
|
ret = strcatf(&str, ">\n");
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
|
|
||||||
args.returned = 0;
|
args.returned = 0;
|
||||||
args.requested = RequestedCount;
|
args.requested = RequestedCount;
|
||||||
args.client = h->req_client;
|
args.client = h->req_client;
|
||||||
args.flags = h->reqflags;
|
args.flags = h->reqflags;
|
||||||
|
args.str = &str;
|
||||||
if( h->reqflags & FLAG_MS_PFS )
|
if( h->reqflags & FLAG_MS_PFS )
|
||||||
{
|
{
|
||||||
if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) )
|
if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) )
|
||||||
@ -1346,13 +1248,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
if( strstr(SearchCriteria, "@id") )
|
if( strstr(SearchCriteria, "@id") )
|
||||||
{
|
{
|
||||||
newSearchCriteria = strdup(SearchCriteria);
|
newSearchCriteria = strdup(SearchCriteria);
|
||||||
newSearchCriteria = SearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0);
|
SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0);
|
||||||
}
|
}
|
||||||
if( strstr(SearchCriteria, "res is ") )
|
if( strstr(SearchCriteria, "res is ") )
|
||||||
{
|
{
|
||||||
if( !newSearchCriteria )
|
if( !newSearchCriteria )
|
||||||
newSearchCriteria = strdup(SearchCriteria);
|
newSearchCriteria = strdup(SearchCriteria);
|
||||||
newSearchCriteria = SearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0);
|
SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0);
|
||||||
}
|
}
|
||||||
#if 0 // Does 360 need this?
|
#if 0 // Does 360 need this?
|
||||||
if( strstr(SearchCriteria, "&") )
|
if( strstr(SearchCriteria, "&") )
|
||||||
@ -1367,14 +1269,16 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
}
|
}
|
||||||
DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria);
|
DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria);
|
||||||
|
|
||||||
sprintf(str_buf, "SELECT (select count(distinct DETAIL_ID) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
|
sql = sqlite3_mprintf("SELECT (select count(distinct DETAIL_ID)"
|
||||||
" where (OBJECT_ID glob '%s$*') and (%s))"
|
" from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
|
||||||
" + "
|
" where (OBJECT_ID glob '%s$*') and (%s))"
|
||||||
"(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
|
" + "
|
||||||
" where (OBJECT_ID = '%s') and (%s))",
|
"(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
|
||||||
ContainerID, SearchCriteria, ContainerID, SearchCriteria);
|
" where (OBJECT_ID = '%s') and (%s))",
|
||||||
|
ContainerID, SearchCriteria, ContainerID, SearchCriteria);
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql);
|
//DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql);
|
||||||
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
ret = sql_get_table(db, sql, &result, NULL, NULL);
|
||||||
|
sqlite3_free(sql);
|
||||||
if( ret == SQLITE_OK )
|
if( ret == SQLITE_OK )
|
||||||
{
|
{
|
||||||
totalMatches = atoi(result[1]);
|
totalMatches = atoi(result[1]);
|
||||||
@ -1428,27 +1332,20 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
sqlite3_free(zErrMsg);
|
sqlite3_free(zErrMsg);
|
||||||
}
|
}
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
strcat(resp, str_buf);
|
ret = strcatf(&str, "</DIDL-Lite></Result>\n"
|
||||||
ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite></Result>\n"
|
"<NumberReturned>%u</NumberReturned>\n"
|
||||||
"<NumberReturned>%u</NumberReturned>\n"
|
"<TotalMatches>%u</TotalMatches>\n"
|
||||||
"<TotalMatches>%u</TotalMatches>\n"
|
"<UpdateID>%u</UpdateID>"
|
||||||
"<UpdateID>%u</UpdateID>"
|
"</u:SearchResponse>",
|
||||||
"</u:SearchResponse>",
|
args.returned, totalMatches, updateID);
|
||||||
args.returned, totalMatches, updateID);
|
BuildSendAndCloseSoapResp(h, str.data, str.off);
|
||||||
memcpy(resp+args.size, &str_buf, ret+1);
|
|
||||||
args.size += ret;
|
|
||||||
BuildSendAndCloseSoapResp(h, resp, args.size);
|
|
||||||
search_error:
|
search_error:
|
||||||
ClearNameValueList(&data);
|
ClearNameValueList(&data);
|
||||||
if( orderBy )
|
|
||||||
free(orderBy);
|
|
||||||
if( newSearchCriteria )
|
|
||||||
free(newSearchCriteria);
|
|
||||||
free(resp);
|
|
||||||
if( h->reqflags & FLAG_MS_PFS )
|
if( h->reqflags & FLAG_MS_PFS )
|
||||||
{
|
|
||||||
sqlite3_free(ContainerID);
|
sqlite3_free(ContainerID);
|
||||||
}
|
free(orderBy);
|
||||||
|
free(newSearchCriteria);
|
||||||
|
free(str.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
11
upnpsoap.h
11
upnpsoap.h
@ -21,7 +21,8 @@
|
|||||||
#ifndef __UPNPSOAP_H__
|
#ifndef __UPNPSOAP_H__
|
||||||
#define __UPNPSOAP_H__
|
#define __UPNPSOAP_H__
|
||||||
|
|
||||||
#define MAX_RESPONSE_SIZE 1048576
|
#define DEFAULT_RESP_SIZE 131072
|
||||||
|
#define MAX_RESPONSE_SIZE 2097152
|
||||||
|
|
||||||
#define CONTENT_DIRECTORY_SCHEMAS \
|
#define CONTENT_DIRECTORY_SCHEMAS \
|
||||||
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" \
|
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" \
|
||||||
@ -32,14 +33,12 @@
|
|||||||
|
|
||||||
struct Response
|
struct Response
|
||||||
{
|
{
|
||||||
char *resp;
|
struct string_s *str;
|
||||||
int start;
|
int start;
|
||||||
int returned;
|
int returned;
|
||||||
int requested;
|
int requested;
|
||||||
int size;
|
uint32_t filter;
|
||||||
int alloced;
|
uint32_t flags;
|
||||||
u_int32_t filter;
|
|
||||||
u_int32_t flags;
|
|
||||||
enum client_types client;
|
enum client_types client;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
14
utils.c
14
utils.c
@ -31,6 +31,20 @@
|
|||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
inline int
|
||||||
|
strcatf(struct string_s *str, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = vsnprintf(str->data + str->off, str->size - str->off, fmt, ap);
|
||||||
|
str->off += ret;
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle)
|
ends_with(const char * haystack, const char * needle)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user