Recent Topics

1 Aug 23, 2008 04:58    

This plugin allows you to send emails to registered users.
You can exclude users and groups, specify message encoding, send as text or/and HTML, attach files and receive detailed reports on selected email.

Advanced users can use external emails table.

[url=http://www.sonorth.com/travel/download/S4UR8MGZW9]Download v1.0.0[/url] for b2evolution 4.1 and up

[url=http://www.sonorth.com/travel/download/KFY5F8WESV]Download legacy version[/url] for b2evolution 2.x - 4.0

If you can't send emails or your users don't receive them, set the $debug value either to 1, 2 or 3 on line 61 and post the output in this thread.

Starting with v1.0.0 the plugin is compatible with b2evolution v4.1 and v5

2 Aug 23, 2008 14:54

sam2kb wrote:

This plugin allows you to send emails to registered users.
You can exclude users and groups, specify message encoding, send as text or/and HTML, and receive detailed reports on selected email.

Things to do: attachments, run as scheduled task, save reports in database.

Thank you very much on this sam2kb, very useful when used with good intentions (:.. just to mention, can a checkbox be added in the registration form whether user wants to receive this kind of mail ?

3 Aug 23, 2008 16:14

can a checkbox be added in the registration form whether user wants to receive this kind of mail

In todo list, thanks.

4 Sep 02, 2008 06:31

sam,

this is great. I have a question on the plugin settings vs the script. If i want to contact user_level (i have too many users to contact by unique id, and no real groups established), then I:

1. assume i can change user id to user_leve in the hackfile

Also, I wanted to find out how the hack interprets the settings on the plugin. If I have the plugin set to exclude a group per the field, but want to contact them later, do i have to change that or does the file supercede the settings?

basically, I am not so bright, and just want to know how to best run this thing. and where from.

5 Sep 02, 2008 17:23

Hi adriscoll

You should use the plugin, it's much better than mail sender hack. By default the plugin sends emails to all users, there's no need to list user IDs.

6 Sep 03, 2008 06:28

Sam,

this is an awesome thing. used it recently, so i have some feedback for you:

1. when i send, it sends double emails to each user. not a disaster, but annoying in the long run

2. on the plugin settings, it mentions i can exclude users, but they still got the email

Other than that, I am excited to see it's further development. As b2evo becomes more popular and in more sites, especially large networks like mine, communication is key.

thanks again

7 Sep 11, 2008 08:38

i don't know what i might be doing wrong but it's sending the e-mails that i try to send out with no html formatting. I know i have the html box clicked but it has no formatting not even breaks. Is there something that forgot to change along the way?

8 Sep 12, 2008 05:04

Thank you all for feedbacks. I'll try to fix the plugin in next few days.

9 Sep 19, 2008 06:00

nothing's happened? I was hoping you would have time to work on it...but i'll see if i can try a couple of things myself and see if i can fix the issues i'm having and if i'm lucky i will keep you posted and show the code adjustments here.

10 Sep 19, 2008 18:19

Actually the new version with reports and attachments is done, but I've no time to upload it :)

11 Sep 20, 2008 04:27

Plugin updated.
The main new feature is file attachments, but there are some bug fixes also ;)

12 Nov 18, 2008 18:12

Hi Sam,

First, this is a great little plugin. It all works well except for the attachment upload.

Here is the error message:

Total attachments size should not exceed 0Mb!

Which I assume is triggered by

load_funcs( 'tools/model/_system.funcs.php' );
if( ($this->get_total_size() + $_FILES['uploadfile']['size']) > (system_check_memory_limit()*1024) )
{ // PHP may run out of memory if attachments size >= PHP memory_limit
$Messages->add( sprintf( $this->T_('Total attachments size should not exceed %sMb!'), number_format( system_check_memory_limit()/1024, 0 ) ), 'error' );
return false;
}

in _mail_sender.plugin.php

This is from the system tab

PHP upload_max_filesize
48M
PHP post_max_size
48M
post_max_size should be larger than upload_max_filesize
PHP memory_limit
n.a.
PHP mbstring extension
Loaded
PHP XML extension
Loaded

I have tried with different file types, none larger than 20kb. It fails when hitting "attach plugin" or if I just browse for the file and then hit "send"

What could be causing this?

Also, a couple of suggestions for the next update

1) Need more than one global send mail setting i.e. Admin's can mail to any group while others can mail to limited groups.

2) Check boxes showing groups rather than having to exclude group and user ids.

13 Nov 19, 2008 06:38

Welcome to the forums.

The error comes from PHP memory_limit n.a.. For some reason the plugin can't check the memory limit and "think" that it's 0.

You can safely change that line from

if( ($this->get_total_size() + $_FILES['uploadfile']['size']) > (system_check_memory_limit()*1024) )


to

if( false )

1) Need more than one global send mail setting i.e. Admin's can mail to any group while others can mail to limited groups.

This is a good idea. I'll add this in new release.

2) Check boxes showing groups rather than having to exclude group and user ids.

What if you have 50 groups and 10000 users?

14 Nov 19, 2008 09:02

sam2kb wrote:

Welcome to the forums.

The error comes from PHP memory_limit n.a.. For some reason the plugin can't check the memory limit and "think" that it's 0.

You can safely change that line from

if( ($this->get_total_size() + $_FILES['uploadfile']['size']) > (system_check_memory_limit()*1024) )


to

if( false )

1) Need more than one global send mail setting i.e. Admin's can mail to any group while others can mail to limited groups.

This is a good idea. I'll add this in new release.

2) Check boxes showing groups rather than having to exclude group and user ids.

What if you have 50 groups and 10000 users?

Awesome Sam! Works like a charm!

I would love to see the first addition! It would really be helpful and provide more control and options.

As far as groups and users, 50 and 1000 are extreme examples I assume. I would think that it would not be on a per user basis so no need to show them cause they will most likely be part of a group. 50 Groups in a drop down rather than a check box does not seem unreasonable to me. Would you not be able to select a group and exclude a user ID from that group? And if you need multiple group selection could you not use a cntrl+select type function?

Just my thoughts but thank you for the quick reply and the easy fix!

15 Nov 30, 2008 23:03

Hi guys,

I have installed the plugin but it didn't work for me. I don't know why!

my b2evo is 2.4.5 at: www.shafey.net

it gives me a message saying 0 successful. can u help me out???

16 Dec 01, 2008 04:38

Did you exclude any groups or users?
Do you receive any other emails from blog like validation or from contact form?

17 Dec 01, 2008 08:22

Yes, I have just tried sending email to my self pretending to be a guest user. It worked and the email went through.

but still the plug in doesn't work.

I have not excluded any users / groups in the plug in settings. I don't know where is this error coming from.

18 Dec 02, 2008 05:46

Edit the following code starting at line 578
replace

// Send email
if( @mail( $to, $subject, $messages, $headers ) )
	return true;

with

// Send email
echo "<pre>\n\n\nSending email\nTo: $to\n\nSubect: $subject\n\nHeaders: $headers\nBody: $messages\n\n</pre>";
mail( $to, $subject, $messages, $headers);
die();

Then try to send emails again. Paste here or PM me the output and I'll try to fix the problem

Good luck

19 Dec 02, 2008 23:40

Hi sam,

it still didn't work, but at least this time it sent me back an error. her is the error message:

Sending email
To: =?unf-8?Q?admin?=

Subect: =?unf-8?Q?Harry=20Potter=20-=20Full=20Collection?=

Headers: From: =?unf-8?Q?www.shafey.net?=
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="bf1fb531b7993dce4bd432ad4ad5f8c5"

Body: This is a multi-part message in MIME format.
--bf1fb531b7993dce4bd432ad4ad5f8c5
Content-type:text/html; charset=unf-8
Content-Transfer-Encoding: 8bit

Hi there,Harry Potter's full collection - FREE DownloadJust pay us a visit at: http://www.shafey.net

--bf1fb531b7993dce4bd432ad4ad5f8c5--

PHP Warning: mail() [function.mail]: SMTP server response: 501 5.5.4 Invalid Address in E:\home\Default\shafey.net\htdocs\plugins\mail_sender_plugin\_mail_sender.plugin.php on line 580

sorry for the truble

20 Dec 09, 2008 18:22

"unf-8" is not a valid charset, change it to "utf-8" in plugin settings

21 lxsparks Jan 24, 2009 22:40

lxsparks

Hi Sam,

I installed your plugin today (as blog admin) but got this error when logged in as a standard blogger:-

Notice: Undefined index: text in /(web address)l/blogs/skins_adm/_adminUI_general.class.php on line 766 (which appears at the very top of all the Back Office pages).

The code it refers to looks like this:

$anchor .= '>'.format_to_output( $loop_details['text'], 'htmlbody' )."</a>";

This does not appear when logged in with admin rights. If I un-install the plugin everything is fine again so it's the plugin which is causing this error.

Any ideas what is going on?

Matthew

22 Jan 25, 2009 02:37

It's a core bug, to fix it go to Users > group settings and set the Antispam to 'View only'
Repeat this for each user group except Administrators

23 lxsparks Jan 25, 2009 20:45

lxsparks

Hi Sam,

Thanks for the swift reply - yes that worked a treat :D

24 Jan 31, 2009 17:05

Great plug in. What is the latest version?

I get the following errors during an attempted file attachment. Any ideas?

How do I correct it?

Warning: move_uploaded_file(/home3/foo/public_html/espn/plugins/mail_sender_plugin/uploads/53616d119d1fbf3b8c4f0b662628d35a) [function.move-uploaded-file]: failed to open stream: No such file or directory in /home3/foo/public_html/espn/plugins/_mail_sender.plugin.php on line 274

Warning: move_uploaded_file() [function.move-uploaded-file]: Unable to move '/var/tmp/php7wH3SM' to '/home3/foo/public_html/espn/plugins/mail_sender_plugin/uploads/53616d119d1fbf3b8c4f0b662628d35a' in /home3/foo/public_html/espn/plugins/_mail_sender.plugin.php on line 274

Warning: Cannot modify header information - head

25 Jan 31, 2009 19:01

Make sure the directory /plugins/mail_sender_plugin/uploads/ is writable (777)

The latest released version is 0.2

26 Feb 05, 2009 08:48

Plugin updated to v0.3

New: multiple select lists of Users and Groups
New: external emails source support
Bugfixes

27 Feb 14, 2009 22:29

MySQL error!

Incorrect integer value: '' for column 'ID' at row 1(Errno=1366)

Your query:

INSERT INTO evo_plugin_mail_sender_50_reports
VALUES (
"",
2,
0,
3,
3,
0,
'1. Sorry, could not send email ( admin )\n2. Sorry, could not send email ( NoName )\n3. Sorry, could not send email ( Amy )',
'2009-02-15 05:25:44'
)

29 Feb 15, 2009 19:06

[2009-02-16 02:02:15] Mail Sender report from "blog.noname.hk"

1. Sorry, could not send email ( admin )
2. Sorry, could not send email ( NoName )
3. Sorry, could not send email ( Amy )

Successfull: 0
Failed: 3
Total: 3
Skipped: 0

i can send mail from gallery2 and b2evolution build-in contact me from..

30 Feb 15, 2009 22:12

No more errors, but still no emails sent, right?

I added debug info so we can find out why it doesn't send messages. Please download [url=http://evocms-plugins.svn.sourceforge.net/viewvc/evocms-plugins/mail_sender_plugin/trunk/_mail_sender.plugin.php?revision=1021]this version[/url] and edit the line 43 to

