Subversion Repositories oidplus

Rev

Rev 1363 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1359 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
namespace ViaThinkSoft\OIDplus;
21
 
22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
26
const BACKUP_RECOVERY_SPECIAL_TEST = false; // ONLY FOR TESTING BACKUP/RESTORE PROCEDURE DURING DEVELOPMENT
27
 
28
class OIDplusPageAdminDatabaseBackup extends OIDplusPagePluginAdmin
29
{
30
 
31
        /**
32
         * @param string $id
33
         * @param array $out
34
         * @param bool $handled
35
         * @return void
36
         * @throws OIDplusException
37
         */
38
        public function gui(string $id, array &$out, bool &$handled) {
39
                $ary = explode('$', $id);
40
                if (isset($ary[1])) {
41
                        $id = $ary[0];
42
                        $tab = $ary[1];
43
                } else {
44
                        $tab = 'export';
45
                }
46
 
47
                if ($id === 'oidplus:database_backup') {
48
                        $handled = true;
49
                        $out['title'] = _L('Database Backup');
50
                        $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : '';
51
 
52
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
53
                                throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as administrator.',OIDplus::gui()->link('oidplus:login$admin')), $out['title'], 401);
54
                        }
55
 
56
                        $out['text'] = '<noscript>';
57
                        $out['text'] .= '<p><font color="red">'._L('You need to enable JavaScript to use this feature.').'</font></p>';
58
                        $out['text'] .= '</noscript>';
59
 
60
                        $out['text'] .= '<br><div id="databaseBackupArea" style="visibility: hidden"><div id="databaseBackupTab" class="container" style="width:100%;">';
61
 
62
                        // ---------------- Tab control
63
                        $out['text'] .= OIDplus::gui()->tabBarStart();
64
                        $out['text'] .= OIDplus::gui()->tabBarElement('export', _L('Backup'), $tab === 'export');
65
                        $out['text'] .= OIDplus::gui()->tabBarElement('import', _L('Restore'), $tab === 'import');
66
                        $out['text'] .= OIDplus::gui()->tabBarEnd();
67
                        $out['text'] .= OIDplus::gui()->tabContentStart();
68
                        // ---------------- "Backup" tab
69
                        $tabcont = '';
70
                        $tabcont .= '<h2>'._L('Create database backup').'</h2>';
71
                        $tabcont .= '<form action="'.OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'download_backup.php" method="POST" target="_blank">';
72
 
73
                        $tabcont .= '<p>'._L('Download a database backup file with the following contents:').'</p>';
74
                        $tabcont .= '<p>';
75
                        $tabcont .= '<input type="checkbox" checked name="database_backup_export_objects" id="database_backup_export_objects"> <label for="database_backup_export_objects">'._L('Objects').'</label><br>';
76
                        $tabcont .= '<input type="checkbox" checked name="database_backup_export_ra" id="database_backup_export_ra"> <label for="database_backup_export_ra">'._L('Registration Authorities').'</label><br>';
77
                        $tabcont .= '<input type="checkbox" checked name="database_backup_export_config" id="database_backup_export_config"> <label for="database_backup_export_config">'._L('Configuration').'</label><br>';
78
                        $tabcont .= '<input type="checkbox" checked name="database_backup_export_log" id="database_backup_export_log"> <label for="database_backup_export_log">'._L('Log messages').'</label><br>';
79
                        $tabcont .= '<input type="checkbox" name="database_backup_export_pki" id="database_backup_export_pki"> <label for="database_backup_export_pki">'._L('Private key (Highly confidential!)').'</label><br>';
80
                        $tabcont .= '</p>';
81
 
82
                        $tabcont .= '<p>';
83
                        $tabcont .= '<input type="checkbox" name="database_backup_export_encrypt" id="database_backup_export_encrypt"> <label for="database_backup_export_encrypt">'._L('Encrypt backup file with the following password (optional)').':</label><br>';
84
                        $tabcont .= '<label style="margin-left:25px;width:160px" for="database_backup_export_password1">'._L('Password').':</label> <input type="password" name="database_backup_export_password1" id="database_backup_export_password1"><br>';
85
                        $tabcont .= '<label style="margin-left:25px;width:160px" for="database_backup_export_password2">'._L('Password (repeat)').':</label> <input type="password" name="database_backup_export_password2" id="database_backup_export_password2"><br>';
86
                        $tabcont .= '</p>';
87
 
88
                        $tabcont .= '<input type="submit" value="'._L('Download backup').'">';
89
 
90
                        $tabcont .= '<p><i>'._L('Attention: Some Database Management Systems (DBMS), OIDplus connectivity plugins, and OIDplus SQL Slang plugins might export and import data differently regarding NULL values, time zones, boolean values, Unicode characters, etc. Please use backup/restore with caution and consider testing the restore procedure on a staging environment first.').'</i></p>';
91
 
92
                        $tabcont .= '</form>';
93
                        $out['text'] .= OIDplus::gui()->tabContentPage('export', $tabcont, $tab === 'export');
94
                        // ---------------- "Restore" tab
95
 
96
                        $tabcont = '';
97
                        $tabcont .= '<h2>'._L('Restore database backup').'</h2>';
98
                        $tabcont .= '<form action="'.OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'restore_backup.php" method="POST" target="_blank" enctype="multipart/form-data">';
99
 
100
                        $tabcont .= '<p>'._L('Choose database backup file here').':<br><input type="file" name="userfile" value="" id="userfile"></p>';
101
 
102
                        $tabcont .= '<p>'._L('Restore the following contents from the backup file (existing data will be deleted):').'</p>';
103
                        $tabcont .= '<p>';
104
                        $tabcont .= '<input type="checkbox" checked name="database_backup_import_objects" id="database_backup_import_objects"> <label for="database_backup_import_objects">'._L('Objects').'</label><br>';
105
                        $tabcont .= '<input type="checkbox" checked name="database_backup_import_ra" id="database_backup_import_ra"> <label for="database_backup_import_ra">'._L('Registration Authorities').'</label><br>';
106
                        $tabcont .= '<input type="checkbox" checked name="database_backup_import_config" id="database_backup_import_config"> <label for="database_backup_import_config">'._L('Configuration').'</label><br>';
107
                        $tabcont .= '<input type="checkbox" checked name="database_backup_import_log" id="database_backup_import_log"> <label for="database_backup_import_log">'._L('Log messages').'</label><br>';
108
                        $tabcont .= '<input type="checkbox" name="database_backup_import_pki" id="database_backup_import_pki"> <label for="database_backup_import_pki">'._L('Private key').' *</label><br>';
109
                        $tabcont .= '</p>';
110
 
111
                        $tabcont .= '<p>';
112
                        $tabcont .= '<label for="database_backup_import_encrypt">'._L('In case the backup is encrypted, enter the decryption password here').':</label><br>';
113
                        $tabcont .= '<label style="margin-left:25px;width:160px" for="database_backup_import_password">'._L('Password').':</label> <input type="password" name="database_backup_import_password" id="database_backup_import_password"><br>';
114
                        $tabcont .= '</p>';
115
 
116
                        $tabcont .= '<input type="submit" value="'._L('Restore backup').'">';
117
 
118
                        $tabcont .= '<p>* <i>'._L('Attention: In case you are cloning a system, e.g. in order to create a staging environment, please DO NOT copy the private key, as this would cause two systems to have the same System ID.').'</i></p>';
119
 
120
 
121
                        $tabcont .= '</form>';
122
                        $out['text'] .= OIDplus::gui()->tabContentPage('import', $tabcont, $tab === 'import');
123
                        $out['text'] .= OIDplus::gui()->tabContentEnd();
124
                        // ---------------- Tab control END
125
 
126
                        $out['text'] .= '</div></div><script>$("#databaseBackupArea")[0].style.visibility = "visible";</script>';
127
                }
128
        }
