/*
 *	SambaSentinel.c
 *	
 *	(C) 2001 Robert Kling
 *	robkli-8@student.luth.se
 *	http://boombox.campus.luth.se
 *	
 *  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.
 *
 * 
 *  See INSTALL file for instructions on compiling this program.
 */

#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include "gtk_common.h"

#include "pixmaps/network.xpm"
#include "pixmaps/doc.xpm"
#include "pixmaps/sound.xpm"
#include "pixmaps/search.xpm"
#include "pixmaps/exe.xpm"
#include "pixmaps/none.xpm"
#include "pixmaps/txt.xpm"
#include "pixmaps/offline.xpm"

#define LIVE_BUILD 1
#define DEBUG_PARSING 0

#define GREP_PROCS "smbstatus -S | grep -E \"\\([[:digit:]]{1,3}\\.[[:digit:]]{1,3}\\.\""
#define GREP_FILES "smbstatus -L | grep DENY"

#define VERSION "0.1"
#define REV_DATE "2001-02-26"
#define KILL_SIGNAL 9
#define MAX_PROCS 20
#define MAX_FILES 100
#define NOF_COLUMNS 7

/** GTK callback functions **/
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
gint kill_process(GtkMenuItem *item, gpointer *data);
gint add_ban_menu_item(GtkMenuItem *item, gpointer *data);
gint remove_ban_callback(GtkMenuItem *item, gpointer *data);
gint browse_with_jags(GtkMenuItem *item, gpointer *data);
gint about_dialog(GtkMenuItem *item, gpointer *data);
gint select_row(GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data);
gint hs_files(GtkWidget *widget, GdkEvent *event, gpointer data);

/** Other functions **/
void print_help_and_exit();
void setup_main_window();
void setup_popup_menu();
void select_row_with_pid(GtkWidget *widget, int pid);
int f_skip_forward(FILE *which_file, int nof_tokens);
void *status_thread(void *);

typedef struct {
	unsigned pid;
	char service[64];
	char machine_name[128];
	char machine_ip[20];
	char uid[64];
	char gid[64];
	int	 file_cnt;
	char *files[MAX_FILES];
} samba_proc;

char *titles[NOF_COLUMNS] = {"PID", "Computer", "IP", "UID", "GID", "Share", "Files"};
char cwidths[NOF_COLUMNS] = {  40 ,     75    ,  95 ,   65 ,   65 ,   55  ,    35   };
char *rowptr[NOF_COLUMNS];
char *file_title[1] = { "Files" };

pthread_t t_status_thread;
pthread_mutex_t mutex;

/** 
 ** The following variables MUST ONLY be accessed WITHIN a mutex
 ** lock or before the status thread is started.			
 **/
samba_proc *proc_list[MAX_PROCS];
char *ban_list[20];
int ban_cnt;
int proc_cnt, time_to_die;
int selected_pid, update_int = 5;
/**
 ** End of "mutex-variables".
 **/

int selected_row;

GtkWidget *window, *button, *hbox, *vbox, *clist_procs, *clist_files;
GtkWidget *statusbar, *scr_win_procs, *scr_win_files, *box;
GtkWidget *popup_menu, *ban_popup_menu, *kill_menu_item, *ban_menu_item, *menuitem;
GtkWidget *remove_ban_menu_item, *about_menu_item, *jags_menu_item;

GdkPixmap *proc_pixmap, *sound_pixmap, *file_pixmap;
GdkBitmap *proc_mask, *sound_mask, *file_mask;
GtkStyle *style;

/***
 *** Main
 ***/

