Passed
Push — master ( 490307...0c396d )
by eertheertyerery
02:03
created

browse_metadata()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 75
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 63
c 1
b 0
f 0
nc 17
nop 5
dl 0
loc 75
rs 7.8739

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php 
2
/*-
3
 * Copyright (c) 2013 - 2021 Rozhuk Ivan <[email protected]>
4
 * All rights reserved.
5
 * 
6
 * Subject to the following obligations and disclaimer of warranty, use and
7
 * redistribution of this software, in source or object code forms, with or
8
 * without modifications are expressly permitted by Whistle Communications;
9
 * provided, however, that:
10
 * 1. Any and all reproductions of the source or object code must include the
11
 *    copyright notice above and the following disclaimer of warranties; and
12
 * 2. No rights are granted, in any manner or form, to use Whistle
13
 *    Communications, Inc. trademarks, including the mark "WHISTLE
14
 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
15
 *    such appears in the above copyright notice or in the software.
16
 * 
17
 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
18
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
19
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
20
 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
21
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
22
 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
23
 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
24
 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
25
 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
26
 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
27
 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
28
 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
29
 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
30
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32
 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
33
 * OF SUCH DAMAGE.
34
 *
35
 * Author: Rozhuk Ivan <[email protected]>
36
 *
37
 */
38
39
/* xml-SOAP MediaServer/ContentDirectory:3 for UPnP/DLNA */
40
/* http://upnp.org/specs/av/UPnP-av-ContentDirectory-v3-Service.pdf */
41
42
 
43
/* Config.*/
44
$basedir = dirname(__FILE__).'/../../upnpdata'; /* File system path. */
45
$baseurl = '/upnpdata'; /* WEB URL path. */
46
47
date_default_timezone_set('UTC');
48
49
50
/* File types. */
51
$file_class = array(
52
	'm3u' => 'object.container.storageFolder',
53
	'xspf' => 'object.container.storageFolder',
54
	'xml' => 'object.container.storageFolder',
55
56
	'bmp' => 'object.item.imageItem.photo',
57
	'gif' => 'object.item.imageItem.photo',
58
	'ico' => 'object.item.imageItem.photo',
59
	'png' => 'object.item.imageItem.photo',
60
	'jpe' => 'object.item.imageItem.photo',
61
	'jpg' => 'object.item.imageItem.photo',
62
	'jpeg' => 'object.item.imageItem.photo',
63
	'tif' => 'object.item.imageItem.photo',
64
	'tiff' => 'object.item.imageItem.photo',
65
	'svg' => 'object.item.imageItem.photo',
66
	'svgz' => 'object.item.imageItem.photo',
67
68
	'flac' => 'object.item.audioItem.musicTrack',
69
	'mp3' => 'object.item.audioItem.musicTrack', 
70
	'wav' => 'object.item.audioItem.musicTrack',
71
	'wma' => 'object.item.audioItem.musicTrack',
72
73
	'flv' => 'object.item.videoItem',
74
	'f4v' => 'object.item.videoItem',
75
	'3g2' => 'object.item.videoItem',
76
	'3gp' => 'object.item.videoItem',
77
	'3gp2' => 'object.item.videoItem',
78
	'3gpp' => 'object.item.videoItem',
79
	'asf' => 'object.item.videoItem',
80
	'asx' => 'object.item.videoItem',
81
	'avi' => 'object.item.videoItem.movie',
82
	'dat' => 'object.item.videoItem',
83
	'iso' => 'object.item.videoItem',
84
	'm2t' => 'object.item.videoItem',
85
	'm2ts' => 'object.item.videoItem',
86
	'm2v' => 'object.item.videoItem',
87
	'm4v' => 'object.item.videoItem',
88
	'mp2v' => 'object.item.videoItem',
89
	'mp4' => 'object.item.videoItem',
90
	'mp4v' => 'object.item.videoItem',
91
	'mpe' => 'object.item.videoItem',
92
	'mpeg' => 'object.item.videoItem',
93
	'mpg' => 'object.item.videoItem',
94
	'mod' => 'object.item.videoItem',
95
	'mov' => 'object.item.videoItem',
96
	'mkv' => 'object.item.videoItem.videoBroadcast',
97
	'mts' => 'object.item.videoItem',
98
	'ogg' => 'object.item.videoItem',
99
	'swf' => 'object.item.videoItem',
100
	'vob' => 'object.item.videoItem',
101
	'ts' => 'object.item.videoItem',
102
	'webm' => 'object.item.videoItem',
103
	'wm' => 'object.item.videoItem',
104
	'wmv' => 'object.item.videoItem',
105
	'wmx' => 'object.item.videoItem',
106
);
107
108
/* MIME types. */
109
$mime_types = array(
110
	'txt' => 'text/plain',
111
	'htm' => 'text/html',
112
	'html' => 'text/html',
113
	'php' => 'text/html',
114
	'css' => 'text/css',
115
	'js' => 'application/javascript',
116
	'json' => 'application/json',
117
	'xml' => 'application/xml',
118
	'swf' => 'application/x-shockwave-flash',
119
120
	/* Images. */
121
	'png' => 'image/png',
122
	'jpe' => 'image/jpeg',
123
	'jpeg' => 'image/jpeg',
124
	'jpg' => 'image/jpeg',
125
	'gif' => 'image/gif',
126
	'bmp' => 'image/bmp',
127
	'ico' => 'image/vnd.microsoft.icon',
128
	'tiff' => 'image/tiff',
129
	'tif' => 'image/tiff',
130
	'svg' => 'image/svg+xml',
131
	'svgz' => 'image/svg+xml',
132
133
	/* Audio. */
134
	'flac' => 'audio/ogg',
135
	'mp3' => 'audio/mpeg', 
136
	'wav' => 'audio/vnd.wave',
137
	'wma' => 'audio/x-ms-wma',
138
139
	/* Video. */
140
	'3gp' => 'video/3gpp',
141
	'3gpp' => 'video/3gpp',
142
	'3g2' => 'video/3gpp2',
143
	'3gpp2' => 'video/3gpp2',
144
	'flv' => 'video/x-flv',
145
	'qt' => 'video/quicktime',
146
	'ogg' => 'video/ogg',
147
	'mov' => 'video/mpeg',
148
	'mp4' => 'video/mp4',
149
	'mkv' => 'video/x-mkv',
150
	'm2ts' => 'video/MP2T',
151
	'ts' => 'video/MP2T',
152
	'webm' => 'video/webm',
153
);
154
155
156
/* Auto variables. */
157
158
/* "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" */
159
$http_hdr_soapact = $_SERVER['HTTP_SOAPACTION'];
160
$soap_shemas = strpos($http_hdr_soapact, 'urn:schemas-upnp-org:service:ContentDirectory:');
161
if (false === $soap_shemas)
162
	return (500);
