2013-03-15 10:20:25 +01:00
< ? php
/**
* Name : Mail Stream
* Description : Mail all items coming into your network feed to an email address
2016-02-02 15:11:13 +01:00
* Version : 1.0
2013-03-15 10:20:25 +01:00
* Author : Matthew Exon < http :// mat . exon . name >
*/
function mailstream_install () {
2017-01-08 16:18:16 +01:00
register_hook ( 'plugin_settings' , 'addon/mailstream/mailstream.php' , 'mailstream_plugin_settings' );
register_hook ( 'plugin_settings_post' , 'addon/mailstream/mailstream.php' , 'mailstream_plugin_settings_post' );
register_hook ( 'post_local_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_hook' );
register_hook ( 'post_remote_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_hook' );
register_hook ( 'cron' , 'addon/mailstream/mailstream.php' , 'mailstream_cron' );
2013-03-15 10:20:25 +01:00
2017-01-08 16:18:16 +01:00
if ( get_config ( 'mailstream' , 'dbversion' ) == '0.1' ) {
q ( 'ALTER TABLE `mailstream_item` DROP INDEX `uid`' );
q ( 'ALTER TABLE `mailstream_item` DROP INDEX `contact-id`' );
q ( 'ALTER TABLE `mailstream_item` DROP INDEX `plink`' );
q ( 'ALTER TABLE `mailstream_item` CHANGE `plink` `uri` char(255) NOT NULL' );
set_config ( 'mailstream' , 'dbversion' , '0.2' );
}
if ( get_config ( 'mailstream' , 'dbversion' ) == '0.2' ) {
q ( 'DELETE FROM `pconfig` WHERE `cat` = "mailstream" AND `k` = "delay"' );
set_config ( 'mailstream' , 'dbversion' , '0.3' );
}
if ( get_config ( 'mailstream' , 'dbversion' ) == '0.3' ) {
q ( 'ALTER TABLE `mailstream_item` CHANGE `created` `created` timestamp NOT NULL DEFAULT now()' );
q ( 'ALTER TABLE `mailstream_item` CHANGE `completed` `completed` timestamp NULL DEFAULT NULL' );
set_config ( 'mailstream' , 'dbversion' , '0.4' );
}
if ( get_config ( 'mailstream' , 'dbversion' ) == '0.4' ) {
q ( 'ALTER TABLE `mailstream_item` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin' );
set_config ( 'mailstream' , 'dbversion' , '0.5' );
}
if ( get_config ( 'mailstream' , 'dbversion' ) == '0.5' ) {
set_config ( 'mailstream' , 'dbversion' , '1.0' );
}
2016-02-02 15:11:13 +01:00
2017-01-08 16:18:16 +01:00
if ( get_config ( 'retriever' , 'dbversion' ) != '1.0' ) {
$schema = file_get_contents ( dirname ( __file__ ) . '/database.sql' );
$arr = explode ( ';' , $schema );
foreach ( $arr as $a ) {
$r = q ( $a );
}
set_config ( 'mailstream' , 'dbversion' , '1.0' );
}
2013-03-15 10:20:25 +01:00
}
function mailstream_uninstall () {
2017-01-08 16:18:16 +01:00
unregister_hook ( 'plugin_settings' , 'addon/mailstream/mailstream.php' , 'mailstream_plugin_settings' );
unregister_hook ( 'plugin_settings_post' , 'addon/mailstream/mailstream.php' , 'mailstream_plugin_settings_post' );
unregister_hook ( 'post_local' , 'addon/mailstream/mailstream.php' , 'mailstream_post_local_hook' );
unregister_hook ( 'post_remote' , 'addon/mailstream/mailstream.php' , 'mailstream_post_remote_hook' );
unregister_hook ( 'post_local_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_local_hook' );
unregister_hook ( 'post_remote_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_remote_hook' );
unregister_hook ( 'post_local_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_hook' );
unregister_hook ( 'post_remote_end' , 'addon/mailstream/mailstream.php' , 'mailstream_post_hook' );
unregister_hook ( 'cron' , 'addon/mailstream/mailstream.php' , 'mailstream_cron' );
unregister_hook ( 'incoming_mail' , 'addon/mailstream/mailstream.php' , 'mailstream_incoming_mail' );
2013-03-15 10:20:25 +01:00
}
function mailstream_module () {}
function mailstream_plugin_admin ( & $a , & $o ) {
2017-01-08 16:18:16 +01:00
$frommail = get_config ( 'mailstream' , 'frommail' );
$template = get_markup_template ( 'admin.tpl' , 'addon/mailstream/' );
$config = array ( 'frommail' ,
t ( 'From Address' ),
$frommail ,
t ( 'Email address that stream items will appear to be from.' ));
$o .= replace_macros ( $template , array (
'$frommail' => $config ,
'$submit' => t ( 'Save Settings' )));
2013-03-15 10:20:25 +01:00
}
function mailstream_plugin_admin_post ( $a ) {
2017-01-08 16:18:16 +01:00
if ( x ( $_POST , 'frommail' )) {
set_config ( 'mailstream' , 'frommail' , $_POST [ 'frommail' ]);
}
2013-03-15 10:20:25 +01:00
}
function mailstream_generate_id ( $a , $uri ) {
2017-01-08 16:18:16 +01:00
// http://www.jwz.org/doc/mid.html
$host = $a -> get_hostname ();
$resource = hash ( 'md5' , $uri );
$message_id = " < " . $resource . " @ " . $host . " > " ;
logger ( 'mailstream: Generated message ID ' . $message_id . ' for URI ' . $uri , LOGGER_DEBUG );
return $message_id ;
2013-03-15 10:20:25 +01:00
}
2016-02-02 15:11:13 +01:00
function mailstream_post_hook ( & $a , & $item ) {
2017-01-08 16:18:16 +01:00
if ( ! get_pconfig ( $item [ 'uid' ], 'mailstream' , 'enabled' )) {
return ;
}
if ( ! $item [ 'uid' ]) {
return ;
}
if ( ! $item [ 'contact-id' ]) {
return ;
}
if ( ! $item [ 'uri' ]) {
return ;
}
if ( get_pconfig ( $item [ 'uid' ], 'mailstream' , 'nolikes' )) {
if ( $item [ 'verb' ] == ACTIVITY_LIKE ) {
return ;
}
}
2013-04-01 02:32:06 +02:00
2017-01-08 16:18:16 +01:00
$message_id = mailstream_generate_id ( $a , $item [ 'uri' ]);
q ( " INSERT INTO `mailstream_item` (`uid`, `contact-id`, `uri`, `message-id`) " .
" VALUES (%d, '%s', '%s', '%s') " , intval ( $item [ 'uid' ]),
intval ( $item [ 'contact-id' ]), dbesc ( $item [ 'uri' ]), dbesc ( $message_id ));
$r = q ( 'SELECT * FROM `mailstream_item` WHERE `uid` = %d AND `contact-id` = %d AND `uri` = "%s"' , intval ( $item [ 'uid' ]), intval ( $item [ 'contact-id' ]), dbesc ( $item [ 'uri' ]));
if ( count ( $r ) != 1 ) {
logger ( 'mailstream_post_remote_hook: Unexpected number of items returned from mailstream_item' , LOGGER_NORMAL );
return ;
}
$ms_item = $r [ 0 ];
logger ( 'mailstream_post_remote_hook: created mailstream_item '
. $ms_item [ 'id' ] . ' for item ' . $item [ 'uri' ] . ' '
. $item [ 'uid' ] . ' ' . $item [ 'contact-id' ], LOGGER_DATA );
$user = mailstream_get_user ( $item [ 'uid' ]);
if ( ! $user ) {
logger ( 'mailstream_post_remote_hook: no user ' . $item [ 'uid' ], LOGGER_NORMAL );
return ;
}
mailstream_send ( $a , $ms_item [ 'message-id' ], $item , $user );
2013-04-01 02:32:06 +02:00
}
function mailstream_get_user ( $uid ) {
2017-01-08 16:18:16 +01:00
$r = q ( 'SELECT * FROM `user` WHERE `uid` = %d' , intval ( $uid ));
if ( count ( $r ) != 1 ) {
logger ( 'mailstream_post_remote_hook: Unexpected number of users returned' , LOGGER_NORMAL );
return ;
}
return $r [ 0 ];
2013-03-15 10:20:25 +01:00
}
function mailstream_do_images ( $a , & $item , & $attachments ) {
2017-01-08 16:18:16 +01:00
if ( ! get_pconfig ( $item [ 'uid' ], 'mailstream' , 'attachimg' )) {
return ;
}
$attachments = array ();
$baseurl = $a -> get_baseurl ();
preg_match_all ( " / \ [img \ =([0-9]*)x([0-9]*) \ ](.*?) \ [ \ /img \ ]/ism " , $item [ " body " ], $matches1 );
preg_match_all ( " / \ [img \ ](.*?) \ [ \ /img \ ]/ism " , $item [ " body " ], $matches2 );
foreach ( array_merge ( $matches1 [ 3 ], $matches2 [ 1 ]) as $url ) {
$redirects ;
$cookiejar = tempnam ( get_temppath (), 'cookiejar-mailstream-' );
$attachments [ $url ] = array (
'data' => fetch_url ( $url , true , $redirects , 0 , Null , $cookiejar ),
'guid' => hash ( " crc32 " , $url ),
'filename' => basename ( $url ),
'type' => $a -> get_curl_content_type ());
if ( strlen ( $attachments [ $url ][ 'data' ])) {
$item [ 'body' ] = str_replace ( $url , 'cid:' . $attachments [ $url ][ 'guid' ], $item [ 'body' ]);
continue ;
}
}
return $attachments ;
2016-02-02 15:11:13 +01:00
}
function mailstream_sender ( $item ) {
2017-01-08 16:18:16 +01:00
$r = q ( 'SELECT * FROM `contact` WHERE `id` = %d' , $item [ 'contact-id' ]);
if ( dbm :: is_result ( $r )) {
$contact = $r [ 0 ];
if ( $contact [ 'name' ] != $item [ 'author-name' ]) {
return $contact [ 'name' ] . ' - ' . $item [ 'author-name' ];
}
}
return $item [ 'author-name' ];
2016-02-02 15:11:13 +01:00
}
function mailstream_decode_subject ( $subject ) {
2017-01-08 16:18:16 +01:00
$html = bbcode ( $subject );
if ( ! $html ) {
return $subject ;
}
$notags = strip_tags ( $html );
if ( ! $notags ) {
return $subject ;
}
$noentity = html_entity_decode ( $notags );
if ( ! $noentity ) {
return $notags ;
}
$nocodes = preg_replace_callback ( " /(&#[0-9]+;)/ " , function ( $m ) { return mb_convert_encoding ( $m [ 1 ], " UTF-8 " , " HTML-ENTITIES " ); }, $noentity );
if ( ! $nocodes ) {
return $noentity ;
}
$trimmed = trim ( $nocodes );
if ( ! $trimmed ) {
return $nocodes ;
}
return $trimmed ;
2013-03-15 10:20:25 +01:00
}
function mailstream_subject ( $item ) {
2017-01-08 16:18:16 +01:00
if ( $item [ 'title' ]) {
return mailstream_decode_subject ( $item [ 'title' ]);
}
$parent = $item [ 'thr-parent' ];
// Don't look more than 100 levels deep for a subject, in case of loops
for ( $i = 0 ; ( $i < 100 ) && $parent ; $i ++ ) {
$r = q ( " SELECT `thr-parent`, `title` FROM `item` WHERE `uri` = '%s' " , dbesc ( $parent ));
if ( ! dbm :: is_result ( $r )) {
break ;
}
if ( $r [ 0 ][ 'thr-parent' ] === $parent ) {
break ;
}
if ( $r [ 0 ][ 'title' ]) {
return t ( 'Re:' ) . ' ' . mailstream_decode_subject ( $r [ 0 ][ 'title' ]);
}
$parent = $r [ 0 ][ 'thr-parent' ];
}
$r = q ( " SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d " ,
intval ( $item [ 'contact-id' ]), intval ( $item [ 'uid' ]));
$contact = $r [ 0 ];
if ( $contact [ 'network' ] === 'dfrn' ) {
return t ( " Friendica post " );
}
if ( $contact [ 'network' ] === 'dspr' ) {
return t ( " Diaspora post " );
}
if ( $contact [ 'network' ] === 'face' ) {
$text = mailstream_decode_subject ( $item [ 'body' ]);
// For some reason these do show up in Facebook
$text = preg_replace ( '/\xA0$/' , '' , $text );
$subject = ( strlen ( $text ) > 150 ) ? ( substr ( $text , 0 , 140 ) . '...' ) : $text ;
return preg_replace ( '/\\s+/' , ' ' , $subject );
}
if ( $contact [ 'network' ] === 'feed' ) {
return t ( " Feed item " );
}
if ( $contact [ 'network' ] === 'mail' ) {
return t ( " Email " );
}
return t ( " Friendica Item " );
2013-03-15 10:20:25 +01:00
}
2016-02-02 15:11:13 +01:00
function mailstream_send ( $a , $message_id , $item , $user ) {
2017-01-08 16:18:16 +01:00
if ( ! $item [ 'visible' ]) {
return ;
}
if ( ! $message_id ) {
return ;
}
require_once ( dirname ( __file__ ) . '/phpmailer/class.phpmailer.php' );
require_once ( 'include/bbcode.php' );
$attachments = array ();
mailstream_do_images ( $a , $item , $attachments );
$frommail = get_config ( 'mailstream' , 'frommail' );
if ( $frommail == " " ) {
$frommail = 'friendica@localhost.local' ;
}
$address = get_pconfig ( $item [ 'uid' ], 'mailstream' , 'address' );
if ( ! $address ) {
$address = $user [ 'email' ];
}
$mail = new PHPmailer ;
try {
$mail -> XMailer = 'Friendica Mailstream Plugin' ;
$mail -> SetFrom ( $frommail , mailstream_sender ( $item ));
$mail -> AddAddress ( $address , $user [ 'username' ]);
$mail -> MessageID = $message_id ;
$mail -> Subject = mailstream_subject ( $item );
if ( $item [ 'thr-parent' ] != $item [ 'uri' ]) {
$mail -> addCustomHeader ( 'In-Reply-To: ' . mailstream_generate_id ( $a , $item [ 'thr-parent' ]));
}
$mail -> addCustomHeader ( 'X-Friendica-Mailstream-URI: ' . $item [ 'uri' ]);
$mail -> addCustomHeader ( 'X-Friendica-Mailstream-Plink: ' . $item [ 'plink' ]);
$encoding = 'base64' ;
foreach ( $attachments as $url => $image ) {
$mail -> AddStringEmbeddedImage ( $image [ 'data' ], $image [ 'guid' ], $image [ 'filename' ], $encoding , $image [ 'type' ]);
}
$mail -> IsHTML ( true );
$mail -> CharSet = 'utf-8' ;
$template = get_markup_template ( 'mail.tpl' , 'addon/mailstream/' );
$item [ 'body' ] = bbcode ( $item [ 'body' ]);
$item [ 'url' ] = $a -> get_baseurl () . '/display/' . $user [ 'nickname' ] . '/' . $item [ 'id' ];
$mail -> Body = replace_macros ( $template , array (
'$upstream' => t ( 'Upstream' ),
'$local' => t ( 'Local' ),
'$item' => $item ));
mailstream_html_wrap ( $mail -> Body );
if ( ! $mail -> Send ()) {
throw new Exception ( $mail -> ErrorInfo );
}
logger ( 'mailstream_send sent message ' . $mail -> MessageID . ' ' . $mail -> Subject , LOGGER_DEBUG );
} catch ( phpmailerException $e ) {
logger ( 'mailstream_send PHPMailer exception sending message ' . $message_id . ': ' . $e -> errorMessage (), LOGGER_NORMAL );
} catch ( Exception $e ) {
logger ( 'mailstream_send exception sending message ' . $message_id . ': ' . $e -> getMessage (), LOGGER_NORMAL );
}
// In case of failure, still set the item to completed. Otherwise
// we'll just try to send it over and over again and it'll fail
// every time.
q ( 'UPDATE `mailstream_item` SET `completed` = now() WHERE `message-id` = "%s"' , dbesc ( $message_id ));
2016-02-02 15:11:13 +01:00
}
/**
* Email tends to break if you send excessively long lines . To make
* bbcode ' s output suitable for transmission , we try to break things
* up so that lines are about 200 characters .
*/
function mailstream_html_wrap ( & $text )
{
2017-01-08 16:18:16 +01:00
$lines = str_split ( $text , 200 );
for ( $i = 0 ; $i < count ( $lines ); $i ++ ) {
$lines [ $i ] = preg_replace ( '/ /' , " \n " , $lines [ $i ], 1 );
}
$text = implode ( $lines );
2013-03-15 10:20:25 +01:00
}
function mailstream_cron ( $a , $b ) {
2017-01-08 16:18:16 +01:00
// Only process items older than an hour in cron. This is because
// we want to give mailstream_post_remote_hook a fair chance to
// send the email itself before cron jumps in. Only if
// mailstream_post_remote_hook fails for some reason will this get
// used, and in that case it's worth holding off a bit anyway.
$ms_item_ids = q ( " SELECT `mailstream_item`.`message-id`, `mailstream_item`.`uri`, `item`.`id` FROM `mailstream_item` JOIN `item` ON (`mailstream_item`.`uid` = `item`.`uid` AND `mailstream_item`.`uri` = `item`.`uri` AND `mailstream_item`.`contact-id` = `item`.`contact-id`) WHERE `mailstream_item`.`completed` IS NULL AND `mailstream_item`.`created` < DATE_SUB(NOW(), INTERVAL 1 HOUR) AND `item`.`visible` = 1 ORDER BY `mailstream_item`.`created` LIMIT 100 " );
logger ( 'mailstream_cron processing ' . count ( $ms_item_ids ) . ' items' , LOGGER_DEBUG );
foreach ( $ms_item_ids as $ms_item_id ) {
if ( ! $ms_item_id [ 'message-id' ] || ! strlen ( $ms_item_id [ 'message-id' ])) {
logger ( 'mailstream_cron: Item ' . $ms_item_id [ 'id' ] . ' URI ' . $ms_item_id [ 'uri' ] . ' has no message-id' , LOGGER_NORMAL );
}
$items = q ( 'SELECT * FROM `item` WHERE `id` = %d' , $ms_item_id [ 'id' ]);
$item = $items [ 0 ];
$users = q ( " SELECT * FROM `user` WHERE `uid` = %d " , intval ( $item [ 'uid' ]));
$user = $users [ 0 ];
if ( $user && $item ) {
mailstream_send ( $a , $ms_item_id [ 'message-id' ], $item , $user );
}
else {
logger ( 'mailstream_cron: Unable to find item ' . $ms_item_id [ 'id' ], LOGGER_NORMAL );
q ( " UPDATE `mailstream_item` SET `completed` = now() WHERE `message-id` = %d " , intval ( $ms_item [ 'message-id' ]));
}
}
mailstream_tidy ();
2013-03-15 10:20:25 +01:00
}
function mailstream_plugin_settings ( & $a , & $s ) {
2017-01-08 16:18:16 +01:00
$enabled = get_pconfig ( local_user (), 'mailstream' , 'enabled' );
$address = get_pconfig ( local_user (), 'mailstream' , 'address' );
$nolikes = get_pconfig ( local_user (), 'mailstream' , 'nolikes' );
$attachimg = get_pconfig ( local_user (), 'mailstream' , 'attachimg' );
$template = get_markup_template ( 'settings.tpl' , 'addon/mailstream/' );
$s .= replace_macros ( $template , array (
'$enabled' => array (
'mailstream_enabled' ,
t ( 'Enabled' ),
$enabled ),
'$address' => array (
'mailstream_address' ,
t ( 'Email Address' ),
$address ,
t ( " Leave blank to use your account email address " )),
'$nolikes' => array (
'mailstream_nolikes' ,
t ( 'Exclude Likes' ),
$nolikes ,
t ( " Check this to omit mailing \" Like \" notifications " )),
'$attachimg' => array (
'mailstream_attachimg' ,
t ( 'Attach Images' ),
$attachimg ,
t ( " Download images in posts and attach them to the email. Useful for reading email while offline. " )),
'$title' => t ( 'Mail Stream Settings' ),
'$submit' => t ( 'Save Settings' )));
2013-03-15 10:20:25 +01:00
}
function mailstream_plugin_settings_post ( $a , $post ) {
2017-01-08 16:18:16 +01:00
if ( $_POST [ 'mailstream_address' ] != " " ) {
set_pconfig ( local_user (), 'mailstream' , 'address' , $_POST [ 'mailstream_address' ]);
}
else {
del_pconfig ( local_user (), 'mailstream' , 'address' );
}
if ( $_POST [ 'mailstream_nolikes' ]) {
set_pconfig ( local_user (), 'mailstream' , 'nolikes' , $_POST [ 'mailstream_enabled' ]);
}
else {
del_pconfig ( local_user (), 'mailstream' , 'nolikes' );
}
if ( $_POST [ 'mailstream_enabled' ]) {
set_pconfig ( local_user (), 'mailstream' , 'enabled' , $_POST [ 'mailstream_enabled' ]);
}
else {
del_pconfig ( local_user (), 'mailstream' , 'enabled' );
}
if ( $_POST [ 'mailstream_attachimg' ]) {
set_pconfig ( local_user (), 'mailstream' , 'attachimg' , $_POST [ 'mailstream_attachimg' ]);
}
else {
del_pconfig ( local_user (), 'mailstream' , 'attachimg' );
}
2013-03-15 10:20:25 +01:00
}
function mailstream_tidy () {
2017-01-08 16:18:16 +01:00
$r = q ( " SELECT id FROM mailstream_item WHERE completed IS NOT NULL AND completed < DATE_SUB(NOW(), INTERVAL 1 YEAR) " );
foreach ( $r as $rr ) {
q ( 'DELETE FROM mailstream_item WHERE id = %d' , intval ( $rr [ 'id' ]));
}
logger ( 'mailstream_tidy: deleted ' . count ( $r ) . ' old items' , LOGGER_DEBUG );
2013-03-15 10:20:25 +01:00
}