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

    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 <ddwebsite.h>
#include <stdexcept>
#include <ddbasetime.h>
#include <ddwebpage.h>
#include <ddwebconnection.h>
#include <dditemlink.h>
#include <ddwebview.h>

#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>


using namespace std;


/**
	Standard constructor.
*/

ddwebpage::ddwebpage( const ddstring& name, ddwebsite* site )
: ddthreadrunnable( name ), _connection( 0 ), _userid( 0 ), _item( 0 ), _site( site ), 
	_queuesize( 0 ), _queuemaxsize( 0 ), 
	_requestcount( 0 ), _maxtimeout( 0 ), _timeoutcount(0)
{
	
	// fragwürdig
	site->get_pagelet_map().add_by_key( name, this );	
		
	//------- snpage	

	// was macht dieser code?
	add_inner_call( "xx_itemselector", 	(ddmemptr) &ddwebpage::create_item_selector );
 	add_inner_call( "sn_countselector", (ddmemptr) &ddwebpage::create_count_selector );
	add_inner_call( "sn_typeselector", 	(ddmemptr) &ddwebpage::create_type_selector );
  // FIX!
  add_inner_call( "sn_groupselector",	(ddmemptr) &ddwebpage::create_group_selector );
	add_inner_call( "sn_usercontent",  	(ddmemptr) &ddwebpage::create_user_content_count ); 
		
	// das sind wohl alles boxen
	//add_inner_call( "sn_pager_alert",		(ddmemptr) &ddwebpage::create_pager_alert );
	//add_inner_call( "sn_pagerbox", 			(ddmemptr) &ddwebpage::create_pager_box );  
	//add_inner_call( "sn_adminpagerbox",	(ddmemptr) &ddwebpage::create_admin_pager_box );  
 
	
	// alt & hässlich
	add_inner_call( "sn_teambox", 			(ddmemptr) &ddwebpage::create_team_box );
	add_inner_call( "sn_memberbox", 		(ddmemptr) &ddwebpage::create_member_box );

}


/**
	Destructor.
*/

ddwebpage::~ddwebpage()
{
	for( int i=0; i<_items.size(); ++i )
		delete _items[i];	
	//delete _worker??
	get_thread()->exit();
}


/**
	alter rott, geht aber
*/

dd_result ddwebpage::create_team_box( dditem& item )
{
	//ddthreadautomutex tmplock( _sesmgrlock );
	get_buffer().append( get_session_manager().create_team_box() );
 	return rs_ok;
	(void) item;
}


/**
	alter rott, geht aber (NICHT!)
*/

dd_result ddwebpage::create_member_box( dditem& item )
{
	// quark!
	//ddthreadautomutex tmplock( _sesmgrlock );
	get_buffer().append( get_session_manager().create_member_box() );
 	return rs_ok;
	(void) item;
}


/**
	FIX!
	Lookup an item in the internal item cache. This is mainly used
	for automatic page generation when one pagelet uses different items,
	and not only one, as in the normal case.
*/

dditem* ddwebpage::lookup_item( const ddstring& rawkey )
{
	// cache lookup
	int i = _items.index_of( rawkey );
	// not found means error
	if( i<0 )
		throw runtime_error( "pagelet lookup item: item not found: " + rawkey );
	
	return _items[i];
	
}


/**
	Eine externe Callback-Funktion registrieren.
	@param key Der Name
	@param ptr Der Funktions-Pointer
*/

void ddwebpage::add_request_handler( const ddstring& requests, const ddstring& itemkey, ddmemptr function, size_t pathlen, size_t access )
{ 	
	
	// da der itemkey automatisch erzeugt wird, ist das item
	// nicht unbedingt vorhanden.
	
	ddstring compkey = itemkey;
	dditem* item = get_site().lookup_item( compkey );
	if( !item )
	{
		// Wenn das item nicht vorhanden ist, probieren wir
		// den dummy
		compkey = "xx_dummy";
		item = get_site().lookup_item( compkey );	
		if( !item )
			throw runtime_error( "sxpage::add_request_handler: item not found: " + compkey );
	}
	
 	ddwebpagecall entry( function, compkey, access, pathlen );
 	// es können mehrere requests angegeben werden
	ddstringrow pages( requests, ";" );
	for( size_t i = 0; i < pages.size(); ++i )
	{
    // den call speichern ... 
    _functions[ pages[i] ] = entry;
		// ... und bei der site als alias anmelden
		get_site().get_pagelet_map().add_alias( get_name(), pages[i] );
	}  
  
  // store a deep copy of item
	_items.add_by_key( compkey, item->clone() );
	
	
}