163
$soap_service_ver = substr($http_hdr_soapact, ($soap_shemas + 46), 1);
164
$soap_service_func = substr($http_hdr_soapact, ($soap_shemas + 48), -1);
165
166
167
if (substr($basedir, -1, 1) !== '/') {
168
	$basedir = $basedir.'/';
169
}
170
$baseurl = implode('/', array_map('rawurlencode', explode('/', $baseurl)));
171
$baseurlpatch = 'http://'.$_SERVER['HTTP_HOST'].$baseurl;
172
if ('/' !== substr($baseurlpatch, -1, 1)) {
173
	$baseurlpatch = $baseurlpatch.'/';
174
}
175
/**
176
 * Apply workaround for the libxml PHP bugs:
177
 * @link https://bugs.php.net/bug.php?id=62577
178
 * @link https://bugs.php.net/bug.php?id=64938
179
 */
180
if (function_exists('libxml_disable_entity_loader')) {
181
	libxml_disable_entity_loader(false);
182
}
183
184
# $server = new SoapServer(null, array('uri' => "urn:schemas-upnp-org:service:ContentDirectory:3"));
185
$server = new SoapServer(dirname(__FILE__)."/../descr/ContentDirectory.wdsl",
186
		array('cache_wsdl' => WSDL_CACHE_MEMORY,
187
			'soap_version' => SOAP_1_2,
188
			'trace' => true
189
		));