129
 
130
        /**
131
         * @param array $json
132
         * @param string|null $ra_email
133
         * @param bool $nonjs
134
         * @param string $req_goto
135
         * @return bool
136
         * @throws OIDplusException
137
         */
138
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
139
                if (!OIDplus::authUtils()->isAdminLoggedIn()) return false;
140
 
141
                if (file_exists(__DIR__.'/img/main_icon16.png')) {
142
                        $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png';
143
                } else {
144
                        $tree_icon = null; // default icon (folder)
145
                }
146
 
147
                $json[] = array(
148
                        'id' => 'oidplus:database_backup',
149
                        'icon' => $tree_icon,
150
                        'text' => _L('Database Backup')
151
                );
152
 
153
                return true;
154
        }
155
 
156
        /**
157
         * @param string $request
158
         * @return array|false
159
         */
160
        public function tree_search(string $request) {
161
                return false;
162
        }
163
 
164
        /**
165
         * @param array $num_rows
166
         * @return string
167
         */
168
        private static function num_rows_list(array $num_rows): string {
169
                $ary2 = [];
170
                foreach ($num_rows as $table => $cnt) {
171
                        if ($cnt !== "n/a") $ary2[] = "$table=$cnt";
172
                }
173
                $out = implode(", ", $ary2);
174
 
175
                if ($out === '') $out = 'No tables selected';
176
                return $out;
177
        }
178
 
179
        /**
1363 daniel-mar 180
         * @param mixed|string|null $datetime
181
         * @return mixed|string|null
182
         */