int main(int argc, char **argv) {

	if (argc > 1) {
		update_int = atoi(argv[1]);
		if (update_int < 1) print_help_and_exit();
	}

	pthread_mutex_init(&mutex, NULL);
	g_thread_init(NULL);
	gtk_init(&argc, &argv);

	setup_main_window();
	setup_popup_menu();
 	
	/** Start t_status_thread that runs smbstatus **/
	pthread_create(&t_status_thread, NULL, (void *) status_thread, NULL);

	/** gtk_main is where the program will be until the user quits **/
	gdk_threads_enter();
    gtk_main();
    gdk_threads_leave();

	/** Tell t_status_thread that its time to quit and wait for it to do so **/
	pthread_mutex_lock(&mutex);
	time_to_die = 1;
	pthread_mutex_unlock(&mutex);
	
    printf("\nSAMBA_SENTINEL: Graciously waiting for t_status_thread to exit: "); fflush(stdout);
	pthread_join(t_status_thread, NULL);
	printf("done.\n");

	return 0;
}

/***
 *** Print help and exit (if faulty commandline parameter(s))
 ***/

void print_help_and_exit() {

	printf("\nSambaSentinel v%s (%s)\n\nUsage: SambaSentinel [seconds between updates]\n\n", VERSION, REV_DATE);
	exit(0);
}

/***
 *** Setup and draw the main window
 ***/ 

