forked from hackzilla-project/TicketBundle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTicketFromMailCommand.php
246 lines (205 loc) · 12.6 KB
/
TicketFromMailCommand.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
<?php
namespace Hackzilla\Bundle\TicketBundle\Command;
use Hackzilla\Bundle\TicketBundle\Entity\TicketMessageAttachment;
use Hackzilla\Bundle\TicketBundle\Event\TicketEvent;
use Hackzilla\Bundle\TicketBundle\Model\TicketMessageInterface;
use Hackzilla\Bundle\TicketBundle\TicketEvents;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class TicketFromMailCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('ticket:create_from_mail')
->setDescription('Create Ticket from emails')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
//if ()
//TODO if the feature is not enable do nothing here !
//set local from parameters
$locale = $this->getContainer()->getParameter('locale');
$this->getContainer()->get('translator')->setLocale($locale);
$em = $this->getContainer()->get('doctrine')->getEntityManager();
//set the user manager with parameter ? Or a special proxy service who load the available user_manager ?
$userManager = $this->getContainer()->get('fos_user.user_manager');
$ticketManager = $this->getContainer()->get('hackzilla_ticket.ticket_manager');
if ($this->getContainer()->getParameter('hackzilla_ticket.from_mail')['imap_validate_crt'] == 'false') {
$noValidateCert = '/novalidate-cert';
} else {
$noValidateCert = '';
}
//todo make a parameter for this value
imap_timeout(60);
$mailbox = new \PhpImap\Mailbox('{' . $this->getContainer()->getParameter('hackzilla_ticket.from_mail')['imap_server_address'] . ':' . $this->getContainer()->getParameter('hackzilla_ticket.from_mail')['imap_server_port'] . '/imap' . $noValidateCert . '}INBOX', $this->getContainer()->getParameter('hackzilla_ticket.from_mail')['imap_login'], $this->getContainer()->getParameter('hackzilla_ticket.from_mail')['imap_pwd'], $this->getContainer()->getParameter('vich_uploader.mappings')['ticket_message_attachment']['upload_destination']);
// Read all messaged into an array:
$mailsIds = $mailbox->searchMailbox('ALL');
if (!$mailsIds) {
//do nothing, but display a message ?
} else {
//we search for a system user, or a ticket_system user, or the first user with the TICKET_ADMIN_ROLE, if we don't have a user corresponding to the from email, we use this one
$owner = $userManager->findUserByUsername('system');
if (!$owner) {
$owner = $userManager->findUserByUsername('ticket_system');
if (!$owner) {
//could be bad if we have lot of users
$users = $userManager->findUsers();
// Add every user with the ROLE_TICKET_ADMIN role
/** @var User $user */
foreach ($users as $user) {
if ($user->hasRole('ROLE_TICKET_ADMIN')) {
$owner = $user;
break;
}
}
}
}
//for each email
foreach ($mailsIds as $mailsId) {
// Get the first message and save its attachment(s) to disk:
$mail = $mailbox->getMail($mailsId);
//resolv utf8 issues
$mail->headers->subject = imap_utf8($mail->headers->subject);
//temporary hotfix to avoid importing mailer error daemon
if ($mail->headers->subject != "Undelivered Mail Returned to Sender" and $mail->headers->from[0]->mailbox != "MAILER-DAEMON") {
//replyTo or mailFrom = mailTo
if ($mail->headers->reply_to[0]) {
$mailTo = $mail->headers->reply_to[0]->mailbox . "@" . $mail->headers->reply_to[0]->host;
} else {
$mailTo = $mail->headers->from[0]->mailbox . "@" . $mail->headers->from[0]->host;
}
//check the mail subject (\[#[0-9]*[\]])
$result = preg_match('(\[#[0-9]*[\]])', $mail->headers->subject, $ticketRef);
//I find a ticket ref [#xxxx] in the subject, I have to extract the id, and verify if the ticket exists
if ($result > 0) {
//search the ref
preg_match('([#]\d*)', $ticketRef[0], $ticketId);
//get the number
$ticketId = explode('#', $ticketId[0]);
//check if the ticket exists
$ticket = $ticketManager->getTicketById($ticketId[1]);
//todo: check and do something with $ticketError ? Could be hack attemps
if (!$ticket) {
$ticketIdError = true;
$newTicket = true;
} else {
$ticketIdError = false;
$newTicket = false;
}
} else {
$newTicket = true;
}
//ticket user should be the user owner of the mail, if it's in the db, else we can use the owner
if ($messageOwner = $userManager->findUserByEmail($mailTo)) {
//do nothing because the assignation is done in the if condition, but good practice or not ?
} else {
//reuse current owner of the ticket
$messageOwner = $owner;
}
if ($newTicket) {
$ticket = $ticketManager->createTicket();
$ticket->setSubject($mail->headers->subject);
//we need to link a message in the new ticket
$message = $ticketManager->createMessage($ticket);
$ticket->setUserCreated($messageOwner);
$ticket->setLastUser($messageOwner);
$message->setCreatedAt(new \DateTime('now'));
$message->setStatus(TicketMessageInterface::STATUS_OPEN)
->setUser($messageOwner)
->setMailDate(new \DateTime($mail->headers->date))
;
//update the ticket once, to have a ticket ID if it's a new one, needed for the attachment
$ticketManager->updateTicket($ticket, $message);
} else {
$message = $ticketManager->createMessage($ticket);
$message->setStatus(TicketMessageInterface::STATUS_OPEN);
}
if ($mail->textPlain) {
$message->setMessage(imap_utf8(nl2br($mail->textPlain)) . "\r\n" . "From: " . $mailTo);
} else {
$message->setMessage(strip_tags(htmlspecialchars(imap_utf8(nl2br($mail->textHtml))), '<br>') . "<br />" . "From: " . $mailTo);
}
$message->setMessagePlain(strip_tags(htmlspecialchars(imap_utf8(nl2br($mail->textPlain))), '<br>'));
$message->setMessageHtml(htmlspecialchars(imap_utf8($mail->textHtml)));
$message->setHeaderRaw(imap_utf8($mail->headersRaw));
if (mb_detect_encoding($mail->headers->from[0]->personal, mb_detect_order(), true) === false) {
//fix for bad "personal" string, if we can't detect the encoding, we choose to override it with an empty string, because sometimes, with bad encoding, serialize/unserialize fail
$mail->headers->from[0]->personal = "";
$mail->headers->reply_to[0]->personal = "";
}
$message->setFrom($mail->headers->from[0]);
$message->setReplyTo($mail->headers->reply_to[0]);
//add a listener to create users with minimal infos ? Sort of pre registration
$message->setUser($messageOwner);
$nbAttachment = count($mail->getAttachments());
//managing attachments
if (1 == $nbAttachment) {
//take the first and only element of the array
$attachment = current($mail->getAttachments());
//set the attachment name
$message->setAttachmentName($attachment->name);
//set the attachemnt file, vich require an UploadedFile object
//https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/known_issues.md#no-upload-is-triggered-when-manually-injecting-an-instance-of-symfonycomponenthttpfoundationfilefile
$message->setAttachmentFile(new UploadedFile($attachment->filePath, $attachment->name, null, null, null, true));
} elseif ($nbAttachment > 1) {
//get the attachment class from config
$attachmentClass = $this->getContainer()->getParameter('hackzilla_ticket.model.message.attachment.class');
//init the array
$newAttachemnts = [];
foreach ($mail->getAttachments() as $attachment) {
//create a new attachment entity
$newAttachemnt = new $attachmentClass();
//$newAttachemnt = new \N2P\HelpdeskBundle\Entity\TicketMessageAttachment();
$newAttachemnt->setMessage($message);
//set the attachment name
$newAttachemnt->setAttachmentFilename($attachment->name);
//set the attachemnt file, vich require an UploadedFile object
//https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/known_issues.md#no-upload-is-triggered-when-manually-injecting-an-instance-of-symfonycomponenthttpfoundationfilefile
$newAttachemnt->setAttachmentFile(new UploadedFile($attachment->filePath, $attachment->name, null, null, null, true));
$em->persist($newAttachemnt);
//todo optimize here to do the flush outside the loop
$em->flush();
$newAttachemnts[] = $newAttachemnt;
}
//$message->setAttachments($newAttachemnts);
}
//add this message to the current ticket
$ticket->addMessage($message);
try {
$ticketManager->updateTicket($ticket, $message);
} catch (\Symfony\Component\HttpFoundation\File\Exception\FileException $fileException) {
//delete attachment and save again
$message->setAttachmentName(null);
$message->setAttachmentFile(null);
//add in the message body an info about the attachment failure
$message->setMessagePlain($message->getMessagePlain() . "\r\n This message add an invalid attachment file, the system ignore it");
$message->setMessageHtml($message->getMessageHtml() . "\r\n This message add an invalid attachment file, the system ignore it");
$ticketManager->updateTicket($ticket, $message);
}
if ($newTicket) {
$this->getContainer()->get('event_dispatcher')->dispatch(TicketEvents::TICKET_CREATE_FROM_MAIL, new TicketEvent($ticket));
} else {
$this->getContainer()->get('event_dispatcher')->dispatch(TicketEvents::TICKET_UPDATE, new TicketEvent($ticket));
}
//mark this mail for deletion
$mailbox->deleteMail($mailsId);
}
} //end of for each loop on mails
//expunge mails
$mailbox->expungeDeletedMails();
}
} //end else, mailbox is not empty
}