From 365e20926f5cbcf832fcd8ac9c715de16206077b Mon Sep 17 00:00:00 2001 From: Nate Date: Mon, 30 Mar 2020 20:08:45 -0600 Subject: [PATCH] Safari Audio Support: Call Recordings, MOH, VM Greetings & Messages and CDR audio files. --- .../resources/classes/call_recordings.php | 117 ++++++++++++++-- app/music_on_hold/music_on_hold.php | 105 +++++++++++++- app/recordings/recordings.php | 96 +++++++------ .../voicemail_greetings.php | 111 ++++++++++++++- .../resources/classes/voicemail.php | 128 +++++++++++++++--- app/xml_cdr/resources/classes/xml_cdr.php | 124 ++++++++++++++--- 6 files changed, 581 insertions(+), 100 deletions(-) diff --git a/app/call_recordings/resources/classes/call_recordings.php b/app/call_recordings/resources/classes/call_recordings.php index 838f66aca9..b5fe8bdad7 100644 --- a/app/call_recordings/resources/classes/call_recordings.php +++ b/app/call_recordings/resources/classes/call_recordings.php @@ -134,14 +134,14 @@ if (!class_exists('call_recordings')) { $default_path = $_SESSION['switch']['call_recordings']['dir']."/".$_SESSION['domain_name']; //build full path - $full_recording_path = $call_recording_path . '/' . $call_recording_name; + $full_recording_path = $call_recording_path.'/'.$call_recording_name; //download the file if (file_exists($full_recording_path)) { //content-range - //if (isset($_SERVER['HTTP_RANGE'])) { - // range_download($full_recording_path); - //} + if (isset($_SERVER['HTTP_RANGE']) && !$this->binary) { + $this->range_download($full_recording_path); + } ob_clean(); $fd = fopen($full_recording_path, "rb"); if ($this->binary) { @@ -151,18 +151,19 @@ if (!class_exists('call_recordings')) { header("Content-Description: File Transfer"); } else { - $file_ext = substr($call_recording_name, -3); - if ($file_ext == "wav") { - header("Content-Type: audio/x-wav"); - } - if ($file_ext == "mp3") { - header("Content-Type: audio/mpeg"); + $file_ext = pathinfo($call_recording_name, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header("Content-Type: audio/x-wav"); break; + case "mp3" : header("Content-Type: audio/mpeg"); break; + case "ogg" : header("Content-Type: audio/ogg"); break; } } header('Content-Disposition: attachment; filename="'.$call_recording_name.'"'); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past - // header("Content-Length: ".filesize($full_recording_path)); + if ($this->binary) { + header("Content-Length: ".filesize($full_recording_path)); + } ob_clean(); fpassthru($fd); } @@ -174,6 +175,100 @@ if (!class_exists('call_recordings')) { } } //method + + /* + * range download method (helps safari play audio sources) + */ + private function range_download($file) { + $fp = @fopen($file, 'rb'); + + $size = filesize($file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + // Now that we've gotten so far without errors we send the accept range header + /* At the moment we only support single ranges. + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ + header("Accept-Ranges: 0-$length"); + // header('Accept-Ranges: bytes'); + // multipart/byteranges + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + // Extract the range string + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range + if (strpos($range, ',') !== false) { + // (?) Shoud this be issued here, or should the first + // range be used? Or should the header be ignored and + // we output the whole content? + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + // If the range starts with an '-' we start from the beginning + // If not, we forward the file pointer + // And make sure to get the end byte if spesified + if ($range0 == '-') { + // The n-number of the last bytes is requested + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; // Calculate new content length + fseek($fp, $start); + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + + // Start buffered download + $buffer = 1024 * 8; + while(!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + // In case we're only outputtin a chunk, make sure we don't + // read past the length + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files + echo fread($fp, $buffer); + flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. + } + + fclose($fp); + } + } //class } diff --git a/app/music_on_hold/music_on_hold.php b/app/music_on_hold/music_on_hold.php index b8d997d04a..ea4f62fb76 100644 --- a/app/music_on_hold/music_on_hold.php +++ b/app/music_on_hold/music_on_hold.php @@ -102,7 +102,7 @@ $stream_path = str_replace('..', '', $stream_path); //get the file and sanitize it - $stream_file = basename($_GET['file']); + $stream_file = basename('danza-espanola-op-37-h-142-xii-arabesca.wav'); $search = array('..', '/', ':'); $stream_file = str_replace($search, '', $stream_file); @@ -111,6 +111,11 @@ //download the file if (file_exists($stream_full_path)) { + //content-range + if (isset($_SERVER['HTTP_RANGE']) && $_GET['t'] != "bin") { + range_download($stream_full_path); + } + $fd = fopen($stream_full_path, "rb"); if ($_GET['t'] == "bin") { header("Content-Type: application/force-download"); @@ -129,7 +134,10 @@ header('Content-Disposition: attachment; filename="'.$stream_file.'"'); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past - header("Content-Length: ".filesize($stream_full_path)); + if ($_GET['t'] == "bin") { + header("Content-Length: ".filesize($stream_full_path)); + } + ob_clean(); fpassthru($fd); } exit; @@ -538,7 +546,7 @@ } echo " ".escape($stream_file)."\n"; echo " "; - echo ""; + echo ""; echo button::create(['type'=>'button','title'=>$text['label-play'].' / '.$text['label-pause'],'icon'=>$_SESSION['theme']['button_icon_play'],'id'=>'recording_button_'.$row_uuid,'onclick'=>"recording_play('".$row_uuid."');"]); //echo button::create(['type'=>'button','title'=>$text['label-stop'],'icon'=>$_SESSION['theme']['button_icon_stop'],'onclick'=>"recording_stop('".$row_uuid."'); this.blur(); return false;"]); echo button::create(['type'=>'button','title'=>$text['label-download'],'icon'=>$_SESSION['theme']['button_icon_download'],'link'=>"?action=download&id=".urlencode($row['music_on_hold_uuid'])."&file=".urlencode($stream_file)]); @@ -567,4 +575,95 @@ //include the footer require_once "resources/footer.php"; +//define the download function (helps safari play audio sources) + function range_download($file) { + $fp = @fopen($file, 'rb'); + + $size = filesize($file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + // Now that we've gotten so far without errors we send the accept range header + /* At the moment we only support single ranges. + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ + header("Accept-Ranges: 0-$length"); + // header('Accept-Ranges: bytes'); + // multipart/byteranges + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + // Extract the range string + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range + if (strpos($range, ',') !== false) { + // (?) Shoud this be issued here, or should the first + // range be used? Or should the header be ignored and + // we output the whole content? + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + // If the range starts with an '-' we start from the beginning + // If not, we forward the file pointer + // And make sure to get the end byte if spesified + if ($range0 == '-') { + // The n-number of the last bytes is requested + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; // Calculate new content length + fseek($fp, $start); + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + + // Start buffered download + $buffer = 1024 * 8; + while(!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + // In case we're only outputtin a chunk, make sure we don't + // read past the length + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files + echo fread($fp, $buffer); + flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. + } + + fclose($fp); + } + ?> \ No newline at end of file diff --git a/app/recordings/recordings.php b/app/recordings/recordings.php index a8736d36c1..f0b9b1d1f1 100644 --- a/app/recordings/recordings.php +++ b/app/recordings/recordings.php @@ -40,7 +40,6 @@ //download the recording if ($_GET['a'] == "download" && (permission_exists('recording_play') || permission_exists('recording_download'))) { - //session_cache_limiter('public'); if ($_GET['type'] = "rec") { //set the path for the directory $path = $_SESSION['switch']['recordings']['dir']."/".$_SESSION['domain_name']; @@ -77,7 +76,7 @@ //send the headers and then the data stream if (file_exists($full_recording_path)) { //content-range - if (isset($_SERVER['HTTP_RANGE'])) { + if (isset($_SERVER['HTTP_RANGE']) && $_GET['t'] != "bin") { range_download($full_recording_path); } @@ -89,18 +88,19 @@ header("Content-Description: File Transfer"); } else { - $file_ext = substr($recording_filename, -3); - if ($file_ext == "wav") { - header("Content-Type: audio/x-wav"); - } - if ($file_ext == "mp3") { - header("Content-Type: audio/mpeg"); + $file_ext = pathinfo($recording_filename, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header("Content-Type: audio/x-wav"); break; + case "mp3" : header("Content-Type: audio/mpeg"); break; + case "ogg" : header("Content-Type: audio/ogg"); break; } } header('Content-Disposition: attachment; filename="'.$recording_filename.'"'); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past - // header("Content-Length: ".filesize($full_recording_path)); + if ($_GET['t'] == "bin") { + header("Content-Length: ".filesize($full_recording_path)); + } ob_clean(); fpassthru($fd); } @@ -223,38 +223,44 @@ } else { //file found in db, check if base64 present - if ($_SESSION['recordings']['storage_type']['text'] == 'base64') { - $found_recording_uuid = array_search($recording_filename, $array_recordings); - if (!$array_base64_exists[$found_recording_uuid]) { - $recording_base64 = base64_encode(file_get_contents($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename)); - //build array - $array['recordings'][0]['domain_uuid'] = $domain_uuid; - $array['recordings'][0]['recording_uuid'] = $found_recording_uuid; - $array['recordings'][0]['recording_base64'] = $recording_base64; - //set temporary permissions - $p = new permissions; - $p->add('recording_edit', 'temp'); - //execute update - $database = new database; - $database->app_name = 'recordings'; - $database->app_uuid = '83913217-c7a2-9e90-925d-a866eb40b60e'; - $database->save($array); - unset($array); - //remove temporary permissions - $p->delete('recording_edit', 'temp'); + if ($_SESSION['recordings']['storage_type']['text'] == 'base64') { + $found_recording_uuid = array_search($recording_filename, $array_recordings); + if (!$array_base64_exists[$found_recording_uuid]) { + $recording_base64 = base64_encode(file_get_contents($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename)); + //build array + $array['recordings'][0]['domain_uuid'] = $domain_uuid; + $array['recordings'][0]['recording_uuid'] = $found_recording_uuid; + $array['recordings'][0]['recording_base64'] = $recording_base64; + //set temporary permissions + $p = new permissions; + $p->add('recording_edit', 'temp'); + //execute update + $database = new database; + $database->app_name = 'recordings'; + $database->app_uuid = '83913217-c7a2-9e90-925d-a866eb40b60e'; + $database->save($array); + unset($array); + //remove temporary permissions + $p->delete('recording_edit', 'temp'); + } } - } } //if base64, remove local file - if ($_SESSION['recordings']['storage_type']['text'] == 'base64' && file_exists($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename)) { - @unlink($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename); - } + if ($_SESSION['recordings']['storage_type']['text'] == 'base64' && file_exists($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename)) { + @unlink($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/'.$recording_filename); + } } } closedir($dh); } + + //redirect + if ($_GET['rd'] != '') { + header("Location: recordings.php"); + exit; + } } //get posted data @@ -506,7 +512,7 @@ //include the footer require_once "resources/footer.php"; -//define the download function +//define the download function (helps safari play audio sources) function range_download($file) { $fp = @fopen($file, 'rb'); @@ -516,16 +522,16 @@ $end = $size - 1; // End byte // Now that we've gotten so far without errors we send the accept range header /* At the moment we only support single ranges. - * Multiple ranges requires some more work to ensure it works correctly - * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 - * - * Multirange support annouces itself with: - * header('Accept-Ranges: bytes'); - * - * Multirange content must be sent with multipart/byteranges mediatype, - * (mediatype = mimetype) - * as well as a boundry header to indicate the various chunks of data. - */ + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ header("Accept-Ranges: 0-$length"); // header('Accept-Ranges: bytes'); // multipart/byteranges @@ -559,8 +565,8 @@ $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; } /* Check the range and make sure it's treated according to the specs. - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - */ + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ // End bytes can not be larger than $end. $c_end = ($c_end > $end) ? $end : $c_end; // Validate the requested range and return an error if it's not correct. diff --git a/app/voicemail_greetings/voicemail_greetings.php b/app/voicemail_greetings/voicemail_greetings.php index 946dd03b78..12e9bad461 100644 --- a/app/voicemail_greetings/voicemail_greetings.php +++ b/app/voicemail_greetings/voicemail_greetings.php @@ -99,6 +99,11 @@ } unset($sql, $row, $greeting_decoded); if (file_exists($v_greeting_dir.'/'.$greeting_filename)) { + //content-range + if (isset($_SERVER['HTTP_RANGE']) && $_GET['t'] != "bin") { + range_download($v_greeting_dir.'/'.$greeting_filename); + } + $fd = fopen($v_greeting_dir.'/'.$greeting_filename, "rb"); if ($_GET['t'] == "bin") { header("Content-Type: application/force-download"); @@ -107,18 +112,19 @@ header("Content-Description: File Transfer"); } else { - $file_ext = substr($greeting_filename, -3); - if ($file_ext == "wav") { - header("Content-Type: audio/x-wav"); - } - if ($file_ext == "mp3") { - header("Content-Type: audio/mpeg"); + $file_ext = pathinfo($greeting_filename, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header("Content-Type: audio/x-wav"); break; + case "mp3" : header("Content-Type: audio/mpeg"); break; + case "ogg" : header("Content-Type: audio/ogg"); break; } } header('Content-Disposition: attachment; filename="'.$greeting_filename.'"'); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past - header("Content-Length: ".filesize($v_greeting_dir.'/'.$greeting_filename)); + if ($_GET['t'] == "bin") { + header("Content-Length: ".filesize($v_greeting_dir.'/'.$greeting_filename)); + } ob_clean(); fpassthru($fd); } @@ -570,4 +576,95 @@ //include the footer require_once "resources/footer.php"; +//define the download function (helps safari play audio sources) + function range_download($file) { + $fp = @fopen($file, 'rb'); + + $size = filesize($file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + // Now that we've gotten so far without errors we send the accept range header + /* At the moment we only support single ranges. + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ + header("Accept-Ranges: 0-$length"); + // header('Accept-Ranges: bytes'); + // multipart/byteranges + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + // Extract the range string + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range + if (strpos($range, ',') !== false) { + // (?) Shoud this be issued here, or should the first + // range be used? Or should the header be ignored and + // we output the whole content? + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + // If the range starts with an '-' we start from the beginning + // If not, we forward the file pointer + // And make sure to get the end byte if spesified + if ($range0 == '-') { + // The n-number of the last bytes is requested + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; // Calculate new content length + fseek($fp, $start); + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + + // Start buffered download + $buffer = 1024 * 8; + while(!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + // In case we're only outputtin a chunk, make sure we don't + // read past the length + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files + echo fread($fp, $buffer); + flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. + } + + fclose($fp); + } + ?> \ No newline at end of file diff --git a/app/voicemails/resources/classes/voicemail.php b/app/voicemails/resources/classes/voicemail.php index 32847e1366..63773b2699 100644 --- a/app/voicemails/resources/classes/voicemail.php +++ b/app/voicemails/resources/classes/voicemail.php @@ -725,9 +725,6 @@ //change the message status $this->message_saved(); - //clear the cache - session_cache_limiter('public'); - //set source folder path $path = $_SESSION['switch']['voicemail']['dir'].'/default/'.$_SESSION['domain_name'].'/'.$this->voicemail_id; @@ -778,32 +775,37 @@ $file_path = $path.'/msg_'.$this->voicemail_message_uuid.'.mp3'; } if ($file_path != '') { + //content-range + if (isset($_SERVER['HTTP_RANGE']) && $this->type != 'bin') { + $this->range_download($file_path); + } + $fd = fopen($file_path, "rb"); if ($this->type == 'bin') { header("Content-Type: application/force-download"); header("Content-Type: application/octet-stream"); header("Content-Type: application/download"); header("Content-Description: File Transfer"); - $file_ext = substr($file_path, -3); - if ($file_ext == "wav") { - header('Content-Disposition: attachment; filename="msg_'.$this->voicemail_message_uuid.'.wav"'); - } - if ($file_ext == "mp3") { - header('Content-Disposition: attachment; filename="msg_'.$this->voicemail_message_uuid.'.mp3"'); + $file_ext = pathinfo($file_path, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header('Content-Disposition: attachment; filename="msg_'.$this->voicemail_message_uuid.'.wav"'); break; + case "mp3" : header('Content-Disposition: attachment; filename="msg_'.$this->voicemail_message_uuid.'.mp3"'); break; + case "ogg" : header('Content-Disposition: attachment; filename="msg_'.$this->voicemail_message_uuid.'.ogg"'); break; } } else { - $file_ext = substr($file_path, -3); - if ($file_ext == "wav") { - header("Content-Type: audio/wav"); - } - if ($file_ext == "mp3") { - header("Content-Type: audio/mpeg"); + $file_ext = pathinfo($file_path, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header("Content-Type: audio/x-wav"); break; + case "mp3" : header("Content-Type: audio/mpeg"); break; + case "ogg" : header("Content-Type: audio/ogg"); break; } } header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // date in the past - header("Content-Length: " . filesize($file_path)); + if ($this->type == 'bin') { + header("Content-Length: ".filesize($file_path)); + } ob_end_clean(); fpassthru($fd); } @@ -815,6 +817,100 @@ } + /* + * range download method (helps safari play audio sources) + */ + private function range_download($file) { + $fp = @fopen($file, 'rb'); + + $size = filesize($file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + // Now that we've gotten so far without errors we send the accept range header + /* At the moment we only support single ranges. + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ + header("Accept-Ranges: 0-$length"); + // header('Accept-Ranges: bytes'); + // multipart/byteranges + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + // Extract the range string + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range + if (strpos($range, ',') !== false) { + // (?) Shoud this be issued here, or should the first + // range be used? Or should the header be ignored and + // we output the whole content? + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + // If the range starts with an '-' we start from the beginning + // If not, we forward the file pointer + // And make sure to get the end byte if spesified + if ($range0 == '-') { + // The n-number of the last bytes is requested + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; // Calculate new content length + fseek($fp, $start); + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + + // Start buffered download + $buffer = 1024 * 8; + while(!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + // In case we're only outputtin a chunk, make sure we don't + // read past the length + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files + echo fread($fp, $buffer); + flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. + } + + fclose($fp); + } + + } //example voicemail messages diff --git a/app/xml_cdr/resources/classes/xml_cdr.php b/app/xml_cdr/resources/classes/xml_cdr.php index 4c7d7a8ec4..4f85fcfaef 100644 --- a/app/xml_cdr/resources/classes/xml_cdr.php +++ b/app/xml_cdr/resources/classes/xml_cdr.php @@ -1086,9 +1086,6 @@ if (!class_exists('xml_cdr')) { public function download($uuid) { if (permission_exists('xml_cdr_view')) { - //cache limiter - session_cache_limiter('public'); - //get call recording from database if (is_uuid($uuid)) { $sql = "select record_name, record_path from v_xml_cdr "; @@ -1106,14 +1103,14 @@ if (!class_exists('xml_cdr')) { } //build full path - $record_file = $record_path . '/' . $record_name; + $record_file = $record_path.'/'.$record_name; //download the file if (file_exists($record_file)) { //content-range - //if (isset($_SERVER['HTTP_RANGE'])) { - // range_download($record_file); - //} + if (isset($_SERVER['HTTP_RANGE']) && $_GET['t'] != "bin") { + $this->range_download($record_file); + } ob_clean(); $fd = fopen($record_file, "rb"); if ($_GET['t'] == "bin") { @@ -1123,28 +1120,119 @@ if (!class_exists('xml_cdr')) { header("Content-Description: File Transfer"); } else { - $file_ext = substr($record_name, -3); - if ($file_ext == "wav") { - header("Content-Type: audio/x-wav"); - } - if ($file_ext == "mp3") { - header("Content-Type: audio/mpeg"); - } - if ($file_ext == "ogg") { - header("Content-Type: audio/ogg"); + $file_ext = pathinfo($record_name, PATHINFO_EXTENSION); + switch ($file_ext) { + case "wav" : header("Content-Type: audio/x-wav"); break; + case "mp3" : header("Content-Type: audio/mpeg"); break; + case "ogg" : header("Content-Type: audio/ogg"); break; } } $record_name = preg_replace('#[^a-zA-Z0-9_\-\.]#', '', $record_name); header('Content-Disposition: attachment; filename="'.$record_name.'"'); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past - header("Content-Length: " . filesize($record_file)); + if ($_GET['t'] == "bin") { + header("Content-Length: ".filesize($record_file)); + } ob_clean(); fpassthru($fd); } } } //end download method + /* + * range download method (helps safari play audio sources) + */ + private function range_download($file) { + $fp = @fopen($file, 'rb'); + + $size = filesize($file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + // Now that we've gotten so far without errors we send the accept range header + /* At the moment we only support single ranges. + * Multiple ranges requires some more work to ensure it works correctly + * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + * + * Multirange support annouces itself with: + * header('Accept-Ranges: bytes'); + * + * Multirange content must be sent with multipart/byteranges mediatype, + * (mediatype = mimetype) + * as well as a boundry header to indicate the various chunks of data. + */ + header("Accept-Ranges: 0-$length"); + // header('Accept-Ranges: bytes'); + // multipart/byteranges + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2 + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + // Extract the range string + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range + if (strpos($range, ',') !== false) { + // (?) Shoud this be issued here, or should the first + // range be used? Or should the header be ignored and + // we output the whole content? + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + // If the range starts with an '-' we start from the beginning + // If not, we forward the file pointer + // And make sure to get the end byte if spesified + if ($range0 == '-') { + // The n-number of the last bytes is requested + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + // (?) Echo some info to the client? + exit; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; // Calculate new content length + fseek($fp, $start); + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + + // Start buffered download + $buffer = 1024 * 8; + while(!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + // In case we're only outputtin a chunk, make sure we don't + // read past the length + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files + echo fread($fp, $buffer); + flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit. + } + + fclose($fp); + } + /** * delete records */ @@ -1223,4 +1311,4 @@ if (!class_exists('xml_cdr')) { } //class } -?> +?> \ No newline at end of file