1#!/usr/bin/env php
2<?php
3
4echo "SabreDAV migrate script for version 2.1\n";
5
6if ($argc<2) {
7
8    echo <<<HELLO
9
10This script help you migrate from a pre-2.1 database to 2.1.
11
12Changes:
13  The 'calendarobjects' table will be upgraded.
14  'schedulingobjects' will be created.
15
16If you don't use the default PDO CalDAV or CardDAV backend, it's pointless to
17run this script.
18
19Keep in mind that ALTER TABLE commands will be executed. If you have a large
20dataset this may mean that this process takes a while.
21
22Lastly: Make a back-up first. This script has been tested, but the amount of
23potential variants are extremely high, so it's impossible to deal with every
24possible situation.
25
26In the worst case, you will lose all your data. This is not an overstatement.
27
28Usage:
29
30php {$argv[0]} [pdo-dsn] [username] [password]
31
32For example:
33
34php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
35php {$argv[0]} sqlite:data/sabredav.db
36
37HELLO;
38
39    exit();
40
41}
42
43// There's a bunch of places where the autoloader could be, so we'll try all of
44// them.
45$paths = [
46    __DIR__ . '/../vendor/autoload.php',
47    __DIR__ . '/../../../autoload.php',
48];
49
50foreach($paths as $path) {
51    if (file_exists($path)) {
52        include $path;
53        break;
54    }
55}
56
57$dsn = $argv[1];
58$user = isset($argv[2])?$argv[2]:null;
59$pass = isset($argv[3])?$argv[3]:null;
60
61echo "Connecting to database: " . $dsn . "\n";
62
63$pdo = new PDO($dsn, $user, $pass);
64$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
65$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
66
67$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
68
69switch($driver) {
70
71    case 'mysql' :
72        echo "Detected MySQL.\n";
73        break;
74    case 'sqlite' :
75        echo "Detected SQLite.\n";
76        break;
77    default :
78        echo "Error: unsupported driver: " . $driver . "\n";
79        die(-1);
80}
81
82echo "Upgrading 'calendarobjects'\n";
83$addUid = false;
84try {
85    $result = $pdo->query('SELECT * FROM calendarobjects LIMIT 1');
86    $row = $result->fetch(\PDO::FETCH_ASSOC);
87
88    if (!$row) {
89        echo "No data in table. Going to try to add the uid field anyway.\n";
90        $addUid = true;
91    } elseif (array_key_exists('uid', $row)) {
92        echo "uid field exists. Assuming that this part of the migration has\n";
93        echo "Already been completed.\n";
94    } else {
95        echo "2.0 schema detected.\n";
96        $addUid = true;
97    }
98
99} catch (Exception $e) {
100    echo "Could not find a calendarobjects table. Skipping this part of the\n";
101    echo "upgrade.\n";
102}
103
104if ($addUid) {
105
106    switch($driver) {
107        case 'mysql' :
108            $pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)');
109            break;
110        case 'sqlite' :
111            $pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT');
112            break;
113    }
114
115    $result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
116    $stmt = $pdo->prepare('UPDATE calendarobjects SET uid = ? WHERE id = ?');
117    $counter = 0;
118
119    while($row = $result->fetch(\PDO::FETCH_ASSOC)) {
120
121        try {
122            $vobj = \Sabre\VObject\Reader::read($row['calendardata']);
123        } catch (\Exception $e) {
124            echo "Warning! Item with id $row[id] could not be parsed!\n";
125            continue;
126        }
127        $uid = null;
128        $item = $vobj->getBaseComponent();
129        if (!isset($item->UID)) {
130            echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n";
131            continue;
132        }
133        $uid = (string)$item->UID;
134        $stmt->execute([$uid, $row['id']]);
135        $counter++;
136
137    }
138
139}
140
141echo "Creating 'schedulingobjects'\n";
142
143switch($driver) {
144
145    case 'mysql' :
146        $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects
147(
148    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
149    principaluri VARCHAR(255),
150    calendardata MEDIUMBLOB,
151    uri VARCHAR(200),
152    lastmodified INT(11) UNSIGNED,
153    etag VARCHAR(32),
154    size INT(11) UNSIGNED NOT NULL
155) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
156        ');
157        break;
158
159
160    case 'sqlite' :
161        $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects (
162    id integer primary key asc,
163    principaluri text,
164    calendardata blob,
165    uri text,
166    lastmodified integer,
167    etag text,
168    size integer
169)
170');
171        break;
172        $pdo->exec('
173            CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);
174        ');
175        break;
176}
177
178echo "Done.\n";
179
180echo "Upgrade to 2.1 schema completed.\n";
181