var $debug = 2;


This will display generated message and errors (if any), please post or PM the results as is with no changes.
You can then change the debug to 0

31 Feb 21, 2009 22:42

Hey guys, So does anyone have any idea how we can make this mail sender not send all the emails at once? Is there a way to schedule it to send some later? I'm running a blog where the users are over 4,000 and i'm sending out nearly 3,200 emails a week. My server kind of froze up the last two times i tried to send out using the plugin. I haven't tried version 0.3 yet however.

32 Feb 21, 2009 23:41

I'm pretty sure v0.3 will freeze also. It all depends on your server settings, but it doesn't mean emails don't get sent. Even if you got a blank page or something emails should be delivered anyway.
Do you receive the report? If you do it means that all emails are sent.

Are those users belong to the same group?

33 Feb 21, 2009 23:43

Some shared hosting companies limit the number of outgoing emails. It usually in a range of 200-1000 outgoing emails in 24 hours.

34 Feb 22, 2009 00:30

I'm on my own server, but i set my timeout to a minute if i'm not mistaken. I know some emails get sent, but to me, it's important that ALL get sent. NO, when I freezes up, I do not get the mail sender report. All the users do belong to the same group, but like I said before it's a large amount of people that i'm sending emails to. I'll see if I can mod something up, just wanted to know if it was addressed because i'm sure i'm not the only one that has this issue. If I am, I guess i'll have to try to solve it for myself and if I have any luck, I will share.

35 Feb 22, 2009 00:38

This is what i get when i change my timeout to 3 minutes:

All done!
Successfull = 3754
Failed = 1
Total = 3755

That's just to give you an idea of how many people are served by this list system as of now.

36 Feb 22, 2009 07:53

Plugin updated to v0.4

The plugin now tries to set the execution time limit to 15 minutes (if supported by server)
Added debug info. If you can't send emails set the $debug value either to 1 or 2 on line 43 and post the output in this thread.
Fixed mail function
Fixed files upload button
Fixed message encoding problems

37 Mar 10, 2009 18:45

Hi i istalled the Email-Sender-Plugin. It works fine.

But is there no way to exclude groups/members on the same site on whitch i have to write the email? Everytime i want to send a email to a other group i have to edit the plugin-options.

Sorry for bad english.

38 Mar 10, 2009 21:54

magic_al wrote:

But is there no way to exclude groups/members on the same site on whitch i have to write the email? Everytime i want to send a email to a other group i have to edit the plugin-options.

This is a good idea, there should be a dropdown menu of all groups and users in Tools tab.
But it'll still depend on the global plugin settings (excluded groups/users) so you'll have an option to send emails to any/all groups/users not excluded in the plugin settings.

Excuse my English too :)

39 May 21, 2009 18:55

I am just curious guys. Let me know what is the maximum number of emails I can send at a time using this product? Thanks in advance.

[u]WP blog removed[/u]

40 May 22, 2009 07:49

Virtually all available, but it depends on your server config.

41 Jun 12, 2009 19:40

Everyone is going to hate me for this, but is it possible to have a newsletter contact management feature?

Let me elaborate a little bit. The people I send out letters too are obviously users, and they entered their emails. At times, they don't want to recieve these emails, and I haven't seen a solid way that this is handled. Secondly, some users might have a full mailbox, or entered their address incorrectly at the time of registration, or use a fake email (I don't know why, it's just done) to sign up. There isn't a a feature that can get 550s and other mail errors and handle them accordingly. Or even get an email with a subject line and delete user from the system. I know it sounds like a lot but I think if this is possible this will be the end to all of the mail sender program in B2evo. It's practically perfect (except for the memory use which can be trimmed later)

42 Jul 28, 2009 07:44

Sorry for the late reply.

At times, they don't want to recieve these emails, and I haven't seen a solid way that this is handled.

It's handled via a checkbox in user profile ;)

Secondly, some users might have a full mailbox, or entered their address incorrectly at the time of registration, or use a fake email (I don't know why, it's just done) to sign up.

b2evo filters invalid emails, plus users couldn't validate their registration if they enter wrong/bad/fake emails

There isn't a a feature that can get 550s and other mail errors and handle them accordingly. Or even get an email with a subject line and delete user from the system. I know it sounds like a lot but I think if this is possible this will be the end to all of the mail sender program in B2evo. It's practically perfect (except for the memory use which can be trimmed later)

This would be a nice option.

43 Jul 30, 2009 02:00

sam2kb wrote:

Sorry for the late reply.

At times, they don't want to recieve these emails, and I haven't seen a solid way that this is handled.

It's handled via a checkbox in user profile ;)

Secondly, some users might have a full mailbox, or entered their address incorrectly at the time of registration, or use a fake email (I don't know why, it's just done) to sign up.

b2evo filters invalid emails, plus users couldn't validate their registration if they enter wrong/bad/fake emails

There isn't a a feature that can get 550s and other mail errors and handle them accordingly. Or even get an email with a subject line and delete user from the system. I know it sounds like a lot but I think if this is possible this will be the end to all of the mail sender program in B2evo. It's practically perfect (except for the memory use which can be trimmed later)

This would be a nice option.

The last was probably the most important feature I would love to see, since the others are handled already. Thanks for taking the time out to write this plugin! Hopefully sometime in the future you can consider my input, it shouldn't be impossible, (just extremely difficult and out of my league) seeing that the system can blog by email.

44 Sep 03, 2009 05:53

Hi,
What permission I should give to a user to be able to send email? No problem as full admin, but who else can access it?
Thanks
Mel

45 Sep 03, 2009 08:18

Group settings > System admin permissions > Settings: "Edit/delete all"
I don't think you can do it on a per-user base

46 Sep 04, 2009 14:18

all in all an great plugin - BuT
is there anyway for combination of
mail sender + (fckeditor or. TinyMCE)
it could be famous ---...,,,---

anyway thx

47 Sep 04, 2009 14:35

In my todo list, thanks.

48 Jan 16, 2010 03:23

Is there any progress in getting the script to resume and/or stop sending messages yet?

49 Jan 16, 2010 03:29

It's been done a while ago, I just forgot to upload the plugin :)
Will do it shortly

50 Jan 17, 2010 20:30

sam2kb wrote:

It's been done for a while, I just forgot to upload the plugin :)
Will do it shortly

This might be dramatic, but that just might save my life. Let me tell you my situation. I'm using B2Evo for a HUGE blog. Huge in terms of a medium sized kinda thing, but the biggest blog I've ever done recently. It has approximately 13,000 users. Logging in and signing up daily are at least 20 ppl. So that being established, I need a system to send them blasts, to say hey, come check out what I blogged about this week or so.

In the past, because the way the plugin works, it times out after around 5 or six minutes. Now, I know I shouldn't even set my timeout that high, but i kinda needed to that because of the plugin. So, i knew a day would come that i would have to say not, I can't set the timeout any higher because, once again, bad security practices. Can you please upload that plugin?

Also, on another note, is there anyway for me to mod the lastest plugin to send raw html? I noticed that the plugin wraps the text in html tags itself. Can it be modded so that i paste in the raw html, and without doing anything to it, it just sends that as the html part of the email? Then it runs the commands and strips the tags it needs to, to get it plain text. I've attached a sort of idea of this, taken from an older version of the plugin that was smaller and thus easier for me to work with the code. It works but only from the settings page, where i past in the content of the email. Please see the file below:

