diff options
Diffstat (limited to 'modules/id3/getid3/write.real.php')
-rw-r--r-- | modules/id3/getid3/write.real.php | 251 |
1 files changed, 203 insertions, 48 deletions
diff --git a/modules/id3/getid3/write.real.php b/modules/id3/getid3/write.real.php index 5ede28d1..1e0240cc 100644 --- a/modules/id3/getid3/write.real.php +++ b/modules/id3/getid3/write.real.php @@ -16,7 +16,7 @@ class getid3_write_real { var $filename; - var $tag_data; + var $tag_data = array(); var $warnings = array(); // any non-critical errors will be stored here var $errors = array(); // any critical errors will be stored here var $paddedlength = 512; // minimum length of CONT tag in bytes @@ -33,50 +33,98 @@ class getid3_write_real // Initialize getID3 engine $getID3 = new getID3; $OldThisFileInfo = $getID3->analyze($this->filename); - if (empty($OldThisFileInfo['chunks']) && !empty($OldThisFileInfo['old_ra_header'])) { + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { $this->errors[] = 'Cannot write Real tags on old-style file format'; + fclose($fp_source); return false; } - $OldPROPinfo = false; - $StartOfDATA = false; - foreach ($OldThisFileInfo['chunks'] as $chunknumber => $chunkarray) { - if ($chunkarray['name'] == 'PROP') { - $OldPROPinfo = $chunkarray; - } elseif ($chunkarray['name'] = 'DATA') { - $StartOfDATA = $chunkarray['offset']; - } + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } + if (!empty($oldChunkInfo['CONT']['length'])) { + $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength); } - if (!empty($OldPROPinfo['length'])) { - $this->paddedlength = max($OldPROPinfo['length'], $this->paddedlength); + $new_CONT_tag_data = $this->GenerateCONTchunk(); + $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data); + $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); + + if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) { + fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); + fwrite($fp_source, $new__RMF_tag_data); + } else { + $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; + fclose($fp_source); + return false; } - $new_real_tag_data = GenerateRealTag(); + if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) { + fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); + fwrite($fp_source, $new_PROP_tag_data); + } else { + $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; + fclose($fp_source); + return false; + } - if (@$OldPROPinfo['length'] == $new_real_tag_data) { + if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) { // new data length is same as old data length - just overwrite - fseek($fp_source, $OldPROPinfo['offset'], SEEK_SET); - fwrite($fp_source, $new_real_tag_data); + fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); + fwrite($fp_source, $new_CONT_tag_data); + fclose($fp_source); + return true; } else { - if (empty($OldPROPinfo)) { - // no existing PROP chunk - $BeforeOffset = $StartOfDATA; - $AfterOffset = $StartOfDATA; + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + $BeforeOffset = $oldChunkInfo['DATA']['offset']; + $AfterOffset = $oldChunkInfo['DATA']['offset']; } else { // new data is longer than old data - $BeforeOffset = $OldPROPinfo['offset']; - $AfterOffset = $OldPROPinfo['offset'] + $OldPROPinfo['length']; + $BeforeOffset = $oldChunkInfo['CONT']['offset']; + $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; } + if ($tempfilename = tempnam('*', 'getID3')) { + ob_start(); + if ($fp_temp = fopen($tempfilename, 'wb')) { + + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fwrite($fp_temp, $new_CONT_tag_data); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; + } + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + } else { + + $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + + } + ob_end_clean(); + } + fclose($fp_source); + return false; } - fclose($fp_source); - return true; } else { $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; @@ -87,28 +135,88 @@ class getid3_write_real return false; } - function GenerateRealTag() { - $RealCONT = "\x00\x00"; // object version + function GenerateRMFchunk(&$chunks) { + $oldCONTexists = false; + foreach ($chunks as $key => $chunk) { + $chunkNameKeys[$chunk['name']] = $key; + if ($chunk['name'] == 'CONT') { + $oldCONTexists = true; + } + } + $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1); - $RealCONT .= BigEndian2String(strlen(@$this->tag_data['title']), 4); - $RealCONT .= @$this->tag_data['title']; + $RMFchunk = "\x00\x00"; // object version + $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4); + $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4); - $RealCONT .= BigEndian2String(strlen(@$this->tag_data['artist']), 4); - $RealCONT .= @$this->tag_data['artist']; + $RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length + return $RMFchunk; + } + + function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) { + $old_CONT_length = 0; + $old_DATA_offset = 0; + $old_INDX_offset = 0; + foreach ($chunks as $key => $chunk) { + $chunkNameKeys[$chunk['name']] = $key; + if ($chunk['name'] == 'CONT') { + $old_CONT_length = $chunk['length']; + } elseif ($chunk['name'] == 'DATA') { + if (!$old_DATA_offset) { + $old_DATA_offset = $chunk['offset']; + } + } elseif ($chunk['name'] == 'INDX') { + if (!$old_INDX_offset) { + $old_INDX_offset = $chunk['offset']; + } + } + } + $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length; - $RealCONT .= BigEndian2String(strlen(@$this->tag_data['copyright']), 4); - $RealCONT .= @$this->tag_data['copyright']; + $PROPchunk = "\x00\x00"; // object version + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4); + $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4); + $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2); - $RealCONT .= BigEndian2String(strlen(@$this->tag_data['comment']), 4); - $RealCONT .= @$this->tag_data['comment']; + $PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length + return $PROPchunk; + } - if ($this->paddedlength > (strlen($RealCONT) + 8)) { - $RealCONT .= str_repeat("\x00", $this->paddedlength - strlen($RealCONT) - 8); + function GenerateCONTchunk() { + foreach ($this->tag_data as $key => $value) { + // limit each value to 0xFFFF bytes + $this->tag_data[$key] = substr($value, 0, 65535); } - $RealCONT = 'CONT'.BigEndian2String(strlen($RealCONT) + 8, 4).$RealCONT; // CONT chunk identifier + chunk length + $CONTchunk = "\x00\x00"; // object version + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2); + $CONTchunk .= @$this->tag_data['title']; + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2); + $CONTchunk .= @$this->tag_data['artist']; + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2); + $CONTchunk .= @$this->tag_data['copyright']; - return $RealCONT; + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2); + $CONTchunk .= @$this->tag_data['comment']; + + if ($this->paddedlength > (strlen($CONTchunk) + 8)) { + $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8); + } + + $CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length + + return $CONTchunk; } function RemoveReal() { @@ -116,22 +224,69 @@ class getid3_write_real if (is_writeable($this->filename)) { if ($fp_source = @fopen($this->filename, 'r+b')) { -return false; - //fseek($fp_source, -128, SEEK_END); - //if (fread($fp_source, 3) == 'TAG') { - // ftruncate($fp_source, filesize($this->filename) - 128); - //} else { - // // no real tag to begin with - do nothing - //} + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { + $this->errors[] = 'Cannot remove Real tags from old-style file format'; + fclose($fp_source); + return false; + } + + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } + + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + fclose($fp_source); + return true; + } + + $BeforeOffset = $oldChunkInfo['CONT']['offset']; + $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; + if ($tempfilename = tempnam('*', 'getID3')) { + ob_start(); + if ($fp_temp = fopen($tempfilename, 'wb')) { + + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; + } + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + + } else { + + $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + + } + ob_end_clean(); + } fclose($fp_source); - return true; + return false; + } else { $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + return false; } - } else { - $this->errors[] = $this->filename.' is not writeable'; } + $this->errors[] = 'File is not writeable: '.$this->filename; return false; } |