void setup_main_window() {

	GtkWidget *test;
	int i, j;

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  	gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
  	gtk_widget_set_usize( GTK_WIDGET (window), 500, 150);
  	gtk_window_set_title(GTK_WINDOW (window), "SambaSentinel v0.1");
  	gtk_container_set_border_width(GTK_CONTAINER (window), 3);

	/** Vertical Pack Box **/
  	vbox = gtk_vbox_new(FALSE, 1);
  	gtk_container_add(GTK_CONTAINER (window), vbox);
  	gtk_widget_show(vbox);

	/** Scrolled Window (for clist_procs) **/
  	scr_win_procs = gtk_scrolled_window_new(NULL, NULL);
  	gtk_container_set_border_width (GTK_CONTAINER (scr_win_procs), 0);
  	gtk_widget_set_usize(scr_win_procs, 100, 50);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scr_win_procs), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 	gtk_box_pack_start(GTK_BOX(vbox), scr_win_procs, TRUE, TRUE, 0);
 	gtk_widget_show(scr_win_procs);

	/** CList (for procs) **/
	clist_procs = gtk_clist_new_with_titles(NOF_COLUMNS, titles);
	gtk_container_set_border_width (GTK_CONTAINER (clist_procs), 0);
	gtk_clist_set_button_actions(GTK_CLIST(clist_procs), 2, 1);
	gtk_signal_connect(GTK_OBJECT(clist_procs), "select_row", GTK_SIGNAL_FUNC(select_row), NULL);
	gtk_signal_connect(GTK_OBJECT(clist_procs), "unselect_row", GTK_SIGNAL_FUNC(select_row), NULL);
	gtk_clist_column_titles_passive(GTK_CLIST(clist_procs));
	gtk_clist_set_row_height(GTK_CLIST(clist_procs), 14);
	gtk_clist_set_shadow_type(GTK_CLIST(clist_procs), GTK_SHADOW_IN);
	for (i = 0; i < NOF_COLUMNS; i++) {
		//gtk_clist_set_column_auto_resize(GTK_CLIST(clist_procs), i, TRUE);
		gtk_clist_set_column_width(GTK_CLIST(clist_procs), i, cwidths[i]);
	}
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scr_win_procs), clist_procs);
	gtk_widget_show(clist_procs);

	/** Scrolled Window (for clist_files) **/
	scr_win_files = gtk_scrolled_window_new(NULL, NULL);
  	gtk_container_set_border_width (GTK_CONTAINER (scr_win_files), 0);
  	gtk_widget_set_usize(scr_win_files, 100, 50);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scr_win_files), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 	gtk_box_pack_start(GTK_BOX(vbox), scr_win_files, TRUE, TRUE, 0);
 	gtk_widget_show(scr_win_files);

 	/** CList (for files) **/
	clist_files = gtk_clist_new_with_titles(1,file_title);
	gtk_clist_set_button_actions(GTK_CLIST(clist_files), 0, 0);
	gtk_clist_set_button_actions(GTK_CLIST(clist_files), 1, 0);
	gtk_clist_set_button_actions(GTK_CLIST(clist_files), 2, 0);
	gtk_container_set_border_width (GTK_CONTAINER (clist_files), 0);
	gtk_clist_column_titles_passive(GTK_CLIST(clist_files));
	gtk_clist_set_row_height(GTK_CLIST(clist_files), 14);
	gtk_clist_set_shadow_type(GTK_CLIST(clist_files), GTK_SHADOW_ETCHED_IN);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scr_win_files), clist_files);
	gtk_widget_show(clist_files);

	/** Horizontal Pack Box **/
  	hbox = gtk_hbox_new(TRUE, 1);
  	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  	gtk_widget_show(hbox);

  	/** Show/Hide files Button **/
	//button = gtk_button_new_with_label ("Show/Hide files");
	button = gtk_button_new_with_label_with_pixmap("Hide/Show files", doc_xpm);
  	gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (hs_files), (gpointer) "Show/Hide files");
  	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  	gtk_widget_set_usize(button, 100, 23);
  	gtk_widget_show(button);

  	/** Kill Button **/
	//button = gtk_button_new_with_label ("Kill process");
	button = gtk_button_new_with_label_with_pixmap("Kill process", exe_xpm);
  	gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (kill_process), NULL);
  	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  	gtk_widget_show(button);

 	/** Ban Button **/
	//button = gtk_button_new_with_label ("Ban user");
	button = gtk_button_new_with_label_with_pixmap("Ban user", exe_xpm);
  	//gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (delete_event), (gpointer) "Quit");
  	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  	gtk_widget_set_sensitive(button, FALSE);
  	gtk_widget_show(button);

  	/** Quit Button **/
  	//button = gtk_button_new_with_label ("Quit");
  	button = gtk_button_new_with_label_with_pixmap("Quit", offline_xpm);
  	gtk_signal_connect(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (delete_event), (gpointer) "Quit");
  	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  	gtk_widget_show(button);

	/** Statusbar **/
  	statusbar = gtk_statusbar_new();
  	gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
  	gtk_widget_set_usize(statusbar, 100, 20);
  	gtk_widget_show(statusbar);

	/** Pretty much done.. so just show everything. **/
  	gtk_widget_show(window);

	/** Create the pixmaps for computer and files **/
	style = gtk_widget_get_style( window );
  	//proc_pixmap = gdk_pixmap_create_from_xpm(window->window, &proc_mask, &proc_style->bg[GTK_STATE_NORMAL], "network.xpm" );
  	proc_pixmap = gdk_pixmap_create_from_xpm_d(window->window, &proc_mask, &style->bg[GTK_STATE_NORMAL], network_xpm );
  	//file_style = gtk_widget_get_style( window );
  	//file_pixmap = gdk_pixmap_create_from_xpm(window->window, &file_mask, &file_style->bg[GTK_STATE_NORMAL], "doc.xpm" );
  	file_pixmap = gdk_pixmap_create_from_xpm_d(window->window, &file_mask, &style->bg[GTK_STATE_NORMAL], doc_xpm );
  	//sound_style = gtk_widget_get_style( window );
  	//sound_pixmap = gdk_pixmap_create_from_xpm(window->window, &sound_mask, &sound_style->bg[GTK_STATE_NORMAL], "sound.xpm" );
  	sound_pixmap = gdk_pixmap_create_from_xpm_d(window->window, &sound_mask, &style->bg[GTK_STATE_NORMAL], sound_xpm );
}

/***
 *** Setup the right-mouse-button-clicked-on-process popup-menu
 ***/