<?php
/**
 *
 * This file implements the Mail Sender plugin for {@link http://b2evolution.net/}.
 *
 * @version 0.1
 * @package plugins
 *
 * @author sam2kb: Russian b2evolution - {@link http://ru.b2evo.net/}
 * @copyright (c)2008 by Russian b2evolution - {@link http://ru.b2evo.net/}.
 *
 * @license GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
 *
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


class mail_sender_plugin extends Plugin
{
	/**
	 * Variables below MUST be overriden by plugin implementations,
	 * either in the subclass declaration or in the subclass constructor.
	 */
	var $name = 'Mail Sender';
	/**
	 * Code, if this is a renderer or pingback plugin.
	 */
	var $code = 'mail_sender';
	var $priority = 30;
	var $version = '0.1';
	var $group = 'ru.b2evo.net';
	var $author = 'Russian b2evolution';
	var $author_url = 'http://ru.b2evo.net';
	var $help_url = 'http://forums.b2evolution.net/viewtopic.php?p=79921';

	var $apply_rendering = 'never';
	var $number_of_installs = 1;
	
	/*var $sender_name;
	var $sender_email;
	var $subject;
	var $body;*/

	/**
	 * Init
	 *
	 * This gets called after a plugin has been registered/instantiated.
	 */
	function PluginInit( & $params )
	{
		$this->short_desc = $this->T_('Send emails to registered users');
		$this->long_desc = $this->T_('Send emails to registered users');
	}


	/**
	 * Define settings that the plugin uses/provides.
	 */
	function GetDefaultSettings()
	{
		global $admin_email, $current_charset;
		
		return array(
				'charset' => array(
					'label' => $this->T_('Email charset'),
					'defaultvalue' => $current_charset,
					'type' => 'text',
					'note' => $this->T_('Example: utf-8, iso-8859-1, windows-1251 etc.'),
				),
				'user_ids' => array(
					'label' => $this->T_('Exclude users'),
					'defaultvalue' => 1,
					'type' => 'text',
					'note' => $this->T_('Don\'t send emails to these users. List user ids separated by ,'),
				),
				'group_ids' => array(
					'label' => $this->T_('Exclude groups'),
					'type' => 'text',
					'note' => $this->T_('Don\'t send emails to these user groups. List user group ids separated by ,'),
				),
				'send_report' => array(
					'label' => $this->T_('Reports email'),
					'defaultvalue' => $admin_email,
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('Send detailed reports to this email address. Leave empty to disable.'),
				),
				'sender_name' => array(
					'label' => $this->T_('Sender\'s name'),
					'defaultvalue' => $this->T_('Site Administrator'),
					'type' => 'text',
					'size' => 40,
				),
				'sender_email' => array(
					'label' => $this->T_('Sender\'s email address'),
					'defaultvalue' => $admin_email,
					'type' => 'text',
					'size' => 40,
				),
				'subject' => array(
					'label' => $this->T_('Message subject'),
					'type' => 'text',
					'size' => 80,
				),
				'body' => array(
					'label' => $this->T_('Message text'),
					'type' => 'html_textarea',
					'rows' => 10,
					'cols' => 65,
				),
			);
	}
	
	
	function PluginSettingsUpdateAction()
	{
		if( $this->Settings->get('send_report') && !is_email($this->Settings->get('send_report')) )
		{
			global $Messages;
			
			$Messages->add( $this->T_('Reports email address is invalid!'), 'error' );
			return false;
		}
	}
	
	
	/**
	 * Event handler: Gets invoked in /admin/_header.php for every backoffice page after
	 * the menu structure is build.
	 */
	function AdminAfterMenuInit()
	{
		// add our tab
		$this->register_menu_entry( 'Mail Sender' );
	}

	
	function AdminTabAction()
	{
		global $Messages, $current_User, $basepath, $success_emails, $failed_emails;
		
		if( !is_logged_in() )
		{
			$Messages->add( $this->T_('You\'re not allowed to send emails!'), 'error' );
			return;
		}
		
		if( $current_User->group_ID != 1 )
		{
			$Messages->add( $this->T_('You\'re not allowed to send emails!'), 'error' );
			return;
		}
		
		if(	param( $this->get_class_id('sender_email'), 'string' )
			&& param( $this->get_class_id('sender_name'), 'string' )
			&& param( $this->get_class_id('subject'), 'string' )
			&& param( $this->get_class_id('body'), 'string' ) )
		{
			$this->as_text = param( $this->get_class_id('as_text'), 'boolean' );
			$this->as_html = param( $this->get_class_id('as_html'), 'boolean' );
			$this->sender_email = param( $this->get_class_id('sender_email'), 'string' );
			$this->sender_name = param( $this->get_class_id('sender_name'), 'string' );
			$this->subject = param( $this->get_class_id('subject'), 'string' );
			$this->body = param( $this->get_class_id('body'), 'string' );
						
			$this->start_sending();
			$Messages->head = $this->T_('All done!');
			$Messages->add( 'Successfull = '.$success_emails.'<br />Failed = '.$failed_emails.'<br />Total = '.($this->total-1), 'success' );
		}
		elseif( param( $this->get_class_id('start_sending') ) )
		{
			$Messages->add( $this->T_('All fields are mandatory'), 'error' );
		}
	}


	function AdminTabPayload()
	{		
		$Form = & new Form();
		$Form->begin_form( 'fform' );
		$Form->begin_fieldset( $this->T_('Mail Sender') );
		$Form->hidden_ctrl(); // needed to pass the "ctrl=tools" param
		$Form->hiddens_by_key( get_memorized() ); // needed to pass all other memorized params, especially "tab"
		$Form->hidden( $this->get_class_id().'_start_sending', 1 );
		
		$Form->checkbox( $this->get_class_id().'_as_text', true, $this->T_('Send as text'), $this->T_('Send email as plain text.') );
		$Form->checkbox( $this->get_class_id().'_as_html', true, $this->T_('Send as HTML'), $this->T_('Send email as HTML formatted text.') );
		$Form->text_input( $this->get_class_id().'_sender_email', $this->Settings->get('sender_email'), 40, $this->T_('From email'), $this->T_('Sender\'s email.') );
		$Form->text_input( $this->get_class_id().'_sender_name', $this->Settings->get('sender_name'), 40, $this->T_('From name'), $this->T_('Sender\'s name.') );
		$Form->text_input( $this->get_class_id().'_subject', $this->Settings->get('subject'), 40, $this->T_('Subject') );
		$Form->textarea( $this->get_class_id().'_body', $this->Settings->get('body'), 10, $this->T_('Message text'), '', 65 );

		$Form->end_fieldset();
		$Form->end_form( array(
				array( 'submit', 'submit', $this->T_('Send message !'), 'SaveButton' ),
				array( 'reset', '', $this->T_('Reset'), 'ResetButton' ) ) );
	}
	
	
	function start_sending()
	{	
		global $Messages, $success_emails, $failed_emails;
		
		if( empty($success_emails) ) { $GLOBALS['success_emails'] = 0; }
		if( empty($failed_emails) ) { $GLOBALS['failed_emails'] = 0; }
		if( empty($total) ) { $GLOBALS['total'] = 0; }
		
		$this->user_ids = $this->array_trim( explode( ',', $this->Settings->get('user_ids') ) );
		$this->group_ids = $this->array_trim( explode( ',', $this->Settings->get('group_ids') ) );
		
		$msg = array();
				
		$UserCache = & get_Cache( 'UserCache' );
		
		$i = 1;		
		for( $recipient_User = & $UserCache->get_first(); !is_null($recipient_User); $recipient_User = & $UserCache->get_next() )
		{
			if( $this->check_user( $recipient_User ) )
			{
				if( $this->send_mail( $recipient_User->get('email'),
								$this->sender_email,
								$this->subject,
								$this->body,
								$this->sender_name,
								$recipient_User->get('preferredname'),
								NULL,
								$this->as_text,
								$this->as_html,
								trim($this->Settings->get('charset')) ) )
				{
					$msg[] = sprintf( $i.'. '.$this->T_('OK ( %s )'), $recipient_User->get('preferredname') );
					$success_emails++;
				}
				else
				{
					$msg[] = sprintf( $i.'. '.$this->T_('Sorry, could not send email ( %s )'), $recipient_User->get('preferredname') );
					$failed_emails++;
				}
				$i++;
			}
		}
		$this->total = $i;
		
		// Create report message
		$this->report_body = "[".date('d-F-Y H:i')."] Mail Sender report from \"".$_SERVER['HTTP_HOST']."\"\n\n";
		$this->report_body .= implode( "\n", $msg );
		$this->report_body .= "\n\nSuccessfull = $success_emails\nFailed = $failed_emails\nTotal = ".($i-1);
		
		if( is_email($this->Settings->get('send_report')) )
		{	// Send report
			$this->send_mail( $this->Settings->get('send_report'),
						$this->sender_email, $this->T_('Mail Sender report'),
						$this->report_body,
						$this->sender_name,
						NULL,
						NULL,
						true,
						false,
						trim($this->Settings->get('charset')) );
		}
	}
	
	
	// Send mail
	function send_mail( $to, $from, $subject, $message, $from_name, $to_name = NULL, $files = NULL,
							$send_plain = true, $send_html = true, $charset = 'utf-8', $encoding = '8' )
	{
		global $i, $uid, $messages, $headers, $failed_emails;
		global $Messages, $current_charset;
		
		$NL = "\n";
		
		// ===================
		// Check the input
		
		$to = trim($to);
		$from = trim($from);
		
		if( empty($to) || empty($from) || empty($from_name) )
		{
			$Messages->add( 'All fields are mandatory!' );
			return false;
		}
		if( empty($to) || ereg( "[\r\n]", $to ) )
		{
			$Messages->add( 'Check the email "'.$to.'" for spaces.' );
			return false;
		}
		if( !is_email($to) )
		{
			$Messages->add( 'Email "'.$to.'" is invalid.' );
			return false;
		}
		if( !is_email($from) )
		{
			$Messages->add( 'Sender\'s email "'.$from.'" is invalid.' );
			return false;
		}

		// ===================
		// Get files
		
		if( !empty($files) )
		{
			$multipart = 'mixed';
			
			if( empty($messages) || empty($headers) )
			{
				$contents = array();
				
				foreach( $files as $file )
				{
					if( !is_file($file) )
					{
						$Messages->add( 'File not found "'.$file.'"' );
						return false;
					}
						
					$content = $this->read_file($file);
					$content = chunk_split(base64_encode($content));
					$filename = basename($file);
					$ctype = $this->get_mime_type($filename);
					
					// Encode file name
					$filename = $this->quoted_printable_encode( $filename, $charset );
								
					$Files[] = array('name' => $filename, 'ctype' => $ctype, 'content' => $content);
				}
			}
		}
		else
		{
			$multipart = 'alternative';
		}
		
		// ===================
		// Construct email
		/*
		$to_name_encoded = $this->quoted_printable_encode( $to_name, $charset );
		$from_name = $this->quoted_printable_encode( $from_name, $charset );
		$subject = $this->quoted_printable_encode( $subject, $charset );*/
		
		
		$to_name_encoded = $to_name;
		$from_name = $from_name;
		$subject = $subject;
		
		if( !empty($to_name) )
			$to = $to_name_encoded.' <'.$to.'>';
		
		if( empty($messages) || empty($headers) || !empty($this->report_body) )
		{
			$uid = md5( time() - rand() );
			
			// Convert encoding of message
			$body = convert_charset( $message, $current_charset, $charset );
			$htmlbody = convert_charset( $this->Settings->get('body'), $current_charset, $charset );
			//$body = $this->Settings->get('body');
			//$htmlbody = preg_replace( "~<userName>~", $to_name_encoded, $htmlbody );

			
			// Headers
			$headers = "From: ".$from_name." <".$from.">\n";				
			$headers .= "MIME-Version: 1.0\n";
			$headers .= "Content-Type: multipart/".$multipart."; boundary=\"".$uid."\"\n\n";
			
			// Message
			$messages  = "This is a multi-part message in MIME format.\n";
			
			if( $send_plain )
			{	// Send as PLAIN text
				$body = preg_replace( "~   ~", $NL, $body );
				$body = preg_replace( "~&nbsp;~", ' ', $body );
				$body = preg_replace( "~&quot;~", '"', $body );
				$messages .= "--".$uid."\n";
				$messages .= "Content-type:text/plain; charset=".$charset."\n";
				$messages .= "Content-Transfer-Encoding: ".$encoding."bit\n\n";
				$messages .= $body."\n\n";
			}
	
			if( $send_html )
			{	// Send as HTML
				$messages .= "--".$uid."\n";
				$messages .= "Content-type:text/html; charset=".$charset."\n";
				$messages .= "Content-Transfer-Encoding: ".$encoding."bit\n\n";
				$messages .= $htmlbody."\n\n";
			}
			
			if( !empty($Files) && is_array($Files) )
			{	// Attach files
				foreach( $Files as $current_file )
				{		
					$messages .= "--".$uid."\n";
					$messages .= "Content-Type: ".$current_file['ctype']."; name=\"".$current_file['name']."\"\n";
					$messages .= "Content-Transfer-Encoding: base64\n";
					$messages .= "Content-Disposition: attachment; filename=\"".$current_file['name']."\"\n\n";
					$messages .= $current_file['content']."\n\n";
				}
			}
			$messages .= "--".$uid."--";
		}
		
		// Free some memory
		if( isset($Files) )
			unset($Files);
			
		// Send email
		if( @mail( $to, $subject, $messages, $headers ) )
			return true;
		
		return false;
	}

	
	// Check if we want to send message to this user
	function check_user( $User )
	{
		if( is_array($this->user_ids) )
		{	// Check user ids
			foreach( $this->user_ids as $user_id )
			{
				if( $User->ID == $user_id )
					return false;
			}
		}
		
		if( is_array($this->group_ids) )
		{	// Check group ids
			foreach( $this->group_ids as $group_id )
			{
				if( $User->group_ID == $group_id )
					return false;
			}
		}
		return true;
	}
	
	
	// Quoted printable encode
	function quoted_printable_encode( $string, $charset = 'utf-8' )
	{
		$string = rawurlencode($string);
		$string = str_replace("%","=",$string);
		return "=?".$charset."?Q?".$string."?=";
	}
	
	
	// Trim array
	function array_trim( $array )
	{
		return array_map( 'trim', $array );
	}

	
	// Get content of local file
	function read_file( $file )
	{
		$file_size = @filesize($file);
		$handle = @fopen($file, "r");
		$content = @fread($handle, $file_size);
		@fclose($handle);
		
		if( !empty($content) )
			return $content;
		
		return false;
	}
		
	
	// Get MIME type of the file
	function get_mime_type( $filename )
	{
		$file_extension = strtolower( substr( strrchr( $filename, '.' ), 1 ) );
		
		switch( $file_extension )
		{
			case "pdf": $ctype = "application/pdf"; break;
			case "exe": $ctype = "application/octet-stream"; break;
			case "zip": $ctype = "application/zip"; break;
			case "doc": $ctype = "application/msword"; break;
			case "xls": $ctype = "application/vnd.ms-excel"; break;
			case "ppt": $ctype = "application/vnd.ms-powerpoint"; break;
			case "gif": $ctype = "image/gif"; break;
			case "png": $ctype = "image/png"; break;
			case "jpeg":
			case "jpg": $ctype = "image/jpg"; break;
			case "mp3": $ctype = "audio/mpeg"; break;
			case "wav": $ctype = "audio/x-wav"; break;
			case "mpeg":
			case "mpg":
			case "mpe": $ctype = "video/mpeg"; break;
			case "mov": $ctype = "video/quicktime"; break;
			case "avi": $ctype = "video/x-msvideo"; break;
			
			default: $ctype = "application/octet-stream";
		}
		return $ctype;
	}
}

