1/* MiniDLNA media server
2 * Copyright (C) 2013  NETGEAR
3 *
4 * This file is part of MiniDLNA.
5 *
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <stdio.h>
19#include <string.h>
20#include <time.h>
21
22#include "clients.h"
23#include "getifaddr.h"
24#include "log.h"
25
26struct client_type_s client_types[] =
27{
28	{ 0,
29	  0,
30	  "Unknown",
31	  NULL,
32	  EMatchNone
33	},
34
35	{ EXbox,
36	  FLAG_MIME_AVI_AVI | FLAG_MS_PFS,
37	  "Xbox 360",
38	  "Xbox/",
39	  EUserAgent
40	},
41
42	{ EPS3,
43	  FLAG_DLNA | FLAG_MIME_AVI_DIVX,
44	  "PLAYSTATION 3",
45	  "PLAYSTATION",
46	  EUserAgent
47	},
48
49	{ EPS3,
50	  FLAG_DLNA | FLAG_MIME_AVI_DIVX,
51	  "PLAYSTATION 3",
52	  "PLAYSTATION 3",
53	  EXAVClientInfo
54	},
55
56	/* Samsung Series [CDE] BDPs and TVs must be separated, or some of our
57	 * advertised extra features trigger a folder browsing bug on BDPs. */
58	/* User-Agent: DLNADOC/1.50 SEC_HHP_BD-D5100/1.0 */
59	{ ESamsungSeriesCDEBDP,
60	  FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
61	  "Samsung Series [CDEF] BDP",
62	  "SEC_HHP_BD",
63	  EUserAgent
64	},
65
66	/* User-Agent: DLNADOC/1.50 SEC_HHP_[TV]UE40D7000/1.0 */
67	/* User-Agent: DLNADOC/1.50 SEC_HHP_ Family TV/1.0 */
68	{ ESamsungSeriesCDE,
69	  FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_DCM10,
70	  "Samsung Series [CDEF]",
71	  "SEC_HHP_",
72	  EUserAgent
73	},
74
75	{ ESamsungSeriesA,
76	  FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
77	  "Samsung Series A",
78	  "SamsungWiselinkPro",
79	  EUserAgent
80	},
81
82	{ ESamsungSeriesB,
83	  FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
84	  "Samsung Series B",
85	  "Samsung DTV DMR",
86	  EModelName
87	},
88
89	/* User-Agent: Panasonic MIL DLNA CP UPnP/1.0 DLNADOC/1.50 */
90	{ EPanasonic,
91	  FLAG_DLNA | FLAG_FORCE_SORT,
92	  "Panasonic",
93	  "Panasonic",
94	  EUserAgent
95	},
96
97	/* User-Agent: IPI/1.0 UPnP/1.0 DLNADOC/1.50 */
98	{ ENetFrontLivingConnect,
99	  FLAG_DLNA | FLAG_FORCE_SORT | FLAG_CAPTION_RES,
100	  "NetFront Living Connect",
101	  "IPI/1",
102	  EUserAgent
103	},
104
105	{ EDenonReceiver,
106	  FLAG_DLNA,
107	  "Denon Receiver",
108	  "bridgeCo-DMP/3",
109	  EUserAgent
110	},
111
112	{ EFreeBox,
113	  FLAG_RESIZE_THUMBS,
114	  "FreeBox",
115	  "fbxupnpav/",
116	  EUserAgent
117	},
118
119	{ EPopcornHour,
120	  FLAG_MIME_FLAC_FLAC,
121	  "Popcorn Hour",
122	  "SMP8634",
123	  EUserAgent
124	},
125
126	/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Blu-ray Disc Player"; mv="2.0" */
127	/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BLU-RAY HOME THEATRE SYSTEM"; mv="2.0"; */
128	/* Sony SMP-100 needs the same treatment as their BDP-S370 */
129	/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Media Player"; mv="2.0" */
130	{ ESonyBDP,
131	  FLAG_DLNA,
132	  "Sony BDP",
133	  "mv=\"2.0\"",
134	  EXAVClientInfo
135	},
136
137	/* User-Agent: Linux/2.6.31-1.0 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.5.0 */
138	{ ELGDevice,
139	  FLAG_DLNA | FLAG_CAPTION_RES,
140	  "LG",
141	  "LGE_DLNA_SDK",
142	  EUserAgent
143	},
144
145	/* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BRAVIA KDL-40EX503"; mv="1.7"; */
146	{ ESonyBravia,
147	  FLAG_DLNA,
148	  "Sony Bravia",
149	  "BRAVIA",
150	  EXAVClientInfo
151	},
152
153	/* X-AV-Client-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="INTERNET TV NSX-40GT 1"; mv="0.1"; */
154	{ ESonyInternetTV,
155	  FLAG_DLNA,
156	  "Sony Internet TV",
157	  "INTERNET TV",
158	  EXAVClientInfo
159	},
160
161	{ ENetgearEVA2000,
162	  FLAG_MS_PFS | FLAG_RESIZE_THUMBS,
163	  "EVA2000",
164	  "Verismo,",
165	  EUserAgent
166	},
167
168	{ EDirecTV,
169	  FLAG_RESIZE_THUMBS,
170	  "DirecTV",
171	  "DIRECTV ",
172	  EUserAgent
173	},
174
175	{ EToshibaTV,
176	  FLAG_DLNA,
177	  "Toshiba TV",
178	  "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2",
179	  EUserAgent
180	},
181
182	{ ERokuSoundBridge,
183	  FLAG_MS_PFS | FLAG_AUDIO_ONLY | FLAG_MIME_WAV_WAV | FLAG_FORCE_SORT,
184	  "Roku SoundBridge",
185	  "Roku SoundBridge",
186	  EModelName
187	},
188
189	{ EMarantzDMP,
190	  FLAG_DLNA | FLAG_MIME_WAV_WAV,
191	  "marantz DMP",
192	  "marantz DMP",
193	  EFriendlyNameSSDP
194	},
195
196	{ EMediaRoom,
197	  FLAG_MS_PFS,
198	  "MS MediaRoom",
199	  "Microsoft-IPTV-Client",
200	  EUserAgent
201	},
202
203	{ ELifeTab,
204	  FLAG_MS_PFS,
205	  "LIFETAB",
206	  "LIFETAB",
207	  EFriendlyName
208	},
209
210	{ EAsusOPlay,
211	  FLAG_DLNA | FLAG_MIME_AVI_AVI | FLAG_CAPTION_RES,
212	  "Asus OPlay Mini/Mini+",
213	  "O!Play",
214	  EUserAgent
215	},
216
217	{ EBubbleUPnP,
218	  FLAG_CAPTION_RES,
219	  "BubbleUPnP",
220	  "BubbleUPnP",
221	  EUserAgent
222	},
223
224	{ EStandardDLNA150,
225	  FLAG_DLNA | FLAG_MIME_AVI_AVI,
226	  "Generic DLNA 1.5",
227	  "DLNADOC/1.50",
228	  EUserAgent
229	},
230
231	{ EStandardUPnP,
232	  0,
233	  "Generic UPnP 1.0",
234	  "UPnP/1.0",
235	  EUserAgent
236	},
237
238	{ 0, 0, NULL, 0 }
239};
240
241struct client_cache_s clients[CLIENT_CACHE_SLOTS];
242
243struct client_cache_s *
244SearchClientCache(struct in_addr addr, int quiet)
245{
246	int i;
247
248	for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
249	{
250		if (clients[i].addr.s_addr == addr.s_addr)
251		{
252			/* Invalidate this client cache if it's older than 1 hour */
253			if ((time(NULL) - clients[i].age) > 3600)
254			{
255				unsigned char mac[6];
256				if (get_remote_mac(addr, mac) == 0 &&
257				    memcmp(mac, clients[i].mac, 6) == 0)
258				{
259					/* Same MAC as last time when we were able to identify the client,
260					 * so extend the timeout by another hour. */
261					clients[i].age = time(NULL);
262				}
263				else
264				{
265					memset(&clients[i], 0, sizeof(struct client_cache_s));
266					return NULL;
267				}
268			}
269			if (!quiet)
270				DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [%s/entry %d]\n",
271					clients[i].type->name, i);
272			return &clients[i];
273		}
274	}
275
276	return NULL;
277}
278
279struct client_cache_s *
280AddClientCache(struct in_addr addr, int type)
281{
282	int i;
283
284	for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
285	{
286		if (clients[i].addr.s_addr)
287			continue;
288		get_remote_mac(addr, clients[i].mac);
289		clients[i].addr = addr;
290		clients[i].type = &client_types[type];
291		clients[i].age = time(NULL);
292		DPRINTF(E_DEBUG, L_HTTP, "Added client [%s/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
293					client_types[type].name, inet_ntoa(clients[i].addr),
294					clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
295					clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], i);
296		return &clients[i];
297	}
298
299	return NULL;
300}
301
302