/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

***************************************************************************/


#include <ddmailmanager.h>
#include <stdio.h>


ddstring ddmailmanager::themailcmd = "/usr/bin/mail -s ";



/**
	Standard Constructor. Startet auch den Background-Thread
*/

ddmailmanager::ddmailmanager()
: ddthreadrunnable( "ddmailmanager" ), _replymails( 0 )
{
	
}


/**
	Destructor.
*/

ddmailmanager::~ddmailmanager()
{
	//delete _worker??
	get_thread()->exit();
}


/**
	Initialisierung.
*/

void ddmailmanager::init_mailer( ddconfig& config, const ddstring& path )
{
	themailcmd      = config["main"]["mail_command"];
	_templates.init_items( path + config["main"]["mail_root"] );
	_mailalerts.init_queries( config["mailmanager"] );
	_mailalerts.connect( config["main"]["connectstring"] );		

	start_thread();
}


/**
	Eine Mail in dier Warteschlange stellen.
*/

size_t ddmailmanager::enqueue_mail(  const ddstring& recv, const ddstring& msg, const ddstring& subject, const ddstring& sender  )
{
 	_mutex.lock();
  _queue.push_back( ddmail( recv, msg, subject, sender ) );
 	_mutex.unlock();
	// den worker losscheuchen
	notify();
	return _queue.size();
}


/**
	In einer Endlosschleife die Warteschlange abarbeiten.
*/

void ddmailmanager::run()
{
  wait();
 	
	while( true )
 	{
    if( _queue.empty() )
			wait();
		
		// jemand hat uns aufgeweckt ...
    try
    {
			_mutex.lock();
			// wakeup bug:  sleep again
			if( !_queue.size() ) 
			{
				_mutex.unlock();		
				continue;
			}
			ddmail mail = _queue.front();
  		_queue.pop_front(); 
 			_mutex.unlock();
			if( !mail.receiver.empty() && !mail.message.empty() )
				send_mail( mail.message, mail.subject, mail.receiver, mail.sender );
	
    }
    catch( exception& error )
    {
			log_error( ddstring("ddmailmanager::run(): ") + error.what() );
    }
 	
	} // while


}


/**
	Testet, ob der user schon benachrichtigt wurden
*/

bool ddmailmanager::is_alertet( const ddstring& nickname, const ddstring& parentid )
{		
	return _alertcache.count( parentid + "." + nickname ) != 0;
}


/**
	Das Altertflag wieder löschen, sodass der user neu benachrichtigt wird, wenn
	Antworten kommen. Das löschen wird durchgeführt, wenn der user den Thread neu aufruft.
*/

void ddmailmanager::clear_alert_flag( const ddstring& nickname, const ddstring& parentid )
{
	_alertcache.erase( parentid + "." + nickname );
}


/**
	Das Alertflag setzen,
*/

void ddmailmanager::set_alert_flag( const ddstring& nickname, const ddstring& parentid )
{
	_alertcache.insert( parentid + "." + nickname );
}
	

/**
	Erzeugt die Benachrichtigungsmails.
*/

void ddmailmanager::create_alert_mails( const ddstring& idparent, const ddstring& nickmail, const ddstring& nickname, const ddstring& subject, const ddstring& domain )
{
   
   // select="select distinct sn_nickmail, sn_nickname from tr_msgmain where id_state & 8 != 0 
	//	and sn_nickmail != '' and id_parent='{id_parent}' and sn_nickmail != '{sn_nickmail}'";
	
	// Wenn hier einige parameter-felder nicht belegt sind, etwa nickmail, dann bleibt der vorher benutzte parameter
	// wert stehen, das ist ein feature, siehe ddatastore::execute
	// natürlich ist dann das Abfrageergebnis fehlerhaft, deswegen resetten wir die query von hand.
	
	_mailalerts.get_query( "select" ).clear_query_data();
	int numrows = _mailalerts.fill( "select", idparent, nickmail );
	for( int i = 0; i < numrows; ++i )
	{		
		bool isguest = (_mailalerts[i][2] == "1" );
		create_reply_mail( idparent, _mailalerts[i][0], _mailalerts[i][1], subject, domain, isguest );
	}

}


/**
	Eine Benachrichtigungsmail verschicken, wenn neue Antworten auf eine Anfrage gegeben wurden.	
*/