void setup_popup_menu() {

	popup_menu = gtk_menu_new();
	ban_popup_menu = gtk_menu_new();

	menuitem = gtk_menu_item_new_with_label_with_pixmap("SambaSentinel", none_xpm);
  	gtk_widget_show (menuitem);
  	gtk_container_add (GTK_CONTAINER (popup_menu), menuitem);
  	gtk_widget_set_sensitive (menuitem, FALSE);

  	menuitem = gtk_menu_item_new();
  	gtk_widget_show (menuitem);
  	gtk_container_add (GTK_CONTAINER (popup_menu), menuitem);
  	gtk_widget_set_sensitive(menuitem, FALSE);
  	
	kill_menu_item = gtk_menu_item_new_with_label_with_pixmap("Kill process", exe_xpm);
	gtk_widget_show (kill_menu_item);
	gtk_container_add (GTK_CONTAINER(popup_menu), kill_menu_item);
	gtk_signal_connect (GTK_OBJECT(kill_menu_item), "activate", GTK_SIGNAL_FUNC (kill_process), NULL);
  	
	ban_menu_item = gtk_menu_item_new_with_label_with_pixmap("Ban computer", none_xpm);
	gtk_widget_show (ban_menu_item);
	gtk_container_add (GTK_CONTAINER (popup_menu), ban_menu_item);
	gtk_signal_connect(GTK_OBJECT(ban_menu_item), "activate", GTK_SIGNAL_FUNC (add_ban_menu_item), NULL);
	gtk_widget_set_sensitive(ban_menu_item, FALSE);

	//remove_ban_menu_item = gtk_menu_item_new_with_label("Un-ban computer");
	remove_ban_menu_item = gtk_menu_item_new_with_label_with_pixmap("Un-ban computer", none_xpm);
	gtk_widget_show (remove_ban_menu_item);
	gtk_container_add (GTK_CONTAINER (popup_menu), remove_ban_menu_item);
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(remove_ban_menu_item), ban_popup_menu);
	gtk_widget_set_sensitive(remove_ban_menu_item, FALSE);

	jags_menu_item = gtk_menu_item_new_with_label_with_pixmap("Browse with Jags", search_xpm);
	gtk_widget_show (jags_menu_item);
	gtk_container_add (GTK_CONTAINER (popup_menu), jags_menu_item);
	gtk_signal_connect (GTK_OBJECT(jags_menu_item), "activate", GTK_SIGNAL_FUNC (browse_with_jags), NULL);
	
	about_menu_item = gtk_menu_item_new_with_label_with_pixmap("About", txt_xpm);
	gtk_widget_show(about_menu_item);
	gtk_container_add(GTK_CONTAINER (popup_menu), about_menu_item);
	gtk_signal_connect(GTK_OBJECT(about_menu_item), "activate", GTK_SIGNAL_FUNC (about_dialog), NULL);
}

/***
 *** Main program quit callback
 ***/

gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {

	gtk_main_quit();
    return FALSE;
}

/***
 *** Add a computer to the ban-list and sub-popup-menu (not active yet)
 ***/

gint add_ban_menu_item(GtkMenuItem *item, gpointer *data) {

	GtkWidget *new_menu_item;
	char str[64];

	gtk_clist_remove(GTK_CLIST(clist_procs), selected_row);
	gtk_clist_clear(GTK_CLIST(clist_files));

	pthread_mutex_lock(&mutex);
	strncpy(str, proc_list[selected_row]->machine_name, 63);
	pthread_mutex_unlock(&mutex);

	ban_list[ban_cnt] = malloc(strlen(str)+1);
	strcpy(ban_list[ban_cnt], str);

	new_menu_item = gtk_menu_item_new_with_label(str);
		
	gtk_widget_show(new_menu_item);
	gtk_container_add(GTK_CONTAINER (ban_popup_menu), GTK_WIDGET(new_menu_item));
	gtk_signal_connect (GTK_OBJECT(new_menu_item), "activate", GTK_SIGNAL_FUNC(remove_ban_callback), ban_list[ban_cnt++]);

	return TRUE;
}

/***
 *** Remove a computer from the ban-list and sub-popup-menu (not active yet)
 ***/