183
        private static function fix_datetime_for_output($datetime) {
184
                if ($datetime === "0000-00-00 00:00:00") $datetime = null; // MySQL might use this as default instead of NULL... But SQL Server cannot read this.
185
 
186
                if (is_string($datetime) && (substr($datetime,4,1) !== '-')) {
187
                        // Let's hope PHP can convert the database language specific string to ymd
188
                        $time = @strtotime($datetime);
189
                        if ($time) {
190
                                $date = date('Y-m-d H:i:s', $time);
191
                                if ($date) {
192
                                        $datetime = $date;
193
                                }
194
                        }
195
                }
196
                return $datetime;
197
        }
198
 
199
        /**
1359 daniel-mar 200
         * @param bool $showReport
201
         * @param bool $export_objects
202
         * @param bool $export_ra
203
         * @param bool $export_config
204
         * @param bool $export_log
205
         * @param bool $export_pki
206
         * @return string
207
         * @throws OIDplusException
208
         * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
209
         */
210
        public static function createBackup(bool $showReport, bool $export_objects=true, bool $export_ra=true, bool $export_config=false, bool $export_log=false, bool $export_pki=false): string {
211
                $num_rows = [
212
                        "objects" => $export_objects ? 0 : "n/a",
213
                        "asn1id" => $export_objects ? 0 : "n/a",
214
                        "iri" => $export_objects ? 0 : "n/a",
215
                        "ra" => $export_ra ? 0 : "n/a",
216
                        "config" => $export_config ? 0 : "n/a",
217
                        "log" => $export_log ? 0 : "n/a",
218
                        "log_object" => $export_log ? 0 : "n/a",
219
                        "log_user" => $export_log ? 0 : "n/a",
220
                        "pki" => $export_pki ? 0 : "n/a"
221
                ];
222
 
223
                if (BACKUP_RECOVERY_SPECIAL_TEST) {
224
                        if ($export_objects) {
225
                                OIDplus::db()->query("delete from ###objects where id like '%_CLONE'");
226
                                OIDplus::db()->query("delete from ###asn1id where oid like '%_CLONE'");
227
                                OIDplus::db()->query("delete from ###iri where oid like '%_CLONE'");
228
                        }
229
                        if ($export_ra) {
230
                                OIDplus::db()->query("delete from ###ra where email like '%_CLONE'");
231
                        }
232
                        if ($export_config) {
233
                                OIDplus::db()->query("delete from ###config where name <> 'oidplus_private_key' and name <> 'oidplus_public_key' and name like '%_CLONE'");
234
                        }
235
                        if ($export_log) {
236
                                OIDplus::db()->query("delete from ###log where addr like '%_CLONE'");
237
                                OIDplus::db()->query("delete from ###log_object where object like '%_CLONE'");
238
                                OIDplus::db()->query("delete from ###log_user where username like '%_CLONE'");
239
                        }
240
                }
241
 
242
                // Backup objects (Tables objects, asn1id, iri)
243
                $objects = [];
244
                if ($export_objects) {
245
                        $res = OIDplus::db()->query("select * from ###objects order by id");
246
                        $rows = [];
247
                        while ($row = $res->fetch_array()) {
248
                                // Not all databases support multiple active rows, so we need to read it in a isolated loop
249
                                $rows[] = $row;
250
                        }
251
                        foreach ($rows as $row) {
252
                                $num_rows["objects"]++;
253
 
254
                                $asn1ids = [];
255
                                $res2 = OIDplus::db()->query("select * from ###asn1id where oid = ? order by name", array($row["id"]));
256
                                while ($row2 = $res2->fetch_array()) {
257
                                        $num_rows["asn1id"]++;
258
                                        $asn1ids[] = [
259
                                                "name" => $row2['name'],
1363 daniel-mar 260
                                                "standardized" => $row2['standardized'] ?? false,
261
                                                "well_known" => $row2['well_known'] ?? false,
1359 daniel-mar 262
                                        ];
263
                                }
264
 
265
                                $iris = [];
266
                                $res2 = OIDplus::db()->query("select * from ###iri where oid = ? order by name", array($row["id"]));
267
                                while ($row2 = $res2->fetch_array()) {
268
                                        $num_rows["iri"]++;
269
                                        $iris[] = [
270
                                                "name" => $row2['name'],
1363 daniel-mar 271
                                                "longarc" => $row2['longarc'] ?? false,
272
                                                "well_known" => $row2['well_known'] ?? false,
1359 daniel-mar 273
                                        ];
274
                                }
275
 
276
                                $objects[] = [
277
                                        "id" => $row["id"],
278
                                        "parent" => $row["parent"],
279
                                        "title" => $row["title"],
280
                                        "description" => $row["description"],
281
                                        "ra_email" => $row["ra_email"],
1363 daniel-mar 282
                                        "confidential" => $row["confidential"] ?? false,
283
                                        "created" => self::fix_datetime_for_output($row["created"]),
284
                                        "updated" => self::fix_datetime_for_output($row["updated"]),
1359 daniel-mar 285
                                        "comment" => $row["comment"],
286
                                        "asn1ids" => $asn1ids,
287
                                        "iris" => $iris
288
                                ];
289
                        }
290
                }
291
 
292
                // Backup RAs (Table ra)
293
                $ra = [];
294
                if ($export_ra) {
295
                        $res = OIDplus::db()->query("select * from ###ra order by email");
296
                        while ($row = $res->fetch_array()) {
297
                                $num_rows["ra"]++;
298
                                $ra[] = [
299
                                        "email" => $row["email"],
300
                                        "ra_name" => $row["ra_name"],
301
                                        "personal_name" => $row["personal_name"],
302
                                        "organization" => $row["organization"],
303
                                        "office" => $row["office"],
304
                                        "street" => $row["street"],
305
                                        "zip_town" => $row["zip_town"],
306
                                        "country" => $row["country"],
307
                                        "phone" => $row["phone"],
308
                                        "mobile" => $row["mobile"],
309
                                        "fax" => $row["fax"],
1363 daniel-mar 310
                                        "privacy" => $row["privacy"] ?? false,
1359 daniel-mar 311
                                        "authkey" => $row["authkey"],
1363 daniel-mar 312
                                        "registered" => self::fix_datetime_for_output($row["registered"]),
313
                                        "updated" => self::fix_datetime_for_output($row["updated"]),
314
                                        "last_login" => self::fix_datetime_for_output($row["last_login"])
1359 daniel-mar 315
                                ];
316
                        }
317
                }
318
 
319
                // Backup configuration (Table config)
320
                $config = [];
321
                if ($export_config) {
322
                        $res = OIDplus::db()->query("select * from ###config where name <> 'oidplus_private_key' and name <> 'oidplus_public_key' order by name");
323
                        while ($row = $res->fetch_array()) {
324
                                $num_rows["config"]++;
325
                                $config[] = [
326
                                        "name" => $row["name"],
327
                                        "value" => $row["value"],
328
                                        "description" => $row["description"],
1363 daniel-mar 329
                                        "protected" => $row["protected"] ?? false,
330
                                        "visible" => $row["visible"] ?? false
1359 daniel-mar 331
                                ];
332
                        }
333
                }
334
 
335
                // Backup logs (Tables log, log_object, log_user)
336
                $log = [];
337
                if ($export_log) {
338
                        $res = OIDplus::db()->query("select * from ###log order by id");
339
                        $rows = [];
340
                        while ($row = $res->fetch_array()) {
341
                                // Not all databases support multiple active rows, so we need to read it in a isolated loop
342
                                $rows[] = $row;
343
                        }
344
                        foreach ($rows as $row) {
345
                                $num_rows["log"]++;
346
 
347
                                $log_objects = [];
348
                                $res2 = OIDplus::db()->query("select * from ###log_object where log_id = ? order by id", array($row["id"]));
349
                                while ($row2 = $res2->fetch_array()) {
350
                                        $num_rows["log_object"]++;
351
                                        $log_objects[] = [
352
                                                "object" => $row2['object'],
1363 daniel-mar 353
                                                "severity" => $row2['severity'] ?? 0
1359 daniel-mar 354
                                        ];
355
                                }
356
 
357
                                $log_users = [];
358
                                $res2 = OIDplus::db()->query("select * from ###log_user where log_id = ? order by id", array($row["id"]));
359
                                while ($row2 = $res2->fetch_array()) {
360
                                        $num_rows["log_user"]++;
361
                                        $log_users[] = [
362
                                                "username" => $row2['username'],
1363 daniel-mar 363
                                                "severity" => $row2['severity'] ?? 0
1359 daniel-mar 364
                                        ];
365
                                }
366
 
367
                                $log[] = [
368
                                        "unix_ts" => $row["unix_ts"],
369
                                        "addr" => $row["addr"],
370
                                        "event" => $row["event"],
371
                                        "objects" => $log_objects,
372
                                        "users" => $log_users
373
                                ];
374
                        }
375
                }
376
 
377
                // Backup public/private key
378
                $pki = [];
379
                if ($export_pki) {
380
                        $num_rows["pki"]++;
381
                        $pki[] = [
382
                                "private_key" => OIDplus::getSystemPrivateKey(),
383
                                "public_key" => OIDplus::getSystemPublicKey()
384
                        ];
385
                }
386
 
387
                // Put everything together
388
                $json = [
389
                        "\$schema" => "urn:oid:1.3.6.1.4.1.37476.2.5.2.8.1.1",
390
                        "oidplus_backup" => [
391
                                "created" => date('Y-m-d H:i:s O'),
392
                                "origin_systemid" => ($sysid = OIDplus::getSystemId(false)) ? (int)$sysid : "unknown",
393
                                "dataset_count" => $num_rows
394
                        ],
395
                        "objects" => $objects,
396
                        "ra" => $ra,
397
                        "config" => $config,
398
                        "log" => $log,
399
                        "pki" => $pki
400
                ];
401
 
402
                // Done!
403
 
404
                $encoded_data = json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
1363 daniel-mar 405
                if ($encoded_data === false) {
406
                        // Some DBMS plugins might not output UTF-8 correctly. In my test case it was SQL Server on ADO/MSOLEDBSQL (where Unicode does not work in OIDplus for some unknown reason)
407
                        array_walk_recursive($json, function (&$value)
408
                        {
409
                                if (is_string($value)) $value = vts_utf8_encode($value);
410
                        });
411
                        $encoded_data = json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
412
                        if ($encoded_data === false) {
413
                                throw new OIDplusException(_L("%1 failed","json_encode"));
414
                        }
415
                }
1359 daniel-mar 416
 
417
                OIDplus::logger()->log("V2:[INFO]A", "Created backup: ".self::num_rows_list($num_rows));
418
 
419
                if ($showReport) {
420
                        echo "<h1>"._L('Backup done')."</h1>";
421
                        foreach ($num_rows as $table_name => $cnt) {
422
                                if ($cnt !== "n/a")  echo "<p>... $table_name: "._L('%1 datasets', $cnt)."</p>";
423
                        }
424
                        echo "<hr>";
425
                }
426
 
427
                return $encoded_data;
428
        }
