#include "captive.h"



#define ACTION_ADD 1
#define ACTION_REMOVE 2
#define ACTION_MODIFY 3
#define ACTION_LOGOFF 4
#define ACTION_INFORMATION 5

#define MSG_OK 1
#define MSG_ERR 2
#define MSG_WARN 3
#define MSG_TXT 4

void printmsg( char *buffer, int flag ) {

	if( flag == MSG_OK ) printf( "<li> <font color=\"#00aa00\">%s</font>", buffer );
	else if( flag == MSG_ERR ) printf( "<li> <font color=\"#aa0000\">%s</font>", buffer );
	else if( flag == MSG_WARN ) printf( "<li> <font color=\"#0000aa\">%s</font>", buffer );
	else if( flag == MSG_TXT ) printf( "<li> %s", buffer );
	else printf( "(bad flag to printmsg)" );
}



int get_post_data( char *buffer, char p_var[20][50], char p_val[20][50] ) {

	int i, n = 0;
	char *ptr;

	for( i = 0; i < strlen(buffer); i++ ) {
		if( !isalnum(buffer[i]) && (buffer[i] != '=') && (buffer[i] != '&') && (buffer[i] != '.') ) return -1;

	}

	ptr = buffer;

	while( 1 ) {
		i = 0;
		/* Get variable name: */
		while( ((*ptr == '.') || isalnum(*ptr)) && (i < 50) ) {
			p_var[n][i] = *ptr;
			ptr++; i++;
		} 
		if( *ptr != '=' ) return -1;
		p_var[n][i] = '\0';
		ptr++;

		/* get variable value: */
		i = 0;
		while( ((*ptr == '.') || (isalnum(*ptr)) || (*ptr == '.')) && (i < 50) ) {      /* decimals OK, o/w alphanumeric */
			p_val[n][i] = *ptr;
			ptr++; i++;
		}
		if( (*ptr != '&') && (*ptr != '\0') ) return -1;
		p_val[n][i] = '\0';
		n++;
		if( *ptr == '\0' ) break;
		ptr++;

	}

	return n;
}




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

	GDBM_FILE dbf = NULL;
	datum key, nextkey;
	datum content;

	int action;
	char fn[100];
	char post_data[1000];
	char param_var[20][50];
	char param_val[20][50];
	int num_param;
	char list_of_users[50][USERNAME_LEN];
	int n_users;
	char msgbuffer[100];

	char      edit_username[USERNAME_LEN];
	userinfo  edit_userinfo;


	int count, found;
	int i, done, retval;
	time_t tp;

	int hasupper, haslower, hasnum;

	char     user_name[1000][USERNAME_LEN];
	userinfo user_info[1000];
	char     temp[USERNAME_LEN];

	time( &tp );

	printf( "Content-type: text/html\r\n\r\n" );


	printf( "<html><head><title>Captive Portal - SmoothWall Express</title></head>\n" );
	printf( "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#000000\" vlink=\"#000000\" alink=\"ff0000\"><center>\n" );


	printf( "<table border=\"0\" cellspacing=\"0\" cellpadding=\"4\">\n" );

	printf( "  <tr>\n" );
	printf( "    <td colspan=\"2\" align=\"center\">\n" );
	printf( "      <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\"><tr><td valign=\"top\" bgcolor=\"#ffffff\" width=\"100\"><img src=\"/ui/assets/3.6/img/pagetitles/page-remote.png\" width=\"100\"></td><td valign=\"top\" align=\"left\" bgcolor=\"#ffffff\" width=\"100%%\"><font face=\"arial\" size=\"-1\"><font size=\"+1\">Captive Portal</font><br>Restrict access to the internet to authenticated users.</font></td></tr></table>\n" );
	printf( "    </td>\n" );
	printf( "  </tr>\n" );






	/* Start handling data submitted by the forms on the web GUI.  */

	post_data[0] = '\0';
	fgets( post_data, 1000, stdin );
	if( post_data[0] != '\0' ) {

/*
		printf( "%s<br><br>\n", post_data );
*/

		num_param = get_post_data( post_data, param_var, param_val ); 

/*
		printf( "num_param = %d<br>\n", num_param );
		for( i = 0; i < num_param; i++ )
			printf( "|%s| --- |%s|<br>\n", param_var[i], param_val[i] );
*/

		printf( "  <tr bgcolor=\"#ffcc66\">\n" );
		printf( "    <td colspan=\"2\" align=\"left\"><font face=\"arial\">\n" );
		printf( "      Messages:<br>\n" );
		printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/black.gif\" width=\"100%%\" height=\"1\" border=\"0\"><br>\n" );
		printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/clear.gif\" width=\"100%%\" height=\"5\" border=\"0\"><br>\n" );
		printf( "      <font face=\"arial\" size=\"-1\">\n" );


		/* get action type ... */

		for( i = 0; i < num_param; i++ ) {
			if( strncmp(param_var[i],"action",6) == 0 ) {
				if( strncmp(param_val[i],"add",3) == 0 ) action = ACTION_ADD;
				else if( strncmp(param_val[i],"remove",6) == 0 ) action = ACTION_REMOVE;
				else if( strncmp(param_val[i],"modify",6) == 0 ) action = ACTION_MODIFY;
				else if( strncmp(param_val[i],"logoff",6) == 0 ) action = ACTION_LOGOFF;
				else if( strncmp(param_val[i],"information",11) == 0 ) action = ACTION_INFORMATION;
				else {
					printf( "Bad form data.\n" ); exit(0);
				}

			}
		}

		if( (action == ACTION_ADD) || (action == ACTION_MODIFY) ) {
			dbf = gdbm_open( "/var/smoothwall/mods/captive/captivedb", 512, GDBM_WRCREAT, S_IRWXU, NULL );

			found = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_var[i],"username",8) == 0 ) {
					strncpy( edit_username, param_val[i], USERNAME_LEN );
					found = 1;
					break;
				}
			}
			if( !found ) { printf( "Bad form data (no username field).\n" ); exit(0); }

			if( action == ACTION_ADD ) printf( "Attempting to add new user %s ...<br>\n", edit_username );
			else printf( "Attempting to modify user \"%s\" ...<br>\n", edit_username );

			found = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_var[i],"password",8) == 0 ) {
					strncpy( edit_userinfo.password, param_val[i], PASSWORD_LEN );
					found = 1;
					break;
				}
			}
			if( !found ) { printf( "Bad form data (no password field).\n" ); exit(0); }
			
			found = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_var[i],"state",5) == 0 ) {
					if( strncmp(param_val[i],"enab",4) == 0 ) edit_userinfo.enabled = 1;
					else if( strncmp(param_val[i],"disa",4) == 0 ) edit_userinfo.enabled = 0;
					else if( strncmp(param_val[i],"onet",4) == 0 ) edit_userinfo.enabled = 2;
					found = 1;
					break;
				}
			}
			if( !found ) { printf( "Bad form data (no state field).\n" ); exit(0); }
			
			found = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_var[i],"logindur",8) == 0 ) {
					if( strlen(param_val[i]) == 0 )
						printmsg( "No login duration specified.<br>\n", MSG_ERR );
					else
						edit_userinfo.login_dur_in_seconds = atof( param_val[i] ) * 3600.0;
					found = 1;
					break;
				}
			}
			if( !found ) { printf( "Bad form data (no login duration field).\n" ); exit(0); }
			
			hasupper = 0; haslower = 0; hasnum = 0;
			for( i = 0; i < strlen(edit_userinfo.password); i++ ) {
				if( isupper(edit_userinfo.password[i]) ) hasupper = 1;
				else if( islower(edit_userinfo.password[i]) ) haslower = 1;
				else if( isdigit(edit_userinfo.password[i]) ) hasnum = 1;
			}

			key.dptr = edit_username;
			key.dsize = strlen( edit_username );


			if( strlen(edit_username) == 0 ) {
				printmsg( "No username specified.<br>\n", MSG_ERR );
			} else if( strlen(edit_userinfo.password) == 0 ) {
				printmsg( "No password specified.<br>\n", MSG_ERR );
			} else if( (edit_userinfo.login_dur_in_seconds < 360) || (edit_userinfo.login_dur_in_seconds > 604800l) ) {
				printmsg( "Time until re-login is forced must be between .1 and 168 hours.<br>\n", MSG_ERR );
			} else if( strlen(edit_userinfo.password) < 6 ) {
				printmsg( "Passwords must be at least six characters long.<br>\n", MSG_ERR );
			} else if( !hasupper ) {
				printmsg( "Passwords must contain at least one upper case character.<br>\n", MSG_ERR );
			} else if( !haslower ) {
				printmsg( "Passwords must contain at least one lower case character.<br>\n", MSG_ERR );
			} else if( !hasnum ) {
				printmsg( "Passwords must contain at least one digit.<br>\n", MSG_ERR );


			} else if( action == ACTION_ADD ) {

				if( gdbm_exists(dbf,key) ) {
					printmsg( "There is already an account with that name.<br>\n", MSG_ERR );
				} else {
					edit_userinfo.ip[0] = '\0';
					edit_userinfo.mac[0] = '\0';
					edit_userinfo.logged_in_now = 0;
					edit_userinfo.login_expires_at = 0;

					content.dptr = (char *)&edit_userinfo;
					content.dsize = sizeof( edit_userinfo );

					retval = gdbm_store( dbf, key, content, GDBM_INSERT );
					if( retval == 0 ) {
        					post_to_log( EVENT_USER_ADD, key.dptr, "N/A", "N/A", (long)tp );
						snprintf( msgbuffer, 100, "The \"%s\" account has been added to the database.<br>\n", edit_username );
						printmsg( msgbuffer, MSG_OK );
					}
					else printmsg( "Invalid key, content, or insertion flag / program may not have write access to captivedb.<br>\n", MSG_ERR );
				}
			} else {
				/* action == ACTION_MODIFY */

				content = gdbm_fetch( dbf, key );

				if( content.dptr != NULL ) {
					strncpy( ((userinfo *)content.dptr)->password, edit_userinfo.password, PASSWORD_LEN );
					((userinfo *)content.dptr)->login_dur_in_seconds = edit_userinfo.login_dur_in_seconds;
					((userinfo *)content.dptr)->enabled = edit_userinfo.enabled;

					retval = gdbm_store( dbf, key, content, GDBM_REPLACE );
					if( retval == 0 ) {
        					post_to_log( EVENT_USER_MODIFY, key.dptr, "N/A", "N/A", (long)tp );
						snprintf( msgbuffer, 100, "The \"%s\" account has been modified.<br>\n", edit_username );
						printmsg( msgbuffer, MSG_OK );
						if( ((userinfo *)content.dptr)->logged_in_now && (edit_userinfo.enabled == 0) )
							printmsg( "You have disabled this account.  Note that this user has <font color=\"aa0000\">not</font> been forcibly logged off.<br>\n", MSG_TXT );
					} else {
						printmsg( "Invalid key, content, or insertion flag / program may not have write access to captivedb.<br>\n", MSG_ERR );
					}
					free( content.dptr );
				} else {
					printmsg( "There is no account with that name.<br>\n", MSG_ERR );
				}
			}

		} else if( action == ACTION_REMOVE ) {
			dbf = gdbm_open( "/var/smoothwall/mods/captive/captivedb", 512, GDBM_WRCREAT, S_IRWXU, NULL );

			n_users = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_val[i],"on",2) == 0 ) {
					strncpy( list_of_users[n_users], param_var[i], USERNAME_LEN );
					n_users++;
					if( n_users == 50 ) break;
				}
			}

			if( n_users == 1 ) {
				printf( "Attempting to delete user %s ...<br>\n", list_of_users[0] );
			} else if( n_users > 1 ) {
				printf( "Attempting to delete users %s", list_of_users[0] );
				for( i = 1; i < n_users-1; i++ ) printf( ", %s", list_of_users[i] );
				if( n_users == 2 ) printf( " and %s ...<br>", list_of_users[n_users-1] );
				else printf( ", and %s ...<br>", list_of_users[n_users-1] );
			}

			if( n_users == 0 )
				printmsg( "No accounts were selected.<br>\n", MSG_ERR );
			else {
				for( i = 0; i < n_users; i++ ) {
					key.dptr = list_of_users[i];
					key.dsize = strlen( list_of_users[i] );
					content = gdbm_fetch( dbf, key );
					if( content.dptr == NULL ) {
						snprintf( msgbuffer, 100, "The account \"%s\" does not exist.<br>\n", list_of_users[i] );
						printmsg( msgbuffer, MSG_ERR );
					} else {
						if( ((userinfo *)content.dptr)->logged_in_now ) {
							/* log client off ... delete firewall rules */
							if( setuid(0) != 0 ) { printf( "<html>Error 5 (a)</html>\n" ); exit(0); }
							iptables_rule( IPTABLES_DEL, ((userinfo *)content.dptr)->ip, ((userinfo *)content.dptr)->mac );
               						post_to_log( EVENT_LOGOFF_FORCED, list_of_users[i], ((userinfo *)content.dptr)->ip,((userinfo *)content.dptr)->mac, (long)tp );
						}
						if( gdbm_delete(dbf,key) == 0 ) {
        						post_to_log( EVENT_USER_DEL, key.dptr, "N/A", "N/A", (long)tp );
							snprintf( msgbuffer, 100, "The account \"%s\" has been deleted.<br>\n", list_of_users[i] );
							printmsg( msgbuffer, MSG_OK );
						} else { 
							snprintf( msgbuffer, 100, "Error deleting \"%s\" - invalid key, content, or insertion flag / program may not have write access to captivedb.<br>\n", list_of_users[i] );
							printmsg( msgbuffer, MSG_ERR );
						}
						free( content.dptr );
					}
				}

			}

		} else if( action == ACTION_LOGOFF ) {
			dbf = gdbm_open( "/var/smoothwall/mods/captive/captivedb", 512, GDBM_WRCREAT, S_IRWXU, NULL );

			n_users = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_val[i],"on",2) == 0 ) {
					strncpy( list_of_users[n_users], param_var[i], USERNAME_LEN );
					n_users++;
					if( n_users == 50 ) break;
				}
			}

                        if( n_users == 1 ) {
                                printf( "Attempting to logoff user %s ...<br>\n", list_of_users[0] );
                        } else if( n_users > 1 ) {
                                printf( "Attempting to logoff users %s", list_of_users[0] );
                                for( i = 1; i < n_users-1; i++ ) printf( ", %s", list_of_users[i] );
                                if( n_users == 2 ) printf( " and %s ...<br>", list_of_users[n_users-1] );
                                else printf( ", and %s ...<br>", list_of_users[n_users-1] );
                        }

			if( n_users == 0 )
				printmsg( "No accounts were selected.<br>\n", MSG_ERR );
			else {
				for( i = 0; i < n_users; i++ ) {
					key.dptr = list_of_users[i];
					key.dsize = strlen( list_of_users[i] );
					content = gdbm_fetch( dbf, key );
					if( content.dptr == NULL ) {
						snprintf( msgbuffer, 100, "The account %s does not exist.<br>\n", list_of_users[i] );
						printmsg( msgbuffer, MSG_ERR );
					} else {
						if( ((userinfo *)content.dptr)->logged_in_now ) {
							/* log client off ... delete firewall rules */
							if( setuid(0) != 0 ) { printf( "<html>Error 5 (a)</html>\n" ); exit(0); }
							iptables_rule( IPTABLES_DEL, ((userinfo *)content.dptr)->ip, ((userinfo *)content.dptr)->mac );

							((userinfo *)content.dptr)->logged_in_now = 0;
							((userinfo *)content.dptr)->ip[0] = '\0';
							((userinfo *)content.dptr)->mac[0] = '\0';
							((userinfo *)content.dptr)->login_expires_at = 0;

							retval = gdbm_store( dbf, key, content, GDBM_REPLACE );
							if( retval == 0 ) {
								snprintf( msgbuffer, 100, "The account %s has been logged off.<br>\n", list_of_users[i] );
               							post_to_log( EVENT_LOGOFF_FORCED, list_of_users[i], ((userinfo *)content.dptr)->ip,((userinfo *)content.dptr)->mac, (long)tp );
								printmsg( msgbuffer, MSG_OK );
							} else {
								snprintf( msgbuffer, 100, "Error logging off \"%s\" - invalid key, content, or insertion flag / program may not have write access to captivedb.<br>\n", list_of_users[i] );
               							post_to_log( EVENT_DATABASE_ERROR, list_of_users[i], ((userinfo *)content.dptr)->ip,((userinfo *)content.dptr)->mac, (long)tp );
								printmsg( msgbuffer, MSG_ERR );
							}
						} else {
							snprintf( msgbuffer, 100, "The account \"%s\" is not currently logged in.<br>\n", list_of_users[i] );
							printmsg( msgbuffer, MSG_ERR );
						}
						free( content.dptr );
					}
				}

			}
		} else if( action == ACTION_INFORMATION ) {

			n_users = 0;
			for( i = 0; i < num_param; i++ ) {
				if( strncmp(param_val[i],"on",2) == 0 ) {
					strncpy( list_of_users[n_users], param_var[i], USERNAME_LEN );
					n_users++;
					if( n_users == 50 ) break;
				}
			}
			if( n_users > 0 ) {
				if( n_users > 1 ) printf( "You can only get information on one account at a time.<br><br>\n" );
				printf( "Information for the <b>%s</b> account:<br><br>\n", list_of_users[0] );

				snprintf( fn, 100, "/var/smoothwall/mods/captive/squid/%s-stats", list_of_users[0] );
				printf( "Web browsing history:<br><li>Summary Last Generated At " );
				printfile( "/var/smoothwall/mods/captive/squid/_time" );
				printf( "<br>\n" );
				printfile( fn );
				printf( "<li>You may also view the <a href=\"captive_squid.cgi?%s\">complete Squid logs</a> of this user's browsing.<br><br>\n", list_of_users[0] );

				printf( "System information (most recent login):<br>" );
				print_nmap_history( list_of_users[0] );
				print_event_history( list_of_users[0] );

				printf( "<br><br>\n" );
			}
			else printmsg( "No account specified.<br>\n", MSG_ERR );

		}

		printf( "      </font>\n" );
		printf( "    </font></td>\n" );
		printf( "  </tr>\n" );
		printf( "  <tr>\n" );
		printf( "    <td colspan=\"2\" bgcolor=\"#ffffff\"><br></td>\n" );
		printf( "  </tr>\n" );


	}



	printf( "  <tr bgcolor=\"#ffcc66\">\n" );
        printf( "    <td colspan=\"2\" align=\"left\"><font face=\"arial\">\n" );
	printf( "      Add Or Modify Account:<br>\n" );
	printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/black.gif\" width=\"100%%\" height=\"1\" border=\"0\"><br>\n" );
	printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/clear.gif\" width=\"100%%\" height=\"5\" border=\"0\"><br>\n" );

        printf( "      <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" border=\"0\">\n" );
        printf( "        <tr bgcolor=\"#ffcc66\">\n" );
        printf( "          <td align=\"left\" colspan=\"15\"><font face=\"arial\" size=\"-1\">\n" );
        printf( "            <form action=\"captive_gui.cgi\" method=\"post\">\n" );

        printf( "            <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" border=\"0\">\n" );
	printf( "              <tr>\n" );
	printf( "                <td align=\"right\"><font face=\"arial\" size=\"-1\">Username:<br></font></td>\n" );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"text\" name=\"username\" size=\"10\" maxlength=\"%d\"> *<br></font></td>\n", USERNAME_LEN-1 );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"radio\" name=\"state\" value=\"enabled\" checked> Account Enabled<br></font></td><td width=\"20\"></td>\n" );
	printf( "                <td align=\"left\" valign=\"bottom\" rowspan=\"2\"><font face=\"arial\" size=\"-1\">* Alphanumeric characters only<br>** decimals OK<br></font></td>\n" );
	printf( "              </tr><tr>\n" );
	printf( "                <td align=\"right\"><font face=\"arial\" size=\"-1\">Password:<br></font></td>\n" );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"text\" name=\"password\" size=\"10\" maxlength=\"%d\"> *<br></font></td>\n", PASSWORD_LEN-1 );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"radio\" name=\"state\" value=\"disabled\"> Account Disabled<br></font></td><td width=\"20\"></td>\n" );
	printf( "              </tr><tr>\n" );
	printf( "                <td align=\"right\"><font face=\"arial\" size=\"-1\">Re-Login After:<br></font></td>\n" );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"text\" name=\"logindur\" size=\"10\" maxlength=\"5\"> hours**<br></font></td>\n" );
	printf( "                <td align=\"left\"><font face=\"arial\" size=\"-1\"><input type=\"radio\" name=\"state\" value=\"onetime\"> Account Good For One Login Only<br></font></td><td width=\"20\"></td>\n" );
	printf( "                <td align=\"right\"><font face=\"arial\" size=\"-1\"><input type=\"submit\" name=\"action\" value=\"add\"> &nbsp; &nbsp; <input type=\"submit\" name=\"action\" value=\"modify\"><br></font></td>\n" );
	printf( "            </table>\n" );
	printf( "            </font></td>\n" );
        printf( "        </tr>\n" );
	printf( "      </table>\n" );
	printf( "    </font></td>\n" );
	printf( "  </tr>\n" );
	printf( "  <tr>\n" );
	printf( "    <td colspan=\"2\" bgcolor=\"#ffffff\"><br></td>\n" );
	printf( "  </tr>\n" );



	


	/* If we have done something to the database (add, modify, delete, etc) then we already
	   have the database open with read/write permissions.  If not, we can open it now with
	   just read permission.
	*/

	if( dbf == NULL ) dbf = gdbm_open( "/var/smoothwall/mods/captive/captivedb", 512, GDBM_READER, S_IRWXU, NULL );