gint remove_ban_callback(GtkMenuItem *item, gpointer *data) {

	int i;

	pthread_mutex_lock(&mutex);
	for (i = 0; i < ban_cnt; i++)
		if (strcmp(ban_list[i], (char *)data) == 0) {
			free(ban_list[i]);
			for (i; i < ban_cnt; i++) ban_list[i] = ban_list[i+1];
			ban_cnt--;
			break;
		}
			
	pthread_mutex_unlock(&mutex);
	gtk_widget_destroy(GTK_WIDGET(item));

	return TRUE;
}

/***
 *** Popup the about dialog-window
 ***/

gint about_dialog(GtkMenuItem *item, gpointer *data) {

	char str[356];

	sprintf(str,"SambaSentinel v%s\n\n"
				"Copyright (C) 2001 Robert Kling\n"
				"http://boombox.campus.luth.se", VERSION, REV_DATE);

	popup_dialog(130, FALSE, "About", str, "Close");

	return TRUE;
}

/***
 *** Browse with Jags callback, check for availibilty etc.
 ***/

gint browse_with_jags(GtkMenuItem *item, gpointer *data) {

	char str[128] = { "jags --help 1> /dev/null" };
	int sysret;

	sysret = system(str);

	switch (sysret) {

		case 0:
			pthread_mutex_unlock(&mutex);
			sprintf(str,"jags -c %s 1> /dev/null &", proc_list[selected_row]->machine_name);
			pthread_mutex_unlock(&mutex);
			system(str);
			
		break;

		case -1:
		popup_dialog(100, TRUE, "(SambaSentinel) Browse with Jags", 
					 	  		"An unknown error occured when trying to start Jags.", 
					 	  		"Close");
		break;

		default:
			popup_dialog(100, TRUE, "(SambaSentinel) Browse with Jags", 
					 	  			"There appear to be a problem starting Jags. Are you sure you have it installed?", 
					 	  			"Close");
		break;
	}

	return TRUE;
}

/***
 *** Kill process callback
 ***/

gint kill_process(GtkMenuItem *item, gpointer *data) {

	char str[128];
	int sysret;

	if (selected_pid != 0) {

		sysret = kill(selected_pid, KILL_SIGNAL);

		if (sysret) {

			switch (errno) {

				case EINVAL:
					sprintf(str,"ERROR: kill signal %d is not allowed on your system..",KILL_SIGNAL);
					popup_dialog(100, TRUE, "(SambaSentinel) Kill process", str, "Close");
					break;

				case ESRCH:
					sprintf(str,"ERROR: process %d does not exist.",selected_pid);
					popup_dialog(100, TRUE, "(SambaSentinel) Kill process", str, "Close");
					break;

				case EPERM:
					sprintf(str,"ERROR: You dont have permission to kill the selected process.");
					popup_dialog(100, TRUE, "(SambaSentinel) Kill process", str, "Close");
					break;
			}
		} else {
			gtk_clist_remove(GTK_CLIST(clist_procs), selected_row);
			gtk_clist_clear(GTK_CLIST(clist_files));
		}

	}
 	
  	return TRUE;
}

/***
 *** Select row callback (for processes)
 ***/

gint select_row(GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data) {

	char newtitle[64] = "File accesses by ";
	char *ptr[1];
	int j;

	pthread_mutex_lock(&mutex);
	gtk_clist_freeze(GTK_CLIST(clist_files));
	gtk_clist_clear(GTK_CLIST(clist_files));

	strncat(newtitle, proc_list[row]->machine_name, 42);
	gtk_clist_set_column_title( GTK_CLIST(clist_files), 0, newtitle);
	selected_pid = proc_list[row]->pid;
	selected_row = row;

	for (j = 0; j < proc_list[row]->file_cnt; j++) {
		ptr[0] = proc_list[row]->files[j];		
		gtk_clist_append(GTK_CLIST(clist_files), ptr);

		if ((char *)strstr(ptr[0], ".mp3") != NULL)
			gtk_clist_set_pixtext(GTK_CLIST(clist_files), j, 0, ptr[0], 5, sound_pixmap, sound_mask );
		else
			gtk_clist_set_pixtext(GTK_CLIST(clist_files), j, 0, ptr[0], 5, file_pixmap, file_mask );
	}		

	gtk_clist_thaw(GTK_CLIST(clist_files));
	pthread_mutex_unlock(&mutex);

	if ((event != NULL) && (event->button == 3)) {
		gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL, 0, event->time);
		// Does anybody know how to give the popup focus so that menuitems 'react' when the mouse is over them?
		//gtk_widget_grab_focus(GTK_WIDGET(popup_menu));
	}
		
    return TRUE;
}