429
 
430
        /**
431
         * @param bool $showReport
432
         * @param string $cont
433
         * @param bool $import_objects
434
         * @param bool $import_ra
435
         * @param bool $import_config
436
         * @param bool $import_log
437
         * @param bool $import_pki
438
         * @return void
439
         * @throws OIDplusException
440
         * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
441
         */
442
        public static function restoreBackup(bool $showReport, string $cont, bool $import_objects=true, bool $import_ra=true, bool $import_config=false, bool $import_log=false, bool $import_pki=false)/*: void*/ {
443
                $num_rows = [
444
                        "objects" => $import_objects ? 0 : "n/a",
445
                        "asn1id" => $import_objects ? 0 : "n/a",
446
                        "iri" => $import_objects ? 0 : "n/a",
447
                        "ra" => $import_ra ? 0 : "n/a",
448
                        "config" => $import_config ? 0 : "n/a",
449
                        "log" => $import_log ? 0 : "n/a",
450
                        "log_object" => $import_log ? 0 : "n/a",
451
                        "log_user" => $import_log ? 0 : "n/a",
452
                        "pki" => $import_pki ? 0 : "n/a"
453
                ];
454
 
455
                //$cont = @file_get_contents($backup_file);
456
                //if ($cont === false) throw new OIDplusException(_L("Could not read file from disk: %1", $backup_file));
457
                $json = @json_decode($cont,true);
458
                if ($json === false) throw new OIDplusException(_L("Could not decode JSON structure of backup file"));
459
 
460
                if (($json["\$schema"]??"") != "urn:oid:1.3.6.1.4.1.37476.2.5.2.8.1.1") {
461
                        throw new OIDplusException(_L("File cannot be restored, because it has a wrong file format (schema)"));
462
                }
463
 
464
                if ($import_objects) {
465
                        $tmp = $json["oidplus_backup"]["dataset_count"]["objects"] ?? "n/a";
466
                        if ($tmp === "n/a") {
467
                                throw new OIDplusException(_L('Backup cannot be restored, because you want to import "%1", but the file was not created with this data.',"objects"));
468
                        }
469
 
470
                        $cnt = count($json["objects"]??[]);
471
                        if ($tmp != $cnt) {
472
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"objects"));
473
                        }