/**
	Eine interne Callback-Funktion registrieren.
	@param key Der Name
	@param ptr Der Funktions-Pointer
*/

void ddwebpage::add_inner_call( const ddstring& key, ddmemptr function, size_t access )
{
	add_inner_call( key, DDDEFAULTITEM, function, access );
}


/**
	Eine interne Callback-Funktion registrieren.
	@param key Der Name
	@param ptr Der Funktions-Pointer
*/

void ddwebpage::add_inner_call( const ddstring& key, const ddstring& itemkey, ddmemptr function, size_t access )
{ 	
	// es können mehrere requests angegeben werden
 	ddwebpagecall entry( function, itemkey, access );
  _functions[ key ] = entry;
	//add_item( itemkey );
	// preload items for each into cache
 	dditem* item = get_site().lookup_item( itemkey );
	if( !item )
		throw runtime_error( "ddwebpage::add_item: item not found: " + itemkey );
	// store a copy of item
	_items.add_by_key( itemkey, item->clone() );
}


/**
	Einen Callback ausführen.
*/

dd_result ddwebpage::execute_call( const ddstring& key, dditem& item )
{
	
	// schlimmer hack: block render call an site weiterreichen
	if( key.contains( "site." ) )
		return get_site().handle_block_render_call( *this, item );
	
	// check call
	ddwebpagecallmap::iterator pos = _functions.find( key );
	if( pos == _functions.end() )
		throw runtime_error( "pagelet execute_call: no entry for key: " + key + " item: " + item.get_name() );
 	
 	ddwebpagecall& entry  = (*pos).second;
  if( !entry.memptr )
		throw runtime_error( "pagelet execute_call: no function for key: " + key + " item: " + item.get_name() );
 
  // check access
  if( !( entry.access & get_user_id().get_access_rights() ) )
		return create_error( "Nanana!", ddwebresponse::ret_403_forbidden );
	
	return ( this->*(entry.memptr) )( item );
	
}


/**
 	Gibt eine Referenz auf den Usermanager zurück.
 	@see ddwebserver.get_user_manager()
 	@see ddusermanager
*/

ddusermanager& ddwebpage::get_user_manager()
{
	return get_site().get_user_manager();
}


/**
 	Gibt eine Referenz auf den Sessionmanager zurück.
*/

ddusersessionmanager& ddwebpage::get_session_manager()
{
	return get_site().get_session_manager();
}


/**
 	Gibt eine Referenz auf den Group-manager zurück.
 	@see ddwebsite.get_groups()
 	@see ddwebgroupmgr
*/

ddwebgroupmgr& ddwebpage::get_groups()
{
	return get_site().get_groups();
}



/**
	@return the current split (parsed) path data.
	This method is a shortcut to @see get_connection().get_request().get_split_path().
*/

ddstringrow& ddwebpage::get_split_path()
{
	return get_connection().get_request().get_split_path();
}


/**
	@return the current raw (unparsed) path data.
	This method is a shortcut to @see get_connection().get_request().get_raw_path().
*/

const ddstring& ddwebpage::get_raw_path()
{
	return get_connection().get_request().get_raw_path();
}


/**
	@return the current cgi data object.
	This method is a shortcut to @see get_connection().get_request().get_cgi()
	@see ddwebcgi
*/

ddwebcgi& ddwebpage::get_cgi()
{
	return get_connection().get_request().get_cgi();
}


/**
	@return eine referenz auf die webview namens 'key'.
	@see ddwebview
	@see ddwebviewmap
*/

ddwebview& ddwebpage::get_view( const ddstring& key )
{
  const ddstring& tablekey = key.empty() ? get_name() : key ;
  return get_site().get_view_map().get_entry( tablekey );
}



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

    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.

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


