* Consolidate client detection code.

* Add forced sorting support for Panasonic devices.
This commit is contained in:
Justin Maggard 2013-03-18 05:37:46 +00:00
parent bc33a34331
commit 1c748c1d5a
12 changed files with 436 additions and 265 deletions

View File

@ -5,7 +5,7 @@ SUBDIRS=po
sbin_PROGRAMS = minidlnad sbin_PROGRAMS = minidlnad
check_PROGRAMS = testupnpdescgen check_PROGRAMS = testupnpdescgen
minidlnad_SOURCES = minidlna.c upnphttp.c upnpdescgen.c upnpsoap.c \ minidlnad_SOURCES = minidlna.c upnphttp.c upnpdescgen.c upnpsoap.c \
upnpreplyparse.c minixml.c \ upnpreplyparse.c minixml.c clients.c \
getifaddr.c daemonize.c upnpglobalvars.c \ getifaddr.c daemonize.c upnpglobalvars.c \
options.c minissdp.c uuid.c upnpevents.c \ options.c minissdp.c uuid.c upnpevents.c \
sql.c utils.c metadata.c scanner.c inotify.c \ sql.c utils.c metadata.c scanner.c inotify.c \

251
clients.c Normal file
View File

@ -0,0 +1,251 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "clients.h"
#include "getifaddr.h"
#include "log.h"
struct client_type_s client_types[] =
{
{ 0,
0,
"Unknown",
NULL,
EMatchNone
},
{ EXbox,
FLAG_MIME_AVI_AVI | FLAG_MS_PFS,
"Xbox 360",
"Xbox/",
EUserAgent
},
{ EPS3,
FLAG_DLNA | FLAG_MIME_AVI_DIVX,
"PLAYSTATION 3",
"PLAYSTATION",
EUserAgent
},
{ EPS3,
FLAG_DLNA | FLAG_MIME_AVI_DIVX,
"PLAYSTATION 3",
"PLAYSTATION 3",
EXAVClientInfo
},
{ ESamsungSeriesCTV,
FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_TV,
"Samsung Series C TV",
"SEC_HHP_TV",
EUserAgent
},
/* User-Agent: DLNADOC/1.50 SEC_HHP_BD-D5100/1.0 */
{ ESamsungSeriesC,
FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
"Samsung Series C",
"SEC_HHP_",
EUserAgent
},
{ ESamsungSeriesA,
FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
"Samsung Series A",
"SamsungWiselinkPro",
EUserAgent
},
{ ESamsungSeriesB,
FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
"Samsung Series B",
"Samsung DTV DMR",
EModelName
},
/* User-Agent: Panasonic MIL DLNA CP UPnP/1.0 DLNADOC/1.50 */
{ EPanasonic,
FLAG_DLNA | FLAG_FORCE_SORT,
"Panasonic",
"Panasonic",
EUserAgent
},
{ EDenonReceiver,
FLAG_DLNA,
"Denon Receiver",
"bridgeCo-DMP/3",
EUserAgent
},
{ EFreeBox,
FLAG_RESIZE_THUMBS,
"FreeBox",
"fbxupnpav/",
EUserAgent
},
{ EPopcornHour,
FLAG_MIME_FLAC_FLAC,
"Popcorn Hour",
"SMP8634",
EUserAgent
},
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Blu-ray Disc Player"; mv="2.0" */
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BLU-RAY HOME THEATRE SYSTEM"; mv="2.0"; */
/* Sony SMP-100 needs the same treatment as their BDP-S370 */
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Media Player"; mv="2.0" */
{ ESonyBDP,
FLAG_DLNA,
"Sony BDP",
"mv=\"2.0\"",
EXAVClientInfo
},
/* User-Agent: Linux/2.6.31-1.0 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.5.0 */
{ ELGDevice,
FLAG_DLNA,
"LG",
"LGE_DLNA_SDK",
EUserAgent
},
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BRAVIA KDL-40EX503"; mv="1.7"; */
{ ESonyBravia,
FLAG_DLNA,
"Sony Bravia",
"BRAVIA",
EXAVClientInfo
},
/* X-AV-Client-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="INTERNET TV NSX-40GT 1"; mv="0.1"; */
{ ESonyInternetTV,
FLAG_DLNA,
"Sony Internet TV",
"INTERNET TV",
EXAVClientInfo
},
{ ENetgearEVA2000,
FLAG_MS_PFS | FLAG_RESIZE_THUMBS,
"EVA2000",
"Verismo,",
EUserAgent
},
{ EDirecTV,
FLAG_RESIZE_THUMBS,
"DirecTV",
"DIRECTV ",
EUserAgent
},
{ EToshibaTV,
FLAG_DLNA,
"Toshiba TV",
"UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2",
EUserAgent
},
{ ERokuSoundBridge,
FLAG_MS_PFS | FLAG_AUDIO_ONLY | FLAG_MIME_WAV_WAV | FLAG_FORCE_SORT,
"Roku SoundBridge",
"Roku SoundBridge",
EModelName
},
{ EMarantzDMP,
FLAG_DLNA | FLAG_MIME_WAV_WAV,
"marantz DMP",
"marantz DMP",
EFriendlyNameSSDP
},
{ EMediaRoom,
FLAG_MS_PFS,
"MS MediaRoom",
"Microsoft-IPTV-Client",
EUserAgent
},
{ ELifeTab,
FLAG_MS_PFS,
"LIFETAB",
"LIFETAB",
EFriendlyName
},
{ EStandardDLNA150,
FLAG_DLNA | FLAG_MIME_AVI_AVI,
"Generic DLNA 1.5",
"DLNADOC/1.50",
EUserAgent
},
{ 0, 0, NULL, 0 }
};
struct client_cache_s clients[CLIENT_CACHE_SLOTS];
int
SearchClientCache(struct in_addr addr, int quiet)
{
int i;
for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
{
if (clients[i].addr.s_addr == addr.s_addr)
{
/* Invalidate this client cache if it's older than 1 hour */
if ((time(NULL) - clients[i].age) > 3600)
{
unsigned char mac[6];
if (get_remote_mac(addr, mac) == 0 &&
memcmp(mac, clients[i].mac, 6) == 0)
{
/* Same MAC as last time when we were able to identify the client,
* so extend the timeout by another hour. */
clients[i].age = time(NULL);
}
else
{
memset(&clients[i], 0, sizeof(struct client_cache_s));
return -1;
}
}
if (!quiet)
DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [%s/entry %d]\n",
client_types[clients[i].type].name, i);
return i;
}
}
return -1;
}
int
AddClientCache(struct in_addr addr, int type)
{
int i;
for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
{
if (clients[i].addr.s_addr)
continue;
get_remote_mac(addr, clients[i].mac);
clients[i].addr = addr;
clients[i].type = type;
clients[i].age = time(NULL);
DPRINTF(E_DEBUG, L_HTTP, "Added client [%s/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
client_types[type].name, inet_ntoa(clients[i].addr),
clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], i);
return 0;
}
return -1;
}

