/*
 * GProFTPD - A GTK+ frontend for the ProFTPD standalone server.
 * Copyright (C) 2001 - 2006 Magnus Loef (Magnus-swe) <magnus-swe@telia.com>
 *
 * 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.
 *
 * This program 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.
 *
 */




#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "widgets.h"
#include "gettext.h"
#include "functions.h"
#include "reread_conf.h"
#include "show_info.h"
#include "apply_user.h"
#include "allocate.h"
#include "populate_users.h"
#include "populate_user_settings.h"
#include "populate_conf_tab.h"


extern char global_server_address[1024];
extern char global_server_port[1024];
extern char global_server_type[1024];

extern int activated;

/* The current directory beeing added and limited */
gchar *dir;

/* The 18 checkbox values */
gchar *dir_val[19];

char *user_profile;
long num_rows = 0;
int row_pos = 0;



/* Get the number of rows in the treeview */
gboolean num_rows_func(GtkTreeModel *model, GtkTreePath *path,
		      GtkTreeIter *iter, struct w *widgets)
{
    num_rows++;
    return FALSE;
}



void append_limit_cmds()
{
    int i, has_limit = 0;

    /* Overwrite selected, add "Allowoverwrite on" before the directory limits */    
    if( dir_val[6] )
      strcat(user_profile, "AllowOverwrite on\n");
    

    strcat(user_profile, "<Limit");

    /* ALLOW CMDs. Iter the 18 checkboxes */
    for(i=1; i<19; i++)
    {
	/* The Box is checked, add to Limit allow */
	if( i == 1 && dir_val[i])
	{
	    strcat(user_profile, " LIST NLST ");
	    has_limit = 1;
	}
	
	if( i == 2 && dir_val[i])
	{
	    strcat(user_profile, " STOR STOU ");
	    has_limit = 1;
	}

	if( i == 3 && dir_val[i])
	{
	    strcat(user_profile, " APPE ");
	    has_limit = 1;
	}

	if( i == 4 && dir_val[i])
	{
	    strcat(user_profile, " RETR ");
	    has_limit = 1;
	}

	if( i == 5 && dir_val[i])
	{
	    strcat(user_profile, " RNFR RNTO ");
	    has_limit = 1;
	}

	/* 6 is AllowOverwrite, its handled before this function is called */

	if( i == 7 && dir_val[i])
	{
	    strcat(user_profile, " DELE ");
	    has_limit = 1;
	}

	if( i == 8 && dir_val[i])
	{
	    strcat(user_profile, " MKD XMKD SITE_MKDIR ");
	    has_limit = 1;
	}

	if( i == 9 && dir_val[i])
	{
	    strcat(user_profile, " RMD XRMD SITE_RMDIR ");
	    has_limit = 1;
	}

	if( i == 10 && dir_val[i])
	{
	    strcat(user_profile, " SITE ");
	    has_limit = 1;
	}

	if( i == 11 && dir_val[i])
	{
	    strcat(user_profile, " SITE_CHMOD ");
	    has_limit = 1;
	}

	if( i == 12 && dir_val[i])
	{
	    strcat(user_profile, " SITE_CHGRP ");
	    has_limit = 1;
	}

	/* Show modification dates "show time" */
	if( i == 13 && dir_val[i])
	{
	    strcat(user_profile, " MTDM ");
	    has_limit = 1;
	}

	/* Print working directory "show working directory" */
	if( i == 14 && dir_val[i])
	{
	    strcat(user_profile, " PWD XPWD ");
	    has_limit = 1;
	}
	
	/* Show sizes */
	if( i == 15 && dir_val[i])
	{
	    strcat(user_profile, " SIZE ");
	    has_limit = 1;
	}

	/* Stat */
	if( i == 16 && dir_val[i])
	{
	    strcat(user_profile, " STAT ");
	    has_limit = 1;
	}

	/* Change working directory */
	if( i == 17 && dir_val[i])
	{
	    strcat(user_profile, " CWD XCWD ");
	    has_limit = 1;
	}

	/* CD up */
	if( i == 18 && dir_val[i])
	{
	    strcat(user_profile, " CDUP XCUP ");
	    has_limit = 1;
	}
    }

    /* If theres no limit the server wont start or go off line */
    if( ! has_limit )
      strcat(user_profile, " NOTHING ");

    strcat(user_profile, ">\n AllowAll\n</Limit>\n");



    has_limit = 0;
    strcat(user_profile, "<Limit");

    /* DENY CMDs. Iter the 18 checkboxes */
    for(i=1; i<19; i++)
    {
	/* The Box is not checked, add to Limit deny */
	if( i == 1 && ! dir_val[i])
	{
	    strcat(user_profile, " LIST NLST ");
	    has_limit = 1;
	}

	if( i == 2 && ! dir_val[i])
	{
	    strcat(user_profile, " STOR STOU ");
	    has_limit = 1;
	}

	if( i == 3 && ! dir_val[i])
	{
	    strcat(user_profile, " APPE ");
	    has_limit = 1;
	}

	if( i == 4 && ! dir_val[i])
	{
	    strcat(user_profile, " RETR ");
	    has_limit = 1;
	}

	if( i == 5 && ! dir_val[i])
	{
	    strcat(user_profile, " RNFR RNTO ");
	    has_limit = 1;
	}

	/* 6 is AllowOverwrite, its handled before this function is called */

	if( i == 7 && ! dir_val[i])
	{
	    strcat(user_profile, " DELE ");
	    has_limit = 1;
	}

	if( i == 8 && ! dir_val[i])
	{
	    strcat(user_profile, " MKD XMKD SITE_MKDIR ");
	    has_limit = 1;
	}

	if( i == 9 && ! dir_val[i])
	{
	    strcat(user_profile, " RMD XRMD SITE_RMDIR ");
	    has_limit = 1;
	}

	if( i == 10 && ! dir_val[i])
	{
	    strcat(user_profile, " SITE ");
	    has_limit = 1;
	}

	if( i == 11 && ! dir_val[i])
	{
	    strcat(user_profile, " SITE_CHMOD ");
	    has_limit = 1;
	}

	if( i == 12 && ! dir_val[i])
	{
	    strcat(user_profile, " SITE_CHGRP ");
	    has_limit = 1;
	}

	/* Show modification dates "show time" */
	if( i == 13 && ! dir_val[i])
	{
	    strcat(user_profile, " MTDM ");
	    has_limit = 1;
	}

	/* Print working directory "show working directory" */
	if( i == 14 && ! dir_val[i])
	{
	    strcat(user_profile, " PWD XPWD ");
	    has_limit = 1;
	}
	
	/* Show sizes */
	if( i == 15 && ! dir_val[i])
	{
	    strcat(user_profile, " SIZE ");
	    has_limit = 1;
	}

	/* Stat */
	if( i == 16 && ! dir_val[i])
	{
	    strcat(user_profile, " STAT ");
	    has_limit = 1;
	}

	/* Change working directory */
	if( i == 17 && ! dir_val[i])
	{
	    strcat(user_profile, " CWD XCWD ");
	    has_limit = 1;
	}

	/* CD up */
	if( i == 18 && ! dir_val[i])
	{
	    strcat(user_profile, " CDUP XCUP ");
	    has_limit = 1;
	}
    }

    /* If theres no limit the server wont start or go off line */
    if( ! has_limit )
      strcat(user_profile, " NOTHING ");


    strcat(user_profile, ">\n DenyAll\n</Limit>\n");
}




