1
|
|
|
#!/usr/bin/env php |
2
|
|
|
<?php |
3
|
|
|
/* |
4
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only |
5
|
|
|
* SPDX-FileCopyrightText: Copyright 2007-2013,2015-2016 Zarafa Deutschland GmbH |
6
|
|
|
* SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH |
7
|
|
|
* |
8
|
|
|
* This is a small command line tool to list folders of a user store or public |
9
|
|
|
* folder available for synchronization. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
define('MAPI_SERVER', 'default:'); |
13
|
|
|
define('SSLCERT_FILE', null); |
14
|
|
|
define('SSLCERT_PASS', null); |
15
|
|
|
|
16
|
|
|
$supported_classes = [ |
17
|
|
|
"IPF.Note" => "SYNC_FOLDER_TYPE_USER_MAIL", |
18
|
|
|
"IPF.Task" => "SYNC_FOLDER_TYPE_USER_TASK", |
19
|
|
|
"IPF.Appointment" => "SYNC_FOLDER_TYPE_USER_APPOINTMENT", |
20
|
|
|
"IPF.Contact" => "SYNC_FOLDER_TYPE_USER_CONTACT", |
21
|
|
|
"IPF.StickyNote" => "SYNC_FOLDER_TYPE_USER_NOTE", |
22
|
|
|
]; |
23
|
|
|
|
24
|
|
|
main(); |
25
|
|
|
|
26
|
|
|
function main() { |
27
|
|
|
listfolders_configure(); |
28
|
|
|
listfolders_handle(); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
function listfolders_configure() { |
32
|
|
|
if (php_sapi_name() != "cli") { |
33
|
|
|
fwrite(STDERR, "This script can only be called from the CLI.\n"); |
34
|
|
|
|
35
|
|
|
exit(1); |
|
|
|
|
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
if (!function_exists("getopt")) { |
39
|
|
|
echo "PHP Function 'getopt()' not found. Please check your PHP version and settings.\n"; |
40
|
|
|
|
41
|
|
|
exit(1); |
|
|
|
|
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
require 'mapi/mapi.util.php'; |
45
|
|
|
require 'mapi/mapidefs.php'; |
46
|
|
|
require 'mapi/mapicode.php'; |
47
|
|
|
require 'mapi/mapitags.php'; |
48
|
|
|
require 'mapi/mapiguid.php'; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
function listfolders_handle() { |
52
|
|
|
$shortoptions = "l:h:u:p:c:"; |
53
|
|
|
$options = getopt($shortoptions); |
54
|
|
|
|
55
|
|
|
$mapi = MAPI_SERVER; |
56
|
|
|
$sslcert_file = SSLCERT_FILE; |
57
|
|
|
$sslcert_pass = SSLCERT_PASS; |
58
|
|
|
$user = "SYSTEM"; |
59
|
|
|
$pass = ""; |
60
|
|
|
|
61
|
|
|
if (isset($options['h'])) { |
62
|
|
|
$mapi = $options['h']; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
// accept a remote user |
66
|
|
|
if (isset($options['u'], $options['p'])) { |
67
|
|
|
$user = $options['u']; |
68
|
|
|
$pass = $options['p']; |
69
|
|
|
} |
70
|
|
|
// accept a certificate and passwort for login |
71
|
|
|
elseif (isset($options['c'], $options['p'])) { |
72
|
|
|
$sslcert_file = $options['c']; |
73
|
|
|
$sslcert_pass = $options['p']; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
$zarafaAdmin = listfolders_zarafa_admin_setup($mapi, $user, $pass, $sslcert_file, $sslcert_pass); |
77
|
|
|
if (isset($zarafaAdmin['adminStore'], $options['l'])) { |
78
|
|
|
listfolders_getlist($zarafaAdmin['adminStore'], $zarafaAdmin['session'], trim($options['l'])); |
79
|
|
|
} |
80
|
|
|
else { |
81
|
|
|
echo "Usage:\n" . |
82
|
|
|
"listfolders.php [actions] [options]\n\n" . |
83
|
|
|
"Actions: [-l username]\n" . |
84
|
|
|
"\t-l username\tlist folders of user, for public folder use 'SYSTEM'\n\n" . |
85
|
|
|
"Global options: [-h path] [[-u remoteuser] [-p password]] [[-c certificate_path] [-p password]]\n" . |
86
|
|
|
"\t-h path\t\tconnect through <path>, e.g. file:///var/run/socket or https://10.0.0.1:237/grommunio\n" . |
87
|
|
|
"\t-u remoteuser\tlogin as authenticated administration user\n" . |
88
|
|
|
"\t-c certificate\tlogin with a ssl certificate located in this location, e.g. /etc/zarafa/ssl/client.pem\n" . |
89
|
|
|
"\t-p password\tpassword of the remoteuser or certificate\n\n"; |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
function listfolders_zarafa_admin_setup($mapi, $user, $pass, $sslcert_file, $sslcert_pass) { |
94
|
|
|
$session = @mapi_logon_zarafa($user, $pass, $mapi, $sslcert_file, $sslcert_pass, 0, 'script', 'script'); |
|
|
|
|
95
|
|
|
|
96
|
|
|
if (!$session) { |
97
|
|
|
printf("User '%s' could not login. The script will exit. Errorcode: 0x%08x\n", $user, mapi_last_hresult()); |
|
|
|
|
98
|
|
|
|
99
|
|
|
exit(1); |
|
|
|
|
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$adminStore = null; |
103
|
|
|
$stores = @mapi_getmsgstorestable($session); |
|
|
|
|
104
|
|
|
$storeslist = @mapi_table_queryallrows($stores, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]); |
|
|
|
|
105
|
|
|
foreach ($storeslist as $store) { |
106
|
|
|
if (isset($store[PR_DEFAULT_STORE]) && $store[PR_DEFAULT_STORE] == true) { |
107
|
|
|
$adminStore = @mapi_openmsgstore($session, $store[PR_ENTRYID]); |
|
|
|
|
108
|
|
|
|
109
|
|
|
break; |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
$zarafauserinfo['admin'] = 1; |
|
|
|
|
113
|
|
|
$admin = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin']) ? true : false; |
114
|
|
|
|
115
|
|
|
if (!$stores || !$storeslist || !$adminStore || !$admin) { |
116
|
|
|
echo "There was error trying to log in as admin or retrieving admin info. The script will exit.\n"; |
117
|
|
|
|
118
|
|
|
exit(1); |
|
|
|
|
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return ["session" => $session, "adminStore" => $adminStore]; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
function listfolders_getlist($adminStore, $session, $user) { |
125
|
|
|
global $supported_classes; |
126
|
|
|
|
127
|
|
|
if (strtoupper($user) == 'SYSTEM') { |
128
|
|
|
// Find the public store store |
129
|
|
|
$storestables = @mapi_getmsgstorestable($session); |
|
|
|
|
130
|
|
|
$result = @mapi_last_hresult(); |
|
|
|
|
131
|
|
|
|
132
|
|
|
if ($result == NOERROR) { |
133
|
|
|
$rows = @mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_MDB_PROVIDER]); |
|
|
|
|
134
|
|
|
|
135
|
|
|
foreach ($rows as $row) { |
136
|
|
|
if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { |
137
|
|
|
if (!isset($row[PR_ENTRYID])) { |
138
|
|
|
echo "Public folder are not available.\nIf this is a multi-tenancy system, use -u and -p and login with an admin user of the company.\nThe script will exit.\n"; |
139
|
|
|
|
140
|
|
|
exit(1); |
|
|
|
|
141
|
|
|
} |
142
|
|
|
$entryid = $row[PR_ENTRYID]; |
143
|
|
|
|
144
|
|
|
break; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
else { |
150
|
|
|
$entryid = @mapi_msgstore_createentryid($adminStore, $user); |
|
|
|
|
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$userStore = @mapi_openmsgstore($session, $entryid); |
|
|
|
|
154
|
|
|
$hresult = mapi_last_hresult(); |
155
|
|
|
|
156
|
|
|
// Cache the store for later use |
157
|
|
|
if ($hresult != NOERROR) { |
158
|
|
|
echo "Could not open store for '{$user}'. The script will exit.\n"; |
159
|
|
|
|
160
|
|
|
exit(1); |
|
|
|
|
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
if (strtoupper($user) != 'SYSTEM') { |
164
|
|
|
$inbox = mapi_msgstore_getreceivefolder($userStore); |
|
|
|
|
165
|
|
|
if (mapi_last_hresult() != NOERROR) { |
166
|
|
|
printf("Could not open inbox for %s (0x%08X). The script will exit.\n", $user, mapi_last_hresult()); |
167
|
|
|
|
168
|
|
|
exit(1); |
|
|
|
|
169
|
|
|
} |
170
|
|
|
$inboxProps = mapi_getprops($inbox, [PR_SOURCE_KEY]); |
|
|
|
|
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
$storeProps = mapi_getprops($userStore, [PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID]); |
174
|
|
|
$root = @mapi_msgstore_openentry($userStore, null); |
|
|
|
|
175
|
|
|
$h_table = @mapi_folder_gethierarchytable($root, CONVENIENT_DEPTH); |
|
|
|
|
176
|
|
|
$subfolders = @mapi_table_queryallrows($h_table, [PR_ENTRYID, PR_DISPLAY_NAME, PR_CONTAINER_CLASS, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_FOLDER_TYPE, PR_ATTR_HIDDEN]); |
177
|
|
|
|
178
|
|
|
echo "Available folders in store '{$user}':\n" . str_repeat("-", 50) . "\n"; |
179
|
|
|
foreach ($subfolders as $folder) { |
180
|
|
|
// do not display hidden and search folders |
181
|
|
|
if ((isset($folder[PR_ATTR_HIDDEN]) && $folder[PR_ATTR_HIDDEN]) || |
182
|
|
|
(isset($folder[PR_FOLDER_TYPE]) && $folder[PR_FOLDER_TYPE] == FOLDER_SEARCH)) { |
183
|
|
|
continue; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
// handle some special folders |
187
|
|
|
if ((strtoupper($user) != 'SYSTEM') && |
188
|
|
|
((isset($inboxProps[PR_SOURCE_KEY]) && $folder[PR_SOURCE_KEY] == $inboxProps[PR_SOURCE_KEY]) || |
189
|
|
|
$folder[PR_ENTRYID] == $storeProps[PR_IPM_SENTMAIL_ENTRYID] || |
190
|
|
|
$folder[PR_ENTRYID] == $storeProps[PR_IPM_WASTEBASKET_ENTRYID])) { |
191
|
|
|
$folder[PR_CONTAINER_CLASS] = "IPF.Note"; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
if (isset($folder[PR_CONTAINER_CLASS]) && array_key_exists($folder[PR_CONTAINER_CLASS], $supported_classes)) { |
195
|
|
|
echo "Folder name:\t" . $folder[PR_DISPLAY_NAME] . "\n"; |
196
|
|
|
echo "Folder ID:\t" . bin2hex($folder[PR_SOURCE_KEY]) . "\n"; |
197
|
|
|
echo "Type:\t\t" . $supported_classes[$folder[PR_CONTAINER_CLASS]] . "\n"; |
198
|
|
|
echo "\n"; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
function CheckMapiExtVersion($version = "") { |
204
|
|
|
// compare build number if requested |
205
|
|
|
if (preg_match('/^\d+$/', $version) && strlen($version) > 3) { |
206
|
|
|
$vs = preg_split('/-/', phpversion("mapi")); |
207
|
|
|
|
208
|
|
|
return $version <= $vs[1]; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
if (extension_loaded("mapi")) { |
212
|
|
|
if (version_compare(phpversion("mapi"), $version) == -1) { |
213
|
|
|
return false; |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
else { |
217
|
|
|
return false; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
return true; |
221
|
|
|
} |
222
|
|
|
|
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.