1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::System::XML; 10## nofilter(TidyAll::Plugin::OTRS::Perl::Require) 11 12use strict; 13use warnings; 14 15use Digest::MD5; 16 17our @ObjectDependencies = ( 18 'Kernel::System::Cache', 19 'Kernel::System::DB', 20 'Kernel::System::Encode', 21 'Kernel::System::Log', 22); 23 24=head1 NAME 25 26Kernel::System::XML - xml lib 27 28=head1 DESCRIPTION 29 30All xml related functions. 31 32=head1 PUBLIC INTERFACE 33 34=head2 new() 35 36Don't use the constructor directly, use the ObjectManager instead: 37 38 my $XMLObject = $Kernel::OM->Get('Kernel::System::XML'); 39 40=cut 41 42sub new { 43 my ( $Type, %Param ) = @_; 44 45 # allocate new hash for object 46 my $Self = {}; 47 bless( $Self, $Type ); 48 49 return $Self; 50} 51 52=head2 XMLHashAdd() 53 54add an XMLHash to storage 55 56 my $Key = $XMLObject->XMLHashAdd( 57 Type => 'SomeType', 58 Key => '123', 59 XMLHash => \@XMLHash, 60 ); 61 62 my $AutoKey = $XMLObject->XMLHashAdd( 63 Type => 'SomeType', 64 KeyAutoIncrement => 1, 65 XMLHash => \@XMLHash, 66 ); 67 68=cut 69 70sub XMLHashAdd { 71 my ( $Self, %Param ) = @_; 72 73 # check needed stuff 74 for (qw(Type XMLHash)) { 75 if ( !$Param{$_} ) { 76 $Kernel::OM->Get('Kernel::System::Log')->Log( 77 Priority => 'error', 78 Message => "Need $_!" 79 ); 80 return; 81 } 82 } 83 84 if ( !$Param{Key} && !$Param{KeyAutoIncrement} ) { 85 $Kernel::OM->Get('Kernel::System::Log')->Log( 86 Priority => 'error', 87 Message => 'Need Key or KeyAutoIncrement param!', 88 ); 89 return; 90 } 91 92 my %ValueHASH = $Self->XMLHash2D( XMLHash => $Param{XMLHash} ); 93 if (%ValueHASH) { 94 if ( !$Param{Key} ) { 95 $Param{Key} = $Self->_XMLHashAddAutoIncrement(%Param); 96 } 97 if ( !$Param{Key} ) { 98 return; 99 } 100 $Self->XMLHashDelete(%Param); 101 102 # get database object 103 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 104 105 # create rand number 106 my $Rand = int( rand(1000000) ); 107 my $TmpKey = "TMP-$Rand-$Param{Type}"; 108 for my $Key ( sort keys %ValueHASH ) { 109 $DBObject->Do( 110 SQL => 111 'INSERT INTO xml_storage (xml_type, xml_key, xml_content_key, xml_content_value) VALUES (?, ?, ?, ?)', 112 Bind => [ \$TmpKey, \$Param{Key}, \$Key, \$ValueHASH{$Key}, ], 113 ); 114 } 115 116 # move new hash if insert is complete 117 $Self->XMLHashMove( 118 OldType => $TmpKey, 119 OldKey => $Param{Key}, 120 NewType => $Param{Type}, 121 NewKey => $Param{Key}, 122 ); 123 return $Param{Key}; 124 } 125 126 $Kernel::OM->Get('Kernel::System::Log')->Log( 127 Priority => 'error', 128 Message => 'Got no %ValueHASH from XMLHash2D()', 129 ); 130 131 return; 132} 133 134=head2 XMLHashUpdate() 135 136update an XMLHash to storage 137 138 $XMLHash[1]->{Name}->[1]->{Content} = 'Some Name'; 139 140 $XMLObject->XMLHashUpdate( 141 Type => 'SomeType', 142 Key => '123', 143 XMLHash => \@XMLHash, 144 ); 145 146=cut 147 148sub XMLHashUpdate { 149 my ( $Self, %Param ) = @_; 150 151 # check needed stuff 152 for (qw(Type Key XMLHash)) { 153 if ( !$Param{$_} ) { 154 $Kernel::OM->Get('Kernel::System::Log')->Log( 155 Priority => 'error', 156 Message => "Need $_!" 157 ); 158 return; 159 } 160 } 161 162 return $Self->XMLHashAdd(%Param); 163} 164 165=head2 XMLHashGet() 166 167get an XMLHash from the database 168 169 my @XMLHash = $XMLObject->XMLHashGet( 170 Type => 'SomeType', 171 Key => '123', 172 ); 173 174 my @XMLHash = $XMLObject->XMLHashGet( 175 Type => 'SomeType', 176 Key => '123', 177 Cache => 0, # (optional) do not use cached data 178 ); 179 180=cut 181 182sub XMLHashGet { 183 my ( $Self, %Param ) = @_; 184 185 my $Content = ''; 186 my @XMLHash; 187 188 # check needed stuff 189 for (qw(Type Key)) { 190 if ( !$Param{$_} ) { 191 $Kernel::OM->Get('Kernel::System::Log')->Log( 192 Priority => 'error', 193 Message => "Need $_!" 194 ); 195 return; 196 } 197 } 198 if ( !defined $Param{Cache} ) { 199 $Param{Cache} = 1; 200 } 201 202 # get cache object 203 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 204 205 # check cache 206 if ( $Param{Cache} ) { 207 my $Cache = $CacheObject->Get( 208 Type => 'XML', 209 Key => "$Param{Type}-$Param{Key}", 210 211 # Don't store complex structure in memory as it will be modified later. 212 CacheInMemory => 0, 213 ); 214 return @{$Cache} if $Cache; 215 } 216 217 # get database object 218 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 219 220 # sql 221 return if !$DBObject->Prepare( 222 SQL => 'SELECT xml_content_key, xml_content_value ' 223 . ' FROM xml_storage WHERE xml_type = ? AND xml_key = ?', 224 Bind => [ \$Param{Type}, \$Param{Key} ], 225 226 ); 227 while ( my @Data = $DBObject->FetchrowArray() ) { 228 if ( defined $Data[1] ) { 229 $Data[1] =~ s/\\/\\\\/g; 230 $Data[1] =~ s/'/\\'/g; 231 } 232 else { 233 $Data[1] = ''; 234 } 235 $Content .= '$XMLHash' . $Data[0] . " = '$Data[1]';\n 1;\n"; 236 } 237 if ( $Content && !eval $Content ) { ## no critic 238 print STDERR "ERROR: XML.pm $@\n"; 239 } 240 241 # set cache 242 if ( $Param{Cache} && $Content ) { 243 $CacheObject->Set( 244 Type => 'XML', 245 Key => "$Param{Type}-$Param{Key}", 246 Value => \@XMLHash, 247 TTL => 24 * 60 * 60, 248 249 # Don't store complex structure in memory as it will be modified later. 250 CacheInMemory => 0, 251 ); 252 } 253 254 return @XMLHash; 255} 256 257=head2 XMLHashDelete() 258 259delete an XMLHash from the database 260 261 $XMLObject->XMLHashDelete( 262 Type => 'SomeType', 263 Key => '123', 264 ); 265 266=cut 267 268sub XMLHashDelete { 269 my ( $Self, %Param ) = @_; 270 271 # check needed stuff 272 for (qw(Type Key)) { 273 if ( !defined $Param{$_} ) { 274 $Kernel::OM->Get('Kernel::System::Log')->Log( 275 Priority => 'error', 276 Message => "Need $_!" 277 ); 278 return; 279 } 280 } 281 282 # remove cache 283 $Kernel::OM->Get('Kernel::System::Cache')->Delete( 284 Type => 'XML', 285 Key => "$Param{Type}-$Param{Key}", 286 ); 287 288 # get database object 289 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 290 291 return if !$DBObject->Do( 292 SQL => 'DELETE FROM xml_storage WHERE xml_type = ? AND xml_key = ?', 293 Bind => [ \$Param{Type}, \$Param{Key} ], 294 ); 295 296 return 1; 297} 298 299=head2 XMLHashMove() 300 301move an XMLHash from one type or/and key to another 302 303 $XMLObject->XMLHashMove( 304 OldType => 'SomeType', 305 OldKey => '123', 306 NewType => 'NewType', 307 NewKey => '321', 308 ); 309 310=cut 311 312sub XMLHashMove { 313 my ( $Self, %Param ) = @_; 314 315 # check needed stuff 316 for (qw(OldType OldKey NewType NewKey)) { 317 if ( !$Param{$_} ) { 318 $Kernel::OM->Get('Kernel::System::Log')->Log( 319 Priority => 'error', 320 Message => "Need $_!" 321 ); 322 return; 323 } 324 } 325 326 # get cache object 327 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 328 329 # remove cache 330 $CacheObject->Delete( 331 Type => 'XML', 332 Key => "$Param{OldType}-$Param{OldKey}", 333 ); 334 $CacheObject->Delete( 335 Type => 'XML', 336 Key => "$Param{NewType}-$Param{NewKey}", 337 ); 338 339 # get database object 340 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 341 342 # delete existing xml hash 343 $DBObject->Do( 344 SQL => 'DELETE FROM xml_storage WHERE xml_type = ? AND xml_key = ?', 345 Bind => [ \$Param{NewType}, \$Param{NewKey} ], 346 ); 347 348 # update xml hash 349 return if !$DBObject->Do( 350 SQL => 'UPDATE xml_storage SET xml_type = ?, xml_key = ? ' 351 . 'WHERE xml_type = ? AND xml_key = ?', 352 Bind => [ \$Param{NewType}, \$Param{NewKey}, \$Param{OldType}, \$Param{OldKey} ], 353 ); 354 355 return 1; 356} 357 358=head2 XMLHashSearch() 359 360Search an XMLHash in the database. 361 362 my @Keys = $XMLObject->XMLHashSearch( 363 Type => 'SomeType', 364 What => [ 365 # each array element is a and condition 366 { 367 # or condition in hash 368 "[%]{'ElementA'}[%]{'ElementB'}[%]{'Content'}" => '%contentA%', 369 "[%]{'ElementA'}[%]{'ElementC'}[%]{'Content'}" => '%contentA%', 370 }, 371 { 372 "[%]{'ElementA'}[%]{'ElementB'}[%]{'Content'}" => '%contentB%', 373 "[%]{'ElementA'}[%]{'ElementC'}[%]{'Content'}" => '%contentB%', 374 }, 375 { 376 # use array reference if different content with same key was searched 377 "[%]{'ElementA'}[%]{'ElementB'}[%]{'Content'}" => ['%contentC%', '%contentD%', '%contentE%'], 378 "[%]{'ElementA'}[%]{'ElementC'}[%]{'Content'}" => ['%contentC%', '%contentD%', '%contentE%'], 379 }, 380 ], 381 ); 382 383=cut 384 385sub XMLHashSearch { 386 my ( $Self, %Param ) = @_; 387 388 # check needed stuff 389 if ( !$Param{Type} ) { 390 $Kernel::OM->Get('Kernel::System::Log')->Log( 391 Priority => 'error', 392 Message => 'Need Type!' 393 ); 394 return; 395 } 396 397 # get database object 398 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 399 400 return if !$DBObject->Prepare( 401 SQL => 'SELECT DISTINCT(xml_key) FROM xml_storage WHERE xml_type = ?', 402 Bind => [ \$Param{Type} ], 403 ); 404 405 # the keys of this hash will be returned 406 my %Hash; 407 408 # initially all keys with the correct type are possible 409 while ( my @Data = $DBObject->FetchrowArray() ) { 410 $Hash{ $Data[0] } = 1; 411 } 412 413 if ( $Param{What} && ref $Param{What} eq 'ARRAY' ) { 414 415 # get like escape string needed for some databases (e.g. oracle) 416 my $LikeEscapeString = $DBObject->GetDatabaseFunction('LikeEscapeString'); 417 418 # the array elements are 'and' combined 419 for my $And ( @{ $Param{What} } ) { 420 421 # the key/value pairs are 'or' combined 422 my @OrConditions; 423 for my $Key ( sort keys %{$And} ) { 424 my $Value = $DBObject->Quote( $And->{$Key} ); 425 $Key = $DBObject->Quote( $Key, 'Like' ); 426 if ( $Value && ref $Value eq 'ARRAY' ) { 427 428 # when an array of possible values is given, 429 # we use 'LIKE'-conditions and combine them with 'OR' 430 for my $Element ( @{$Value} ) { 431 $Element = $DBObject->Quote( $Element, 'Like' ); 432 push @OrConditions, 433 " (xml_content_key LIKE '$Key' $LikeEscapeString " 434 . "AND xml_content_value LIKE '$Element' $LikeEscapeString)"; 435 } 436 } 437 else { 438 439 # when a single possible value is given, 440 # we use a 'LIKE'-condition 441 $Value = $DBObject->Quote( $Value, 'Like' ); 442 push @OrConditions, 443 " (xml_content_key LIKE '$Key' $LikeEscapeString " 444 . "AND xml_content_value LIKE '$Value' $LikeEscapeString )"; 445 } 446 } 447 448 # assemble the SQL 449 my $SQL = 'SELECT DISTINCT(xml_key) FROM xml_storage WHERE xml_type = ? '; 450 if (@OrConditions) { 451 $SQL .= 'AND ( ' . join( ' OR ', @OrConditions ) . ' ) '; 452 } 453 454 # execute 455 $DBObject->Prepare( 456 SQL => $SQL, 457 Bind => [ \$Param{Type} ], 458 ); 459 460 # intersection between the current key set, and the keys from the last 'SELECT' 461 # only the keys which are in all results survive 462 my %HashNew; 463 while ( my @Data = $DBObject->FetchrowArray() ) { 464 if ( $Hash{ $Data[0] } ) { 465 $HashNew{ $Data[0] } = 1; 466 } 467 } 468 %Hash = %HashNew; 469 } 470 } 471 472 my @Keys = keys %Hash; 473 474 return @Keys; 475} 476 477=head2 XMLHashList() 478 479generate a list of XMLHashes in the database 480 481 my @Keys = $XMLObject->XMLHashList( 482 Type => 'SomeType', 483 ); 484 485=cut 486 487sub XMLHashList { 488 my ( $Self, %Param ) = @_; 489 490 # check needed stuff 491 if ( !$Param{Type} ) { 492 $Kernel::OM->Get('Kernel::System::Log')->Log( 493 Priority => 'error', 494 Message => 'Need Type!' 495 ); 496 return; 497 } 498 499 # get database object 500 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 501 502 return if !$DBObject->Prepare( 503 SQL => 'SELECT distinct(xml_key) FROM xml_storage WHERE xml_type = ?', 504 Bind => [ \$Param{Type} ], 505 ); 506 507 my @Keys; 508 while ( my @Data = $DBObject->FetchrowArray() ) { 509 push @Keys, $Data[0]; 510 } 511 512 return @Keys; 513} 514 515=head2 XMLHash2XML() 516 517generate an XML string from an XMLHash 518 519 my $XMLString = $XMLObject->XMLHash2XML(@XMLHash); 520 521=cut 522 523sub XMLHash2XML { 524 my ( $Self, @XMLHash ) = @_; 525 526 my $Output = '<?xml version="1.0" encoding="utf-8"?>' . "\n"; 527 528 $Self->{XMLHash2XMLLayer} = 0; 529 for my $Key (@XMLHash) { 530 $Output .= $Self->_ElementBuild( %{$Key} ); 531 } 532 533 return $Output; 534} 535 536=head2 XMLParse2XMLHash() 537 538parse an XML file and return an XMLHash structure 539 540 my @XMLHash = $XMLObject->XMLParse2XMLHash( String => $FileString ); 541 542 XML: 543 ==== 544 <Contact role="admin" type="organization"> 545 <Name type="long">Example Inc.</Name> 546 <Email type="primary">info@exampe.com<Domain>1234.com</Domain></Email> 547 <Email type="secondary">sales@example.com</Email> 548 <Telephone country="germany">+49-999-99999</Telephone> 549 </Contact> 550 551 ARRAY: 552 ====== 553 @XMLHash = ( 554 undef, 555 { 556 Contact => [ 557 undef, 558 { 559 role => 'admin', 560 type => 'organization', 561 Name => [ 562 undef, 563 { 564 Content => 'Example Inc.', 565 type => 'long', 566 }, 567 ], 568 Email => [ 569 undef, 570 { 571 type => 'primary', 572 Content => 'info@exampe.com', 573 Domain => [ 574 undef, 575 { 576 Content => '1234.com', 577 }, 578 ], 579 }, 580 { 581 type => 'secondary', 582 Content => 'sales@exampe.com', 583 }, 584 ], 585 Telephone => [ 586 undef, 587 { 588 country => 'germany', 589 Content => '+49-999-99999', 590 }, 591 ], 592 } 593 ], 594 } 595 ); 596 597 $XMLHash[1]{Contact}[1]{TagKey} = "[1]{'Contact'}[1]"; 598 $XMLHash[1]{Contact}[1]{role} = "admin"; 599 $XMLHash[1]{Contact}[1]{type} = "organization"; 600 $XMLHash[1]{Contact}[1]{Name}[1]{TagKey} = "[1]{'Contact'}[1]{'Name'}[1]"; 601 $XMLHash[1]{Contact}[1]{Name}[1]{Content} = "Example Inc."; 602 $XMLHash[1]{Contact}[1]{Name}[1]{type} = "long"; 603 $XMLHash[1]{Contact}[1]{Email}[1]{TagKey} = "[1]{'Contact'}[1]{'Email'}[1]"; 604 $XMLHash[1]{Contact}[1]{Email}[1]{Content} = "info\@exampe.com"; 605 $XMLHash[1]{Contact}[1]{Email}[1]{Domain}[1]{TagKey} = "[1]{'Contact'}[1]{'Email'}[1]{'Domain'}[1]"; 606 $XMLHash[1]{Contact}[1]{Email}[1]{Domain}[1]{Content} = "1234.com"; 607 $XMLHash[1]{Contact}[1]{Email}[2]{TagKey} = "[1]{'Contact'}[1]{'Email'}[2]"; 608 $XMLHash[1]{Contact}[1]{Email}[2]{type} = "secondary"; 609 $XMLHash[1]{Contact}[1]{Email}[2]{Content} = "sales\@exampe.com"; 610 611=cut 612 613sub XMLParse2XMLHash { 614 my ( $Self, %Param ) = @_; 615 616 my @XMLStructure = $Self->XMLParse(%Param); 617 return () if !@XMLStructure; 618 619 my @XMLHash = ( undef, $Self->XMLStructure2XMLHash( XMLStructure => \@XMLStructure ) ); 620 return @XMLHash; 621 622} 623 624=head2 XMLHash2D() 625 626returns a simple hash with tag keys as keys and the values of C<XMLHash> as values. 627As a side effect the data structure C<XMLHash> is enriched with tag keys. 628 629 my %Hash = $XMLObject->XMLHash2D( XMLHash => \@XMLHash ); 630 631For example: 632 633 $Hash{"[1]{'Planet'}[1]{'Content'}"'} = 'Sun'; 634 635=cut 636 637sub XMLHash2D { 638 my ( $Self, %Param ) = @_; 639 640 # check needed stuff 641 if ( !defined $Param{XMLHash} ) { 642 $Kernel::OM->Get('Kernel::System::Log')->Log( 643 Priority => 'error', 644 Message => 'XMLHash not defined!', 645 ); 646 return; 647 } 648 649 $Self->{XMLLevel} = 0; 650 $Self->{XMLTagCount} = 0; 651 $Self->{XMLHash} = {}; 652 $Self->{XMLHashReturn} = 1; 653 undef $Self->{XMLLevelTag}; 654 undef $Self->{XMLLevelCount}; 655 656 my $Count = 0; 657 for my $Item ( @{ $Param{XMLHash} } ) { 658 if ( ref $Item eq 'HASH' ) { 659 for ( sort keys %{$Item} ) { 660 $Self->_XMLHash2D( 661 Key => $Item->{Tag}, 662 Item => $Item, 663 Counter => $Count 664 ); 665 } 666 } 667 $Count++; 668 } 669 $Self->{XMLHashReturn} = 0; 670 671 return %{ $Self->{XMLHash} }; 672} 673 674=head2 XMLStructure2XMLHash() 675 676get an @XMLHash from a @XMLStructure with current TagKey param 677 678 my @XMLHash = $XMLObject->XMLStructure2XMLHash( XMLStructure => \@XMLStructure ); 679 680=cut 681 682sub XMLStructure2XMLHash { 683 my ( $Self, %Param ) = @_; 684 685 # check needed stuff 686 if ( !defined $Param{XMLStructure} ) { 687 $Kernel::OM->Get('Kernel::System::Log')->Log( 688 Priority => 'error', 689 Message => 'XMLStructure not defined!' 690 ); 691 return; 692 } 693 694 $Self->{Tll} = 0; 695 $Self->{XMLTagCount} = 0; 696 $Self->{XMLHash2} = {}; 697 $Self->{XMLHashReturn} = 1; 698 undef $Self->{XMLLevelTag}; 699 undef $Self->{XMLLevelCount}; 700 701 for my $Item ( @{ $Param{XMLStructure} } ) { 702 if ( ref $Item eq 'HASH' ) { 703 $Self->_XMLStructure2XMLHash( 704 Key => $Item->{Tag}, 705 Item => $Item, 706 Type => 'ARRAY' 707 ); 708 } 709 } 710 $Self->{XMLHashReturn} = 0; 711 712 return ( \%{ $Self->{XMLHash2} } ); 713} 714 715=head2 XMLParse() 716 717parse an XML file 718 719 my @XMLStructure = $XMLObject->XMLParse( String => $FileString ); 720 721 my @XMLStructure = $XMLObject->XMLParse( String => \$FileStringScalar ); 722 723=cut 724 725sub XMLParse { 726 my ( $Self, %Param ) = @_; 727 728 # check needed stuff 729 if ( !defined $Param{String} ) { 730 $Kernel::OM->Get('Kernel::System::Log')->Log( 731 Priority => 'error', 732 Message => 'String not defined!' 733 ); 734 return; 735 } 736 737 # check input type 738 if ( ref $Param{String} ) { 739 $Param{String} = ${ $Param{String} }; 740 } 741 742 # get encode object 743 my $EncodeObject = $Kernel::OM->Get('Kernel::System::Encode'); 744 745 # create checksum 746 my $CookedString = $Param{String}; 747 $EncodeObject->EncodeOutput( \$CookedString ); 748 my $MD5Object = Digest::MD5->new(); 749 $MD5Object->add($CookedString); 750 my $Checksum = $MD5Object->hexdigest(); 751 752 # get cache object 753 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 754 755 # check cache 756 if ($Checksum) { 757 758 # set CacheInMemory to 0 to prevent modification of the cache from outside 759 # See bug#[12761](https://bugs.otrs.org/show_bug.cgi?id=12761) for further details 760 my $Cache = $CacheObject->Get( 761 Type => 'XMLParse', 762 Key => $Checksum, 763 CacheInMemory => 0, 764 ); 765 766 return @{$Cache} if $Cache; 767 } 768 769 # cleanup global vars 770 undef $Self->{XMLARRAY}; 771 $Self->{XMLLevel} = 0; 772 $Self->{XMLTagCount} = 0; 773 undef $Self->{XMLLevelTag}; 774 undef $Self->{XMLLevelCount}; 775 776 # convert string 777 if ( $Param{String} =~ /(<.+?>)/ ) { 778 if ( $1 !~ /(utf-8|utf8)/i && $1 =~ /encoding=('|")(.+?)('|")/i ) { 779 my $SourceCharset = $2; 780 $Param{String} =~ s/$SourceCharset/utf-8/i; 781 $Param{String} = $EncodeObject->Convert( 782 Text => $Param{String}, 783 From => $SourceCharset, 784 To => 'utf-8', 785 Force => 1, 786 ); 787 } 788 } 789 790 # load parse package and parse 791 my $UseFallback = 1; 792 793 if ( eval 'require XML::Parser' ) { ## no critic 794 my $Parser = XML::Parser->new( 795 Handlers => { 796 Start => sub { $Self->_HS(@_); }, 797 End => sub { $Self->_ES(@_); }, 798 Char => sub { $Self->_CS(@_); }, 799 ExternEnt => sub { return '' }, # suppress loading of external entities 800 }, 801 ); 802 803 # get sourcename now to avoid a possible race condition where 804 # $@ could get altered after a failing eval! 805 my $Sourcename = $Param{Sourcename} ? "\n\n($Param{Sourcename})" : ''; 806 807 if ( eval { $Parser->parse( $Param{String} ) } ) { 808 $UseFallback = 0; 809 810 # remember, XML::Parser is managing e. g. & by it self 811 $Self->{XMLQuote} = 0; 812 } 813 else { 814 $Kernel::OM->Get('Kernel::System::Log')->Log( 815 Priority => 'error', 816 Message => "C-Parser: $@!$Sourcename" 817 ); 818 $Kernel::OM->Get('Kernel::System::Log')->Log( 819 Priority => 'error', 820 Message => 821 "XML::Parser had errors, falling back to XML::Parser::Lite. Offending XML was: $Param{String}", 822 ); 823 } 824 } 825 826 if ($UseFallback) { 827 require XML::Parser::Lite; ## no critic 828 829 my $Parser = XML::Parser::Lite->new( 830 Handlers => { 831 Start => sub { $Self->_HS(@_); }, 832 End => sub { $Self->_ES(@_); }, 833 Char => sub { $Self->_CS(@_); }, 834 ExternEnt => sub { return '' }, # suppress loading of external entities 835 }, 836 ); 837 $Parser->parse( $Param{String} ); 838 839 # remember, XML::Parser::Lite is managing e. g. & NOT by it self 840 $Self->{XMLQuote} = 1; 841 } 842 843 # quote 844 for my $XMLElement ( @{ $Self->{XMLARRAY} } ) { 845 $Self->_Decode($XMLElement); 846 } 847 848 # set cache 849 if ($Checksum) { 850 851 # set CacheInMemory to 0 to prevent modification of the cache from outside 852 # See bug#[12761](https://bugs.otrs.org/show_bug.cgi?id=12761) for further details 853 $CacheObject->Set( 854 Type => 'XMLParse', 855 Key => $Checksum, 856 Value => $Self->{XMLARRAY}, 857 TTL => 30 * 24 * 60 * 60, 858 CacheInMemory => 0, 859 ); 860 } 861 862 return @{ $Self->{XMLARRAY} }; 863} 864 865=begin Internal: 866 867=head2 _XMLHashAddAutoIncrement() 868 869Generate a new integer key. 870All keys for that type must be integers. 871 872 my $Key = $XMLObject->_XMLHashAddAutoIncrement( 873 Type => 'SomeType', 874 KeyAutoIncrement => 1, 875 ); 876 877=cut 878 879sub _XMLHashAddAutoIncrement { 880 my ( $Self, %Param ) = @_; 881 882 my $KeyAutoIncrement = 0; 883 my @KeysExists; 884 885 # check needed stuff 886 for (qw(Type KeyAutoIncrement)) { 887 if ( !$Param{$_} ) { 888 $Kernel::OM->Get('Kernel::System::Log')->Log( 889 Priority => 'error', 890 Message => "Need $_!" 891 ); 892 return; 893 } 894 } 895 896 # get database object 897 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 898 899 return if !$DBObject->Prepare( 900 SQL => 'SELECT DISTINCT(xml_key) FROM xml_storage WHERE xml_type = ?', 901 Bind => [ \$Param{Type} ], 902 ); 903 904 while ( my @Data = $DBObject->FetchrowArray() ) { 905 if ( $Data[0] ) { 906 push @KeysExists, $Data[0]; 907 } 908 } 909 910 for my $Key (@KeysExists) { 911 if ( $Key !~ /^\d{1,99}$/ ) { 912 $Kernel::OM->Get('Kernel::System::Log')->Log( 913 Priority => 'error', 914 Message => "No KeyAutoIncrement possible, no int key exists ($Key)!", 915 ); 916 return; 917 } 918 if ( $Key > $KeyAutoIncrement ) { 919 $KeyAutoIncrement = $Key; 920 } 921 } 922 923 $KeyAutoIncrement++; 924 return $KeyAutoIncrement; 925} 926 927sub _ElementBuild { 928 my ( $Self, %Param ) = @_; 929 930 my @Tag; 931 my @Sub; 932 my $Output = ''; 933 934 if ( $Param{Key} ) { 935 $Self->{XMLHash2XMLLayer}++; 936 $Output .= "<$Param{Key}"; 937 } 938 939 for ( sort keys %Param ) { 940 if ( ref $Param{$_} eq 'ARRAY' ) { 941 push @Tag, $_; 942 push @Sub, $Param{$_}; 943 } 944 elsif ( $_ ne 'Content' && $_ ne 'Key' && $_ !~ /^Tag/ ) { 945 if ( defined $Param{$_} ) { 946 $Param{$_} =~ s/&/&/g; 947 $Param{$_} =~ s/</</g; 948 $Param{$_} =~ s/>/>/g; 949 $Param{$_} =~ s/"/"/g; 950 } 951 $Output .= " $_=\"$Param{$_}\""; 952 } 953 } 954 if ( $Param{Key} ) { 955 $Output .= '>'; 956 } 957 if ( defined $Param{Content} ) { 958 959 # encode 960 $Param{Content} =~ s/&/&/g; 961 $Param{Content} =~ s/</</g; 962 $Param{Content} =~ s/>/>/g; 963 $Param{Content} =~ s/"/"/g; 964 $Output .= $Param{Content}; 965 } 966 else { 967 $Output .= "\n"; 968 } 969 for ( 0 .. $#Sub ) { 970 for my $K ( @{ $Sub[$_] } ) { 971 if ( defined $K ) { 972 $Output .= $Self->_ElementBuild( 973 %{$K}, 974 Key => $Tag[$_], 975 ); 976 } 977 } 978 } 979 980 if ( $Param{Key} ) { 981 $Output .= "</$Param{Key}>\n"; 982 $Self->{XMLHash2XMLLayer} = $Self->{XMLHash2XMLLayer} - 1; 983 } 984 985 return $Output; 986} 987 988sub _XMLHash2D { 989 my ( $Self, %Param ) = @_; 990 991 if ( !defined $Param{Item} ) { 992 return ''; 993 } 994 elsif ( ref $Param{Item} eq 'HASH' ) { 995 $Self->{XMLLevel}++; 996 $Self->{XMLTagCount}++; 997 $Self->{XMLLevelTag}->{ $Self->{XMLLevel} } = $Param{Key}; 998 if ( $Self->{Tll} && $Self->{Tll} > $Self->{XMLLevel} ) { 999 for ( ( $Self->{XMLLevel} + 1 ) .. 30 ) { 1000 undef $Self->{XMLLevelCount}->{$_}; #->{$Element} = 0; 1001 } 1002 } 1003 $Self->{XMLLevelCount}->{ $Self->{XMLLevel} }->{ $Param{Key} || '' }++; 1004 1005 # remember old level 1006 $Self->{Tll} = $Self->{XMLLevel}; 1007 1008 my $Key = "[$Param{Counter}]"; 1009 for ( 2 .. ( $Self->{XMLLevel} ) ) { 1010 $Key .= "{'$Self->{XMLLevelTag}->{$_}'}"; 1011 $Key .= "[" . $Self->{XMLLevelCount}->{$_}->{ $Self->{XMLLevelTag}->{$_} } . "]"; 1012 } 1013 1014 # add tag key to the passed in data structure 1015 $Param{Item}->{TagKey} = $Key; 1016 1017 for ( sort keys %{ $Param{Item} } ) { 1018 if ( defined $Param{Item}->{$_} && ref $Param{Item}->{$_} ne 'ARRAY' ) { 1019 $Self->{XMLHash}->{ $Key . "{'$_'}" } = $Param{Item}->{$_}; 1020 } 1021 $Self->_XMLHash2D( 1022 Key => $_, 1023 Item => $Param{Item}->{$_}, 1024 Counter => $Param{Counter} 1025 ); 1026 } 1027 $Self->{XMLLevel} = $Self->{XMLLevel} - 1; 1028 } 1029 elsif ( ref $Param{Item} eq 'ARRAY' ) { 1030 for ( @{ $Param{Item} } ) { 1031 $Self->_XMLHash2D( 1032 Key => $Param{Key}, 1033 Item => $_, 1034 Counter => $Param{Counter} 1035 ); 1036 } 1037 } 1038 1039 return 1; 1040} 1041 1042sub _XMLStructure2XMLHash { 1043 my ( $Self, %Param ) = @_; 1044 1045 my $Output = ''; 1046 1047 if ( !defined $Param{Item} ) { 1048 return; 1049 } 1050 elsif ( ref $Param{Item} eq 'HASH' ) { 1051 if ( $Param{Item}->{TagType} eq 'End' ) { 1052 return ''; 1053 } 1054 $Self->{XMLTagCount}++; 1055 $Self->{XMLLevelTag}->{ $Param{Item}->{TagLevel} } = $Param{Key}; 1056 if ( $Self->{Tll} && $Self->{Tll} > $Param{Item}->{TagLevel} ) { 1057 for ( ( $Param{Item}->{TagLevel} + 1 ) .. 30 ) { 1058 undef $Self->{XMLLevelCount}->{$_}; 1059 } 1060 } 1061 $Self->{XMLLevelCount}->{ $Param{Item}->{TagLevel} }->{ $Param{Key} }++; 1062 1063 # remember old level 1064 $Self->{Tll} = $Param{Item}->{TagLevel}; 1065 1066 if ( $Param{Item}->{TagLevel} == 1 ) { 1067 for ( sort keys %{ $Param{Item} } ) { 1068 if ( !defined $Param{Item}->{$_} ) { 1069 $Param{Item}->{$_} = ''; 1070 } 1071 if ( $_ !~ /^Tag/ ) { 1072 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1073 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ]->{$_} = $Param{Item}->{$_}; 1074 } 1075 } 1076 } 1077 elsif ( $Param{Item}->{TagLevel} == 2 ) { 1078 for ( sort keys %{ $Param{Item} } ) { 1079 if ( !defined $Param{Item}->{$_} ) { 1080 $Param{Item}->{$_} = ''; 1081 } 1082 if ( $_ !~ /^Tag/ ) { 1083 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1084 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1085 ->{ $Self->{XMLLevelTag}->{2} } 1086 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ]->{$_} = $Param{Item}->{$_}; 1087 } 1088 } 1089 } 1090 elsif ( $Param{Item}->{TagLevel} == 3 ) { 1091 for ( sort keys %{ $Param{Item} } ) { 1092 if ( !defined $Param{Item}->{$_} ) { 1093 $Param{Item}->{$_} = ''; 1094 } 1095 if ( $_ !~ /^Tag/ ) { 1096 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1097 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1098 ->{ $Self->{XMLLevelTag}->{2} } 1099 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1100 ->{ $Self->{XMLLevelTag}->{3} } 1101 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ]->{$_} = $Param{Item}->{$_}; 1102 } 1103 } 1104 } 1105 elsif ( $Param{Item}->{TagLevel} == 4 ) { 1106 for ( sort keys %{ $Param{Item} } ) { 1107 if ( !defined $Param{Item}->{$_} ) { 1108 $Param{Item}->{$_} = ''; 1109 } 1110 if ( $_ !~ /^Tag/ ) { 1111 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1112 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1113 ->{ $Self->{XMLLevelTag}->{2} } 1114 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1115 ->{ $Self->{XMLLevelTag}->{3} } 1116 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1117 ->{ $Self->{XMLLevelTag}->{4} } 1118 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ]->{$_} = $Param{Item}->{$_}; 1119 } 1120 } 1121 } 1122 elsif ( $Param{Item}->{TagLevel} == 5 ) { 1123 for ( sort keys %{ $Param{Item} } ) { 1124 if ( !defined $Param{Item}->{$_} ) { 1125 $Param{Item}->{$_} = ''; 1126 } 1127 if ( $_ !~ /^Tag/ ) { 1128 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1129 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1130 ->{ $Self->{XMLLevelTag}->{2} } 1131 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1132 ->{ $Self->{XMLLevelTag}->{3} } 1133 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1134 ->{ $Self->{XMLLevelTag}->{4} } 1135 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1136 ->{ $Self->{XMLLevelTag}->{5} } 1137 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ]->{$_} = $Param{Item}->{$_}; 1138 } 1139 } 1140 } 1141 elsif ( $Param{Item}->{TagLevel} == 6 ) { 1142 for ( sort keys %{ $Param{Item} } ) { 1143 if ( !defined $Param{Item}->{$_} ) { 1144 $Param{Item}->{$_} = ''; 1145 } 1146 if ( $_ !~ /^Tag/ ) { 1147 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1148 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1149 ->{ $Self->{XMLLevelTag}->{2} } 1150 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1151 ->{ $Self->{XMLLevelTag}->{3} } 1152 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1153 ->{ $Self->{XMLLevelTag}->{4} } 1154 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1155 ->{ $Self->{XMLLevelTag}->{5} } 1156 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1157 ->{ $Self->{XMLLevelTag}->{6} } 1158 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ]->{$_} = $Param{Item}->{$_}; 1159 } 1160 } 1161 } 1162 elsif ( $Param{Item}->{TagLevel} == 7 ) { 1163 for ( sort keys %{ $Param{Item} } ) { 1164 if ( !defined $Param{Item}->{$_} ) { 1165 $Param{Item}->{$_} = ''; 1166 } 1167 if ( $_ !~ /^Tag/ ) { 1168 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1169 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1170 ->{ $Self->{XMLLevelTag}->{2} } 1171 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1172 ->{ $Self->{XMLLevelTag}->{3} } 1173 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1174 ->{ $Self->{XMLLevelTag}->{4} } 1175 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1176 ->{ $Self->{XMLLevelTag}->{5} } 1177 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1178 ->{ $Self->{XMLLevelTag}->{6} } 1179 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1180 ->{ $Self->{XMLLevelTag}->{7} } 1181 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ]->{$_} = $Param{Item}->{$_}; 1182 } 1183 } 1184 } 1185 elsif ( $Param{Item}->{TagLevel} == 8 ) { 1186 for ( sort keys %{ $Param{Item} } ) { 1187 if ( !defined $Param{Item}->{$_} ) { 1188 $Param{Item}->{$_} = ''; 1189 } 1190 if ( $_ !~ /^Tag/ ) { 1191 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1192 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1193 ->{ $Self->{XMLLevelTag}->{2} } 1194 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1195 ->{ $Self->{XMLLevelTag}->{3} } 1196 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1197 ->{ $Self->{XMLLevelTag}->{4} } 1198 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1199 ->{ $Self->{XMLLevelTag}->{5} } 1200 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1201 ->{ $Self->{XMLLevelTag}->{6} } 1202 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1203 ->{ $Self->{XMLLevelTag}->{7} } 1204 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1205 ->{ $Self->{XMLLevelTag}->{8} } 1206 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ]->{$_} = $Param{Item}->{$_}; 1207 } 1208 } 1209 } 1210 elsif ( $Param{Item}->{TagLevel} == 9 ) { 1211 for ( sort keys %{ $Param{Item} } ) { 1212 if ( !defined $Param{Item}->{$_} ) { 1213 $Param{Item}->{$_} = ''; 1214 } 1215 if ( $_ !~ /^Tag/ ) { 1216 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1217 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1218 ->{ $Self->{XMLLevelTag}->{2} } 1219 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1220 ->{ $Self->{XMLLevelTag}->{3} } 1221 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1222 ->{ $Self->{XMLLevelTag}->{4} } 1223 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1224 ->{ $Self->{XMLLevelTag}->{5} } 1225 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1226 ->{ $Self->{XMLLevelTag}->{6} } 1227 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1228 ->{ $Self->{XMLLevelTag}->{7} } 1229 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1230 ->{ $Self->{XMLLevelTag}->{8} } 1231 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1232 ->{ $Self->{XMLLevelTag}->{9} } 1233 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ]->{$_} = $Param{Item}->{$_}; 1234 } 1235 } 1236 } 1237 elsif ( $Param{Item}->{TagLevel} == 10 ) { 1238 for ( sort keys %{ $Param{Item} } ) { 1239 if ( !defined $Param{Item}->{$_} ) { 1240 $Param{Item}->{$_} = ''; 1241 } 1242 if ( $_ !~ /^Tag/ ) { 1243 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1244 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1245 ->{ $Self->{XMLLevelTag}->{2} } 1246 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1247 ->{ $Self->{XMLLevelTag}->{3} } 1248 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1249 ->{ $Self->{XMLLevelTag}->{4} } 1250 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1251 ->{ $Self->{XMLLevelTag}->{5} } 1252 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1253 ->{ $Self->{XMLLevelTag}->{6} } 1254 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1255 ->{ $Self->{XMLLevelTag}->{7} } 1256 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1257 ->{ $Self->{XMLLevelTag}->{8} } 1258 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1259 ->{ $Self->{XMLLevelTag}->{9} } 1260 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1261 ->{ $Self->{XMLLevelTag}->{10} } 1262 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ]->{$_} = $Param{Item}->{$_}; 1263 } 1264 } 1265 } 1266 elsif ( $Param{Item}->{TagLevel} == 11 ) { 1267 for ( sort keys %{ $Param{Item} } ) { 1268 if ( !defined $Param{Item}->{$_} ) { 1269 $Param{Item}->{$_} = ''; 1270 } 1271 if ( $_ !~ /^Tag/ ) { 1272 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1273 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1274 ->{ $Self->{XMLLevelTag}->{2} } 1275 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1276 ->{ $Self->{XMLLevelTag}->{3} } 1277 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1278 ->{ $Self->{XMLLevelTag}->{4} } 1279 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1280 ->{ $Self->{XMLLevelTag}->{5} } 1281 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1282 ->{ $Self->{XMLLevelTag}->{6} } 1283 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1284 ->{ $Self->{XMLLevelTag}->{7} } 1285 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1286 ->{ $Self->{XMLLevelTag}->{8} } 1287 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1288 ->{ $Self->{XMLLevelTag}->{9} } 1289 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1290 ->{ $Self->{XMLLevelTag}->{10} } 1291 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ] 1292 ->{ $Self->{XMLLevelTag}->{11} } 1293 ->[ $Self->{XMLLevelCount}->{11}->{ $Self->{XMLLevelTag}->{11} } ]->{$_} = $Param{Item}->{$_}; 1294 } 1295 } 1296 } 1297 elsif ( $Param{Item}->{TagLevel} == 12 ) { 1298 for ( sort keys %{ $Param{Item} } ) { 1299 if ( !defined $Param{Item}->{$_} ) { 1300 $Param{Item}->{$_} = ''; 1301 } 1302 if ( $_ !~ /^Tag/ ) { 1303 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1304 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1305 ->{ $Self->{XMLLevelTag}->{2} } 1306 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1307 ->{ $Self->{XMLLevelTag}->{3} } 1308 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1309 ->{ $Self->{XMLLevelTag}->{4} } 1310 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1311 ->{ $Self->{XMLLevelTag}->{5} } 1312 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1313 ->{ $Self->{XMLLevelTag}->{6} } 1314 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1315 ->{ $Self->{XMLLevelTag}->{7} } 1316 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1317 ->{ $Self->{XMLLevelTag}->{8} } 1318 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1319 ->{ $Self->{XMLLevelTag}->{9} } 1320 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1321 ->{ $Self->{XMLLevelTag}->{10} } 1322 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ] 1323 ->{ $Self->{XMLLevelTag}->{11} } 1324 ->[ $Self->{XMLLevelCount}->{11}->{ $Self->{XMLLevelTag}->{11} } ] 1325 ->{ $Self->{XMLLevelTag}->{12} } 1326 ->[ $Self->{XMLLevelCount}->{12}->{ $Self->{XMLLevelTag}->{12} } ]->{$_} = $Param{Item}->{$_}; 1327 } 1328 } 1329 } 1330 elsif ( $Param{Item}->{TagLevel} == 13 ) { 1331 for ( sort keys %{ $Param{Item} } ) { 1332 if ( !defined $Param{Item}->{$_} ) { 1333 $Param{Item}->{$_} = ''; 1334 } 1335 if ( $_ !~ /^Tag/ ) { 1336 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1337 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1338 ->{ $Self->{XMLLevelTag}->{2} } 1339 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1340 ->{ $Self->{XMLLevelTag}->{3} } 1341 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1342 ->{ $Self->{XMLLevelTag}->{4} } 1343 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1344 ->{ $Self->{XMLLevelTag}->{5} } 1345 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1346 ->{ $Self->{XMLLevelTag}->{6} } 1347 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1348 ->{ $Self->{XMLLevelTag}->{7} } 1349 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1350 ->{ $Self->{XMLLevelTag}->{8} } 1351 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1352 ->{ $Self->{XMLLevelTag}->{9} } 1353 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1354 ->{ $Self->{XMLLevelTag}->{10} } 1355 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ] 1356 ->{ $Self->{XMLLevelTag}->{11} } 1357 ->[ $Self->{XMLLevelCount}->{11}->{ $Self->{XMLLevelTag}->{11} } ] 1358 ->{ $Self->{XMLLevelTag}->{12} } 1359 ->[ $Self->{XMLLevelCount}->{12}->{ $Self->{XMLLevelTag}->{12} } ] 1360 ->{ $Self->{XMLLevelTag}->{13} } 1361 ->[ $Self->{XMLLevelCount}->{13}->{ $Self->{XMLLevelTag}->{13} } ]->{$_} = $Param{Item}->{$_}; 1362 } 1363 } 1364 } 1365 elsif ( $Param{Item}->{TagLevel} == 14 ) { 1366 for ( sort keys %{ $Param{Item} } ) { 1367 if ( !defined $Param{Item}->{$_} ) { 1368 $Param{Item}->{$_} = ''; 1369 } 1370 if ( $_ !~ /^Tag/ ) { 1371 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1372 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1373 ->{ $Self->{XMLLevelTag}->{2} } 1374 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1375 ->{ $Self->{XMLLevelTag}->{3} } 1376 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1377 ->{ $Self->{XMLLevelTag}->{4} } 1378 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1379 ->{ $Self->{XMLLevelTag}->{5} } 1380 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1381 ->{ $Self->{XMLLevelTag}->{6} } 1382 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1383 ->{ $Self->{XMLLevelTag}->{7} } 1384 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1385 ->{ $Self->{XMLLevelTag}->{8} } 1386 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1387 ->{ $Self->{XMLLevelTag}->{9} } 1388 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1389 ->{ $Self->{XMLLevelTag}->{10} } 1390 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ] 1391 ->{ $Self->{XMLLevelTag}->{11} } 1392 ->[ $Self->{XMLLevelCount}->{11}->{ $Self->{XMLLevelTag}->{11} } ] 1393 ->{ $Self->{XMLLevelTag}->{12} } 1394 ->[ $Self->{XMLLevelCount}->{12}->{ $Self->{XMLLevelTag}->{12} } ] 1395 ->{ $Self->{XMLLevelTag}->{13} } 1396 ->[ $Self->{XMLLevelCount}->{13}->{ $Self->{XMLLevelTag}->{13} } ] 1397 ->{ $Self->{XMLLevelTag}->{14} } 1398 ->[ $Self->{XMLLevelCount}->{14}->{ $Self->{XMLLevelTag}->{14} } ]->{$_} = $Param{Item}->{$_}; 1399 } 1400 } 1401 } 1402 elsif ( $Param{Item}->{TagLevel} == 15 ) { 1403 for ( sort keys %{ $Param{Item} } ) { 1404 if ( !defined $Param{Item}->{$_} ) { 1405 $Param{Item}->{$_} = ''; 1406 } 1407 if ( $_ !~ /^Tag/ ) { 1408 $Self->{XMLHash2}->{ $Self->{XMLLevelTag}->{1} } 1409 ->[ $Self->{XMLLevelCount}->{1}->{ $Self->{XMLLevelTag}->{1} } ] 1410 ->{ $Self->{XMLLevelTag}->{2} } 1411 ->[ $Self->{XMLLevelCount}->{2}->{ $Self->{XMLLevelTag}->{2} } ] 1412 ->{ $Self->{XMLLevelTag}->{3} } 1413 ->[ $Self->{XMLLevelCount}->{3}->{ $Self->{XMLLevelTag}->{3} } ] 1414 ->{ $Self->{XMLLevelTag}->{4} } 1415 ->[ $Self->{XMLLevelCount}->{4}->{ $Self->{XMLLevelTag}->{4} } ] 1416 ->{ $Self->{XMLLevelTag}->{5} } 1417 ->[ $Self->{XMLLevelCount}->{5}->{ $Self->{XMLLevelTag}->{5} } ] 1418 ->{ $Self->{XMLLevelTag}->{6} } 1419 ->[ $Self->{XMLLevelCount}->{6}->{ $Self->{XMLLevelTag}->{6} } ] 1420 ->{ $Self->{XMLLevelTag}->{7} } 1421 ->[ $Self->{XMLLevelCount}->{7}->{ $Self->{XMLLevelTag}->{7} } ] 1422 ->{ $Self->{XMLLevelTag}->{8} } 1423 ->[ $Self->{XMLLevelCount}->{8}->{ $Self->{XMLLevelTag}->{8} } ] 1424 ->{ $Self->{XMLLevelTag}->{9} } 1425 ->[ $Self->{XMLLevelCount}->{9}->{ $Self->{XMLLevelTag}->{9} } ] 1426 ->{ $Self->{XMLLevelTag}->{10} } 1427 ->[ $Self->{XMLLevelCount}->{10}->{ $Self->{XMLLevelTag}->{10} } ] 1428 ->{ $Self->{XMLLevelTag}->{11} } 1429 ->[ $Self->{XMLLevelCount}->{11}->{ $Self->{XMLLevelTag}->{11} } ] 1430 ->{ $Self->{XMLLevelTag}->{12} } 1431 ->[ $Self->{XMLLevelCount}->{12}->{ $Self->{XMLLevelTag}->{12} } ] 1432 ->{ $Self->{XMLLevelTag}->{13} } 1433 ->[ $Self->{XMLLevelCount}->{13}->{ $Self->{XMLLevelTag}->{13} } ] 1434 ->{ $Self->{XMLLevelTag}->{14} } 1435 ->[ $Self->{XMLLevelCount}->{14}->{ $Self->{XMLLevelTag}->{14} } ] 1436 ->{ $Self->{XMLLevelTag}->{15} } 1437 ->[ $Self->{XMLLevelCount}->{15}->{ $Self->{XMLLevelTag}->{15} } ]->{$_} = $Param{Item}->{$_}; 1438 } 1439 } 1440 } 1441 } 1442 1443 return 1; 1444} 1445 1446sub _Decode { 1447 my ( $Self, $A ) = @_; 1448 1449 # get encode object 1450 my $EncodeObject = $Kernel::OM->Get('Kernel::System::Encode'); 1451 1452 for ( sort keys %{$A} ) { 1453 if ( ref $A->{$_} eq 'ARRAY' ) { 1454 for my $B ( @{ $A->{$_} } ) { 1455 $Self->_Decode($B); 1456 } 1457 } 1458 1459 # decode 1460 elsif ( defined $A->{$_} ) { 1461 1462 # check if decode is already done by parser 1463 if ( $Self->{XMLQuote} ) { 1464 my %Map = ( 1465 'amp' => '&', 1466 'lt' => '<', 1467 'gt' => '>', 1468 'quot' => '"', 1469 ); 1470 $A->{$_} =~ s/&(amp|lt|gt|quot);/$Map{$1}/g; 1471 } 1472 1473 # convert into default charset 1474 $A->{$_} = $EncodeObject->Convert( 1475 Text => $A->{$_}, 1476 From => 'utf-8', 1477 To => 'utf-8', 1478 Force => 1, 1479 ); 1480 } 1481 } 1482 1483 return 1; 1484} 1485 1486sub _HS { 1487 my ( $Self, $Expat, $Element, %Attr ) = @_; 1488 1489 if ( $Self->{LastTag} ) { 1490 push @{ $Self->{XMLARRAY} }, { %{ $Self->{LastTag} }, Content => $Self->{C} }; 1491 } 1492 1493 undef $Self->{LastTag}; 1494 undef $Self->{C}; 1495 1496 $Self->{XMLLevel}++; 1497 $Self->{XMLTagCount}++; 1498 $Self->{XMLLevelTag}->{ $Self->{XMLLevel} } = $Element; 1499 1500 if ( $Self->{Tll} && $Self->{Tll} > $Self->{XMLLevel} ) { 1501 for ( ( $Self->{XMLLevel} + 1 ) .. 30 ) { 1502 undef $Self->{XMLLevelCount}->{$_}; 1503 } 1504 } 1505 1506 $Self->{XMLLevelCount}->{ $Self->{XMLLevel} }->{$Element}++; 1507 1508 # remember old level 1509 $Self->{Tll} = $Self->{XMLLevel}; 1510 1511 my $Key = ''; 1512 for ( 1 .. ( $Self->{XMLLevel} ) ) { 1513 $Key .= "{'$Self->{XMLLevelTag}->{$_}'}"; 1514 $Key .= "[" . $Self->{XMLLevelCount}->{$_}->{ $Self->{XMLLevelTag}->{$_} } . "]"; 1515 } 1516 1517 $Self->{LastTag} = { 1518 %Attr, 1519 TagType => 'Start', 1520 Tag => $Element, 1521 TagLevel => $Self->{XMLLevel}, 1522 TagCount => $Self->{XMLTagCount}, 1523 TagLastLevel => $Self->{XMLLevelTag}->{ ( $Self->{XMLLevel} - 1 ) }, 1524 }; 1525 1526 return 1; 1527} 1528 1529sub _CS { 1530 my ( $Self, $Expat, $Element, $I, $II ) = @_; 1531 1532 if ( $Self->{LastTag} ) { 1533 $Self->{C} .= $Element; 1534 } 1535 1536 return 1; 1537} 1538 1539sub _ES { 1540 my ( $Self, $Expat, $Element ) = @_; 1541 1542 $Self->{XMLTagCount}++; 1543 1544 if ( $Self->{LastTag} ) { 1545 push @{ $Self->{XMLARRAY} }, { %{ $Self->{LastTag} }, Content => $Self->{C} }; 1546 } 1547 1548 undef $Self->{LastTag}; 1549 undef $Self->{C}; 1550 1551 push( 1552 @{ $Self->{XMLARRAY} }, 1553 { 1554 TagType => 'End', 1555 TagLevel => $Self->{XMLLevel}, 1556 TagCount => $Self->{XMLTagCount}, 1557 Tag => $Element 1558 }, 1559 ); 1560 1561 $Self->{XMLLevel} = $Self->{XMLLevel} - 1; 1562 1563 return 1; 1564} 1565 15661; 1567 1568=end Internal: 1569 1570=head1 TERMS AND CONDITIONS 1571 1572This software is part of the OTRS project (L<https://otrs.org/>). 1573 1574This software comes with ABSOLUTELY NO WARRANTY. For details, see 1575the enclosed file COPYING for license information (GPL). If you 1576did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 1577 1578=cut 1579