From 1f9e3b54f9a137e83cfddf85f1c6493db7378a3c Mon Sep 17 00:00:00 2001 From: arjen Date: Sat, 5 Oct 2002 10:25:48 +0000 Subject: [PATCH] Creation of gcm_input and a first approach to a web interface --- src/gcm_input/gcm_input.cpp | 183 ++++++++++++ src/gcm_input/makefile | 13 + src/gcm_input/message.cpp | 510 ++++++++++++++++++++++++++++++++ src/gcm_input/message.h | 150 ++++++++++ src/gnucomo.conf | 12 + src/include/database.h | 91 ++++++ src/include/gnucomo_config.h | 59 ++++ src/lib/database.cpp | 106 +++++++ src/lib/gnucomo_config.cpp | 91 ++++++ src/phpclasses/configuration.class.php | 109 +++++++ src/phpclasses/gnucomo_config.php | 62 ++++ src/web/change.png | Bin 0 -> 1744 bytes src/web/classes/configuration.class.php | 109 +++++++ src/web/classes/gnucomo_config.php | 62 ++++ src/web/closed_package.png | Bin 0 -> 1619 bytes src/web/exit.png | Bin 0 -> 2238 bytes src/web/functions.php | 77 +++++ src/web/gnucomo.css | 47 +++ src/web/harddisk.png | Bin 0 -> 2868 bytes src/web/index.html | 12 + src/web/log.png | Bin 0 -> 2120 bytes src/web/login.php | 59 ++++ src/web/menu.html | 17 ++ src/web/open_package.png | Bin 0 -> 2234 bytes src/web/password.png | Bin 0 -> 3024 bytes src/web/server.png | Bin 0 -> 2050 bytes src/web/service.png | Bin 0 -> 4800 bytes src/web/user.png | Bin 0 -> 1750 bytes 28 files changed, 1769 insertions(+) create mode 100644 src/gcm_input/gcm_input.cpp create mode 100644 src/gcm_input/makefile create mode 100644 src/gcm_input/message.cpp create mode 100644 src/gcm_input/message.h create mode 100644 src/gnucomo.conf create mode 100644 src/include/database.h create mode 100644 src/include/gnucomo_config.h create mode 100644 src/lib/database.cpp create mode 100644 src/lib/gnucomo_config.cpp create mode 100644 src/phpclasses/configuration.class.php create mode 100644 src/phpclasses/gnucomo_config.php create mode 100644 src/web/change.png create mode 100644 src/web/classes/configuration.class.php create mode 100644 src/web/classes/gnucomo_config.php create mode 100644 src/web/closed_package.png create mode 100644 src/web/exit.png create mode 100755 src/web/functions.php create mode 100644 src/web/gnucomo.css create mode 100644 src/web/harddisk.png create mode 100644 src/web/index.html create mode 100644 src/web/log.png create mode 100755 src/web/login.php create mode 100644 src/web/menu.html create mode 100644 src/web/open_package.png create mode 100644 src/web/password.png create mode 100644 src/web/server.png create mode 100644 src/web/service.png create mode 100644 src/web/user.png diff --git a/src/gcm_input/gcm_input.cpp b/src/gcm_input/gcm_input.cpp new file mode 100644 index 0000000..51fa9b7 --- /dev/null +++ b/src/gcm_input/gcm_input.cpp @@ -0,0 +1,183 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : gcm_input.cpp +** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Application to store client messages into the database +** The client message contains a log file from one of the +** system logs. Gcm_input parses the log file and enters +** the raw data into the 'log' table of the gnucomo database. +** +** The system log may arrive in one of several forms: +** 1. Log file (archived or not) from a client machine. +** 2. Log file from a client system, arriving in a clear Email. +** 3. Log file from a client machine, arriving in an +** encrypted Email. +** +** additional information we need that may not be availble in +** the content of the log is: +** - FQDN of the client. +** - Time of arrival of the log +** - Service that created the log. +** +** If the log arrives in an Email, these items probably are +** available in the mail header. Otherwise, they have to be +** provided as command line arguments. +** +** Command line arguments: +** -c Configuration name (default = gnucomo). +** -d Date and time of log arrival. +** -h FQDN of the client. +** -s Service that created the log. +** -T Test mode. Do not alter the database. +** -v Verbose (debug) output. +** -V Print version and exit. +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Aug 29, 2002 +** LAST UPDATE : Sep 30, 2002 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: gcm_input.cpp,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +static const char *RCSID = "$Id: gcm_input.cpp,v 1.1 2002-10-05 10:25:49 arjen Exp $"; + +#include + +#include "message.h" + +bool verbose = false; +bool testmode = false; + +static char *Version = "gcm_input version 0.0.3 - Sep 30, 2002"; + + +/*========================================================================= +** NAME : main +** SYNOPSIS : int main(int argc, char *argv[]) +** PARAMETERS : +** RETURN VALUE : +** +** DESCRIPTION : Parse command line arguments and establish a connection +** to the database. +** When we have a database connection, parse the log file +** from stdin. +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Sep 30, 2002 +**========================================================================= +*/ + +int main(int argc, char *argv[]) +{ + const char *usage = "Usage: gcm_input [-c configname] [-h hostname] [-d date]" + " [-s service] [-T] [-v] [-V]\n"; + + gnucomo_config cfg; + char *config_name = "gnucomo"; + + /* Parse command line arguments */ + + UTC arrival = Now(); + String hostname(""), service(""); + int option; + + + while ((option = getopt(argc, argv, "c:h:d:s:TvV")) != -1) + { + switch (option) + { + case 'c': + config_name = optarg; + break; + + case 'h': + hostname = optarg; + break; + + case 'd': + arrival = String(optarg); + if (!arrival.proper()) + { + cerr << "gcm_input: Invalid date string: " << optarg << ".\n"; + exit(1); + } + break; + + case 's': + service = optarg; + break; + + case 'T': + testmode = true; + break; + + case 'v': + verbose = true; + break; + + case 'V': + cout << Version << "\n"; + exit(0); + + case '?': + case ':': + cerr << usage; + exit(1); + } + } + + if (verbose) + { + cout << "Hostname = " << hostname; + cout << " Arrival = " << arrival; + cout << " Service = " << service << "\n"; + } + + /* Get the configuration file */ + + if (!cfg.read(config_name)) + { + cerr << "Can not read Gnucomo configuration file for " << config_name << ".\n"; + exit(1); + } + + if (verbose) + { + cout << "Config OK.\n"; + } + + /* Try to connect to the database */ + + gnucomo_database db(&cfg); + + client_message msg(&cin, db); + + if (msg.classify(hostname, arrival, service) > 0.9) + { + msg.enter(); + } + + return 0; +} + diff --git a/src/gcm_input/makefile b/src/gcm_input/makefile new file mode 100644 index 0000000..4341009 --- /dev/null +++ b/src/gcm_input/makefile @@ -0,0 +1,13 @@ +CXXFLAGS = -I/usr/include/gnome-xml -g + +GCMINPUT_O = gcm_input.o gnucomo_config.o database.o message.o + +gcm_input : $(GCMINPUT_O) + $(CXX) -o gcm_input $(GCMINPUT_O) -lAXE -lxml -lpq++ + +gcm_input.o : message.h database.h +database.o : database.h +message.o : message.h + +clean: + rm -f gcm_input $(GCMINPUT_O) diff --git a/src/gcm_input/message.cpp b/src/gcm_input/message.cpp new file mode 100644 index 0000000..9b1ef1a --- /dev/null +++ b/src/gcm_input/message.cpp @@ -0,0 +1,510 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : message.cpp +** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Implementation of the message handling classes +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Sep 16, 2002 +** LAST UPDATE : Oct 05, 2002 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: message.cpp,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +static const char *RCSID = "$Id: message.cpp,v 1.1 2002-10-05 10:25:49 arjen Exp $"; + +#include "message.h" + +extern bool verbose; /* Defined in the main application */ +extern bool testmode; + +/* Utility functions */ + +String SQL_Escape(String s); + +/*========================================================================= +** NAME : operator >> +** SYNOPSIS : bool operator >> (message_buffer &, String &) +** PARAMETERS : +** RETURN VALUE : True if input was available. +** +** DESCRIPTION : Input operator. Read the next line from the message. +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Sep 30, 2002 +**========================================================================= +*/ + +bool operator >> (message_buffer &b, String &s) +{ + bool input_ok = false; + + if (b.next_line == b.buffer.end()) + { + String l; + + //cout << " buffer is depleted.\n"; + if (*(b.input) >> l) + { + b.buffer.push_back(l); + + // next_line keeps pointing to the end. + + s = l; + input_ok = true; + //cout << " new line from input.\n"; + } + } + else + { + //cout << " reading from cache.\n"; + s = *(b.next_line); + b.next_line++; + input_ok = true; + } + return input_ok; +} + +client_message::client_message(istream *in, gnucomo_database db) +{ + input.from(in); + database = db; + + hostname = ""; + mail_header = false; + gpg_encrypted = false; + classification = UNKNOWN; + certainty = 0.0; +} + +static const String syslog_date_re("[[:alpha:]]{3} [ 123][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2}"); +static const String mail_date_re("[[:alpha:]]{3}, [ 123]?[0-9] [[:alpha:]]{3} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4}"); +static const String unix_date_re("[[:alpha:]]{3} [[:alpha:]]{3} [ 123][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4}"); + +static const regex re_syslog(syslog_date_re + " [a-z]+ [[:alpha:]]+.*:.+"); +static const regex re_PGP("-----BEGIN PGP MESSAGE-----"); +static const regex re_dump("^ *DUMP: Date of this level"); +static const regex re_accesslog("(GET|POST) .+ HTTP"); +static const regex re_errorlog("^\\[" + unix_date_re + "\\] \\[(error|notice)\\] .+"); + +static const regex re_syslog_date("[[:alpha:]]{3} [ 123][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2}"); +static const regex re_uxmail_from("^From - " + unix_date_re); +static const regex re_mail_From("^From:[[:blank:]]+"); +static const regex re_mail_Date("^Date:[[:blank:]]+" + mail_date_re); +static const regex re_email_address("[[:alnum:]_.-]+@[[:alnum:]_.-]+"); +static const regex re_email_user("[[:alnum:]_.-]+@"); + +/*========================================================================= +** NAME : classify +** SYNOPSIS : double classify(String host, date arriv_d, hour arriv_t, String serv) +** PARAMETERS : +** RETURN VALUE : The certainty with which the message is classified. +** +** DESCRIPTION : +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Oct 05, 2002 +**========================================================================= +*/ + +double client_message::classify(String host, UTC arriv, String serv) +{ + String line; + + hostname = host; + arrival = arriv; + service = serv; + + /* First, check if the message has a mail header. */ + + if (input >> line && line == re_uxmail_from) + { + String from_address; + + mail_header = true; + + /* Scan ahead for the hostname and date of arrival. */ + + while (input >> line && line != "") + { + if (line == re_mail_From) + { + from_address = line(re_email_address); + from_address(re_email_user) = ""; // Remove the user part; + hostname = from_address; + } + if (line == re_mail_Date) + { + arrival = UTC(line(regex(mail_date_re))); + } + } + } + else + { + // Push the first line back, we need to read it again. + --input; + } + + /* + * Now that we have the mail header out of the way, try to figure + * out what the content of the message is. + */ + + + while (input >> line && certainty < 0.9) + { + cout << " testing: " << line << "\n"; + if (line == re_syslog) + { + certainty = 1.0; + classification = SYSLOG; + if (verbose) + { + cout << "Syslog detected.\n"; + } + } + else if (line == re_PGP) + { + certainty = 1.0; + gpg_encrypted = true; + cerr << "The message is PGP/GnuPG encrypted.\n"; + } + else if (line == re_dump) + { + certainty = 1.0; + if (verbose) + { + cout << "DUMP output detected.\n"; + } + } + else if (line == re_accesslog) + { + certainty = 1.0; + classification = ACCESSLOG; + service = "httpd"; + if (verbose) + { + cout << "HTTP access log detected.\n"; + } + } + else if (line == re_errorlog) + { + certainty = 1.0; + classification = ERRORLOG; + service = "httpd"; + if (verbose) + { + cout << "HTTP error log detected.\n"; + } + } + } + input.rewind(); + + if (hostname == "") + { + cerr << "Can not determine the hostname where the message came from.\n"; + certainty = 0.0; + } + else if (!arrival.proper()) + { + cerr << "Arrival time is not knwon.\n"; + certainty = 0.0; + } + else + { + certainty = 1.0; + } + + return certainty; +} + +/*========================================================================= +** NAME : enter +** SYNOPSIS : int enter() +** PARAMETERS : +** RETURN VALUE : The number of lines successfully parsed from the input +** +** DESCRIPTION : +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Oct 05, 2002 +**========================================================================= +*/ + +int client_message::enter() +{ + long nr_lines = 0; + String line; + + /* Double-check the classification of the message */ + + if (classification == UNKNOWN || certainty < 0.9 || gpg_encrypted) + { + return 0; + } + + if (mail_header) + { + // Skip the mail header. + + while (input >> line && line != ""); + } + + /* Try to find the host in the database */ + + String objectid; + + objectid = database.find_host(hostname); + if (objectid == "") + { + cerr << "Please define the host " << hostname << " in the database.\n"; + return 0; + } + if (verbose) + { + cout << "Object id for " << hostname << " is " << objectid << "\n"; + } + + /* Scan the input line by line, entring records into the database */ + + String rest; // Rest of the line to be parsed + + while (input >> line) + { + if (verbose) + { + cout << line << "\n"; + } + + + /* Check each line if it contains valid information */ + + const regex *check; + + switch (classification) + { + case SYSLOG: + check = &re_syslog; + break; + case ACCESSLOG: + check = &re_accesslog; + break; + case ERRORLOG: + check = &re_errorlog; + break; + } + + if (line == *check) + { + date log_date; + hour log_time; + int i; + + String insertion("insert into log (objectid, servicecode," + " object_timestamp, timestamp, rawdata) values ("); + String datestring; + + switch (classification) + { + case SYSLOG: + log_date = line; + log_time = line; + if (log_date.Year() < 0 || log_date.Year() > 2500) + { + // The year is not in the log file. Assume the year of arrival + + log_date = date(log_date.Day(), log_date.Month(), date(arrival).Year()); + } + + if (verbose) + { + cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + } + rest = line << 16; + i = rest.index(' '); + if (rest(0,i) == hostname(0,i)) + { + rest <<= i + 1; + if (verbose) + { + cout << " Hostname matches.\n"; + cout << " rest = " << rest << "\n"; + } + for (i = 0; isalpha(rest[i]) && i < ~rest; i++); + if (verbose) + { + cout << " Service name = " << rest(0,i) << "\n"; + } + + /* Insert a new record into the log table */ + + insertion += "'" + objectid + "',"; + insertion += "'" + rest(0,i) + "',"; + insertion += "'" + log_date.format() + " " + log_time.format() + "',"; + insertion += "'" + arrival.format() + "',"; + insertion += "'" + SQL_Escape(line) + "'"; + insertion += ")"; + + if (testmode) + { + cout << insertion << "\n"; + } + else + { + database.Query(insertion); + } + + if (verbose) + { + cout << "\n\n"; + } + + nr_lines++; + } + else + { + cerr << " Hostname " << rest(0,i) << " does not match.\n"; + } + break; + + case ACCESSLOG: + datestring = line(regex("\\[.+\\]")); + datestring <<= 1; + datestring >>= 1; + datestring[datestring.index(':')] = ' '; + log_date = datestring; + log_time = datestring; + if (verbose) + { + cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + } + insertion += "'" + objectid + "',"; + insertion += "'" + service + "',"; + insertion += "'" + log_date.format() + " " + log_time.format() + "',"; + insertion += "'" + arrival.format() + "',"; + insertion += "'" + SQL_Escape(line) + "'"; + insertion += ")"; + + if (testmode) + { + cout << insertion << "\n"; + } + else + { + database.Query(insertion); + } + + if (verbose) + { + cout << "\n\n"; + } + + nr_lines++; + break; + + case ERRORLOG: + datestring = line(regex("\\[.+\\]")); + datestring <<= 1; + datestring >>= 1; + log_date = datestring; + log_time = datestring; + if (verbose) + { + cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + } + insertion += "'" + objectid + "',"; + insertion += "'" + service + "',"; + insertion += "'" + log_date.format() + " " + log_time.format() + "',"; + insertion += "'" + arrival.format() + "',"; + insertion += "'" + SQL_Escape(line) + "'"; + insertion += ")"; + + if (testmode) + { + cout << insertion << "\n"; + } + else + { + database.Query(insertion); + } + + if (verbose) + { + cout << "\n\n"; + } + + nr_lines++; + break; + } + } + else + { + cerr << "gcm_input WARNING: Not a valid line: " << line << "\n"; + } + } + + if (verbose) + { + cout << nr_lines << " lines parsed from the log file.\n"; + } + return nr_lines; +} + +/*========================================================================= +** NAME : SQL_Escape +** SYNOPSIS : String SQL_Escape(String) +** PARAMETERS : +** RETURN VALUE : +** +** DESCRIPTION : Insert backslashes before single quotes. +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : +**========================================================================= +*/ + +String SQL_Escape(String s) +{ + int i; + + for (i = 0; i < ~s; i++) + { + if (s[i] == '\'') + { + s(i,0) = "\\"; + i++; + } + } + + return s; +} diff --git a/src/gcm_input/message.h b/src/gcm_input/message.h new file mode 100644 index 0000000..03a8209 --- /dev/null +++ b/src/gcm_input/message.h @@ -0,0 +1,150 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : message.h +** SYSTEM NAME : +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Classes to for handling client messages +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Sep 16, 2002 +** LAST UPDATE : Oct 05, 2002 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: message.h,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +/* static const char *RCSID = "$Id: message.h,v 1.1 2002-10-05 10:25:49 arjen Exp $"; */ + +#include +#include +#include +#include + +#include "database.h" + +/* +/////////////////////////////////////////////////////////////////////////// +// NAME : message_buffer +// BASECLASS : +// MEMBERS : +// OPERATORS : +// METHODS : rewind() +// +// DESCRIPTION : +// +// RELATIONS : +// SEE ALSO : +// LAST MODIFIED : Sep 30, 2002 +/////////////////////////////////////////////////////////////////////////// +*/ + +class message_buffer +{ + istream *input; + list buffer; + + list::iterator next_line; + +public: + + message_buffer() + { + input = 0; + next_line = buffer.begin(); + } + + message_buffer(istream *in) + { + input = in; + next_line = buffer.begin(); + } + + void from(istream *in) + { + input = in; + } + + friend bool operator >> (message_buffer &, String &); + + void rewind() + { + next_line = buffer.begin(); + } + + void operator ++() + { + if (next_line != buffer.end()) + { + next_line++; + } + } + + void operator --() + { + if (next_line != buffer.begin()) + { + next_line--; + } + } +}; + +/* +/////////////////////////////////////////////////////////////////////////// +// NAME : client_message +// BASECLASS : +// MEMBERS : +// OPERATORS : +// METHODS : classify() +// enter() +// +// DESCRIPTION : +// +// RELATIONS : +// SEE ALSO : +// LAST MODIFIED : Oct 05, 2002 +/////////////////////////////////////////////////////////////////////////// +*/ + +class client_message +{ + String hostname; // Where the message came from (FQDN) + UTC arrival; // When we got the message. + String service; // Service that created the message + + bool mail_header; // Does the message contain a mail header ? + bool gpg_encrypted; // Is the message encrypted ? + + double certainty; // How certain are we about the message + enum + { + UNKNOWN, SYSLOG, ACCESSLOG, ERRORLOG + } classification; + + + message_buffer input; + gnucomo_database database; + +public: + + client_message(istream *in, gnucomo_database db); + + double classify(String host, UTC arrival = Now(), String serv = ""); + int enter(); +}; + diff --git a/src/gnucomo.conf b/src/gnucomo.conf new file mode 100644 index 0000000..90499ef --- /dev/null +++ b/src/gnucomo.conf @@ -0,0 +1,12 @@ + + + + PostgreSQL + gnucomo + arjen + guess again :-) + localhost + 5432 + + + diff --git a/src/include/database.h b/src/include/database.h new file mode 100644 index 0000000..5fb2323 --- /dev/null +++ b/src/include/database.h @@ -0,0 +1,91 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : database.h +** SYSTEM NAME : +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Classes to provide an abstract layer on the Gnucomo +** database. +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Sep 10, 2002 +** LAST UPDATE : +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: database.h,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +/* static const char *RCSID = "$Id: database.h,v 1.1 2002-10-05 10:25:49 arjen Exp $"; */ + +#include +#include "gnucomo_config.h" + +/* +/////////////////////////////////////////////////////////////////////////// +// NAME : gnucomo_database +// BASECLASS : configuration +// MEMBERS : +// OPERATORS : +// METHODS : Database - Obtain the database access string +// +// DESCRIPTION : +// +// RELATIONS : +// SEE ALSO : +// LAST MODIFIED : Sep 16, 2002 +/////////////////////////////////////////////////////////////////////////// +*/ + +class gnucomo_database +{ + gnucomo_config *cfg; + PgDatabase *db; + +public: + + gnucomo_database() + { + cfg = 0; + db = 0; + } + + gnucomo_database(gnucomo_config *c); // Use the configuration to connect to the database + + // Low-level database access functions + + int Query(String qry) + { + ExecStatusType result; + + result = db->Exec(qry); + if (result == PGRES_TUPLES_OK || result == PGRES_COMMAND_OK) + { + return db->Tuples(); + } + else + { + cerr << "Database query error: " << db->ErrorMessage() << "\n"; + return -1; + } + } + + // Return the objectid of the host given its name. + + String find_host(const String hostname); +}; + diff --git a/src/include/gnucomo_config.h b/src/include/gnucomo_config.h new file mode 100644 index 0000000..4c854be --- /dev/null +++ b/src/include/gnucomo_config.h @@ -0,0 +1,59 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : gnucomo.h +** SYSTEM NAME : +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Jul 24, 2002 +** LAST UPDATE : +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: gnucomo_config.h,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +/* static const char *RCSID = "$Id: gnucomo_config.h,v 1.1 2002-10-05 10:25:49 arjen Exp $"; */ + +#include + +/* +/////////////////////////////////////////////////////////////////////////// +// NAME : gnucomo_config +// BASECLASS : configuration +// MEMBERS : +// OPERATORS : +// METHODS : Database - Obtain the database access string +// +// DESCRIPTION : +// +// RELATIONS : +// SEE ALSO : +// LAST MODIFIED : +/////////////////////////////////////////////////////////////////////////// +*/ + +class gnucomo_config : public configuration +{ + +public: + + String Database(); // Return the database access string. +}; + diff --git a/src/lib/database.cpp b/src/lib/database.cpp new file mode 100644 index 0000000..134a536 --- /dev/null +++ b/src/lib/database.cpp @@ -0,0 +1,106 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : database.cpp +** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Implementation of the gnucomo database classes +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Sep 10, 2002 +** LAST UPDATE : Sep 26, 2002 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: database.cpp,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +static const char *RCSID = "$Id: database.cpp,v 1.1 2002-10-05 10:25:49 arjen Exp $"; + +#include "database.h" + +extern bool verbose; /* Defined in the main application */ + +/*========================================================================= +** NAME : gnucomo_database +** SYNOPSIS : gnucomo_database(gnucomo_config &c); +** PARAMETERS : +** RETURN VALUE : Database constructor. Establishes a connection with +** the database server. +** +** DESCRIPTION : +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Sep 26, 2002 +**========================================================================= +*/ + +gnucomo_database::gnucomo_database(gnucomo_config *c) +{ + cfg = c; + + if (verbose) + { + cout << "Database connection string = " << cfg->Database() << "\n"; + } + + db = new PgDatabase(cfg->Database()); + + if (db->ConnectionBad()) + { + cerr << "Can not connect to database: " << db->ErrorMessage(); + } +} + +/*========================================================================= +** NAME : find_host +** SYNOPSIS : String gnucomo_database::find_host(String hostname); +** PARAMETERS : +** RETURN VALUE : Find a hostname in the 'object' table of the gnucomo database +** and return its object id. +** Return an empty string as objectid if the hostname is +** not found. +** +** DESCRIPTION : +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : Sep 16, 2002 +**========================================================================= +*/ + +String gnucomo_database::find_host(const String hostname) +{ + String objectid(""); + String check_host("select objectid from object where "); + + check_host += "objectname = '"; + check_host += hostname; + check_host += "'"; + + if (Query(check_host) > 0) + { + objectid = String(db->GetValue(0, "objectid")); + } + + return objectid; +} diff --git a/src/lib/gnucomo_config.cpp b/src/lib/gnucomo_config.cpp new file mode 100644 index 0000000..794241e --- /dev/null +++ b/src/lib/gnucomo_config.cpp @@ -0,0 +1,91 @@ + +/************************************************************************** +** (c) Copyright 2002, Andromeda Technology & Automation +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : gnucomo_config.cpp +** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Implementation of the gnucomo_config class. +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Jul 24, 2002 +** LAST UPDATE : Jul 24, 2002 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: gnucomo_config.cpp,v $ + Revision 1.1 2002-10-05 10:25:49 arjen + Creation of gcm_input and a first approach to a web interface + +*****************************/ + +static const char *RCSID = "$Id: gnucomo_config.cpp,v 1.1 2002-10-05 10:25:49 arjen Exp $"; + +#include "gnucomo_config.h" + + + +/*========================================================================= +** NAME : Database +** SYNOPSIS : String gnucomo_config::Database() +** PARAMETERS : +** RETURN VALUE : The database access string +** +** DESCRIPTION : +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : +**========================================================================= +*/ + +String gnucomo_config::Database() +{ + String param; + String access_string(""); + + param = find_parameter("database", "name"); + if (param != "") + { + access_string += "dbname=" + param; + } + + param = find_parameter("database", "user"); + if (param != "") + { + access_string += " user=" + param; + } + param = find_parameter("database", "password"); + if (param != "") + { + access_string += " password=" + param; + } + + /* FIXME: This needs a fix in the AXE library first */ + + /* + param = find_parameter("database", "host"); + if (param != "") + { + access_string += " host=" + param; + } + param = find_parameter("database", "port"); + if (param != "") + { + access_string += " port=" + param; + } +*/ + return access_string; +} diff --git a/src/phpclasses/configuration.class.php b/src/phpclasses/configuration.class.php new file mode 100644 index 0000000..5d5968e --- /dev/null +++ b/src/phpclasses/configuration.class.php @@ -0,0 +1,109 @@ +type == XML_ELEMENT_NODE && $node[$i]->tagname == $tag) + { + $element = $node[$i]; + } + $i++; + } + + return $element; + } + + function read($app_name) + { + $filename = "/etc/" . $app_name . ".conf"; + $this->system = xmldocfile($filename); + + if (!$this->system) + { + $filename = "/usr/local/etc/" . $app_name . ".conf"; + $this->system = xmldocfile($filename); + } + + if ($this->system) + { + $root = $this->system->root(); + + if ($root->tagname != $app_name) + { + print("Configuration error: Wrong configuration file.
"); + $this->system = false; + } + } + else + { + print("Configuration error: Configuration file for $app_name not found.
"); + } + } + + function find_parameter($section, $parameter) + { + $param_value = ""; + + if ($this->system) + { + $root_node = $this->system->root(); + $section_node = $this->xmlFindTag($root_node->children(), $section); + if ($section_node) + { + $param_node = $this->xmlFindTag($section_node->children(), $parameter); + if ($param_node) + { + $param_node = $param_node->children(); + } + if ($param_node && $param_node[0]->type == XML_TEXT_NODE) + { + $param_value = $param_node[0]->content; + } + } + } + + return $param_value; + } +} + +?> diff --git a/src/phpclasses/gnucomo_config.php b/src/phpclasses/gnucomo_config.php new file mode 100644 index 0000000..ea57c2f --- /dev/null +++ b/src/phpclasses/gnucomo_config.php @@ -0,0 +1,62 @@ +find_parameter("database", "name"); + if ($param != "") + { + $access_string .= "dbname=" . $param; + } + + $param = $this->find_parameter("database", "user"); + if ($param != "") + { + $access_string .= " user=" . $param; + } + + $param = $this->find_parameter("database", "password"); + if ($param != "") + { + $access_string .= " password=" . $param; + } + + $param = $this->find_parameter("database", "host"); + if ($param != "") + { + $access_string .= " host=" . $param; + } + + $param = $this->find_parameter("database", "port"); + if ($param != "") + { + $access_string .= " port=" . $param; + } + return $access_string; + } +}; +?> diff --git a/src/web/change.png b/src/web/change.png new file mode 100644 index 0000000000000000000000000000000000000000..d413512e5e48d542b7c18444533feedafe7ec979 GIT binary patch literal 1744 zcmV;>1~2)EP)>F&@YH z!VXF9qFw)_kw1>k(Rt2$-rqU%LSKFL)mLBt&k_!34EyOE)7{~w? z0iiVsZrr#5z|71Hg+hU1v52B5ZIF7sj;gA>di4r`SS)q|P}l(ZuK+jzV{UGaR4N5P zB9Yhx0&Ig(6ou!{pEEx{KL%ug%6=h!6u=2IR#sLhl}e;iDVCR)cLZ@b9OUzP%H=XE zD=Vz6t?}f^lLLe36`<2)OsohT1+oB3OH0&hHBzY*U%!6c2C}lULZwon(P*%=v_vwQ zWM*c@hDZZCKbRA}ByrnmVzJn9K*MsCbzl?-MIwK@c?)LiGA34R{;CVrAwCxhr=u{FO$t?zX8n8&j+z0q5;LN zAhWZxzJmAX_^4Pu#aujt&<4UXJ=F95VNj`5ICA6&!C=si>bDli2pjjN9U*!Fr~)TE9?v&bRW~(G zPEG=R7bcxfqpB+Td>%=X2m}InJRYjmD!Q&ysZj)`VzEC1 z{{iak;!wSu#7(SK81r~M3#zIT3~O^+r+oRVCCR<^R(9&nkJUT zWq`5EmoG0Uih>Pd+oULpB$G*;PAAP~lSZRKAQ0f<#fzAx$-R5`ICkt9w{G2PPj>6X zJOAr8`Mm&af>0D?3gxe$CmlXDOG<3=9mA zPN&;>lSm}+`FuQh@PKBsNuf|c*Y$RCpFe;8GVlS*l^O>HVE3~@hFhP%bij1JKl~Hm z&6_t|xNxC;*x4XXr<1pD-)?fUTrP*M>zq1u>d*i>``a+?*5utC<2tbJM*-kBfKCJG z1c8l+*dR`)vn#-UsuHkKCTU{%w9fYWkQ*q_x@wP)k8c72+O}-VJ}7(9egQhQTg}J7 zvSF1yg7l(;1lUa`7DRFU(xUW>oar(Rg~}m0000type == XML_ELEMENT_NODE && $node[$i]->tagname == $tag) + { + $element = $node[$i]; + } + $i++; + } + + return $element; + } + + function read($app_name) + { + $filename = "/etc/" . $app_name . ".conf"; + $this->system = xmldocfile($filename); + + if (!$this->system) + { + $filename = "/usr/local/etc/" . $app_name . ".conf"; + $this->system = xmldocfile($filename); + } + + if ($this->system) + { + $root = $this->system->root(); + + if ($root->tagname != $app_name) + { + print("Configuration error: Wrong configuration file.
"); + $this->system = false; + } + } + else + { + print("Configuration error: Configuration file for $app_name not found.
"); + } + } + + function find_parameter($section, $parameter) + { + $param_value = ""; + + if ($this->system) + { + $root_node = $this->system->root(); + $section_node = $this->xmlFindTag($root_node->children(), $section); + if ($section_node) + { + $param_node = $this->xmlFindTag($section_node->children(), $parameter); + if ($param_node) + { + $param_node = $param_node->children(); + } + if ($param_node && $param_node[0]->type == XML_TEXT_NODE) + { + $param_value = $param_node[0]->content; + } + } + } + + return $param_value; + } +} + +?> diff --git a/src/web/classes/gnucomo_config.php b/src/web/classes/gnucomo_config.php new file mode 100644 index 0000000..ea57c2f --- /dev/null +++ b/src/web/classes/gnucomo_config.php @@ -0,0 +1,62 @@ +find_parameter("database", "name"); + if ($param != "") + { + $access_string .= "dbname=" . $param; + } + + $param = $this->find_parameter("database", "user"); + if ($param != "") + { + $access_string .= " user=" . $param; + } + + $param = $this->find_parameter("database", "password"); + if ($param != "") + { + $access_string .= " password=" . $param; + } + + $param = $this->find_parameter("database", "host"); + if ($param != "") + { + $access_string .= " host=" . $param; + } + + $param = $this->find_parameter("database", "port"); + if ($param != "") + { + $access_string .= " port=" . $param; + } + return $access_string; + } +}; +?> diff --git a/src/web/closed_package.png b/src/web/closed_package.png new file mode 100644 index 0000000000000000000000000000000000000000..d786ccafc1db3f2044fd1ab37c051919ca31b3ca GIT binary patch literal 1619 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 M07*qoM6N<$g5cCag#Z8m literal 0 HcmV?d00001 diff --git a/src/web/functions.php b/src/web/functions.php new file mode 100755 index 0000000..61e64a8 --- /dev/null +++ b/src/web/functions.php @@ -0,0 +1,77 @@ +"; + $login .= "

