-
Notifications
You must be signed in to change notification settings - Fork 10
/
hook.php
350 lines (297 loc) · 13.5 KB
/
hook.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
<?php
/*
-------------------------------------------------------------------------
MailAnalyzer plugin for GLPI
Copyright (C) 2011-2024 by Raynet SAS a company of A.Raymond Network.
https://www.araymond.com/
-------------------------------------------------------------------------
LICENSE
This file is part of MailAnalyzer plugin for GLPI.
This file is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This plugin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this plugin. If not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------
*/
/**
* Summary of plugin_mailanalyzer_install
* @return boolean
*/
function plugin_mailanalyzer_install() {
global $DB;
if (!$DB->tableExists("glpi_plugin_mailanalyzer_message_id")) {
$query = "CREATE TABLE `glpi_plugin_mailanalyzer_message_id` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`message_id` VARCHAR(255) NOT NULL DEFAULT '0',
`tickets_id` INT UNSIGNED NOT NULL DEFAULT '0',
`mailcollectors_id` int UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE INDEX `message_id` (`message_id`,`mailcollectors_id`),
INDEX `tickets_id` (`tickets_id`)
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=innoDB;
";
$DB->query($query) or die("error creating glpi_plugin_mailanalyzer_message_id " . $DB->error());
} else {
if (count($DB->listTables('glpi_plugin_mailanalyzer_message_id', ['engine' => 'MyIsam'])) > 0) {
$query = "ALTER TABLE glpi_plugin_mailanalyzer_message_id ENGINE = InnoDB";
$DB->query($query) or die("error updating ENGINE in glpi_plugin_mailanalyzer_message_id " . $DB->error());
}
}
if ($DB->fieldExists("glpi_plugin_mailanalyzer_message_id","mailgate_id"))
{
//STEP - UPDATE MAILGATE_ID INTO MAILCOLLECTORS_ID
$query = "ALTER TABLE `glpi_plugin_mailanalyzer_message_id`
CHANGE COLUMN `mailgate_id` `mailcollectors_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `message_id`,
DROP INDEX `message_id`,
ADD UNIQUE INDEX `message_id` (`message_id`, `mailcollectors_id`) USING BTREE;";
$DB->query($query) or die("error updating ENGINE in glpi_plugin_mailanalyzer_message_id " . $DB->error());
}
if (!$DB->fieldExists("glpi_plugin_mailanalyzer_message_id","mailcollectors_id"))
{
//STEP - ADD mailcollectors_id
$query = "ALTER TABLE glpi_plugin_mailanalyzer_message_id ADD COLUMN `mailcollectors_id` int UNSIGNED NOT NULL DEFAULT 0 AFTER `message_id`";
$DB->query($query) or die("error updating ENGINE in glpi_plugin_mailanalyzer_message_id " . $DB->error());
//STEP - REMOVE UNICITY CONSTRAINT
$query = "ALTER TABLE glpi_plugin_mailanalyzer_message_id DROP INDEX `message_id`";
$DB->query($query) or die("error updating ENGINE in glpi_plugin_mailanalyzer_message_id " . $DB->error());
//STEP - ADD NEW UNICITY CONSTRAINT
$query = "ALTER TABLE glpi_plugin_mailanalyzer_message_id ADD UNIQUE KEY `message_id` (`message_id`,`mailcollectors_id`);";
$DB->query($query) or die("error updating ENGINE in glpi_plugin_mailanalyzer_message_id " . $DB->error());
}
if (!$DB->fieldExists('glpi_plugin_mailanalyzer_message_id', 'tickets_id')) {
// then we must change the name and the length of id and ticket_id to 11
$query = "ALTER TABLE `glpi_plugin_mailanalyzer_message_id`
CHANGE COLUMN `id` `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
CHANGE COLUMN `ticket_id` `tickets_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `message_id`,
DROP INDEX `ticket_id`,
ADD INDEX `ticket_id` (`tickets_id`);";
$DB->query($query) or die('Cannot alter glpi_plugin_mailanalyzer_message_id table! ' . $DB->error());
}
return true;
}
/**
* Summary of plugin_mailanalyzer_uninstall
* @return boolean
*/
function plugin_mailanalyzer_uninstall() {
// nothing to uninstall
// do not delete table
return true;
}
/**
* Summary of PluginMailAnalyzer
*/
class PluginMailAnalyzer {
/**
* Create default mailgate
* @param int $mailcollectors_id is the id of the mail collector in GLPI DB
* @return bool|MailCollector
*
*/
static function openMailgate($mailcollectors_id) {
$mailgate = new MailCollector();
$mailgate->getFromDB($mailcollectors_id);
$mailgate->uid = -1;
//Connect to the Mail Box
try {
$mailgate->connect();
} catch (Throwable $e) {
Toolbox::logError(
'An error occured trying to connect to collector.',
$e->getMessage(),
"\n",
$e->getTraceAsString()
);
Session::addMessageAfterRedirect(
__('An error occured trying to connect to collector.') . "<br/>" . $e->getMessage(),
false,
ERROR
);
return false;
}
return $mailgate;
}
/**
* Summary of plugin_pre_item_add_mailanalyzer
* @param mixed $parm
* @return void
*/
public static function plugin_pre_item_add_mailanalyzer($parm) {
global $DB, $mailgate;
if (isset($parm->input['_mailgate'])) {
// this ticket have been created via email receiver.
// and we have the Laminas\Mail\Storage\Message object in the _message key
// Analyze emails to establish conversation
if (isset($mailgate)) {
// mailgate has been open by web page call, then use it
$local_mailgate = $mailgate;
} else {
// mailgate is not open. Called by cron
// then locally create a mailgate
$local_mailgate = PluginMailAnalyzer::openMailgate($parm->input['_mailgate']);
if ($local_mailgate === false) {
// can't connect to the mail server, then cancel ticket creation
$parm->input = false;// []; // empty array...
return;
}
}
// we must check if this email has not been received yet!
// test if 'message-id' is in the DB
$messageId = $parm->input['_message']->messageid;
$mailgateId = $local_mailgate->fields['id'];
$uid = $parm->input['_uid'];
$res = $DB->request(
'glpi_plugin_mailanalyzer_message_id',
[
'AND' =>
[
'tickets_id' => ['!=', 0],
'message_id' => $messageId,
'mailcollectors_id' => $mailgateId
]
]
);
if ($row = $res->current()) {
// email already received
// must prevent ticket creation
$parm->input = false; //[ ];
// as Ticket creation is cancelled, then email is not deleted from mailbox
// then we need to set deletion flag to true to this email from mailbox folder
$local_mailgate->deleteMails($uid, MailCollector::REFUSED_FOLDER); // NOK Folder
return;
}
// search for 'Thread-Index' and 'References'
$messages_id = self::getMailReferences($parm->input['_message']);
if (count($messages_id) > 0) {
$res = $DB->request(
'glpi_plugin_mailanalyzer_message_id',
['AND' =>
[
'tickets_id' => ['!=',0],
'message_id' => $messages_id,
'mailcollectors_id' => $mailgateId
],
'ORDER' => 'tickets_id DESC'
]
);
if ($row = $res->current()) {
// TicketFollowup creation only if ticket status is not closed
$locTicket = new Ticket();
$locTicket->getFromDB((integer)$row['tickets_id']);
if ($locTicket->fields['status'] != CommonITILObject::CLOSED) {
$ticketfollowup = new ITILFollowup();
$input = $parm->input;
$input['items_id'] = $row['tickets_id'];
$input['users_id'] = $parm->input['_users_id_requester'];
$input['add_reopen'] = 1;
$input['itemtype'] = 'Ticket';
unset($input['urgency']);
unset($input['entities_id']);
unset($input['_ruleid']);
$ticketfollowup->add($input);
// add message id to DB in case of another email will use it
$DB->insert(
'glpi_plugin_mailanalyzer_message_id',
[
'message_id' => $messageId,
'tickets_id' => $input['items_id'],
'mailcollectors_id' => $mailgateId
]
);
// prevent Ticket creation. Unfortunately it will return an error to receiver when started manually from web page
$parm->input = false; // []; // empty array...
// as Ticket creation is cancelled, then email is not deleted from mailbox
// then we need to set deletion flag to true to this email from mailbox folder
$local_mailgate->deleteMails($uid, MailCollector::ACCEPTED_FOLDER); // OK folder
return;
} else {
// ticket creation, but linked to the closed one...
$parm->input['_link'] = ['link' => '1', 'tickets_id_1' => '0', 'tickets_id_2' => $row['tickets_id']];
}
}
}
// can't find ref into DB, then this is a new ticket, in this case insert refs and message_id into DB
$messages_id[] = $messageId;
// this is a new ticket
// then add references and message_id to DB
foreach ($messages_id as $ref) {
$res = $DB->request('glpi_plugin_mailanalyzer_message_id', ['message_id' => $ref, 'mailcollectors_id' => $mailgateId]);
if (count($res) <= 0) {
$DB->insert('glpi_plugin_mailanalyzer_message_id', ['message_id' => $ref, 'mailcollectors_id' => $mailgateId]);
}
}
}
}
/**
* Summary of plugin_item_add_mailanalyzer
* @param mixed $parm
*/
public static function plugin_item_add_mailanalyzer($parm) {
global $DB;
if (isset($parm->input['_mailgate'])) {
// this ticket have been created via email receiver.
// update the ticket ID for the message_id only for newly created tickets (tickets_id == 0)
// Are 'Thread-Index' or 'Refrences' present?
$messages_id = self::getMailReferences($parm->input['_message']);
$messages_id[] = $parm->input['_message']->messageid;
$DB->update(
'glpi_plugin_mailanalyzer_message_id',
[
'tickets_id' => $parm->fields['id']
],
[
'WHERE' =>
[
'AND' =>
[
'tickets_id' => 0,
'message_id' => $messages_id
]
]
]
);
}
}
/**
* Summary of getMailReferences
* @param Laminas\Mail\Storage\Message $message
* @return array
*/
private static function getMailReferences(Laminas\Mail\Storage\Message $message) {
$messages_id = []; // by default
$config = Config::getConfigurationValues('plugin:mailanalyzer');
// search for 'Thread-Index'
if (isset($message->threadindex) && isset($config['use_threadindex']) && $config['use_threadindex']) {
// exemple of thread-index : ac5rwreerb4gv3pcr8gdflszrsqhoa==
// explanations to decode this property: http://msdn.microsoft.com/en-us/library/ee202481%28v=exchg.80%29.aspx
$messages_id[] = bin2hex(substr(base64_decode($message->threadindex), 6, 16 ));
}
// search for 'References'
if (isset($message->references)) {
// we may have a forwarded email that looks like reply-to
if (preg_match_all('/<.*?>/', $message->references, $matches)) {
$messages_id = array_merge($messages_id, $matches[0]);
}
}
// clean $messages_id array
return array_filter($messages_id, function($val) {return $val != trim('', '< >');});
}
/**
* Summary of plugin_item_purge_mailanalyzer
* @param mixed $item
*/
static function plugin_item_purge_mailanalyzer($item) {
global $DB;
// the ticket is purged, then we are going to purge the matching rows in glpi_plugin_mailanalyzer_message_id table
// DELETE FROM glpi_plugin
$DB->delete('glpi_plugin_mailanalyzer_message_id', ['tickets_id' => $item->getID()]);
}
}