/*
 * POP3Lite - 3lite POP3 Daemon
 * Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu>
 *
 * This file is part of POP3Lite.
 *
 * POP3Lite 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.
 *
 * POP3Lite is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/*
 * This file contains most of the functions mentioned in pop3lite.h
 */

#include "main.h"

#include <sys/param.h>

#include <pop3lite.h>
#include <ctype.h>
#include <glib.h>
#include <time.h>
#include <syslog.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef HAVE_FLOCK
#	include <sys/file.h>
#endif

#ifdef HAVE_FCNTL_H
#	include <fcntl.h>
#endif

#ifdef HAVE_LOCKFILE
#	include <lockfile.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#ifndef MAXHOSTNAMELEN
#	define MAXHOSTNAMELEN 64
#endif

static const char rcsid[]="$Id: misc.c,v 1.18.2.6 2001/06/08 09:27:05 algernon Exp $";
static const char received_header[] =
	"Received: by %s (mbox %s)\n (with pop3lite (%s) %.24s)";

/**
 * p3l_received_header: return a pop3lite header
 * @data: control->data
 *
 * constructcs a received: header
 *
 * returns: the header, which should be freed
 **/

char *
p3l_received_header ( GHashTable *data )
{
	time_t lasttime;
	char *user;
	char hostname[MAXHOSTNAMELEN];

#ifdef HAVE_GETHOSTNAME
	gethostname ( hostname, sizeof ( hostname ) );
#else
	strcpy ( (char *) hostname, "unknown" );
#endif

	lasttime = time ( (time_t *) 0 );
	user = g_hash_table_lookup ( data, "USER" );
	return g_strdup_printf ( received_header, hostname,
				 user, VERSION, ctime ( &lasttime ) );
}

/**
 * p3l_parse_buffer: replace From [...] with Received:
 * @buffer: the mail text
 * @data: control->data
 * @buffer_size: size of the buffer
 * @new_size: pointer where the new size will be placed
 *
 * Replaces the first line (From [,,,]) of the message with
 * the POP3Lite-specific Received: header. (buffer will be
 * freed!)
 *
 * Returns: the new buffer
 **/

char *
p3l_parse_buffer ( char *buffer, GHashTable *data,
		   size_t buffer_size,
		   size_t *new_size )
{
	char *banner, *buff, *xfrom;
	size_t nsize, i = 0;

	/*
	 * Prepare X-From_ field
	 */

	i = strchr ( buffer, '\n' ) - buffer;
	xfrom = (char *) g_malloc ( i+10 );
	memcpy ( xfrom, "\nX-From_: ", 10 );
	memcpy ( xfrom + 10, buffer + strlen ( "From " ), i - strlen ( "From " ) );
	xfrom[ i - strlen ( "From " ) + 10 ]='\0';

	/*
	 * Get the banner
	 */

	banner = g_hash_table_lookup ( data, "BANNER" );

	nsize = buffer_size + strlen ( banner ) - i + strlen ( xfrom );

	/*
	 * Prepare the new buffer
	 */

	buff = g_malloc (  nsize );
	memcpy ( buff, banner, strlen ( banner ) );
	memcpy ( buff + strlen ( banner ), xfrom, strlen ( xfrom ) );
	memcpy ( buff + strlen ( banner ) + strlen ( xfrom ), buffer + i, buffer_size - i );
	g_free ( buffer);

	if ( new_size != NULL )
		*new_size = nsize;

	return buff;
}

/**
 * p3l_respond: generate a POP3Lite response
 * code: response code see POP3Code above
 * message: the actual message
 *
 * Generates a response suitable to be used as the
 * return value of POP3 Command implementations.
 *
 * Returns: the POP3Lite response struct
 **/

CommandResponse *
p3l_respond ( POP3Code code, const char *message )
{
	CommandResponse *res;

	res = (CommandResponse *) g_malloc ( sizeof ( CommandResponse ) );
	res->code = code;
	res->message = g_strdup ( message );

	return res;
}

/**
 * p3l_is_enabled: check if an option is enabled
 * @option: the option string
 *
 * Parses @option, and returns a boolean indicating
 * wheter it is enabled or not. (true, yes, on means
 * enabled, everything else is disabled)
 *
 * Returns: a boolean
 **/

gboolean
p3l_is_enabled ( const char *option )
{
	char *tmp;
	gboolean v_ret = FALSE;

	tmp = g_strdup ( option );

	if ( tmp == NULL )
		return FALSE;

	g_strup ( tmp );

	if ( !strcmp ( tmp, "TRUE" ) || !strcmp ( tmp, "ON" ) ||
	     !strcmp ( tmp, "YES" ) )
	     	v_ret = TRUE;

	g_free ( tmp );
	return v_ret;
}