GNU Computer Monitoring

"; + $login .= "
"; + $login .= ""; + $login .= ""; + $login .= ""; + $login .= ""; + $login .= "
GnuCoMo logo
"; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= " "; + $login .= "
Username
Password
 
"; + $login .= "
"; + $login .= "
"; + $login .= ""; + + return $login; +} + +/******************************************************************************************/ +/* array function query( resource connection, string sqlquery ) */ +/* */ +/* gives an array return. */ +/* arr[0] = boolean if action completed whitout error, return true otherwise false */ +/* arr[1] = result sql query result */ +/* arr[2] = int number of rows */ +/* arr[3] = string error information */ +/******************************************************************************************/ +function query( $conn, $sql ) { + + $query_result = pg_exec( $conn, $sql ); + + if( $query_result != FALSE ) { + $query_nRows = pg_NumRows($query_result); + $query_error = pg_errormessage( $conn ); + return array( TRUE, $query_result, $query_nRows, "" ); + } else { + $query_error = @pg_errormessage( $conn ); + return array( FALSE, $query_result, 0, $query_error ); + } +} + +/******************************************************************************************/ +/* void function pgsql_error( string error ) */ +/* */ +/* prints an error message */ +/******************************************************************************************/ +function pgsql_error( $err ) { + echo "An error occured. Please contact your systemadministrator.
"; + echo "Error: $err"; +} +?> diff --git a/src/web/gnucomo.css b/src/web/gnucomo.css new file mode 100644 index 0000000..f4a27fb --- /dev/null +++ b/src/web/gnucomo.css @@ -0,0 +1,47 @@ + +body +{ + background-color : rgb(80, 120, 100); +} + +h1 +{ + font-family : sans-serif ; + text-align : center ; +} + +h2 +{ + font-family : sans-serif ; +} + +div.login +{ + background : rgb(80, 120, 150) ; + border: solid; + border-width: thick; + border-color : cyan ; + padding : 1em; + margin : 10% ; + width: 80%; + text-align : center ; +} +table +{ + width : 80% ; + border-width : medium ; + border-style : none ; + border-collapse : collapse; + padding : 1px ; +} + +td +{ + font-family : sans-serif ; +} + +th +{ + border-width : thin ; + border-style : solid ; +} diff --git a/src/web/harddisk.png b/src/web/harddisk.png new file mode 100644 index 0000000000000000000000000000000000000000..981b148eb05329741b5379969caf9ac2e9d0db66 GIT binary patch literal 2868 zcmV-43(NG0P)8(l9_YfGw1hzZtr`}3;(z2 zEf$M;z;vHQF=?^>XrjepF|Vhmhh#FzU^0p0x~Qs3ZEY>lXp~qiwkY>k7LGdEitu;b zaR&gZs+P|A{eEt{?Kb-Q`ic=+kcdT(KoLG3k6&_ZB9T~dBoSh z9gR)R{Opqk(dBChH^%T+*5eCS62AN@vgiA7Z5_`x5&jAQp4xX8_Z`1{-Xi3M!nd}z zdfnaKY~8w*uY6eLH{V=ODEa|R+rc*TSb7$`ab44Mk%R(BsG%Ach2lWJLo>#$Vlp zZRNRg<8}_*FumW%Ij%9-9;@TFnP%yG7kX2ksL5*C_hHdxo>O~REG7|F=(AwJSb#-+SkH@+17LS$7 zE<^KcNJ1f_7w&62VCFMemI;8P=kQzyNoXKRAQZ6kSh-PTp&?peAVx|W# zLK+Bv7+KLuNp6gsy*S>+*2jphz6E%#RM<1s#H(|n-<}=8?0JdY}Svt0n#d9nq zSwdEPe0y!0pB&|qB4#CEqUQm=^w}E;)Gonu9TZ;x*LA3AYQd>#BzxiCy#3BO4!-;* z2mYC1BrRb(9;vLv`B8(>cTZ#H9^#61pTn_CJlDdt3_RDv7b;9{Yp!3*2cPfe&D6>n zMO?B5T3T8r!GF*@MO9NPzF-8!7eLhlXu$~nNIjwYW=1az(E0QUe*4@7ZoQ_0N56h8 zZ~paudJ|tI7>@8_-vB>-vC|U?L5FykUqcT{YAXAB$Hicrb*p%=C{b%@C zn?l{HO?Z|8LculixQ+#`gX`LuhC$mBkFZ~m9FuU~ImgI9yEuF1Jb$;de0-&#_w`{` zUUr!xHfS;je~fT@V7-K0A3X)LC=L#LfHVQpI zbX7ZVKX`~AeW8)({&9q@pV+`q`^~)F`yksMcoW<4_|!^4W0i($X0dGt%g7?j0@WV^ zz_hHgdk*xUp|kTPcJJOjy@2uX>(;HKy}g}Nr%v(Wi!bub(@&ONc=ENEakQ$*;RA-U z9xJY1OUBOP8|&xB_+Ccb_iAr-6t-=+lIof|hRz&A)(X(77Q#03D82yscaQTzW=Y9; zN1iXhD@wV~OTWg$x3#s=-rml!W5;M{Xkg2hEo|AcrR>7;Of0uE=T7qUTOQXoMOag% z(YD0n>@ZXY;M3RFa8p|Y$&|%A$rSbO>qKgzsNs4>Qo{fQ{TllYJjckOQ3elS+xBDu zMfkh#zMJOeW|l2m#)%Ur=<4dCsi}#Mjt(TLkh2vP6$AnSEX%@iZ2X!ZAqwp-l}>Z- zz4uaAS4Z25rR>~t6C!t>o4*hQz_oJZN0a>L%yIPLa}1AK*zORvX`uL$SdN1~P=SZV zuV0!BKdk_O6)RSh2K4v$vvK1_+S}XFb)8@^NJT{jLY8q{7a_~&w!zp~j#pnj#uHCG zL0w%Pk#LaPKeK@?9qXy9Z@|i@F*7QXEKmXwjLZeHnNftIQB@fscOk=A-a_)y091wo z5NYIruD?wS|JY-X!MMq+Bw+vk{Uj0zwr}6gqmMj9Bog78Yp$WDx*ErI$>;M74-eDd z-_O2Z?FFE*shPUEI_|pj^Ta;1isr@|WY0$UgE)E)*UTcxa$&aC!Wij6gyKUDMyRcI z@e3E-FmW6Q%d$zOQyhKcFu#9oGCVK|zDz(am*bgdp5gxc@5i=n_CLIj{SQNR{bj)9 zP6UO{@W`F5EWc$Fp6h@bMpXo6K8>9p!8Y=Eu7#}l5&j^uqN4c2g&V7WLREECm?>n( zBySj;85rZf!}^r)U0q$1;L8M@J9n;haPQu|n5Ie3pZ+ijdZOLD=ODeZMB@#gL#u7Z zbU;x9!{5@ON zDvQNB6Fh(b7#bS-!RYAd_v`EHkt7MrvKSj1W5^kFWXJ;m6dds zswQSKJvYXor+Q29x~?Nh5$(Uba9wv%;bETp1I2U#SiXFD>3AZM z0O0iL(*W$+wF}F#P*oMnvKB3E#@oC%I3|j49EWT+TRMOE@Zr+;_X{2txIt7wo^UQo7!& SF{WDp0000@C literal 0 HcmV?d00001 diff --git a/src/web/index.html b/src/web/index.html new file mode 100644 index 0000000..b7a4b12 --- /dev/null +++ b/src/web/index.html @@ -0,0 +1,12 @@ + + + + + +GnuCoMo Web Interface + + + + + + diff --git a/src/web/log.png b/src/web/log.png new file mode 100644 index 0000000000000000000000000000000000000000..b0783b0b3e8a9b4e53d047023e16d4f9222d6adb GIT binary patch literal 2120 zcmV-O2)Fl%P)g0h(pd?IC0<)K>P(-IKvsvaE3EHtPt&ZXrb=+VW5P5;px%K7l-PF)0Le! zo~Co#KWrA>g#6|+yT5$$k>r)fcl&aC5G_8M6xZImG5ym&Z_NLgmUbT~0SmZx+z z5%7(tdf)o_%a8xr1>3>#(&2oF3M9#(hgXvEBtLj#fBNQ|*T;Xqad-BQw5a!;CkN_| zAlDJ$JOA^bqWotSAbtyxrcwSyRoTeWFSu4tSMEOtgn*R?G(Y^0C#Q4o01i$;~m0R17?mj~0 zFlvjU3F##tiQ2?#YND;Xb7(4iz8CRi_MoJ4Actt(7XHC1VjUVs12QDNNe!)bQ+ zbY<=|zB#;glHJNnJ2$nP8tc+F6n#e^?OJSHcSSVo`5po1`|{$eFYO-fZN)@Uf+J?w zkC94~Yz>KbM(Cu6S*4_t1wns6Z{eFQ2iK3X8(GyHEQ;p9L*5~~_FB^pyPoe6aBiTUzp@*@G0Pph z1I?%xap7E#OXo*izBFQIG@u`W$P1LSY+V=;_hJYmOj=WBIo^9*(}0jP7Hn<7HE1mm z0+0fv!h4SjG-Z)dlm)hIsGP%y5aR{L$>}1urw4Z?<1#Hy#<@Mdc5CsMMOOa{SONu5 z+#}%fZurbA-@WwSyoAhpDi@-oh}p`ZrR1@_fGeXAw^}mV?lau!;Tl2Ngv83BO+zEV zAweh_K!*WR1PCcn%8~T@G`1$Ip+SKMCq34K7Yc{OmIfz5G!4yag?A0ZodF-*IzD*y z*Khm+*auF4h3*RYcw!FkW^%Qt{V-b@q;MpOW7N}BMZxWv=i}p)y&ilic1({}RB6R1 z5%^w$at0Cf5K0k6F+HtNAv6eV<8h5gI#4=7Mw%e$^!|Ld8|xyRzNC_Jet5{;}v}lN2 zMLnAk2uErfCgVA)*@~0QvVWNJ_QxsXc{Uu+(wo2pDD~#H;kBF9ucF(Pac~tFX(e?p zlmS%XFjmoaqQc#g*E$eF2q|hOu(lwH0(M89?L-j>h|8Km;yG^{^5u$1YwG?0yeD7f z2oC~@n+7O-O7G$>2t{cf%I5TR&1e96q>*ue6Fs~TB+(Pw@9ZBv3hV-N-61dAd0I96 z9(W(vs~Z;<#sxq!O>OTc^dav{!LS#~t&7|7&ffW*EiGc9dt1a?8i_+`k5>(4Qxi%> zx+*CrE4-I@?}+<7N>kHV&{2p>E5crgjzhExNR^>HDX~tV;|NnjUwHyk1i%h31j4Qd zWV<)Az;Szh3?zUCA|M7rAaWjUow)eq6OX@i>HLn^+v%~>7sS$%XE~;>$%=}sEIF)e zDpO*G#2brNf*>jh;uw#@n+6$4q_ODC5y*-#4hXy@NkZx}<-+cW)WSOlGuLtufY2Ln zJJ$+GfiY0Eq}KveyVl!-=(!)h_}uru`sEA#OIr~dOI4T5R~3z|SS?c)nWZQzYU8LI z2i}4Iq@?QH<6Q+(A%Y06HBv|f5XJ#Xq!8X?5#+|>_m^DW3AlD+T(-O#!0C;*TitE} zbStK6)vmo00IC?W+ zX5EsO9qY5V-Fy1#vwJ~l13o;-$+L>%@r?O0rz~qsU3*hGfAcuEWohf_qBOSg##mQ5 zhw-26X3KUfpu<4)l@xr6ylP3ijg6Y?7)G^u7PVm;wgn^s`t9d);9cNt;3lvT z57rq7F&%DQ#}hhMHUy)y|9jZ+X3{aZ>TcoJu^v yp_kYwi~rYiBk@0e#zS@Jb%ryX;S6W^ulP4{lu)4Y3xK2m0000 + + + + + +GNUCoMo login + + +read("gnucomo"); + //echo "Database Access string = " . $config->Database(); + + $conn = pg_connect($config->Database()); + + // connect to the database + //$conn = pg_Connect( "host=$pgsqlhost port=$pgsqlport dbname=$dbname user=$name password=$passw" ); + if( !$conn ) { + echo "Error connecting, try again."; + echo login_form(); + $_SESSION["login"] = true; + } else { + $sql = "SELECT * FROM user_gnucomo WHERE username='$name' and password='$passw'"; + $res = query( $conn, $sql ); + + if( $res[0] ) { + if ($res[2] == 1) + { + $arr = pg_fetch_array( $res[1], 0 ); + $_SESSION["login.ini"] = $arr; + } + else + { + echo "Login incorrect.
"; + } + + // user is authorised, step to next page. + echo "Ok!"; + } else { + pgsql_error( "SQL: error.
" . $res[3] ); + } + } +} else { + echo login_form(); + $_SESSION["login"] = true; +} +?> + + diff --git a/src/web/menu.html b/src/web/menu.html new file mode 100644 index 0000000..64a9019 --- /dev/null +++ b/src/web/menu.html @@ -0,0 +1,17 @@ + + + + + +GnuCoMo Web Interface + + + Objects + Users + Packages + Change Password + Logout +
+ + + diff --git a/src/web/open_package.png b/src/web/open_package.png new file mode 100644 index 0000000000000000000000000000000000000000..62500ade89ee502e368e644b981b75f5fbbdc78b GIT binary patch literal 2234 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 literal 0 HcmV?d00001 diff --git a/src/web/service.png b/src/web/service.png new file mode 100644 index 0000000000000000000000000000000000000000..41e5f6b7acc9f59b75d44f43a2f97638f99193ee GIT binary patch literal 4800 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 literal 0 HcmV?d00001 -- 2.11.0