?>

51 Jan 17, 2010 21:05

In the past, because the way the plugin works, it times out after around 5 or six minutes. Now, I know I shouldn't even set my timeout that high, but i kinda needed to that because of the plugin. So, i knew a day would come that i would have to say not, I can't set the timeout any higher because, once again, bad security practices. Can you please upload that plugin?

In version 0.4 max_execution_time is already set to 15 minutes. This applies to the plugin only and doesn't make any difference for other PHP scripts on your server, so there's nothing to worry about.

Also, on another note, is there anyway for me to mod the lastest plugin to send raw html? I noticed that the plugin wraps the text in html tags itself. Can it be modded so that i paste in the raw html, and without doing anything to it, it just sends that as the html part of the email?

I like the idea, will add an option for it.

52 Jan 17, 2010 21:21

In version 0.4 max_execution_time is already set to 15 minutes. This applies to the plugin only and doesn't make any difference for other PHP scripts on your server, so there's nothing to worry about.

I understand that, but you also have to set the max execution time for php. Since all the time the emails are sending the script is still executing, it still reads into that time so you will need to change your server max execution timeor it will never make it to the 15 minutes and the server will stop it. This is the case I think with mod_fcgi and just executing php under FastCGI.

Thanks for the quick reply!

53 Jan 17, 2010 21:35

I understand that, but you also have to set the max execution time for php.

Are you sure about it? Check the line 490 in v0.4
Maybe your server doesn't support changing PHP config through ini_set()

54 Jan 17, 2010 22:20

sam2kb wrote:

I understand that, but you also have to set the max execution time for php.

Are you sure about it? Check the line 490 in v0.4
Maybe your server doesn't support changing PHP config through ini_set()

hmmm...probably not. Didn't notice that before. Will take another look tonight. I stayed away from the new version because of the size of my database or rather the amount of users, but I think I should take another look at it and see what's going on with my server. Thank you!

55 Jan 17, 2010 23:43

BTW v0.4 doesn't strip html tags from message text

<b>bold text</b>
or
<a href="#">link</a>

56 Jan 20, 2010 05:24

sam2kb wrote:

BTW v0.4 doesn't strip html tags from message text

<b>bold text</b>
or
<a href="#">link</a>

Thank you for that. I just finished sending out all my mail, and it went ok. Only two real problems I'm having but they don't have anything to so with the plugin directly.

First, the plugin kinda rushes the mail server, so the mail doesn't hit the queue, it sort of forces it to try to send. Don't know if that's a php mail thing or a postfix config thing ( I need to make sure that doesn't happen in my postfix config, and I'll let you know when I do)

Second, the mail sender plugin doesn't quite have the option for users to choose not to recieve emails fromt he admin, i see the code and checking there in the code but I can't see where this is actually happening on the user forms/and the like.

57 Jan 20, 2010 05:31

First, the plugin kinda rushes the mail server, so the mail doesn't hit the queue, it sort of forces it to try to send.

Don't know if it's related to your question, but the newest version allows you to set a delay between individual messages. Something like 0.2 sec should be fine for busy servers to not eat all resources.

Second, the mail sender plugin doesn't quite have the option for users to choose not to recieve emails fromt he admin, i see the code and checking there in the code but I can't see where this is actually happening on the user forms/and the like.

The option is in user profiles

58 Jan 20, 2010 23:02

sam2kb wrote:

First, the plugin kinda rushes the mail server, so the mail doesn't hit the queue, it sort of forces it to try to send.

Don't know if it's related to your question, but the newest version allows you to set a delay between individual messages. Something like 0.2 sec should be fine for busy servers to not eat all resources.

That's great! I really need something like that, but then i'll have to set the timeout to 5 hrs! LOL. I'm trying to find a way to make this work, but this plugin is so amazing and a lifesaver. Thanks!

sam2kb wrote:

Second, the mail sender plugin doesn't quite have the option for users to choose not to recieve emails fromt he admin, i see the code and checking there in the code but I can't see where this is actually happening on the user forms/and the like.

The option is in user profiles

Yes the option is in the user profiles, but only in the admin area. Is there a way to have it outside? Like when the user clicks edit user profile from the menu it shows up there too. If you don't know what I'm talking about, it's the file called by the core _profile.disp.php

59 Jan 21, 2010 00:01

With 0.2 sec delay you can send 300 emails per minute and 10 000 every 35 minutes :)
Maybe 0.2 is too long... but you got the idea

Yes the option is in the user profiles, but only in the admin area. Is there a way to have it outside? Like when the user clicks edit user profile from the menu it shows up there too. If you don't know what I'm talking about, it's the file called by the core _profile.disp.php

There's no plugin hook to add new fields to user profile form in skin. I'll add the hook, but you'll see it in b2evo 4 only :(

60 Jan 26, 2010 20:25

sam2kb wrote:

With 0.2 sec delay you can send 300 emails per minute and 10 000 every 35 minutes :)
Maybe 0.2 is too long... but you got the idea

Yes the option is in the user profiles, but only in the admin area. Is there a way to have it outside? Like when the user clicks edit user profile from the menu it shows up there too. If you don't know what I'm talking about, it's the file called by the core _profile.disp.php

There's no plugin hook to add new fields to user profile form in skin. I'll add the hook, but you'll see it in b2evo 4 only :(

that sucks! Well i don't know where b2evo 4 is going, because it seemed like such a dramatic change from 2.4.7 to 3.3.x so I guess I'll have to wait.

Love the delay idea, how would I implement it now?

61 Jan 27, 2010 03:42

Plugin updated to v0.5

New:
Options to choose starting position and the number of emails to be sent
Text field for custom addresses, one email per line
max_execution_time increased to 30 minutes
* Configurable delay between messages
* Optional "Reply-To" email

* edit the code to configure

62 Jan 27, 2010 22:04

sam2kb wrote:

Plugin updated to v0.5

New:
Options to choose starting position and the number of emails to be sent
Text field for custom addresses, one email per line
max_execution_time increased to 30 minutes
* Configurable delay between messages
* Optional "Reply-To" email

* edit the code to configure

Didn't try out the new version yet, but I have a question, or a request of something i'll need. I'm trying out this new php-dkim thing. Now, I really want to incorporate this signature and dkim system into the mail sender plugin. Question is how do I do this? At what point is the email fully assembled? including headers? I try line 766 but it says cannot sign. Don't think the full message is assembled then seeing as it says that it cannot sign. Maybe it's because the variable names are different but it should accept anything in that position and renaming didn't quite work anyway. That is when I would like to use this function that works outside of the mail sender:

$headers = AddDKIM($headers,$subject,$body) . $headers;

where $headers are the headers, $subject is you know, and $body is the body of the email ($messages).
It takes the header and wraps in the dkim signature header and another header if im not mistaken.

Do you have any idea how this could be made possible?

If not, dont worry, I'll just have to figure it out, but if you have an idea i would love a direction. Thank you.

63 Jan 28, 2010 01:20

Try this

// Free some memory
if( isset($Files) ) unset($Files);
		
// DKIM header
$headers = AddDKIM( $headers, $subject, $messages ).$headers;

64 Jan 28, 2010 02:19

sam2kb wrote:

Try this

// Free some memory
if( isset($Files) ) unset($Files);
		
// DKIM header
$headers = AddDKIM( $headers, $subject, $messages ).$headers;

That didn't work, sadly. I think I need to go back to basics and trace this problem from the start of the email process to the end. I know it works because I'm sending mail now from another php mailer. The only problem i'm having is with these headers. I don't know why the function doesn't take the headers as they are. :(

I'll keep trying update you if i make any headway.

65 Jan 28, 2010 03:01

What does it return? Does it print any error?

try this

$dkim = AddDKIM( $headers, $subject, $messages );
die($dkim);

66 Jan 29, 2010 22:35

sam2kb wrote:

What does it return? Does it print any error?

try this

$dkim = AddDKIM( $headers, $subject, $messages );
die($dkim);

It prints the error: "Cannot sign"

This is the script it comes from:



<?
/***************************************************************************\
*  PHP-DKIM ($Id: dkim.php,v 1.2 2008/09/30 10:21:52 evyncke Exp $)
*  
*  Copyright (c) 2008 
*  Eric Vyncke
*          
* This program is a free software distributed under GNU/GPL licence.
* See also the file GPL.html
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
*THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ***************************************************************************/

require_once 'dkim-cfg.php' ;