/**
 * bintohex: convert a binary string to hexadecimal
 * @ptr: the binary sequence
 * @size: length of the sequence
 * @checkendian: wheter to check endianness, or not
 *
 * Converts a binary sequence to its hexadecimal
 * representation.
 *
 * Returns: the hexadecimal representation.
 **/

const char *
bintohex ( const void *ptr, size_t size, const int checkendian )
{
	static char hex[33],*hp;
	int inc = 1;

	{
		const int test = 1;
		if ( checkendian && ! *(const char *)&test )
			ptr = (const char *) ptr + size + ( inc = -1 );
	}

	for ( hp = hex; size-- ; ptr = (const char *) ptr + inc, hp += 2 )
		sprintf ( hp, "%02x", *(unsigned char *) ptr );

	return hex;
}

/**
 * p3l_command_replace: replace a command
 * @commands: the command table (control->{auth,trans}_commands
 * @name: the command name
 * @new_command: the new command
 *
 * Replaces a command in a given table.
 *
 * Returns: the old command.
 **/

const gpointer
p3l_command_replace ( GHashTable *commands, const char *name, gpointer new_command )
{
	gpointer backup;

	backup = g_hash_table_lookup ( commands, name );
	g_hash_table_insert ( commands, g_strdup ( name ), new_command );

	return backup;
}

/**
 * p3l_split_lines: split a buffer into lines
 * @buffer: source
 * @size: the buffer's size
 *
 * Splits the buffer into lines. This is NULL-aware, that means,
 * that the strings are NOT null terminated, therefore, their
 * size must be stored.
 *
 * Returns: a pointer to a P3LString array
 **/

P3LString **
p3l_split_lines ( char *buffer, size_t size, size_t *lines )
{
	P3LString **output;
	size_t i, j = 0, line = 0;

	*lines = 0;

	/*
	 * In the first run, count the number of lines...
	 */
	for ( i = 0; i < size; i++ )
		if ( buffer[i] == '\n' ) (*lines)++;

	/*
	 * Allocate enough memory
	 */
	output = (P3LString **) g_malloc ( ( (*lines) + 1 ) * sizeof ( P3LString * ) );

	/*
	 * Do the copying...
	 */
	i=0;
	while ( i < size && size > 0 )
	{
		j=i;
		while ( j < size - 1 && buffer[j] != '\n' )
			j++;

		output[line] = (P3LString *) g_malloc (  sizeof ( P3LString ) );
		output[line]->length = j - i + 1;
		output[line]->str = &buffer[i];

		line++;
		i = j + 1;
		
	}
	if ( i > 0 && buffer[i-1] != '\n' ) line--;
	(*lines) = line;
	output[line] = NULL;

	return output;
}

/**
 * p3l_lock_fd: lock a file
 * @fd: file descriptor
 *
 * Locks a file.
 *
 * Returns: 0 on success, -1 otherwise
 **/

int
p3l_lock_fd ( int fd, char *fn )
{
	int retcode;
#ifdef HAVE_FCNTL_H
	struct flock arg;
	
	arg.l_type = F_WRLCK;
	arg.l_whence = arg.l_start = arg.l_len = arg.l_pid = 0;	
	retcode = fcntl ( fd, F_SETLK, &arg );
#else
	retcode = flock ( fd, LOCK_EX | LOCK_NB );
#endif
	
#ifdef HAVE_LOCKFILE
	retcode = ( lockfile_create ( g_strdup_printf ( "%s.lock", fn ), 1, L_PID ) == 0 ) ? 0 : -1;
#endif
	return retcode;
}

/**
 * p3l_unlock_fd: unlock a file
 * @fd: file descriptor
 *
 * Unlocks a file.
 *
 * Returns: 0 on success, -1 otherwise
 **/

int
p3l_unlock_fd ( int fd, char *fn )
{
	int retcode;
#ifdef HAVE_FCNTL_H
	struct flock arg;
#endif
	
#ifdef HAVE_LOCKFILE
	retcode = ( lockfile_remove ( g_strdup_printf ( "%s.lock", fn ) ) == 0 ) ? 0 : -1;
#endif

#ifdef HAVE_FCNTL_H
	arg.l_type = F_UNLCK;
	arg.l_whence = arg.l_start = arg.l_len = arg.l_pid = 0;	
	retcode = fcntl ( fd, F_SETLK, &arg );
#else
	retcode = flock( fd, LOCK_UN );
#endif
	return retcode;
}