474
 
475
                        $tmp = $json["oidplus_backup"]["dataset_count"]["asn1id"] ?? "n/a";
476
                        $cnt_asn1id = 0;
477
                        foreach (($json["objects"]??[]) as $row) {
478
                                $cnt_asn1id += count($row['asn1ids']??[]);
479
                        }
480
                        if ($tmp != $cnt_asn1id) {
481
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"asn1id"));
482
                        }
483
 
484
                        $tmp = $json["oidplus_backup"]["dataset_count"]["iri"] ?? "n/a";
485
                        $cnt_iri = 0;
486
                        foreach (($json["objects"]??[]) as $row) {
487
                                $cnt_iri += count($row['iris']??[]);
488
                        }
489
                        if ($tmp != $cnt_iri) {
490
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"iri"));
491
                        }
492
                }
493
 
494
                if ($import_ra) {
495
                        $tmp = $json["oidplus_backup"]["dataset_count"]["ra"] ?? "n/a";
496
                        if ($tmp === "n/a") {
497
                                throw new OIDplusException(_L('Backup cannot be restored, because you want to import "%1", but the file was not created with this data.',"ra"));
498
                        }
499
                        $cnt = count($json["ra"]??[]);
500
                        if ($tmp != $cnt) {
501
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"ra"));
502
                        }