/**
	Berechnet die url der letzten Seite eines mehrseitigen eintrags:
	z.B: /t/2223234/34
*/
	
ddstring ddwebpage::get_jump_last_url()
{
	if( get_split_path().size() < 2 )
		return "";
	//??
	size_t lastpage = (context.numrows - 2) / context.itemspp;
	// hack: die letzte seite eines threads anzeigen
	if( !lastpage )
		return "/" + get_split_path()[0] + "/" + get_split_path()[1];
		
	return  "/" + get_split_path()[0] + "/" + get_split_path()[1] + "/" + ddstrutil::from_size_t( lastpage );
	
}


ddstring ddwebpage::get_image_path( bool realdir )
{
	bool is_member = ( get_split_path()[0] == "member" ); 
	ddstring path =  is_member ? DDUSERPICPATH + get_user_id().get_id() : DDPICPATH;	
	if( !is_member && realdir )
		path += + "sn_" + get_site().get_name() + "/";
	return path;
}

	
/**
	Erzeugt die Auswahlbox, in der sich die Artikel auf der Eingangseite
	nach Anzahl der Antworten filtern lassen.
*/

dd_result ddwebpage::create_count_selector( dditem& item )
{
  const ddstring& key = get_user_id().get_property( DDANSWERCOUNT );
	item.clear_children();
  item[ key.empty() ? "x" : key ].from_string( "selected" );
	get_buffer().append( item.to_string() );
	return rs_ok;
}



/**
	Erzeugt die Auswahlbox, in der sich die Anzahl der pro Seite angezeigten Artikel
	einstellen lässt.
*/

dd_result ddwebpage::create_item_selector( dditem& item )
{
  item.clear_children();
	ddstring xxx = ddstrutil::from_int( get_user_id().get_items_per_page() );
  item[xxx].from_string( "selected" );
	get_buffer().append( item.to_string() );
		
	return rs_ok;
	
}


/**
	FIX! Erzeugt eine Auswahlbox mit Artikeltypen, (news,faqs etc)
*/

dd_result ddwebpage::create_type_selector( dditem& item )
{
 	item.clear_children();
	ddstring& id_type = get_cgi()["id_type"];
  if( !id_type.empty() && id_type !="#" ) 
		item[id_type].from_string( "selected" );
	get_buffer().append( item.to_string() );
	return rs_ok;
}


/**
	FIX! Erzeugt eine Auswahlbox mit den SN-Diskussionsgruppen.
*/

dd_result ddwebpage::create_group_selector( dditem& item )
{
	ddstring groups = get_groups().create_group_selector( get_cgi()["id_group"], get_user_id().is_admin() );
	get_buffer().append( groups );
	return rs_ok;
	(void) item;
}


/**
	Anzeige, wieviele FAQ/News Beiträge unsere geschätzten user 
	geschrieben haben.
*/

dd_result ddwebpage::create_user_content_count( dditem& item )
{
	get_buffer().append( get_site().get_user_content_count() );
	return rs_ok;
	(void) item;
}


/**
	Holt die ID eines Draft-postings aus der DB. Wird gebraucht für
	die Bildverwaltung, Die DB zugriffe auf die draft-table sollen
	wg. locking hier konzentriert werden.
*/

ddstring ddwebpage::fetch_draft_id( bool docreate )
{
	// FIX! LOCK!
	
	ddwebview& table = get_view( "sxpageforumstart.draft" );
	dduserid&  uid   = get_user_id();
	
	// das ist die edit-id, mutti oder kind
	ddstring entryid = uid.get_property( DDENTRYID );
 	
 	// nix zu editieren -> dann arbeiten wir vielleicht an was neuem ?
 	if( entryid.empty() )
 		table.execute( "fetch_draft", uid.get_id() ).read( entryid );
	
	// auch nicht ? dann bauen wir einen draft eintrag,
	// falls gewünscht
	if( entryid.empty() && docreate )
	{
		
		// hier kann es auch sein, das ein beitrag editiert wird
		// und die Id bzw. das verzeichnis schon vorhanden sind 
		// -> hinzufügen statt neu bauen
			
		table.execute( "create_draft", uid.get_id() );
		table.execute( "draft_id" ).read( entryid );
		
	}
		
	// keine leer id zurückgeben, sonst wird das ganze verzeichnis
	// 'articleimage/' statt 'articleimage/xyz' geladen	
	if( entryid.empty() )
		entryid = "0";

	return entryid;
	
}



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

    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.

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


