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