* Consolidate client detection code.
* Add forced sorting support for Panasonic devices.
This commit is contained in:
parent
bc33a34331
commit
1c748c1d5a
@ -5,7 +5,7 @@ SUBDIRS=po
|
||||
sbin_PROGRAMS = minidlnad
|
||||
check_PROGRAMS = testupnpdescgen
|
||||
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 \
|
||||
options.c minissdp.c uuid.c upnpevents.c \
|
||||
sql.c utils.c metadata.c scanner.c inotify.c \
|
||||
|
251
clients.c
Normal file
251
clients.c
Normal 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
76
clients.h
Normal 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
|
@ -326,7 +326,7 @@ getsyshwaddr(char * buf, int len)
|
||||
}
|
||||
|
||||
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;
|
||||
FILE * arp;
|
||||
|
@ -30,7 +30,8 @@
|
||||
#define __MINIDLNATYPES_H__
|
||||
|
||||
#include "config.h"
|
||||
#include <netinet/in.h>
|
||||
#include "clients.h"
|
||||
//#include <netinet/in.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
@ -66,28 +67,6 @@ enum file_types {
|
||||
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 {
|
||||
char *path; /* base path */
|
||||
media_types types; /* types of files to scan */
|
||||
@ -100,12 +79,4 @@ struct album_art_name_s {
|
||||
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
|
||||
|
77
minissdp.c
77
minissdp.c
@ -346,8 +346,7 @@ ParseUPnPClient(char *location)
|
||||
int content_len = sizeof(buf);
|
||||
struct NameValueParserData xml;
|
||||
int client;
|
||||
enum client_types type = 0;
|
||||
uint32_t flags = 0;
|
||||
int type = 0;
|
||||
char *model, *serial, *name;
|
||||
|
||||
if (strncmp(location, "http://", 7) != 0)
|
||||
@ -437,33 +436,46 @@ close:
|
||||
name = GetValueFromNameValueList(&xml, "friendlyName");
|
||||
if (model)
|
||||
{
|
||||
int i;
|
||||
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;
|
||||
flags |= FLAG_MS_PFS;
|
||||
flags |= FLAG_AUDIO_ONLY;
|
||||
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)
|
||||
if (client_types[i].match_type != EModelName)
|
||||
continue;
|
||||
if (strstr(model, client_types[i].match) != NULL)
|
||||
{
|
||||
type = ESamsungSeriesB;
|
||||
flags |= FLAG_SAMSUNG;
|
||||
flags |= FLAG_DLNA;
|
||||
flags |= FLAG_NO_RESIZE;
|
||||
type = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
flags |= FLAG_DLNA;
|
||||
flags |= FLAG_MIME_WAV_WAV;
|
||||
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) < 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);
|
||||
if (client < 0)
|
||||
{
|
||||
for (client=0; client<CLIENT_CACHE_SLOTS; client++)
|
||||
{
|
||||
if (clients[client].addr.s_addr)
|
||||
continue;
|
||||
get_remote_mac(dest.sin_addr, clients[client].mac);
|
||||
clients[client].addr = dest.sin_addr;
|
||||
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;
|
||||
}
|
||||
AddClientCache(dest.sin_addr, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
clients[client].type = type;
|
||||
clients[client].age = time(NULL);
|
||||
}
|
||||
clients[client].type = type;
|
||||
clients[client].flags = flags;
|
||||
clients[client].age = time(NULL);
|
||||
}
|
||||
|
||||
/* ProcessSSDPRequest()
|
||||
|
@ -89,7 +89,6 @@ char db_path[PATH_MAX] = {'\0'};
|
||||
char log_path[PATH_MAX] = {'\0'};
|
||||
struct media_dir_s * media_dirs = NULL;
|
||||
struct album_art_name_s * album_art_names = NULL;
|
||||
struct client_cache_s clients[CLIENT_CACHE_SLOTS];
|
||||
short int scanning = 0;
|
||||
volatile short int quitting = 0;
|
||||
volatile uint32_t updateID = 0;
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "minidlnatypes.h"
|
||||
#include "clients.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
@ -64,7 +65,6 @@
|
||||
# define SERVER_NAME "MiniDLNA"
|
||||
#endif
|
||||
|
||||
#define CLIENT_CACHE_SLOTS 20
|
||||
#define USE_FORK 1
|
||||
#define DB_VERSION 9
|
||||
|
||||
@ -194,7 +194,7 @@ extern uint32_t runtime_flags;
|
||||
#define GETFLAG(mask) (runtime_flags & mask)
|
||||
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
||||
|
||||
extern const char * pidfilename;
|
||||
extern const char *pidfilename;
|
||||
|
||||
extern char uuidvalue[];
|
||||
|
||||
@ -229,9 +229,8 @@ extern sqlite3 *db;
|
||||
extern char friendly_name[];
|
||||
extern char db_path[];
|
||||
extern char log_path[];
|
||||
extern struct media_dir_s * media_dirs;
|
||||
extern struct album_art_name_s * album_art_names;
|
||||
extern struct client_cache_s clients[CLIENT_CACHE_SLOTS];
|
||||
extern struct media_dir_s *media_dirs;
|
||||
extern struct album_art_name_s *album_art_names;
|
||||
extern short int scanning;
|
||||
extern volatile short int quitting;
|
||||
extern volatile uint32_t updateID;
|
||||
|
210
upnphttp.c
210
upnphttp.c
@ -77,6 +77,7 @@
|
||||
#include <libexif/exif-loader.h>
|
||||
#include "tivo_utils.h"
|
||||
#include "tivo_commands.h"
|
||||
#include "clients.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 */
|
||||
static void
|
||||
ParseHttpHeaders(struct upnphttp * h)
|
||||
@ -302,121 +269,42 @@ ParseHttpHeaders(struct upnphttp * h)
|
||||
}
|
||||
else if(strncasecmp(line, "User-Agent", 10)==0)
|
||||
{
|
||||
char *s;
|
||||
int i;
|
||||
/* Skip client detection if we already detected it. */
|
||||
if( h->req_client )
|
||||
goto next_header;
|
||||
p = colon + 1;
|
||||
while(isspace(*p))
|
||||
p++;
|
||||
if(strncasecmp(p, "Xbox/", 5)==0)
|
||||
for (i = 0; client_types[i].name; i++)
|
||||
{
|
||||
h->req_client = EXbox;
|
||||
h->reqflags |= FLAG_MIME_AVI_AVI;
|
||||
h->reqflags |= FLAG_MS_PFS;
|
||||
}
|
||||
else if(strncmp(p, "PLAYSTATION", 11)==0)
|
||||
{
|
||||
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;
|
||||
if (client_types[i].match_type != EUserAgent)
|
||||
continue;
|
||||
if (strstrc(p, client_types[i].match, '\r') != NULL)
|
||||
{
|
||||
h->req_client = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(strncasecmp(line, "X-AV-Client-Info", 16)==0)
|
||||
{
|
||||
int i;
|
||||
/* 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;
|
||||
p = colon + 1;
|
||||
while(isspace(*p))
|
||||
p++;
|
||||
if(strstrc(p, "PLAYSTATION 3", '\r'))
|
||||
for (i = 0; client_types[i].name; i++)
|
||||
{
|
||||
h->req_client = EPS3;
|
||||
h->reqflags |= FLAG_DLNA;
|
||||
h->reqflags |= FLAG_MIME_AVI_DIVX;
|
||||
}
|
||||
/* 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" */
|
||||
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;
|
||||
if (client_types[i].match_type != EXAVClientInfo)
|
||||
continue;
|
||||
if (strstrc(p, client_types[i].match, '\r') != NULL)
|
||||
{
|
||||
h->req_client = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(strncasecmp(line, "Transfer-Encoding", 17)==0)
|
||||
@ -485,13 +373,19 @@ ParseHttpHeaders(struct upnphttp * h)
|
||||
}
|
||||
else if(strncasecmp(line, "FriendlyName", 12)==0)
|
||||
{
|
||||
int i;
|
||||
p = colon + 1;
|
||||
while(isspace(*p))
|
||||
p++;
|
||||
if(strstrc(p, "LIFETAB", '\r'))
|
||||
for (i = 0; client_types[i].name; i++)
|
||||
{
|
||||
h->req_client = ELifeTab;
|
||||
h->reqflags |= FLAG_MS_PFS;
|
||||
if (client_types[i].match_type != EFriendlyName)
|
||||
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. */
|
||||
if( n < 0 )
|
||||
{
|
||||
for( n=0; n<CLIENT_CACHE_SLOTS; n++ )
|
||||
{
|
||||
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;
|
||||
}
|
||||
AddClientCache(h->clientaddr, h->req_client);
|
||||
}
|
||||
else if( (clients[n].type < EStandardDLNA150 && h->req_client == EStandardDLNA150) ||
|
||||
(clients[n].type == ESamsungSeriesB && h->req_client == ESamsungSeriesA) )
|
||||
else
|
||||
{
|
||||
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 detected a Samsung Series B earlier, don't overwrite it with Series A info */
|
||||
h->reqflags |= clients[n].flags;
|
||||
h->req_client = clients[n].type;
|
||||
return;
|
||||
if ((ctype < EStandardDLNA150 && type == EStandardDLNA150) ||
|
||||
(ctype == ESamsungSeriesB && type == ESamsungSeriesA))
|
||||
{
|
||||
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 )
|
||||
{
|
||||
h->reqflags |= clients[n].flags;
|
||||
h->req_client = clients[n].type;
|
||||
}
|
||||
}
|
||||
@ -979,7 +864,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
||||
if(strcmp(ROOTDESC_PATH, HttpUrl) == 0)
|
||||
{
|
||||
/* 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];
|
||||
i = 0;
|
||||
@ -995,7 +880,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
||||
friendly_name[i] = '\0';
|
||||
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);
|
||||
}
|
||||
@ -1837,7 +1722,7 @@ resized_error:
|
||||
}
|
||||
|
||||
void
|
||||
SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
SendResp_dlnafile(struct upnphttp *h, char *object)
|
||||
{
|
||||
char header[1024];
|
||||
struct string_s str;
|
||||
@ -1850,6 +1735,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
int64_t id;
|
||||
int sendfh;
|
||||
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;
|
||||
enum client_types client;
|
||||
char path[PATH_MAX];
|
||||
@ -1861,7 +1747,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
#endif
|
||||
|
||||
id = strtoll(object, NULL, 10);
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
if( cflags & FLAG_MS_PFS )
|
||||
{
|
||||
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);
|
||||
/* 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 )
|
||||
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");
|
||||
/* Samsung TVs (well, at least the A950) do this for some reason,
|
||||
* 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);
|
||||
goto error;
|
||||
|
18
upnphttp.h
18
upnphttp.h
@ -128,21 +128,6 @@ struct upnphttp {
|
||||
#define FLAG_XFERBACKGROUND 0x00004000
|
||||
#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
|
||||
#define MSG_MORE 0
|
||||
#endif
|
||||
@ -194,9 +179,6 @@ Send501(struct upnphttp *);
|
||||
void
|
||||
SendResp_upnphttp(struct upnphttp *);
|
||||
|
||||
int
|
||||
SearchClientCache(struct in_addr addr, int quiet);
|
||||
|
||||
void
|
||||
SendResp_icon(struct upnphttp *, char * url);
|
||||
void
|
||||
|
19
upnpsoap.c
19
upnpsoap.c
@ -322,16 +322,17 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
|
||||
#define FILTER_PV_SUBTITLE_FILE_URI 0x08000000
|
||||
#define FILTER_AV_MEDIA_CLASS 0x10000000
|
||||
|
||||
static u_int32_t
|
||||
set_filter_flags(char * filter, struct upnphttp *h)
|
||||
static uint32_t
|
||||
set_filter_flags(char *filter, struct upnphttp *h)
|
||||
{
|
||||
char *item, *saveptr = NULL;
|
||||
uint32_t flags = 0;
|
||||
int samsung = client_types[h->req_client].flags & FLAG_SAMSUNG;
|
||||
|
||||
if( !filter || (strlen(filter) <= 1) )
|
||||
/* Not the full 32 bits. Skip vendor-specific stuff by default. */
|
||||
return 0xFFFFFF;
|
||||
if( h->reqflags & FLAG_SAMSUNG )
|
||||
if( samsung )
|
||||
flags |= FILTER_DLNA_NAMESPACE;
|
||||
item = strtok_r(filter, ",", &saveptr);
|
||||
while( item != NULL )
|
||||
@ -371,7 +372,7 @@ set_filter_flags(char * filter, struct upnphttp *h)
|
||||
else if( strcmp(item, "upnp:albumArtURI") == 0 )
|
||||
{
|
||||
flags |= FILTER_UPNP_ALBUMARTURI;
|
||||
if( h->reqflags & FLAG_SAMSUNG )
|
||||
if( samsung )
|
||||
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
|
||||
}
|
||||
else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 )
|
||||
@ -1124,8 +1125,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
|
||||
args.returned = 0;
|
||||
args.requested = RequestedCount;
|
||||
args.client = h->req_client;
|
||||
args.flags = h->reqflags;
|
||||
args.client = client_types[h->req_client].type;
|
||||
args.flags = client_types[h->req_client].flags;
|
||||
args.str = &str;
|
||||
if( args.flags & FLAG_MS_PFS )
|
||||
{
|
||||
@ -1201,7 +1202,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
else
|
||||
ret = asprintf(&orderBy, "order by length(OBJECT_ID), OBJECT_ID");
|
||||
}
|
||||
else if( args.client == ERokuSoundBridge )
|
||||
else if( args.flags & FLAG_FORCE_SORT )
|
||||
{
|
||||
#ifdef __sparc__
|
||||
if( totalMatches < 10000 )
|
||||
@ -1570,8 +1571,8 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
|
||||
args.returned = 0;
|
||||
args.requested = RequestedCount;
|
||||
args.client = h->req_client;
|
||||
args.flags = h->reqflags;
|
||||
args.client = client_types[h->req_client].type;
|
||||
args.flags = client_types[h->req_client].flags;
|
||||
args.str = &str;
|
||||
if( args.flags & FLAG_MS_PFS )
|
||||
{
|
||||
|
@ -33,6 +33,9 @@
|
||||
#define PV_NAMESPACE \
|
||||
" xmlns:pv=\"http://www.pv.com/pvns/\""
|
||||
|
||||
#define FLAG_FREE_OBJECT_ID 0x40000000
|
||||
#define FLAG_ROOT_CONTAINER 0x80000000
|
||||
|
||||
struct Response
|
||||
{
|
||||
struct string_s *str;
|
||||
|
Loading…
x
Reference in New Issue
Block a user