/**
	Constructor.
*/

ddwebpageauto::ddwebpageauto( const ddstring& name, ddwebsite* site )
: ddwebpage( name, site )
{

}


void ddwebpageauto::init_page( const ddstring& configstr )
{
	ddstringrow pages( configstr, ";" );
	for( size_t i=0; i<pages.size(); ++i )
	{
    size_t dot = pages[i].rfind('.');
		//add_outer_call( pages[i], pages[i].substr( 0, dot ) );
		add_request_handler( pages[i], pages[i].substr( 0, dot ) );
	}
}
	

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

    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.

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


/**
	Constructor.
*/

ddwebpagestatic::ddwebpagestatic( const ddstring& name, ddwebsite* site )
: ddwebpageauto( name, site )
{
	init_page( site->get_config()["pagemap"]["snautopages"] );
}


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

    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.

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


/**
	Constructor.
*/

ddwebpagehelp::ddwebpagehelp( const ddstring& name, ddwebsite* site )
: ddwebpageauto( name, site )
{
	context.pagetype = "help";
	init_page( site->get_config()["pagemap"]["snpagehelp"] );
}


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

    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.

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

/**
	Ab hier: Proxy
*/

/**
	Einen Request in die Wait-Queue schreiben.
*/

size_t ddwebpage::enqueue_request( ddwebconnection* connection, dduserid* userid )
{	
	ddthreadautomutex tmplock( _mutex );
	userid->user_lock();
	ddwebpagerequest request( connection, userid );
	_queue.push_back( request );
 	_queuesize = _queue.size();
  if( _queuesize > _queuemaxsize )
		_queuemaxsize = _queuesize;	
	return _queuesize;
}


/**
	Einen Request aus der Wait-Queue holen
*/

bool ddwebpage::dequeue_request( ddwebpagerequest& request )
{
	ddthreadautomutex tmplock( _mutex );
  if( _queue.empty() )
    return false;
	request = _queue.front();
	_queue.pop_front();
  _queuesize = _queue.size();
	return true;	
}


/**
	Die Requests in einer Endlosschleife bearbeiten.
	Wenn der Worker-Thread aufgeweckt wird, wird @see handle_request
	aufgerufen.
*/

void ddwebpage::run()
{
  
  wait();
 	_fifo = ::open( "dope.fifo", O_WRONLY );
  if( _fifo<0 )
		throw runtime_error( "ddwebpage::run(): fifo failure!" );

	while( true )
 	{
    if( _queue.empty() )
			wait();
    
	  // jemand hat uns aufgeweckt ...
		// request.connection == 0: das sollte nicht passieren, warum passierts trotzdem ?
		ddwebpagerequest request;
    if( !( dequeue_request( request ) && request.connection ) )
    	continue;
		
		// ora et labora ...
		webpage_handle_request( request );
				
		// ergebnis ins domain log schreiben.
		// hier stellt sich eigentlich die frage, was im fehler fall geloggt
		// werden soll: kommt der fehler ins error log oder wird die html-antwort
		// (etwa 404 not found) als normaler request ins mainlog geschrieben?
		// Mometan ist lezteres der fall. 
		
		// vor dem loggen muss connection->handle_xy aufgerufen worden sein,
		// damit die content-length gesetzt ist.
		
		get_site().log_message( ddlogdevice::lg_main, request.connection->get_log_entry() ); 	

		// den user wieder freigeben (zum löschen wg. timeout )
    get_user_id().user_unlock();
    
    // notify parent
		::write( _fifo, DDFIFONOTIFIER, ::strlen(DDFIFONOTIFIER) );							
 	
 	} // loop

}
	

/**
	Ex proxy methode, wird von @see run() beim wecken des Workers
	aufgerufen. Setzt user_id und webconnection und ruft die
	methode @see create_content() auf.
*/

