00001 <?php
00004
00005
00007
00010
00011
00012
00013
00015
00016 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
00017
00018 class getid3_write_id3v2
00019 {
00020 var $filename;
00021 var $tag_data;
00022 var $paddedlength = 4096;
00023 var $majorversion = 3;
00024 var $minorversion = 0;
00025 var $merge_existing_data = false;
00026 var $id3v2_default_encodingid = 0;
00027 var $id3v2_use_unsynchronisation = false;
00028 var $warnings = array();
00029 var $errors = array();
00030
00031 function getid3_write_id3v2() {
00032 return true;
00033 }
00034
00035 function WriteID3v2() {
00036
00037
00038
00039 if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
00040
00041 $getID3 = new getID3;
00042 $OldThisFileInfo = $getID3->analyze($this->filename);
00043 if ($this->merge_existing_data) {
00044
00045 if (!empty($OldThisFileInfo['id3v2'])) {
00046 $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
00047 }
00048 }
00049 $this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
00050
00051 if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
00052
00053 if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
00054
00055
00056 if (file_exists($this->filename)) {
00057
00058 ob_start();
00059 if ($fp = fopen($this->filename, 'r+b')) {
00060 rewind($fp);
00061 fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
00062 fclose($fp);
00063 } else {
00064 $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents());
00065 }
00066 ob_end_clean();
00067
00068 } else {
00069
00070 ob_start();
00071 if ($fp = fopen($this->filename, 'wb')) {
00072 rewind($fp);
00073 fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
00074 fclose($fp);
00075 } else {
00076 $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents());
00077 }
00078 ob_end_clean();
00079
00080 }
00081
00082 } else {
00083
00084 if ($tempfilename = tempnam('*', 'getID3')) {
00085 ob_start();
00086 if ($fp_source = fopen($this->filename, 'rb')) {
00087 if ($fp_temp = fopen($tempfilename, 'wb')) {
00088
00089 fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
00090
00091 rewind($fp_source);
00092 if (!empty($OldThisFileInfo['avdataoffset'])) {
00093 fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00094 }
00095
00096 while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00097 fwrite($fp_temp, $buffer, strlen($buffer));
00098 }
00099
00100 fclose($fp_temp);
00101 fclose($fp_source);
00102 copy($tempfilename, $this->filename);
00103 unlink($tempfilename);
00104 ob_end_clean();
00105 return true;
00106
00107 } else {
00108
00109 $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
00110
00111 }
00112 fclose($fp_source);
00113
00114 } else {
00115
00116 $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents());
00117
00118 }
00119 ob_end_clean();
00120 }
00121 return false;
00122
00123 }
00124
00125 } else {
00126
00127 $this->errors[] = '$this->GenerateID3v2Tag() failed';
00128
00129 }
00130
00131 if (!empty($this->errors)) {
00132 return false;
00133 }
00134 return true;
00135 } else {
00136 $this->errors[] = '!is_writeable('.$this->filename.')';
00137 }
00138 return false;
00139 }
00140
00141 function RemoveID3v2() {
00142
00143
00144
00145 if (is_writeable(dirname($this->filename))) {
00146
00147
00148
00149 if ($fp_source = @fopen($this->filename, 'rb')) {
00150
00151 $getID3 = new getID3;
00152 $OldThisFileInfo = $getID3->analyze($this->filename);
00153 rewind($fp_source);
00154 if ($OldThisFileInfo['avdataoffset'] !== false) {
00155 fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00156 }
00157 if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
00158 while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00159 fwrite($fp_temp, $buffer, strlen($buffer));
00160 }
00161 fclose($fp_temp);
00162 } else {
00163 $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"';
00164 }
00165 fclose($fp_source);
00166 } else {
00167 $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
00168 }
00169 if (file_exists($this->filename)) {
00170 unlink($this->filename);
00171 }
00172 rename($this->filename.'getid3tmp', $this->filename);
00173
00174 } elseif (is_writable($this->filename)) {
00175
00176
00177
00178 if ($fp_source = @fopen($this->filename, 'rb')) {
00179
00180 $getID3 = new getID3;
00181 $OldThisFileInfo = $getID3->analyze($this->filename);
00182 rewind($fp_source);
00183 if ($OldThisFileInfo['avdataoffset'] !== false) {
00184 fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
00185 }
00186 if ($fp_temp = tmpfile()) {
00187 while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
00188 fwrite($fp_temp, $buffer, strlen($buffer));
00189 }
00190 fclose($fp_source);
00191 if ($fp_source = @fopen($this->filename, 'wb')) {
00192 rewind($fp_temp);
00193 while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) {
00194 fwrite($fp_source, $buffer, strlen($buffer));
00195 }
00196 fseek($fp_temp, -128, SEEK_END);
00197 fclose($fp_source);
00198 } else {
00199 $this->errors[] = 'Could not open '.$this->filename.' mode "wb"';
00200 }
00201 fclose($fp_temp);
00202 } else {
00203 $this->errors[] = 'Could not create tmpfile()';
00204 }
00205 } else {
00206 $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
00207 }
00208
00209 } else {
00210
00211 $this->errors[] = 'Directory and file both not writeable';
00212
00213 }
00214
00215 if (!empty($this->errors)) {
00216 return false;
00217 }
00218 return true;
00219 }
00220
00221
00222 function GenerateID3v2TagFlags($flags) {
00223 switch ($this->majorversion) {
00224 case 4:
00225
00226 $flag = (@$flags['unsynchronisation'] ? '1' : '0');
00227 $flag .= (@$flags['extendedheader'] ? '1' : '0');
00228 $flag .= (@$flags['experimental'] ? '1' : '0');
00229 $flag .= (@$flags['footer'] ? '1' : '0');
00230 $flag .= '0000';
00231 break;
00232
00233 case 3:
00234
00235 $flag = (@$flags['unsynchronisation'] ? '1' : '0');
00236 $flag .= (@$flags['extendedheader'] ? '1' : '0');
00237 $flag .= (@$flags['experimental'] ? '1' : '0');
00238 $flag .= '00000';
00239 break;
00240
00241 case 2:
00242
00243 $flag = (@$flags['unsynchronisation'] ? '1' : '0');
00244 $flag .= (@$flags['compression'] ? '1' : '0');
00245 $flag .= '000000';
00246 break;
00247
00248 default:
00249 return false;
00250 break;
00251 }
00252 return chr(bindec($flag));
00253 }
00254
00255
00256 function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
00257 switch ($this->majorversion) {
00258 case 4:
00259
00260 $flag1 = '0';
00261 $flag1 .= $TagAlter ? '1' : '0';
00262 $flag1 .= $FileAlter ? '1' : '0';
00263 $flag1 .= $ReadOnly ? '1' : '0';
00264 $flag1 .= '0000';
00265
00266 $flag2 = '0';
00267 $flag2 .= $GroupingIdentity ? '1' : '0';
00268 $flag2 .= '00';
00269 $flag2 .= $Compression ? '1' : '0';
00270 $flag2 .= $Encryption ? '1' : '0';
00271 $flag2 .= $Unsynchronisation ? '1' : '0';
00272 $flag2 .= $DataLengthIndicator ? '1' : '0';
00273 break;
00274
00275 case 3:
00276
00277 $flag1 = $TagAlter ? '1' : '0';
00278 $flag1 .= $FileAlter ? '1' : '0';
00279 $flag1 .= $ReadOnly ? '1' : '0';
00280 $flag1 .= '00000';
00281
00282 $flag2 = $Compression ? '1' : '0';
00283 $flag2 .= $Encryption ? '1' : '0';
00284 $flag2 .= $GroupingIdentity ? '1' : '0';
00285 $flag2 .= '00000';
00286 break;
00287
00288 default:
00289 return false;
00290 break;
00291
00292 }
00293 return chr(bindec($flag1)).chr(bindec($flag2));
00294 }
00295
00296 function GenerateID3v2FrameData($frame_name, $source_data_array) {
00297 if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
00298 return false;
00299 }
00300 $framedata = '';
00301
00302 if (($this->majorversion < 3) || ($this->majorversion > 4)) {
00303
00304 $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
00305
00306 } else {
00307
00308 switch ($frame_name) {
00309 case 'UFID':
00310
00311
00312
00313 if (strlen($source_data_array['data']) > 64) {
00314 $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
00315 } else {
00316 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
00317 $framedata .= substr($source_data_array['data'], 0, 64);
00318 }
00319 break;
00320
00321 case 'TXXX':
00322
00323
00324
00325
00326 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00327 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00328 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00329 } else {
00330 $framedata .= chr($source_data_array['encodingid']);
00331 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00332 $framedata .= $source_data_array['data'];
00333 }
00334 break;
00335
00336 case 'WXXX':
00337
00338
00339
00340
00341 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00342 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00343 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00344 } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
00345
00346
00347 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00348 } else {
00349 $framedata .= chr($source_data_array['encodingid']);
00350 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00351 $framedata .= $source_data_array['data'];
00352 }
00353 break;
00354
00355 case 'IPLS':
00356
00357
00358
00359 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00360 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
00361 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00362 } else {
00363 $framedata .= chr($source_data_array['encodingid']);
00364 $framedata .= $source_data_array['data'];
00365 }
00366 break;
00367
00368 case 'MCDI':
00369
00370
00371 $framedata .= $source_data_array['data'];
00372 break;
00373
00374 case 'ETCO':
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385 if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00386 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00387 } else {
00388 $framedata .= chr($source_data_array['timestampformat']);
00389 foreach ($source_data_array as $key => $val) {
00390 if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
00391 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
00392 } elseif (($key != 'timestampformat') && ($key != 'flags')) {
00393 if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
00394
00395
00396 $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
00397 } else {
00398 $framedata .= chr($val['typeid']);
00399 $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00400 }
00401 }
00402 }
00403 }
00404 break;
00405
00406 case 'MLLT':
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416 if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
00417 $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
00418 } else {
00419 $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
00420 }
00421 if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
00422 $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
00423 } else {
00424 $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
00425 }
00426 if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
00427 $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
00428 } else {
00429 $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
00430 }
00431 if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
00432 if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
00433 $framedata .= chr($source_data_array['bitsforbytesdeviation']);
00434 } else {
00435 $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
00436 }
00437 } else {
00438 $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
00439 }
00440 if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
00441 if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
00442 $framedata .= chr($source_data_array['bitsformsdeviation']);
00443 } else {
00444 $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
00445 }
00446 } else {
00447 $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
00448 }
00449 foreach ($source_data_array as $key => $val) {
00450 if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
00451 $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
00452 $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT);
00453 }
00454 }
00455 for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
00456 $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
00457 $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4));
00458 $framedata .= chr($highnibble & $lownibble);
00459 }
00460 break;
00461
00462 case 'SYTC':
00463
00464
00465
00466
00467
00468
00469 if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00470 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00471 } else {
00472 $framedata .= chr($source_data_array['timestampformat']);
00473 foreach ($source_data_array as $key => $val) {
00474 if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
00475 $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
00476 } elseif (($key != 'timestampformat') && ($key != 'flags')) {
00477 if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
00478 $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
00479 } else {
00480 if ($val['tempo'] > 255) {
00481 $framedata .= chr(255);
00482 $val['tempo'] -= 255;
00483 }
00484 $framedata .= chr($val['tempo']);
00485 $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00486 }
00487 }
00488 }
00489 }
00490 break;
00491
00492 case 'USLT':
00493
00494
00495
00496
00497
00498 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00499 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00500 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00501 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00502 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00503 } else {
00504 $framedata .= chr($source_data_array['encodingid']);
00505 $framedata .= strtolower($source_data_array['language']);
00506 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00507 $framedata .= $source_data_array['data'];
00508 }
00509 break;
00510
00511 case 'SYLT':
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00524 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00525 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00526 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00527 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00528 } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
00529 $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
00530 } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
00531 $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
00532 } elseif (!is_array($source_data_array['data'])) {
00533 $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
00534 } else {
00535 $framedata .= chr($source_data_array['encodingid']);
00536 $framedata .= strtolower($source_data_array['language']);
00537 $framedata .= chr($source_data_array['timestampformat']);
00538 $framedata .= chr($source_data_array['contenttypeid']);
00539 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00540 ksort($source_data_array['data']);
00541 foreach ($source_data_array['data'] as $key => $val) {
00542 $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00543 $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
00544 }
00545 }
00546 break;
00547
00548 case 'COMM':
00549
00550
00551
00552
00553
00554 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00555 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00556 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00557 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00558 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00559 } else {
00560 $framedata .= chr($source_data_array['encodingid']);
00561 $framedata .= strtolower($source_data_array['language']);
00562 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00563 $framedata .= $source_data_array['data'];
00564 }
00565 break;
00566
00567 case 'RVA2':
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577 $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
00578 foreach ($source_data_array as $key => $val) {
00579 if ($key != 'description') {
00580 $framedata .= chr($val['channeltypeid']);
00581 $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true);
00582 if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
00583 $framedata .= chr($val['bitspeakvolume']);
00584 if ($val['bitspeakvolume'] > 0) {
00585 $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
00586 }
00587 } else {
00588 $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
00589 }
00590 }
00591 }
00592 break;
00593
00594 case 'RVAD':
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610 if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
00611 $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
00612 } else {
00613 $incdecflag .= '00';
00614 $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0';
00615 $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0';
00616 $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0';
00617 $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0';
00618 $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0';
00619 $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0';
00620 $framedata .= chr(bindec($incdecflag));
00621 $framedata .= chr($source_data_array['bitsvolume']);
00622 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
00623 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
00624 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
00625 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
00626 if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
00627 $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
00628 $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
00629 $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00630 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
00631 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
00632 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
00633 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
00634 }
00635 if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
00636 $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00637 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
00638 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
00639 }
00640 if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
00641 $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
00642 $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
00643 }
00644 }
00645 break;
00646
00647 case 'EQU2':
00648
00649
00650
00651
00652
00653
00654
00655
00656 if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
00657 $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
00658 } else {
00659 $framedata .= chr($source_data_array['interpolationmethod']);
00660 $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
00661 foreach ($source_data_array['data'] as $key => $val) {
00662 $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
00663 $framedata .= getid3_lib::BigEndian2String($val, 2, false, true);
00664 }
00665 }
00666 break;
00667
00668 case 'EQUA':
00669
00670
00671
00672
00673
00674
00675
00676
00677 if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
00678 $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
00679 } else {
00680 $framedata .= chr($source_data_array['adjustmentbits']);
00681 foreach ($source_data_array as $key => $val) {
00682 if ($key != 'bitsvolume') {
00683 if (($key > 32767) || ($key < 0)) {
00684 $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
00685 } else {
00686 if ($val >= 0) {
00687
00688 $key |= 0x8000;
00689 }
00690 $framedata .= getid3_lib::BigEndian2String($key, 2, false);
00691 $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
00692 }
00693 }
00694 }
00695 }
00696 break;
00697
00698 case 'RVRB':
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710 if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
00711 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
00712 } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
00713 $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
00714 } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
00715 $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
00716 } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
00717 $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
00718 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
00719 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
00720 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
00721 $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
00722 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
00723 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
00724 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
00725 $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
00726 } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
00727 $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
00728 } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
00729 $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
00730 } else {
00731 $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
00732 $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
00733 $framedata .= chr($source_data_array['bouncesL']);
00734 $framedata .= chr($source_data_array['bouncesR']);
00735 $framedata .= chr($source_data_array['feedbackLL']);
00736 $framedata .= chr($source_data_array['feedbackLR']);
00737 $framedata .= chr($source_data_array['feedbackRR']);
00738 $framedata .= chr($source_data_array['feedbackRL']);
00739 $framedata .= chr($source_data_array['premixLR']);
00740 $framedata .= chr($source_data_array['premixRL']);
00741 }
00742 break;
00743
00744 case 'APIC':
00745
00746
00747
00748
00749
00750
00751 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00752 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00753 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00754 } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
00755 $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
00756 } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
00757 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
00758 } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
00759
00760
00761 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00762 } else {
00763 $framedata .= chr($source_data_array['encodingid']);
00764 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
00765 $framedata .= chr($source_data_array['picturetypeid']);
00766 $framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00767 $framedata .= $source_data_array['data'];
00768 }
00769 break;
00770
00771 case 'GEOB':
00772
00773
00774
00775
00776
00777
00778 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00779 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00780 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
00781 } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
00782 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
00783 } elseif (!$source_data_array['description']) {
00784 $this->errors[] = 'Missing Description in '.$frame_name;
00785 } else {
00786 $framedata .= chr($source_data_array['encodingid']);
00787 $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
00788 $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00789 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
00790 $framedata .= $source_data_array['data'];
00791 }
00792 break;
00793
00794 case 'PCNT':
00795
00796
00797
00798
00799 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
00800 break;
00801
00802 case 'POPM':
00803
00804
00805
00806
00807
00808
00809 if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
00810 $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
00811 } elseif (!IsValidEmail($source_data_array['email'])) {
00812 $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
00813 } else {
00814 $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
00815 $framedata .= chr($source_data_array['rating']);
00816 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
00817 }
00818 break;
00819
00820 case 'RBUF':
00821
00822
00823
00824
00825 if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
00826 $this->errors[] = 'Invalid Buffer Size in '.$frame_name;
00827 } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
00828 $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
00829 } else {
00830 $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
00831 $flag .= '0000000';
00832 $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
00833 $framedata .= chr(bindec($flag));
00834 $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
00835 }
00836 break;
00837
00838 case 'AENC':
00839
00840
00841
00842
00843
00844 if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
00845 $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
00846 } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
00847 $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
00848 } else {
00849 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
00850 $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
00851 $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
00852 $framedata .= $source_data_array['encryptioninfo'];
00853 }
00854 break;
00855
00856 case 'LINK':
00857
00858
00859
00860
00861 if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
00862 $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
00863 } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
00864
00865
00866 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
00867 } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
00868 $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00869 } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
00870 $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00871 } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
00872 $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00873 } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
00874 $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
00875 } else {
00876 $framedata .= $source_data_array['frameid'];
00877 $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
00878 switch ($source_data_array['frameid']) {
00879 case 'COMM':
00880 case 'SYLT':
00881 case 'USLT':
00882 case 'PRIV':
00883 case 'USER':
00884 case 'AENC':
00885 case 'APIC':
00886 case 'GEOB':
00887 case 'TXXX':
00888 $framedata .= $source_data_array['additionaldata'];
00889 break;
00890 case 'ASPI':
00891 case 'ETCO':
00892 case 'EQU2':
00893 case 'MCID':
00894 case 'MLLT':
00895 case 'OWNE':
00896 case 'RVA2':
00897 case 'RVRB':
00898 case 'SYTC':
00899 case 'IPLS':
00900 case 'RVAD':
00901 case 'EQUA':
00902
00903 break;
00904 case 'RBUF':
00905 if ($this->majorversion == 3) {
00906
00907 } else {
00908 $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
00909 }
00910
00911 default:
00912 if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
00913
00914 } else {
00915 $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
00916 }
00917 break;
00918 }
00919 }
00920 break;
00921
00922 case 'POSS':
00923
00924
00925
00926 if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
00927 $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
00928 } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
00929 $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
00930 } else {
00931 $framedata .= chr($source_data_array['timestampformat']);
00932 $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
00933 }
00934 break;
00935
00936 case 'USER':
00937
00938
00939
00940
00941 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00942 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00943 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00944 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
00945 $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
00946 } else {
00947 $framedata .= chr($source_data_array['encodingid']);
00948 $framedata .= strtolower($source_data_array['language']);
00949 $framedata .= $source_data_array['data'];
00950 }
00951 break;
00952
00953 case 'OWNE':
00954
00955
00956
00957
00958
00959 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00960 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00961 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00962 } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
00963 $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
00964 } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
00965 $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
00966 } else {
00967 $framedata .= chr($source_data_array['encodingid']);
00968 $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
00969 $framedata .= $source_data_array['purchasedate'];
00970 $framedata .= $source_data_array['seller'];
00971 }
00972 break;
00973
00974 case 'COMR':
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
00986 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
00987 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
00988 } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
00989 $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
00990 } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
00991 $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
00992 } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
00993 $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
00994 } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
00995 $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
00996 } else {
00997 $framedata .= chr($source_data_array['encodingid']);
00998 unset($pricestring);
00999 foreach ($source_data_array['price'] as $key => $val) {
01000 if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
01001 $pricestrings[] = $key.$val['value'];
01002 } else {
01003 $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
01004 }
01005 }
01006 $framedata .= implode('/', $pricestrings);
01007 $framedata .= $source_data_array['pricevaliduntil'];
01008 $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
01009 $framedata .= chr($source_data_array['receivedasid']);
01010 $framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
01011 $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
01012 $framedata .= $source_data_array['mime']."\x00";
01013 $framedata .= $source_data_array['logo'];
01014 }
01015 break;
01016
01017 case 'ENCR':
01018
01019
01020
01021
01022 if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
01023 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
01024 } else {
01025 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01026 $framedata .= ord($source_data_array['methodsymbol']);
01027 $framedata .= $source_data_array['data'];
01028 }
01029 break;
01030
01031 case 'GRID':
01032
01033
01034
01035
01036 if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
01037 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
01038 } else {
01039 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01040 $framedata .= ord($source_data_array['groupsymbol']);
01041 $framedata .= $source_data_array['data'];
01042 }
01043 break;
01044
01045 case 'PRIV':
01046
01047
01048
01049 $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
01050 $framedata .= $source_data_array['data'];
01051 break;
01052
01053 case 'SIGN':
01054
01055
01056
01057 if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
01058 $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
01059 } else {
01060 $framedata .= ord($source_data_array['groupsymbol']);
01061 $framedata .= $source_data_array['data'];
01062 }
01063 break;
01064
01065 case 'SEEK':
01066
01067
01068 if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
01069 $this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)';
01070 } else {
01071 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
01072 }
01073 break;
01074
01075 case 'ASPI':
01076
01077
01078
01079
01080
01081
01082
01083 if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
01084 $this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)';
01085 } elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
01086 $this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)';
01087 } elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
01088 $this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)';
01089 } elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
01090 $this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)';
01091 } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
01092 $this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name;
01093 } else {
01094 $framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
01095 $framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
01096 $framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
01097 $framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
01098 foreach ($source_data_array['indexes'] as $key => $val) {
01099 $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
01100 }
01101 }
01102 break;
01103
01104 case 'RGAD':
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115 if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
01116 $this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)';
01117 } elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
01118 $this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)';
01119 } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
01120 $this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)';
01121 } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
01122 $this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)';
01123 } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
01124 $this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)';
01125 } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
01126 $this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)';
01127 } else {
01128 $framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
01129 $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
01130 $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
01131 }
01132 break;
01133
01134 default:
01135 if ($frame_name{0} == 'T') {
01136
01137
01138
01139 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
01140 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
01141 $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
01142 } else {
01143 $framedata .= chr($source_data_array['encodingid']);
01144 $framedata .= $source_data_array['data'];
01145 }
01146 } elseif ($frame_name{0} == 'W') {
01147
01148
01149 if (!$this->IsValidURL($source_data_array['data'], false, false)) {
01150
01151
01152 $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
01153 } else {
01154 $framedata .= $source_data_array['data'];
01155 }
01156 } else {
01157 $this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()';
01158 }
01159 break;
01160 }
01161 }
01162 if (!empty($this->errors)) {
01163 return false;
01164 }
01165 return $framedata;
01166 }
01167
01168 function ID3v2FrameIsAllowed($frame_name, $source_data_array) {
01169 static $PreviousFrames = array();
01170
01171 if ($frame_name === null) {
01172
01173
01174 $PreviousFrames = array();
01175 return true;
01176 }
01177
01178 if ($this->majorversion == 4) {
01179 switch ($frame_name) {
01180 case 'UFID':
01181 case 'AENC':
01182 case 'ENCR':
01183 case 'GRID':
01184 if (!isset($source_data_array['ownerid'])) {
01185 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01186 } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01187 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01188 } else {
01189 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01190 }
01191 break;
01192
01193 case 'TXXX':
01194 case 'WXXX':
01195 case 'RVA2':
01196 case 'EQU2':
01197 case 'APIC':
01198 case 'GEOB':
01199 if (!isset($source_data_array['description'])) {
01200 $this->errors[] = '[description] not specified for '.$frame_name;
01201 } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01202 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01203 } else {
01204 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01205 }
01206 break;
01207
01208 case 'USER':
01209 if (!isset($source_data_array['language'])) {
01210 $this->errors[] = '[language] not specified for '.$frame_name;
01211 } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
01212 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
01213 } else {
01214 $PreviousFrames[] = $frame_name.$source_data_array['language'];
01215 }
01216 break;
01217
01218 case 'USLT':
01219 case 'SYLT':
01220 case 'COMM':
01221 if (!isset($source_data_array['language'])) {
01222 $this->errors[] = '[language] not specified for '.$frame_name;
01223 } elseif (!isset($source_data_array['description'])) {
01224 $this->errors[] = '[description] not specified for '.$frame_name;
01225 } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01226 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01227 } else {
01228 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01229 }
01230 break;
01231
01232 case 'POPM':
01233 if (!isset($source_data_array['email'])) {
01234 $this->errors[] = '[email] not specified for '.$frame_name;
01235 } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01236 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01237 } else {
01238 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01239 }
01240 break;
01241
01242 case 'IPLS':
01243 case 'MCDI':
01244 case 'ETCO':
01245 case 'MLLT':
01246 case 'SYTC':
01247 case 'RVRB':
01248 case 'PCNT':
01249 case 'RBUF':
01250 case 'POSS':
01251 case 'OWNE':
01252 case 'SEEK':
01253 case 'ASPI':
01254 case 'RGAD':
01255 if (in_array($frame_name, $PreviousFrames)) {
01256 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01257 } else {
01258 $PreviousFrames[] = $frame_name;
01259 }
01260 break;
01261
01262 case 'LINK':
01263
01264
01265 if (!isset($source_data_array['frameid'])) {
01266 $this->errors[] = '[frameid] not specified for '.$frame_name;
01267 } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01268 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01269 } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01270
01271 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01272 } else {
01273 $PreviousFrames[] = $frame_name.$source_data_array['frameid'];
01274 $PreviousFrames[] = $source_data_array['frameid'];
01275 }
01276 break;
01277
01278 case 'COMR':
01279
01280
01281 break;
01282
01283 case 'PRIV':
01284 case 'SIGN':
01285 if (!isset($source_data_array['ownerid'])) {
01286 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01287 } elseif (!isset($source_data_array['data'])) {
01288 $this->errors[] = '[data] not specified for '.$frame_name;
01289 } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
01290 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
01291 } else {
01292 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
01293 }
01294 break;
01295
01296 default:
01297 if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01298 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01299 }
01300 break;
01301 }
01302
01303 } elseif ($this->majorversion == 3) {
01304
01305 switch ($frame_name) {
01306 case 'UFID':
01307 case 'AENC':
01308 case 'ENCR':
01309 case 'GRID':
01310 if (!isset($source_data_array['ownerid'])) {
01311 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01312 } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01313 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01314 } else {
01315 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01316 }
01317 break;
01318
01319 case 'TXXX':
01320 case 'WXXX':
01321 case 'APIC':
01322 case 'GEOB':
01323 if (!isset($source_data_array['description'])) {
01324 $this->errors[] = '[description] not specified for '.$frame_name;
01325 } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01326 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01327 } else {
01328 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01329 }
01330 break;
01331
01332 case 'USER':
01333 if (!isset($source_data_array['language'])) {
01334 $this->errors[] = '[language] not specified for '.$frame_name;
01335 } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
01336 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
01337 } else {
01338 $PreviousFrames[] = $frame_name.$source_data_array['language'];
01339 }
01340 break;
01341
01342 case 'USLT':
01343 case 'SYLT':
01344 case 'COMM':
01345 if (!isset($source_data_array['language'])) {
01346 $this->errors[] = '[language] not specified for '.$frame_name;
01347 } elseif (!isset($source_data_array['description'])) {
01348 $this->errors[] = '[description] not specified for '.$frame_name;
01349 } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01350 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01351 } else {
01352 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01353 }
01354 break;
01355
01356 case 'POPM':
01357 if (!isset($source_data_array['email'])) {
01358 $this->errors[] = '[email] not specified for '.$frame_name;
01359 } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01360 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01361 } else {
01362 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01363 }
01364 break;
01365
01366 case 'IPLS':
01367 case 'MCDI':
01368 case 'ETCO':
01369 case 'MLLT':
01370 case 'SYTC':
01371 case 'RVAD':
01372 case 'EQUA':
01373 case 'RVRB':
01374 case 'PCNT':
01375 case 'RBUF':
01376 case 'POSS':
01377 case 'OWNE':
01378 case 'RGAD':
01379 if (in_array($frame_name, $PreviousFrames)) {
01380 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01381 } else {
01382 $PreviousFrames[] = $frame_name;
01383 }
01384 break;
01385
01386 case 'LINK':
01387
01388
01389 if (!isset($source_data_array['frameid'])) {
01390 $this->errors[] = '[frameid] not specified for '.$frame_name;
01391 } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01392 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01393 } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01394
01395 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01396 } else {
01397 $PreviousFrames[] = $frame_name.$source_data_array['frameid'];
01398 $PreviousFrames[] = $source_data_array['frameid'];
01399 }
01400 break;
01401
01402 case 'COMR':
01403
01404
01405 break;
01406
01407 case 'PRIV':
01408 if (!isset($source_data_array['ownerid'])) {
01409 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01410 } elseif (!isset($source_data_array['data'])) {
01411 $this->errors[] = '[data] not specified for '.$frame_name;
01412 } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
01413 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
01414 } else {
01415 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
01416 }
01417 break;
01418
01419 default:
01420 if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01421 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01422 }
01423 break;
01424 }
01425
01426 } elseif ($this->majorversion == 2) {
01427
01428 switch ($frame_name) {
01429 case 'UFI':
01430 case 'CRM':
01431 case 'CRA':
01432 if (!isset($source_data_array['ownerid'])) {
01433 $this->errors[] = '[ownerid] not specified for '.$frame_name;
01434 } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
01435 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
01436 } else {
01437 $PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
01438 }
01439 break;
01440
01441 case 'TXX':
01442 case 'WXX':
01443 case 'PIC':
01444 case 'GEO':
01445 if (!isset($source_data_array['description'])) {
01446 $this->errors[] = '[description] not specified for '.$frame_name;
01447 } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
01448 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
01449 } else {
01450 $PreviousFrames[] = $frame_name.$source_data_array['description'];
01451 }
01452 break;
01453
01454 case 'ULT':
01455 case 'SLT':
01456 case 'COM':
01457 if (!isset($source_data_array['language'])) {
01458 $this->errors[] = '[language] not specified for '.$frame_name;
01459 } elseif (!isset($source_data_array['description'])) {
01460 $this->errors[] = '[description] not specified for '.$frame_name;
01461 } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
01462 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
01463 } else {
01464 $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
01465 }
01466 break;
01467
01468 case 'POP':
01469 if (!isset($source_data_array['email'])) {
01470 $this->errors[] = '[email] not specified for '.$frame_name;
01471 } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
01472 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
01473 } else {
01474 $PreviousFrames[] = $frame_name.$source_data_array['email'];
01475 }
01476 break;
01477
01478 case 'IPL':
01479 case 'MCI':
01480 case 'ETC':
01481 case 'MLL':
01482 case 'STC':
01483 case 'RVA':
01484 case 'EQU':
01485 case 'REV':
01486 case 'CNT':
01487 case 'BUF':
01488 if (in_array($frame_name, $PreviousFrames)) {
01489 $this->errors[] = 'Only one '.$frame_name.' tag allowed';
01490 } else {
01491 $PreviousFrames[] = $frame_name;
01492 }
01493 break;
01494
01495 case 'LNK':
01496
01497
01498 if (!isset($source_data_array['frameid'])) {
01499 $this->errors[] = '[frameid] not specified for '.$frame_name;
01500 } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
01501 $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
01502 } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
01503
01504 $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
01505 } else {
01506 $PreviousFrames[] = $frame_name.$source_data_array['frameid'];
01507 $PreviousFrames[] = $source_data_array['frameid'];
01508 }
01509 break;
01510
01511 default:
01512 if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
01513 $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
01514 }
01515 break;
01516 }
01517 }
01518
01519 if (!empty($this->errors)) {
01520 return false;
01521 }
01522 return true;
01523 }
01524
01525 function GenerateID3v2Tag($noerrorsonly=true) {
01526 $this->ID3v2FrameIsAllowed(null, '');
01527
01528 $tagstring = '';
01529 if (is_array($this->tag_data)) {
01530 foreach ($this->tag_data as $frame_name => $frame_rawinputdata) {
01531 foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) {
01532 if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
01533 unset($frame_length);
01534 unset($frame_flags);
01535 $frame_data = false;
01536 if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) {
01537 if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) {
01538 $FrameUnsynchronisation = false;
01539 if ($this->majorversion >= 4) {
01540
01541 $unsynchdata = $frame_data;
01542 if ($this->id3v2_use_unsynchronisation) {
01543 $unsynchdata = $this->Unsynchronise($frame_data);
01544 }
01545 if (strlen($unsynchdata) != strlen($frame_data)) {
01546
01547 $FrameUnsynchronisation = true;
01548 $frame_data = $unsynchdata;
01549 if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) {
01550
01551 } else {
01552 $TagUnsynchronisation = true;
01553 }
01554 } else {
01555 if (isset($TagUnsynchronisation)) {
01556 $TagUnsynchronisation = false;
01557 }
01558 }
01559 unset($unsynchdata);
01560
01561 $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true);
01562 } else {
01563 $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false);
01564 }
01565 $frame_flags = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false);
01566 }
01567 } else {
01568 $this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed';
01569 }
01570 if ($frame_data === false) {
01571 $this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"';
01572 if ($noerrorsonly) {
01573 return false;
01574 } else {
01575 unset($frame_name);
01576 }
01577 }
01578 } else {
01579
01580 $this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"';
01581 unset($frame_name);
01582 unset($frame_length);
01583 unset($frame_flags);
01584 unset($frame_data);
01585 }
01586 if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
01587 $tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data;
01588 }
01589 }
01590 }
01591
01592 if (!isset($TagUnsynchronisation)) {
01593 $TagUnsynchronisation = false;
01594 }
01595 if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) {
01596
01597 $unsynchdata = $this->Unsynchronise($tagstring);
01598 if (strlen($unsynchdata) != strlen($tagstring)) {
01599
01600 $TagUnsynchronisation = true;
01601 $tagstring = $unsynchdata;
01602 }
01603 }
01604
01605 while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) {
01606 $this->paddedlength += 1024;
01607 }
01608
01609 $footer = false;
01610 if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
01611
01612
01613 $tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
01614 }
01615 if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) {
01616
01617
01618 $TagUnsynchronisation = true;
01619 $tagstring .= "\x00";
01620 }
01621
01622 $tagheader = 'ID3';
01623 $tagheader .= chr($this->majorversion);
01624 $tagheader .= chr($this->minorversion);
01625 $tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation));
01626 $tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true);
01627
01628 return $tagheader.$tagstring;
01629 }
01630 $this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()';
01631 return false;
01632 }
01633
01634 function ID3v2IsValidPriceString($pricestring) {
01635 if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') {
01636 return false;
01637 } elseif (!$this->IsANumber(substr($pricestring, 3), true)) {
01638 return false;
01639 }
01640 return true;
01641 }
01642
01643 function ID3v2FrameFlagsLookupTagAlter($framename) {
01644
01645 switch ($framename) {
01646 case 'RGAD':
01647 $allow = true;
01648 default:
01649 $allow = false;
01650 break;
01651 }
01652 return $allow;
01653 }
01654
01655 function ID3v2FrameFlagsLookupFileAlter($framename) {
01656
01657 switch ($framename) {
01658 case 'RGAD':
01659 return false;
01660 break;
01661
01662 default:
01663 return false;
01664 break;
01665 }
01666 }
01667
01668 function ID3v2IsValidETCOevent($eventid) {
01669 if (($eventid < 0) || ($eventid > 0xFF)) {
01670
01671 return false;
01672 } elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) {
01673
01674 return false;
01675 } elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) {
01676
01677 return false;
01678 } elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) {
01679
01680 return false;
01681 } elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) {
01682
01683 return false;
01684 }
01685 return true;
01686 }
01687
01688 function ID3v2IsValidSYLTtype($contenttype) {
01689 if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) {
01690 return true;
01691 } elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) {
01692 return true;
01693 }
01694 return false;
01695 }
01696
01697 function ID3v2IsValidRVA2channeltype($channeltype) {
01698 if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) {
01699 return true;
01700 }
01701 return false;
01702 }
01703
01704 function ID3v2IsValidAPICpicturetype($picturetype) {
01705 if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) {
01706 return true;
01707 }
01708 return false;
01709 }
01710
01711 function ID3v2IsValidAPICimageformat($imageformat) {
01712 if ($imageformat == '-->') {
01713 return true;
01714 } elseif ($this->majorversion == 2) {
01715 if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) {
01716 return true;
01717 }
01718 } elseif (($this->majorversion == 3) || ($this->majorversion == 4)) {
01719 if ($this->IsValidMIMEstring($imageformat)) {
01720 return true;
01721 }
01722 }
01723 return false;
01724 }
01725
01726 function ID3v2IsValidCOMRreceivedAs($receivedas) {
01727 if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) {
01728 return true;
01729 }
01730 return false;
01731 }
01732
01733 function ID3v2IsValidRGADname($RGADname) {
01734 if (($RGADname >= 0) && ($RGADname <= 2)) {
01735 return true;
01736 }
01737 return false;
01738 }
01739
01740 function ID3v2IsValidRGADoriginator($RGADoriginator) {
01741 if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
01742 return true;
01743 }
01744 return false;
01745 }
01746
01747 function ID3v2IsValidTextEncoding($textencodingbyte) {
01748 static $ID3v2IsValidTextEncoding_cache = array(
01749 2 => array(true, true),
01750 3 => array(true, true),
01751 4 => array(true, true, true, true));
01752 return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
01753 }
01754
01755 function Unsynchronise($data) {
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768 $data = str_replace("\xFF\x00", "\xFF\x00\x00", $data);
01769 $unsyncheddata = '';
01770 $datalength = strlen($data);
01771 for ($i = 0; $i < $datalength; $i++) {
01772 $thischar = $data{$i};
01773 $unsyncheddata .= $thischar;
01774 if ($thischar == "\xFF") {
01775 $nextchar = ord($data{$i + 1});
01776 if (($nextchar & 0xE0) == 0xE0) {
01777
01778 $unsyncheddata .= "\x00";
01779 }
01780 }
01781 }
01782 return $unsyncheddata;
01783 }
01784
01785 function is_hash($var) {
01786
01787
01788 if (is_array($var)) {
01789 $keys = array_keys($var);
01790 $all_num = true;
01791 for ($i = 0; $i < count($keys); $i++) {
01792 if (is_string($keys[$i])) {
01793 return true;
01794 }
01795 }
01796 }
01797 return false;
01798 }
01799
01800 function array_join_merge($arr1, $arr2) {
01801
01802
01803 if (is_array($arr1) && is_array($arr2)) {
01804
01805 $new_array = array();
01806
01807 if ($this->is_hash($arr1) && $this->is_hash($arr2)) {
01808
01809 $keys = array_merge(array_keys($arr1), array_keys($arr2));
01810 foreach ($keys as $key) {
01811 $new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]);
01812 }
01813 } else {
01814
01815 $new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2))));
01816 }
01817 return $new_array;
01818 } else {
01819
01820 return $arr2 ? $arr2 : $arr1;
01821 }
01822 }
01823
01824 function IsValidMIMEstring($mimestring) {
01825 if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
01826 return true;
01827 }
01828 return false;
01829 }
01830
01831 function IsWithinBitRange($number, $maxbits, $signed=false) {
01832 if ($signed) {
01833 if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
01834 return true;
01835 }
01836 } else {
01837 if (($number >= 0) && ($number <= pow(2, $maxbits))) {
01838 return true;
01839 }
01840 }
01841 return false;
01842 }
01843
01844 function safe_parse_url($url) {
01845 $parts = @parse_url($url);
01846 $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
01847 $parts['host'] = (isset($parts['host']) ? $parts['host'] : '');
01848 $parts['user'] = (isset($parts['user']) ? $parts['user'] : '');
01849 $parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : '');
01850 $parts['path'] = (isset($parts['path']) ? $parts['path'] : '');
01851 $parts['query'] = (isset($parts['query']) ? $parts['query'] : '');
01852 return $parts;
01853 }
01854
01855 function IsValidURL($url, $allowUserPass=false) {
01856 if ($url == '') {
01857 return false;
01858 }
01859 if ($allowUserPass !== true) {
01860 if (strstr($url, '@')) {
01861
01862
01863 return false;
01864 }
01865 }
01866 if ($parts = $this->safe_parse_url($url)) {
01867 if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
01868 return false;
01869 } elseif (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
01870 return false;
01871 } elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
01872 return false;
01873 } elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
01874 return false;
01875 } elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
01876 return false;
01877 } elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
01878 return false;
01879 } else {
01880 return true;
01881 }
01882 }
01883 return false;
01884 }
01885
01886 function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
01887 $long_description = str_replace(' ', '_', strtolower(trim($long_description)));
01888 static $ID3v2ShortFrameNameLookup = array();
01889 if (empty($ID3v2ShortFrameNameLookup)) {
01890
01891
01892 $ID3v2ShortFrameNameLookup[2]['comment'] = 'COM';
01893 $ID3v2ShortFrameNameLookup[2]['album'] = 'TAL';
01894 $ID3v2ShortFrameNameLookup[2]['beats_per_minute'] = 'TBP';
01895 $ID3v2ShortFrameNameLookup[2]['composer'] = 'TCM';
01896 $ID3v2ShortFrameNameLookup[2]['genre'] = 'TCO';
01897 $ID3v2ShortFrameNameLookup[2]['copyright'] = 'TCR';
01898 $ID3v2ShortFrameNameLookup[2]['encoded_by'] = 'TEN';
01899 $ID3v2ShortFrameNameLookup[2]['language'] = 'TLA';
01900 $ID3v2ShortFrameNameLookup[2]['length'] = 'TLE';
01901 $ID3v2ShortFrameNameLookup[2]['original_artist'] = 'TOA';
01902 $ID3v2ShortFrameNameLookup[2]['original_filename'] = 'TOF';
01903 $ID3v2ShortFrameNameLookup[2]['original_lyricist'] = 'TOL';
01904 $ID3v2ShortFrameNameLookup[2]['original_album_title'] = 'TOT';
01905 $ID3v2ShortFrameNameLookup[2]['artist'] = 'TP1';
01906 $ID3v2ShortFrameNameLookup[2]['band'] = 'TP2';
01907 $ID3v2ShortFrameNameLookup[2]['conductor'] = 'TP3';
01908 $ID3v2ShortFrameNameLookup[2]['remixer'] = 'TP4';
01909 $ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB';
01910 $ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC';
01911 $ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK';
01912 $ID3v2ShortFrameNameLookup[2]['size'] = 'TSI';
01913 $ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS';
01914 $ID3v2ShortFrameNameLookup[2]['description'] = 'TT1';
01915 $ID3v2ShortFrameNameLookup[2]['title'] = 'TT2';
01916 $ID3v2ShortFrameNameLookup[2]['subtitle'] = 'TT3';
01917 $ID3v2ShortFrameNameLookup[2]['lyricist'] = 'TXT';
01918 $ID3v2ShortFrameNameLookup[2]['user_text'] = 'TXX';
01919 $ID3v2ShortFrameNameLookup[2]['year'] = 'TYE';
01920 $ID3v2ShortFrameNameLookup[2]['unique_file_identifier'] = 'UFI';
01921 $ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics'] = 'ULT';
01922 $ID3v2ShortFrameNameLookup[2]['url_file'] = 'WAF';
01923 $ID3v2ShortFrameNameLookup[2]['url_artist'] = 'WAR';
01924 $ID3v2ShortFrameNameLookup[2]['url_source'] = 'WAS';
01925 $ID3v2ShortFrameNameLookup[2]['copyright_information'] = 'WCP';
01926 $ID3v2ShortFrameNameLookup[2]['url_publisher'] = 'WPB';
01927 $ID3v2ShortFrameNameLookup[2]['url_user'] = 'WXX';
01928
01929
01930 $ID3v2ShortFrameNameLookup[3]['audio_encryption'] = 'AENC';
01931 $ID3v2ShortFrameNameLookup[3]['attached_picture'] = 'APIC';
01932 $ID3v2ShortFrameNameLookup[3]['comment'] = 'COMM';
01933 $ID3v2ShortFrameNameLookup[3]['commercial'] = 'COMR';
01934 $ID3v2ShortFrameNameLookup[3]['encryption_method_registration'] = 'ENCR';
01935 $ID3v2ShortFrameNameLookup[3]['event_timing_codes'] = 'ETCO';
01936 $ID3v2ShortFrameNameLookup[3]['general_encapsulated_object'] = 'GEOB';
01937 $ID3v2ShortFrameNameLookup[3]['group_identification_registration'] = 'GRID';
01938 $ID3v2ShortFrameNameLookup[3]['linked_information'] = 'LINK';
01939 $ID3v2ShortFrameNameLookup[3]['music_cd_identifier'] = 'MCDI';
01940 $ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table'] = 'MLLT';
01941 $ID3v2ShortFrameNameLookup[3]['ownership'] = 'OWNE';
01942 $ID3v2ShortFrameNameLookup[3]['play_counter'] = 'PCNT';
01943 $ID3v2ShortFrameNameLookup[3]['popularimeter'] = 'POPM';
01944 $ID3v2ShortFrameNameLookup[3]['position_synchronisation'] = 'POSS';
01945 $ID3v2ShortFrameNameLookup[3]['private'] = 'PRIV';
01946 $ID3v2ShortFrameNameLookup[3]['recommended_buffer_size'] = 'RBUF';
01947 $ID3v2ShortFrameNameLookup[3]['reverb'] = 'RVRB';
01948 $ID3v2ShortFrameNameLookup[3]['synchronised_lyrics'] = 'SYLT';
01949 $ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes'] = 'SYTC';
01950 $ID3v2ShortFrameNameLookup[3]['album'] = 'TALB';
01951 $ID3v2ShortFrameNameLookup[3]['beats_per_minute'] = 'TBPM';
01952 $ID3v2ShortFrameNameLookup[3]['composer'] = 'TCOM';
01953 $ID3v2ShortFrameNameLookup[3]['genre'] = 'TCON';
01954 $ID3v2ShortFrameNameLookup[3]['copyright'] = 'TCOP';
01955 $ID3v2ShortFrameNameLookup[3]['playlist_delay'] = 'TDLY';
01956 $ID3v2ShortFrameNameLookup[3]['encoded_by'] = 'TENC';
01957 $ID3v2ShortFrameNameLookup[3]['lyricist'] = 'TEXT';
01958 $ID3v2ShortFrameNameLookup[3]['file_type'] = 'TFLT';
01959 $ID3v2ShortFrameNameLookup[3]['content_group_description'] = 'TIT1';
01960 $ID3v2ShortFrameNameLookup[3]['title'] = 'TIT2';
01961 $ID3v2ShortFrameNameLookup[3]['subtitle'] = 'TIT3';
01962 $ID3v2ShortFrameNameLookup[3]['initial_key'] = 'TKEY';
01963 $ID3v2ShortFrameNameLookup[3]['language'] = 'TLAN';
01964 $ID3v2ShortFrameNameLookup[3]['length'] = 'TLEN';
01965 $ID3v2ShortFrameNameLookup[3]['media_type'] = 'TMED';
01966 $ID3v2ShortFrameNameLookup[3]['original_album_title'] = 'TOAL';
01967 $ID3v2ShortFrameNameLookup[3]['original_filename'] = 'TOFN';
01968 $ID3v2ShortFrameNameLookup[3]['original_lyricist'] = 'TOLY';
01969 $ID3v2ShortFrameNameLookup[3]['original_artist'] = 'TOPE';
01970 $ID3v2ShortFrameNameLookup[3]['file_owner'] = 'TOWN';
01971 $ID3v2ShortFrameNameLookup[3]['artist'] = 'TPE1';
01972 $ID3v2ShortFrameNameLookup[3]['band'] = 'TPE2';
01973 $ID3v2ShortFrameNameLookup[3]['conductor'] = 'TPE3';
01974 $ID3v2ShortFrameNameLookup[3]['remixer'] = 'TPE4';
01975 $ID3v2ShortFrameNameLookup[3]['part_of_set'] = 'TPOS';
01976 $ID3v2ShortFrameNameLookup[3]['publisher'] = 'TPUB';
01977 $ID3v2ShortFrameNameLookup[3]['tracknumber'] = 'TRCK';
01978 $ID3v2ShortFrameNameLookup[3]['internet_radio_station_name'] = 'TRSN';
01979 $ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner'] = 'TRSO';
01980 $ID3v2ShortFrameNameLookup[3]['isrc'] = 'TSRC';
01981 $ID3v2ShortFrameNameLookup[3]['encoder_settings'] = 'TSSE';
01982 $ID3v2ShortFrameNameLookup[3]['user_text'] = 'TXXX';
01983 $ID3v2ShortFrameNameLookup[3]['unique_file_identifier'] = 'UFID';
01984 $ID3v2ShortFrameNameLookup[3]['terms_of_use'] = 'USER';
01985 $ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics'] = 'USLT';
01986 $ID3v2ShortFrameNameLookup[3]['commercial'] = 'WCOM';
01987 $ID3v2ShortFrameNameLookup[3]['copyright_information'] = 'WCOP';
01988 $ID3v2ShortFrameNameLookup[3]['url_file'] = 'WOAF';
01989 $ID3v2ShortFrameNameLookup[3]['url_artist'] = 'WOAR';
01990 $ID3v2ShortFrameNameLookup[3]['url_source'] = 'WOAS';
01991 $ID3v2ShortFrameNameLookup[3]['url_station'] = 'WORS';
01992 $ID3v2ShortFrameNameLookup[3]['payment'] = 'WPAY';
01993 $ID3v2ShortFrameNameLookup[3]['url_publisher'] = 'WPUB';
01994 $ID3v2ShortFrameNameLookup[3]['url_user'] = 'WXXX';
01995
01996
01997
01998 $ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3];
01999
02000
02001 $ID3v2ShortFrameNameLookup[3]['equalisation'] = 'EQUA';
02002 $ID3v2ShortFrameNameLookup[3]['involved_people_list'] = 'IPLS';
02003 $ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment'] = 'RVAD';
02004 $ID3v2ShortFrameNameLookup[3]['date'] = 'TDAT';
02005 $ID3v2ShortFrameNameLookup[3]['time'] = 'TIME';
02006 $ID3v2ShortFrameNameLookup[3]['original_release_year'] = 'TORY';
02007 $ID3v2ShortFrameNameLookup[3]['recording_dates'] = 'TRDA';
02008 $ID3v2ShortFrameNameLookup[3]['size'] = 'TSIZ';
02009 $ID3v2ShortFrameNameLookup[3]['year'] = 'TYER';
02010
02011
02012
02013 $ID3v2ShortFrameNameLookup[4]['audio_seek_point_index'] = 'ASPI';
02014 $ID3v2ShortFrameNameLookup[4]['equalisation'] = 'EQU2';
02015 $ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment'] = 'RVA2';
02016 $ID3v2ShortFrameNameLookup[4]['seek'] = 'SEEK';
02017 $ID3v2ShortFrameNameLookup[4]['signature'] = 'SIGN';
02018 $ID3v2ShortFrameNameLookup[4]['encoding_time'] = 'TDEN';
02019 $ID3v2ShortFrameNameLookup[4]['original_release_time'] = 'TDOR';
02020 $ID3v2ShortFrameNameLookup[4]['recording_time'] = 'TDRC';
02021 $ID3v2ShortFrameNameLookup[4]['release_time'] = 'TDRL';
02022 $ID3v2ShortFrameNameLookup[4]['tagging_time'] = 'TDTG';
02023 $ID3v2ShortFrameNameLookup[4]['involved_people_list'] = 'TIPL';
02024 $ID3v2ShortFrameNameLookup[4]['musician_credits_list'] = 'TMCL';
02025 $ID3v2ShortFrameNameLookup[4]['mood'] = 'TMOO';
02026 $ID3v2ShortFrameNameLookup[4]['produced_notice'] = 'TPRO';
02027 $ID3v2ShortFrameNameLookup[4]['album_sort_order'] = 'TSOA';
02028 $ID3v2ShortFrameNameLookup[4]['performer_sort_order'] = 'TSOP';
02029 $ID3v2ShortFrameNameLookup[4]['title_sort_order'] = 'TSOT';
02030 $ID3v2ShortFrameNameLookup[4]['set_subtitle'] = 'TSST';
02031 }
02032 return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)];
02033
02034 }
02035
02036 }
02037
02038 ?>