diff --git a/core/inlineimage.class.inc.php b/core/inlineimage.class.inc.php index a204ca493..4782efff4 100644 --- a/core/inlineimage.class.inc.php +++ b/core/inlineimage.class.inc.php @@ -16,7 +16,7 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see -define('INLINEIMAGE_DOWNLOAD_URL', 'pages/ajax.render.php?operation=download_inlineimage&id='); +define('INLINEIMAGE_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_inlineimage&id='); /** * Persistent classes (internal): store images referenced inside HTML formatted text fields diff --git a/core/ormdocument.class.inc.php b/core/ormdocument.class.inc.php index d447fc0fe..f33c00ae3 100644 --- a/core/ormdocument.class.inc.php +++ b/core/ormdocument.class.inc.php @@ -1,5 +1,5 @@ GetData()); - return utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400"; + return utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400"; } @@ -144,5 +144,45 @@ class ormDocument } return $bRet; } + + /** + * Downloads a document to the browser, either as 'inline' or 'attachment' + * + * @param WebPage $oPage The web page for the output + * @param string $sClass Class name of the object + * @param mixed $id Identifier of the object + * @param string $sAttCode Name of the attribute containing the document to download + * @param string $sContentDisposition Either 'inline' or 'attachment' + * @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file + * @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField + * @return none + */ + public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null) + { + try + { + $oObj = MetaModel::GetObject($sClass, $id, false, false); + if (!is_object($oObj)) + { + throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it"); + } + if (($sSecretField != null) && ($oObj->Get($sSecretField) != $sSecretValue)) + { + usleep(200); + throw new Exception("Invalid secret for class '$sClass' - the object does not exist or you are not allowed to view it"); + } + $oDocument = $oObj->Get($sAttCode); + if (is_object($oDocument)) + { + $oPage->TrashUnexpectedOutput(); + $oPage->SetContentType($oDocument->GetMimeType()); + //$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName()); + $oPage->add($oDocument->GetData()); + } + } + catch(Exception $e) + { + $oPage->p($e->getMessage()); + } + } } -?> diff --git a/datamodels/1.x/itop-attachments/main.attachments.php b/datamodels/1.x/itop-attachments/main.attachments.php index 75442a217..d5fef0837 100644 --- a/datamodels/1.x/itop-attachments/main.attachments.php +++ b/datamodels/1.x/itop-attachments/main.attachments.php @@ -282,7 +282,7 @@ EOF } else { - var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents'; + var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.document.php?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents'; $('#attachments').append('

'+data.msg+'

'); if($sIsDeleteEnabled) { @@ -313,7 +313,7 @@ EOF $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); - $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; + $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $oPage->add('

'.$sFileName.'

  
'); } @@ -340,7 +340,7 @@ EOF $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); - $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; + $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $oPage->add('

'.$sFileName.'

  
'); $oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."', false /* not an line image */]);"); } @@ -374,7 +374,7 @@ EOF $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); - $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; + $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $oPage->add('

'.$sFileName.'

  
'); } } diff --git a/datamodels/2.x/itop-attachments/main.attachments.php b/datamodels/2.x/itop-attachments/main.attachments.php index 1458bddd5..394febd32 100755 --- a/datamodels/2.x/itop-attachments/main.attachments.php +++ b/datamodels/2.x/itop-attachments/main.attachments.php @@ -16,7 +16,7 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see -define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.render.php?operation=download_document&class=Attachment&field=contents&id='); +define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id='); class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension { diff --git a/js/simple_graph.js b/js/simple_graph.js index 447bf5e9f..8d520b2e6 100644 --- a/js/simple_graph.js +++ b/js/simple_graph.js @@ -857,7 +857,7 @@ $(function() jTab.find('span').html(sTabText+' '); } $.post(sUrl, oParams, function(data) { - var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&field=contents&id='+data.att_id; + var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id='+data.att_id; var sIcon = GetAbsoluteUrlModulesRoot()+'itop-attachments/icons/pdf.png'; if (jTab != null) { diff --git a/pages/ajax.document.php b/pages/ajax.document.php new file mode 100644 index 000000000..64358aac1 --- /dev/null +++ b/pages/ajax.document.php @@ -0,0 +1,106 @@ + + + +/** + * Handles various ajax requests + * + * @copyright Copyright (C) 2010-2016 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +require_once('../approot.inc.php'); +require_once(APPROOT.'application/utils.inc.php'); + + +if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER) && (strlen($_SERVER['HTTP_IF_MODIFIED_SINCE']) > 0)) +{ + // The content is garanteed to be unmodified since the URL includes a signature based on the contents of the document + header('not modified', true, 304); + exit; +} + +try +{ + require_once(APPROOT.'/application/application.inc.php'); + require_once(APPROOT.'/application/webpage.class.inc.php'); + require_once(APPROOT.'/application/ajaxwebpage.class.inc.php'); + require_once(APPROOT.'/application/startup.inc.php'); + + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); + LoginWebPage::DoLoginEx(null /* any portal */, false); + + $oPage = new ajax_page(""); + $oPage->no_cache(); + + $operation = utils::ReadParam('operation', ''); + $sClass = utils::ReadParam('class', 'MissingAjaxParam', false, 'class'); + + switch($operation) + { + case 'download_document': + $id = utils::ReadParam('id', ''); + $sField = utils::ReadParam('field', ''); + if ($sClass == 'Attachment') + { + $iCacheSec = 31556926; // One year ahead: an attachment cannot change + } + else + { + $iCacheSec = (int)utils::ReadParam('cache', 0); + } + if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField)) + { + ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'attachment'); + if ($iCacheSec > 0) + { + $oPage->add_header("Expires: "); // Reset the value set in ajax_page + $oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec"); + $oPage->add_header("Pragma: cache"); // Reset the value set .... where ? + $oPage->add_header("Last-Modified: Wed, 15 Jun 2015 13:21:15 GMT"); // An arbitrary date in the past is ok + } + } + break; + + case 'download_inlineimage': + $id = utils::ReadParam('id', ''); + $sSecret = utils::ReadParam('s', ''); + $iCacheSec = 31556926; // One year ahead: an inline image cannot change + if (!empty($id) && !empty($sSecret)) + { + ormDocument::DownloadDocument($oPage, 'InlineImage', $id, 'contents', 'attachment', 'secret', $sSecret); + $oPage->add_header("Expires: "); // Reset the value set in ajax_page + $oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec"); + $oPage->add_header("Pragma: cache"); // Reset the value set .... where ? + $oPage->add_header("Last-Modified: Wed, 15 Jun 2016 13:21:15 GMT"); // An arbitrary date in the past is ok + } + break; + + default: + $oPage->p("Invalid query."); + } + + $oPage->output(); +} +catch (Exception $e) +{ + // note: transform to cope with XSS attacks + echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8'); + IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString()); +} + diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 94caa8be0..76debff93 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -772,22 +772,7 @@ try $sField = utils::ReadParam('field', ''); if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField)) { - DownloadDocument($oPage, $sClass, $id, $sField, 'inline'); - } - break; - - case 'download_document': - $id = utils::ReadParam('id', ''); - $sField = utils::ReadParam('field', ''); - $iCacheSec = (int) utils::ReadParam('cache', 0); - if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField)) - { - DownloadDocument($oPage, $sClass, $id, $sField, 'attachment'); - if ($iCacheSec > 0) - { - $oPage->add_header("Expires: "); // Reset the value set in ajax_page - $oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec"); - } + ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'inline'); } break; @@ -2433,21 +2418,6 @@ EOF $oPage->add(""); break; - case 'download_inlineimage': - $id = utils::ReadParam('id', ''); - $sSecret = utils::ReadParam('s', ''); - $iCacheSec = (int) utils::ReadParam('cache', 0); - if (!empty($id) && !empty($sSecret)) - { - DownloadDocument($oPage, 'InlineImage', $id, 'contents', 'attachment', 'secret', $sSecret); - if ($iCacheSec > 0) - { - $oPage->add_header("Expires: "); // Reset the value set in ajax_page - $oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec"); - } - } - break; - case 'custom_fields_update': $oPage->SetContentType('application/json'); $sAttCode = utils::ReadParam('attcode', ''); @@ -2489,47 +2459,3 @@ catch (Exception $e) echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8'); IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString()); } - - - -/** - * Downloads a document to the browser, either as 'inline' or 'attachment' - * - * @param WebPage $oPage The web page for the output - * @param string $sClass Class name of the object - * @param mixed $id Identifier of the object - * @param string $sAttCode Name of the attribute containing the document to download - * @param string $sContentDisposition Either 'inline' or 'attachment' - * @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file - * @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField - * @return none - */ -function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null) -{ - try - { - $oObj = MetaModel::GetObject($sClass, $id, false, false); - if (!is_object($oObj)) - { - throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it"); - } - if (($sSecretField != null) && ($oObj->Get($sSecretField) != $sSecretValue)) - { - usleep(200); - throw new Exception("Invalid secret for class '$sClass' - the object does not exist or you are not allowed to view it"); - } - $oDocument = $oObj->Get($sAttCode); - if (is_object($oDocument)) - { - $oPage->TrashUnexpectedOutput(); - $oPage->SetContentType($oDocument->GetMimeType()); - $oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName()); - $oPage->add($oDocument->GetData()); - } - } - catch(Exception $e) - { - $oPage->p($e->getMessage()); - } -} -?>