void ddmailmanager::create_reply_mail( const ddstring& parentid, const ddstring& nickmail, const ddstring& nickname, const ddstring& subject, const ddstring& dom, bool isguest )
{
	
	ddstring domain = dom;
	domain.replace_all( "computer", "www" );
	
	// Bei neuen Antworten wird der user benachrichtigt und als benachrichtigt markiert, wenn er denn thread 
	// nochmal besucht, wird die markierung gelöscht, sodass bei neuen Antworten wieder ein benachrichtigung
	// verschickt wird.
	
	if( nickmail.empty() || is_alertet( nickname, parentid ) )
		return;
		
	// ALT:
	// Es soll nur eine mail pro tag und thread und person verschickt werden.		
	//ddstring key = ddbasedate::now().to_string_de() + "." + parentid + "." + nickname;
	//cout << " ------------ REPLYMAIL: " << key  << endl;
	//if( _alertcache.count( key ) != 0 )	
	//		return;
	if( !isguest )	
		set_alert_flag( nickname, parentid );

	ddstring tpl = "sn_replymail";	
	int i = _templates.index_of( tpl );
	if( i<0 )
		throw runtime_error("ddmailmanager::create_reply_mail() template not found: " + tpl );

	ddstring thread = parentid.as_int() <= DDLASTID ? "/threads/" : "/t/" ;
	ddstring link1 =  thread + parentid;
	ddstring link2 =  "/admin_drop_alert?id_parent=" + parentid;
	
	// Hack: Zwischen altem und neum Format unterscheiden
	// alt:
	//if( domain != "www" ) 
		link2 = link1 + "?sx_action=on_drop_subscription";
		
	link2 += "&sn_nickmail=";
	link2 += nickmail.dup().escape();
	
	ddstring cleansbj = subject;
	cleansbj.replace_all( "RE: ", "" );
	
	// neu: subject konfigurierbar machen
	dditem& mailsubj = _templates[i]["sn_mailsubject"];
	dditem& mailbody = _templates[i]["sn_mailbody"];
	
	mailbody["sn_nickname"].from_string( nickname );
	mailbody["sn_msgsubject"].from_string( cleansbj );
	mailbody["sn_link1"].from_string( link1 );	
	mailbody["sn_link2"].from_string( link2 );	
	
	mailsubj["sn_msgsubject"].from_string( cleansbj );
	
	_replymails++;
	enqueue_mail( nickmail, mailbody.to_string(), mailsubj.to_string(), "sourceworx.org Forum" );

}


/**
	Erzeugt die Mail, die den user über die Änderung seines Nicknames benachrichtigt:
	blödi$$ -> bloedi__ . Damit sind die Sn-Nicks eMail-konform.
*/

void ddmailmanager::create_nick_mail( const ddstring& nickname, const ddstring& nickmail, const ddstring& newnick )
{
	ddstring tpl = "sn_nickchange";
	
	int i = _templates.index_of( tpl );
	if( i<0 )
		throw runtime_error("ddmailmanager::create_nick_mail() template not found: " + tpl );

	dditem& mailitem = _templates[i];
	mailitem["sn_nickname1"].from_string( nickname );
	mailitem["sn_nickname2"].from_string( nickname );
	mailitem["sn_nickname3"].from_string( newnick );

	enqueue_mail( nickmail, mailitem.to_string(), "Ihr neuer Nickname: " + newnick, "sourceworx.org Forum"  );

}


/**
	Erzeugt die erste Anmeldemail mit Passwort und Login.
*/

void ddmailmanager::create_login_mail( const ddstring& nickname, const ddstring& nickmail, const ddstring& passwd, const ddstring& domain )
{
	ddstring tpl = "sn_newuser";
	
	int i = _templates.index_of( tpl );
	if( i<0 )
		throw runtime_error("ddmailmanager::create_passwd_mail() template not found: " + tpl );

	dditem& mailitem = _templates[i];
	mailitem["sn_nickname1"].from_string( nickname );
	mailitem["sn_nickname2"].from_string( nickname );
	mailitem["sn_passwd"].from_string( passwd );
	

	ddstring link1 = "/member_activate?sn_nickname=" + nickname + "&sn_passwd=" + passwd;
	ddstring link2 = "/member_login?sn_nickname=" + nickname;
	mailitem["sn_loginlink1"].from_string( link1 );
	mailitem["sn_loginlink2"].from_string( link2 );
	
	enqueue_mail( nickmail, mailitem.to_string(), "Ihr Passwort", "sourceworx.org Forum"  );

}


/**
	Password verschicken.
*/

