From 3171c860a5313feb2c696758097871ee18c4de7e Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Sun, 30 Jun 2024 22:58:00 +0200 Subject: [PATCH] reading and writing config file works --- configuration.cc | 86 +++++++- configuration.h | 16 +- json.cc | 534 +++++++++++++++++++++++++++++++++++++++++++++++ json.h | 80 +++++++ main.cc | 5 +- meson.build | 6 +- 6 files changed, 710 insertions(+), 17 deletions(-) create mode 100644 json.cc create mode 100644 json.h diff --git a/configuration.cc b/configuration.cc index 0587fb4..b5fed0a 100644 --- a/configuration.cc +++ b/configuration.cc @@ -1,7 +1,13 @@ + +#include #include +#include #include +#include + #include "configuration.h" #include "miniwebcam.h" +#include "json.h" Configuration config; @@ -11,6 +17,7 @@ Configuration config; Configuration::Configuration() { http_port = DEFAULT_HTTP_PORT; https_port = DEFAULT_HTTPS_PORT; + filename = DEFAULT_CONFIG_FILE; runasdaemon = 0; initflags = 0; @@ -23,40 +30,99 @@ Configuration::~Configuration() { /* * print current configuration */ -int Configuration::Print() { - printf ("#\n# default ports for http and https\n"); - if (http_port == DEFAULT_HTTP_PORT) printf ("# "); - printf ("http_port %d\n", http_port); - if (https_port == DEFAULT_HTTPS_PORT) printf ("# "); - printf ("https_port %d\n", https_port); - - return 0; +int Configuration::PrintConfig() { + JSONParse jp; + + // + // save current language and set to default C + std::string savedlocale = setlocale(LC_ALL, NULL); + setlocale (LC_ALL, "C"); + + jp.AddObject("http_port", http_port); + jp.AddObject("https_port", https_port); + + // + // output the json string + printf ("%s\n", jp.ToString().c_str()); + + // + // restore language + setlocale (LC_ALL, savedlocale.c_str()); + + return 1; +}; + + +int Configuration::LoadFile(std::string fn) { + JSONParse jp; + int i; + + // + // save current language and set to default C + std::string savedlocale = setlocale(LC_ALL, NULL); + setlocale (LC_ALL, "C"); + + // read from file + if (jp.LoadFromFile(fn) != 0) { + fprintf (stderr, "Could not read json file '%s'. Error: %s\n", fn.c_str(), strerror(errno)); + return 0; + } + + if (jp.GetValueInt("http_port", &i)) http_port = i; + if (jp.GetValueInt("https_port", &i)) https_port = i; + + // + // restore language + setlocale (LC_ALL, savedlocale.c_str()); + + return 1; } + int Configuration::LoadArgs(int argc, char **argv) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-F") == 0) runasdaemon = 0; if (strcmp(argv[i], "-D") == 0) runasdaemon = 1; if (strcmp(argv[i], "-dump_config") == 0) initflags |= CONF_INITFLAGS_PRINT; + if (strcmp(argv[i], "-config") == 0) { + if (++i < argc) { + filename = argv[i]; + } + else + ErrorExit("config file missing", -1); + } if (strcmp(argv[i], "-h") == 0) initflags |= CONF_INITFLAGS_HELP; if (strcmp(argv[i], "-http_port") == 0) { if (++i < argc) { + http_port = atoi(argv[i]); + } + else + ErrorExit("missing port parameter", -1); + } + if (strcmp(argv[i], "-https_port") == 0) { + if (++i < argc) { + https_port = atoi(argv[i]); } else ErrorExit("missing port parameter", -1); } } - return 0; -} + return 1; +}; + void Configuration::Help() { printf ("Parameters:\n"); printf (" -F run in foreground\n"); printf (" -D run as daemon\n"); printf ("\n"); + printf (" -http_port INT port to listen for http connections\n"); + printf (" -https_port INT port to listen for https connections\n"); + printf ("\n"); + printf (" -config FILE load this configfile\n"); printf (" -dump_config print the config file\n"); printf ("\n"); printf (" -H print this help\n"); diff --git a/configuration.h b/configuration.h index 85a5aa5..0294d11 100644 --- a/configuration.h +++ b/configuration.h @@ -2,8 +2,11 @@ #ifndef _CONFIGURATION_H_ #define _CONFIGURATION_H_ +#include + #define DEFAULT_HTTP_PORT 10080 #define DEFAULT_HTTPS_PORT 10081 +#define DEFAULT_CONFIG_FILE "/etc/miniwebcam.conf.json" #define CONF_INITFLAGS_PRINT 0x0001 #define CONF_INITFLAGS_HELP 0x0002 @@ -13,18 +16,23 @@ private: int http_port; int https_port; int runasdaemon; - + std::string filename; int initflags; - public: Configuration(); ~Configuration(); int LoadArgs(int argc, char **argv); - int Print(); // print current configuration - void Help(); // print Help + int LoadFile(std::string fn); + int GetInitFlags() { return initflags; }; + std::string GetFilename() { return filename; }; + + int PrintConfig(); // print current configuration + void Help(); // print Help + // + }; extern Configuration config; diff --git a/json.cc b/json.cc new file mode 100644 index 0000000..fad7399 --- /dev/null +++ b/json.cc @@ -0,0 +1,534 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json.h" + +/*********************************************************************** + *********************************************************************** + * + * JSONParse + * + *********************************************************************** + *********************************************************************** + */ + +enum { + STEP_NONE = 0, + STEP_STARTNAME, + STEP_NAME, + STEP_STARTVALUE, + STEP_VALUE, + STEP_END +}; + + +/* + * clear out all data + */ +void JSONParse::Clear() { + jsondata = ""; + names.clear(); +} + +/* + * read every element and keep only this in memory. + */ +int JSONParse::Set(string json) { + int i; + int step; + int level; + bool ignorenext; + + JSONElement jelement; + + Clear(); + + // find start and read until end + for (step = STEP_NONE, i = 0, ignorenext = false; (unsigned int)i < json.length(); i++) { + // need to copy next character + if (ignorenext) { + ignorenext = false; + if (step == STEP_NAME) jelement.name += json[i]; + if (step == STEP_VALUE) jelement.value += json[i]; + } + + // searching for startname + else if (step == STEP_NONE) { + if (json[i] == '{') { + step = STEP_STARTNAME; + continue; + } + } + + // searching for startname + else if (step == STEP_STARTNAME) { + if (json[i] == '"') { + step = STEP_NAME; + continue; + } + } + + // copy name + else if (step == STEP_NAME) { + if (json[i] == '"') { + step = STEP_STARTVALUE; + continue; + } + else { + jelement.name += json[i]; + continue; + } + } + + // searching for startvalue + else if (step == STEP_STARTVALUE) { + if (json[i] == '"') { + step = STEP_VALUE; + jelement.type = JSON_T_STRING; + continue; + } + if (json[i] == '{') { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_OBJECT; + jelement.value = "{"; + continue; + } + if (json[i] == '[') { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_ARRAY; + jelement.value = "["; + continue; + } + if ((json[i] >= '0' && json[i] <= '9') || + (json[i] == '+' || json[i] == '-')) { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_NUMBER; + jelement.value = json[i]; + continue; + } + } + + // copy value + else if (step == STEP_VALUE) { + if (jelement.type == JSON_T_STRING) { + if (json[i] == '"') step = STEP_END; + else jelement.value += json[i]; + continue; + } + else if (jelement.type == JSON_T_OBJECT) { + if (json[i] == '}' && level == 0) { + jelement.value += json[i]; + step = STEP_END; + } + else { + if (json[i] == '{') level++; // increase level + if (json[i] == '}') level--; // decrease level + jelement.value += json[i]; + } + continue; + } + else if (jelement.type == JSON_T_ARRAY) { + if (json[i] == ']' && level == 0) { + jelement.value += json[i]; + step = STEP_END; + } + else { + if (json[i] == '[') level++; // increase level + if (json[i] == ']') level--; // decrease level + jelement.value += json[i]; + } + continue; + } + else if (jelement.type == JSON_T_NUMBER) { + if ((json[i] < '0' || json[i] > '9') && json[i] != '.' && + json[i] != '+' && json[i] != 'e' && json[i] != 'E') step = STEP_END; + else { + jelement.value += json[i]; + continue; + } + } + } + + // another element? + if (step == STEP_END) { + if (json[i] == ',') { + if (jelement.type != JSON_T_NONE) { + names.push_back (jelement); + } + jelement.Clear(); + step = STEP_STARTNAME; + } + continue; + } + } + if (jelement.type != JSON_T_NONE) { + names.push_back (jelement); + } + + return 0; +}; + + +int JSONParse::GetValue(string varname, string *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + *dest = ""; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + *dest = iter->value; + return 1; + } + } + + return 0; +}; + + +int JSONParse::GetValueInt(string varname, int *dest) { + string s; + int res = GetValue(varname, &s); + if (res) { + *dest = atoi (s.c_str()); + return 1; + } + return 0; +}; + + +int JSONParse::GetValueDouble(string varname, double *dest) { + string s; + int res = GetValue(varname, &s); + if (res) { + *dest = atof (s.c_str()); + return 1; + } + return 0; +}; + + +int JSONParse::GetValueInt64(string varname, int64_t *dest) { + string s; + int res = GetValue(varname, &s); + if (res) { + *dest = atol (s.c_str()); + return 1; + } + return 0; +}; + + +int JSONParse::GetObjectJson(string varname, JSONParse *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + dest->Set(iter->value); + return 1; + } + } + + return 0; +}; + + +#define MAXRECURSIVE 255 +int JSONParse::GetIdx(string src, int idx, string *dest) { + char recursive[MAXRECURSIVE]; + int i = 0, rcnt = 0, cnt = 0; + + (*dest) = ""; + + for (i = 0; i < MAXRECURSIVE; i++) recursive[i] = 0; + for (i = 0; (unsigned int) i < src.length() && rcnt < MAXRECURSIVE && cnt <= idx; i++) { + if (src[i] == '[') { + recursive[rcnt++] = src[i]; + continue; + } + else if (src[i] == '{' && recursive[rcnt] != '"') recursive[++rcnt] = src[i]; + else if (src[i] == '}' && recursive[rcnt] == '{') rcnt--; + else if (src[i] == '"' && recursive[rcnt] == '"') rcnt--; + else if (src[i] == '"') recursive[++rcnt] = src[i]; + else if (src[i] == ',' && rcnt == 1) { + cnt++; + continue; + } + else if (src[i] == ']' && rcnt == 1) { + continue; + } + + if (rcnt > 0 && cnt == idx) { + (*dest) += src[i]; + if (src[i] == '\\') (*dest) += src[i]; + } + else { + if (src[i] == '\\')i++; + } + } + + // + // final checks + if (cnt == 0 && idx == 0 && // empty source/array? + dest->size() == 0) return 0; // + if (cnt >= idx) return 1; // found the right element + return 0; // element not found +} +#undef MAXRECURSIVE + +int JSONParse::GetValueIdx(string varname, int idx, string *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + return GetIdx(iter->value, idx, dest); + } + } + + return 0; +}; + +int JSONParse::GetObjectIdx(string varname, int idx, JSONParse *dest) { + list::iterator iter; + string deststr; + int ret = 0; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + ret = GetIdx(iter->value, idx, &deststr); + if (ret == 1) dest->Set(deststr); + return ret; + } + } + + return 0; +}; + +list JSONParse::GetElements() { + list l; + list::iterator iter; + + l.clear(); + for (iter = names.begin(); iter != names.end(); iter++) { + l.push_back(*iter); + } + + return l; +}; + + +void JSONParse::AddObject (JSONElement element) { + names.push_back (element); +}; + + +void JSONParse::AddObject (string name, JSONParse jp) { + JSONElement je; + je.SetObject(name, jp.ToString()); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, int val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, int64_t val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, string val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, double val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +string JSONParse::ToString() { + list::iterator iter; + string output; + int level, i; + + output = "{"; + + for (level = 1, iter = names.begin(); iter != names.end(); iter++) { + if (iter != names.begin()) output += ","; + output += "\n"; + for (i = 0; i < 4*level; i++) output += " "; + output += iter->GetString(); + } + + output += "\n}\n"; + + return output; +}; + + + +/* + * Load/Save elements to a file. + * Return Value: -1 .. on Error, errno will be set + * 0 .. on Success + */ +int JSONParse::LoadFromFile(string filename) { + int fd; + struct stat fs; + char *buffer; + + if (stat(filename.c_str(), &fs) != 0) return -1; + buffer = (char *) malloc (fs.st_size+1); + memset (buffer, 0x0, fs.st_size+1); + + fd = open(filename.c_str(), O_RDONLY); + if (fd < 0) return -1; + read (fd, buffer, fs.st_size); + close (fd); + + Set(buffer); + free (buffer); + + return 0; +}; + +int JSONParse::SaveToFile(string filename) { + ofstream out(filename); + + if (!out) return -1; + + out << ToString(); + out.close(); + + return 0; +}; + + + + +/*********************************************************************** + *********************************************************************** + * + * JSONElement + * + *********************************************************************** + *********************************************************************** + */ + + +void JSONElement::Set (string n, double v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, int v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, int64_t v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, string v) { + name = n; + value = v; + type = JSON_T_STRING; +}; + + +void JSONElement::SetArray (string n, list *l) { + list::iterator iter; + + name = n; + value = "["; + type = JSON_T_STRING; + + for (iter = l->begin(); iter != l->end(); iter++) { + if (iter != l->begin()) value += ","; + value += iter->GetString(); + } + value += "]"; +}; + + +void JSONElement::SetObject (string n, string s) { + name = n; + value = s; + type = JSON_T_OBJECT; +}; + + +string JSONElement::GetString () { + string output = ""; + string filename = __FILE__; + + switch (type) { + case(JSON_T_NUMBER): + output += "\"" + name + "\" : " + value; + break; + case(JSON_T_STRING): + if (value.length()==0) { + output += "\"" + name + "\" : \"\""; + } + else if (value[0] != '"') { + output += "\"" + name + "\" : \"" + value + "\""; + } + else output += "\"" + name + "\" : " + value; + break; + case(JSON_T_OBJECT): + output += "\"" + name + "\" : " + value; + break; + case(JSON_T_ARRAY): + if (value.length()==0) { + output += "\"" + name + "\" : []"; + } + else if (value[0] != '[') { + output += "\"" + name + "\" : [" + value + "]"; + } + else output += "\"" + name + "\" : " + value; + break; + default: + output += "\"error\" : \""+ filename + ":" + to_string(__LINE__) + " JSONElement unknown type error\"("+to_string(type)+")"; + break; + } + + return output; +}; diff --git a/json.h b/json.h new file mode 100644 index 0000000..69080fd --- /dev/null +++ b/json.h @@ -0,0 +1,80 @@ + +// +// +// + +#ifndef _JSON_H_ +#define _JSON_H_ + +#include +#include +#include + +using namespace std; + +enum { + JSON_T_NONE, + JSON_T_STRING, + JSON_T_NUMBER, + JSON_T_OBJECT, + JSON_T_ARRAY +}; + +class JSONElement { +public: + int type; + string name; + string value; + + JSONElement() { Clear(); }; + ~JSONElement() {}; + + void Clear() { type = JSON_T_NONE; name = ""; value = ""; }; + void Set (string n, double v); + void Set (string n, int v); + void Set (string n, int64_t v); + void Set (string n, string v); + void SetArray (string n, list *l); + void SetObject (string n, string s); + string GetString(); +}; + +class JSONParse { +private: + string jsondata; + list names; + +public: + JSONParse() { Set("{}"); }; + JSONParse(string json) { Set(json); }; + ~JSONParse() {}; + + void Clear(); + int Set(string json); + + int GetValue(string varname, string *dest); + int GetValueInt(string varname, int *dest); + int GetValueDouble(string varname, double *dest); + int GetValueInt64(string varname, int64_t *dest); + int GetObjectJson(string varname, JSONParse *dest); + + int GetIdx(string src, int idx, string *dest); + int GetValueIdx(string varname, int idx, string *dest); + int GetObjectIdx(string varname, int idx, JSONParse *dest); + + list GetElements(); + + void AddObject (JSONElement element); + void AddObject (string name, int val); + void AddObject (string name, int64_t val); + void AddObject (string name, string val); + void AddObject (string name, double val); + void AddObject (string name, JSONParse jp); + + int LoadFromFile(string filename); + int SaveToFile(string filename); + + string ToString(); +}; + +#endif // _JSON_H_ diff --git a/main.cc b/main.cc index e311d1e..6df6fc0 100644 --- a/main.cc +++ b/main.cc @@ -1,7 +1,9 @@ #include #include + #include + #include "configuration.h" #include "miniwebcam.h" @@ -12,8 +14,9 @@ void ErrorExit(std::string text, int errorcode) { int main(int argc, char **argv) { config.LoadArgs (argc, argv); + config.LoadFile (config.GetFilename()); if (config.GetInitFlags() & CONF_INITFLAGS_PRINT) { - config.Print(); + config.PrintConfig(); return 0; } diff --git a/meson.build b/meson.build index ea77bfa..60e0e79 100644 --- a/meson.build +++ b/meson.build @@ -6,12 +6,14 @@ project('miniwebcam', 'cpp', default_options: [ miniwebcam_src = [ 'main.cc', 'configuration.cc', - 'webserver.cc' + 'webserver.cc', + 'json.cc' ] miniwebcam_headers = [ 'configuration.h', - 'miniwebcam.h' + 'miniwebcam.h', + 'json.h' ]