76
clients.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef __CLIENTS_H__
#define __CLIENTS_H__
#include <stdint.h>
#include <netinet/in.h>
#define CLIENT_CACHE_SLOTS 20
#define FLAG_DLNA 0x00000001
#define FLAG_MIME_AVI_DIVX 0x00000002
#define FLAG_MIME_AVI_AVI 0x00000004
#define FLAG_MIME_FLAC_FLAC 0x00000008
#define FLAG_MIME_WAV_WAV 0x00000010
#define FLAG_RESIZE_THUMBS 0x00000020
#define FLAG_NO_RESIZE 0x00000040
#define FLAG_MS_PFS 0x00000080 // Microsoft PlaysForSure client
#define FLAG_SAMSUNG 0x00000100
#define FLAG_SAMSUNG_TV 0x00000200
#define FLAG_AUDIO_ONLY 0x00000400
#define FLAG_FORCE_SORT 0x00000800
enum match_types {
EMatchNone,
EUserAgent,
EXAVClientInfo,
EFriendlyName,
EModelName,
EFriendlyNameSSDP
};
enum client_types {
EXbox = 1,
EPS3,
EDenonReceiver,
EDirecTV,
EFreeBox,
ELGDevice,
ELifeTab,
EMarantzDMP,
EMediaRoom,
ENetgearEVA2000,
EPanasonic,
EPopcornHour,
ERokuSoundBridge,
ESamsungSeriesA,
ESamsungSeriesB,
ESamsungSeriesC,
ESamsungSeriesCTV,
ESonyBDP,
ESonyBravia,
ESonyInternetTV,
EToshibaTV,
EStandardDLNA150
};
struct client_type_s {
enum client_types type;
uint32_t flags;
const char *name;
const char *match;
enum match_types match_type;
};
struct client_cache_s {
struct in_addr addr;
unsigned char mac[6];
enum client_types type;
time_t age;
};
extern struct client_type_s client_types[];
extern struct client_cache_s clients[CLIENT_CACHE_SLOTS];
int SearchClientCache(struct in_addr addr, int quiet);
int AddClientCache(struct in_addr addr, int type);
#endif