190
191
192
function xml_encode($string) {
193
194
	return (str_replace(
195
	    array("&", "<", ">", /*'"',*/	"'"),
196
	    array("&amp;", "&lt;", "&gt;", /*"&quot;",*/	"&apos;"), 
197
	    $string));
198
}
199
200
function xml_decode($string) {
201
202
	return (str_replace(
203
	    array("&amp;", "&lt;", "&gt;", "&quot;", "&apos;"), 
204
	    array("&", "<", ">", '"', "'"),
205
	    $string));
206
}
207
208
209
function upnp_url_encode($url) {
210
211
	if ('http://' !== substr($url, 0, 7) ||
212
	    false === ($url_path_off = strrpos($url, '/', 8)))
213
		return (implode('/', array_map('rawurlencode', explode('/', $url))));
214
		//return (xml_encode(implode('/', array_map('rawurlencode', explode('/', $url)))));
215
		//return (xml_encode($url));
216
		//return ('<![CDATA[' . xml_encode($url) . ']]');
217
218
	return (substr($url, 0, $url_path_off).implode('/', array_map('rawurlencode', explode('/', substr($url, $url_path_off)))));
219
	//return (substr($url, 0, $url_path_off) . xml_encode(implode('/', array_map('rawurlencode', explode('/', substr($url, $url_path_off))))));
220
	//return (substr($url, 0, $url_path_off) . xml_encode(substr($url, $url_path_off)));
221
	//return ('<![CDATA[$url]]');
222
}
223
224
225
function upnp_get_class($file, $def) {
226
	global $file_class;
227
228
	if (!isset($file))
229
		return ($def);
230
	$dot = strrpos($file, '.');
231
	if (false === $dot)
232
		return ($def);
233
	$ext = strtolower(substr($file, ($dot + 1)));
234
	if (isset($file_class[$ext])) /* Skip unsupported file type. */
235
		return ($file_class[$ext]);
236
237
	return ($def);
238
}
239
240
241
function get_named_val($name, $buf) { /* ...val_name="value"... */
242
243
	$st_off = strpos($buf, $name);
244
	if (false === $st_off)
245
		return (null);
246
	$st_off += strlen($name);
247
	if ('="' !== substr($buf, $st_off, 2))
248
		return (null);
249
	$st_off += 2;
250
	$en_off = strpos($buf, '"', $st_off);
251
	if (false === $en_off)
252
		return (null);
253
254
	return (substr($buf, $st_off, ($en_off - $st_off)));
255
}
256
257
258
function m3u_calc_items_count($filename) {
259
260
	$items_count = 0;
261
	$fd = fopen($filename, 'r');
262
	if (false === $fd)
263
		return ($items_count);
264
	while (!feof($fd)) { /* Read the file line by line... */
265
		$buffer = trim(fgets($fd));
266
		if (false === strpos($buffer, '#EXTINF:')) /* Skip empty/bad lines. */
267
			continue;
268
		$entry = trim(fgets($fd));
269
		if (false === strpos($entry, '://'))
270
			continue;
271
		$items_count++;
272
	} 
273
	fclose($fd);
274
275
	return ($items_count);
276
}
277
278
279
function m3u_browse($filename, $StartingIndex, $RequestedCount, $UpdateID, $Result) {
280
	$NumberReturned = 0;
281
	$TotalMatches = 0;
282
283
	/* Open the file. */
284
	$fd = fopen($filename, 'r');
285
	if (false === $fd) {
286
		return (array('Result' => '',
287
				'NumberReturned' => 0,
288
				'TotalMatches' => 0,
289
				'UpdateID' => $UpdateID));
290
	}
291
	$date = upnp_date(filectime($filename), 1);
292
	if (is_writable($filename)) {
293
		$Restricted = '0';
294
	} else {
295
		$Restricted = '1';
296
	}
297
298
	//$logo_url_path = 'http://iptvremote.ru/channels/android/160/';
299
	//$logo_url_path = 'http://172.16.0.254/download/tmp/image/';
300
	while (!feof($fd)) { /* Read the file line by line... */
301
		$buffer = trim(fgets($fd));
302
		//if($buffer === false)
303
		//	break;
304
		if (false === strpos($buffer, '#EXTINF:')) { /* Skip empty/bad lines. */
305
			/*if (false !== strpos($buffer, '#EXTM3U')) {
306
				$logo_url_path = get_named_val('url-tvg-logo', $buffer);
307
				if (null !== $logo_url_path) {
308
					if ('/' !== substr($logo_url_path, -1, 1))
309
						$logo_url_path = $logo_url_path . '/';
310
				} else {
311
					$logo_url_path = 'http://iptvremote.ru/channels/android/160/';
312
					$logo_url_path = 'http://172.16.0.254/download/tmp/image/';
313
				}
314
			}*/
315
			continue;
316
		}
317
		$entry = trim(fgets($fd));
318
		if (false === strpos($entry, '://'))
319
			continue;
320
		/* Ok, item matched and may be returned. */
321
		$TotalMatches++;
322
		if (0 < $StartingIndex &&
323
		    $TotalMatches < $StartingIndex)
324
			continue; /* Skip first items. */
325
		if (0 < $RequestedCount &&
326
		    $NumberReturned >= $RequestedCount)
327
			continue; /* Do not add more than requested. */
328
		$NumberReturned++;
329
		/* Add item to result. */
330
		$title = xml_encode(trim(substr($buffer, (strpos($buffer, ',') + 1))));
331
		//$en_entry = upnp_url_encode($entry);
332
		$en_entry = xml_encode($entry);
333
		$iclass = upnp_get_class($entry, 'object.item.videoItem.videoBroadcast');
334
		$mimetype = 'video/mpeg';
335
		if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */
336
			$Result = $Result.
337
			    "<container id=\"$en_entry\" parentID=\"$ObjectID\" restricted=\"$Restricted\">".
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ObjectID seems to be never defined.
Loading history...
338
				"<dc:title>$title</dc:title>".
339
				'<upnp:class>object.container.storageFolder</upnp:class>'.
340
			    '</container>';
341
		} else {
342
			//$logo = get_named_val("tvg-logo", $buffer);
343
			//if (null === $logo) {
344
			//	$logo = trim(substr($buffer, (strpos($buffer, ',') + 1)));
345
			//}
346
			//$icon_url = upnp_url_encode($logo_url_path . mb_convert_case($logo, MB_CASE_LOWER, "UTF-8") . '.png');
347
			$Result = $Result.
348
			    "<item id=\"$en_entry\" parentID=\"$ObjectID\" restricted=\"$Restricted\">".
349
				"<dc:title>$title</dc:title>".
350
				"<dc:date>$date</dc:date>".
351
				//"<upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0\">$icon_url</upnp:albumArtURI>" .
352
				//"<upnp:icon>$icon_url</upnp:icon>" .
353
				"<upnp:class>$iclass</upnp:class>".
354
				"<res protocolInfo=\"http-get:*:$mimetype:*\">$en_entry</res>".
355
			    '</item>';
356
		}
357
	} 
