1#!/usr/bin/env php
2<?php
3/*
4 +-----------------------------------------------------------------------+
5 | This file is part of the Roundcube Webmail client                     |
6 |                                                                       |
7 | Copyright (C) The Roundcube Dev Team                                  |
8 | Copyright (C) Kolab Systems AG                                        |
9 |                                                                       |
10 | Licensed under the GNU General Public License version 3 or            |
11 | any later version with exceptions for skins & plugins.                |
12 | See the README file for a full license statement.                     |
13 |                                                                       |
14 | PURPOSE:                                                              |
15 |   Import keys from Enigma's homedir into database for multihost       |
16 |   support.                                                            |
17 +-----------------------------------------------------------------------+
18 | Author: Aleksander Machniak <alec@alec.pl>                            |
19 +-----------------------------------------------------------------------+
20*/
21
22define('INSTALL_PATH', realpath(__DIR__ . '/../../../') . '/');
23
24require INSTALL_PATH . 'program/include/clisetup.php';
25
26$rcmail = rcube::get_instance();
27
28// get arguments
29$args = rcube_utils::get_opt([
30        'u' => 'user',
31        'h' => 'host',
32        'd' => 'dir',
33        'x' => 'dry-run',
34]);
35
36if (!empty($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'help') {
37    print_usage();
38    exit;
39}
40
41if (empty($args['dir'])) {
42    rcube::raise_error("--dir argument is required", true);
43}
44
45$host = get_host($args);
46$dirs = [];
47
48// Read the homedir and iterate over all subfolders (as users)
49if (empty($args['user'])) {
50    if ($dh = opendir($args['dir'])) {
51        while (($dir = readdir($dh)) !== false) {
52            if ($dir != '.' && $dir != '..') {
53                $dirs[$args['dir'] . '/' . $dir] = $dir;
54            }
55        }
56        closedir($dh);
57    }
58}
59// a single user
60else {
61    $dirs = [$args['dir'] => $args['user']];
62}
63
64foreach ($dirs as $dir => $user) {
65    echo "Importing keys from $dir\n";
66
67    if ($user_id = get_user_id($user, $host)) {
68        reset_state($user_id, !empty($args['dry-run']));
69        import_dir($user_id, $dir, !empty($args['dry-run']));
70    }
71}
72
73
74function print_usage()
75{
76    print "Usage: import.sh [options]\n";
77    print "Options:\n";
78    print "    --user=username User, if not set --dir subfolders will be iterated\n";
79    print "    --host=host     The IMAP hostname or IP the given user is related to\n";
80    print "    --dir=path      Location of the gpg homedir\n";
81    print "    --dry-run       Do nothing, just list found user/files\n";
82}
83
84function get_host($args)
85{
86    global $rcmail;
87
88    if (empty($args['host'])) {
89        $hosts = $rcmail->config->get('default_host', '');
90        if (is_string($hosts)) {
91            $args['host'] = $hosts;
92        }
93        else if (is_array($hosts) && count($hosts) == 1) {
94            $args['host'] = reset($hosts);
95        }
96        else {
97            rcube::raise_error("Specify a host name", true);
98        }
99
100        // host can be a URL like tls://192.168.12.44
101        $host_url = parse_url($args['host']);
102        if (!empty($host_url['host'])) {
103            $args['host'] = $host_url['host'];
104        }
105    }
106
107    return $args['host'];
108}
109
110function get_user_id($username, $host)
111{
112    global $rcmail;
113
114    $db = $rcmail->get_dbh();
115
116    // find user in local database
117    $user = rcube_user::query($username, $host);
118
119    if (empty($user)) {
120        rcube::raise_error("User does not exist: $username");
121    }
122
123    return $user->ID;
124}
125
126function reset_state($user_id, $dry_run = false)
127{
128    global $rcmail;
129
130    if ($dry_run) {
131        return;
132    }
133
134    $db = $rcmail->get_dbh();
135
136    $db->query("DELETE FROM " . $db->table_name('filestore', true)
137        . " WHERE `user_id` = ? AND `context` = ?",
138        $user_id, 'enigma');
139}
140
141function import_dir($user_id, $dir, $dry_run = false)
142{
143    global $rcmail;
144
145    $db       = $rcmail->get_dbh();
146    $table    = $db->table_name('filestore', true);
147    $db_files = ['pubring.gpg', 'secring.gpg', 'pubring.kbx'];
148    $maxsize  = min($db->get_variable('max_allowed_packet', 1048500), 4*1024*1024) - 2000;
149
150    foreach (glob("$dir/private-keys-v1.d/*.key") as $file) {
151        $db_files[] = substr($file, strlen($dir) + 1);
152    }
153
154    foreach ($db_files as $file) {
155        if ($mtime = @filemtime("$dir/$file")) {
156            $data     = file_get_contents("$dir/$file");
157            $data     = base64_encode($data);
158            $datasize = strlen($data);
159
160            if ($datasize > $maxsize) {
161                rcube::raise_error([
162                        'code' => 605, 'line' => __LINE__, 'file' => __FILE__,
163                        'message' => "Enigma: Failed to save $file. Size exceeds max_allowed_packet."
164                    ], true, false);
165
166                continue;
167            }
168
169            echo "* $file\n";
170
171            if ($dry_run) {
172                continue;
173            }
174
175            $result = $db->query(
176                "INSERT INTO $table (`user_id`, `context`, `filename`, `mtime`, `data`)"
177                . " VALUES(?, 'enigma', ?, ?, ?)",
178                $user_id, $file, $mtime, $data);
179
180            if ($db->is_error($result)) {
181                rcube::raise_error([
182                        'code' => 605, 'line' => __LINE__, 'file' => __FILE__,
183                        'message' => "Enigma: Failed to save $file into database."
184                    ], true, false);
185            }
186        }
187    }
188}
189