View File

@ -326,7 +326,7 @@ getsyshwaddr(char * buf, int len)
} }
int int
get_remote_mac(struct in_addr ip_addr, unsigned char * mac) get_remote_mac(struct in_addr ip_addr, unsigned char *mac)
{ {
struct in_addr arp_ent; struct in_addr arp_ent;
FILE * arp; FILE * arp;

View File

@ -30,7 +30,8 @@
#define __MINIDLNATYPES_H__ #define __MINIDLNATYPES_H__
#include "config.h" #include "config.h"
#include <netinet/in.h> #include "clients.h"
//#include <netinet/in.h>
#include <time.h> #include <time.h>
#include <fcntl.h> #include <fcntl.h>
@ -66,28 +67,6 @@ enum file_types {
TYPE_FILE TYPE_FILE
}; };
enum client_types {
EXbox = 1,
EPS3,
ESamsungSeriesC,
EDenonReceiver,
EFreeBox,
EPopcornHour,
EMediaRoom,
ESonyBDP,
ESonyBravia,
ERokuSoundBridge,
EToshibaTV,
ELGDevice,
ENetgearEVA2000,
ESamsungSeriesA,
ESamsungSeriesB,
EMarantzDMP,
ELifeTab,
EDirecTV,
EStandardDLNA150 = 100
};
struct media_dir_s { struct media_dir_s {
char *path; /* base path */ char *path; /* base path */
media_types types; /* types of files to scan */ media_types types; /* types of files to scan */
@ -100,12 +79,4 @@ struct album_art_name_s {
struct album_art_name_s *next; struct album_art_name_s *next;
}; };
struct client_cache_s {
struct in_addr addr;
unsigned char mac[6];
enum client_types type;
uint32_t flags;
time_t age;
};
#endif #endif

View File

@ -346,8 +346,7 @@ ParseUPnPClient(char *location)
int content_len = sizeof(buf); int content_len = sizeof(buf);
struct NameValueParserData xml; struct NameValueParserData xml;
int client; int client;
enum client_types type = 0; int type = 0;
uint32_t flags = 0;
char *model, *serial, *name; char *model, *serial, *name;
if (strncmp(location, "http://", 7) != 0) if (strncmp(location, "http://", 7) != 0)
@ -437,33 +436,46 @@ close:
name = GetValueFromNameValueList(&xml, "friendlyName"); name = GetValueFromNameValueList(&xml, "friendlyName");
if (model) if (model)
{ {
int i;
DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model); DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
if (strstr(model, "Roku SoundBridge") != NULL) for (i = 0; client_types[i].name; i++)
{ {
type = ERokuSoundBridge; if (client_types[i].match_type != EModelName)
flags |= FLAG_MS_PFS; continue;
flags |= FLAG_AUDIO_ONLY; if (strstr(model, client_types[i].match) != NULL)
flags |= FLAG_MIME_WAV_WAV;
}
else if ((strcmp(model, "Samsung DTV DMR") == 0) && serial )
{
DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
/* The Series B I saw was 20081224DMR. Series A should be older than that. */
if (atoi(serial) > 20081200)
{ {
type = ESamsungSeriesB; type = i;
flags |= FLAG_SAMSUNG; break;
flags |= FLAG_DLNA;
flags |= FLAG_NO_RESIZE;
} }
} }
else
/* Special Samsung handling. It's very hard to tell Series A from B */
if (type > 0 && client_types[type].type == ESamsungSeriesB)
{ {
if (name && (strcmp(name, "marantz DMP") == 0)) if (serial)
{ {
type = EMarantzDMP; DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
flags |= FLAG_DLNA; /* The Series B I saw was 20081224DMR. Series A should be older than that. */
flags |= FLAG_MIME_WAV_WAV; if (atoi(serial) < 20081201)
type = 0;
}
else
{
type = 0;
}
}
if (type == 0 && name != NULL)
{
for (i = 0; client_types[i].name; i++)
{
if (client_types[i].match_type != EFriendlyNameSSDP)
continue;
if (strcmp(name, client_types[i].match) == 0)
{
type = i;
break;
}
} }
} }
} }
@ -474,22 +486,13 @@ close:
client = SearchClientCache(dest.sin_addr, 1); client = SearchClientCache(dest.sin_addr, 1);
if (client < 0) if (client < 0)
{ {
for (client=0; client<CLIENT_CACHE_SLOTS; client++) AddClientCache(dest.sin_addr, type);
{ }
if (clients[client].addr.s_addr) else
continue; {
get_remote_mac(dest.sin_addr, clients[client].mac); clients[client].type = type;
clients[client].addr = dest.sin_addr; clients[client].age = time(NULL);
DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
type, inet_ntoa(clients[client].addr),
clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
break;
}
} }
clients[client].type = type;
clients[client].flags = flags;
clients[client].age = time(NULL);
} }
/* ProcessSSDPRequest() /* ProcessSSDPRequest()

View File

@ -89,7 +89,6 @@ char db_path[PATH_MAX] = {'\0'};
char log_path[PATH_MAX] = {'\0'}; char log_path[PATH_MAX] = {'\0'};
struct media_dir_s * media_dirs = NULL; struct media_dir_s * media_dirs = NULL;
struct album_art_name_s * album_art_names = NULL; struct album_art_name_s * album_art_names = NULL;
struct client_cache_s clients[CLIENT_CACHE_SLOTS];
short int scanning = 0; short int scanning = 0;
volatile short int quitting = 0; volatile short int quitting = 0;
volatile uint32_t updateID = 0; volatile uint32_t updateID = 0;

View File

@ -52,6 +52,7 @@
#include <time.h> #include <time.h>
#include "minidlnatypes.h" #include "minidlnatypes.h"
#include "clients.h"
#include "config.h" #include "config.h"
#include <sqlite3.h> #include <sqlite3.h>
@ -64,7 +65,6 @@
# define SERVER_NAME "MiniDLNA" # define SERVER_NAME "MiniDLNA"
#endif #endif
#define CLIENT_CACHE_SLOTS 20
#define USE_FORK 1 #define USE_FORK 1
#define DB_VERSION 9 #define DB_VERSION 9
@ -194,7 +194,7 @@ extern uint32_t runtime_flags;
#define GETFLAG(mask) (runtime_flags & mask) #define GETFLAG(mask) (runtime_flags & mask)
#define CLEARFLAG(mask) runtime_flags &= ~mask #define CLEARFLAG(mask) runtime_flags &= ~mask
extern const char * pidfilename; extern const char *pidfilename;
extern char uuidvalue[]; extern char uuidvalue[];
@ -229,9 +229,8 @@ extern sqlite3 *db;
extern char friendly_name[]; extern char friendly_name[];
extern char db_path[]; extern char db_path[];
extern char log_path[]; extern char log_path[];
extern struct media_dir_s * media_dirs; extern struct media_dir_s *media_dirs;
extern struct album_art_name_s * album_art_names; extern struct album_art_name_s *album_art_names;
extern struct client_cache_s clients[CLIENT_CACHE_SLOTS];
extern short int scanning; extern short int scanning;
extern volatile short int quitting; extern volatile short int quitting;
extern volatile uint32_t updateID; extern volatile uint32_t updateID;

View File

@ -77,6 +77,7 @@
#include <libexif/exif-loader.h> #include <libexif/exif-loader.h>
#include "tivo_utils.h" #include "tivo_utils.h"
#include "tivo_commands.h" #include "tivo_commands.h"
#include "clients.h"
#include "sendfile.h" #include "sendfile.h"
@ -130,40 +131,6 @@ Delete_upnphttp(struct upnphttp * h)
} }
} }
int
SearchClientCache(struct in_addr addr, int quiet)
{
int i;
for( i=0; i<CLIENT_CACHE_SLOTS; i++ )
{
if( clients[i].addr.s_addr == addr.s_addr )
{
/* Invalidate this client cache if it's older than 1 hour */
if( (time(NULL) - clients[i].age) > 3600 )
{
unsigned char mac[6];
if( get_remote_mac(addr, mac) == 0 &&
memcmp(mac, clients[i].mac, 6) == 0 )
{
/* Same MAC as last time when we were able to identify the client,
* so extend the timeout by another hour. */
clients[i].age = time(NULL);
}
else
{
memset(&clients[i], 0, sizeof(struct client_cache_s));
return -1;
}
}
if( !quiet )
DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [type %d/entry %d]\n",
clients[i].type, i);
return i;
}
}
return -1;
}
/* parse HttpHeaders of the REQUEST */ /* parse HttpHeaders of the REQUEST */
static void static void
ParseHttpHeaders(struct upnphttp * h) ParseHttpHeaders(struct upnphttp * h)
@ -302,121 +269,42 @@ ParseHttpHeaders(struct upnphttp * h)
} }
else if(strncasecmp(line, "User-Agent", 10)==0) else if(strncasecmp(line, "User-Agent", 10)==0)
{ {
char *s; int i;
/* Skip client detection if we already detected it. */ /* Skip client detection if we already detected it. */
if( h->req_client ) if( h->req_client )
goto next_header; goto next_header;
p = colon + 1; p = colon + 1;
while(isspace(*p)) while(isspace(*p))
p++; p++;
if(strncasecmp(p, "Xbox/", 5)==0) for (i = 0; client_types[i].name; i++)
{ {
h->req_client = EXbox; if (client_types[i].match_type != EUserAgent)
h->reqflags |= FLAG_MIME_AVI_AVI; continue;
h->reqflags |= FLAG_MS_PFS; if (strstrc(p, client_types[i].match, '\r') != NULL)
} {
else if(strncmp(p, "PLAYSTATION", 11)==0) h->req_client = i;
{ break;
h->req_client = EPS3; }
h->reqflags |= FLAG_DLNA;
h->reqflags |= FLAG_MIME_AVI_DIVX;
}
else if((s=strstrc(p, "SEC_HHP_", '\r')))
{
h->req_client = ESamsungSeriesC;
h->reqflags |= FLAG_SAMSUNG;
h->reqflags |= FLAG_DLNA;
h->reqflags |= FLAG_NO_RESIZE;
if(strstrc(s+8, "TV", '\r'))
h->reqflags |= FLAG_SAMSUNG_TV;
}
else if(strncmp(p, "SamsungWiselinkPro", 18)==0)
{
h->req_client = ESamsungSeriesA;
h->reqflags |= FLAG_SAMSUNG;
h->reqflags |= FLAG_DLNA;
h->reqflags |= FLAG_NO_RESIZE;
}
else if(strstrc(p, "bridgeCo-DMP/3", '\r'))
{
h->req_client = EDenonReceiver;
h->reqflags |= FLAG_DLNA;
}
else if(strstrc(p, "fbxupnpav/", '\r'))
{
h->req_client = EFreeBox;
h->reqflags |= FLAG_RESIZE_THUMBS;
}
else if(strncmp(p, "SMP8634", 7)==0)
{
h->req_client = EPopcornHour;
h->reqflags |= FLAG_MIME_FLAC_FLAC;
}
else if(strstrc(p, "Microsoft-IPTV-Client", '\r'))
{
h->req_client = EMediaRoom;
h->reqflags |= FLAG_MS_PFS;
}
else if(strstrc(p, "LGE_DLNA_SDK", '\r'))
{
h->req_client = ELGDevice;
h->reqflags |= FLAG_DLNA;
}
else if(strncmp(p, "Verismo,", 8)==0)
{
h->req_client = ENetgearEVA2000;
h->reqflags |= FLAG_MS_PFS;
h->reqflags |= FLAG_RESIZE_THUMBS;
}
else if(strstrc(p, "DIRECTV ", '\r'))
{
h->req_client = EDirecTV;
h->reqflags |= FLAG_RESIZE_THUMBS;
}
else if(strstrc(p, "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2", '\r'))
{
h->req_client = EToshibaTV;
h->reqflags |= FLAG_DLNA;
}
else if(strstrc(p, "DLNADOC/1.50", '\r'))
{
h->req_client = EStandardDLNA150;
h->reqflags |= FLAG_DLNA;
h->reqflags |= FLAG_MIME_AVI_AVI;
} }
} }
else if(strncasecmp(line, "X-AV-Client-Info", 16)==0) else if(strncasecmp(line, "X-AV-Client-Info", 16)==0)
{ {
int i;
/* Skip client detection if we already detected it. */ /* Skip client detection if we already detected it. */
if( h->req_client && h->req_client < EStandardDLNA150 ) if( h->req_client && client_types[h->req_client].type < EStandardDLNA150 )
goto next_header; goto next_header;
p = colon + 1; p = colon + 1;
while(isspace(*p)) while(isspace(*p))
p++; p++;
if(strstrc(p, "PLAYSTATION 3", '\r')) for (i = 0; client_types[i].name; i++)
{ {
h->req_client = EPS3; if (client_types[i].match_type != EXAVClientInfo)
h->reqflags |= FLAG_DLNA; continue;
h->reqflags |= FLAG_MIME_AVI_DIVX; if (strstrc(p, client_types[i].match, '\r') != NULL)
} {
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Blu-ray Disc Player"; mv="2.0" */ h->req_client = i;
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BLU-RAY HOME THEATRE SYSTEM"; mv="2.0"; */ break;
/* Sony SMP-100 needs the same treatment as their BDP-S370 */ }
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Media Player"; mv="2.0" */
else if(strstrc(p, "Blu-ray Disc Player", '\r') ||
strstrc(p, "BLU-RAY HOME THEATRE SYSTEM", '\r') ||
strstrc(p, "Media Player", '\r'))
{
h->req_client = ESonyBDP;
h->reqflags |= FLAG_DLNA;
}
/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BRAVIA KDL-40EX503"; mv="1.7"; */
/* X-AV-Client-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="INTERNET TV NSX-40GT 1"; mv="0.1"; */
else if(strstrc(p, "BRAVIA", '\r') ||
strstrc(p, "INTERNET TV", '\r'))
{
h->req_client = ESonyBravia;
h->reqflags |= FLAG_DLNA;
} }
} }
else if(strncasecmp(line, "Transfer-Encoding", 17)==0) else if(strncasecmp(line, "Transfer-Encoding", 17)==0)
@ -485,13 +373,19 @@ ParseHttpHeaders(struct upnphttp * h)
} }
else if(strncasecmp(line, "FriendlyName", 12)==0) else if(strncasecmp(line, "FriendlyName", 12)==0)
{ {
int i;
p = colon + 1; p = colon + 1;
while(isspace(*p)) while(isspace(*p))
p++; p++;
if(strstrc(p, "LIFETAB", '\r')) for (i = 0; client_types[i].name; i++)
{ {
h->req_client = ELifeTab; if (client_types[i].match_type != EFriendlyName)
h->reqflags |= FLAG_MS_PFS; continue;
if (strstrc(p, client_types[i].match, '\r') != NULL)
{
h->req_client = i;
break;
}
} }
} }
} }
@ -532,35 +426,26 @@ next_header:
/* Add this client to the cache if it's not there already. */ /* Add this client to the cache if it's not there already. */
if( n < 0 ) if( n < 0 )
{ {
for( n=0; n<CLIENT_CACHE_SLOTS; n++ ) AddClientCache(h->clientaddr, h->req_client);
{
if( clients[n].addr.s_addr )
continue;
get_remote_mac(h->clientaddr, clients[n].mac);
clients[n].addr = h->clientaddr;
DPRINTF(E_DEBUG, L_HTTP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
h->req_client, inet_ntoa(clients[n].addr),
clients[n].mac[0], clients[n].mac[1], clients[n].mac[2],
clients[n].mac[3], clients[n].mac[4], clients[n].mac[5], n);
break;
}
} }
else if( (clients[n].type < EStandardDLNA150 && h->req_client == EStandardDLNA150) || else
(clients[n].type == ESamsungSeriesB && h->req_client == ESamsungSeriesA) )
{ {
enum client_types type = client_types[h->req_client].type;
enum client_types ctype = client_types[clients[n].type].type;
/* If we know the client and our new detection is generic, use our cached info */ /* If we know the client and our new detection is generic, use our cached info */
/* If we detected a Samsung Series B earlier, don't overwrite it with Series A info */ /* If we detected a Samsung Series B earlier, don't overwrite it with Series A info */
h->reqflags |= clients[n].flags; if ((ctype < EStandardDLNA150 && type == EStandardDLNA150) ||
h->req_client = clients[n].type; (ctype == ESamsungSeriesB && type == ESamsungSeriesA))
return; {
h->req_client = clients[n].type;
return;
}
clients[n].type = h->req_client;
clients[n].age = time(NULL);
} }
clients[n].type = h->req_client;
clients[n].flags = h->reqflags & 0xFFF00000;
clients[n].age = time(NULL);
} }
else if( n >= 0 ) else if( n >= 0 )
{ {
h->reqflags |= clients[n].flags;
h->req_client = clients[n].type; h->req_client = clients[n].type;
} }
} }
@ -979,7 +864,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
if(strcmp(ROOTDESC_PATH, HttpUrl) == 0) if(strcmp(ROOTDESC_PATH, HttpUrl) == 0)
{ {
/* If it's a Xbox360, we might need a special friendly_name to be recognized */ /* If it's a Xbox360, we might need a special friendly_name to be recognized */
if( h->req_client == EXbox ) if( client_types[h->req_client].type == EXbox )
{ {
char model_sav[2]; char model_sav[2];
i = 0; i = 0;
@ -995,7 +880,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
friendly_name[i] = '\0'; friendly_name[i] = '\0';
memcpy(modelnumber, model_sav, 2); memcpy(modelnumber, model_sav, 2);
} }
else if( h->reqflags & FLAG_SAMSUNG_TV ) else if( client_types[h->req_client].flags & FLAG_SAMSUNG_TV )
{ {
sendXMLdesc(h, genRootDescSamsung); sendXMLdesc(h, genRootDescSamsung);
} }
@ -1837,7 +1722,7 @@ resized_error:
} }
void void
SendResp_dlnafile(struct upnphttp * h, char * object) SendResp_dlnafile(struct upnphttp *h, char *object)
{ {
char header[1024]; char header[1024];
struct string_s str; struct string_s str;
@ -1850,6 +1735,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
int64_t id; int64_t id;
int sendfh; int sendfh;
uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B; uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B;
uint32_t cflags = client_types[h->req_client].flags;
static struct { int64_t id; static struct { int64_t id;
enum client_types client; enum client_types client;
char path[PATH_MAX]; char path[PATH_MAX];
@ -1861,7 +1747,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
#endif #endif
id = strtoll(object, NULL, 10); id = strtoll(object, NULL, 10);
if( h->reqflags & FLAG_MS_PFS ) if( cflags & FLAG_MS_PFS )
{ {
if( strstr(object, "?albumArt=true") ) if( strstr(object, "?albumArt=true") )
{ {
@ -1897,7 +1783,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
{ {
strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1); strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1);
/* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */ /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */
if( h->reqflags & FLAG_SAMSUNG ) if( cflags & FLAG_SAMSUNG )
{ {
if( strcmp(last_file.mime+6, "x-matroska") == 0 ) if( strcmp(last_file.mime+6, "x-matroska") == 0 )
strcpy(last_file.mime+8, "mkv"); strcpy(last_file.mime+8, "mkv");
@ -1955,7 +1841,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n"); DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n");
/* Samsung TVs (well, at least the A950) do this for some reason, /* Samsung TVs (well, at least the A950) do this for some reason,
* and I don't see them fixing this bug any time soon. */ * and I don't see them fixing this bug any time soon. */
if( !(h->reqflags & FLAG_SAMSUNG) || GETFLAG(DLNA_STRICT_MASK) ) if( !(cflags & FLAG_SAMSUNG) || GETFLAG(DLNA_STRICT_MASK) )
{ {
Send406(h); Send406(h);
goto error; goto error;

View File

@ -128,21 +128,6 @@ struct upnphttp {
#define FLAG_XFERBACKGROUND 0x00004000 #define FLAG_XFERBACKGROUND 0x00004000
#define FLAG_CAPTION 0x00008000 #define FLAG_CAPTION 0x00008000
#define FLAG_DLNA 0x00100000
#define FLAG_MIME_AVI_DIVX 0x00200000
#define FLAG_MIME_AVI_AVI 0x00400000
#define FLAG_MIME_FLAC_FLAC 0x00800000
#define FLAG_MIME_WAV_WAV 0x01000000
#define FLAG_RESIZE_THUMBS 0x02000000
#define FLAG_NO_RESIZE 0x04000000
#define FLAG_MS_PFS 0x08000000 // Microsoft PlaysForSure client
#define FLAG_SAMSUNG 0x10000000
#define FLAG_SAMSUNG_TV 0x20000000
#define FLAG_AUDIO_ONLY 0x40000000
#define FLAG_FREE_OBJECT_ID 0x00000001
#define FLAG_ROOT_CONTAINER 0x00000002
#ifndef MSG_MORE #ifndef MSG_MORE
#define MSG_MORE 0 #define MSG_MORE 0
#endif #endif
@ -194,9 +179,6 @@ Send501(struct upnphttp *);
void void
SendResp_upnphttp(struct upnphttp *); SendResp_upnphttp(struct upnphttp *);
int
SearchClientCache(struct in_addr addr, int quiet);
void void
SendResp_icon(struct upnphttp *, char * url); SendResp_icon(struct upnphttp *, char * url);
void void

View File

@ -322,16 +322,17 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
#define FILTER_PV_SUBTITLE_FILE_URI 0x08000000 #define FILTER_PV_SUBTITLE_FILE_URI 0x08000000
#define FILTER_AV_MEDIA_CLASS 0x10000000 #define FILTER_AV_MEDIA_CLASS 0x10000000
static u_int32_t static uint32_t
set_filter_flags(char * filter, struct upnphttp *h) set_filter_flags(char *filter, struct upnphttp *h)
{ {
char *item, *saveptr = NULL; char *item, *saveptr = NULL;
uint32_t flags = 0; uint32_t flags = 0;
int samsung = client_types[h->req_client].flags & FLAG_SAMSUNG;
if( !filter || (strlen(filter) <= 1) ) if( !filter || (strlen(filter) <= 1) )
/* Not the full 32 bits. Skip vendor-specific stuff by default. */ /* Not the full 32 bits. Skip vendor-specific stuff by default. */
return 0xFFFFFF; return 0xFFFFFF;
if( h->reqflags & FLAG_SAMSUNG ) if( samsung )
flags |= FILTER_DLNA_NAMESPACE; flags |= FILTER_DLNA_NAMESPACE;
item = strtok_r(filter, ",", &saveptr); item = strtok_r(filter, ",", &saveptr);
while( item != NULL ) while( item != NULL )
@ -371,7 +372,7 @@ set_filter_flags(char * filter, struct upnphttp *h)
else if( strcmp(item, "upnp:albumArtURI") == 0 ) else if( strcmp(item, "upnp:albumArtURI") == 0 )
{ {
flags |= FILTER_UPNP_ALBUMARTURI; flags |= FILTER_UPNP_ALBUMARTURI;
if( h->reqflags & FLAG_SAMSUNG ) if( samsung )
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID; flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
} }
else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 ) else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 )
@ -1124,8 +1125,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
args.returned = 0; args.returned = 0;
args.requested = RequestedCount; args.requested = RequestedCount;
args.client = h->req_client; args.client = client_types[h->req_client].type;
args.flags = h->reqflags; args.flags = client_types[h->req_client].flags;
args.str = &str; args.str = &str;
if( args.flags & FLAG_MS_PFS ) if( args.flags & FLAG_MS_PFS )
{ {
@ -1201,7 +1202,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
else else
ret = asprintf(&orderBy, "order by length(OBJECT_ID), OBJECT_ID"); ret = asprintf(&orderBy, "order by length(OBJECT_ID), OBJECT_ID");
} }
else if( args.client == ERokuSoundBridge ) else if( args.flags & FLAG_FORCE_SORT )
{ {
#ifdef __sparc__ #ifdef __sparc__
if( totalMatches < 10000 ) if( totalMatches < 10000 )
@ -1570,8 +1571,8 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
args.returned = 0; args.returned = 0;
args.requested = RequestedCount; args.requested = RequestedCount;
args.client = h->req_client; args.client = client_types[h->req_client].type;
args.flags = h->reqflags; args.flags = client_types[h->req_client].flags;
args.str = &str; args.str = &str;
if( args.flags & FLAG_MS_PFS ) if( args.flags & FLAG_MS_PFS )
{ {

View File

@ -33,6 +33,9 @@
#define PV_NAMESPACE \ #define PV_NAMESPACE \
" xmlns:pv=\"http://www.pv.com/pvns/\"" " xmlns:pv=\"http://www.pv.com/pvns/\""
#define FLAG_FREE_OBJECT_ID 0x40000000
#define FLAG_ROOT_CONTAINER 0x80000000
struct Response struct Response
{ {
struct string_s *str; struct string_s *str;