1<?php 2/** 3 * DokuWiki Plugin gtime (Action Component) 4 * 5 * @license Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 6 * @author Michael Kirchner <michael@hirnreck.de> 7 */ 8 9/* 10 * Copyright (C) Michael Kirchner <michael@hirnreck.de> 11 * 12 * This file is part of the GuardTime dokuwiki plugin. 13 * 14 * Licensed under the Apache License, Version 2.0 (the "License"); 15 * you may not use this file except in compliance with the License. 16 * You may obtain a copy of the License at 17 * 18 * http://www.apache.org/licenses/LICENSE-2.0 19 * 20 * Unless required by applicable law or agreed to in writing, software 21 * distributed under the License is distributed on an "AS IS" BASIS, 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 * See the License for the specific language governing permissions and 24 * limitations under the License. 25 */ 26 27 28 29// must be run within Dokuwiki 30if (!defined('DOKU_INC')) die(); 31 32if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 33if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 34if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 35 36// error_reporting(E_ALL); 37// ini_set('display_errors', 'On'); 38 39require_once DOKU_PLUGIN.'action.php'; 40require_once(DOKU_INC.'inc/pluginutils.php'); 41 42 43class action_plugin_gtime_timestamp extends DokuWiki_Action_Plugin { 44 45 // todo: 46 // Button to download zip with file and timestamp data 47 // include setup variables into dokuwiki setup 48 49 public function register(Doku_Event_Handler &$controller) { 50 $Timestamper_URL=$this->getConf('Timestamper_URL'); 51 //The URL of the service for creating timestamps. 52 53 $Extender_URL=$this->getConf('Extender_URL'); 54 //The URL of the service for connecting timestamps to Integrity Codes. 55 56 $Publications_URL=$this->getConf('Publications_URL'); 57 //The URL where the GuardTime publications file can be downloaded. 58 59 $Publications_TTL=$this->getConf('Publications_TTL'); 60 //Time of life for Publicationsfile in minutes 61 62 $controller->register_hook('IO_WIKIPAGE_WRITE', 'AFTER', $this, 'handle_io_wikipage_write'); 63 $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'handle_tpl_act_render'); 64 } 65 66 67 /** 68 * Logs exception and stack trace to error log. 69 * 70 * @param $exception the exception to log 71 * @return void 72 */ 73 function gt_log_exception($exception) { 74 75 error_log($exception->getMessage()); 76 error_log($exception->getTraceAsString()); 77 } 78 79 80 81 /** 82 * Gets publications file. 83 * 84 * This method uses gt_publications_file to cache the file for 85 * publications_ttl minutes in tmp. 86 * 87 * @return GTPublicationsFile cached or freshly downloaded publications file 88 */ 89 90 91 function gt_publications_file() { 92 93 $Publications_URL=$this->getConf('Publications_URL'); 94 //The URL where the GuardTime publications file can be downloaded. 95 96 97 if (empty($Publications_URL)) { 98 return null; 99 } 100 101 $Publications_TTL=$this->getConf('Publications_TTL'); 102 //Time of life for Publicationsfile in minutes 103 104 if (!is_integer($Publications_TTL)) { 105 $ttl = 60; 106 } 107 108 $Publications_TTL = $Publications_TTL * 60 ; 109 110 111 $filename = DOKU_INC.'/data/tmp/publications.bin'; 112 113 if (file_exists($filename)) { 114 $filetime = filemtime($filename); 115 } 116 117 if (empty($filetime) || ($filetime + $Publications_TTL) < time()) { 118 119 120 try { 121 122 $newFile = GTHttpClient::getPublicationsFile($Publications_URL); 123 $result = $newFile->verify(); 124 125 if ($result->isValid()) { 126 $newFile->save($filename); 127 return $newFile; 128 } else { 129 return null; 130 131 } 132 } catch (GTException $e) { 133 gt_log_exception($e); 134 } 135 136 } 137 138 try { 139 return GTPublicationsFile::load($filename); 140 141 } catch (GTException $e) { 142 gt_log_exception($e); 143 144 } 145 } 146 147 148 /** 149 * Extends GuardTime timestamp. 150 * 151 * @param $timestamp 152 * @param $INFO 153 * @return void 154 */ 155 function gt_extend($timestamp,$INFO) { 156 157 $Publications_URL=$this->getConf('Publications_URL'); 158 //The URL where the GuardTime publications file can be downloaded. 159 160 if ($timestamp->isExtended()) { 161 return; 162 } 163 164 $Extender_URL=$this->getConf('Extender_URL'); 165 166 if (empty($Extender_URL)) { 167 return; 168 } 169 170 $publicationsFile = $this->gt_publications_file(); 171 172 173 if (empty($publicationsFile)) { 174 return; 175 } 176 177 if (!$timestamp->isExtendable($publicationsFile)) { 178 return; 179 // What does that mean? only PKI signed? 180 } 181 182 try { 183 184 GTHttpClient::extend($timestamp, $Extender_URL); 185 186 } catch (GTException $e) { 187 $this->gt_log_exception($e); 188 } 189 190 if ($timestamp->isExtended()) { 191 $timestamp->save($INFO['filepath'].'.gtts'); 192 } 193 } 194 195 196 197 /** 198 * Main Handle for Timestamping. 199 * 200 * @param $event 201 * @param $param 202 * @return void 203 */ 204 public function handle_io_wikipage_write(Doku_Event &$event, $param) { 205 206 // This routine is called when a file is written in pages or attic. 207 // Whenever we do this, a timestamp is created and stored with the file with extension gtts. 208 // (The exception is when the file is deleted with empty content, then no new timestamp is 209 // needed and the old timestampfile is deleted.) 210 // While this is also done on the aktual pages, it is also done on the attic. This is 211 // actually more important, as the attic is always kept. Be aware that when using compression 212 // the timestamp will be on the compressed files! 213 214 215 require_once(DOKU_INC.'lib/plugins/gtime/gtlib/guardtime.php'); 216 217 $Timestamper_URL=$this->getConf('Timestamper_URL'); 218 //The URL of the service for creating timestamps. 219 220 221 $data = $event->data; 222 223 // For debugging: 224 // $finfo = ""; 225 // if ($data[0][1]=="") $finfo = " Empty"; 226 // if (!file_exists($data[0][0])) { $finfo .= " New"; } else { $finfo .= " Exists" ;}; 227 // error_log("Filename: ".$data[0][0].$finfo." NS: ".$data[1]." ID: ".$data[2]." Rev: ".$data[3]); 228 229 if ($data[0][1]=="") { 230 // No content means the file is deleted 231 // so the Timestamp should be deleted too, otherwise we clutter the dir 232 unlink ($data[0][0].'.gtts'); 233 } 234 else { 235 236 // hash the data file (not content!) using the default algorithm (currently SHA-256) 237 $dataHash = new GTDataHash(GTHashAlgorithm::getByName('DEFAULT')); 238 $dataHash->updateFile($data[0][0]); 239 $dataHash->close(); 240 241 242 // request a timestamp for the hash value 243 try { 244 $timestamp = GTHttpClient::create($dataHash, $Timestamper_URL); 245 } catch (GTException $e) { 246 $this->gt_log_exception($e); 247 } 248 249 250 // save the timestamp for archiving it along with the data file 251 $timestamp->save($data[0][0].'.gtts'); 252 } 253 254 255 } 256 257 258 /** 259 * Handle for Output of Timestamping information at the page bottom. 260 * 261 * @param $event 262 * @param $param 263 * @return void 264 */ 265 266 public function handle_tpl_act_render(Doku_Event &$event, $param) { 267 global $ID; 268 global $REV; 269 global $INFO; 270 global $conf; 271 global $ACT; 272 global $mylang; 273 274 $this->setupLocale(); 275 $mylang=$this->lang; 276 277 require_once(DOKU_INC.'lib/plugins/gtime/gtlib/guardtime.php'); 278 279 $Publications_URL=$this->getConf('Publications_URL'); 280 //The URL where the GuardTime publications file can be downloaded. 281 282 283 //only when show action, page exists and its ID matches with settings 284 if ($ACT == 'show' && $INFO['exists'] ){ 285 286 if (file_exists($INFO['filepath'].".gtts") ) { 287 // load the timestamp 288 // error_log("Info-Filepath: ".$INFO['filepath']); 289 $timestamp = GTTimestamp::load($INFO['filepath'].".gtts"); 290 291 // Extend timestamp 292 if (!$timestamp->isExtended()) { 293 $this->gt_extend($timestamp,$INFO); 294 } 295 296 // hash the data file using the algorithm from the timestamp 297 $dataHash = new GTDataHash($timestamp->getHashAlgorithm()); 298 $dataHash->updateFile($INFO['filepath']); 299 $dataHash->close(); 300 301 // load the publications file 302 // we assume it's trusted local copy and skip verification here 303 $publicationsFile = $this->gt_publications_file(); 304 305 try { 306 // verify against the publications file 307 $result = GTHttpClient::verify($timestamp, $dataHash, null, $publicationsFile); 308 309 } catch (GTException $e) { 310 $this->gt_log_exception($e); 311 } 312 313 if ($result->isValid()) { 314 $invalid_warning =""; 315 } else { 316 $invalid_warning ='<span class="guardtime_timestamp_invalid">'.$mylang['Validation_failed'].'</span>'; 317 } 318 319 //This, by some reason does not work with caching 320 $link_to_download = exportlink($ID,"gtime",$REV==true ? "rev=$REV,purge=true": "purge=true"); 321 322 323 324 $valid_summary=<<<EOT 325 <div class="guardtime_timestamp_summary"> 326 327 328 <a href="http://www.guardtime.com" title="Timestamped by GuardTime"><img 329 src="lib/plugins/gtime/assets/guardtime.png" alt="Timestamped by GuardTime"/></a> 330 331 <span> 332 $invalid_warning 333 {$mylang['pagesign']} {$timestamp->getProperty(GTTimestamp::REGISTERED_TIME)} 334 <a href="javascript:toggel_GT_Details();" class="guardtime_timestamp_buttons">{$mylang['Details_Button']}</a> 335 336 <a href=$link_to_download class="guardtime_timestamp_buttons">{$mylang['Download_Button']}</a> 337 </span> 338 339 <div class="clear"></div> 340 341 </div> 342 343EOT; 344 345 346 $valid_details=<<<EOT 347 <div class="guardtime_timestamp_details" id="guardtime_timestamp_details" style="display: none;"> 348 <table cellpadding="0" cellspacing="0" border="0" class="guardtime_details"> 349 <tbody> 350 351 <tr> 352 <td>{$mylang['Registration_Time']}</td> 353 <td>{$timestamp->getProperty(GTTimestamp::REGISTERED_TIME)}</td> 354 </tr> 355 356 <tr> 357 <td>{$mylang['Integrity_Code']}</td> 358 <td>{$timestamp->getProperty(GTTimestamp::PUBLICATION)}</td> 359 </tr> 360 361 <tr> 362 <td>{$mylang['History_Checkpoint']}</td> 363 <td>{$timestamp->getProperty(GTTimestamp::PUBLICATION_TIME)}</td> 364 </tr> 365 366 <tr> 367 <td>{$mylang['Issuer_Name']}</td> 368 <td>{$timestamp->getProperty(GTTimestamp::ISSUER_NAME)}</td> 369 </tr> 370 371 <tr> 372 <td>{$mylang['Policy']}</td> 373 <td>{$timestamp->getProperty(GTTimestamp::POLICY_ID)}</td> 374 </tr> 375 376 <tr> 377 <td>{$mylang['Hash_Algorithm']}</td> 378 <td>{$timestamp->getHashAlgorithm()->getName()}</td> 379 </tr> 380 381 <tr> 382 <td>{$mylang['Message_Hash']}</td> 383 <td>{$timestamp->getProperty(GTTimestamp::HASHED_MESSAGE)}</td> 384 </tr> 385 386 <tr> 387 <td>{$mylang['Error_Code']}</td> 388 <td>{$result->getGtResult()->getErrorCode()}</td> 389 </tr> 390 391 <tr> 392 <td>{$mylang['Status_Code']}</td> 393 <td>{$result->getGtResult()->getErrorCode()}</td> 394 </tr> 395 396 </tbody> 397 </table> 398 </div> 399 400EOT; 401 402 } else { 403 404 $invalid_warning ='<span class="guardtime_timestamp_invalid">'.$mylang['Timestamp_missing'].'</span>'; 405 $valid_summary=<<<EOT 406 <div class="guardtime_timestamp_summary"> 407 <a href="http://www.guardtime.com" title="Timestamped by GuardTime"><img 408 src="lib/plugins/gtime/assets/guardtime.png" alt="Timestamped by GuardTime"/></a> 409 <span> 410 $invalid_warning 411 </span> 412 <div class="clear"></div> 413 </div> 414 415EOT; 416 $valid_details=<<<EOT 417EOT; 418 419 } 420 421 422 423 $valid_output=<<<EOT 424<div class="guardtime_timestamp"> 425 $valid_summary 426 $valid_details 427</div> 428 429EOT; 430 431 print $valid_output; 432 433 434 } 435 436 } 437 438} 439 440// vim:ts=4:sw=4:et: 441