/***
 *** Hide and show files callback
 ***/

gint hs_files(GtkWidget *widget, GdkEvent *event, gpointer data) {

	static int files_visible = 1;
	
    if (files_visible) {
    	gtk_widget_hide(scr_win_files);
    	files_visible = 0;
    }
    else {
    	gtk_widget_show(scr_win_files);
    	files_visible = 1;
    }

    return TRUE;
}

/***
 *** The status thread that continously runs smbstatus and updates the lists
 ***/

void *status_thread(void *arg) {

	int i, j, id;
	char slask[256], slask2[256], name[256];

	FILE *fp;

	while (1) {

#if LIVE_BUILD

		strcpy(slask2, GREP_PROCS);
		strcat(slask2, " >");
		strcpy(slask, getenv("HOME"));
		strcat(slask, "/.smbprocs.tmp");
		strcat(slask2, slask);
		system(slask2);
		if ((fp = fopen(slask,"r")) == NULL) exit(0);	

#else
		if ((fp = fopen("procs.txt","r")) == NULL) exit(0);
#endif
		
		proc_cnt = 0;
		pthread_mutex_lock(&mutex);

		while (!feof(fp) && (proc_cnt < MAX_PROCS)) {

			if (proc_list[proc_cnt] != NULL) { 
				for (i = 0; i < proc_list[proc_cnt]->file_cnt; i++) free(proc_list[proc_cnt]->files[i]);
				free(proc_list[proc_cnt]);
			}
				
			proc_list[proc_cnt] = (samba_proc *)calloc(1,sizeof(samba_proc));
			proc_list[proc_cnt]->file_cnt = 0;

			fscanf(fp,"%s",slask); if (feof(fp)) break;
			strncpy(proc_list[proc_cnt]->service, slask, 64);
			//printf("\n%s",slask);
			fscanf(fp,"%s",slask);
			strncpy(proc_list[proc_cnt]->uid, slask, 64);
			//printf("\n%s",slask);
			fscanf(fp,"%s",slask);
			strncpy(proc_list[proc_cnt]->gid, slask, 64);
			//printf("\n%s",slask);
			fscanf(fp,"%s",slask);
			proc_list[proc_cnt]->pid = atoi(slask);
			//printf("\n%s",slask);
			fscanf(fp,"%s",slask);
			strncpy(proc_list[proc_cnt]->machine_name, slask, 128);
			//printf("\n%s",slask);
			fscanf(fp,"%s",slask);
			strncpy(proc_list[proc_cnt]->machine_ip, slask+1, 20);
			proc_list[proc_cnt]->machine_ip[strlen(proc_list[proc_cnt]->machine_ip)-1] = 0;
			//printf("\n%s",slask);

			f_skip_forward(fp,5);
			proc_cnt++;
		}

		fclose(fp);

#if LIVE_BUILD
		strcpy(slask2, GREP_FILES);
		strcat(slask2, " >");
		//strcpy(slask2, "smbstatus -L | grep DENY > ");
		strcpy(slask, getenv("HOME"));
		strcat(slask, "/.smbfiles.tmp");
		strcat(slask2, slask);
		system(slask2);
		if ((fp = fopen(slask,"r")) == NULL) exit(0);
#else	
		if ((fp = fopen("files.txt","r")) == NULL) exit(0);
#endif
		while (!feof(fp)) {
		
			fscanf(fp,"%s",slask);
			//printf("\n%s",slask);
			
			for (i = 0; i < proc_cnt; i++)
				if (atoi(slask) == proc_list[i]->pid) break;
			
			for (j = 1; j < 4; j++) { 
				fscanf(fp,"%s",slask); 
				//printf("\nj = %d %s",j,slask);
			}

			fscanf(fp,"%s",name);
			fgets(slask, 255, fp);
			strcat(name,slask);
			name[strlen(name)-1] = 0;
 			//printf("\n%s",name);

			if ((i < proc_cnt) && (proc_list[i]->file_cnt < MAX_FILES)) {

				proc_list[i]->files[proc_list[i]->file_cnt] = (char *)malloc(strlen(name)+1);
				strcpy(proc_list[i]->files[proc_list[i]->file_cnt], name);
				proc_list[i]->file_cnt++;
			}	
		}

		fclose(fp);

		gdk_threads_enter();
		id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "");
		gtk_statusbar_push(GTK_STATUSBAR(statusbar), id, "Updating list.." );
		gtk_clist_freeze(GTK_CLIST(clist_procs));                 
		gtk_clist_clear(GTK_CLIST(clist_procs));

		for (i = 0; i < proc_cnt; i++) {

#if DEBUG_PARSING			
			printf("\n\nService: %s",proc_list[i]->service);
			printf("\nPID    : %d",proc_list[i]->pid);
			printf("\nMachine: %s",proc_list[i]->machine_name);
			printf("\nIP     : %s",proc_list[i]->machine_ip);
			printf("\nFiles  :\n");
			for (j = 0; j < proc_list[i]->file_cnt; j++) printf("%s\n",proc_list[i]->files[j]);
#endif			
			
			sprintf(slask,"%d",proc_list[i]->pid);
			rowptr[0] = slask;
			rowptr[1] = proc_list[i]->machine_name;
			rowptr[2] = proc_list[i]->machine_ip;
			rowptr[3] = proc_list[i]->uid;
			rowptr[4] = proc_list[i]->gid;
			rowptr[5] = proc_list[i]->service;
			sprintf(slask2,"%d",proc_list[i]->file_cnt);
			rowptr[6] = slask2;
			
			gtk_clist_append(GTK_CLIST(clist_procs), rowptr);
			gtk_clist_set_pixtext(GTK_CLIST(clist_procs), i, 1, rowptr[1], 5, proc_pixmap, proc_mask );
		}

		gtk_clist_thaw(GTK_CLIST(clist_procs));
		
		for (i = 0; i < proc_cnt; i++)
			if (proc_list[i]->pid == selected_pid) {
				pthread_mutex_unlock(&mutex);
				select_row_with_pid(clist_procs, selected_pid);
				break;
			}
			else if (i == proc_cnt-1) selected_pid = 0;	

		gdk_threads_leave();
		pthread_mutex_unlock(&mutex);

		for (i = 0; i < update_int; i++) {
			usleep(1000000);
			if (i == 0) gtk_statusbar_pop(GTK_STATUSBAR(statusbar), id);
			pthread_mutex_lock(&mutex);
			if (time_to_die) {
				pthread_mutex_unlock(&mutex);
				pthread_exit(NULL);
			} else
				pthread_mutex_unlock(&mutex);
		}

	} // end of while
}

/***
 *** A function to select the process with a given pid (called after update)
 ***/

void select_row_with_pid(GtkWidget *widget, int pid) {

	char *test;
	int i = 0, j;

	for (i = 0; i < proc_cnt; i++) {
		gtk_clist_get_text( GTK_CLIST(widget), i, 0, &test);
		sscanf(test, "%d", &j);
		if (j == pid) {
			gtk_clist_select_row(GTK_CLIST(clist_procs), i, 0);
			break;
		}
	}
}

/***
 *** Skip nof_tokes in a file
 ***/ 

int f_skip_forward(FILE *which_file, int nof_tokens) {

	int i = 0;
	char slask[128];

	while (!feof(which_file) && (i++ < nof_tokens))	fscanf(which_file,"%s",slask);
		
	if (i == nof_tokens) return 1; else	return 0;
}

syntax highlighted by Code2HTML, v. 0.9.1