if( dbf != NULL ) {
	count = 0;
	key = gdbm_firstkey ( dbf );
if( key.dptr != NULL ) {
	key.dptr[key.dsize] = '\0';   /* Add null term, just to be safe. */
	while ( key.dptr ) {

		content = gdbm_fetch( dbf, key );

		strncpy( user_name[count], key.dptr, key.dsize );
		user_info[count] = *(userinfo *)content.dptr;

		count++;

		free ( content.dptr );
		nextkey = gdbm_nextkey ( dbf, key );

       		free ( key.dptr );
		key = nextkey;
		if( key.dptr != NULL ) key.dptr[key.dsize] = '\0';   /* Add null term, just to be safe. */
	}

	done = 0;


	/* Bubble sort.  Simple, but OK for relatively small data sets.  */
	while( !done ) {
		done = 1;
		for( i = 0; i < count-1; i++ ) {
			if( strncmp(user_name[i],user_name[i+1],USERNAME_LEN) > 0 ) {
				strncpy( temp, user_name[i], USERNAME_LEN );
				strncpy( user_name[i], user_name[i+1], USERNAME_LEN );
				strncpy( user_name[i+1], temp, USERNAME_LEN );
				userinfoswap( &user_info[i], &user_info[i+1] );
				done = 0;
			}
		}
	}




	printf( "  <tr bgcolor=\"#ffcc66\">\n" );
	printf( "    <td colspan=\"2\" align=\"left\"><font face=\"arial\">\n" );
	printf( "      Current Accounts:<br>\n" );
	printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/black.gif\" width=\"100%%\" height=\"1\" border=\"0\"><br>\n" );
	printf( "      <img src=\"/ui/assets/3.6/img/pagetitles/clear.gif\" width=\"100%%\" height=\"5\" border=\"0\"><br>\n" );

	printf( "      <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" border=\"0\">\n" );
	printf( "        <tr bgcolor=\"#ffcc66\">\n" );
	printf( "          <td valign=\"bottom\" align=\"left\"><font face=\"arial\" size=\"-1\"><b>Username</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"left\"><font face=\"arial\" size=\"-1\"><b>Password</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"right\"><font face=\"arial\" size=\"-1\"><b>Must Login<br>Again After</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"center\"><font face=\"arial\" size=\"-1\"><b>Account<br>Enabled?</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"center\"><font face=\"arial\" size=\"-1\"><b>IP Address</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"center\"><font face=\"arial\" size=\"-1\"><b>MAC Address</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"center\"><font face=\"arial\" size=\"-1\"><b>Login Time<br>Remaining</b></font></td><td width=\"10\"></td>\n" );
	printf( "          <td valign=\"bottom\" align=\"center\"><font face=\"arial\" size=\"-1\"><b>Mark</b></font></td>\n" );
	printf( "        </tr>\n" );

	for( i = 0; i < count; i++ ) {
		if( (i%2) == 1 ) printf( "        <tr bgcolor=\"#ffee88\">\n" );
		else printf( "        <tr bgcolor=\"#ff9900\">\n" );

		printf( "          <td align=\"left\"><font face=\"arial\" size=\"-1\"><a href=\"captive_gui.cgi?username=%s\" style=\"text-decoration:none\">%s</a></font></td><td width=\"10\"></td>\n", user_name[i], user_name[i] );
		printf( "          <td align=\"left\"><font face=\"arial\" size=\"-1\">%s</font></td><td width=\"10\"></td>\n", user_info[i].password );
		printf( "          <td align=\"right\"><font face=\"arial\" size=\"-1\">%.1f hours &nbsp;</font></td><td width=\"10\"></td>\n", (float)user_info[i].login_dur_in_seconds/3600.0 );
		if( user_info[i].enabled == 1 )
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">yes</font></td><td width=\"10\"></td>\n" );
		else if( user_info[i].enabled == 2 )
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">one-time</font></td><td width=\"10\"></td>\n" );
		else
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">no</font></td><td width=\"10\"></td>\n" );

		if( user_info[i].logged_in_now ) {
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">%s</font></td><td width=\"10\"></td>\n", user_info[i].ip );
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">%s</font></td><td width=\"10\"></td>\n", user_info[i].mac );
			printf( "          <td align=\"center\"><font face=\"arial\" size=\"-1\">%s</font></td><td width=\"10\"></td>\n", timeleftstring(user_info[i].login_expires_at-(long)tp) );
		} else {
			printf( "          <td></td><td width=\"10\"></td>\n" );
			printf( "          <td></td><td width=\"10\"></td>\n" );
			printf( "          <td></td><td width=\"10\"></td>\n" );
		}
		printf( "          <td align=\"center\"><input type=\"checkbox\" name=\"%s\"></td>\n", user_name[i] );

	}


	printf( "        </tr>\n" );

	printf( "      </table>\n" );
	printf( "    </font></td>\n" );
	printf( "  </tr>\n" );

	printf( "  <tr bgcolor=\"#ffcc66\">\n" );
	printf( "    <td colspan=\"2\" width=\"100%%\" align=\"right\"><input type=\"submit\" name=\"action\" value=\"information\"> &nbsp; &nbsp; <input type=\"submit\" name=\"action\" value=\"remove\"> &nbsp; &nbsp; <input type=\"submit\" name=\"action\" value=\"logoff\"></td>\n" );
	printf( "  </tr>\n" );
}
}

	printf( "</table>\n" );





	printf( "</center></body></html>\n" );



	if( dbf != NULL ) gdbm_close( dbf );

	return 0;
}
