ILIAS  release_5-2 Revision v5.2.25-18-g3f80b82851
Real.php
Go to the documentation of this file.
1 <?php
2 
3 namespace GetId3\Write;
4 
7 
10 // available at http://getid3.sourceforge.net //
11 // or http://www.getid3.org //
13 // See readme.txt for more details //
15 // //
16 // write.real.php //
17 // module for writing RealAudio/RealVideo tags //
18 // dependencies: module.tag.real.php //
19 // ///
21 
29 class Real
30 {
35  public $filename;
40  public $tag_data = array();
45  public $fread_buffer_size = 32768; // read buffer size in bytes
50  public $warnings = array(); // any non-critical errors will be stored here
55  public $errors = array(); // any critical errors will be stored here
60  public $paddedlength = 512; // minimum length of CONT tag in bytes
61 
66  public function __construct()
67  {
68  return true;
69  }
70 
75  public function WriteReal()
76  {
77  // File MUST be writeable - CHMOD(646) at least
78  if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
79 
80  // Initialize GetId3 engine
81  $getID3 = new GetId3Core();
82  $OldThisFileInfo = $getID3->analyze($this->filename);
83  if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
84  $this->errors[] = 'Cannot write Real tags on old-style file format';
85  fclose($fp_source);
86 
87  return false;
88  }
89 
90  if (empty($OldThisFileInfo['real']['chunks'])) {
91  $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
92  fclose($fp_source);
93 
94  return false;
95  }
96  foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
97  $oldChunkInfo[$chunkarray['name']] = $chunkarray;
98  }
99  if (!empty($oldChunkInfo['CONT']['length'])) {
100  $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
101  }
102 
103  $new_CONT_tag_data = $this->GenerateCONTchunk();
104  $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
105  $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
106 
107  if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
108  fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
109  fwrite($fp_source, $new__RMF_tag_data);
110  } else {
111  $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
112  fclose($fp_source);
113 
114  return false;
115  }
116 
117  if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
118  fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
119  fwrite($fp_source, $new_PROP_tag_data);
120  } else {
121  $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
122  fclose($fp_source);
123 
124  return false;
125  }
126 
127  if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
128 
129  // new data length is same as old data length - just overwrite
130  fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
131  fwrite($fp_source, $new_CONT_tag_data);
132  fclose($fp_source);
133 
134  return true;
135 
136  } else {
137 
138  if (empty($oldChunkInfo['CONT'])) {
139  // no existing CONT chunk
140  $BeforeOffset = $oldChunkInfo['DATA']['offset'];
141  $AfterOffset = $oldChunkInfo['DATA']['offset'];
142  } else {
143  // new data is longer than old data
144  $BeforeOffset = $oldChunkInfo['CONT']['offset'];
145  $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
146  }
147  if ($tempfilename = tempnam(GetId3Core::getTempDir(), 'getID3')) {
148  if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
149 
150  rewind($fp_source);
151  fwrite($fp_temp, fread($fp_source, $BeforeOffset));
152  fwrite($fp_temp, $new_CONT_tag_data);
153  fseek($fp_source, $AfterOffset, SEEK_SET);
154  while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
155  fwrite($fp_temp, $buffer, strlen($buffer));
156  }
157  fclose($fp_temp);
158 
159  if (copy($tempfilename, $this->filename)) {
160  unlink($tempfilename);
161  fclose($fp_source);
162 
163  return true;
164  }
165  unlink($tempfilename);
166  $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
167 
168  } else {
169  $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
170  }
171  }
172  fclose($fp_source);
173 
174  return false;
175 
176  }
177 
178  }
179  $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
180 
181  return false;
182  }
183 
189  public function GenerateRMFchunk(&$chunks)
190  {
191  $oldCONTexists = false;
192  foreach ($chunks as $key => $chunk) {
193  $chunkNameKeys[$chunk['name']] = $key;
194  if ($chunk['name'] == 'CONT') {
195  $oldCONTexists = true;
196  }
197  }
198  $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
199 
200  $RMFchunk = "\x00\x00"; // object version
201  $RMFchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
202  $RMFchunk .= Helper::BigEndian2String($newHeadersCount, 4);
203 
204  $RMFchunk = '.RMF'.Helper::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
205 
206  return $RMFchunk;
207  }
208 
215  public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data)
216  {
217  $old_CONT_length = 0;
218  $old_DATA_offset = 0;
219  $old_INDX_offset = 0;
220  foreach ($chunks as $key => $chunk) {
221  $chunkNameKeys[$chunk['name']] = $key;
222  if ($chunk['name'] == 'CONT') {
223  $old_CONT_length = $chunk['length'];
224  } elseif ($chunk['name'] == 'DATA') {
225  if (!$old_DATA_offset) {
226  $old_DATA_offset = $chunk['offset'];
227  }
228  } elseif ($chunk['name'] == 'INDX') {
229  if (!$old_INDX_offset) {
230  $old_INDX_offset = $chunk['offset'];
231  }
232  }
233  }
234  $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
235 
236  $PROPchunk = "\x00\x00"; // object version
237  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
238  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
239  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
240  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
241  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
242  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
243  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
244  $PROPchunk .= Helper::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
245  $PROPchunk .= Helper::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
246  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
247  $PROPchunk .= Helper::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
248 
249  $PROPchunk = 'PROP'.Helper::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
250 
251  return $PROPchunk;
252  }
253 
258  public function GenerateCONTchunk()
259  {
260  foreach ($this->tag_data as $key => $value) {
261  // limit each value to 0xFFFF bytes
262  $this->tag_data[$key] = substr($value, 0, 65535);
263  }
264 
265  $CONTchunk = "\x00\x00"; // object version
266 
267  $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0), 2);
268  $CONTchunk .= (!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '');
269 
270  $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0), 2);
271  $CONTchunk .= (!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '');
272 
273  $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
274  $CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
275 
276  $CONTchunk .= Helper::BigEndian2String((!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0), 2);
277  $CONTchunk .= (!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '');
278 
279  if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
280  $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
281  }
282 
283  $CONTchunk = 'CONT'.Helper::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
284 
285  return $CONTchunk;
286  }
287 
292  public function RemoveReal()
293  {
294  // File MUST be writeable - CHMOD(646) at least
295  if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
296 
297  // Initialize GetId3 engine
298  $getID3 = new GetId3Core();
299  $OldThisFileInfo = $getID3->analyze($this->filename);
300  if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
301  $this->errors[] = 'Cannot remove Real tags from old-style file format';
302  fclose($fp_source);
303 
304  return false;
305  }
306 
307  if (empty($OldThisFileInfo['real']['chunks'])) {
308  $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
309  fclose($fp_source);
310 
311  return false;
312  }
313  foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
314  $oldChunkInfo[$chunkarray['name']] = $chunkarray;
315  }
316 
317  if (empty($oldChunkInfo['CONT'])) {
318  // no existing CONT chunk
319  fclose($fp_source);
320 
321  return true;
322  }
323 
324  $BeforeOffset = $oldChunkInfo['CONT']['offset'];
325  $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
326  if ($tempfilename = tempnam(GetId3Core::getTempDir(), 'getID3')) {
327  if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
328 
329  rewind($fp_source);
330  fwrite($fp_temp, fread($fp_source, $BeforeOffset));
331  fseek($fp_source, $AfterOffset, SEEK_SET);
332  while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
333  fwrite($fp_temp, $buffer, strlen($buffer));
334  }
335  fclose($fp_temp);
336 
337  if (copy($tempfilename, $this->filename)) {
338  unlink($tempfilename);
339  fclose($fp_source);
340 
341  return true;
342  }
343  unlink($tempfilename);
344  $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
345 
346  } else {
347  $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
348  }
349  }
350  fclose($fp_source);
351 
352  return false;
353  }
354  $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
355 
356  return false;
357  }
358 
359 }
static BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false)
Definition: Helper.php:444
GetId3() by James Heinrich info@getid3.org //.
Definition: Real.php:29
GeneratePROPchunk(&$chunks, &$new_CONT_tag_data)
Definition: Real.php:215
GetId3() by James Heinrich info@getid3.org //.
Definition: GetId3Core.php:25
Create styles array
The data for the language used.
GenerateRMFchunk(&$chunks)
Definition: Real.php:189