358
	fclose($fd);
359
360
	$Result = $Result.'</DIDL-Lite>';
361
	return (array('Result' => $Result,
362
			'NumberReturned' => $NumberReturned,
363
			'TotalMatches' => $TotalMatches,
364
			'UpdateID' => $UpdateID));
365
}
366
367
368
function upnp_mime_content_type($filename) {
369
	global $mime_types;
370
371
	$def = 'video/mpeg';
372
373
	if (!isset($filename))
374
		return ($def);
375
	$dot = strrpos($filename, '.');
376
	if (false === $dot)
377
		return ($def);
378
	$ext = strtolower(substr($filename, ($dot + 1)));
379
	if (array_key_exists($ext, $mime_types)) {
380
		return ($mime_types[$ext]);
381
	} elseif (function_exists('finfo_open')) {
382
		$finfo = finfo_open(FILEINFO_MIME);
383
		$mimetype = finfo_file($finfo, $filename);
384
		finfo_close($finfo);
385
		return ($mimetype);
386
	}
387
388
	return ($def);
389
}
390
391
/* Format:
392
 * 0: 2005-02-10
393
 * 1: 2004-05-08T10:00:00
394
 * */
395
function upnp_date($timedate, $format) {
396
	$res = date('Y-m-d', $timedate);
397
398
	if (1 === $format) {
399
		$res = $res.'T'.date('H:i:s', $timedate);
400
	}
401
402
	return ($res);
403
}
404
405
406
function browse_metadata($filename, $dir, $ObjectID, $UpdateID, $Result) {
407
408
	/* Is file/dir exist? */
409
	$stat = stat($filename);
410
	if (false === $stat) { /* No such file/dir. */
411
		return (array('Result' => '',
412
				'NumberReturned' => 0,
413
				'TotalMatches' => 0,
414
				'UpdateID' => $UpdateID));
415
	}
416
417
	/* Collect data. */
418
	if (is_writable($filename)) {
419
		$WriteStatus = 'WRITABLE';
420
		$Restricted = '0';
421
	} else {
422
		$WriteStatus = 'NOT_WRITABLE';
423
		$Restricted = '1';
424
	}
425
	$basefilename = basename($dir);
426
	if ('0' === $ObjectID) {
427
		$title = 'root';
428
		$ParentID = '-1';
429
	} else {
430
		$title = xml_encode($basefilename);
431
		$ParentID = upnp_url_encode(dirname($dir));
432
	}
433
434
	if (is_dir($filename)) { /* Dir. */
435
		$StorageTotal = disk_total_space($filename);
436
		$StorageFree = disk_free_space($filename);
437
		$StorageUsed = ($StorageTotal - $StorageFree);
438
		$ChildCount = (count(scandir($filename)) - 2);
439
		$Result = $Result.
440
		    "<container id=\"$ObjectID\" parentID=\"$ParentID\" restricted=\"$Restricted\" searchable=\"1\" childCount=\"$ChildCount\">".
441
			"<dc:title>$title</dc:title>".
442
			'<upnp:class>object.container.storageFolder</upnp:class>'.
443
			"<upnp:storageTotal>$StorageTotal</upnp:storageTotal>".
444
			"<upnp:storageFree>$StorageFree</upnp:storageFree>".
445
			"<upnp:storageUsed>$StorageUsed</upnp:storageUsed>".
446
			"<upnp:writeStatus>$WriteStatus</upnp:writeStatus>";
447
		if ('0' === $ObjectID) {
448
			$Result = $Result.
449
				'<upnp:searchClass includeDerived="1">object.item.audioItem</upnp:searchClass>'.
450
				'<upnp:searchClass includeDerived="1">object.item.imageItem</upnp:searchClass>'.
451
				'<upnp:searchClass includeDerived="1">object.item.videoItem</upnp:searchClass>';
452
		}
453
		$Result = $Result.'</container>';
454
	} else { /* File or playlist. */
455
		$iclass = upnp_get_class($basefilename, 'object.item.videoItem');
456
		if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */
457
			$ChildCount = m3u_calc_items_count($filename);
458
			$Result = $Result.
459
			    "<container id=\"$ObjectID\" parentID=\"$ParentID\" restricted=\"$Restricted\" searchable=\"1\" childCount=\"$ChildCount\">".
460
				"<dc:title>$title</dc:title>".
461
				'<upnp:class>object.container.storageFolder</upnp:class>'.
462
			    '</container>';
463
		} else {
464
			$date = upnp_date(filectime($filename), 1);
465
			$size = filesize($filename);
466
			$mimetype = upnp_mime_content_type($filename);
467
			$Result = $Result.
468
			    "<item id=\"$ObjectID\" parentID=\"$ParentID\" restricted=\"$Restricted\">".
469
				"<dc:title>$title</dc:title>".
470
				"<dc:date>$date</dc:date>".
471
				"<upnp:class>$iclass</upnp:class>".
472
				"<res size=\"$size\" protocolInfo=\"http-get:*:$mimetype:*\">$ObjectID</res>".
473
			    '</item>';
474
		}
475
	}