503
                }
504
 
505
                if ($import_config) {
506
                        $tmp = $json["oidplus_backup"]["dataset_count"]["config"] ?? "n/a";
507
                        if ($tmp === "n/a") {
508
                                throw new OIDplusException(_L('Backup cannot be restored, because you want to import "%1", but the file was not created with this data.',"config"));
509
                        }
510
                        $cnt = count($json["config"]??[]);
511
                        if ($tmp != $cnt) {
512
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"config"));
513
                        }
514
                }
515
 
516
                if ($import_log) {
517
                        $tmp = $json["oidplus_backup"]["dataset_count"]["log"] ?? "n/a";
518
                        if ($tmp === "n/a") {
519
                                throw new OIDplusException(_L('Backup cannot be restored, because you want to import "%1", but the file was not created with this data.',"log"));
520
                        }
521
 
522
                        $cnt = count($json["log"]??[]);
523
                        if ($tmp != $cnt) {
524
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"log"));
525
                        }
526
 
527
                        $tmp = $json["oidplus_backup"]["dataset_count"]["log_object"] ?? "n/a";
528
                        $cnt_objects = 0;
529
                        foreach (($json["log"]??[]) as $row) {
530
                                $cnt_objects += count($row['objects']??[]);
531
                        }
532
                        if ($tmp != $cnt_objects) {
533
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"log_object"));
534
                        }
535
 
536
                        $tmp = $json["oidplus_backup"]["dataset_count"]["log_user"] ?? "n/a";
537
                        $cnt_users = 0;
538
                        foreach (($json["log"]??[]) as $row) {
539
                                $cnt_users += count($row['users']??[]);
540
                        }
541
                        if ($tmp != $cnt_users) {
542
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"log_user"));
543
                        }
544
                }
545
 
546
                if ($import_pki) {
547
                        $tmp = $json["oidplus_backup"]["dataset_count"]["pki"] ?? "n/a";
548
                        if ($tmp === "n/a") {
549
                                throw new OIDplusException(_L('Backup cannot be restored, because you want to import "%1", but the file was not created with this data.',"pki"));
550
                        }
551
                        if (($tmp !== 0) && ($tmp !== 1)) {
552
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" is invalid',"pki"));
553
                        }
554
                        $cnt = count($json["pki"]??[]);
555
                        if ($tmp != $cnt) {
556
                                throw new OIDplusException(_L('Backup cannot be restored, because the number of "%1" does not match',"pki"));
557
                        }
558
                }
559
 
1363 daniel-mar 560
                if (OIDplus::db()->getSlang()->id() == 'mssql') {
561
                        // MSSQL: Try to find out if the other system created in YMD format
562
                        $has_ymd_format = false;
563
                        foreach (($json["objects"]??[]) as $row) {
564
                                if (substr($row["created"]??'',4,1) === '-') $has_ymd_format = true;
565
                                if (substr($row["updated"]??'',4,1) === '-') $has_ymd_format = true;
566
 
567
                        }
568
                        foreach (($json["ra"]??[]) as $row) {
569
                                if (substr($row["registered"]??'',4,1) === '-') $has_ymd_format = true;
570
                                if (substr($row["updated"]??'',4,1) === '-') $has_ymd_format = true;
571
                                if (substr($row["last_login"]??'',4,1) === '-') $has_ymd_format = true;
572
                        }
573
                        if ($has_ymd_format) {
574
                                OIDplus::db()->query("SET DATEFORMAT ymd;");
575
                        }
576
 
577
                        // Convert "0000-00-00 00:00:00" (MySQL) to NULL
578
                        if (isset($json["objects"])) {
579
                                foreach ($json["objects"] as &$row) {
580
                                        if ($row["created"] === "0000-00-00 00:00:00") $row["created"] = null;
581
                                        if ($row["updated"] === "0000-00-00 00:00:00") $row["updated"] = null;
582
                                }
583
                                unset($row);
584
                        }
585
                        if (isset($json["ra"])) {
586
                                foreach ($json["ra"] as &$row) {
587
                                        if ($row["registered"] === "0000-00-00 00:00:00") $row["registered"] = null;
588
                                        if ($row["updated"] === "0000-00-00 00:00:00") $row["updated"] = null;
589
                                        if ($row["last_login"] === "0000-00-00 00:00:00") $row["last_login"] = null;
590
                                }
591
                                unset($row);
592
                        }
593
                }
594
 
1359 daniel-mar 595
                if (OIDplus::db()->transaction_supported()) OIDplus::db()->transaction_begin();
