1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8//this script may only be included - so its better to die if called directly.
9if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) {
10	header('location: index.php');
11	exit;
12}
13
14class Messu extends TikiLib
15{
16
17	/**
18	 * Put sent message to 'sent' box
19	 */
20	function save_sent_message($user, $from, $to, $cc, $subject, $body, $priority, $replyto_hash = '')
21	{
22		global $prefs;
23		$userlib = TikiLib::lib('user');
24		$smarty = TikiLib::lib('smarty');
25
26		$subject = strip_tags($subject);
27		$body = strip_tags($body, '<a><b><img><i>');
28		// Prevent duplicates
29		$hash = md5($subject . $body);
30
31		if ($this->getOne(
32			'select count(*) from `messu_sent` where `user`=? and `user_from`=? and `hash`=?',
33			[$user, $from, $hash]
34		)
35		) {
36			return false;
37		}
38
39		$query = 'insert into `messu_sent`' .
40						' (`user`, `user_from`, `user_to`, `user_cc`, `subject`, `body`, `date`,' .
41						' `isRead`, `isReplied`, `isFlagged`, `priority`, `hash`, `replyto_hash`)' .
42						' values(?,?,?,?,?,?,?,?,?,?,?,?,?)';
43		$this->query(
44			$query,
45			[
46				$user,
47				$from,
48				$to,
49				$cc,
50				$subject,
51				$body,
52				(int) $this->now,
53				'n',
54				'n',
55				'n',
56				(int) $priority,
57				$hash,
58				$replyto_hash
59			]
60		);
61
62		return true;
63	}
64
65	/**
66	 * Send a message to a user
67	 *
68	 * @param string $user		username
69	 * @param string $from		from username
70	 * @param string $to		to username (again?)
71	 * @param string $cc		cc username
72	 * @param string $subject
73	 * @param string $body
74	 * @param int    $priority
75	 * @param string $replyto_hash
76	 * @param string $replyto_email y/n
77	 * @param string $bcc_sender	y/n send blind copy email to from user's
78	 * @return bool				success
79	 */
80	function post_message($user, $from, $to, $cc, $subject, $body, $priority, $replyto_hash = '', $replyto_email = '', $bcc_sender = '')
81	{
82		global $prefs;
83		$userlib = TikiLib::lib('user');
84		$smarty = TikiLib::lib('smarty');
85
86		$subject = strip_tags($subject);
87		$body = strip_tags($body, '<a><b><img><i>');
88		// Prevent duplicates
89		$hash = md5($subject . $body);
90
91		if ($this->getOne(
92			'select count(*) from `messu_messages` where `user`=? and `user_from`=? and `hash`=?',
93			[$user, $from, $hash]
94		)
95		) {
96			return false;
97		}
98
99		$query = 'insert into `messu_messages`' .
100					' (`user`, `user_from`, `user_to`, `user_cc`, `subject`, `body`, `date`' .
101					', `isRead`, `isReplied`, `isFlagged`, `priority`, `hash`, `replyto_hash`)' .
102					' values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
103
104		$this->query(
105			$query,
106			[
107				$user,
108				$from,
109				$to,
110				$cc,
111				$subject,
112				$body,
113				(int) $this->now,
114				'n',
115				'n',
116				'n',
117				(int) $priority,
118				$hash,
119				$replyto_hash
120			]
121		);
122
123		// Now check if the user should be notified by email
124		$magId = $this->getOne('select LAST_INSERT_ID() from `messu_messages`', []);
125		$foo = parse_url($_SERVER['REQUEST_URI']);
126		$machine = $this->httpPrefix(true) . $foo['path'];
127		$machine = str_replace('messu-compose', 'messu-mailbox', $machine);
128		$machine = str_replace('messu-broadcast', 'messu-mailbox', $machine);
129		// For non-sefurl calls, replace tiki-ajax_services with messu-mailbox if
130		// service called is user > send_message
131		if ($foo['query'] == "controller=user&action=send_message") {
132			$machine = str_replace('tiki-ajax_services', 'messu-mailbox', $machine);
133		}
134		//For sefurl service call user > send_message, redirect to messu-mailbox.php
135		$machine = str_replace('tiki-user-send_message', 'messu-mailbox.php', $machine);
136
137		if ($this->get_user_preference($user, 'minPrio', 6) <= $priority) {
138			if (! isset($_SERVER['SERVER_NAME'])) {
139				$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
140			}
141			$email = $userlib->get_user_email($user);
142			if ($userlib->user_exists($from)) {
143				$from_email = $userlib->get_user_email($from);		// $from_email required for TikiMail constructor
144			} elseif ($from == 'tiki-contact.php' && ! empty($prefs['sender_email'])) {
145				$from_email = $prefs['sender_email'];
146			} else {
147				return false;										// non-existent users can't send messages (etc)
148			}
149			if ($email) {
150				include_once('lib/webmail/tikimaillib.php');
151				$smarty->assign('mail_site', $_SERVER['SERVER_NAME']);
152				$smarty->assign('mail_machine', $machine);
153				$smarty->assign('mail_date', $this->now);
154				$smarty->assign('mail_user', stripslashes($user));
155				$smarty->assign('mail_from', stripslashes($from));
156				$smarty->assign('mail_subject', stripslashes($subject));
157				$smarty->assign('mail_body', stripslashes($body));
158				$smarty->assign('mail_truncate', $prefs['messu_truncate_internal_message']);
159				$smarty->assign('messageid', $magId);
160
161				try {
162					$mail = new TikiMail($user, $from_email);
163					$lg = $this->get_user_preference($user, 'language', $prefs['site_language']);
164
165					if (empty($subject)) {
166						$s = $smarty->fetchLang($lg, 'mail/messu_message_notification_subject.tpl');
167						$mail->setSubject(sprintf($s, $_SERVER['SERVER_NAME']));
168					} else {
169						$mail->setSubject($subject);
170					}
171
172					$mail_data = $smarty->fetchLang($lg, 'mail/messu_message_notification.tpl');
173					$mail->setText($mail_data);
174
175					if ($from_email) {
176						if ($bcc_sender === 'y' && ! empty($from_email)) {
177							$mail->setBcc($from_email);
178						}
179
180						if ($replyto_email !== 'y' && $userlib->get_user_preference($from, 'email is public', 'n') == 'n') {
181							$from_email = '';	// empty $from_email if not to be used - saves getting it twice
182						}
183
184						if (! empty($from_email)) {
185							$mail->setReplyTo($from_email);
186						}
187					}
188
189					if (! $mail->send([$email], 'mail')) {
190						return false; //TODO echo $mail->errors;
191					}
192				} catch (Zend\Mail\Exception\ExceptionInterface $e) {
193					Feedback::error($e->getMessage());
194					return false;
195				}
196			}
197		}
198		return true;
199	}
200
201	/**
202	 * Get a list of messages from users mailbox or users mail archive (from
203	 * which depends on $dbsource)
204	 */
205	function list_user_messages(
206		$user,
207		$offset,
208		$maxRecords,
209		$sort_mode,
210		$find,
211		$flag = '',
212		$flagval = '',
213		$prio = '',
214		$dbsource,
215		$replyto_hash = '',
216		$orig_or_reply = 'r'
217	) {
218
219		if ($dbsource == '') {
220			$dbsource = 'messages';
221		}
222
223		$bindvars = [$user];
224		$mid = '';
225
226		if ($prio) {
227			$mid = ' and priority=? ';
228			$bindvars[] = $prio;
229		}
230		if ($replyto_hash) {
231			// find replies
232			if ($orig_or_reply == 'r') {
233				$mid .= ' and replyto_hash=? ';
234			// find original for the reply
235			} else {
236				$mid .= ' and hash=? ';
237			}
238			$bindvars[] = $replyto_hash;
239		}
240		if ($flag) {
241			// Process the flags
242			$mid .= " and `$flag`=? ";
243			$bindvars[] = $flagval;
244		}
245		if ($find) {
246			$findesc = '%' . $find . '%';
247			$mid .= ' and (`subject` like ? or `body` like ?)';
248			$bindvars[] = $findesc;
249			$bindvars[] = $findesc;
250		}
251
252		$query = 'select * from `messu_' . $dbsource . "` where `user`=? $mid order by " .
253						$this->convertSortMode($sort_mode) . ',' . $this->convertSortMode('msgId_desc');
254		$query_cant = 'select count(*) from `messu_' . $dbsource . "` where `user`=? $mid";
255		$result = $this->query($query, $bindvars, $maxRecords, $offset);
256		$cant = $this->getOne($query_cant, $bindvars);
257		$ret = [];
258
259		while ($res = $result->fetchRow()) {
260			$res['len'] = strlen($res['body']);
261
262			if (empty($res['subject'])) {
263				$res['subject'] = tra('NONE');
264			}
265
266			$ret[] = $res;
267		}
268
269		$retval = [];
270		$retval['data'] = $ret;
271		$retval['cant'] = $cant;
272		return $retval;
273	}
274
275	/**
276	 * Get the number of messages in the users mailbox or mail archive (from
277	 * which depends on $dbsource)
278	 */
279	function count_messages($user, $dbsource = 'messages', $unreadOnly = false, $newSince = 0)
280	{
281		if ($dbsource == '') {
282			$dbsource = 'messages';
283		}
284
285		$bindvars = [$user];
286		$query_cant = 'select count(*) from `messu_' . $dbsource . '` where `user`=?';
287		if ($unreadOnly == true) {
288			$query_cant .= ' and `isRead`="n"';
289		}
290		if (! empty($newSince)) {
291			$query_cant .= ' and `date` >= ?';
292			$bindvars[] = $newSince;
293		}
294		$cant = $this->getOne($query_cant, $bindvars);
295		return $cant;
296	}
297
298	/**
299	 * Update message flagging
300	 */
301	function flag_message($user, $msgId, $flag, $val, $dbsource = 'messages')
302	{
303		if (! $msgId || ! (in_array($flag, ['isRead', 'isFlagged']))) {
304			return false;
305		}
306
307		if ($dbsource == '') {
308			$dbsource = 'messages';
309		}
310
311		$query = 'update `messu_' . $dbsource . "` set `$flag`=? where `user`=? and `msgId`=?";
312		return $this->query($query, [$val, $user, (int)$msgId]);
313	}
314
315	/**
316	 * Mark a message as replied
317	 */
318	function mark_replied($user, $replyto_hash, $dbsource = 'sent')
319	{
320		if ((! $replyto_hash) || ($replyto_hash == '')) {
321			return false;
322		}
323
324		if ($dbsource == '') {
325			$dbsource = 'sent';
326		}
327
328		$query = 'update `messu_' . $dbsource . '` set `isReplied`=? where `user`=? and `hash`=?';
329		$this->query($query, ['y', $user, $replyto_hash]);
330	}
331
332	/**
333	 * Delete message from mailbox or users mail archive (from which depends on
334	 * $dbsource)
335	 */
336	function delete_message($user, $msgId, $dbsource = 'messages')
337	{
338		if (! $msgId) {
339			return false;
340		}
341
342		if ($dbsource == '') {
343			$dbsource = 'messages';
344		}
345
346		$query = 'delete from `messu_' . $dbsource . '` where `user`=? and `msgId`=?';
347		return $this->query($query, [$user, (int)$msgId]);
348	}
349
350	/**
351	 * Move message from mailbox to users mail archive
352	 */
353	function archive_message($user, $msgId, $dbsource = 'messages')
354	{
355		if (! $msgId) {
356			return false;
357		}
358
359		if ($dbsource == '') {
360			$dbsource = 'messages';
361		}
362
363		$columns = '`user`, `user_from`, `user_to`, `user_cc`, `subject`, `body`, `date`, `isRead`, `isReplied`, `isFlagged`, `priority`, `hash`, `replyto_hash`';
364		$query = 'insert into `messu_archive` (' . $columns . ') select ' . $columns . ' from `messu_' . $dbsource . '` where `user`=? and `msgId`=?';
365		$this->query($query, [$user, (int)$msgId]);
366
367		$query = 'delete from `messu_' . $dbsource . '` where `user`=? and `msgId`=?';
368		return $this->query($query, [$user, (int)$msgId]);
369	}
370
371	/**
372	 * Move message from archive to users mailbox
373	 */
374	function unarchive_message($user, $msgId, $dbsource = 'messages')
375	{
376		if (! $msgId) {
377			return false;
378		}
379
380		$dbsource = $this->get_archive_source($user, $msgId);
381
382		if ($dbsource == '') {
383			$dbsource = 'messages';
384		}
385
386		$columns = '`user`, `user_from`, `user_to`, `user_cc`, `subject`, `body`, `date`, `isRead`, `isReplied`, `isFlagged`, `priority`, `hash`, `replyto_hash`';
387		$query = 'insert into `messu_' . $dbsource . '` (' . $columns . ') select ' . $columns . ' from `messu_archive` where `user`=? and `msgId`=?';
388		$this->query($query, [$user, (int)$msgId]);
389
390		$query = 'delete from `messu_archive` where `user`=? and `msgId`=?';
391		return $this->query($query, [$user, (int)$msgId]);
392	}
393
394	/**
395	 * Move read message older than x days from mailbox to users mail archive
396	 */
397	function archive_messages($user, $days, $dbsource = 'messages')
398	{
399		if ($days < 1) {
400			return false;
401		}
402
403		if ($dbsource == '') {
404			$dbsource = 'messages';
405		}
406
407		$age = $this->now - ($days * 3600 * 24);
408
409		// TODO: only move as much msgs into archive as there is space left in there
410		$query = 'insert into `messu_archive` select * from `messu_' . $dbsource . '` where `user`=? and `isRead`=? and `date`<=?';
411		$this->query($query, [$user, 'y', (int)$age]);
412
413		$query = 'delete from `messu_' . $dbsource . '` where `user`=? and `isRead`=? and `date`<=?';
414		$this->query($query, [$user, 'y', (int)$age]);
415	}
416
417	/**
418	 * Move forward to the next message and get it from the database
419	 */
420	function get_next_message($user, $msgId, $sort_mode, $find, $flag, $flagval, $prio, $dbsource = 'messages')
421	{
422		if (! $msgId) {
423			return 0;
424		}
425
426		if ($dbsource == '') {
427			$dbsource = 'messages';
428		}
429
430		$mid = '';
431		$bindvars = [$user,(int)$msgId];
432		if ($prio) {
433			$mid .= ' and priority=? ';
434			$bindvars[] = $prio;
435		}
436
437		if ($flag) {
438			// Process the flags
439			$mid .= " and `$flag`=? ";
440			$bindvars[] = $flagval;
441		}
442
443		if ($find) {
444			$findesc = '%' . $find . '%';
445			$mid .= ' and (`subject` like ? or `body` like ?)';
446			$bindvars[] = $findesc;
447			$bindvars[] = $findesc;
448		}
449
450		$query = 'select min(`msgId`) as `nextmsg` from `messu_' . $dbsource . "` where `user`=? and `msgId` > ? $mid";
451		$result = $this->query($query, $bindvars, 1, 0);
452		$res = $result->fetchRow();
453
454		if (! $res) {
455			return false;
456		}
457
458		return $res['nextmsg'];
459	}
460
461	/**
462	 * Move backward to the next message and get it from the database
463	 */
464	function get_prev_message($user, $msgId, $sort_mode, $find, $flag, $flagval, $prio, $dbsource = 'messages')
465	{
466		if (! $msgId) {
467			return 0;
468		}
469
470		if ($dbsource == '') {
471			$dbsource = 'messages';
472		}
473
474		$mid = '';
475		$bindvars = [$user, (int)$msgId];
476		if ($prio) {
477			$mid .= ' and priority=? ';
478			$bindvars[] = $prio;
479		}
480
481		if ($flag) {
482			// Process the flags
483			$mid .= " and `$flag`=? ";
484			$bindvars[] = $flagval;
485		}
486		if ($find) {
487			$findesc = '%' . $find . '%';
488			$mid .= ' and (`subject` like ? or `body` like ?)';
489			$bindvars[] = $findesc;
490			$bindvars[] = $findesc;
491		}
492
493		$query = 'select max(`msgId`) as `prevmsg` from `messu_' . $dbsource . "` where `user`=? and `msgId` < ? $mid";
494		$result = $this->query($query, $bindvars, 1, 0);
495		$res = $result->fetchRow();
496
497		if (! $res) {
498			return false;
499		}
500
501		return $res['prevmsg'];
502	}
503
504	/**
505	 * Get a message from the users mailbox or his mail archive (from which
506	 * depends on $dbsource)
507	 */
508	function get_message($user, $msgId, $dbsource = 'messages')
509	{
510		if ($dbsource == '') {
511			$dbsource = 'messages';
512		}
513
514		$bindvars = [$user, (int)$msgId];
515		$query = 'select * from `messu_' . $dbsource . '` where `user`=? and `msgId`=?';
516		$result = $this->query($query, $bindvars);
517		$res = $result->fetchRow();
518		$res['parsed'] = TikiLib::lib('parser')->parse_data($res['body']);
519		$res['len'] = strlen($res['parsed']);
520
521		if (empty($res['subject'])) {
522			$res['subject'] = tra('NONE');
523		}
524
525		return $res;
526	}
527
528	/**
529	 * Get message from the users mailbox or his mail archive (from which
530	 * depends on $dbsource)
531	 */
532	function get_messages($user, $dbsource = 'messages', $subject = '', $to = '', $from = '')
533	{
534		if ($dbsource == '') {
535			$dbsource = 'messages';
536		}
537
538		$bindvars[] = $user;
539
540		$mid = '';
541
542		// find mails with a specific subject
543		if ($subject <> '') {
544			$findesc = '%' . $subject . '%';
545			$bindvars[] = $findesc;
546			$mid .= ' and `subject` like ?';
547		}
548
549		// find mails to a specific user (to, cc, bcc)
550		if ($to <> '') {
551			$findesc = '%' . $to . '%';
552			$bindvars[] = $findesc;
553			$bindvars[] = $findesc;
554			$bindvars[] = $findesc;
555			$mid .= ' and (`user_to` like ? or `user_cc` like ? or `user_bcc` like ?)';
556		}
557
558		// find mails from a specific user
559		if ($from <> '') {
560			$findesc = '%' . $from . '%';
561			$bindvars[] = $findesc;
562			$mid .= ' and `user_from` like ?';
563		}
564		$query = 'select * from `messu_' . $dbsource . "` where `user`=? $mid";
565
566		$result = $this->query($query, $bindvars);
567		$ret = [];
568
569		while ($res = $result->fetchRow()) {
570			$res['parsed'] = TikiLib::lib('parser')->parse_data($res['body']);
571			$res['len'] = strlen($res['parsed']);
572			if (empty($res['subject'])) {
573				$res['subject'] = tra('NONE');
574			}
575			$ret[] = $res;
576		}
577		return $ret;
578	}
579
580	/**
581	 * Get mail source info from the  mail archive
582	 */
583	function get_archive_source($user, $msgId)
584	{
585		$dbsource ='';
586
587		$res= $this->get_message($user, $msgId, 'archive');
588
589		if($res['user_from']==$user){
590			$dbsource = 'sent';
591		}
592
593		return $dbsource;
594	}
595
596}
597