annotate marccore.php @ 3:5c8c4fa95803 draft

Added support for transfer chaining and some bugfixes
author Ivo Smits <Ivo@UCIS.nl>
date Mon, 17 Nov 2014 01:19:05 +0100
parents 3ac7bd7495fd
children c642254dc9ee
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
1 <?php
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
2 if (!function_exists('hex2bin')) { function hex2bin($h) { return pack("H*", $h); } }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
3
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
4 class MARCUpdate implements ArrayAccess {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
5 private $_version, $_key, $_serial, $_label, $_extensions, $_value = FALSE, $_valueoffset = -1, $_message, $_tags = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
6 public function __get($name) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
7 switch (strtolower($name)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
8 case 'version': return $this->_version;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
9 case 'key': return $this->_key;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
10 case 'serial': return $this->_serial;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
11 case 'label': return $this->_label;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
12 case 'expiration': return (isset($this->_extensions[4]) && strlen($this->_extensions[4]) >= 4) ? marc_decode_int32be($this->_extensions[4]) : NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
13 case 'transfer': return isset($this->_extensions[1]) ? $this->_extensions[1] : NULL;
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
14 case 'transferchain': return isset($this->_extensions[5]) ? $this->_extensions[5] : NULL;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
15 case 'value':
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
16 if ($this->_value === FALSE) $this->_value = self::DecodeValue($this->_message, $this->_valueoffset);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
17 return $this->_value;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
18 case 'updatemessage': return $this->_message;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
19 case 'tags': return $this->_tags; break;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
20 default: throw new Exception('Property '.$name.' does not exist');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
21 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
22 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
23 public function __set($name, $value) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
24 switch (strtolower($name)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
25 case 'version': case 'key': case 'serial': case 'label': case 'value': case 'expiration': case 'transfer': case 'updatemessage': case 'tags': throw new Exception('Property '.$name.' is read only');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
26 default: throw new Exception('Property '.$name.' does not exist or is read-only');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
27 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
28 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
29 public function __isset($name) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
30 switch (strtolower($name)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
31 case 'version': case 'key': case 'serial': case 'label': case 'value': case 'updatemessage': case 'tags': return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
32 case 'expiration': return isset($this->_extensions[4]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
33 case 'transfer': return isset($this->_extensions[1]);
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
34 case 'transferchain': return isset($this->_extensions[5]);
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
35 default: return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
36 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
37 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
38 public function __unset($name) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
39 switch (strtolower($name)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
40 case 'version': case 'key': case 'serial': case 'label': case 'value': case 'expiration': case 'transfer': case 'updatemessage': case 'tags': throw new Exception('Property '.$name.' is read only');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
41 default: throw new Exception('Property '.$name.' does not exist');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
42 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
43 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
44 public function offsetSet($offset, $value) { $this->__set($offset, $value); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
45 public function offsetExists($offset) { return $this->__isset($offset); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
46 public function offsetUnset($offset) { $this->__unset($offset); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
47 public function offsetGet($offset) { return $this->__get($offset); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
48
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
49 private function __construct() { }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
50 public static function Decode($data) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
51 $upd = new MARCUpdate();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
52 $upd->_message = $data;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
53 if (strlen($data) < 1 + NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
54 $upd->_version = ord($data[0]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
55 if ($upd->_version != 2) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
56 $upd->_key = substr($data, 1, NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
57 $i = NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES + 64 + 1;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
58 $l = strlen($data);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
59 if (!$l || $l < $i+4+1+1) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
60 $upd->_serial = marc_decode_int32be($data, $i); $i += 4;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
61 $labellen = ord($data[$i++]);
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
62 if ($l < $i+$labellen+1) return FALSE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
63 $upd->_label = substr($data, $i, $labellen); $i += $labellen;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
64 $upd->_extensions = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
65 for ($numext = ord($data[$i++]); $numext > 0; $numext--) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
66 if ($l < $i+1+2) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
67 $extid = ord($data[$i++]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
68 $extlen = marc_decode_int16be($data, $i); $i += 2;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
69 if ($l < $i + $extlen) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
70 $upd->_extensions[$extid] = substr($data, $i, $extlen);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
71 $i += $extlen;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
72 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
73 $upd->_valueoffset = $i;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
74 return $upd;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
75 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
76 public function Verify() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
77 $data = $this->_message;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
78 if (strlen($data) < 1 + NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
79 $version = ord($data[0]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
80 if ($version != 2) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
81 $key = substr($data, 1, NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
82 $data = substr($data, NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES + 1);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
83 $data = nacl_crypto_sign_ed25519_open($data, $key);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
84 return $data !== NULL && $data !== FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
85 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
86 public function Create($upd, $seckey, $current = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
87 if (strlen($seckey) == 32) nacl_crypto_sign_ed25519_keypair($seckey, $seckey);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
88 if (strlen($seckey) < 64) throw new Exception('Signing key is not valid');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
89 if (!isset($upd['key'])) $upd['key'] = substr($seckey, 32, 32);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
90 if ($upd['key'] != substr($seckey, 32, 32)) throw new Exception('Resource key is not valid');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
91 if (!isset($upd['label'])) throw new Exception('Resource label not set');
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
92 $upd['label'] = (string)$upd['label'];
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
93 if (strlen($upd['label']) > 255) throw new Exception('Resource label too long');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
94 if (!isset($upd['serial'])) $upd['serial'] = time();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
95 if ($current) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
96 if ($upd['serial'] <= $current['serial']) $upd['serial'] = $current['serial'] + 1;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
97 if (!self::CanImport($upd, $current)) throw new Exception('Can not update resource');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
98 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
99 if (isset($upd['transfer']) && (strlen($upd['transfer']) != 0 && strlen($upd['transfer']) != NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES)) throw new Exception('Transfer recipient key is not valid');
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
100 if ($current) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
101 unset($upd['transferchain']);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
102 if (isset($current['transferchain']) && ($chain = self::Decode($current['transferchain'])) && $chain->Verify() && $chain->serial >= time() - 365*24*60*60 && isset($chain->transfer) && ($chain->transfer == $upd['key'] || ($upd['key'] == $current['key'] && !strlen($chain->transfer)))) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
103 $upd['transferchain'] = $current['transferchain'];
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
104 } elseif (isset($current['transfer']) && isset($current['updatemessage']) && $current['serial'] >= time() - 365*24*60*60 && isset($current['transfer']) && ($current['transfer'] == $upd['key'] || !strlen($current['transfer']))) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
105 $upd['transferchain'] = $current['updatemessage'];
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
106 }
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
107 }
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
108 if (isset($upd['transfer'])) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
109 if (isset($upd['transferchain'])) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
110 $chain = self::Decode($upd['transferchain']);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
111 while ($chain && $chain->key == $upd['key']) $chain = ($chain->Verify() && $chain->serial >= time() - 365*24*60*60 && isset($chain->transferchain)) ? self::Decode($chain->transferchain) : NULL;
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
112 if ($chain && $chain->Verify() && $chain->serial >= time() - 365*24*60*60) $upd['transferchain'] = $chain->updatemessage; else unset($upd['transferchain']);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
113 }
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
114 if (isset($upd['value']) && !is_null($upd['value'])) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
115 $chain = array('label' => $upd['label'], 'serial' => $upd['serial'], 'key' => $upd['key'], 'transfer' => $upd['transfer']);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
116 if (isset($upd['expiration'])) $chain['expiration'] = $upd['expiration'];
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
117 if (isset($upd['transferchain'])) $chain['transferchain'] = $upd['transferchain'];
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
118 $chain = self::Create($chain, $seckey);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
119 if ($chain && strlen($chain->updatemessage) <= 0xffff) $upd['transferchain'] = $chain->updatemessage;
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
120 }
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
121 }
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
122 $data = marc_encode_int32be($upd['serial']);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
123 $data .= chr(strlen($upd['label'])).$upd['label'];
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
124 $value = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
125 if (isset($upd->_extensions)) foreach ($upd->_extensions as $identifier => $item) $value[$identifier] = $item;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
126 if (isset($upd['transfer'])) $value[1] = $upd['transfer'];
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
127 if (isset($upd['expiration'])) $value[4] = marc_encode_int32be($upd['expiration']);
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
128 if (isset($upd['transferchain'])) $value[5] = $upd['transferchain'];
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
129 if (count($value) > 0xff) throw new Exception('Too many extensions used');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
130 $data .= chr(count($value));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
131 foreach ($value as $identifier => $item) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
132 $item = (string)$item;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
133 if (strlen($item) > 0xffff) throw new Exception('Extension data too big');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
134 $data .= chr($identifier).marc_encode_int16be(strlen($item)).$item;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
135 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
136 if (isset($upd['value'])) $data .= self::EncodeValue($upd['value']);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
137 $data = nacl_crypto_sign_ed25519($data, $seckey);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
138 if (!strlen($data)) throw new Exception('Failed to sign data');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
139 if (!strlen(nacl_crypto_sign_ed25519_open($data, $upd['key']))) throw new Exception('Key pair is not valid');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
140 $data = chr(2).$upd['key'].$data;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
141 return self::Decode($data);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
142 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
143 public function ToArray() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
144 $arr = array('version' => $this->version, 'key' => $this->key, 'serial' => $this->serial, 'label' => $this->label, 'value' => $this->value);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
145 if (isset($this->expiration)) $arr['expiration'] = $this->expiration;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
146 if (isset($this->transfer)) $arr['transfer'] = $this->transfer;
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
147 if (isset($this->transferchain)) $arr['transferchain'] = $this->transferchain;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
148 return $arr;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
149 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
150
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
151 private static function EncodeValue($data) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
152 if (is_null($data)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
153 return chr(0);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
154 } else if (is_scalar($data)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
155 return chr(1).(string)$data;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
156 } else if (is_array($data)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
157 $iscollection = TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
158 foreach ($data as $key => $value) if (!is_int($key) || $key < 0) $iscollection = FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
159 $ret = $iscollection ? chr(2) : chr(3);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
160 foreach ($data as $key => $value) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
161 if (!$iscollection) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
162 if (strlen($key) > 0xff) throw new Exception('Dictionary key is too long');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
163 $ret .= chr(strlen($key)).$key;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
164 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
165 $bytes = self::EncodeValue($value);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
166 $ret .= marc_encode_int32be(strlen($bytes)).$bytes;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
167 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
168 return $ret;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
169 } else {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
170 throw new Exception('Unable to encode type '.gettype($data));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
171 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
172 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
173 private static function DecodeValue($data, $offset = 0, $length = -1) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
174 if ($length == -1) $length = strlen($data) - $offset;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
175 if ($length < 0) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
176 if ($length == 0) return NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
177 $type = ord($data[$offset]); $offset++; $length--;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
178 switch ($type) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
179 case 0: return NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
180 case 1: return substr($data, $offset, $length);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
181 case 2:
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
182 $value = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
183 while ($length > 0) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
184 if (4 > $length) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
185 $len = marc_decode_int32be($data, $offset); $offset += 4; $length -= 4;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
186 if ($len > $length) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
187 $value[] = self::DecodeValue($data, $offset, $len); $offset += $len; $length -= $len;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
188 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
189 return $value;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
190 case 3:
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
191 $value = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
192 while ($length > 0) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
193 if (1 > $length) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
194 $len = ord($data[$offset]); $offset++; $length--;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
195 if ($len + 4 > $length) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
196 $key = substr($data, $offset, $len); $offset += $len; $length -= $len;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
197 $len = marc_decode_int32be($data, $offset); $offset += 4; $length -= 4;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
198 if ($len > $length) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
199 $value[$key] = self::DecodeValue($data, $offset, $len); $offset += $len; $length -= $len;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
200 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
201 return $value;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
202 default: throw new Exception('Unsupported type code '.$type);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
203 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
204 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
205 public static function CanImport($nw, $cu = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
206 if (!$nw || !isset($nw['label'])) return FALSE;
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
207 if ($nw['serial'] > time() + 7*24*60*60) return FALSE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
208 if ($cu) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
209 if ($nw['label'] != $cu['label']) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
210 if ($cu['serial'] >= $nw['serial']) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
211 if ($cu['key'] == $nw['key']) return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
212 if (isset($cu['expiration']) && $cu['expiration'] < time()) return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
213 if ($cu['serial'] < time() - 365*24*60*60) return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
214 if (isset($cu['transfer']) && (!strlen($cu['transfer']) || $cu['transfer'] == $nw['key'])) return TRUE;
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
215 if (isset($nw['transferchain']) && ($chain = MARCUpdate::Decode($nw['transferchain'])) && $chain->Verify() && self::CanImport($nw, $chain) && self::CanImport($chain, $cu)) return TRUE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
216 return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
217 } else {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
218 if ($nw['serial'] < time() - 365*24*60*60) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
219 if (isset($nw['expiration']) && $nw['expiration'] < time()) return FALSE;
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
220 return TRUE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
221 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
222 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
223 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
224 abstract class MARCDatabase {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
225 private $_importResourceFilterCallbacks = array(), $_importResourceCallbacks = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
226 public abstract function GetResource($label);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
227 protected abstract function ImportInternal($resource);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
228 public abstract function GetResources($labelprefix = NULL, $mints = NULL);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
229 public abstract function DeleteResource($label);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
230 public function Import($resource, $force = FALSE) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
231 if (is_string($resource)) $resource = MARCUpdate::Decode($resource);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
232 if (!$resource) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
233 $current = $this->GetResource($resource['label']);
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
234 if (!$force && !$this->CanImportResource($resource, $current)) return FALSE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
235 if (!$resource->Verify()) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
236 if (!$this->ImportInternal($resource)) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
237 $this->ResourceImported($resource);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
238 return $resource;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
239 }
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
240 protected function CanImportResource($resource, $current) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
241 foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE;
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
242 if (!MARCUpdate::CanImport($resource, $current)) return FALSE;
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
243 return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
244 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
245 protected function ResourceImported($resource) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
246 foreach ($this->_importResourceCallbacks as $callback) call_user_func($callback, $this, $resource);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
247 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
248 public function RegisterResourceFilterCallback($callback) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
249 $this->_importResourceFilterCallbacks[] = $callback;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
250 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
251 public function RegisterResourceImportCallback($callback) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
252 $this->_importResourceCallbacks[] = $callback;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
253 }
3
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
254 public function UpdateResource($resource, $seckey, $force = FALSE) {
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
255 if (is_a($resource, 'MARCUpdate')) $resource = $resource->ToArray();
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
256 if (!$force) unset($resource['serial'], $resource['key']);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
257 $res = MARCUpdate::Create($resource, $seckey, $force ? NULL : $this->GetResource($resource['label']));
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
258 if (!$res) return FALSE;
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
259 return $this->Import($res, $force);
5c8c4fa95803 Added support for transfer chaining and some bugfixes
Ivo Smits <Ivo@UCIS.nl>
parents: 0
diff changeset
260 }
0
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
261 public function SyncHTTP($server, $options = array()) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
262 $log = isset($options['log']) ? $options['log'] : TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
263 $result = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
264 $method = isset($options['method']) ? $options['method'] : 'PUT';
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
265 if ($method != 'POST' && $method != 'PUT' && $method != 'GET') throw new Exception('Unsupported method '.$method);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
266 $result['exported'] = 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
267 $post = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
268 if ($method != 'GET' && (!isset($options['noexport']) || !$options['noexport'])) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
269 $post = $method == 'POST' ? array() : '';
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
270 $updates = $this->GetResources(NULL, isset($options['exporttimestamp']) ? $options['exporttimestamp'] : NULL);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
271 foreach ($updates as $update) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
272 if (!is_string($update)) $update = $update['updatemessage'];
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
273 if ($method == 'PUT') {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
274 $post .= marc_encode_int32be(strlen($update)).$update;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
275 } else {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
276 $post[] = 'update[]='.urlencode($update);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
277 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
278 $result['exported']++;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
279 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
280 $updates = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
281 if ($method == 'POST') $post = implode('&', $post);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
282 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
283 if ($log) echo "Sending ".strlen($post)." bytes... ";
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
284 $result['bytessent'] = strlen($post);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
285 $ch = curl_init();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
286 $timestamp = isset($options['importtimestamp']) ? intval($options['importtimestamp']) : 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
287 curl_setopt($ch, CURLOPT_URL, $server.'?version=3&get='.$timestamp);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
288 if ($method == 'PUT') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
289 else if ($method == 'POST') curl_setopt($ch, CURLOPT_POST, TRUE);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
290 if ($post != NULL) curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
291 $post = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
292 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
293 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
294 curl_setopt($ch, CURLOPT_TIMEOUT, 60);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
295 $response = curl_exec($ch);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
296 if ($response === FALSE) throw new Exception('HTTP request failed');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
297 if ($log) echo "received ".strlen($response)." bytes.\n";
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
298 $result['bytesreceived'] = strlen($response);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
299 if (($httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE)) != 200) throw new Exception('Received unexpected HTTP code '.$httpcode.' - '.(strlen($response)?var_export($response,TRUE):''));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
300 if (($contenttype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE)) != 'application/octet-stream') throw new Exception('Received unexpected content type '.$contenttype.' - '.(strlen($response)?var_export($response,TRUE):''));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
301 curl_close($ch);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
302 $i = 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
303 if ($i + 2 > strlen($response)) throw new Exception('Data has been truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
304 $version = ord($response[$i++]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
305 if ($version != 3) throw new Exception('Unsupported protocol version '.$version);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
306 for ($numext = ord($response[$i++]); $numext > 0; $numext--) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
307 if ($i + 3 > strlen($response)) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
308 $extid = ord($response[$i++]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
309 $extlen = marc_decode_int16be($response, $i); $i += 2;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
310 if ($i + $extlen > strlen($response)) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
311 switch ($extid) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
312 case 2:
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
313 if ($extlen >= 4 * 3) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
314 $result['remotereceived'] = marc_decode_int32be($response, $i + 0);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
315 $result['remoteimported'] = marc_decode_int32be($response, $i + 4);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
316 $result['remoteexported'] = marc_decode_int32be($response, $i + 8);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
317 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
318 break;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
319 case 3: if ($extlen >= 4) $result['importtimestamp'] = marc_decode_int32be($response, $i); break;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
320 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
321 $i += $extlen;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
322 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
323 if ($log) echo "Exported ".$result['exported']." updates of which ".(isset($result['remoteimported']) ? $result['remoteimported'] : 'none')." were imported.\n";
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
324 $result['updates'] = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
325 $result['imported'] = 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
326 $result['updatesreceived'] = 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
327 while ($i < strlen($response)) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
328 if ($i + 4 > strlen($response)) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
329 $len = marc_decode_int32be($response, $i); $i += 4;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
330 if ($i + $len > strlen($response)) throw new Exception('Truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
331 $result['updatesreceived']++;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
332 $value = substr($response, $i, $len); $i += $len;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
333 $res = MARCUpdate::Decode($value);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
334 $result['updates'][] = $res;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
335 if (!$this->Import($value)) continue;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
336 $result['imported']++;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
337 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
338 if ($log) echo "Imported ".$result['imported']." out of ".$result['updatesreceived']." updates.\n";
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
339 return $result;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
340 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
341 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
342 class MARCDatabaseFlatFile extends MARCDatabase {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
343 private $dbfile = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
344 private $resources = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
345 private $changed = FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
346 public function GetResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
347 if (!isset($this->resources[$label])) return NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
348 return $this->resources[$label]['update'];
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
349 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
350 public function DeleteResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
351 if (!isset($this->resources[$label])) return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
352 unset($this->resources[$label]);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
353 $this->changed = TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
354 return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
355 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
356 protected function ImportInternal($update) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
357 $this->resources[$update['label']] = array('timestamp' => time(), 'update' => $update);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
358 $this->changed = TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
359 return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
360 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
361 public function GetResources($labelprefix = NULL, $mints = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
362 return new MARCDatabaseFlatFileFilteredResourceIterator(new ArrayIterator($this->resources), $labelprefix, $mints);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
363 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
364 public function __construct($filename = FALSE) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
365 if ($filename) $this->Open($filename);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
366 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
367 private function OpenFile($filename) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
368 $this->Close();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
369 $this->dbfile = fopen($filename, 'c+');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
370 if (!$this->dbfile) throw new Exception('Could not open database file');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
371 if (!flock($this->dbfile, LOCK_EX)) throw new Exception('Could not lock database file');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
372 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
373 public function Open($filename) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
374 $this->OpenFile($filename);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
375 $this->resources = array();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
376 $this->changed = FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
377 rewind($this->dbfile);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
378 while (true) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
379 $data = fread($this->dbfile, 8);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
380 if (strlen($data) == 0) break;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
381 if (strlen($data) != 8) throw new Exception('Database truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
382 $ts = marc_decode_int32be($data, 0);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
383 $len = marc_decode_int32be($data, 4);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
384 $data = fread($this->dbfile, $len);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
385 if (strlen($data) != $len) throw new Exception('Database truncated');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
386 $res = MARCUpdate::Decode($data);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
387 if (!$res) continue;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
388 $this->resources[$res['label']] = array('timestamp' => $ts, 'update' => $res);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
389 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
390 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
391 public function Save() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
392 if (!$this->dbfile) throw new Exception('No database file is open');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
393 rewind($this->dbfile);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
394 ftruncate($this->dbfile, 0);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
395 foreach ($this->resources as $res) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
396 fwrite($this->dbfile, marc_encode_int32be($res['timestamp']));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
397 $u = (string)$res['update']['updatemessage'];
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
398 fwrite($this->dbfile, marc_encode_int32be(strlen($u)));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
399 fwrite($this->dbfile, $u);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
400 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
401 $this->changed = FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
402 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
403 public function SaveAs($filename) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
404 $this->OpenFile($filename);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
405 $this->Save();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
406 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
407 public function Close() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
408 if ($this->dbfile) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
409 flock($this->dbfile, LOCK_UN);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
410 fclose($this->dbfile);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
411 $this->dbfile = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
412 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
413 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
414 public function IsChanged() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
415 return $this->changed;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
416 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
417 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
418 class MARCDatabaseFlatFileFilteredResourceIterator implements Iterator {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
419 private $source, $labelprefixlength, $labelprefix, $mints;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
420 public function __construct($source, $labelprefix, $mints) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
421 $this->source = $source;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
422 $this->labelprefixlength = is_null($labelprefix) ? 0 : strlen($labelprefix);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
423 $this->labelprefix = $labelprefix;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
424 $this->mints = intval($mints);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
425 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
426 public function current() { $r = $this->source->current(); return $r ? $r['update'] : NULL; }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
427 public function key() { return $this->source->key(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
428 public function valid() { return $this->source->valid(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
429 public function next() { $this->source->next(); $this->findnext(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
430 public function rewind() { $this->source->rewind(); $this->findnext(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
431 private function findnext() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
432 while ($this->source->valid()) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
433 $c = $this->source->current();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
434 if ($c['timestamp'] >= $this->mints && (!$this->labelprefixlength || substr($c['update']['label'], 0, $this->labelprefixlength) == $this->labelprefix)) break;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
435 $this->source->next();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
436 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
437 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
438 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
439 class MARCDatabaseSQLite extends MARCDatabase {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
440 private $pdo = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
441 protected function DBPrepareStatement($query, $args = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
442 $stmt = $this->pdo->prepare($query);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
443 if (!is_array($args) && !is_null($args)) $args = array($args);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
444 $stmt->execute($args);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
445 return $stmt;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
446 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
447 private function DBUpdate($query, $args = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
448 $stmt = $this->DBPrepareStatement($query, $args);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
449 $cnt = $stmt->rowCount();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
450 $stmt->closeCursor();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
451 return $cnt;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
452 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
453 public function __construct($filename) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
454 $this->pdo = new PDO('sqlite:'.$filename);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
455 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
456 $this->DBUpdate('CREATE TABLE IF NOT EXISTS `resources` (`timestamp` INTEGER, `label` BLOB PRIMARY KEY, `update` BLOB)');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
457 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
458 public function GetResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
459 $stmt = $this->DBPrepareStatement('SELECT `update` FROM `resources` WHERE `label` = ?', $label);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
460 $res = $stmt->fetchColumn();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
461 $stmt->closeCursor();
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
462 if ($res === FALSE) return NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
463 return MARCUpdate::Decode($res);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
464 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
465 public function DeleteResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
466 return $this->DBUpdate('DELETE FROM `resources` WHERE `label` = ?', $label) != 0;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
467 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
468 protected function ImportInternal($update) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
469 $args = array('d' => $update['updatemessage'], 't' => time(), 'l' => $update['label']);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
470 if ($this->DBUpdate('UPDATE `resources` SET `update` = :d, `timestamp` = :t WHERE `label` = :l', $args) == 0)
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
471 $this->DBUpdate('INSERT OR IGNORE INTO `resources` (`label`, `update`, `timestamp`) VALUES (:l, :d, :t)', $args);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
472 return TRUE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
473 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
474 public function GetResources($labelprefix = NULL, $mints = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
475 $labelprefix = is_null($labelprefix) ? '' : (string)$labelprefix;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
476 return $this->GetResourcesFromQuery('SELECT `update` FROM `resources` WHERE `timestamp` >= ? AND SUBSTR(`label`, 1, ?) = ? ORDER BY `label`', array(intval($mints), strlen($labelprefix), $labelprefix));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
477 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
478 public function GetResourcesFromQuery($query, $args = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
479 return $this->GetResourcesFromStatement($this->DBPrepareStatement($query, $args));
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
480 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
481 public function GetResourcesFromStatement($statement) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
482 return new MARCDatabaseSQLiteResourceIterator($statement);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
483 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
484 public function IsChanged() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
485 return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
486 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
487 public function Close() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
488 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
489 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
490 class MARCDatabaseSQLiteResourceIterator implements Iterator {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
491 private $source, $current = FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
492 public function __construct($source) { $this->source = $source; }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
493 public function current() { return $this->current === FALSE ? NULL : MARCUpdate::Decode($this->current); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
494 public function key() { return NULL; }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
495 public function valid() { return $this->current !== FALSE; }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
496 public function next() { $this->current = $this->source->fetchColumn(); if ($this->current === FALSE) $this->source->closeCursor(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
497 public function rewind() { $this->next(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
498 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
499 class MARCDatabaseDBA extends MARCDatabase {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
500 private $db = NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
501 public function __construct($path, $mode = 'cd', $handler = 'qdbm') {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
502 $this->db = dba_open($path, $mode, $handler);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
503 if ($this->db === FALSE) throw new Exception('Could not open database');
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
504 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
505 public function GetResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
506 $r = dba_fetch($label, $this->db);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
507 if ($r === FALSE) return NULL;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
508 return MARCUpdate::Decode($r);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
509 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
510 public function DeleteResource($label) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
511 return dba_delete($label, $this->db);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
512 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
513 protected function ImportInternal($update) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
514 return dba_replace($update['label'], $update['updatemessage'], $this->db);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
515 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
516 public function GetResources($labelprefix = NULL, $mints = NULL) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
517 return new MARCDatabaseDBAFilteredResourceIterator($this->db, $labelprefix, $mints);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
518 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
519 public function Save() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
520 dba_sync($this->db);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
521 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
522 public function Close() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
523 dba_close($this->db);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
524 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
525 public function IsChanged() {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
526 return FALSE;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
527 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
528 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
529 class MARCDatabaseDBAFilteredResourceIterator implements Iterator {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
530 private $db, $currentkey = FALSE, $labelprefixlength, $labelprefix, $mints;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
531 public function __construct($db, $labelprefix, $mints) {
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
532 $this->db = $db;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
533 $this->labelprefixlength = is_null($labelprefix) ? 0 : strlen($labelprefix);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
534 $this->labelprefix = $labelprefix;
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
535 $this->mints = intval($mints);
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
536 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
537 public function current() { return MARCUpdate::Decode(dba_fetch($this->currentkey, $this->db)); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
538 public function key() { return $this->currentkey; }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
539 public function valid() { return strlen($this->currentkey); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
540 public function next() { $this->currentkey = dba_nextkey($this->db); $this->findnext(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
541 public function rewind() { $this->currentkey = dba_firstkey($this->db); $this->findnext(); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
542 private function findnext() { while ($this->currentkey !== FALSE && ($this->labelprefixlength && substr($this->currentkey, 0, $this->labelprefixlength) != $this->labelprefix)) $this->currentkey = dba_nextkey($this->db); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
543 }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
544
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
545 function marc_decode_int32be($data, $i = 0) { return (ord($data[$i]) << 24) | (ord($data[$i+1]) << 16) | (ord($data[$i+2]) << 8) | ord($data[$i+3]); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
546 function marc_decode_int16be($data, $i = 0) { return (ord($data[$i+0]) << 8) | ord($data[$i+1]); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
547 function marc_encode_int32be($v) { return pack("N", $v); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
548 function marc_encode_int16be($v) { return pack("n", $v); }
3ac7bd7495fd Initial commit
Ivo Smits <Ivo@UCIS.nl>
parents:
diff changeset
549