Bootswatch, Summernote, and Captcheck mods for Mods for HESK (mods-for-hesk.com). In use at support.netsyms.com.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

pipe_functions.inc.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <?php
  2. /**
  3. *
  4. * This file is part of HESK - PHP Help Desk Software.
  5. *
  6. * (c) Copyright Klemen Stirn. All rights reserved.
  7. * https://www.hesk.com
  8. *
  9. * For the full copyright and license agreement information visit
  10. * https://www.hesk.com/eula.php
  11. *
  12. */
  13. /* Check if this is a valid include */
  14. if (!defined('IN_SCRIPT')) {
  15. die('Invalid attempt');
  16. }
  17. // Include all functions needed for email piping
  18. hesk_load_database_functions();
  19. require(HESK_PATH . 'inc/email_functions.inc.php');
  20. require(HESK_PATH . 'inc/posting_functions.inc.php');
  21. require(HESK_PATH . 'inc/htmLawed.php');
  22. require(HESK_PATH . 'inc/mail/rfc822_addresses.php');
  23. require(HESK_PATH . 'inc/mail/mime_parser.php');
  24. require(HESK_PATH . 'inc/mail/email_parser.php');
  25. /*** FUNCTIONS ***/
  26. function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority = -1)
  27. {
  28. global $hesk_settings, $hesklang, $hesk_db_link, $ticket;
  29. $modsForHesk_settings = mfh_getSettings();
  30. // Process "Reply-To:" or "From:" email
  31. $tmpvar['email'] = isset($results['reply-to'][0]['address']) ? hesk_validateEmail($results['reply-to'][0]['address'], 'ERR', 0) : hesk_validateEmail($results['from'][0]['address'], 'ERR', 0);
  32. // Email missing, invalid or banned?
  33. if (!$tmpvar['email'] || hesk_isBannedEmail($tmpvar['email'])) {
  34. return hesk_cleanExit();
  35. }
  36. // Process "Reply-To:" or "From:" name, convert to UTF-8, set to "[Customer]" if not set
  37. if (isset($results['reply-to'][0]['name']) && strlen($results['reply-to'][0]['name'])) {
  38. $tmpvar['name'] = $results['reply-to'][0]['name'];
  39. if (!empty($results['reply-to'][0]['encoding'])) {
  40. $tmpvar['name'] = hesk_encodeUTF8($tmpvar['name'], $results['reply-to'][0]['encoding']);
  41. }
  42. } else {
  43. $tmpvar['name'] = isset($results['from'][0]['name']) ? $results['from'][0]['name'] : $hesklang['pde'];
  44. if (!empty($results['from'][0]['encoding'])) {
  45. $tmpvar['name'] = hesk_encodeUTF8($tmpvar['name'], $results['from'][0]['encoding']);
  46. }
  47. }
  48. $tmpvar['name'] = hesk_input($tmpvar['name'], '', '', 1, 50) or $tmpvar['name'] = $hesklang['pde'];
  49. // Process "To:" email (not yet implemented, for future use)
  50. // $tmpvar['to_email'] = hesk_validateEmail($results['to'][0]['address'],'ERR',0);
  51. // Process email subject, convert to UTF-8, set to "[Piped email]" if none set
  52. $tmpvar['subject'] = isset($results['subject']) ? $results['subject'] : $hesklang['pem'];
  53. if (!empty($results['subject_encoding'])) {
  54. $tmpvar['subject'] = hesk_encodeUTF8($tmpvar['subject'], $results['subject_encoding']);
  55. }
  56. $tmpvar['subject'] = hesk_input($tmpvar['subject'], '', '', 1, 70) or $tmpvar['subject'] = $hesklang['pem'];
  57. // Process email message, convert to UTF-8
  58. $tmpvar['message'] = isset($results['message']) ? $results['message'] : '';
  59. if (!empty($results['encoding'])) {
  60. $tmpvar['message'] = hesk_encodeUTF8($tmpvar['message'], $results['encoding']);
  61. }
  62. $tmpvar['message'] = hesk_input($tmpvar['message'], '', '', 1);
  63. // Message missing?
  64. if (strlen($tmpvar['message']) == 0) {
  65. // Message required? Ignore this email.
  66. if ($hesk_settings['eml_req_msg']) {
  67. return hesk_cleanExit();
  68. }
  69. // Message not required? Assign a default message
  70. $tmpvar['message'] = $hesklang['def_msg'];
  71. // Track duplicate emails based on subject
  72. $message_hash = md5($tmpvar['subject']);
  73. } else {
  74. $message_hash = md5($tmpvar['message']);
  75. }
  76. // Strip quoted reply from email
  77. $tmpvar['message'] = hesk_stripQuotedText($tmpvar['message']);
  78. // Convert URLs to links, change newlines to <br />
  79. $tmpvar['message'] = hesk_makeURL($tmpvar['message']);
  80. $tmpvar['message'] = nl2br($tmpvar['message']);
  81. # For debugging purposes
  82. # die( bin2hex($tmpvar['message']) );
  83. # die($tmpvar['message']);
  84. // Try to detect "delivery failed" and "noreply" emails - ignore if detected
  85. if (hesk_isReturnedEmail($tmpvar)) {
  86. return hesk_cleanExit();
  87. }
  88. // Check for email loops
  89. if (hesk_isEmailLoop($tmpvar['email'], $message_hash)) {
  90. return hesk_cleanExit();
  91. }
  92. // OK, everything seems OK. Now determine if this is a reply to a ticket or a new ticket
  93. if (preg_match('/\[#([A-Z0-9]{3}\-[A-Z0-9]{3}\-[A-Z0-9]{4})\]/', str_replace(' ', '', $tmpvar['subject']), $matches)) {
  94. // We found a possible tracking ID
  95. $tmpvar['trackid'] = $matches[1];
  96. // Does it match one in the database?
  97. $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid`='" . hesk_dbEscape($tmpvar['trackid']) . "' LIMIT 1");
  98. if (hesk_dbNumRows($res)) {
  99. $ticket = hesk_dbFetchAssoc($res);
  100. // Do email addresses match?
  101. if (strpos(strtolower($ticket['email']), strtolower($tmpvar['email'])) === false) {
  102. $tmpvar['trackid'] = '';
  103. }
  104. // Is this ticket locked? Force create a new one if it is
  105. if ($ticket['locked']) {
  106. $tmpvar['trackid'] = '';
  107. }
  108. } else {
  109. $tmpvar['trackid'] = '';
  110. }
  111. }
  112. // If tracking ID is empty, generate a new one
  113. if (empty($tmpvar['trackid'])) {
  114. $tmpvar['trackid'] = hesk_createID();
  115. $is_reply = 0;
  116. } else {
  117. $is_reply = 1;
  118. }
  119. // Process attachments
  120. $tmpvar['attachmment_notices'] = '';
  121. $tmpvar['attachments'] = '';
  122. $num = 0;
  123. if ($hesk_settings['attachments']['use'] && isset($results['attachments'][0])) {
  124. foreach ($results['attachments'] as $k => $v) {
  125. // Clean attachment names
  126. $myatt['real_name'] = hesk_cleanFileName($v['orig_name']);
  127. // Check number of attachments, delete any over max number
  128. if ($num >= $hesk_settings['attachments']['max_number']) {
  129. $tmpvar['attachmment_notices'] .= sprintf($hesklang['attnum'], $myatt['real_name']) . "\n";
  130. continue;
  131. }
  132. // Check file extension
  133. $ext = strtolower(strrchr($myatt['real_name'], "."));
  134. if (!in_array($ext, $hesk_settings['attachments']['allowed_types'])) {
  135. $tmpvar['attachmment_notices'] .= sprintf($hesklang['atttyp'], $myatt['real_name']) . "\n";
  136. continue;
  137. }
  138. // Check file size
  139. $myatt['size'] = $v['size'];
  140. if ($myatt['size'] > ($hesk_settings['attachments']['max_size'])) {
  141. $tmpvar['attachmment_notices'] .= sprintf($hesklang['attsiz'], $myatt['real_name']) . "\n";
  142. continue;
  143. }
  144. // Generate a random file name
  145. $useChars = 'AEUYBDGHJLMNPQRSTVWXZ123456789';
  146. $tmp = $useChars{mt_rand(0, 29)};
  147. for ($j = 1; $j < 10; $j++) {
  148. $tmp .= $useChars{mt_rand(0, 29)};
  149. }
  150. $myatt['saved_name'] = substr($tmpvar['trackid'] . '_' . md5($tmp . $myatt['real_name']), 0, 200) . $ext;
  151. // Rename the temporary file
  152. rename($v['stored_name'], HESK_PATH . $hesk_settings['attach_dir'] . '/' . $myatt['saved_name']);
  153. // Insert into database
  154. hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "attachments` (`ticket_id`,`saved_name`,`real_name`,`size`) VALUES ('" . hesk_dbEscape($tmpvar['trackid']) . "','" . hesk_dbEscape($myatt['saved_name']) . "','" . hesk_dbEscape($myatt['real_name']) . "','" . intval($myatt['size']) . "')");
  155. $tmpvar['attachments'] .= hesk_dbInsertID() . '#' . $myatt['real_name'] . '#' . $myatt['saved_name'] . ',';
  156. $num++;
  157. }
  158. if (strlen($tmpvar['attachmment_notices'])) {
  159. $tmpvar['message'] .= "<br /><br />" . hesk_input($hesklang['attrem'], '', '', 1) . "<br />" . nl2br(hesk_input($tmpvar['attachmment_notices'], '', '', 1));
  160. }
  161. }
  162. // Delete the temporary files
  163. deleteAll($results['tempdir']);
  164. // If this is a reply add a new reply
  165. if ($is_reply) {
  166. // Set last replier name to customer name
  167. $ticket['lastreplier'] = ($tmpvar['name'] == $hesklang['pde']) ? $tmpvar['email'] : $tmpvar['name'];;
  168. // If staff hasn't replied yet, keep ticket status "New", otherwise set it to "Waiting reply from staff"
  169. $new_status_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsNewTicketStatus` = 1");
  170. $waiting_reply_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsCustomerReplyStatus` = 1");
  171. $new_status = hesk_dbFetchAssoc($new_status_rs);
  172. $waiting_reply_rs = hesk_dbFetchAssoc($waiting_reply_rs);
  173. $ticket['status'] = $ticket['status'] ? $waiting_reply_rs['id'] : $new_status['id'];
  174. // Update ticket as necessary
  175. hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `lastchange`=NOW(),`status`='{$ticket['status']}',`replies`=`replies`+1,`lastreplier`='0' WHERE `id`='" . intval($ticket['id']) . "'");
  176. // If customer replied, we assume staff replies have been read (no way to be sure if ticket.php hasn't been opened)
  177. hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` SET `read` = '1' WHERE `replyto` = '" . intval($ticket['id']) . "' AND `staffid` != '0' ");
  178. // Insert reply into database
  179. hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`) VALUES ('" . intval($ticket['id']) . "','" . hesk_dbEscape($ticket['lastreplier']) . "','" . hesk_dbEscape($tmpvar['message']) . "',NOW(),'" . hesk_dbEscape($tmpvar['attachments']) . "')");
  180. // --> Prepare reply message
  181. // 1. Generate the array with ticket info that can be used in emails
  182. $info = array(
  183. 'email' => $ticket['email'],
  184. 'category' => $ticket['category'],
  185. 'priority' => $ticket['priority'],
  186. 'owner' => $ticket['owner'],
  187. 'trackid' => $ticket['trackid'],
  188. 'status' => $ticket['status'],
  189. 'name' => $ticket['name'],
  190. 'lastreplier' => $ticket['lastreplier'],
  191. 'subject' => $ticket['subject'],
  192. 'message' => stripslashes($tmpvar['message']),
  193. 'attachments' => $tmpvar['attachments'],
  194. 'dt' => hesk_date($ticket['dt'], true),
  195. 'lastchange' => hesk_date($ticket['lastchange'], true),
  196. 'id' => $ticket['id'],
  197. );
  198. // 2. Add custom fields to the array
  199. foreach ($hesk_settings['custom_fields'] as $k => $v) {
  200. $info[$k] = $v['use'] ? $ticket[$k] : '';
  201. }
  202. // 3. Make sure all values are properly formatted for email
  203. $ticket = hesk_ticketToPlain($info, 1, 0);
  204. // --> Process custom fields before sending
  205. foreach ($hesk_settings['custom_fields'] as $k => $v) {
  206. $ticket[$k] = $v['use'] ? hesk_msgToPlain($ticket[$k], 1) : '';
  207. }
  208. // --> If ticket is assigned just notify the owner
  209. if ($ticket['owner']) {
  210. hesk_notifyAssignedStaff(false, 'new_reply_by_customer', $modsForHesk_settings, 'notify_reply_my');
  211. } // --> No owner assigned, find and notify appropriate staff
  212. else {
  213. hesk_notifyStaff('new_reply_by_customer', "`notify_reply_unassigned`='1'", $modsForHesk_settings);
  214. }
  215. return $ticket['trackid'];
  216. } // END REPLY
  217. // Not a reply, but a new ticket. Add it to the database
  218. $tmpvar['category'] = $set_category;
  219. $tmpvar['priority'] = $set_priority < 0 ? hesk_getCategoryPriority($tmpvar['category']) : $set_priority;
  220. // Auto assign tickets if aplicable
  221. $tmpvar['owner'] = 0;
  222. $tmpvar['history'] = $pop3 ? sprintf($hesklang['thist16'], hesk_date()) : sprintf($hesklang['thist11'], hesk_date());
  223. $tmpvar['openedby'] = $pop3 ? -2 : -1;
  224. $autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
  225. #print_r($autoassign_owner);
  226. if ($autoassign_owner) {
  227. $tmpvar['owner'] = $autoassign_owner['id'];
  228. $tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
  229. }
  230. // Custom fields will be empty as there is no reliable way of detecting them
  231. foreach ($hesk_settings['custom_fields'] as $k => $v) {
  232. $tmpvar[$k] = '';
  233. }
  234. $tmpvar['latitude'] = NULL;
  235. $tmpvar['longitude'] = NULL;
  236. $tmpvar['html'] = 0;
  237. $tmpvar['user_agent'] = NULL;
  238. $tmpvar['screen_resolution_width'] = "NULL";
  239. $tmpvar['screen_resolution_height'] = "NULL";
  240. $tmpvar['due_date'] = "";
  241. // Insert ticket to database
  242. $ticket = hesk_newTicket($tmpvar);
  243. // Notify the customer
  244. if ($hesk_settings['notify_new']) {
  245. $possible_SPAM = false;
  246. // Do we need to check subject for SPAM tags?
  247. if ($hesk_settings['notify_skip_spam']) {
  248. foreach ($hesk_settings['notify_spam_tags'] as $tag) {
  249. if (strpos($tmpvar['subject'], $tag) !== false) {
  250. $possible_SPAM = true;
  251. break;
  252. }
  253. }
  254. }
  255. // SPAM tags not found or not checked, send email
  256. if ($possible_SPAM === false) {
  257. hesk_notifyCustomer($modsForHesk_settings);
  258. }
  259. }
  260. // Need to notify staff?
  261. // --> From autoassign?
  262. if ($tmpvar['owner'] && $autoassign_owner['notify_assigned']) {
  263. hesk_notifyAssignedStaff($autoassign_owner, 'ticket_assigned_to_you', $modsForHesk_settings);
  264. } // --> No autoassign, find and notify appropriate staff
  265. elseif (!$tmpvar['owner']) {
  266. hesk_notifyStaff('new_ticket_staff', " `notify_new_unassigned` = '1' ", $modsForHesk_settings);
  267. }
  268. return $ticket['trackid'];
  269. } // END hesk_email2ticket()
  270. function hesk_encodeUTF8($in, $encoding)
  271. {
  272. $encoding = strtoupper($encoding);
  273. switch ($encoding) {
  274. case 'UTF-8':
  275. return $in;
  276. break;
  277. case 'ISO-8859-1':
  278. return utf8_encode($in);
  279. break;
  280. default:
  281. return iconv($encoding, 'UTF-8', $in);
  282. break;
  283. }
  284. } // END hesk_encodeUTF8()
  285. function hesk_stripQuotedText($message)
  286. {
  287. global $hesk_settings, $hesklang;
  288. // Stripping quoted text disabled?
  289. if (!$hesk_settings['strip_quoted']) {
  290. return $message;
  291. }
  292. // Loop through available languages and ty to find the tag
  293. foreach ($hesk_settings['languages'] as $language => $settings) {
  294. if (($found = strpos($message, $settings['hr'])) !== false) {
  295. // "Reply above this line" tag found, strip quoted reply
  296. $message = substr($message, 0, $found);
  297. $message .= "\n" . $hesklang['qrr'];
  298. // Set language to the detected language
  299. hesk_setLanguage($language);
  300. break;
  301. }
  302. }
  303. return $message;
  304. } // END hesk_stripQuotedText()
  305. function hesk_isReturnedEmail($tmpvar)
  306. {
  307. // Check noreply email addresses
  308. if (preg_match('/not?[\-_\.]?reply@/i', $tmpvar['email'])) {
  309. return true;
  310. }
  311. // Check mailer daemon email addresses
  312. if (preg_match('/mail(er)?[\-_\.]?daemon@/i', $tmpvar['email'])) {
  313. return true;
  314. }
  315. // Check autoreply subjects
  316. if (preg_match('/^[\[\(]?Auto(mat(ic|ed))?[ \-]?reply/i', $tmpvar['subject'])) {
  317. return true;
  318. }
  319. // Check out of office subjects
  320. if (preg_match('/^Out of Office/i', $tmpvar['subject'])) {
  321. return true;
  322. }
  323. // Check delivery failed email subjects
  324. if (
  325. preg_match('/DELIVERY FAILURE/i', $tmpvar['subject']) ||
  326. preg_match('/Undelivered Mail Returned to Sender/i', $tmpvar['subject']) ||
  327. preg_match('/Delivery Status Notification \(Failure\)/i', $tmpvar['subject']) ||
  328. preg_match('/Returned mail\: see transcript for details/i', $tmpvar['subject'])
  329. ) {
  330. return true;
  331. }
  332. // Check Mail Delivery sender name
  333. if (preg_match('/Mail[ \-_]?Delivery/i', $tmpvar['name'])) {
  334. return true;
  335. }
  336. // Check Delivery failed message
  337. if (preg_match('/postmaster@/i', $tmpvar['email']) && preg_match('/Delivery has failed to these recipients/i', $tmpvar['message'])) {
  338. return true;
  339. }
  340. // No pattern detected, seems like this is not a returned email
  341. return false;
  342. } // END hesk_isReturnedEmail()
  343. function hesk_isEmailLoop($email, $message_hash)
  344. {
  345. global $hesk_settings, $hesklang, $hesk_db_link;
  346. // If $hesk_settings['loop_hits'] is set to 0 this function is disabled
  347. if (!$hesk_settings['loop_hits']) {
  348. return false;
  349. }
  350. // Escape wildcards in email
  351. $email_like = hesk_dbEscape(hesk_dbLike($email));
  352. // Delete expired DB entries
  353. hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "pipe_loops` WHERE `dt` < (NOW() - INTERVAL " . intval($hesk_settings['loop_time']) . " SECOND) ");
  354. // Check current entry
  355. $res = hesk_dbQuery("SELECT `hits`, `message_hash` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "pipe_loops` WHERE `email` LIKE '{$email_like}' LIMIT 1");
  356. // Any active entry*
  357. if (hesk_dbNumRows($res)) {
  358. list($num, $md5) = hesk_dbFetchRow($res);
  359. $num++;
  360. // Number of emails in a time period reached?
  361. if ($num >= $hesk_settings['loop_hits']) {
  362. return true;
  363. }
  364. // Message exactly the same as in previous email?
  365. if ($message_hash == $md5) {
  366. return true;
  367. }
  368. // Update DB entry
  369. hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "pipe_loops` SET `hits` = `hits` + 1, `message_hash` = '" . hesk_dbEscape($message_hash) . "' WHERE `email` LIKE '{$email_like}'");
  370. } else {
  371. // First instance, insert a new database row
  372. hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "pipe_loops` (`email`, `message_hash`) VALUES ('" . hesk_dbEscape($email) . "', '" . hesk_dbEscape($message_hash) . "')");
  373. }
  374. // No loop rule trigered
  375. return false;
  376. } // END hesk_isEmailLoop()
  377. function hesk_cleanExit()
  378. {
  379. global $results;
  380. // Delete the temporary files
  381. deleteAll($results['tempdir']);
  382. // Return NULL
  383. return NULL;
  384. } // END hesk_cleanExit()