Implemented MXML for password file

SVN revision: 1225
This commit is contained in:
Stefan Ritt 2005-03-01 23:48:18 +00:00
parent 7469ee06c4
commit 737dd7a227
3 changed files with 1336 additions and 281 deletions

View File

@ -6,6 +6,9 @@
Contents: Web server program for Electronic Logbook ELOG Contents: Web server program for Electronic Logbook ELOG
$Log$ $Log$
Revision 1.572 2005/03/01 23:48:17 ritt
Implemented MXML for password file
Revision 1.571 2005/02/22 09:34:04 ritt Revision 1.571 2005/02/22 09:34:04 ritt
Fixed bug with logbook names containing blanks and 'List' link Fixed bug with logbook names containing blanks and 'List' link
@ -932,6 +935,7 @@ char pidfile[256]; /* Pidfile name
/* local includes */ /* local includes */
#include "regex.h" #include "regex.h"
#include "mxml.h"
BOOL running_as_daemon; /* Running as a daemon/service? */ BOOL running_as_daemon; /* Running as a daemon/service? */
int elog_tcp_port = (int) DEFAULT_PORT; /* Server's TCP port */ int elog_tcp_port = (int) DEFAULT_PORT; /* Server's TCP port */
@ -1195,7 +1199,7 @@ int run_service(void);
#endif #endif
void show_error(char *error); void show_error(char *error);
BOOL enum_user_line(LOGBOOK * lbs, int n, char *user); BOOL enum_user_line(LOGBOOK * lbs, int n, char *user, int size);
int get_user_line(char *logbook_name, char *user, char *password, char *full_name, int get_user_line(char *logbook_name, char *user, char *password, char *full_name,
char *email, char *email_notify); char *email, char *email_notify);
int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk); int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk);
@ -1361,8 +1365,6 @@ char *xstrdup(const char *string)
return s; return s;
} }
/*------------------------------------------------------------------*/
/*----------------------- Message handling -------------------------*/ /*----------------------- Message handling -------------------------*/
/* Have vasprintf? (seems that only libc6 based Linux has this) */ /* Have vasprintf? (seems that only libc6 based Linux has this) */
@ -3568,7 +3570,7 @@ void retrieve_email_from(LOGBOOK * lbs, char *ret, char attrib[MAX_N_ATTR][NAME_
/* if nothing available, figure out email from an administrator */ /* if nothing available, figure out email from an administrator */
if (strchr(str, '@') == NULL) { if (strchr(str, '@') == NULL) {
for (i = 0;; i++) { for (i = 0;; i++) {
if (!enum_user_line(lbs, i, login_name)) if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
break; break;
get_user_line(lbs->name, login_name, NULL, NULL, str, NULL); get_user_line(lbs->name, login_name, NULL, NULL, str, NULL);
if (is_admin_user(lbs->name, login_name) && strchr(str, '@')) if (is_admin_user(lbs->name, login_name) && strchr(str, '@'))
@ -7254,9 +7256,8 @@ void add_subst_time(LOGBOOK * lbs,
BOOL change_pwd(LOGBOOK * lbs, char *user, char *pwd) BOOL change_pwd(LOGBOOK * lbs, char *user, char *pwd)
{ {
char str[256], file_name[256], line[256], *p, *pl; char str[256], file_name[256];
char *buf; PMXML_NODE xml_tree, node;
int i, fh, size;
getcfg(lbs->name, "Password file", str, sizeof(str)); getcfg(lbs->name, "Password file", str, sizeof(str));
@ -7267,75 +7268,23 @@ BOOL change_pwd(LOGBOOK * lbs, char *user, char *pwd)
strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name));
} }
fh = open(file_name, O_RDWR | O_BINARY, 644); xml_tree = mxml_parse_file(file_name, str, sizeof(str));
if (fh > 0) { if (!xml_tree)
lseek(fh, 0, SEEK_END); return FALSE;
size = TELL(fh);
lseek(fh, 0, SEEK_SET);
buf = xmalloc(size + 1); sprintf(str, "/list/user[name=%s]/password", user);
read(fh, buf, size); node = mxml_find_node(xml_tree, str);
buf[size] = 0; if (node == NULL) {
pl = buf; mxml_free_tree(xml_tree);
return FALSE;
while (pl < buf + size) {
for (i = 0; pl[i] && pl[i] != '\r' && pl[i] != '\n'; i++)
line[i] = pl[i];
line[i] = 0;
if (line[0] == ';' || line[0] == '#' || line[0] == 0) {
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
continue;
}
strcpy(str, line);
if (strchr(str, ':'))
*strchr(str, ':') = 0;
if (strcmp(str, user) == 0)
break;
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
}
/* return if not found */
if (pl >= buf + size) {
xfree(buf);
close(fh);
return FALSE;
}
p = strchr(line, ':');
if (p)
p = strchr(p + 1, ':');
if (p == NULL)
return FALSE;
/* replace password */
lseek(fh, 0, SEEK_SET);
write(fh, buf, pl - buf);
sprintf(str, "%s:%s%s\n", user, pwd, p);
write(fh, str, strlen(str));
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
write(fh, pl, strlen(pl));
TRUNCATE(fh);
xfree(buf);
close(fh);
return TRUE;
} }
return FALSE; mxml_replace_node_value(node, pwd);
mxml_write_tree(file_name, xml_tree);
mxml_free_tree(xml_tree);
return TRUE;
} }
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
@ -8347,7 +8296,7 @@ void show_edit_form(LOGBOOK * lbs, int message_id, BOOL breply, BOOL bedit, BOOL
rsprintf("<option value=\"\">- %s -\n", loc("please select")); rsprintf("<option value=\"\">- %s -\n", loc("please select"));
for (i = 0;; i++) { for (i = 0;; i++) {
if (!enum_user_line(lbs, i, login_name)) if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
break; break;
get_user_line(lbs->name, login_name, NULL, str, NULL, NULL); get_user_line(lbs->name, login_name, NULL, str, NULL, NULL);
@ -9927,10 +9876,11 @@ int save_config(char *buffer, char *error)
int save_user_config(LOGBOOK * lbs, char *user, BOOL new_user, BOOL activate) int save_user_config(LOGBOOK * lbs, char *user, BOOL new_user, BOOL activate)
{ {
char file_name[256], str[256], line[256], *buf, *pl, new_pwd[80], new_pwd2[80]; char file_name[256], str[256], *pl, new_pwd[80], new_pwd2[80];
char smtp_host[256], email_addr[256], mail_from[256], subject[256], mail_text[2000]; char smtp_host[256], email_addr[256], mail_from[256], subject[256], mail_text[2000];
char admin_user[80], enc_pwd[80], url[256]; char admin_user[80], enc_pwd[80], url[256];
int i, fh, size, self_register; int self_register;
PMXML_NODE xml_tree, node;
/* check for full name */ /* check for full name */
if (!isparam("new_full_name") || *getparam("new_full_name") == 0) { if (!isparam("new_full_name") || *getparam("new_full_name") == 0) {
@ -9979,92 +9929,50 @@ int save_user_config(LOGBOOK * lbs, char *user, BOOL new_user, BOOL activate)
strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name));
} }
fh = open(file_name, O_RDWR | O_BINARY | O_CREAT, 0644); xml_tree = mxml_parse_file(file_name, str, sizeof(str));
if (fh < 0) { if (xml_tree == NULL) {
sprintf(str, loc("Cannot open file <b>%s</b>"), file_name);
strcat(str, ": ");
strlcat(str, strerror(errno), sizeof(str));
show_error(str); show_error(str);
return 0; return 0;
} }
sprintf(str, "/list/user[name=%s]", user);
node = mxml_find_node(xml_tree, str);
lseek(fh, 0, SEEK_END); if (node && new_user) {
size = TELL(fh); sprintf(str, "%s \"%s\" %s", loc("Login name"), user, loc("exists already"));
lseek(fh, 0, SEEK_SET); show_error(str);
mxml_free_tree(xml_tree);
buf = xmalloc(size + 1); return 0;
read(fh, buf, size);
buf[size] = 0;
pl = buf;
while (pl < buf + size) {
for (i = 0; pl[i] && pl[i] != '\r' && pl[i] != '\n'; i++)
line[i] = pl[i];
line[i] = 0;
/* skip outcommented lines */
if (line[0] == ';' || line[0] == '#') {
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
continue;
}
if (line[0] == 0)
break;
strcpy(str, line);
if (strchr(str, ':'))
*strchr(str, ':') = 0;
if (strcmp(str, user) == 0) {
if (new_user) {
sprintf(str, "%s \"%s\" %s", loc("Login name"), user, loc("exists already"));
show_error(str);
xfree(buf);
close(fh);
return 0;
}
break;
}
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
} }
if (new_user) { if (new_user) {
lseek(fh, 0, SEEK_END); node = mxml_add_node(xml_tree, "user", NULL);
if (strlen(buf) != 0 && (buf[strlen(buf) - 1] != '\r' && buf[strlen(buf) - 1] != '\n'))
write(fh, "\n", 1); if (activate) {
mxml_add_node(node, "name", getparam("new_user_name"));
mxml_add_node(node, "password", getparam("encpwd"));
mxml_add_node(node, "full_name", getparam("new_full_name"));
mxml_add_node(node, "email", getparam("new_user_email"));
mxml_add_node(node, "email_notify", getparam("email_notify"));
} else {
mxml_add_node(node, "name", getparam("new_user_name"));
mxml_add_node(node, "password", new_pwd);
mxml_add_node(node, "full_name", getparam("new_full_name"));
mxml_add_node(node, "email", getparam("new_user_email"));
mxml_add_node(node, "email_notify", getparam("email_notify"));
}
if (activate)
sprintf(str, "%s:%s:%s:%s:%s\n", getparam("new_user_name"),
getparam("encpwd"), getparam("new_full_name"),
getparam("new_user_email"), getparam("email_notify"));
else
sprintf(str, "%s:%s:%s:%s:%s\n", getparam("new_user_name"),
new_pwd, getparam("new_full_name"), getparam("new_user_email"), getparam("email_notify"));
write(fh, str, strlen(str));
} else { } else {
/* replace line */ /* replace record */
lseek(fh, 0, SEEK_SET); mxml_replace_subvalue(node, "name", getparam("new_user_name"));
write(fh, buf, pl - buf); mxml_replace_subvalue(node, "password", new_pwd);
mxml_replace_subvalue(node, "full_name", getparam("new_full_name"));
sprintf(str, "%s:%s:%s:%s:%s\n", getparam("new_user_name"), mxml_replace_subvalue(node, "email", getparam("new_user_email"));
new_pwd, getparam("new_full_name"), getparam("new_user_email"), getparam("email_notify")); mxml_replace_subvalue(node, "email_notify", getparam("email_notify"));
write(fh, str, strlen(str));
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
write(fh, pl, strlen(pl));
TRUNCATE(fh);
} }
xfree(buf); mxml_write_tree(file_name, xml_tree);
close(fh); mxml_free_tree(xml_tree);
} }
/* if requested, send notification email to admin user */ /* if requested, send notification email to admin user */
@ -10199,8 +10107,8 @@ int save_user_config(LOGBOOK * lbs, char *user, BOOL new_user, BOOL activate)
int remove_user(LOGBOOK * lbs, char *user) int remove_user(LOGBOOK * lbs, char *user)
{ {
char file_name[256], str[256], line[256], *buf, *pl; char file_name[256], str[256];
int i, fh, size; PMXML_NODE xml_tree, node;
getcfg(lbs->name, "Password file", str, sizeof(str)); getcfg(lbs->name, "Password file", str, sizeof(str));
@ -10211,63 +10119,26 @@ int remove_user(LOGBOOK * lbs, char *user)
strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name));
} }
fh = open(file_name, O_RDWR | O_BINARY, 0644); xml_tree = mxml_parse_file(file_name, str, sizeof(str));
if (fh < 0) { if (xml_tree == NULL)
sprintf(str, loc("Cannot open file <b>%s</b>"), file_name); return FALSE;
strcat(str, ": ");
strcat(str, strerror(errno)); sprintf(str, "/list/user[name=%s]", user);
show_error(str); node = mxml_find_node(xml_tree, str);
return 0; if (node == NULL) {
mxml_free_tree(xml_tree);
return FALSE;
} }
lseek(fh, 0, SEEK_END); mxml_delete_node(node);
size = TELL(fh);
lseek(fh, 0, SEEK_SET);
buf = xmalloc(size + 1); if (!mxml_write_tree(file_name, xml_tree)) {
read(fh, buf, size); mxml_free_tree(xml_tree);
buf[size] = 0; return FALSE;
pl = buf;
while (pl < buf + size) {
for (i = 0; pl[i] && pl[i] != '\r' && pl[i] != '\n'; i++)
line[i] = pl[i];
line[i] = 0;
if (line[0] == ';' || line[0] == '#' || line[0] == 0) {
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
continue;
}
strcpy(str, line);
if (strchr(str, ':'))
*strchr(str, ':') = 0;
if (strcmp(str, user) == 0)
break;
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
} }
/* remove line */ mxml_free_tree(xml_tree);
lseek(fh, 0, SEEK_SET); return TRUE;
write(fh, buf, pl - buf);
pl += strlen(line);
while (*pl && (*pl == '\r' || *pl == '\n'))
pl++;
write(fh, pl, strlen(pl));
TRUNCATE(fh);
xfree(buf);
close(fh);
return 1;
} }
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
@ -10327,7 +10198,7 @@ void show_config_page(LOGBOOK * lbs)
/* count user list */ /* count user list */
for (n = 0;; n++) { for (n = 0;; n++) {
if (!enum_user_line(lbs, n, str)) if (!enum_user_line(lbs, n, str, sizeof(str)))
break; break;
} }
@ -10337,7 +10208,7 @@ void show_config_page(LOGBOOK * lbs)
user_list[i] = xcalloc(NAME_LENGTH, 1); user_list[i] = xcalloc(NAME_LENGTH, 1);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
enum_user_line(lbs, i, user_list[i]); enum_user_line(lbs, i, user_list[i], NAME_LENGTH);
/* sort list */ /* sort list */
qsort(user_list, n, sizeof(char *), ascii_compare); qsort(user_list, n, sizeof(char *), ascii_compare);
@ -10422,7 +10293,7 @@ void show_forgot_pwd_page(LOGBOOK * lbs)
strcpy(name, getparam("login_name")); strcpy(name, getparam("login_name"));
for (i = 0;; i++) { for (i = 0;; i++) {
if (!enum_user_line(lbs, i, login_name)) if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
break; break;
get_user_line(lbs->name, login_name, NULL, full_name, user_email, NULL); get_user_line(lbs->name, login_name, NULL, full_name, user_email, NULL);
@ -17122,7 +16993,7 @@ void submit_elog(LOGBOOK * lbs)
|| atoi(str) == 0) { || atoi(str) == 0) {
/* go through password file */ /* go through password file */
for (index = 0;; index++) { for (index = 0;; index++) {
if (!enum_user_line(lbs, index, user)) if (!enum_user_line(lbs, index, user, sizeof(user)))
break; break;
get_user_line(lbs->name, user, NULL, NULL, user_email, email_notify); get_user_line(lbs->name, user, NULL, NULL, user_email, email_notify);
@ -18373,12 +18244,105 @@ BOOL check_password(LOGBOOK * lbs, char *name, char *password, char *redir)
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
BOOL convert_password_file(char *file_name)
{
char name[256], password[256], full_name[256], email[256], email_notify[256];
int i, len, fh;
char *buf, *p;
PMXML_NODE root, list, node;
fh = open(file_name, O_RDONLY | O_TEXT);
if (fh < 0)
return FALSE;
len = lseek(fh, 0, SEEK_END);
lseek(fh, 0, SEEK_SET);
buf = malloc(len+1);
assert(buf);
i = read(fh, buf, len);
buf[i] = 0;
close(fh);
p = buf;
/* skip leading spaces or new lines */
while (*p && isspace(*p))
p++;
root = mxml_create_root_node();
list = mxml_add_node(root, "list", NULL);
while (*p) {
/* skip comment lines */
if (*p != ';' && *p != '#') {
for (i=0 ; i<sizeof(name) && *p && *p != ':' ; i++)
name[i] = *p++;
name[i] = 0;
if (*p++ != ':') {
free(buf);
return FALSE;
}
for (i=0 ; i<sizeof(password) && *p && *p != ':' ; i++)
password[i] = *p++;
password[i] = 0;
if (*p++ != ':') {
free(buf);
return FALSE;
}
for (i=0 ; i<sizeof(full_name) && *p && *p != ':' ; i++)
full_name[i] = *p++;
full_name[i] = 0;
if (*p++ != ':') {
free(buf);
return FALSE;
}
for (i=0 ; i<sizeof(email) && *p && *p != ':' ; i++)
email[i] = *p++;
email[i] = 0;
if (*p++ != ':') {
free(buf);
return FALSE;
}
for (i=0 ; i<sizeof(email_notify) && *p && *p != '\n' ; i++)
email_notify[i] = *p++;
email_notify[i] = 0;
if (*p++ != '\n') {
free(buf);
return FALSE;
}
node = mxml_add_node(list, "user", NULL);
mxml_add_node(node, "name", name);
mxml_add_node(node, "password", password);
mxml_add_node(node, "full_name", full_name);
mxml_add_node(node, "email", email);
mxml_add_node(node, "email_notify", email_notify);
}
while (*p && isspace(*p))
p++;
}
mxml_write_tree(file_name, root);
mxml_free_tree(root);
free(buf);
return TRUE;
}
/*------------------------------------------------------------------*/
int get_user_line(char *logbook_name, char *user, char *password, char *full_name, int get_user_line(char *logbook_name, char *user, char *password, char *full_name,
char *email, char *email_notify) char *email, char *email_notify)
{ {
char str[256], line[256], file_name[256], *p; char str[256], line[256], file_name[256];
FILE *f; FILE *f;
int i, fd; int fd;
PMXML_NODE xml_tree, user_node, node;
if (password) if (password)
password[0] = 0; password[0] = 0;
@ -18418,51 +18382,37 @@ int get_user_line(char *logbook_name, char *user, char *password, char *full_nam
return 1; return 1;
} }
while (!feof(f)) { /* check if in XML format, otherwise convert it */
line[0] = 0; line[0] = 0;
fgets(line, sizeof(line), f); fgets(line, sizeof(line), f);
if (strstr(line, "<?xml") == 0) {
if (line[0] == ';' || line[0] == '#' || line[0] == 0) fclose(f);
continue; if (!convert_password_file(file_name))
return 1;
strcpy(str, line);
if (strchr(str, ':'))
*strchr(str, ':') = 0;
if (strcmp(str, user) == 0)
break;
} }
fclose(f);
if (strcmp(str, user) != 0) if ((xml_tree = mxml_parse_file(file_name, str, sizeof(str))) == NULL) {
show_error(str);
return 1;
}
sprintf(str, "/list/user[name=%s]", user);
if ((user_node = mxml_find_node(xml_tree, str)) == NULL) {
mxml_free_tree(xml_tree);
return 2; return 2;
}
/* if user found, retrieve other info */ /* if user found, retrieve other info */
p = line; if ((node = mxml_find_node(user_node, "password")) != NULL && password && mxml_get_value(node))
strlcpy(password, mxml_get_value(node), 256);
for (i = 0; i < 4; i++) { if ((node = mxml_find_node(user_node, "full_name")) != NULL && full_name && mxml_get_value(node))
if (strchr(p, ':') == NULL) strlcpy(full_name, mxml_get_value(node), 256);
break; if ((node = mxml_find_node(user_node, "email")) != NULL && email && mxml_get_value(node))
p = strchr(p, ':') + 1; strlcpy(email, mxml_get_value(node), 256);
if ((node = mxml_find_node(user_node, "email_notify")) != NULL && email_notify && mxml_get_value(node))
while (*p && *p == ' ') strlcpy(email_notify, mxml_get_value(node), 256);
p++;
strlcpy(str, p, sizeof(str));
if (strchr(str, ':'))
*strchr(str, ':') = 0;
while (str[strlen(str) - 1] == ' ' || str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = 0;
if (i == 0 && password)
strcpy(password, str);
else if (i == 1 && full_name)
strcpy(full_name, str);
else if (i == 2 && email)
strcpy(email, str);
else if (i == 3 && email_notify)
strcpy(email_notify, str);
}
mxml_free_tree(xml_tree);
return 1; return 1;
} else { } else {
if (user[0]) if (user[0])
@ -18474,11 +18424,10 @@ int get_user_line(char *logbook_name, char *user, char *password, char *full_nam
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
BOOL enum_user_line(LOGBOOK * lbs, int n, char *user) BOOL enum_user_line(LOGBOOK * lbs, int n, char *user, int size)
{ {
char str[256], line[256], file_name[256]; char str[256], file_name[256];
FILE *f; PMXML_NODE xml_tree, node;
int i;
getcfg(lbs->name, "Password file", str, sizeof(str)); getcfg(lbs->name, "Password file", str, sizeof(str));
@ -18492,35 +18441,20 @@ BOOL enum_user_line(LOGBOOK * lbs, int n, char *user)
strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name));
} }
f = fopen(file_name, "r"); if ((xml_tree = mxml_parse_file(file_name, str, sizeof(str))) == NULL) {
i = 0; show_error(str);
if (f != NULL) { return FALSE;
while (!feof(f)) {
line[0] = 0;
fgets(line, sizeof(line), f);
if (line[0] == ';' || line[0] == '#' || line[0] == 0)
continue;
if (strchr(line, ':') == NULL)
continue;
strcpy(str, line);
if (strchr(str, ':'))
*strchr(str, ':') = 0;
if (i == n) {
strcpy(user, str);
fclose(f);
return TRUE;
}
i++;
}
fclose(f);
} }
return FALSE; sprintf(str, "/list/user[%d]/name", n);
if ((node = mxml_find_node(xml_tree, str)) == NULL) {
mxml_free_tree(xml_tree);
return FALSE;
}
strlcpy(user, mxml_get_value(node), size);
mxml_free_tree(xml_tree);
return TRUE;
} }
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/

1050
src/mxml.c Executable file

File diff suppressed because it is too large Load Diff

71
src/mxml.h Executable file
View File

@ -0,0 +1,71 @@
/********************************************************************\
Name: mxml.h
Created by: Stefan Ritt
Contents: Header file for mxml.c
$Log$
Revision 1.1 2005/03/01 23:48:18 ritt
Implemented MXML for password file
\********************************************************************/
/*------------------------------------------------------------------*/
#define MXML_NAME_LENGTH 64
typedef struct {
int fh;
int level;
BOOL element_is_open;
BOOL data_was_written;
char **stack;
} MXML_WRITER;
typedef struct mxml_struct *PMXML_NODE;
typedef struct mxml_struct {
char name[MXML_NAME_LENGTH]; // name of element <[name]>[value]</[name]>
char *value; // value of element
int n_attributes; // list of attributes
char *attribute_name;
char **attribute_value;
int line_number; // line number for source file
PMXML_NODE parent; // pointer to parent element
int n_children; // list of children
PMXML_NODE child;
} MXML_NODE;
/*------------------------------------------------------------------*/
MXML_WRITER *mxml_open_document(const char *file_name);
BOOL mxml_start_element(MXML_WRITER *writer, const char *name);
BOOL mxml_end_element(MXML_WRITER *writer);
BOOL mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value);
BOOL mxml_write_value(MXML_WRITER *writer, const char *value);
BOOL mxml_close_document(MXML_WRITER *writer);
int mxml_get_number_of_children(PMXML_NODE pnode);
PMXML_NODE mxml_find_node(PMXML_NODE tree, char *xml_path);
char *mxml_get_value(PMXML_NODE pnode);
char *mxml_get_attribute(PMXML_NODE pnode, char *name);
BOOL mxml_add_attribute(PMXML_NODE pnode, char *attrib_name, char *attrib_value);
PMXML_NODE mxml_add_node(PMXML_NODE parent, char *node_name, char *value);
BOOL mxml_replace_node_name(PMXML_NODE pnode, char *new_name);
BOOL mxml_replace_node_value(PMXML_NODE pnode, char *value);
BOOL mxml_replace_subvalue(PMXML_NODE pnode, char *name, char *value);
BOOL mxml_replace_attribute_name(PMXML_NODE pnode, char *old_name, char *new_name);
BOOL mxml_replace_attribute_value(PMXML_NODE pnode, char *attrib_name, char *attrib_value);
BOOL mxml_delete_node(PMXML_NODE pnode);
BOOL mxml_delete_attribute(PMXML_NODE, char *attrib_name);
PMXML_NODE mxml_create_root_node();
PMXML_NODE mxml_parse_file(char *file_name, char *error, int error_size);
BOOL mxml_write_tree(char *file_name, PMXML_NODE tree);
void mxml_free_tree(PMXML_NODE tree);
/*------------------------------------------------------------------*/