void ddwebpage::webpage_handle_request( ddwebpagerequest& request )
{
	
	// connection holen
	ddwebconnection* connection = request.connection; 
	set_connection( connection );
	// daten übergeben
	set_user_id( request.userid );
	// store connection ip
	get_user_id().set_host_ip( connection->get_host_ip() );
	
	_requestcount++;

	
	time_t timeout = ::time(0) - request.starttime;
	if( timeout > _maxtimeout )
		_maxtimeout = timeout;		
	// mal wieder zu lange gebraucht?
	if( timeout > DDREQUESTTIMEOUT )
	{
		_timeoutcount++;
		return	webpage_handle_error( connection, "Request Timeout", ddwebresponse::ret_408_request_time_out );				
	}
		
	// Den workerthread losscheuchen		
	// Achtung! die connection kann derweil ihren zustand ändern!
	// macht aber nix, das wird in connection.handle_write eingefangen.
	
	try
	{
		
		// cachen ist jetzt immer erlaubt, damit die browser nicht
		// immer alles neu laden und dann jeweils am Seitenanfang kleben bleiben.
		connection->get_response().set_allow_caching( true );
	
		int retcode = ddwebresponse::ret_200_ok;
		
		// PFUSCH: Verweise auf supportware.de mit gewalt umbiegen
		const ddstring& dompath = connection->get_request().get_host_name();
			


			// kein redirekt: content basteln
			
			// result >  0 means ok
			// result == 0 means temp redirect: 302
			// result <  0 means error
	
			int result = webpage_handle_content( *connection, *request.userid );
			// redirekt request
			if( result == rs_retry )
				retcode = ddwebresponse::ret_302_moved_temporarily;
			else if( result == rs_redirect )	
				retcode = ddwebresponse::ret_301_moved_permanently;
			// ggf. die Fehlermeldungen des pagelets übernehmen, falls dieses verkackt hat			
			else if( result < 0 )			
				return webpage_handle_error( connection, context.errortext, context.errorcode );		

				
		// alles ok: BEACHTE: ANTI-SCHREIB FLAG SETZEN!
		connection->connection_handle_response( retcode, context.contenttype, false );
		
	}
	catch( exception& error )
	{
		return webpage_handle_error( connection, ddstring("pagelet core exception:")+error.what(), ddwebresponse::ret_500_internal_server_error );				
	}
	

}


/**
	Sucht die Passende Callback-Methode und das dafür passende Item und
	ruft sie auf. Im Erfolgsfall wird das Item in das Ausgabepuffer 
	geschrieben.
*/

dd_result ddwebpage::webpage_handle_content( ddwebconnection& connection, dduserid& user )
{
  
  dd_result result = rs_error;

  try
  {
				
		// NEU: 'relative methode', also etwa /threads/1233?snx=moo
		// werden vorher abgefangen
		
		const ddstringrow& path = get_split_path();
		ddstring actionkey	= path[0];
		ddwebcgi& cgi = get_cgi();
		
		if( cgi.contains_key( DDACTIONKEY ) && !cgi[ DDACTIONKEY ].empty() )
			actionkey =	cgi[ DDACTIONKEY ];
		
		// NEU: die Pfadlänge wird schon hier geprüft und
		// die pfadargumente werden gesetzt
		size_t minlen = _functions[actionkey].pathlen;
		if( path.size() < minlen )
			return create_error( "Pfad zu kurz!", ddwebresponse::ret_404_not_found );
		
		// den context resetten ...
		context.reset();
		
		// ... und werte setzen
		// die pfadargumente richten sich nach der pfadlänge
		if( path.size() > 1 )
			context.id = context.idgrp = path[1];
		
		// wenn die pfadlänge größer als die minimallänge ist,
		// dann ist das letzte stück die blocknummer (=seite) ...
		if( path.size() > minlen )
			context.navipage = path[ path.size()-1 ].as_int();
		
		// ... falls das cgi allerdings eine seitenangabe enthält, dann ist diese
		// massgeblich
		if( cgi.contains_key( "page" ) )
			context.navipage =  cgi["page"].as_int();
		
		// ausschnitt der tabelle ermitteln
		context.itemspp  = get_user_id().get_items_per_page();
		context.navifrom = context.navipage * context.itemspp;
		context.navito   = context.navifrom + context.itemspp;
		
		// default title
		context.pagetitle = context.pagetitlestatic;
	
		
		// die ids im referer werden neuerdings auch gebraucht,
		// für die bildverwaltung: es kommt ein verwaltungsrequest
		// _ohne_ id, also muss rausgefunden werden, welcher eintrag
		// gemeint sein könnte. hier gibts zwei fälle: mutti & kind.
		
		// die id wird nur gesetzt, wenn tatsächlich was editiert wurde.
		
		ddstring itemkey    = _functions[actionkey].itemkey;
		_item = lookup_item( itemkey );
		
		// butter bei die fische
		result = execute_call( actionkey, *_item );		
    
    if( result == rs_ok )
    { 		     	
     	result = get_item().flush_content( *this );		  
			user.increment_hits();      
			
			// speichert den request als eine art internen referer
			user.set_property( DDLASTPATH, get_raw_path() );					
  	}
  }
  catch( exception& error )
  {
  	ddstring msg = "pagelet content exception: " + get_name();
		ddstring fullmsg = msg + " :" + error.what();
		log_error( fullmsg );
		// achtung: nur chefs bekommen die Details
		return create_error( user.is_admin() ? fullmsg : msg );  	
  }
  

	return result;
	
}