596
                try {
597
 
598
                        // Restore objects (Tables objects, asn1id, iri)
599
                        if ($import_objects) {
600
                                if (!BACKUP_RECOVERY_SPECIAL_TEST) {
601
                                        OIDplus::db()->query("delete from ###objects");
602
                                        OIDplus::db()->query("delete from ###asn1id");
603
                                        OIDplus::db()->query("delete from ###iri");
604
                                }
605
                                foreach (($json["objects"]??[]) as $row) {
606
                                        if (BACKUP_RECOVERY_SPECIAL_TEST) {
607
                                                $row['id'] .= '_CLONE';
608
                                                if (substr($row['parent'], -1) != ':') $row['parent'] .= '_CLONE';
609
                                        }
610
 
611
                                        $num_rows["objects"]++;
612
                                        OIDplus::db()->query("insert into ###objects (id, parent, title, description, ra_email, confidential, created, updated, comment) values (?, ?, ?, ?, ?, ?, ?, ?, ?)",
613
                                                array($row["id"]??null,
614
                                                        $row["parent"]??null,
615
                                                        $row["title"]??null,
616
                                                        $row["description"]??null,
617
                                                        $row["ra_email"]??null,
1452 daniel-mar 618
                                                        (bool)($row["confidential"]??false),
619
                                                        $row["created"]??'1900-01-01 00:00:00',
620
                                                        $row["updated"]??'1900-01-01 00:00:00',
1359 daniel-mar 621
                                                        $row["comment"]??null)
622
                                        );
623
 
624
                                        foreach (($row["asn1ids"]??[]) as $row2) {
625
                                                $num_rows["asn1id"]++;
626
                                                OIDplus::db()->query("insert into ###asn1id (oid, name, standardized, well_known) values (?, ?, ?, ?)",
627
                                                        array($row["id"]??null, // sic: $row, not $row2
628
                                                                $row2["name"]??null,
1452 daniel-mar 629
                                                                (bool)($row2["standardized"]??false),
630
                                                                (bool)($row2["well_known"]??false))
1359 daniel-mar 631
                                                );
632
                                        }
633
 
634
                                        foreach (($row["iris"]??[]) as $row2) {
635
                                                $num_rows["iri"]++;
636
                                                OIDplus::db()->query("insert into ###iri (oid, name, longarc, well_known) values (?, ?, ?, ?)",
637
                                                        array($row["id"]??null, // sic: $row, not $row2
638
                                                                $row2["name"]??null,
1452 daniel-mar 639
                                                                (bool)($row2["longarc"]??false),
640
                                                                (bool)($row2["well_known"]??false))
1359 daniel-mar 641
                                                );
642
                                        }
643
                                }
1452 daniel-mar 644
                                OIDplus::db()->query("update ###objects set created = null where created = '1900-01-01 00:00:00';");
645
                                OIDplus::db()->query("update ###objects set updated = null where updated = '1900-01-01 00:00:00';");
1359 daniel-mar 646
                        }
647
 
648
                        // Restore RAs (Table ra)
649
                        if ($import_ra) {
650
                                if (!BACKUP_RECOVERY_SPECIAL_TEST) {
651
                                        OIDplus::db()->query("delete from ###ra");
652
                                }
653
                                foreach (($json["ra"]??[]) as $row) {
654
                                        if (BACKUP_RECOVERY_SPECIAL_TEST) {
655
                                                $row['email'] .= '_CLONE';
656
                                        }
657
 
658
                                        $num_rows["ra"]++;
659
                                        OIDplus::db()->query("insert into ###ra (email, ra_name, personal_name, organization, office, street, zip_town, country, phone, mobile, fax, privacy, authkey, registered, updated, last_login) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
660
                                                array($row["email"]??null,
661
                                                        $row["ra_name"]??null,
662
                                                        $row["personal_name"]??null,
663
                                                        $row["organization"]??null,
664
                                                        $row["office"]??null,
665
                                                        $row["street"]??null,
666
                                                        $row["zip_town"]??null,
667
                                                        $row["country"]??null,
668
                                                        $row["phone"]??null,
669
                                                        $row["mobile"]??null,
670
                                                        $row["fax"]??null,
1452 daniel-mar 671
                                                        (bool)($row["privacy"]??false),
1359 daniel-mar 672
                                                        $row["authkey"]??null,
1452 daniel-mar 673
                                                        $row["registered"]??'1900-01-01 00:00:00',
674
                                                        $row["updated"]??'1900-01-01 00:00:00',
675
                                                        $row["last_login"]??'1900-01-01 00:00:00')
1359 daniel-mar 676
                                        );
677
                                }
1452 daniel-mar 678
                                OIDplus::db()->query("update ###ra set registered = null where registered = '1900-01-01 00:00:00';");
679
                                OIDplus::db()->query("update ###ra set updated = null where updated = '1900-01-01 00:00:00';");
680
                                OIDplus::db()->query("update ###ra set last_login = null where last_login = '1900-01-01 00:00:00';");
1359 daniel-mar 681
                        }
682
 
683
                        // Restore configuration (Table config)
684
                        if ($import_config) {
685
                                if (!BACKUP_RECOVERY_SPECIAL_TEST) {
686
                                        OIDplus::db()->query("delete from ###config where name <> 'oidplus_private_key' and name <> 'oidplus_public_key'");
687
                                }
688
 
689
                                foreach (($json["config"]??[]) as $row) {
690
                                        if (BACKUP_RECOVERY_SPECIAL_TEST) {
691
                                                $row['name'] .= '_CLONE';
692
                                        }
693
 
694
                                        $num_rows["config"]++;
695
                                        OIDplus::db()->query("insert into ###config (name, value, description, protected, visible) values (?, ?, ?, ?, ?)",
696
                                                array($row["name"]??null,
697
                                                        $row["value"]??null,
698
                                                        $row["description"]??null,
1452 daniel-mar 699
                                                        (bool)($row["protected"]??false),
700
                                                        (bool)($row["visible"]??false))
1359 daniel-mar 701
                                        );
702
                                }
703
 
704
                        }
705
 
706
                        // Restore logs (Tables log, log_object, log_user)
707
                        if ($import_log) {
708
                                if (!BACKUP_RECOVERY_SPECIAL_TEST) {
709
                                        OIDplus::db()->query("delete from ###log");
710
                                        OIDplus::db()->query("delete from ###log_object");
711
                                        OIDplus::db()->query("delete from ###log_user");
712
                                }
713
                                foreach (($json["log"]??[]) as $row) {
714
                                        if (BACKUP_RECOVERY_SPECIAL_TEST) {
715
                                                $row['addr'] .= '_CLONE';
716
                                        }
717
 
718
                                        $num_rows["log"]++;
719
                                        OIDplus::db()->query("insert into ###log (unix_ts, addr, event) values (?, ?, ?)",
720
                                                array($row["unix_ts"]??null,
721
                                                        $row["addr"]??null,
722
                                                        $row["event"]??null)
723
                                        );
724
                                        $row['id'] = OIDplus::db()->insert_id();
725
                                        if ($row['id'] <= 0) {
726
                                                throw new OIDplusException(_L("Error during restore of backup: Cannot get insert_id of log entry!"));
727
                                        }
728
 
729
                                        foreach (($row["objects"]??[]) as $row2) {
730
                                                $num_rows["log_object"]++;
731
                                                OIDplus::db()->query("insert into ###log_object (log_id, object, severity) values (?, ?, ?)",
732
                                                        array($row["id"], // sic: $row, not $row2
733
                                                                $row2["object"]??null,
1363 daniel-mar 734
                                                                $row2["severity"]??0)
1359 daniel-mar 735
                                                );
736
                                        }
737
 
738
                                        foreach (($row["users"]??[]) as $row2) {
739
                                                $num_rows["log_user"]++;
740
                                                OIDplus::db()->query("insert into ###log_user (log_id, username, severity) values (?, ?, ?)",
741
                                                        array($row["id"], // sic: $row, not $row2
742
                                                                $row2["username"]??null,
1363 daniel-mar 743
                                                                $row2["severity"]??0)
1359 daniel-mar 744
                                                );
745
                                        }
746
                                }
747
                        }
748
 
749
                        // Restore public/private key
750
                        if ($import_pki) {
751
                                $privkey = $json["pki"][0]["private_key"] ?? null;
752
                                $pubkey = $json["pki"][0]["public_key"] ?? null;
753
                                if ($privkey && $pubkey) {
754
                                        $num_rows["pki"]++;
755
                                        // Note: If the private key is not encrypted, then it will be re-encrypted during the next call of OIDplus::getPkiStatus()
756
                                        OIDplus::db()->query("update ###config set value = ? where name = 'oidplus_private_key'", [$privkey]);
757
                                        OIDplus::db()->query("update ###config set value = ? where name = 'oidplus_public_key'", [$pubkey]);
758
                                        OIDplus::config()->clearCache();
759
                                }
760
                        }
761
 
762
                        // Done!
763
 
764
                        OIDplus::logger()->log("V2:[WARN]A", "EXECUTED OBJECT AND RA DATABASE BACKUP RECOVERY: ".self::num_rows_list($num_rows));
765
 
766
                        if (OIDplus::db()->transaction_supported()) OIDplus::db()->transaction_commit();
767
 
768
                        if ($showReport) {
769
                                echo "<h1>"._L('Backup restore done')."</h1>";
770
                                foreach ($num_rows as $table_name => $cnt) {
771
                                        if ($cnt !== "n/a") echo "<p>... $table_name: "._L('%1 datasets', $cnt)."</p>";
772
                                }
773
                                echo "<hr>";
774
                        }
775
 
776
                } catch (\Exception $e) {
777
                        if (OIDplus::db()->transaction_supported()) OIDplus::db()->transaction_rollback();
778
                        throw $e;
779
                }
780
        }
781
 
782
}