/**
 * p3l_read_fd: read the contents of an opened file
 * @fd: file descriptor
 * @size: variable to put filesize into
 *
 * Reads the contents of an already opened file.
 *
 * Returns: the contents, free-able
 **/

gpointer
p3l_read_fd ( int fd, size_t *size )
{
	char *buffer, *mailbox=NULL;
	size_t mbsize=0;
	ssize_t r;

	buffer = (char *) g_malloc ( 10240 );
	while ( ( r = read ( fd, buffer, 10240 ) ) > 0 )
	{
		mailbox = (char *) g_realloc ( mailbox, mbsize + r );
		memcpy ( mailbox + mbsize, buffer, r );
		mbsize += r;
	}
	g_free ( buffer );

	if ( size != NULL )
		*size = mbsize;

	return (gpointer) mailbox;
}

/**
 * p3l_read_file: read the contents of a file
 * @fn: filename
 * @size: variable to put filesize into
 *
 * Opens the file and reads its contents.
 *
 * Returns: the contents, free-able
 **/

gpointer
p3l_read_file ( const char *filename, size_t *size )
{
	gpointer buffer;
	int fd;

	if ( ( fd = open ( filename, O_RDONLY ) ) < 0 )
		return NULL;

	buffer = p3l_read_fd ( fd, size );

	close ( fd );

	return buffer;
}

/**
 * p3l_get_peer_name: get the peer's hostname
 *
 * This one figures out the remote end's hostname.
 *
 * Returns: the hostname.
 **/

char *
p3l_get_peer_name ( void )
{
	struct sockaddr_in peer;
	socklen_t peersize = sizeof peer;
	char *hostname;
	struct hostent *host;

	if ( getpeername ( 0, (struct sockaddr *) &peer, &peersize ) < 0 )
		return NULL;
	if ( peer.sin_family != AF_INET )
		return NULL;

	hostname = inet_ntoa ( peer.sin_addr );

	host = gethostbyaddr ( (char *) &peer.sin_addr.s_addr,
			       sizeof peer.sin_addr.s_addr, AF_INET );
	if ( host != NULL )
		hostname = host->h_name;

	return hostname;
}

/**
 * p3l_is_numeric: check if a string represents a number
 * @str: the string to check
 *
 * Determines if a string represents a number or not.
 *
 * Returns: TRUE if it does, FALSE if not.
 **/

gboolean
p3l_is_numeric ( const char* str )
{
	int i, start = 0;

	if ( str == NULL )
		return FALSE;

	if ( str[0] == '+' || str [1] == '-' )
		start = 1;

	for ( i = start; i < strlen ( str ); i++ )
		if ( ! isdigit ( str[i] ) )
			return FALSE;

	return TRUE;
}

/**
 * p3l_register_alarm: register an alarm hook
 * @hook: the hook to run
 * @interval: the interval (in seconds) to wait between calls
 *
 * Installs a new hook to be run on SIGALRM.
 *
 * Returns: nothing
 **/

void
p3l_register_alarm ( p3l_module_hook hook, unsigned int interval )
{
	HandlerInfo *hinfo;
	unsigned int max_ticks;

	max_ticks = interval / SIGALRM_INTERVAL;

	hinfo = (HandlerInfo *) g_malloc ( sizeof ( HandlerInfo ) );
	hinfo->counter = 0;
	hinfo->tick_max = max_ticks;
	hinfo->hook = hook;

	alarm_handlers = g_list_append ( alarm_handlers, hinfo );
}

/**
 * p3l_get_alarm_hooks: returns the registered SIGALRM hooks
 *
 * This function returns a GList of HandlerInfo structs of the
 * registered SIGALRM hooks.
 *
 * Returns: the list
 **/

GList *
p3l_get_alarm_hooks ( void )
{
	return alarm_handlers;
}

/**
 * p3l_unregister_alarm: unregister an alarm hook
 * @hook: the hook to remove
 *
 * Removes an registered SIGALRM hook.
 *
 * Returns: nothing.
 **/

void
p3l_unregister_alarm ( p3l_module_hook hook )
{
	unsigned int i;
	HandlerInfo *hinfo;

	for ( i = 0; i < g_list_length ( alarm_handlers ); i++ )
	{
		hinfo = (HandlerInfo *) g_list_nth_data ( alarm_handlers, i );
		if ( hinfo->hook == hook )
			alarm_handlers = g_list_remove ( alarm_handlers, hinfo );
	}
}


syntax highlighted by Code2HTML, v. 0.9.1