/* For each in the directory treeview */
gboolean dirs_foreach(GtkTreeModel *model, GtkTreePath *path,
		      GtkTreeIter *iter, struct w *widgets)
{
    int i;

    /* Iter the 18 checkboxes on each row and save the values */
    for(i=1; i<19; i++)
    {
	gtk_tree_model_get(model, iter, i, &dir_val[i], -1);
    }

    /* This is the first toplevel home directory */
    if( row_pos == 0 )
    {
	gtk_tree_model_get(model, iter, 0, &dir, -1);    
	row_pos++;

	/* Need write: 1 2  4 5 6 7 8  10 */
	if( dir_val[1] || dir_val[2] || dir_val[4] ||  dir_val[5]
	||  dir_val[6] || dir_val[7] || dir_val[8] || dir_val[10] )
          make_dir_chmod(dir, "0777"); /* Does mkdir -p, no need to check if it exists */
	else
	  make_dir_chmod(dir, "0777"); /* 0755 Noone else can write either... limited by the server. */

	/* Return true so it stops here this time */
	return TRUE;
    }

    row_pos++;

    /* Ignore the first/home dir */
    if( row_pos > 2 )
    {
	/* Add the rest of the users directories */
	gtk_tree_model_get(model, iter, 0, &dir, -1);

	/* Need write: 1 2  4 5 6 7 8  10 */
	if( dir_val[1] || dir_val[2] || dir_val[4] ||  dir_val[5]
	||  dir_val[6] || dir_val[7] || dir_val[8] || dir_val[10] )
          make_dir_chmod(dir, "0777"); /* Does mkdir -p, no need to check if it exists */
	else
	  make_dir_chmod(dir, "0777"); /* 0755 Noone else can write either... limited by the server. */

    
	/* A directory, not the users home */
	strcat(user_profile, "<Directory ");
	strcat(user_profile, dir);
	strcat(user_profile, ">\n");

	append_limit_cmds();

	strcat(user_profile, "</Directory>\n");

	g_free(dir);
    }
    
    /* Return false to keep foreach func going */
    return FALSE;
}						   