void ddmailmanager::create_passwd_mail( const ddstring& nickname, const ddstring& nickmail, const ddstring& passwd )
{
	ddstring tpl = "sn_passwd";
	
	int i = _templates.index_of( tpl );
	if( i<0 )
		throw runtime_error("ddmailmanager::create_passwd_mail() template not found: " + tpl );

	dditem& mailitem = _templates[i];
	mailitem["sn_nickname"].from_string( nickname );
	mailitem["sn_passwd"].from_string( passwd );

	enqueue_mail( nickmail, mailitem.to_string(), "Ihr Passwort", "sourceworx.org Forum" );
}


/**
	Erzeugt die Hinweismail, 'sie haben post' etc.
*/

void ddmailmanager::create_hint_mail( const ddstring& nickname, const ddstring& nickmail, const ddstring& subject, const ddstring& sysmessage, const ddstring& syslink )
{
	
	ddstring tpl = "sn_passwd";
	
	int i = _templates.index_of( tpl );
	if( i<0 )
		throw runtime_error("ddmailmanager::create_passwd_mail() template not found: " + tpl );

	dditem& mailitem = _templates[i];
	mailitem["sn_nickname"].from_string( nickname );
	//mailitem["sn_passwd"].from_string( passwd );

	enqueue_mail( nickmail, mailitem.to_string(), subject, "sourceworx.org Forum" );
	
}


/**
	Das ist die alte Version der Mail-Funktion, die in mail(x) rein-piped in sendmail. (@see pipe_mail)
	Damit kann man _keine_ html mails verschicken, weil man die header (content-type etc) nicht beeinflussen 
	kann, @see http://forums11.itrc.hp.com/service/forums/questionanswer.do?admit=109447626+1251151055312+28353475&threadId=198929
	@see http://www.linuxquestions.org/questions/linux-networking-3/sending-html-email-via-mailx-380855/
*/

void ddmailmanager::send_mail( const ddstring& msg, const ddstring& cleansbj, const ddstring& recv, const ddstring& sender )
{  
	//strip quotes in subject
	ddstring subject  = cleansbj;
	subject.replace_all( '\"',' ' );			
	ddstring cmd = themailcmd + "\"" + subject + "\" ";
	if( !sender.empty() )
		cmd += " -r \"" + sender + "\" ";
	cmd += recv;
	
	FILE* outstream = ::popen( cmd.c_str(),"w" );
	if( !outstream )
	{
		// diese Funktion ist static, also müssen wir den (ebenfalls statischen) logger
		// direkt ansprechen
		thelog.log_message( ddlogdevice::lg_error, "error: Could not run send MAIL: " + cmd );
		return;
	}
	::fputs( msg.c_str(),outstream );
	::fclose( outstream );
}



/**
	Das ist die Version der Mail-Funktion, die in sendmail rein-piped statt in mail(x)
	Dann kann man dann auch die header einträge bestimmen und html senden.
	'mail' ist ein alias für 'mailx' welches ein client für 'sendmail' ist wobei 'sendmail' nicht mehr
	existiert sondern die compatibilty-layer von postfix ist. oh unix!
	
	das subject muss noch ins template-item geschrieben werden, daher wird das hier mit übergeben.
	
*/

void ddmailmanager::pipe_mail( dditem& templ, const ddstring& recv, const ddstring& subj, const ddstring& sender )
{  
	//# Cat the header and message file into sendmail and send it
// cat header.tmp message.html | /usr/sbin/sendmail whomever@whereever.org

	//strip quotes in subject
	ddstring subject  = subj;
	subject.replace_all( '\"',' ' );	
	
	// das ganze geraffel wird jetzt in mail-template
	// reingeschrieben und an den mail-server gepipet
	// damit es auch schön in der mail angezeigt wird
	
	templ.set_field_value( "sn_mailsubject", subject );		
	templ.set_field_value( "sn_mailto", recv );		
	templ.set_field_value( "sn_mailfrom", sender );		
	
	ddstring cmd = "/usr/sbin/sendmail ";
	
	// geht nicht
	//if( !sender.empty() )
	//	cmd += " -r " + sender + " ";
	cmd += recv;
	
	cout << "RAAAAAMOOOOO: " << cmd << endl;
	
	FILE* outstream = ::popen( cmd.c_str(),"w" );
	if( !outstream )
	{
		// diese Funktion ist static, also müssen wir den (ebenfalls statischen) logger
		// direkt ansprechen
		thelog.log_message( ddlogdevice::lg_error, "error: Could not run pipe MAIL: " + cmd );
		return;
	}
	::fputs( templ.to_string().c_str(),outstream );
	::fclose( outstream );
}