if ($open_SSL_pub == '' or $open_SSL_priv == '') {
	die("DKIM not configured, please run:<ol>
	<li>openssl genrsa -out key.priv 384</li>
	<li>openssl rsa -in key.priv -out key.pub -pubout -outform PEM</li>
	</ol> 
	Then copy & paste the public and private keys into dkim-cfg.php") ;
}

function BuildDNSTXTRR() {
	global $open_SSL_pub,$DKIM_s ;
	
	$pub_lines=explode("\n",$open_SSL_pub) ;
	$txt_record="$DKIM_s._domainkey\tIN\tTXT\t\"v=DKIM1\\; k=rsa\\; g=*\\; s=email\; h=sha1\\; t=s\\; p=" ;
	foreach($pub_lines as $pub_line)
		if (strpos($pub_line,'-----') !== 0) $txt_record.=$pub_line ;
	$txt_record.="\;\"" ;
	print("Excellent, you have DKIM keys
	You should add the following DNS RR:
	$txt_record

") ;
}
	
function DKIMQuotedPrintable($txt) {
    $tmp="";
    $line="";
    for ($i=0;$i<strlen($txt);$i++) {
		$ord=ord($txt[$i]) ;
        if ( ((0x21 <= $ord) && ($ord <= 0x3A))
			|| $ord == 0x3C
			|| ((0x3E <= $ord) && ($ord <= 0x7E)) )
            $line.=$txt[$i];
        else
            $line.="=".sprintf("%02X",$ord);
    }
    return $line;
}

function DKIMBlackMagic($s) {
	global $open_SSL_priv ;
	if (openssl_sign($s, $signature, $open_SSL_priv))
		return base64_encode($signature) ;
	else
		die("Cannot sign") ;
}

function NiceDump($what,$body) {
	print("After canonicalization ($what):\n") ;
	for ($i=0; $i<strlen($body); $i++)
		if ($body[$i] == "\r") print("'OD'") ;
		elseif ($body[$i] == "\n") print("'OA'\n") ;
		elseif ($body[$i] == "\t") print("'09'") ;
		elseif ($body[$i] == " ") print("'20'") ;
		else print($body[$i]) ;
	print("\n------\n") ;
}

function SimpleHeaderCanonicalization($s) {
	return $s ;
}

function RelaxedHeaderCanonicalization($s) {
	// First unfold lines
	$s=preg_replace("/\r\n\s+/"," ",$s) ;
	// Explode headers & lowercase the heading
	$lines=explode("\r\n",$s) ;
	foreach ($lines as $key=>$line) {
		list($heading,$value)=explode(":",$line,2) ;
		$heading=strtolower($heading) ;
		$value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces
		$lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value
	}
	// Implode it again
	$s=implode("\r\n",$lines) ;
	// Done :-)
	return $s ;
}

function SimpleBodyCanonicalization($body) {
	if ($body == '') return "\r\n" ;
	
	// Just in case the body comes from Windows, replace all \r\n by the Unix \n
	$body=str_replace("\r\n","\n",$body) ;
	// Replace all \n by \r\n
	$body=str_replace("\n","\r\n",$body) ;
	// Should remove trailing empty lines... I.e. even a trailing \r\n\r\n
	// TODO
	while (substr($body,strlen($body)-4,4) == "\r\n\r\n")
		$body=substr($body,0,strlen($body)-2) ;
//	NiceDump('SimpleBody',$body) ;
	return $body ;
}

function AddDKIM($headers_line,$subject,$body) {
	global $DKIM_s, $DKIM_d, $DKIM_i;
	
//??? a tester	$body=str_replace("\n","\r\n",$body) ;
	$DKIM_a='rsa-sha1'; // Signature & hash algorithms
	$DKIM_c='relaxed/simple'; // Canonicalization of header/body
	$DKIM_q='dns/txt'; // Query method
	$DKIM_t=time() ; // Signature Timestamp = number of seconds since 00:00:00 on January 1, 1970 in the UTC time zone
	$subject_header="Subject: $subject" ;
	$headers=explode("\r\n",$headers_line) ;
	foreach($headers as $header)
		if (strpos($header,'From:') === 0)
			$from_header=$header ;
		elseif (strpos($header,'To:') === 0)
			$to_header=$header ;
	$from=str_replace('|','=7C',DKIMQuotedPrintable($from_header)) ;
	$to=str_replace('|','=7C',DKIMQuotedPrintable($to_header)) ;
	$subject=str_replace('|','=7C',DKIMQuotedPrintable($subject_header)) ; // Copied header fields (dkim-quoted-printable
	$body=SimpleBodyCanonicalization($body) ;
	$DKIM_l=strlen($body) ; // Length of body (in case MTA adds something afterwards)
	$DKIM_bh=base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body
	$i_part=($DKIM_i == '')? '' : " i=$DKIM_i;" ;
	$b='' ; // Base64 encoded signature
	$dkim="DKIM-Signature: v=1; a=$DKIM_a; q=$DKIM_q; l=$DKIM_l; s=$DKIM_s;\r\n".
		"\tt=$DKIM_t; c=$DKIM_c;\r\n".
		"\th=From:To:Subject;\r\n".
		"\td=$DKIM_d;$i_part\r\n".
		"\tz=$from\r\n".
		"\t|$to\r\n".
		"\t|$subject;\r\n".
		"\tbh=$DKIM_bh;\r\n".
		"\tb=";
	$to_be_signed=RelaxedHeaderCanonicalization("$from_header\r\n$to_header\r\n$subject_header\r\n$dkim") ;
	$b=DKIMBlackMagic($to_be_signed) ;
	return "X-DKIM: php-dkim.sourceforge.net\r\n".$dkim.$b."\r\n" ;
}
?>

This is the particular function that isn't working right. I don't know what the problem is, because it works when I just use the test scripts, and such.

function DKIMBlackMagic($s) {
	global $open_SSL_priv ;
	if (openssl_sign($s, $signature, $open_SSL_priv))
		return base64_encode($signature) ;
	else
		die("Cannot sign") ;
}

AFAIK, that's where the function hangs and has a problem there. From what I can tell, I need to format the headers in the same format that the tester email sends them to the function.

$headers="From: \"Fresh DKIM Manager\" <$sender>\r\n".
	"To: $to\r\n".
	"Reply-To: $sender\r\n".
	"Content-Type: text/html\r\n".
	"MIME-Version: 1.0" ;

That's what I'm going to try to do today. Thanks for your help.

67 Jan 29, 2010 23:06

Replace this part in DKIM script

$headers = explode("\r\n",$headers_line);

with

$headers = array_map( 'trim', explode("\n",$headers_line) );

68 Jan 29, 2010 23:47

sam2kb wrote:

Replace this part in DKIM script

$headers = explode("\r\n",$headers_line);

with

$headers = array_map( 'trim', explode("\n",$headers_line) );

This is sorta kicking my butt. I knew I should have read more on php instead of going the design route. :(

I'll keep trying and thanks for the suggestion. I did that, and I'm going to try to see exactly where the to header gets put in, because I still have yet to find that place. This, I think, is why the function AddDkim is failing.

69 Jan 30, 2010 03:41

I know what the problem is!

I need to figure out a way to create the headers for each mail. The dkim signature is based on the length of the message, subject, to and from headers. Using that info the Dkim signature is created. So is there a way to completely change your script to generate headers like the one I showed earlier for each email and then let the dkim script sign it and return it to be sent?

It's asking a lot, I know, but since I send so much mail, yahoo and gmail are starting to accuse me of spamming so according to them, I need to implement one of their recommended authentication techniques in order to be whitelisted. :(

Thanks in advance!

70 Jan 30, 2010 03:48

Did you try to change this line?

$headers = array_map( 'trim', explode("\n",$headers_line) );

It won't work until you do this

I'll play with DKIM and let you know how to fix it.

71 Jan 30, 2010 04:20

sam2kb wrote:

Did you try to change this line?

$headers = array_map( 'trim', explode("\n",$headers_line) );

It won't work until you do this

I'll play with DKIM and let you know how to fix it.

I did change that line, still getting the cannot sign message. I also made sure to print the headers so that I can see them.

72 Feb 09, 2010 05:03

the limit integer that is thrown into the mix with the v0.5 of the plugin. Do you just set it to 0 to get unlimited?

73 Feb 09, 2010 05:19

No, but you can fix it by editing the line 580

if( $this->limit == 0 ) $this->limit = NULL; // No limit
$rows = array_slice( $rows, ($this->start - 1), $this->limit );

75 Feb 09, 2010 19:11

I tried the DKIM script.

The plugin must be heavily edited to work with dkim-php, plus the script needs some modification itself.

76 Feb 10, 2010 02:04

Yes, I figured, so modified the mail plugin to use pear::mail_mime instead of the dkim script. Same authentication and all, it even sends the emails faster since its connected via SMTP.

77 Feb 17, 2010 06:05

sam2kb wrote:

No, but you can fix it by editing the line 580

if( $this->limit == 0 ) $this->limit = NULL; // No limit
$rows = array_slice( $rows, ($this->start - 1), $this->limit );

That did not work sadly. Maybe you should give the option to disable the limit?

78 Feb 17, 2010 06:09

It works for me... Try this then

if( $this->limit == 0 )
{
    $rows = array_slice( $rows, ($this->start - 1) );
}
else
{
    $rows = array_slice( $rows, ($this->start - 1), $this->limit );
}

79 Feb 17, 2010 06:44

sam2kb wrote:

It works for me... Try this then

if( $this->limit == 0 )
{
    $rows = array_slice( $rows, ($this->start - 1) );
}
else
{
    $rows = array_slice( $rows, ($this->start - 1), $this->limit );
}

That works, I don't know why the other didn't. Before it just sent the email to 0 people according to the status message, or so i thought, because I see that it did send out emails and they were received but it never reported them as being sent. If you understand me.

80 Mar 15, 2010 07:18

too bad i dont have an smtp server for my free hosting site :( but good job sam

81 Mar 16, 2010 20:33

This may sound crazy but how do I actually send the email. I have gone in and saved but where do I actually send the email from?

82 Mar 16, 2010 20:35

There's a new "Mail sender" tab in Tools section

83 Mar 16, 2010 20:35

I found it. Thanks! It works! Great plugin.

85 Apr 26, 2010 02:42

jmcausing wrote:

too bad i dont have an smtp server for my free hosting site :( but good job sam

it's ok my blog is a little big so it needed space and with thousands of emails being sent out, there was some servers blocking my mail without dkim so i just needed to get this done.

86 Apr 26, 2010 15:10

Ok new problem. When sending a segmented portion of emails, the system loops back on itself.
Example:
start: 4010
limit: 500
total emails: 12752

I turned on debug and sent out ten emails everything was fine. Everything was fine, then tried sending 500. problem was that it send 500, then another 500 and then the window timed out, probably within sending the next 500.

Am I the only one experiencing this? Is there something I did to the code, because I remember we did change a line in my code to facilitate having 0 mean no limit and the script would just send out all the emails. Any help would be greatly appreciated. Thanks again, and I will try to take a look at it today, but maybe you can give me a little direction also. Its just so weird that it reacts like that for 500 emails but not for 10.

87 Apr 26, 2010 17:26

Can you paste your code here? It shouldn't loop on emails, it may just get 1500 instead of 500 for some reason.

88 Apr 26, 2010 19:06

below is the code modified of course to use smtp

<?php
/**
 *
 * This file implements the Mail Sender plugin for {@link http://b2evolution.net/}.
 *
 * @package plugins
 *
 * @author sam2kb: Russian b2evolution - {@link http://ru.b2evo.net/}
 * @copyright (c)2008 by Russian b2evolution - {@link http://ru.b2evo.net/}.
 *
 * @license GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
 *
 */
require_once('/usr/share/pear/Mail.php');
require_once('/usr/share/pear/Mail/mime.php');


if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
	
class mail_sender_plugin extends Plugin
{
//include ( '/usr/share/pear/Mail.php');
//require_once "Mail.php";
	/**
	 * Variables below MUST be overriden by plugin implementations,
	 * either in the subclass declaration or in the subclass constructor.
	 */
	var $name = 'Mail Sender';
	/**
	 * Code, if this is a renderer or pingback plugin.
	 */
	var $code = 'mail_sender';
	var $priority = 30;
	var $version = '0.5';
	var $group = 'ru.b2evo.net';
	var $author = 'Russian b2evolution & Mocha Delight';
	var $author_url = 'http://ru.b2evo.net';
	var $help_url = 'http://forums.b2evolution.net/viewtopic.php?p=79921';

	var $apply_rendering = 'never';
	var $number_of_installs = 1;
	
	var $files = array();
	var $allow_unsubscribe = false;
	
	/*
	 * Advanced settings
	 */
	
	// Uploads folder, relative to /plugins/ directory
	var $path = 'mail_sender_plugin/uploads/';
	
	// Delay between emails in milliseconds, sleep for 0.2 sec ('false' to diable)
	var $sleep_time = 200;
	
	// 1 - display debug info (emails get sent)
	// 2 - display debug info and die (stop after the first email)
	// 3 - display debug info (do not send emails)
	var $debug = 1;
	
	/*
	 * External emails source
	 */
	var $ext_source = false; // 'true' to enable
	// SQL to get the emails column.
	// Example: 'SELECT email FROM emails_table WHERE type = $OPTION$'
	var $ext_sql = '';
	// Will be displayed as source options like <option value="users">Users</option>
	// and replaced in SQL. So the above SQL may look like
	// 'SELECT email FROM emails_table WHERE type = "users"'
	// if you selected "Users" in "Emails source" menu
	var $ext_options = array(
				'admins'	=> 'Administrators',
				'users'		=> 'Users',
			);
	
	/**
	 * Init
	 *
	 * This gets called after a plugin has been registered/instantiated.
	 */
	function PluginInit( & $params )
	{
		$this->short_desc = $this->T_('Send emails to registered users');
		$this->long_desc = $this->T_('Send emails to registered users');
		
		$this->path = $GLOBALS['plugins_path'].$this->path;
	}
	
	
	/**
	 * Event handler: Called when we detect a version change (in {@link Plugins::register()}).
	 *
	 * Use this for your upgrade needs.
	 *
	 * @param array Associative array of parameters.
	 *              'old_version': The old version of your plugin as stored in DB.
	 *              'db_row': an array with the columns of the plugin DB entry (in T_plugins).
	 *                        The key 'plug_version' is the same as the 'old_version' key.
	 * @return boolean If this method returns false, the Plugin's status gets changed to "needs_config" and
	 *                 it gets unregistered for the current request.
	 */
	function PluginVersionChanged( & $params )
	{
		if( version_compare( $params['old_version'], '0.3', '<' ) )
		{	// Add user_ID column to reports table
			global $DB;

			$this->db_add_col( $this->get_sql_table('reports'), 'user_ID', 'int(11) default "0" AFTER ID' );
		}
		return true;
	}
	
	
	function BeforeInstall()
	{
		$this->check_uploads_dir();
		return true;
	}
	
	
	/*
	function BeforeUninstallPayload( & $params )
	{	// TODO: Keep reports table after uninstall
		
	}
	*/
	
	
	function GetDbLayout()
	{
		return array(
			"CREATE TABLE IF NOT EXISTS ".$this->get_sql_table('uploads')." (
					ID int(11) NOT NULL auto_increment,
					name text NOT NULL,
					type varchar(200) NOT NULL,
					size int(11) default '0',
					name_md5 varchar(32) NOT NULL,
					PRIMARY KEY ID ( ID )
				)",

			"CREATE TABLE IF NOT EXISTS ".$this->get_sql_table('reports')." (
					ID int(11) NOT NULL auto_increment,
					user_ID int(11) default '0',
					successfull int(11) default '0',
					failed int(11) default '0',
					total int(11) default '0',
					report text NOT NULL,
					date datetime NOT NULL default '2000-01-01 00:00:00',
					PRIMARY KEY ID ( ID )
				)",
		);
	}
	
	
	/**
	 * Event handler: Called as action before displaying the "Edit plugin" form,
	 * which includes the display of the {@link Plugin::$Settings plugin's settings}.
	 *
	 * You may want to use this to check existing settings or display notes about
	 * something.
	 */
	function PluginSettingsEditAction()
	{
		global $msexcl_groups;
		
		$msexcl_groups = @implode( ', ', $this->Settings->get('group_ids') );
	}
	
	
	
	/**
	 * Define settings that the plugin uses/provides.
	 */
	function GetDefaultSettings()
	{
		global $admin_email, $msexcl_groups;
				
		return array(
				'attachments' => array(
					'label' => $this->T_('Enable attachments'),
					'defaultvalue' => 1,
					'type' => 'checkbox',
					'note' => $this->T_('Do you want to send emails with attachments?'),
				),
				'charset' => array(
					'label' => $this->T_('Email charset'),
					'defaultvalue' => 'utf-8',
					'type' => 'text',
					'note' => $this->T_('Example: utf-8, iso-8859-1, windows-1251 etc. Note: it\'s highly recommended to use utf-8 here.'),
				),
				'smtp_domain' => array(
					'label' => $this->T_('SMTP Domain'),
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('Host name, usually mail.domain.com'),
				),
				'smtp_username' => array(
					'label' => $this->T_('SMTP username'),
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('Username for smtp access'),
				),
				'smtp_pass' => array(
					'label' => $this->T_('SMTP Password'),
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('password for smtp access'),
				),
				'reply_to' => array(
					'label' => $this->T_('Reply-to email address'),
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('reply to email address'),
				),
				'send_report' => array(
					'label' => $this->T_('Report email'),
					'defaultvalue' => $admin_email,
					'type' => 'text',
					'size' => 40,
					'note' => $this->T_('Send detailed reports to this email address. Leave empty to disable.'),
				),
				'sender_name' => array(
					'label' => $this->T_('Sender\'s name'),
					'defaultvalue' => $this->T_('Site Administrator'),
					'type' => 'text',
					'size' => 40,
				),
				'sender_email' => array(
					'label' => $this->T_('Sender\'s email address'),
					'defaultvalue' => $admin_email,
					'type' => 'text',
					'size' => 40,
				),
				'subject' => array(
					'label' => $this->T_('Message subject'),
					'type' => 'text',
					'size' => 80,
				),
				'body' => array(
					'label' => $this->T_('Message text'),
					'type' => 'html_textarea',
					'rows' => 10,
					'cols' => 65,
				),
				'group_ids' => array(
					'label' => $this->T_('Exclude groups'),
					'type' => 'select_group',
					'multiple' => 'multiple',
					'defaultvalue' => 1,
					'allow_none' => 'allow_none',
					'note' => $this->T_('Don\'t send emails to selected user groups. Use "CTRL" and "SHIFT" keys to select multiple items.').
							'<br />'.sprintf( $this->T_('Currently excluded group ids: %s.'), $msexcl_groups ),
				),
			);
	}
	
	
	function GetDefaultUserSettings()
	{
		return array(
				'mail_sender' => array(
					'label' => $this->T_('Receive emails'),
					'defaultvalue' => true,
					'type' => 'checkbox',
					'note' => $this->T_('Check this if you want to receive emails from site administrator.'),
				),
			);
	}
	
	
	function PluginSettingsUpdateAction()
	{
		if( $this->Settings->get('send_report') && !is_email($this->Settings->get('send_report')) )
		{
			global $Messages;
			
			$Messages->add( $this->T_('Report email address is invalid!'), 'error' );
			return false;
		}
	}
	
	
	/**
	 * Event handler: Gets invoked in /admin/_header.php for every backoffice page after
	 * the menu structure is build.
	 */
	function AdminAfterMenuInit()
	{
		// add our tab
		$this->register_menu_entry( 'Mail Sender' );
	}

	
	function AdminTabAction()
	{
		global $Messages, $Settings, $DB, $current_User, $basepath, $success_emails, $failed_emails;
		
		if( !is_logged_in() )
		{
			$Messages->add( $this->T_('You\'re not allowed to send emails!'), 'error' );
			return;
		}
		
		if( !$current_User->check_perm( 'options', 'edit' ) )
		{
			$Messages->add( $this->T_('You\'re not allowed to send emails!'), 'error' );
			return;
		}
		
		// Check permissions:
		if( ! $Settings->get('upload_enabled') )
		{ // Upload is globally disabled
			$Messages->add( $this->T_('Upload is disabled.'), 'error' );
		}
		if( ! $current_User->check_perm( 'files', 'add' ) )
		{ // We do not have permission to add files
			$Messages->add( $this->T_('You have no permission to add/upload files.'), 'error' );
		}
		
		// We want to delete the requested file
		if( $msdelfile = param( 'msdelfile', 'string' ) )
		{
			if( @unlink($this->path.$msdelfile) )
				$Messages->add( $this->T_('File deleted.'), 'success' );
			
			if( $DB->query( 'DELETE FROM '.$this->get_sql_table('uploads').' WHERE name_md5 = '.$DB->quote($msdelfile) ) )
				$Messages->add( $this->T_('File settings deleted.'), 'success' );
		}
				
		if( isset($_FILES) && !empty( $_FILES['uploadfile']['tmp_name'] ) )
		{	// Some files have been uploaded:
			if( $_FILES['uploadfile']['error'] )
			{ // PHP has detected an error!:
				switch( $_FILES['uploadfile']['error']  )
				{		
					case UPLOAD_ERR_INI_SIZE: // bigger than allowed in php.ini
						$failedFiles = $this->T_('The file exceeds the upload_max_filesize directive in php.ini.');
						break;
	
					case UPLOAD_ERR_PARTIAL:
						$failedFiles = $this->T_('The file was only partially uploaded.');
						break;
	
					case UPLOAD_ERR_NO_FILE:
						// Is probably the same as empty($lName) before.
						$failedFiles = $this->T_('No file was uploaded.');
						break;
	
					case 6: // numerical value of UPLOAD_ERR_NO_TMP_DIR
					# (min_php: 4.3.10, 5.0.3) case UPLOAD_ERR_NO_TMP_DIR:
						// Missing a temporary folder.
						$failedFiles = $this->T_('Missing a temporary folder (upload_tmp_dir in php.ini).');
						break;
	
					default:
						$failedFiles = $this->T_('Unknown error.').' #'.$_FILES['uploadfile']['error'] ;
				}
			}
			
			load_funcs( 'tools/model/_system.funcs.php' );
			$memory_limit = system_check_memory_limit();
			// system_check_memory_limit() may return NULL
			// http://forums.b2evolution.net/viewtopic.php?p=83696
			if( !empty($memory_limit) )
			{				
				if( ($this->get_total_size() + $_FILES['uploadfile']['size']) > ($memory_limit * 1024) )
				{	// PHP may run out of memory if attachments size >= PHP memory_limit
					$Messages->add( sprintf( $this->T_('Total attachments size should not exceed %sMb!'), number_format( $memory_limit / 1024, 0 ) ), 'error' );
					return false;
				}
			}
			
			if( is_uploaded_file( $_FILES['uploadfile']['tmp_name'] ) )
			{	// Attempt to move the uploaded file to the requested target location:
				if( move_uploaded_file( $_FILES['uploadfile']['tmp_name'] , $this->path.md5($_FILES['uploadfile']['name']) ) )
				{
					$sql = 'INSERT INTO '.$this->get_sql_table('uploads').'
							(name, type, size, name_md5) VALUES (
							'.$DB->quote( $_FILES['uploadfile']['name'] ).',
							'.$DB->quote( $_FILES['uploadfile']['type'] ).',
							'.$_FILES['uploadfile']['size'].',
							'.$DB->quote( md5($_FILES['uploadfile']['name']) ).')';
							
					if( $DB->query($sql) )
					{	// File saved in DB
						$Messages->add( sprintf( $this->T_('The file &laquo;%s&raquo; has been successfully uploaded.'), $_FILES['uploadfile']['name'] ), 'success' );
					}
				}
				else
				{
					$failedFiles  = $this->T_('An unknown error occurred when moving the uploaded file on the server.');
				}
			}
			else
			{	// Ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working.
				$failedFiles  = $this->T_('The file does not seem to be a valid upload! It may exceed the upload_max_filesize directive in php.ini.');
			}
			
			if( !empty($failedFiles) )
			{	// Transmit file error to next page!
				$Messages->add( $failedFiles, 'error' );
				unset($failedFiles);
			}
		}
		elseif( isset($_POST['attach']) )
		{	// Attach button clicked with no file selected
			$Messages->add( $this->T_('Select a file first.') );
		}
		elseif(	param( $this->get_class_id('sender_email'), 'string' ) &&
				param( $this->get_class_id('sender_name'), 'string' ) &&
				param( $this->get_class_id('subject'), 'string' ) &&
				param( $this->get_class_id('body'), 'html' ) &&
				!isset($_POST['attach']) )
		{
			$this->start = abs( param( $this->get_class_id('start'), 'integer' ) );
			$this->limit = abs( param( $this->get_class_id('limit'), 'integer' ) );
			
			$this->source = param( $this->get_class_id('source'), 'string' );
			$this->as_text = param( $this->get_class_id('as_text'), 'boolean' );
			$this->as_html = param( $this->get_class_id('as_html'), 'boolean' );
			$this->sender_email = param( $this->get_class_id('sender_email'), 'string' );
			$this->sender_name = param( $this->get_class_id('sender_name'), 'string' );
			$this->subject = param( $this->get_class_id('subject'), 'string' );
			$this->body = param( $this->get_class_id('body'), 'html' );
			
			// Use provided emails
			$this->custom_emails = param( $this->get_class_id('emails'), 'html' );
			
			// Start sending emails
			$this->start_sending();
			
			if( !$Messages->count('error') )
			{
				$Messages->head = $this->T_('All done!');
				$Messages->add( sprintf( $this->T_('Successfull: %sFailed: %sTotal: %s'),
							$success_emails.'<br />', $failed_emails.'<br />', $this->total ), 'success' );
			}
		}
		elseif( param( $this->get_class_id('start_sending') ) )
		{
			$Messages->add( $this->T_('All fields are mandatory'), 'error' );
		}
		// Load files settings
		$this->files = $DB->get_results( 'SELECT * FROM '.$this->get_sql_table('uploads'), 'ARRAY_A' );
	}


	function AdminTabPayload()
	{
		$opt = array( '<option value="" selected="selected">Site Users (default)</option>' );
		if( count($this->ext_options) > 0 )
		{
			foreach( $this->ext_options as $k => $v )
			{
				$opt[] = '<option value="'.$k.'">'.$v.'</option>';
			}
		}
		$source_options = implode( "\n", $opt );
		
		// Display the form
		$Form = & new Form( 'admin.php', '', 'post', 'none', 'multipart/form-data' );
		$Form->begin_form( 'fform' );
		$Form->begin_fieldset( $this->T_('Mail Sender') );
		$Form->hidden_ctrl(); // needed to pass the "ctrl=tools" param
		$Form->hiddens_by_key( get_memorized() ); // needed to pass all other memorized params, especially "tab"
		$Form->hidden( $this->get_class_id().'_start_sending', 1 );
		
		$Form->text_input( $this->get_class_id().'_start', 1, 5, $this->T_('Starting email'), $this->T_('Start sending from this email, skipping all previous.') );
		$Form->text_input( $this->get_class_id().'_limit', 0, 5, $this->T_('Limit'), $this->T_('The number of emails you want to send at a time.') );
		echo '<hr />';
		$Form->checkbox( $this->get_class_id().'_as_text', true, $this->T_('Send as text'), $this->T_('Send email as plain text.') );
		$Form->checkbox( $this->get_class_id().'_as_html', true, $this->T_('Send as HTML'), $this->T_('Send email as HTML formatted text.') );
		$Form->text_input( $this->get_class_id().'_sender_email', $this->Settings->get('sender_email'), 40, $this->T_('From email'), $this->T_('Sender\'s email.') );
		$Form->text_input( $this->get_class_id().'_sender_name', $this->Settings->get('sender_name'), 40, $this->T_('From name'), $this->T_('Sender\'s name.') );
		$Form->text_input( $this->get_class_id().'_subject', $this->Settings->get('subject'), 40, $this->T_('Subject') );
		if( $this->ext_source )
		{
			$Form->select_input_options( $this->get_class_id().'_source', $source_options, $this->T_('Emails source'), $this->T_('Get emails list from selected source') );
		}
		
		if( $this->Settings->get('attachments') )
		{	// Attachments enabled
			$this->check_uploads_dir( false );
			
			echo '<fieldset><div class="label"><label>'.$this->T_('Choose a file').':</label></div>';
			echo '<div class="input">
					<input name="uploadfile" size="40" type="file" />
					<input type="submit" name="attach" value="'.format_to_output( $this->T_('Attach file'), 'formvalue' ).'" class="ActionButton" >
				  </div></fieldset>';
			
			if( !empty($this->files) )
			{	// Display uploaded files
				foreach( $this->files as $Fkey => $Fvalue )
				{
					echo '<fieldset><div class="label"><label>'.($Fkey+1).':</label></div>';
					echo '<div class="input"><table width="350" border="0" cellspacing="0" cellpadding="0"><tr>
							<td nowrap="nowrap" width="95%">'.$Fvalue['name'].'</td>
							<td nowrap="nowrap" style="padding:0 20px">'.number_format( $Fvalue['size']/1024, 2 ).' '.$this->T_('KB').'.</td>
							<td><a href="'.regenerate_url( '', 'msdelfile='.$Fvalue['name_md5']).'"
									title="'.$this->T_('Delete this file').'">'.get_icon('delete').'</a></td>
						 </tr></table></div></fieldset>';
				}
				echo '<fieldset><div class="label"><label></label></div>';
				echo '<div class="input"><span class="notes">'.$this->T_('Total size').': '.$this->get_total_size( true ).'</span></div></fieldset>';
			}
		}
		$Form->textarea( $this->get_class_id().'_emails', '', 3, $this->T_('Email addresses'), $this->T_('One email address per line.'), 65 );
		
		$Form->textarea( $this->get_class_id().'_body', $this->Settings->get('body'), 10, $this->T_('Message text'), '', 65 );
        
		$Form->end_fieldset();
		$Form->end_form( array(
				array( 'submit', 'submit', $this->T_('Send message !'), 'SaveButton' ),
				array( 'reset', '', $this->T_('Reset'), 'ResetButton' ) ) );
	}
	
	
	function start_sending()
	{	
		global $Messages, $DB, $success_emails, $failed_emails, $current_User;
		
		if( function_exists( 'set_time_limit' ) )
		{
			@set_time_limit( 2700 ); // 45 minutes
		}
		@ini_set( 'max_execution_time', '2700' );
		@ini_set( 'max_input_time', '2700' );
		@ini_set( 'memory_limit', '1024M' );
		
		$Messages->clear( 'all' );
		if( !isset($success_emails) ) { $GLOBALS['success_emails'] = 0; }
		if( !isset($failed_emails) ) { $GLOBALS['failed_emails'] = 0; }
		
		// Load files settings
		$this->files = $DB->get_results( 'SELECT * FROM '.$this->get_sql_table('uploads'), 'ARRAY_A' );
		$this->group_ids = $this->Settings->get('group_ids');
		$this->user_ids = $this->Settings->get('user_ids');
		
		if( $this->custom_emails )
		{	// Use external supplied emails
		
			// Validate emails field
			$emails_data = $emails = $this->custom_emails;
			if( !is_array($emails) )
			{
				if( strstr( $emails, 'tp://' ) || is_file( $emails ) )
				{	// Load emails list from remote or local file
					if( !$emails_data = $this->get_data($emails) )
					{
						$Messages->add( sprintf( $this->T_('The file %s cannot be read!'), '<b>'.$emails.'</b>' ), 'error' );
					}
				}
			}
			$rows = $this->array_trim( explode( "\n" , $emails_data ) );
		}
		elseif( $this->ext_source && $this->source )
		{	// Use external emails source
			$SQL = str_replace( '$OPTION$', $DB->quote($this->source), $this->ext_sql );
			$rows = $DB->get_results( $SQL, 'ARRAY_N' );
		}
		else
		{	// Get b2evo emails
			$rows = array();
			$UserCache = & get_Cache( 'UserCache' );
			for( $recipient_User = & $UserCache->get_first();
				!is_null($recipient_User);
				$recipient_User = & $UserCache->get_next() )
			{
				if( $this->check_user( $recipient_User ) )
				{
					$rows[] = array( $recipient_User->get('email'), $recipient_User->get('preferredname') );
				}
			}
		}
		
		if( empty($rows) )
		{
			$Messages->add( $this->T_('No emails found. Nothing to do.') );
			return false;
		}
		
		$msg = array();
		$all = $i = 1;
		$total_emails = count($rows);
		
		if( $this->start > $total_emails )
		{
			$Messages->add( sprintf( $this->T_('There are only %d emails found. Decrease the starting email number.'), $total_emails ), 'note' );
			return;
		}
		
		if( $this->limit == 0 ) 
		{ 
			$rows = array_slice( $rows, ($this->start - 1) ); 
		} 
		else 
		{ 
			$rows = array_slice( $rows, ($this->start - 1), $this->limit ); 
		} 
				
		// Debug
		if( $this->debug )
		{
			echo '<h2 style="margin-top:30px">Emails count: '.$total_emails.'</h2>';
			echo '<h2>Emails limit: '.$this->limit.'</h2>';
			echo '<h2>First '.($this->start-1).' emails skipped</h2>';
			echo '<h2>Emails to send: '.count($rows).'</h2>';
		}
		
		foreach( $rows as $row )
		{
			$email = (is_array($row) && isset($row[0])) ? $row[0] : $row;
			$user_name = (isset($row[1]) && is_array($row)) ? $row[1] : '';
			$ok = (empty($user_name)) ? $email : $user_name;
			
			if( !is_email( $email ) )
			{
				// Debug
				if( $this->debug ) echo '<br />Bad email: '.$email;
				
				continue;
			}
			
			// Debug
			if( $this->debug )
			{
				echo '<br />Email: '.$email;
				echo '<br />Name: '.$user_name;
			}
			
			if( $this->send_mail( $email, // Email
						$this->sender_email,
						$this->subject,
						$this->body,
						$this->sender_name,
						$user_name, // User name
						$this->files,
						$this->as_text,
						$this->as_html,
						trim($this->Settings->get('charset')) ) )
			{
				$msg[] = sprintf( ($i).'. '.$this->T_('OK ( %s )'), $ok );
				$success_emails++;
			}
			else
			{
				$msg[] = sprintf( ($i).'. '.$this->T_('Sorry, could not send email ( %s )'), $ok );
				$failed_emails++;
			}
			$i++;
			$all++;
		}
						
		if( is_email($this->Settings->get('send_report')) )
		{	// Create report message
			global $localtimenow;
			
			$this->total = $i-1;
			
			$this->report_body = '['.date('Y-m-d H:i:s', $localtimenow).'] '.
						sprintf( $this->T_('Mail Sender report from %s'), '"'.$_SERVER['HTTP_HOST'].'"' )."\n\n".
						implode( "\n", $msg )."\n\n".
						sprintf( $this->T_('Successfull: %sFailed: %sTotal: %s'),
						$success_emails."\n", $failed_emails."\n", $this->total."\n" );
			
			// Send report
			$this->send_mail(
						$this->Settings->get('send_report'),
						$this->sender_email,
						$this->T_('Mail Sender report'),
						$this->report_body,
						$this->sender_name,
						NULL,
						NULL,
						true,
						false,
						trim($this->Settings->get('charset'))
					);
				
			// Save report in DB
			$DB->query('INSERT INTO '.$this->get_sql_table('reports').'
						(user_ID, successfull, failed, total, report, date) VALUES (
						'.(int)$current_User->ID.',
						'.$success_emails.',
						'.$failed_emails.',
						'.$this->total.',
						'.$DB->quote( implode( "\n", $msg ) ).',
						'.$DB->quote( date('Y-m-d H:i:s', $localtimenow) ).'
					)'
				);
		}
	}
	
	
	// Send mail
	function send_mail( $to, $from, $subject, $message, $from_name, $to_name = NULL, $files = array(),
							$send_plain = true, $send_html = true, $charset = 'utf-8', $encoding = '8' )
	{
		global $Messages, $i, $uid, $messages, $headers;
		
		$NL = "\n";
		$to = trim($to);
		$from = trim($from);
		
		if( $this->sleep_time )
		{	// Sleep
			usleep( $this->sleep_time * 1000 );
		}
		
		// ===================
		// Check the input
		
		if( empty($to) || empty($from) || empty($from_name) )
		{
			$Messages->add( 'All fields are mandatory!' );
			return false;
		}
		if( !is_email($from) )
		{
			$Messages->add( 'Sender\'s email "'.$from.'" is invalid.' );
			return false;
		}
		if( !is_email($to) )
		{
			return false;
		}
		
		// ===================
		// Get files
		
		if( !empty($files) )
		{
			if( empty($messages) || empty($headers) )
			{
				$Files = $this->get_file_content( $files, $charset );
				if( empty($Files) ) return false;
			}
			$multipart = 'mixed';
		}
		else
		{
			$multipart = 'alternative';
		}
		
		// ===================
		// Construct email
		
		$to_saved = $to;
		$unsubscribe = NULL;
		
		if( $this->allow_unsubscribe )
		{
			$CODE = strtoupper(md5($to_saved));
			//$unsubscribe = "\n\n\n\n-- \nSome text here";
		}
		
		$to_name_encoded = $this->quoted_printable_encode( $to_name, $charset );
		$from_name = $this->quoted_printable_encode( $from_name, $charset );
		$subject = $this->quoted_printable_encode( $subject, $charset );
		if( !empty($to_name) ) $to = $to_name_encoded.' <'.$to.'>';
		
		if( empty($messages) || empty($hdrs) || !empty($this->report_body) || !empty($CODE) )
		{	// We don't want to generate headers and message every time!
			$uid = md5( time() - rand() );
			$uid2 = md5( $uid );
			if( $multipart != 'mixed' ) $uid2 = $uid;
			
			// Convert message encoding
			$message = $message.$unsubscribe;
			$body = $this->convert_charset( $message, $charset );
			$plain_body = strip_tags($body);
			$html_body = $body;
			$crlf = "\r\n";
			//collect SMTP auth
			$host = $this->Settings->get('smtp_domain');
			$username = $this->Settings->get('smtp_username');
			$password = $this->Settings->get('smtp_pass');
			
			// Construct Message headers
			$from = $from_name." <".$from.">";
			$hdrs = array ('From' => $from,
			  'To' => $to,
			  'Reply-To' => $this->Settings->get('sender_name').' <'.$this->Settings->get('reply_to').'>',
			  'Subject' => $subject,
			  'Return-path' => $this->Settings->get('sender_name').' <'.$this->Settings->get('sender_email').'>',
			  'Date' => date("r"));
  
			
			
			//construct actual body of messages
			$mime = new Mail_mime($crlf);

			$mime->setTXTBody($plain_body);
			$mime->setHTMLBody($html_body);
			//$mime->addAttachment($file, 'text/plain');
			
			//do not ever try to call these lines in reverse order
			$body = $mime->get();
			$hdrs = $mime->headers($hdrs);
			
			//start compiling email info with SMTP auth
			$smtp = Mail::factory('smtp',
			   array ('host' => $host,
					'auth' => true,
					'persist' => true,
					'localhost' => $host,
					'username' => $username,
					'password' => $password));
			
						
			if( !empty($Files) && is_array($Files) )
			{	// Attach files
				foreach( $Files as $c_file )
				{
					$messages .= '--'.$uid.$NL;
					$messages .= 'Content-Type: '.$c_file['ctype'].'; name="'.$c_file['name'].'"'.$NL;
					$messages .= 'Content-Transfer-Encoding: base64'.$NL;
					$messages .= 'Content-Disposition: attachment; filename="'.$c_file['name'].'"'.$NL.$NL;
					$messages .= $c_file['content'].$NL.$NL;
				}
			}
			$messages .= '--'.$uid.'--';
		}
		// Free some memory
		if( isset($Files) ) unset($Files);
		
		switch( $this->debug )
		{
			case 1:
				echo '<h2>TO:</h2>';
				pre_dump($to);
				echo '<h2>SUBJECT:</h2>';
				pre_dump($subject);
				echo '<h2>HEADERS:</h2>';
				pre_dump($hdrs);
				
				if( $mail = $smtp->send($to, $hdrs, $body) ) return true;
				break;
			
			case 2:
				echo '<h2>TO:</h2>';
				pre_dump($to);
				echo '<h2>SUBJECT:</h2>';
				pre_dump($subject);
				echo '<h2>HEADERS:</h2>';
				pre_dump($headers);
				echo '<h2>MESSAGE:</h2>';
				pre_dump($messages);
				
				mail( $to, $subject, $messages, $headers );
				die;
				break;
			
			case 3:
				echo '<h2>TO:</h2>';
				pre_dump($to);
				return true;
				break;
			
			case 0:
			default:
				// Send email
				 //$mail = $smtp->send($to, $headers, $body );
				//if(  $mail ) return true;
				//$mail = $smtp->send($to, $hdrs, $body);
				//$mail = $smtp->send($to, $hdrs, $body);
				if( $mail = $smtp->send($to, $hdrs, $body) ) return true;
				//$mail = $smtp->send($to, $headers, $body);

	
		}
		return false;
	}

	
	// Check if we want to send message to this user
	function check_user( $User )
	{
		global $UserSettings;
		
		if( !$this->UserSettings->get( 'mail_sender', $User->ID ) )
		{	// User don't want to receive emails
			return false;
		}
		
		if( is_array($this->group_ids) )
		{	// Check group ids
			if( in_array( $User->group_ID, $this->group_ids ) )
				return false;
		}
		
		if( is_array($this->user_ids) )
		{	// Check user ids
			if( in_array( $User->ID, $this->user_ids ) )
				return false;
		}
		return true;
	}
	
	
	// Quoted printable encode
	function quoted_printable_encode( $string, $charset = 'utf-8' )
	{
		$string = rawurlencode($string);
		$string = str_replace("%","=",$string);
		return "=?".$charset."?Q?".$string."?=";
	}
	
	
	// Trim array
	function array_trim( $array )
	{
		return array_map( 'trim', $array );
	}

		
	// Get MIME type of the file
	function get_mime_type( $filename )
	{
		$file_extension = strtolower( substr( strrchr( $filename, '.' ), 1 ) );
		
		switch( $file_extension )
		{
			case "pdf": $ctype = "application/pdf"; break;
			case "exe": $ctype = "application/octet-stream"; break;
			case "zip": $ctype = "application/zip"; break;
			case "doc": $ctype = "application/msword"; break;
			case "xls": $ctype = "application/vnd.ms-excel"; break;
			case "ppt": $ctype = "application/vnd.ms-powerpoint"; break;
			case "gif": $ctype = "image/gif"; break;
			case "png": $ctype = "image/png"; break;
			case "jpeg":
			case "jpg": $ctype = "image/jpg"; break;
			case "mp3": $ctype = "audio/mpeg"; break;
			case "wav": $ctype = "audio/x-wav"; break;
			case "mpeg":
			case "mpg":
			case "mpe": $ctype = "video/mpeg"; break;
			case "mov": $ctype = "video/quicktime"; break;
			case "avi": $ctype = "video/x-msvideo"; break;
			
			default: $ctype = "application/octet-stream";
		}
		return $ctype;
	}
	
	
	function get_file_content( $files, $charset = NULL )
	{
		global $Messages;

		if( empty($files) )
		{	
			$Messages->add( $this->T_('File not found') );
			return false;
		}
		
		$Files = $info = array();		
		if( is_array( $files ) )
		{
			foreach( $files as $file )
			{
				if( !$content = $this->get_data( $this->path.$file['name_md5'] ) )
				{
					$Messages->add( 'Cannot attach file ['.$file['name'].']' );
					return false;
				}
				
				$content = chunk_split( base64_encode($content) );
				$filename = $this->quoted_printable_encode( $file['name'], $charset );
				$Files[] = array('name' => $filename, 'ctype' => $file['type'], 'content' => $content);
			}
			//die( var_export($Files) );
		}
		return $Files;
	}
	
	
	function get_total_size( $kb = false )
	{
		global $DB;
		
		// Load files settings
		$this->files = $DB->get_results( 'SELECT * FROM '.$this->get_sql_table('uploads'), 'ARRAY_A' );

		$size = 0;
		if( !empty($this->files) )
		{
			foreach( $this->files as $file )
			{
				$size = $size + $file['size'];
			}
		}
		
		if( $kb )
		{
			return number_format( $size/1024, 2 ).' '.$this->T_('KB').'.';
		}
		return $size;
	}
	
			
	function check_uploads_dir( $msg = true )
	{
		global $Messages;
		
		if( !is_dir($this->path) )
			@mkdir($this->path);
			
		if( $msg && !is_writable($this->path) )
		{
			$Messages->add( sprintf( $this->T_('You must create the following directory with write permissions (777):%s'), '<br />'.$this->path), 'error' );
		}
	}
	
	
	/**
	 * Convert a string from one charset to another.
	 *
	 */
	function convert_charset( $string, $dest_charset, $src_charset = NULL )
	{
		if( $dest_charset == $src_charset || $dest_charset == '' )
		{ // no conversation required
			return $string;
		}	
		if( empty($src_charset) )
		{
			if( function_exists('mb_detect_encoding') )
			{
				$detect_string = $string.'a';
				$src_charset = mb_detect_encoding($detect_string, 'UTF-8, ISO-8859-1, ISO-8859-15', true);
			}
		}	
		if( function_exists('mb_convert_variables') )
		{ // mb_string extension:
			mb_convert_variables( $dest_charset, $src_charset, $string );
		}	
		return $string;
	}
	
	
	// Read remote or local file
	function get_data( $filename )
	{
		if( ! $content = @file_get_contents($filename) )
		{
			$content = fetch_remote_page( $filename, $info );
			if($info['status'] != '200') $content = '';
		}
		// Return content
		if( !empty($content) ) return $content;		
		return false;
	}
	
	
	/**
	 * @return boolean Does a given column name exist in DB?
	 */
	function db_col_exists( $table, $col_name )
	{
		global $DB;
	
		$col_name = strtolower($col_name);
	
		foreach( $DB->get_results('SHOW COLUMNS FROM '.$table) as $row )
			if( strtolower($row->Field) == $col_name )
				return true;
	
		return false;
	}
	
	
	/**
	 * Add a column, if it does not already exist.
	 */
	function db_add_col( $table, $col_name, $col_desc )
	{
		global $DB;
	
		if( $this->db_col_exists($table, $col_name) )
			return false;
	
		$DB->query( 'ALTER TABLE '.$table.' ADD COLUMN '.$col_name.' '.$col_desc );
	}
}

?>

Another question about this plugin, does it really check the user account to see if they want to receive emails from administrator?

Unsubscribing would be a great feature but how would b2 handle this? I can see it is in the works?

Great job again by the way

89 Jun 05, 2010 22:38

Plugin updated to v0.6

- Added unsubscribe feature.
Each email has an unsubscribe link now. This can be disabled in plugin config (in file).

90 Jan 19, 2011 02:08

Plugin updated to v0.7

- File lock created to prevent sending of duplicate emails
- Added ignore_user_abort and shutdown functions
- Improved user permission checks
- Fixed some bugs

91 Feb 27, 2011 02:24

the download link seems to be broken

92 Feb 27, 2011 18:26

It works for me now. My server was down for a few hours yesterday. Can you try again

93 Feb 27, 2011 21:55

sam2kb wrote:

It works for me now. My server was down for a few hours yesterday. Can you try again

yea it's fine now, thanks.

i am trying to figure how to make that associated categories plugin by examining this plugin :D

94 Feb 25, 2012 06:41

Plugin updated to v1.0.0

The plugin was rewritten, uses PHP Mailer 5.2.1 library
Now supports sending messages through "sendmail" and/or external SMTP servers.


Form is loading...