/* Fixme mod: group, comment and shell */
void apply_user(struct w *widgets)
{
    /* Change the selected users configuration. */
    /* First delete the user from the conf then add it */
    FILE *fp;
    long conf_size;
    int password_length = 0;
    gchar *info;
    char *old_buffer, *new_buffer, *config, *address_buffer, *port_buffer;
    long profile_size = 0;
    int user_changed = 0;
    int found = 0;
    gchar *UserUsername;
    char *temp_user;

    G_CONST_RETURN gchar *username;
    G_CONST_RETURN gchar *groupname;
    G_CONST_RETURN gchar *comment;
    G_CONST_RETURN gchar *password;
    G_CONST_RETURN gchar *login_from;
    G_CONST_RETURN gchar *max_logins;

    /* Get the settings from the entries */
    username = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_entry[0]));
    password = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_entry[1]));
    groupname = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_entry[2]));
    comment = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_entry[3]));
    login_from = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_entry[5]));
    max_logins = gtk_entry_get_text(GTK_ENTRY(widgets->user_set_spinbutton[0]));

    if( password!=NULL )
      password_length = strlen(password);

    /* If the user has written a password thats less then MIN_PASS_LEN chars long */
    if( password_length > 0 && password_length < MIN_PASS_LEN )
    {
        info = g_strdup_printf(_("The minimum password length is %d.\n"), MIN_PASS_LEN);
	show_info(info);
	g_free(info);
        return;
    }



    /* Set global num_rows */
    num_rows = 0;
    gtk_tree_model_foreach(GTK_TREE_MODEL(widgets->directory_store), (GtkTreeModelForeachFunc) num_rows_func, widgets);

    /* (Global char) Statics + entries + number of rows (dirlen + APPE STOR STOU etc) */
    profile_size = 16384 + 1400 + (num_rows * 16384);
    /* Allocate the user profile */
    user_profile = allocate(profile_size);


    /* Set the users home dir and its values globally */
    row_pos = 0; /* Only get the home directory */
    gtk_tree_model_foreach(GTK_TREE_MODEL(widgets->directory_store), (GtkTreeModelForeachFunc) dirs_foreach, widgets);

    /* Create the user profile... */

    /* Home directory */
    snprintf(user_profile, 16384, "\n<Anonymous %s>\n", dir);

    /* Free dir after foreach (itered once) */
    g_free(dir);


    /* Username */    
    strcat(user_profile, "User ");
    strcat(user_profile, username);
    strcat(user_profile, "\n");

    /* Groupname */
    strcat(user_profile, "Group ");
    strcat(user_profile, groupname);
    strcat(user_profile, "\n");

    /* Require password */
    if( ! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->user_set_checkbutton[1])) )
      strcat(user_profile, "AnonRequirePassword off\n");
    else
      strcat(user_profile, "AnonRequirePassword on\n");

    
    /* Maximum logins */
    strcat(user_profile, "MaxClients ");
    strcat(user_profile, max_logins);
    strcat(user_profile, " \"");
    strcat(user_profile, "The server is full, hosting %m users");
    strcat(user_profile, "\"\n");

    /* Static Welcome and chdir msgs */
    strcat(user_profile, "DisplayLogin welcome.msg\n");
    strcat(user_profile, "DisplayFirstChdir .msg\n");

    /* Logins from, if nothing is specified we allow everything */
    if( login_from == NULL || strlen(login_from) < 3 )
    {
	strcat(user_profile, "<Limit LOGIN>\n");
	strcat(user_profile, "Allow from all\nDeny from all\n");
	strcat(user_profile, "</Limit>\n");
    }
    else
      {
	 strcat(user_profile, "<Limit LOGIN>\n");
	 strcat(user_profile, "Allow from ");
	 strcat(user_profile, login_from);
	 strcat(user_profile, "\nDeny from all\n");
	 strcat(user_profile, "</Limit>\n");
      }


    /* Append the Limit CMDs for the home directory */
    append_limit_cmds();

    /* Append all other directories to the user profile */
    row_pos = 1; 
    gtk_tree_model_foreach(GTK_TREE_MODEL(widgets->directory_store), (GtkTreeModelForeachFunc) dirs_foreach, widgets);
    

    /* If not checked, dont show this user in the statistics (picked up by gprostats) */
    if( ! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->user_set_checkbutton[2])) )
      strcat(user_profile, "#gplockstats\n");

    /* Profile end */
    strcat(user_profile, "</Anonymous>\n");


    /* We have the new profile, insert it where the old user was located in the selected server */
    UserUsername = g_strdup_printf("User %s\n", username);

    address_buffer = allocate(8192+15);
    port_buffer = allocate(8192+3);               

    if( strstr((char *)global_server_type, "Virtualhost") )
       sprintf(address_buffer, "<VirtualHost %s>\n", global_server_address);
    else
       sprintf(address_buffer, global_server_address);
     
    sprintf(port_buffer, "Port %s\n", global_server_port);


    /* Make changes for this users settings */
    if((fp=fopen(PROFTPD_CONF, "r"))==NULL)
    {
	free(address_buffer); free(port_buffer);
        return;
    }
    fseek(fp, 0, SEEK_END);
    conf_size = ftell(fp);
    rewind(fp);

    old_buffer = allocate(conf_size+3);
    config = allocate(conf_size+profile_size+3);


    /* If the selected server is a vhost we collect everything up to
       the first vhost with the same name as the selected one */
    if( strstr((char *)global_server_type, "Virtualhost") )
    {
	if( conf_size > 1 )
	while(fgets(old_buffer, conf_size, fp)!=NULL)
	{
	    strcat(config, old_buffer);
	    
	    /* If its the correct address */
	    if( ! strcmp(old_buffer, address_buffer) )
	    {
	       while(fgets(old_buffer, conf_size, fp)!=NULL)
	       {
		  strcat(config, old_buffer);
		 
		  /* This server was not the right one */
		  if( strstr(old_buffer, "</VirtualHost>") )
		    break;
		 
		  if( ! strcmp(old_buffer, port_buffer) )
		  {
		     found = 1;
	             break;
	          }
	       }
	    }
	    if( found )
	      break;
	}
    }


    /* We have scrolled to the correct VirtualHost or its the default server */

    new_buffer = allocate(8192);

    /* Change the selected users settings */
    if( conf_size > 1 )
    while(fgets(old_buffer, conf_size, fp)!=NULL)
    {
	if( strstr(old_buffer, "<Anonymous ") )
	{
	    /* Save this directory incase its not the correct user */
	    strcat(new_buffer, old_buffer);

	    while(fgets(old_buffer, conf_size, fp)!=NULL)
	    {
		/* This is the correct user in the correct server */
        	if( strstr(old_buffer, UserUsername) 
		&& ! strstr(old_buffer, "AllowUser") 
		&& ! strstr(old_buffer, "DirFakeUser") && ! user_changed )
        	{
		    /* The new profile is added */
		    strcat(config, user_profile);
		    new_buffer[0]='\0';
		    user_changed = 1;

		    /* The old profile is deleted */
		    while(fgets(old_buffer, conf_size, fp)!=NULL)
		    {
			if( strstr(old_buffer, "</Anonymous") )
			  break;

			/* Some more safety breaks */
			if( strstr(old_buffer, "<Anonymous") )
			  break;

			if( strstr(old_buffer, "</VirutalHost") )
			  break;

			if( strstr(old_buffer, "<VirutalHost") )
			  break;
		    }
		}
		else
		  {
		     if( strlen(new_buffer) > 0 )
		     {
		        strcat(config, new_buffer);
		        new_buffer[0]='\0';
		     }
		     strcat(config, old_buffer);
		     break;
		  }
	    }
	}
	else
	  strcat(config, old_buffer);
    }
    free(old_buffer);
    free(new_buffer);
    g_free(UserUsername);
    fclose(fp);


    free(address_buffer); free(port_buffer);
    free(user_profile);


    if( ! user_changed )
    {
	info = g_strdup_printf(_("Error: user not found, couldnt change its settings.\n"));
	show_info(info);
	g_free(info);
	free(config);
        return;
    }


    /* Write the new config with the changed users settings */
    if((fp=fopen(PROFTPD_CONF, "w+"))==NULL) 
    {
	free(config);
        return;
    }
    fputs(config, fp);
    fclose(fp);
    free(config);



    /* Ban or unban the user in ftpusers depending on the selection */
    if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->user_set_checkbutton[0])) ) 
    {
	/* Ban the user */
	if( ! is_banned((char *)username) )
	{
	    if((fp=fopen(GP_FTPUSERS, "a"))==NULL) 
	    {
	    }
	    else
	      {
		 fputs("\n", fp);
		 fputs(username, fp);
		 fputs("\n", fp);
		 fclose(fp);
	      }
	}
    }
    else
      {
         /* Remove the user from ftpusers if its there */
         if( is_banned((char *)username) )
	 {
	    if((fp=fopen(GP_FTPUSERS, "r"))==NULL) 
	    {
	    }
	    else
	      {
	         fseek(fp, 0, SEEK_END);
	         conf_size = ftell(fp);
	         rewind(fp);
		 
	         config = allocate(conf_size+4096);        
	         old_buffer = allocate(conf_size+4096);        
		 temp_user = allocate(8192);
		 
		 if( conf_size > 1 )
		 while(fgets(old_buffer, conf_size, fp)!=NULL)
		 {
		    sscanf(old_buffer, "%s", temp_user);
		    if( ! strcmp(temp_user, username) )
		    {
		    }
		    else
	    	      strcat(config, old_buffer);
		 }			
		 fclose(fp);
		 free(old_buffer);
		 free(temp_user);

		 if((fp=fopen(GP_FTPUSERS, "w+"))==NULL) 
		 {
		 }
		 else
	           {
		      fputs(config, fp);
		      fclose(fp);
		   }
		 free(config);
	      }
	 }
    }




    /* Change the users password if its >= MIN_PASS_LEN */
    if( password_length >= MIN_PASS_LEN )
    {

#ifndef USE_DARWIN
	if( user_exists(username) )
#elif USE_DARWIN
	if( niutil_user_exists(username) )
#endif
	{
#ifndef USE_DARWIN
	    password_user(username, password);
#elif USE_DARWIN
	    niutil_password_user(username, password);
#endif
    	    info = g_strdup_printf(_("\nThe Password was changed.\n"));
    	    show_info(info);
    	    g_free(info);
	}
	else
	    {
		info = g_strdup_printf(_("The password was not changed.\nThe user didnt exist in shadow or passwd.\n"));
        	show_info(info);
        	g_free(info);
	    }
    }


    fix_newlines_in_conf();

    /* Update the userlist */
    populate_users(widgets);

    /* Populate the user settings (it also populates the directories) */
    populate_user_settings(widgets);

    /* Populate the conf tab */    
    populate_conf_tab(widgets);
    
    /* Update the server */
    reread_conf(widgets);
}
