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. &amp; 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. &amp; 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/&/&amp;/g;
947                $Param{$_} =~ s/</&lt;/g;
948                $Param{$_} =~ s/>/&gt;/g;
949                $Param{$_} =~ s/"/&quot;/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/&/&amp;/g;
961        $Param{Content} =~ s/</&lt;/g;
962        $Param{Content} =~ s/>/&gt;/g;
963        $Param{Content} =~ s/"/&quot;/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