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::Web::Request; 10 11use strict; 12use warnings; 13 14use CGI (); 15use CGI::Carp; 16use File::Path qw(); 17 18use Kernel::System::VariableCheck qw(:all); 19 20our @ObjectDependencies = ( 21 'Kernel::Config', 22 'Kernel::System::CheckItem', 23 'Kernel::System::Encode', 24 'Kernel::System::Web::UploadCache', 25 'Kernel::System::FormDraft', 26 'Kernel::System::Main', 27); 28 29=head1 NAME 30 31Kernel::System::Web::Request - global CGI interface 32 33=head1 DESCRIPTION 34 35All cgi param functions. 36 37=head1 PUBLIC INTERFACE 38 39=head2 new() 40 41create param object. Do not use it directly, instead use: 42 43 use Kernel::System::ObjectManager; 44 local $Kernel::OM = Kernel::System::ObjectManager->new( 45 'Kernel::System::Web::Request' => { 46 WebRequest => CGI::Fast->new(), # optional, e. g. if fast cgi is used 47 } 48 ); 49 my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 50 51If Kernel::System::Web::Request is instantiated several times, they will share the 52same CGI data (this can be helpful in filters which do not have access to the 53ParamObject, for example. 54 55If you need to reset the CGI data before creating a new instance, use 56 57 CGI::initialize_globals(); 58 59before calling Kernel::System::Web::Request->new(); 60 61=cut 62 63sub new { 64 my ( $Type, %Param ) = @_; 65 66 # allocate new hash for object 67 my $Self = {}; 68 bless( $Self, $Type ); 69 70 # get config object 71 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 72 73 # max 5 MB posts 74 $CGI::POST_MAX = $ConfigObject->Get('WebMaxFileUpload') || 1024 * 1024 * 5; ## no critic 75 76 # query object (in case use already existing WebRequest, e. g. fast cgi) 77 $Self->{Query} = $Param{WebRequest} || CGI->new(); 78 79 return $Self; 80} 81 82=head2 Error() 83 84to get the error back 85 86 if ( $ParamObject->Error() ) { 87 print STDERR $ParamObject->Error() . "\n"; 88 } 89 90=cut 91 92sub Error { 93 my ( $Self, %Param ) = @_; 94 95 # Workaround, do not check cgi_error() with perlex, CGI module is not 96 # working with perlex. 97 if ( $ENV{'GATEWAY_INTERFACE'} && $ENV{'GATEWAY_INTERFACE'} =~ /^CGI-PerlEx/ ) { 98 return; 99 } 100 101 return if !$Self->{Query}->cgi_error(); 102 ## no critic 103 return $Self->{Query}->cgi_error() . ' - POST_MAX=' . ( $CGI::POST_MAX / 1024 ) . 'KB'; 104 ## use critic 105} 106 107=head2 GetParam() 108 109to get single request parameters. By default, trimming is performed on the data. 110 111 my $Param = $ParamObject->GetParam( 112 Param => 'ID', 113 Raw => 1, # optional, input data is not changed 114 ); 115 116=cut 117 118sub GetParam { 119 my ( $Self, %Param ) = @_; 120 121 my $Value = $Self->{Query}->param( $Param{Param} ); 122 123 # Fallback to query string for mixed requests. 124 my $RequestMethod = $Self->{Query}->request_method() // ''; 125 if ( $RequestMethod eq 'POST' && !defined $Value ) { 126 $Value = $Self->{Query}->url_param( $Param{Param} ); 127 } 128 129 $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$Value ); 130 131 my $Raw = defined $Param{Raw} ? $Param{Raw} : 0; 132 133 if ( !$Raw ) { 134 135 # If it is a plain string, perform trimming 136 if ( ref \$Value eq 'SCALAR' ) { 137 $Kernel::OM->Get('Kernel::System::CheckItem')->StringClean( 138 StringRef => \$Value, 139 TrimLeft => 1, 140 TrimRight => 1, 141 ); 142 } 143 } 144 145 return $Value; 146} 147 148=head2 GetParamNames() 149 150to get names of all parameters passed to the script. 151 152 my @ParamNames = $ParamObject->GetParamNames(); 153 154Example: 155 156Called URL: index.pl?Action=AdminSystemConfiguration;Subaction=Save;Name=Config::Option::Valid 157 158 my @ParamNames = $ParamObject->GetParamNames(); 159 print join " :: ", @ParamNames; 160 #prints Action :: Subaction :: Name 161 162=cut 163 164sub GetParamNames { 165 my $Self = shift; 166 167 # fetch all names 168 my @ParamNames = $Self->{Query}->param(); 169 170 # Fallback to query string for mixed requests. 171 my $RequestMethod = $Self->{Query}->request_method() // ''; 172 if ( $RequestMethod eq 'POST' ) { 173 my %POSTNames; 174 @POSTNames{@ParamNames} = @ParamNames; 175 my @GetNames = $Self->{Query}->url_param(); 176 GETNAME: 177 for my $GetName (@GetNames) { 178 next GETNAME if !defined $GetName; 179 push @ParamNames, $GetName if !exists $POSTNames{$GetName}; 180 } 181 } 182 183 for my $Name (@ParamNames) { 184 $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$Name ); 185 } 186 187 return @ParamNames; 188} 189 190=head2 GetArray() 191 192to get array request parameters. 193By default, trimming is performed on the data. 194 195 my @Param = $ParamObject->GetArray( 196 Param => 'ID', 197 Raw => 1, # optional, input data is not changed 198 ); 199 200=cut 201 202sub GetArray { 203 my ( $Self, %Param ) = @_; 204 205 my @Values = $Self->{Query}->multi_param( $Param{Param} ); 206 207 # Fallback to query string for mixed requests. 208 my $RequestMethod = $Self->{Query}->request_method() // ''; 209 if ( $RequestMethod eq 'POST' && !@Values ) { 210 @Values = $Self->{Query}->url_param( $Param{Param} ); 211 } 212 213 $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \@Values ); 214 215 my $Raw = defined $Param{Raw} ? $Param{Raw} : 0; 216 217 if ( !$Raw ) { 218 219 # get check item object 220 my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem'); 221 222 VALUE: 223 for my $Value (@Values) { 224 225 # don't validate CGI::File::Temp objects from file uploads 226 next VALUE if !$Value || ref \$Value ne 'SCALAR'; 227 228 $CheckItemObject->StringClean( 229 StringRef => \$Value, 230 TrimLeft => 1, 231 TrimRight => 1, 232 ); 233 } 234 } 235 236 return @Values; 237} 238 239=head2 GetUploadAll() 240 241gets file upload data. 242 243 my %File = $ParamObject->GetUploadAll( 244 Param => 'FileParam', # the name of the request parameter containing the file data 245 ); 246 247 returns ( 248 Filename => 'abc.txt', 249 ContentType => 'text/plain', 250 Content => 'Some text', 251 ); 252 253=cut 254 255sub GetUploadAll { 256 my ( $Self, %Param ) = @_; 257 258 # get upload 259 my $Upload = $Self->{Query}->upload( $Param{Param} ); 260 return if !$Upload; 261 262 # get real file name 263 my $UploadFilenameOrig = $Self->GetParam( Param => $Param{Param} ) || 'unknown'; 264 265 my $NewFileName = "$UploadFilenameOrig"; # use "" to get filename of anony. object 266 $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$NewFileName ); 267 268 # replace all devices like c: or d: and dirs for IE! 269 $NewFileName =~ s/.:\\(.*)/$1/g; 270 $NewFileName =~ s/.*\\(.+?)/$1/g; 271 272 # return a string 273 my $Content = ''; 274 while (<$Upload>) { 275 $Content .= $_; 276 } 277 close $Upload; 278 279 my $ContentType = $Self->_GetUploadInfo( 280 Filename => $UploadFilenameOrig, 281 Header => 'Content-Type', 282 ); 283 284 return ( 285 Filename => $NewFileName, 286 Content => $Content, 287 ContentType => $ContentType, 288 ); 289} 290 291sub _GetUploadInfo { 292 my ( $Self, %Param ) = @_; 293 294 # get file upload info 295 my $FileInfo = $Self->{Query}->uploadInfo( $Param{Filename} ); 296 297 # return if no upload info exists 298 return 'application/octet-stream' if !$FileInfo; 299 300 # return if no content type of upload info exists 301 return 'application/octet-stream' if !$FileInfo->{ $Param{Header} }; 302 303 # return content type of upload info 304 return $FileInfo->{ $Param{Header} }; 305} 306 307=head2 SetCookie() 308 309set a cookie 310 311 $ParamObject->SetCookie( 312 Key => ID, 313 Value => 123456, 314 Expires => '+3660s', 315 Path => 'otrs/', # optional, only allow cookie for given path 316 Secure => 1, # optional, set secure attribute to disable cookie on HTTP (HTTPS only) 317 HTTPOnly => 1, # optional, sets HttpOnly attribute of cookie to prevent access via JavaScript 318 ); 319 320=cut 321 322sub SetCookie { 323 my ( $Self, %Param ) = @_; 324 325 $Param{Path} ||= ''; 326 327 return $Self->{Query}->cookie( 328 -name => $Param{Key}, 329 -value => $Param{Value}, 330 -expires => $Param{Expires}, 331 -secure => $Param{Secure} || '', 332 -httponly => $Param{HTTPOnly} || '', 333 -path => '/' . $Param{Path}, 334 ); 335} 336 337=head2 GetCookie() 338 339get a cookie 340 341 my $String = $ParamObject->GetCookie( 342 Key => ID, 343 ); 344 345=cut 346 347sub GetCookie { 348 my ( $Self, %Param ) = @_; 349 350 return $Self->{Query}->cookie( $Param{Key} ); 351} 352 353=head2 IsAJAXRequest() 354 355checks if the current request was sent by AJAX 356 357 my $IsAJAXRequest = $ParamObject->IsAJAXRequest(); 358 359=cut 360 361sub IsAJAXRequest { 362 my ( $Self, %Param ) = @_; 363 364 return ( $Self->{Query}->http('X-Requested-With') // '' ) eq 'XMLHttpRequest' ? 1 : 0; 365} 366 367=head2 LoadFormDraft() 368 369Load specified draft. 370This will read stored draft data and inject it into the param object 371for transparent use by frontend module. 372 373 my $FormDraftID = $ParamObject->LoadFormDraft( 374 FormDraftID => 123, 375 UserID => 1, 376 ); 377 378=cut 379 380sub LoadFormDraft { 381 my ( $Self, %Param ) = @_; 382 383 return if !$Param{FormDraftID} || !$Param{UserID}; 384 385 # get draft data 386 my $FormDraft = $Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftGet( 387 FormDraftID => $Param{FormDraftID}, 388 UserID => $Param{UserID}, 389 ); 390 return if !IsHashRefWithData($FormDraft); 391 392 # Verify action. 393 my $Action = $Self->GetParam( Param => 'Action' ); 394 return if $FormDraft->{Action} ne $Action; 395 396 # add draft name to form data 397 $FormDraft->{FormData}->{FormDraftTitle} = $FormDraft->{Title}; 398 399 # create FormID and add to form data 400 my $FormID = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(); 401 $FormDraft->{FormData}->{FormID} = $FormID; 402 403 # set form data to param object, depending on type 404 KEY: 405 for my $Key ( sort keys %{ $FormDraft->{FormData} } ) { 406 my $Value = $FormDraft->{FormData}->{$Key} // ''; 407 408 # array value 409 if ( IsArrayRefWithData($Value) ) { 410 $Self->{Query}->param( 411 -name => $Key, 412 -values => $Value, 413 ); 414 next KEY; 415 } 416 417 # scalar value 418 $Self->{Query}->param( 419 -name => $Key, 420 -value => $Value, 421 ); 422 } 423 424 # add UploadCache data 425 my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); 426 for my $File ( @{ $FormDraft->{FileData} } ) { 427 return if !$UploadCacheObject->FormIDAddFile( 428 %{$File}, 429 FormID => $FormID, 430 ); 431 } 432 433 return $Param{FormDraftID}; 434} 435 436=head2 SaveFormDraft() 437 438Create or replace draft using data from param object and upload cache. 439Specified params can be overwritten if necessary. 440 441 my $FormDraftID = $ParamObject->SaveFormDraft( 442 UserID => 1 443 ObjectType => 'Ticket', 444 ObjectID => 123, 445 OverrideParams => { # optional, can contain strings and array references 446 Subaction => undef, 447 UserID => 1, 448 CustomParam => [ 1, 2, 3, ], 449 ... 450 }, 451 ); 452 453=cut 454 455sub SaveFormDraft { 456 my ( $Self, %Param ) = @_; 457 458 # check params 459 return if !$Param{UserID} || !$Param{ObjectType} || !IsInteger( $Param{ObjectID} ); 460 461 # gather necessary data for backend 462 my %MetaParams; 463 for my $Param (qw(Action FormDraftID FormDraftTitle FormID)) { 464 $MetaParams{$Param} = $Self->GetParam( 465 Param => $Param, 466 ); 467 } 468 return if !$MetaParams{Action}; 469 470 # determine session name param (SessionUseCookie = 0) for exclusion 471 my $SessionName = $Kernel::OM->Get('Kernel::Config')->Get('SessionName') || 'SessionID'; 472 473 # compile override list 474 my %OverrideParams = IsHashRefWithData( $Param{OverrideParams} ) ? %{ $Param{OverrideParams} } : (); 475 476 # these params must always be excluded for safety, they take precedence 477 for my $Name ( 478 qw(Action ChallengeToken FormID FormDraftID FormDraftTitle FormDraftAction LoadFormDraftID), 479 $SessionName 480 ) 481 { 482 $OverrideParams{$Name} = undef; 483 } 484 485 # Gather all params. 486 # Exclude, add or override by using OverrideParams if necessary. 487 my @ParamNames = $Self->GetParamNames(); 488 my %ParamSeen; 489 my %FormData; 490 PARAM: 491 for my $Param ( @ParamNames, sort keys %OverrideParams ) { 492 next PARAM if $ParamSeen{$Param}++; 493 my $Value; 494 495 # check for overrides first 496 if ( exists $OverrideParams{$Param} ) { 497 498 # allow only strings and array references as value 499 if ( 500 IsStringWithData( $OverrideParams{$Param} ) 501 || IsArrayRefWithData( $OverrideParams{$Param} ) 502 ) 503 { 504 $Value = $OverrideParams{$Param}; 505 } 506 507 # skip all other parameters (including those specified to be excluded by using 'undef') 508 else { 509 next PARAM; 510 } 511 } 512 513 # get other values from param object 514 if ( !defined $Value ) { 515 my @Values = $Self->GetArray( Param => $Param ); 516 next PARAM if !IsArrayRefWithData( \@Values ); 517 518 # store single occurances as string 519 if ( scalar @Values == 1 ) { 520 $Value = $Values[0]; 521 } 522 523 # store multiple occurances as array reference 524 else { 525 $Value = \@Values; 526 } 527 } 528 529 $FormData{$Param} = $Value; 530 } 531 532 # get files from upload cache 533 my @FileData = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDGetAllFilesData( 534 FormID => $MetaParams{FormID}, 535 ); 536 537 # prepare data to add or update draft 538 my %FormDraft = ( 539 FormData => \%FormData, 540 FileData => \@FileData, 541 FormDraftID => $MetaParams{FormDraftID}, 542 ObjectType => $Param{ObjectType}, 543 ObjectID => $Param{ObjectID}, 544 Action => $MetaParams{Action}, 545 Title => $MetaParams{FormDraftTitle}, 546 UserID => $Param{UserID}, 547 ); 548 549 # update draft 550 if ( $MetaParams{FormDraftID} ) { 551 return if !$Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftUpdate(%FormDraft); 552 return 1; 553 } 554 555 # create new draft 556 return if !$Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftAdd(%FormDraft); 557 return 1; 558} 559 5601; 561 562=head1 TERMS AND CONDITIONS 563 564This software is part of the OTRS project (L<https://otrs.org/>). 565 566This software comes with ABSOLUTELY NO WARRANTY. For details, see 567the enclosed file COPYING for license information (GPL). If you 568did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 569 570=cut 571