/**
	Einen 302 bzw. 301 redirect erzeugen.
  @param reqpath das Sprungziel
*/

dd_result ddwebpage::create_redirect( const ddstring& reqpath, dd_redirect_type redirecttype )
{
  
  //dd_result retval = hardredirect ? rs_redirect : rs_retry;
  dd_result retval = redirecttype == ret_301_moved_permanently ? rs_redirect : rs_retry;
  
  // hack: auf die ursprungsseite zurückschicken
	ddstring referer = reqpath;
  if( referer.empty() )
		referer = get_connection().get_request().get_referer();
	if( referer.empty() )
		referer = "/";

	ddwebrequest&  request  = get_connection().get_request();
	ddwebresponse& response = get_connection().get_response();
	
	//	FIX!
	//	Was zur Hölle passiert hier??

	if( referer.find("http://") != ddstring::npos )
	{
		size_t i=7;
		// den host-part suchen
		while( i<referer.size() && referer[i] != '/' )
			i++;
		response.get_location() = referer;
		request.set_raw_path( referer.substr( i ) );
		return retval;
	}
	response.get_location() = "http://"+request.get_host_port()+referer;
	request.set_raw_path( referer );
	return retval;
}


/**
	Fehlermeldung erzeugen und der connection bescheid geben.
*/

void ddwebpage::webpage_handle_error( ddwebconnection* connection, const ddstring& errortext, int errorcode )
{
	ddstring msg = " (f&uuml;r: " + get_site().get_name() +": ";
	msg += connection->get_request().get_raw_path() +")";
	// BEACHTE: ANTI-SCHREIB FLAG SETZEN!
	connection->connection_handle_error( errortext + msg, errorcode, false );		
}


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

    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.

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

/**
	Ab hier: pageletmap
*/

/**
	search for a pagelet key and return the internal index or NULL.
	( substring match, first match is returned )	
	@param key the match key 
*/

ddwebpage* ddwebpagemap::lookup( const ddstring& matchstring )
{
	size_t qm = matchstring.find('?');
	ddstring seekstr;
	if( qm == ddstring::npos )
		seekstr = matchstring;
	else
		seekstr = matchstring.substr( 0, qm );		
	seekstr.to_lower();
	ddmapindex::iterator pos = _index.find( seekstr );
	if( pos == _index.end() )
		return 0;		
	return _data[(*pos).second];		
}



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

    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.

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





sxpagehelp::sxpagehelp( const ddstring& name, ddwebsite* site )
: ddwebpageauto( name, site )
{
	context.pagetype = "help";
	init_page( site->get_config()["pagemap"]["sxpagehelp"] );
	//context.breadcrump = "<a href='/sx_about_help_main.html' class='box_title_bc'>Hilfe</a>";
}

