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