476
	$Result = $Result.'</DIDL-Lite>';
477
	return (array('Result' => $Result,
478
			'NumberReturned' => 1,
479
			'TotalMatches' => 1,
480
			'UpdateID' => $UpdateID));
481
}
482
483
/* ContentDirectory funcs */
484
485
function GetSearchCapabilities() {
486
	// 'upnp:class'; /* dc:title,upnp:class,upnp:artist */
487
	//$SearchCaps = 'dc:creator,dc:date,dc:title,upnp:album,upnp:actor,upnp:artist,upnp:class,upnp:genre,@id,@parentID,@refID';
488
	$SearchCaps = 'dc:title';
489
490
	return ($SearchCaps);
491
}
492
493
494
function GetSortCapabilities() {
495
	$SortCaps = 'dc:title';
496
	/* dc:title,upnp:genre,upnp:album,dc:creator,res@size,
497
	 * res@duration,res@bitrate,dc:publisher,
498
	 * upnp:originalTrackNumber,dc:date,upnp:producer,upnp:rating,
499
	 * upnp:actor,upnp:director,dc:description
500
	 */
501
502
	return ($SortCaps);
503
}
504
505
506
function GetSortExtensionCapabilities() {
507
	$SortExtensionCaps = '';
508
509
	return ($SortExtensionCaps);
510
}
511
512
513
function GetFeatureList() {
514
	$FeatureList = '';
515
516
	return ($FeatureList);
517
}
518
519
520
function GetSystemUpdateID() {
521
	$Id = '1';
522
523
	return ($Id);
524
}
525
526
527
function GetServiceResetToken() {
528
	$ResetToken = '1';
529
530
	return ($ResetToken);
531
}
532
533
534
function Browse($ObjectID, $BrowseFlag, $Filter, $StartingIndex,
535
    $RequestedCount, $SortCriteria) {
536
	global $basedir, $baseurl, $baseurlpatch;
537
	$Result = '<DIDL-Lite'.
538
		    ' xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"'.
539
		    ' xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/"'.
540
		    ' xmlns:dc="http://purl.org/dc/elements/1.1/"'.
541
		    ' xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"'.
542
		    ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'.
543
		    ' xsi:schemaLocation="'.
544
			'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ http://www.upnp.org/schemas/av/didl-lite.xsd '.
545
			'urn:schemas-upnp-org:metadata-1-0/upnp/ http://www.upnp.org/schemas/av/upnp.xsd">';
546
	$ParentID = '-1';
0 ignored issues
show
Unused Code introduced by
The assignment to $ParentID is dead and can be removed.
Loading history...
547
	$NumberReturned = 0;
548
	$TotalMatches = 0;
549
	$UpdateID = 1;
550
551
	/* Check input param. */
552
	if (isset($ObjectID)) {
553
		$ObjectID_len = strlen($ObjectID);
554
		if ((1 === $ObjectID_len || (
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (1 === $ObjectID_len || ...substr($ObjectID, 0, 1), Probably Intended Meaning: 1 === $ObjectID_len || (...ubstr($ObjectID, 0, 1))
Loading history...
555
		     3 === $ObjectID_len && (
556
		      '_T' === substr($ObjectID, 1, 2) ||
557
		      '_D' === substr($ObjectID, 1, 2) ||
558
		      '_L' === substr($ObjectID, 1, 2)))) && (
559
		    '0' === substr($ObjectID, 0, 1) ||
560
		    'A' === substr($ObjectID, 0, 1) ||
561
		    'I' === substr($ObjectID, 0, 1) ||
562
		    'V' === substr($ObjectID, 0, 1) ||
563
		    'P' === substr($ObjectID, 0, 1) ||
564
		    'T' === substr($ObjectID, 0, 1))) { /* V, I, A, P, T - from X_GetFeatureList() */
565
			$ObjectID = '0';
566
			$dir = '';
567
		} else {
568
			$dir = rawurldecode(xml_decode($ObjectID));
569
			if ('/' !== substr($dir, -1, 1)) {
570
				$dir = $dir.'/';
571
			}
572
			/* Sec check: .. in path */
573
			$dotdotdir = '';
574
			$dirnames = explode('/', $dir);
575
			$dirnames_size = sizeof($dirnames);
576
			for ($di = 0; $di < $dirnames_size; $di++) {
577
				if ('.' === $dirnames[$di])
578
					continue;
579
				if ('..' === $dirnames[$di]) {
580
					$dir = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $dir is dead and can be removed.
Loading history...
581
					break;
582
				}
583
				if ($dirnames_size >= $di) {
584
					$dotdotdir = $dotdotdir.$dirnames[$di].'/';
585
				}
586
			}
587
			$dir = $dotdotdir;
588
			if ('/' === substr($dir, 0, 1) /*|| !is_dir($basedir.$dir)*/) {
589
				$dir = '';
590
			}
591
			/* Remove tail slash from file name. */
592
			if (!is_dir($basedir.$dir) &&
593
			    '/' === substr($dir, -1, 1)) {
594
				$dir = substr($dir, 0, -1);
595
			}
596
		}
597
	} else {
598
		$ObjectID = '0';
599
		$dir = '';
600
	}
601
602
	if ('BrowseMetadata' === $BrowseFlag) {
603
		return (browse_metadata($basedir.$dir, $dir, $ObjectID,
604
		    $UpdateID, $Result));
605
	}
606
607
	if (!isset($StartingIndex)) {
608
		$StartingIndex = 0;
609
	}
610
	if (!isset($RequestedCount)) {
611
		$RequestedCount = 0;
612
	}
613
614
	if (!is_dir($basedir.$dir)) { /* Play list file? */
615
		return (m3u_browse($basedir.$dir, $StartingIndex,
616
		    $RequestedCount, $UpdateID, $Result));
617
	}
618
619
	/* Scan directory and add to play list.*/
620
	$entries = scandir($basedir.$dir);
621
	/* Add dirs to play list. */
622
	foreach ($entries as $entry) {
623
		$filename = $basedir.$dir.$entry;
624
		if ('.' === substr($entry, 0, 1) ||
625
		    !is_dir($filename)) /* Skip files. */
626
			continue;
627
		/* Ok, item matched and may be returned. */
628
		$TotalMatches++;
629
		if (0 < $StartingIndex &&
630
		    $TotalMatches < $StartingIndex)
631
			continue; /* Skip first items. */
632
		if (0 < $RequestedCount &&
633
		    $NumberReturned >= $RequestedCount)
634
			continue; /* Do not add more than requested. */
635
		$NumberReturned++;
636
		/* Add item to result. */
637
		if (is_writable($filename)) {
638
			$Restricted = '0';
639
		} else {
640
			$Restricted = '1';
641
		}
642
		$title = xml_encode($entry);
643
		$en_entry = upnp_url_encode($dir.$entry);
644
		$ChildCount = (count(scandir($filename)) - 2);
645
		$Result = $Result.
646
		    "<container id=\"$en_entry\" parentID=\"$ObjectID\" restricted=\"$Restricted\" searchable=\"1\" childCount=\"$ChildCount\">".
647
			"<dc:title>$title</dc:title>".
648
			'<upnp:class>object.container.storageFolder</upnp:class>'.
649
		    '</container>';
650
	}
651
	/* Add files to play list. */
652
	foreach ($entries as $entry) {
653
		$filename = $basedir.$dir.$entry;
654
		if (is_dir($filename)) /* Skip dirs. */
655
			continue;
656
		$iclass = upnp_get_class($entry, null);
657
		if (null === $iclass) /* Skip unsupported file type. */
658
			continue;
659
		/* Ok, item matched and may be returned. */
660
		$TotalMatches++;
661
		if (0 < $StartingIndex &&
662
		    $TotalMatches < $StartingIndex)
663
			continue; /* Skip first items. */
664
		if (0 < $RequestedCount &&
665
		    $NumberReturned >= $RequestedCount)
666
			continue; /* Do not add more than requested. */
667
		$NumberReturned++;
668
		/* Add item to result. */
669
		if (is_writable($filename)) {
670
			$Restricted = '0';
671
		} else {
672
			$Restricted = '1';
673
		}
674
		$title = xml_encode($entry);
675
		$en_entry = upnp_url_encode($dir.$entry);
676
		if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */
677
			$ChildCount = m3u_calc_items_count($filename);
678
			$Result = $Result.
679
			    "<container id=\"$en_entry\" parentID=\"$ObjectID\" restricted=\"$Restricted\" searchable=\"1\" childCount=\"$ChildCount\">".
680
				"<dc:title>$title</dc:title>".
681
				'<upnp:class>object.container.storageFolder</upnp:class>'.
682
			    '</container>';
683
		} else {
684
			$date = upnp_date(filectime($filename), 1);
685
			$size = filesize($filename);
686
			$mimetype = upnp_mime_content_type($filename);
687
			$res_info_ex = '';
688
			$Result = $Result.
689
			    "<item id=\"$en_entry\" parentID=\"$ObjectID\" restricted=\"$Restricted\">".
690
				"<dc:title>$title</dc:title>".
691
				"<dc:date>$date</dc:date>".
692
				"<upnp:class>$iclass</upnp:class>";
693
			if ('object.item.imageItem' === substr($iclass, 0, 21)) {
694
				$Result = $Result.
695
				    "<upnp:albumArtURI>$baseurlpatch$en_entry</upnp:albumArtURI>".
696
				    "<upnp:icon>$baseurlpatch$en_entry</upnp:icon>";
697
				$img_info = getimagesize($filename);
698
				if (false !== $img_info) {
699
					$res_info_ex = ' resolution="'.$img_info[0].'x'.$img_info[1].'"';
700
				}
701
			}
702
			$Result = $Result.
703
				"<res size=\"$size\"$res_info_ex protocolInfo=\"http-get:*:$mimetype:*\">$baseurlpatch$en_entry</res>".
704
			    '</item>';
705
		}
706
	}
707
708
	$Result = $Result.'</DIDL-Lite>';
709
	return (array('Result' => $Result,
710
			'NumberReturned' => $NumberReturned,
711
			'TotalMatches' => $TotalMatches,
712
			'UpdateID' => $UpdateID));
713
}
714
715
716
function Search($ContainerID, $SearchCriteria, $Filter, $StartingIndex,
717
    $RequestedCount, $SortCriteria) {
718
	global $basedir, $baseurl, $baseurlpatch;
719
	$Result = '<DIDL-Lite'.
720
		    ' xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"'.
721
		    ' xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/"'.
722
		    ' xmlns:dc="http://purl.org/dc/elements/1.1/"'.
723
		    ' xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"'.
724
		    ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'.
725
		    ' xsi:schemaLocation="'.
726
			'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ http://www.upnp.org/schemas/av/didl-lite.xsd '.
727
			'urn:schemas-upnp-org:metadata-1-0/upnp/ http://www.upnp.org/schemas/av/upnp.xsd">';
728
	$NumberReturned = 0;
729
	$TotalMatches = 0;
730
	$UpdateID = 1;
731
732
	$Result = $Result.'</DIDL-Lite>';
733
734
	return (array('Result' => $Result,
735
			'NumberReturned' => $NumberReturned,
736
			'TotalMatches' => $TotalMatches,
737
			'UpdateID' => $UpdateID));
738
}
739
740
741
function CreateObject($ContainerID, $Elements) {
742
	$ObjectID = '';
743
	$Result = '';
744
745
	return (array('ObjectID' => $ObjectID,
746
			'Result' => $Result));
747
}
748
749
750
function DestroyObject($ObjectID) {
751
}
752
753
754
function UpdateObject($ObjectID, $CurrentTagValue, $NewTagValue) {
755
}
756
757
758
function MoveObject($ObjectID, $NewParentID, $NewObjectID) {
759
}
760
761
762
/* Samsung private. */
763
function X_GetFeatureList() {
764
	$FeatureList = 
765
		'<?xml version="1.0" encoding="UTF-8"?>'.
766
		'<Features'.
767
		' xmlns="urn:schemas-upnp-org:av:avs"'.
768
		' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'.
769
		' xmlns:sec="http://www.sec.co.kr/dlna"'.
770
		' xsi:schemaLocation="urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd">'.
771
			'<Feature name="samsung.com_BASICVIEW" version="1">'.
772
			    '<container id="A" type="object.item.audioItem"/>'.
773
			    '<container id="I" type="object.item.imageItem"/>'.
774
			    '<container id="V" type="object.item.videoItem"/>'.
775
			    '<container id="P" type="object.item.playlistItem"/>'.
776
			    '<container id="T" type="object.item.textItem"/>'.
777
			'</Feature>'.
778
		'</Features>';
779
780
	return ($FeatureList);
781
}
782
783
function X_SetBookmark($CategoryType, $RID, $ObjectID, $PosSecond) {
784
	/* Return:
785
	 * <sec:dcmInfo>BM=number of seconds</sec:dcmInfo>
786
	 * <upnp:lastPlaybackPosition>HH:MM:SS</upnp:lastPlaybackPosition>
787
	 */
788
}
789
790
791
792
/* Process request. */
793
794
$request_body = @file_get_contents('php://input');
795
if (is_string($request_body) === false) {
796
	header($_SERVER["SERVER_PROTOCOL"]." 400 Bad request");
797
	die();	
798
}
799
try {
800
	$server->addFunction(array('GetSearchCapabilities',
801
					'GetSortCapabilities',
802
					'GetSortExtensionCapabilities',
803
					'GetFeatureList',
804
					'GetSystemUpdateID',
805
					'GetServiceResetToken',
806
					'Browse',
807
					'Search',
808
					'CreateObject',
809
					'DestroyObject',
810
					'UpdateObject',
811
					'MoveObject',
812
					'X_GetFeatureList',
813
					'X_SetBookmark'
814
				));
815
	ob_start();
816
	/* Type checking done before, it is safe to ignore type warning here. */
817
	$server->handle(/** @scrutinizer ignore-type */ $request_body); 
818
	$soapXml = ob_get_clean();
819
} catch (Exception $e) {
820
	$server->fault($e->getCode(), $e->getMessage());
821
	throw $e;
822
}
823
824
825
/* Post processing. */
826
827
828
function get_resp_tag_name($sxml) {
829
	$tag_st = strpos($sxml, '<SOAP-ENV:Body><SOAP-ENV:');
830
	if (false === $tag_st)
831
		return (false);
832
	$tag_st += 25;
833
	$tag_end = strpos($sxml, '>', $tag_st);
834
	if (false === $tag_end)
835
		return (false);
836
	return (substr($sxml, $tag_st, ($tag_end - $tag_st)));
837
}
838
839
function get_tag_ns($req, $tag) {
840
	$rreq = strrev($req);
841
	$ns_st = strpos($rreq, strrev(":$tag>"));
842
	if (false === $ns_st)
843
		return (false);
844
	$ns_st += (strlen($tag) + 2);
845
	$ns_end = strpos($rreq, '/<', $ns_st);
846
	if (false === $ns_end)
847
		return (false);
848
	return (strrev(substr($rreq, $ns_st, ($ns_end - $ns_st))));
849
}
850
851
function tag_ns_replace($req, $sxml, $tag, $ns = false) {
852
	if (false === $tag)
853
		return ($sxml);
854
	if (false === $ns)
855
		$ns = get_tag_ns($req, $tag);
856
	if (false === $ns)
857
		return ($sxml);
858
	while ($tag_st = strpos($sxml, "<SOAP-ENV:$tag")) {
859
		$tag_end = strpos($sxml, '>', $tag_st);
860
		if (false === $tag_end)
861
			return ($sxml);
862
		$old_tag_data = substr($sxml, $tag_st, ($tag_end - $tag_st));
863
		$new_tag_data = str_replace('SOAP-ENV', $ns, $old_tag_data);
864
		$sxml = str_replace($old_tag_data, $new_tag_data, $sxml);
865
	}
866
	return (str_replace("</SOAP-ENV:$tag>", "</$ns:$tag>", $sxml));
867
}
868
869
870
/* Add encodingStyle attr. */
871
$soapXml = str_replace('<SOAP-ENV:Envelope', '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"', $soapXml);
872
873
/* Add xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1" */
874
$soap_act_resp_tag = get_resp_tag_name($soapXml);
875
$soapXml = str_replace("<SOAP-ENV:$soap_act_resp_tag", "<SOAP-ENV:$soap_act_resp_tag xmlns:SOAP-ENV=\"urn:schemas-upnp-org:service:ContentDirectory:$soap_service_ver\"", $soapXml);
876
877
/* Restore name spaces from request. */
878
$soapXml = tag_ns_replace($request_body, $soapXml, 'Envelope');
879
$soapXml = tag_ns_replace($request_body, $soapXml, 'Body');
880
$soapXml = tag_ns_replace($request_body, $soapXml, $soap_act_resp_tag, get_tag_ns($request_body, $soap_service_func));
881
882
$length = strlen($soapXml);
883
header('Content-Length: '.$length);
884
echo $soapXml;
885
886
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
887