Replaced the SAX parser with DOM + XPath. Added RelaxNG schema validation of the configuration file. Made domain and mount options elements optional. - susmb - mounting of SMB/CIFS shares via FUSE
(HTM) git clone git://git.codemadness.org/susmb
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 3f2aa4880ce666e10ff6bafe46d4041e7e49fe8b
(DIR) parent d83453ae77a122ee32d410edd113b6915252e9df
(HTM) Author: geoff <devnull@localhost>
Date: Tue, 2 Jan 2007 22:19:03 +0000
Replaced the SAX parser with DOM + XPath.
Added RelaxNG schema validation of the configuration file.
Made domain and mount options elements optional.
Diffstat:
M Makefile | 14 ++++++++++----
M conffile.c | 204 +++++++++++++++++--------------
M conffile.h | 41 ++++++++-----------------------
A config.rng | 46 +++++++++++++++++++++++++++++++
M doc/README | 1 +
D doc/sax_model.mdzip | 0
D doc/sax_state_machine.png | 0
M options.c | 8 ++++----
M options.h | 4 ++--
D sax.c | 720 -------------------------------
D sax.h | 27 ---------------------------
M usmb.c | 88 ++++++++-----------------------
M usmb.h | 2 +-
M usmb_dir.c | 2 +-
M usmb_dir.h | 2 +-
M usmb_file.c | 2 +-
M usmb_file.h | 2 +-
M utils.c | 2 +-
M utils.h | 2 +-
M version.c | 4 ++--
M version.h | 4 ++--
A xml.c | 158 +++++++++++++++++++++++++++++++
A xml.h | 35 +++++++++++++++++++++++++++++++
23 files changed, 410 insertions(+), 958 deletions(-)
---
(DIR) diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
# usmb - mount SMB shares via FUSE and Samba
-# Copyright (C) 2006 Geoff Johnstone
+# Copyright (C) 2006-2007 Geoff Johnstone
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -36,8 +36,8 @@ CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
LDLIBS += $(shell pkg-config --libs-only-l $(PACKAGES))
LDFLAGS += $(shell pkg-config --libs-only-L $(PACKAGES))
-SOURCES = conffile.c options.c sax.c usmb.c usmb_dir.c usmb_file.c utils.c \
- version.c
+SOURCES = conffile.c options.c usmb.c usmb_dir.c usmb_file.c utils.c \
+ version.c xml.c
OBJECTS = $(SOURCES:.c=.o)
PROGRAM = usmb
@@ -45,6 +45,12 @@ PROGRAM = usmb
all: $(PROGRAM)
+conffile.c: config.rng.h
+config.rng.h: config.rng
+ sed -e 's/"/\\"/g' -e 's/\(.*\)/ "\1" \\/' \
+ -e '1istatic const char *rng_$(^:.rng=) =' $^ > config.rng.h
+ echo ' "";' >> config.rng.h
+
debug: CFLAGS += -ggdb -DDEBUG
debug: all
@@ -58,7 +64,7 @@ clean:
distclean: clean
- $(RM) core usmb-*.tar.bz2 usmb-*.tar.gz doc/*.mdzip.bak
+ $(RM) core usmb-*.tar.bz2 usmb-*.tar.gz doc/*.mdzip.bak config.rng.h
install-strip: STRIPFLAGS = -s
(DIR) diff --git a/conffile.c b/conffile.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,17 +17,18 @@
*/
#include <assert.h>
-#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "conffile.h"
#include "utils.h"
+#include "xml.h"
+#include "config.rng.h"
struct conffile {
- GHashTable *credentials;
- GHashTable *mounts;
+ xmlDocPtr doc;
+ xmlXPathContextPtr ctx;
};
@@ -40,17 +41,9 @@ struct credentials * credentials_new (const char *domain,
if (NULL == creds)
return NULL;
- creds->domain = xstrdup (domain);
- creds->username = xstrdup (username);
- creds->password = xstrdup (password);
-
- if ((NULL == creds->domain) ||
- (NULL == creds->username) ||
- (NULL == creds->password))
- {
- credentials_destroy (creds);
- creds = NULL;
- }
+ creds->domain = domain;
+ creds->username = username;
+ creds->password = password;
return creds;
}
@@ -59,11 +52,6 @@ struct credentials * credentials_new (const char *domain,
void credentials_destroy (struct credentials *creds)
{
assert (creds != NULL);
-
- xfree (creds->password);
- xfree (creds->username);
- xfree (creds->domain);
-
free (creds);
}
@@ -80,21 +68,12 @@ struct mount * mount_new (const char *server, const char *share,
if (NULL == mount)
return NULL;
- mount->server = xstrdup (server);
- mount->share = xstrdup (share);
- mount->mountpoint = xstrdup (mountpoint);
- mount->options = xstrdup (options);
+ mount->server = server;
+ mount->share = share;
+ mount->mountpoint = mountpoint;
+ mount->options = options;
mount->credentials = credentials;
- if ((NULL == mount->server) ||
- (NULL == mount->share) ||
- (NULL == mount->mountpoint) ||
- (NULL == mount->options))
- {
- mount_destroy (mount);
- mount = NULL;
- }
-
return mount;
}
@@ -102,35 +81,50 @@ struct mount * mount_new (const char *server, const char *share,
void mount_destroy (struct mount *mount)
{
assert (NULL != mount);
-
- xfree (mount->options);
- xfree (mount->mountpoint);
- xfree (mount->share);
- xfree (mount->server);
-
free (mount);
}
-struct conffile * conffile_new (void)
+struct conffile * conffile_new (const char *filename)
{
- struct conffile *cf = malloc (sizeof (struct conffile));
-
- if (NULL == cf)
+ xmlDocPtr doc;
+ xmlXPathContextPtr ctx;
+ struct conffile *cf;
+
+ doc = xmlParseFile (filename);
+ if (NULL == doc)
+ {
+ fprintf (stderr, "Cannot parse %s\n", filename);
return NULL;
+ }
+ if (!xml_validate_relaxng (doc, rng_config))
+ {
+ fprintf (stderr, "%s isn't a valid USMB configuration\n", filename);
+ xmlFreeDoc (doc);
+ return NULL;
+ }
- cf->credentials = g_hash_table_new_full (g_str_hash, g_str_equal, free,
- (GDestroyNotify)credentials_destroy);
- cf->mounts = g_hash_table_new_full (g_str_hash, g_str_equal,
- free, (GDestroyNotify)mount_destroy);
+ ctx = xmlXPathNewContext (doc);
+ if (NULL == ctx)
+ {
+ fputs ("Cannot create XPath context\n", stderr);
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+
+ cf = malloc (sizeof (struct conffile));
- if ((NULL == cf->credentials) || (NULL == cf->mounts))
+ if (NULL == cf)
{
- conffile_destroy (cf);
- cf = NULL;
+ xmlXPathFreeContext (ctx);
+ xmlFreeDoc (doc);
+ return NULL;
}
+ cf->doc = doc;
+ cf->ctx = ctx;
+
return cf;
}
@@ -139,69 +133,93 @@ void conffile_destroy (struct conffile *cf)
{
assert (NULL != cf);
- if (NULL != cf->mounts)
- g_hash_table_destroy (cf->mounts);
-
- if (NULL != cf->credentials)
- g_hash_table_destroy (cf->credentials);
-
+ xmlXPathFreeContext (cf->ctx);
+ xmlFreeDoc (cf->doc);
free (cf);
}
-bool conffile_add_mount (struct conffile *cf, char *key, struct mount *mount)
+struct mount * conffile_get_mount (struct conffile *cf, const char *key)
{
+ char xpath[2048];
+ char *creds = NULL;
+ void *server = NULL, *share = NULL, *mountpoint = NULL, *options = NULL;
+ void *domain = NULL, *username = NULL, *password = NULL;
+ bool ok = true;
+ struct credentials *credentials = NULL;
+ struct mount *ret = NULL;
+
assert (NULL != cf);
assert (NULL != key);
- assert (NULL != mount);
- DEBUG (fprintf (stderr, "add_mount (%s)\n", key));
+ if (strchr (key, '\''))
+ return NULL;
- if (NULL != g_hash_table_lookup (cf->mounts, key))
- {
- fprintf (stderr, "Duplicate mount name: %s\n", key);
- return false;
- }
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/mount[@id='%s']/server/text()", key);
+ xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&server) || (ok = false);
- g_hash_table_insert (cf->mounts, key, mount);
- return true;
-}
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/mount[@id='%s']/share/text()", key);
+ xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&share) || (ok = false);
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/mount[@id='%s']/mountpoint/text()", key);
+ xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&mountpoint) || (ok = false);
-bool conffile_add_credentials (struct conffile *cf, char *key,
- struct credentials *credentials)
-{
- assert (NULL != cf);
- assert (NULL != key);
- assert (NULL != credentials);
-
- DEBUG (fprintf (stderr, "add_credentials (%s)\n", key));
+ snprintf (xpath, sizeof (xpath), "/usmbconfig/mount[@id='%s']", key);
+ xml_xpath_attr_value (cf->ctx, BAD_CAST xpath, BAD_CAST "credentials",
+ (xmlChar **)&creds) || (ok = false);
- if (NULL != g_hash_table_lookup (cf->credentials, key))
+ if (!ok)
{
- fprintf (stderr, "Duplicate credentials name: %s\n", key);
- return false;
+ xfree (creds);
+ xfree (mountpoint);
+ xfree (share);
+ xfree (server);
+
+ return NULL;
}
- g_hash_table_insert (cf->credentials, key, credentials);
- return true;
-}
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/credentials[@id='%s']/domain/text()", creds);
+ (void)xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&domain);
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/credentials[@id='%s']/username/text()", creds);
+ xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&username) || (ok = false);
-struct mount * conffile_get_mount (struct conffile *cf, const char *key)
-{
- assert (NULL != cf);
- assert (NULL != key);
+ snprintf (xpath, sizeof (xpath),
+ "/usmbconfig/credentials[@id='%s']/password/text()", creds);
+ xml_xpath_text (cf->ctx, BAD_CAST xpath, (void *)&password) || (ok = false);
- return g_hash_table_lookup (cf->mounts, key);
-}
+ if (ok)
+ {
+ credentials = credentials_new (domain, username, password);
+ if (NULL != credentials)
+ {
+ ret = mount_new (server, share, mountpoint, credentials, options);
+ ok = (NULL != ret);
+ }
+ }
-struct credentials * conffile_get_credentials (struct conffile *cf,
- const char *key)
-{
- assert (NULL != cf);
- assert (NULL != key);
+ if (!ok)
+ {
+ if (NULL != credentials)
+ credentials_destroy (credentials);
- return g_hash_table_lookup (cf->credentials, key);
+ xfree (password);
+ xfree (domain);
+ xfree (username);
+ xfree (creds);
+ xfree (mountpoint);
+ xfree (share);
+ xfree (server);
+
+ return NULL;
+ }
+
+ return ret;
}
+
(DIR) diff --git a/conffile.h b/conffile.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,49 +22,28 @@
#include <stdbool.h>
struct credentials {
- char *domain;
- char *username;
- char *password;
+ const char *domain;
+ const char *username;
+ const char *password;
};
struct mount {
- char *server;
- char *share;
- char *mountpoint;
+ const char *server;
+ const char *share;
+ const char *mountpoint;
const struct credentials *credentials;
- char *options;
+ const char *options;
};
struct conffile;
- struct credentials * credentials_new (const char *domain,
- const char *username,
- const char *password);
-
- void credentials_destroy (struct credentials *creds);
-
- struct mount * mount_new (const char *server, const char *share,
- const char *mountpoint,
- const struct credentials *credentials,
- const char *options);
-
- void mount_destroy (struct mount *mount);
-
- struct conffile * conffile_new (void);
-
+ struct conffile * conffile_new (const char *filename);
void conffile_destroy (struct conffile *cf);
- bool conffile_add_mount (struct conffile *cf, char *key, struct mount *mount);
-
- bool conffile_add_credentials (struct conffile *cf, char *key,
- struct credentials *credentials);
-
struct mount * conffile_get_mount (struct conffile *cf, const char *key);
-
- struct credentials * conffile_get_credentials (struct conffile *cf,
- const char *key);
+ void mount_destroy (struct mount *mount);
#endif
(DIR) diff --git a/config.rng b/config.rng
@@ -0,0 +1,46 @@
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+
+ <start>
+ <ref name="usmbconfig" />
+ </start>
+
+
+ <define name="usmbconfig">
+ <element name="usmbconfig">
+ <zeroOrMore>
+ <choice>
+ <ref name="credentials" />
+ <ref name="mount" />
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+
+ <define name="credentials">
+ <element name="credentials">
+ <attribute name="id" />
+ <optional>
+ <element name="domain"> <text /> </element>
+ </optional>
+ <element name="username"> <text /> </element>
+ <element name="password"> <text /> </element>
+ </element>
+ </define>
+
+
+ <define name="mount">
+ <element name="mount">
+ <attribute name="id" />
+ <attribute name="credentials" />
+ <element name="server"> <text /> </element>
+ <element name="share"> <text /> </element>
+ <element name="mountpoint"> <text /> </element>
+ <optional>
+ <element name="options"> <text /> </element>
+ </optional>
+ </element>
+ </define>
+
+</grammar>
+
(DIR) diff --git a/doc/README b/doc/README
@@ -43,6 +43,7 @@ Installation
------------
Sorry, no autoconf yet.
+You need GNU sed.
Edit the Makefile with brain engaged.
Run make.
Run make install (maybe as root).
(DIR) diff --git a/doc/sax_model.mdzip b/doc/sax_model.mdzip
Binary files differ.
(DIR) diff --git a/doc/sax_state_machine.png b/doc/sax_state_machine.png
Binary files differ.
(DIR) diff --git a/options.c b/options.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -133,7 +133,7 @@ bool parse_args (int *argc, char ***argv,
* mount point
*/
#define MAXARGS 12
-void build_fuse_args (char *options, char *mountpoint,
+void build_fuse_args (const char *options, const char *mountpoint,
int *out_argc, char ***out_argv)
{
assert (NULL != mountpoint);
@@ -159,10 +159,10 @@ void build_fuse_args (char *options, char *mountpoint,
if ((NULL != options) && ('\0' != options[0]))
{
argv[argc++] = "-o";
- argv[argc++] = options;
+ argv[argc++] = (char *)options;
}
- argv[argc++] = mountpoint;
+ argv[argc++] = (char *)mountpoint;
argv[argc] = NULL; // for good measure...
assert (argc < MAXARGS);
(DIR) diff --git a/options.h b/options.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
bool parse_args (int *argc, char ***argv,
const char **mountid, const char **out_conffile);
- void build_fuse_args (char *options, char *mountpoint,
+ void build_fuse_args (const char *options, const char *mountpoint,
int *out_argc, char ***out_argv);
#endif
(DIR) diff --git a/sax.c b/sax.c
@@ -1,720 +0,0 @@
-/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <libxml/xmlreader.h>
-#include <glib.h>
-#include "conffile.h"
-#include "sax.h"
-#include "utils.h"
-
-
-/* Configuration file format
- * -------------------------
- *
- * usmbconfig section
- * Any number of credentials and mount sections
- *
- * credentials section
- * domain
- * username
- * password
- *
- * mount section
- * server
- * share
- * mountpoint
- * options
- *
- * Example:
- * <usmbconfig>
- *
- * <credentials id="workgroup">
- * <domain>WORKGROUP</domain>
- * <username>user</username>
- * <password>password</password>
- * </credentials>
- *
- * <mount id="home" credentials="workgroup">
- * <server>127.0.0.1</server>
- * <share>home</share>
- * <mountpoint>/tmp/smb</mountpoint>
- * <options>readonly</options>
- * </mount>
- *
- * </usmbconfig>
- */
-
-
-/* SAX state machine
- * -----------------
- *
- * See doc/sax_state_machine.png or the MagicDraw model doc/sax_model.mdzip.
- */
-
-
-enum sax_state {
- START,
- INITIAL,
- USMBCFG,
- DONE,
- FINISH,
- CREDS,
- MOUNT,
- SERVER,
- SHARE,
- MOUNTPT,
- OPTIONS,
- DOMAIN,
- USER,
- PASS,
- ERROR
-};
-
-
-enum element {
- ELT_USMBCFG,
- ELT_CREDS,
- ELT_DOMAIN,
- ELT_USER,
- ELT_PASS,
- ELT_MOUNT,
- ELT_SERVER,
- ELT_SHARE,
- ELT_MOUNTPT,
- ELT_OPTIONS,
- ELT_UNKNOWN
-};
-
-
-// passed as the ctx pointer into SAX callbacks
-struct sax_parser {
- enum sax_state state;
-
- union {
- struct {
- char *id;
-
- bool got_domain;
- bool got_username;
- bool got_password;
-
- GString *domain;
- GString *username;
- GString *password;
- } credentials;
-
- struct {
- char *id;
- struct credentials *creds;
-
- bool got_server;
- bool got_share;
- bool got_mountpoint;
- bool got_options;
-
- GString *server;
- GString *share;
- GString *mountpoint;
- GString *options;
- } mount;
- } strs;
-
- struct conffile *cf;
-};
-
-
-static enum element get_element (const char *elt)
-{
- const char *elts[] = {
- [ ELT_USMBCFG ] = "usmbconfig",
- [ ELT_CREDS ] = "credentials",
- [ ELT_DOMAIN ] = "domain",
- [ ELT_USER ] = "username",
- [ ELT_PASS ] = "password",
- [ ELT_MOUNT ] = "mount",
- [ ELT_SERVER ] = "server",
- [ ELT_SHARE ] = "share",
- [ ELT_MOUNTPT ] = "mountpoint",
- [ ELT_OPTIONS ] = "options"
- };
-
- for (size_t i = 0; i < sizeof (elts) / sizeof (elts[0]); ++i)
- if (!strcmp (elt, elts[i]))
- return i;
-
- return ELT_UNKNOWN;
-}
-
-
-static void start_document (void *ctx)
-{
- struct sax_parser *sp = ctx;
- assert (sp->state == START);
- sp->state = INITIAL;
-}
-
-
-static void end_document (void *ctx)
-{
- struct sax_parser *sp = ctx;
-
- if (ERROR == sp->state)
- return;
-
- if (sp->state != DONE)
- {
- fputs ("Unexpected end of configuration file\n", stderr);
- sp->state = ERROR;
- }
-
- sp->state = FINISH;
-}
-
-
-static void error (void *ctx, const char *msg, ...)
-{
- struct sax_parser *sp = ctx;
-
- if (ERROR == sp->state)
- return;
-
- va_list ap;
-
- fputs ("Parse error: ", stderr);
- va_start (ap, msg);
- vfprintf (stderr, msg ,ap);
- va_end (ap);
- sp->state = ERROR;
-}
-
-
-typedef void * (* cf_get_fn) (struct conffile *, const char *);
-static bool handle_id (char **out, const char *in, cf_get_fn func,
- struct conffile *cf)
-{
- assert (NULL != out);
- assert (NULL != in);
-
- if (NULL != *out)
- {
- fprintf (stderr,
- "Multiple ID attributes in <mount> or <credentials>: %s\n", in);
- return false;
- }
-
- if (NULL != func (cf, in))
- {
- fprintf (stderr, "Duplicate ID: %s\n", in);
- return false;
- }
-
- *out = xstrdup (in);
-
- if (NULL == *out)
- {
- fputs ("Out of memory\n", stderr);
- return false;
- }
-
- return true;
-}
-
-
-static void start_mount (struct sax_parser *sp, const xmlChar **attrs)
-{
- sp->strs.mount.id = NULL;
- sp->strs.mount.creds = NULL;
-
- // must have id and credentials attributes
- for (int i = 0; NULL != attrs[i]; i += 2)
- {
- assert (NULL != attrs[i+1]);
-
- if (!strcmp (attrs[i], "id"))
- {
- if (!handle_id (&sp->strs.mount.id, (const char *)attrs[i+1],
- (cf_get_fn)conffile_get_mount, sp->cf))
- {
- sp->state = ERROR;
- return;
- }
- }
-
- else if (!strcmp (attrs[i], "credentials"))
- {
- if (NULL != sp->strs.mount.creds)
- {
- fputs ("Duplicate credentials attribute in <mount>\n", stderr);
- sp->state = ERROR;
- return;
- }
-
- sp->strs.mount.creds =
- conffile_get_credentials (sp->cf, (const char *)attrs[i+1]);
-
- if (NULL == sp->strs.mount.creds)
- {
- fprintf (stderr, "Credentials %s not found for <mount>\n", attrs[i+1]);
- sp->state = ERROR;
- return;
- }
- }
-
- else
- {
- fprintf (stderr, "Unrecognised <mount> attribute %s\n", attrs[i]);
- sp->state = ERROR;
- return;
- }
- }
-
- if ((NULL == sp->strs.mount.id) ||
- (NULL == sp->strs.mount.creds))
- {
- fputs ("<mount> must have id and credentials attributes\n", stderr);
- sp->state = ERROR;
- return;
- }
-
- sp->strs.mount.server = g_string_new (NULL);
- sp->strs.mount.share = g_string_new (NULL);
- sp->strs.mount.mountpoint = g_string_new (NULL);
- sp->strs.mount.options = g_string_new (NULL);
-
- sp->strs.mount.got_server = false;
- sp->strs.mount.got_share = false;
- sp->strs.mount.got_mountpoint = false;
- sp->strs.mount.got_options = false;
-
- sp->state = MOUNT;
-}
-
-
-static void start_creds (struct sax_parser *sp, const xmlChar **attrs)
-{
- assert (NULL == sp->strs.credentials.domain);
- assert (NULL == sp->strs.credentials.username);
- assert (NULL == sp->strs.credentials.password);
-
- sp->strs.credentials.id = NULL;
-
- // must have an id attribute
- for (int i = 0; NULL != attrs[i]; i += 2)
- {
- assert (NULL != attrs[i+1]);
-
- if (!strcmp (attrs[i], "id"))
- {
- if (!handle_id (&sp->strs.credentials.id, (const char *)attrs[i+1],
- (cf_get_fn)conffile_get_credentials, sp->cf))
- {
- sp->state = ERROR;
- return;
- }
- }
-
- else
- {
- fprintf (stderr, "Unrecognised <credentials> attribute %s\n", attrs[i]);
- sp->state = ERROR;
- return;
- }
- }
-
- if (NULL == sp->strs.credentials.id)
- {
- fputs ("<credentials> must have an id attribute\n", stderr);
- sp->state = ERROR;
- return;
- }
-
- sp->strs.credentials.domain = g_string_new (NULL);
- sp->strs.credentials.username = g_string_new (NULL);
- sp->strs.credentials.password = g_string_new (NULL);
-
- sp->strs.credentials.got_domain = false;
- sp->strs.credentials.got_username = false;
- sp->strs.credentials.got_password = false;
-
- sp->state = CREDS;
-}
-
-
-static void start_char_state (struct sax_parser *sp,
- GString *target,
- bool *got,
- const char *name,
- const xmlChar **attrs)
-{
- assert (NULL != target);
-
- if (ERROR == sp->state)
- return;
-
- if ((NULL != attrs) && (NULL != attrs[0]))
- {
- fprintf (stderr, "Spurious attributes on <%s>\n", name);
- sp->state = ERROR;
- return;
- }
-
- if (false != *got)
- {
- fprintf (stderr, "Duplicate <%s>\n", name);
- sp->state = ERROR;
- return;
- }
-
- *got = true;
-}
-
-
-static void start_server (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.mount.server, &sp->strs.mount.got_server,
- "server", attrs);
-}
-
-
-static void start_share (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.mount.share, &sp->strs.mount.got_share,
- "share", attrs);
-}
-
-
-static void start_mtpt (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.mount.mountpoint,
- &sp->strs.mount.got_mountpoint, "mountpoint", attrs);
-}
-
-
-static void start_options (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.mount.options, &sp->strs.mount.got_options,
- "options", attrs);
-}
-
-
-static void start_domain (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.credentials.domain,
- &sp->strs.credentials.got_domain, "domain", attrs);
-}
-
-
-static void start_username (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.credentials.username,
- &sp->strs.credentials.got_username, "username", attrs);
-}
-
-
-static void start_password (struct sax_parser *sp, const xmlChar **attrs)
-{
- start_char_state (sp, sp->strs.credentials.password,
- &sp->strs.credentials.got_password, "password", attrs);
-}
-
-
-static void start_element (void *ctx,
- const xmlChar *name, const xmlChar **attrs)
-{
- struct sax_parser *sp = ctx;
-
- if (ERROR == sp->state)
- return;
-
- /* all valid transitions on <element> */
- const struct {
- enum sax_state to, from;
- enum element on;
- void (*fn) (struct sax_parser *, const xmlChar **attrs);
- } transitions[] = {
- { .from = INITIAL, .on = ELT_USMBCFG, .to = USMBCFG, .fn = NULL },
- { .from = USMBCFG, .on = ELT_MOUNT, .to = MOUNT, .fn = start_mount },
- { .from = USMBCFG, .on = ELT_CREDS, .to = CREDS, .fn = start_creds },
- { .from = MOUNT, .on = ELT_SERVER, .to = SERVER, .fn = start_server },
- { .from = MOUNT, .on = ELT_SHARE, .to = SHARE, .fn = start_share },
- { .from = MOUNT, .on = ELT_MOUNTPT, .to = MOUNTPT, .fn = start_mtpt },
- { .from = MOUNT, .on = ELT_OPTIONS, .to = OPTIONS, .fn = start_options },
- { .from = CREDS, .on = ELT_DOMAIN, .to = DOMAIN, .fn = start_domain },
- { .from = CREDS, .on = ELT_USER, .to = USER, .fn = start_username },
- { .from = CREDS, .on = ELT_PASS, .to = PASS, .fn = start_password }
- };
-
-
- enum element elt = get_element ((const char *)name);
-
- for (size_t i = 0; i < sizeof (transitions) / sizeof (transitions[0]); ++i)
- {
- if ((transitions[i].from == sp->state) &&
- (transitions[i].on == elt))
- {
- if (NULL != transitions[i].fn)
- transitions[i].fn (sp, attrs);
-
- if (ERROR != sp->state)
- sp->state = transitions[i].to;
- return;
- }
- }
-
- fprintf (stderr, "Unexpected <%s>\n", name);
- sp->state = ERROR;
-}
-
-
-static void end_mount (struct sax_parser *sp)
-{
- // must have id, creds, server, share and mountpoint; options are optional
- assert (NULL != sp->strs.mount.id);
- assert (NULL != sp->strs.mount.creds);
-
- if (!sp->strs.mount.got_server || (0 == sp->strs.mount.server->len))
- {
- fprintf (stderr, "Mount %s: no server given\n", sp->strs.mount.id);
- sp->state = ERROR;
- }
-
- else if (!sp->strs.mount.got_share || (0 == sp->strs.mount.share->len))
- {
- fprintf (stderr, "Mount %s: no share given\n", sp->strs.mount.id);
- sp->state = ERROR;
- }
-
- else if (!sp->strs.mount.got_mountpoint ||
- (0 == sp->strs.mount.mountpoint->len))
- {
- fprintf (stderr, "Mount %s: no mount point given\n", sp->strs.mount.id);
- sp->state = ERROR;
- }
-
- else
- {
- struct mount *mount =
- mount_new (sp->strs.mount.server->str,
- sp->strs.mount.share->str,
- sp->strs.mount.mountpoint->str,
- sp->strs.mount.creds,
- sp->strs.mount.options->str);
-
- if ((NULL == mount) ||
- !conffile_add_mount (sp->cf, sp->strs.mount.id, mount))
- sp->state = ERROR;
- }
-
- g_string_free (sp->strs.mount.server, TRUE);
- g_string_free (sp->strs.mount.share, TRUE);
- g_string_free (sp->strs.mount.mountpoint, TRUE);
- g_string_free (sp->strs.mount.options, TRUE);
-
- // we need the mount ID (for the hashtable key) if parsing succeeded
- if (ERROR == sp->state)
- xfree (sp->strs.mount.id);
-}
-
-
-static void end_creds (struct sax_parser *sp)
-{
- // must have username; domain and password are optional
- assert (NULL != sp->strs.credentials.id);
-
- if (!sp->strs.credentials.got_username ||
- (0 == sp->strs.credentials.username->len))
- {
- fprintf (stderr, "Credentials %s: no username given\n",
- sp->strs.credentials.id);
- sp->state = ERROR;
- }
-
- else
- {
- struct credentials *creds =
- credentials_new (sp->strs.credentials.domain->str,
- sp->strs.credentials.username->str,
- sp->strs.credentials.password->str);
-
- if ((NULL == creds) ||
- !conffile_add_credentials (sp->cf, sp->strs.credentials.id, creds))
- sp->state = ERROR;
- }
-
- g_string_free (sp->strs.credentials.domain, TRUE);
- g_string_free (sp->strs.credentials.username, TRUE);
- g_string_free (sp->strs.credentials.password, TRUE);
-
- sp->strs.credentials.domain = NULL;
- sp->strs.credentials.username = NULL;
- sp->strs.credentials.password = NULL;
-
- if (ERROR == sp->state)
- xfree (sp->strs.credentials.id);
-}
-
-
-static void end_element (void *ctx, const xmlChar *name)
-{
- struct sax_parser *sp = ctx;
-
- if (ERROR == sp->state)
- return;
-
- /* all valid transitions on </element> */
- const struct {
- enum sax_state to, from;
- enum element on;
- void (*fn) (struct sax_parser *);
- } transitions[] = {
- { .from = USMBCFG, .on = ELT_USMBCFG, .to = DONE, .fn = NULL },
- { .from = MOUNT, .on = ELT_MOUNT, .to = USMBCFG, .fn = end_mount },
- { .from = CREDS, .on = ELT_CREDS, .to = USMBCFG, .fn = end_creds },
- { .from = SERVER, .on = ELT_SERVER, .to = MOUNT, .fn = NULL },
- { .from = SHARE, .on = ELT_SHARE, .to = MOUNT, .fn = NULL },
- { .from = MOUNTPT, .on = ELT_MOUNTPT, .to = MOUNT, .fn = NULL },
- { .from = OPTIONS, .on = ELT_OPTIONS, .to = MOUNT, .fn = NULL },
- { .from = DOMAIN, .on = ELT_DOMAIN, .to = CREDS, .fn = NULL },
- { .from = USER, .on = ELT_USER, .to = CREDS, .fn = NULL },
- { .from = PASS, .on = ELT_PASS, .to = CREDS, .fn = NULL }
- };
-
-
- enum element elt = get_element ((const char *)name);
-
- for (size_t i = 0; i < sizeof (transitions) / sizeof (transitions[0]); ++i)
- {
- if ((transitions[i].from == sp->state) &&
- (transitions[i].on == elt))
- {
- if (NULL != transitions[i].fn)
- transitions[i].fn (sp);
-
- if (ERROR != sp->state)
- sp->state = transitions[i].to;
- return;
- }
- }
-
- fprintf (stderr, "Unexpected </%s>\n", name);
- sp->state = ERROR;
-}
-
-
-// strips leading and trailing whitespace; returns true iff characters remain
-static bool strip_whitespace (const char **str, int *len)
-{
- assert (NULL != str);
- assert (NULL != len);
-
- /* leading whitespace */
- while ((*len > 0) && isspace (**str))
- {
- ++(*str);
- --(*len);
- }
-
- /* trailing whitespace */
- while ((*len > 0) && (isspace ((*str)[*len - 1])))
- --(*len);
-
- return (0 != *len);
-}
-
-
-static inline void append_chars (GString *target, const xmlChar *str, int len)
-{
- assert (NULL != target);
- assert (NULL != str);
-
- g_string_append_len (target, (const char *)str, len);
-}
-
-
-static void chars (void *ctx, const xmlChar *str, int len)
-{
- struct sax_parser *sp = ctx;
-
- if (ERROR == sp->state)
- return;
-
- // all states admit whitespace characters
- if (!strip_whitespace ((const char **)&str, &len))
- return;
-
- // these states admit non-whitespace characters
- switch (sp->state)
- {
- case SERVER: append_chars (sp->strs.mount.server, str, len); break;
- case SHARE: append_chars (sp->strs.mount.share, str, len); break;
- case MOUNTPT: append_chars (sp->strs.mount.mountpoint, str, len); break;
- case OPTIONS: append_chars (sp->strs.mount.options, str, len); break;
- case DOMAIN: append_chars (sp->strs.credentials.domain, str, len); break;
- case USER: append_chars (sp->strs.credentials.username, str, len); break;
- case PASS: append_chars (sp->strs.credentials.password, str, len); break;
-
- default:
- fputs ("Unexpected characters: ", stderr);
- while (len--)
- fputc (*str++, stderr);
- fputc ('\n', stderr);
- sp->state = ERROR;
- break;
- }
-}
-
-
-struct conffile * parse_xml_conf (const char *file)
-{
- LIBXML_TEST_VERSION;
-
- struct _xmlSAXHandler sax = {
- .startDocument = start_document,
- .endDocument = end_document,
- .startElement = start_element,
- .endElement = end_element,
- .error = error,
- .characters = chars,
- };
-
-
- struct sax_parser user_data = {
- .state = START
- };
-
- user_data.cf = conffile_new();
-
- if (NULL != user_data.cf)
- {
- if ((0 != xmlSAXUserParseFile (&sax, &user_data, file)) ||
- (user_data.state != FINISH))
- {
- fputs ("Parse error\n", stderr);
- conffile_destroy (user_data.cf);
- user_data.cf = NULL;
- }
- }
-
- return user_data.cf;
-}
(DIR) diff --git a/sax.h b/sax.h
@@ -1,27 +0,0 @@
-/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef SAX_H
- #define SAX_H
-
- #include "conffile.h"
-
- struct conffile * parse_xml_conf (const char *file);
-
-#endif
-
(DIR) diff --git a/usmb.c b/usmb.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,7 +32,6 @@
#include <string.h>
#include "conffile.h"
#include "options.h"
-#include "sax.h"
#include "usmb.h"
#include "usmb_dir.h"
#include "usmb_file.h"
@@ -41,7 +40,7 @@
SMBCCTX *ctx;
-static const char *share = NULL;
+static char *share = NULL;
char * make_url (const char *path)
@@ -62,9 +61,7 @@ static inline void do_strncpy (char *to, const char *from, int tolen)
}
-static char *domain = NULL;
-static char *username = NULL;
-static char *password = NULL;
+static struct mount *mount;
static void auth_fn (const char *srv, const char *shr, char *wg, int wglen,
char *un, int unlen, char *pw, int pwlen)
@@ -73,13 +70,11 @@ static void auth_fn (const char *srv, const char *shr, char *wg, int wglen,
(void)shr;
DEBUG (fprintf (stderr, "Authenticating for \\\\%s\\%s\n", srv, shr));
- assert (NULL != domain);
- assert (NULL != username);
- assert (NULL != password);
+ if (NULL != mount->credentials->domain)
+ do_strncpy (wg, mount->credentials->domain, wglen);
- do_strncpy (wg, domain, wglen);
- do_strncpy (un, username, unlen);
- do_strncpy (pw, password, pwlen);
+ do_strncpy (un, mount->credentials->username, unlen);
+ do_strncpy (pw, mount->credentials->password, pwlen);
}
@@ -205,45 +200,22 @@ static bool check_conf_perms (const char *conffile)
}
-static bool fix_up_conf_details (struct mount *mount)
+static bool create_share_name (struct mount *mount)
{
assert (NULL != mount->credentials);
size_t len = strlen ("smb:///") +
strlen (mount->server) +
strlen (mount->share) + 1;
- char *str;
- if (NULL == (str = malloc (len)))
- {
- perror ("Cannot allocate share name");
- return false;
- }
-
- strcpy (str, "smb://");
- strcat (str, mount->server);
- strcat (str, "/");
- strcat (str, mount->share);
- domain = xstrdup (mount->credentials->domain);
- username = xstrdup (mount->credentials->username);
- password = xstrdup (mount->credentials->password);
-
- if ((NULL == domain) ||
- (NULL == username) ||
- (NULL == password))
+ if (NULL == (share = malloc (len)))
{
- fputs ("Out of memory\n", stderr);
- xfree (password);
- xfree (username);
- xfree (domain);
- free (str);
+ perror ("Cannot allocate share name");
return false;
}
- share = str;
-
+ snprintf (share, len, "smb://%s/%s", mount->server, mount->share);
DEBUG (fprintf (stderr, "Share URL: %s\n", share));
- DEBUG (fprintf (stderr, "Username: %s\\%s\n", domain, username));
return true;
}
@@ -265,12 +237,11 @@ int main (int argc, char **argv)
if (!check_conf_perms (conffile))
return EXIT_FAILURE;
- struct conffile *cf = parse_xml_conf (conffile);
+ struct conffile *cf = conffile_new (conffile);
if (NULL == cf)
return EXIT_FAILURE;
struct mount *mount = conffile_get_mount (cf, mountid);
-
if (NULL == mount)
{
fprintf (stderr, "Mount %s not defined in configuration file %s\n",
@@ -279,42 +250,27 @@ int main (int argc, char **argv)
return EXIT_FAILURE;
}
- char *mountpoint = xstrdup (mount->mountpoint);
- char *options = xstrdup (mount->options);
-
- if ((NULL == mountpoint) ||
- (mount->options && (NULL == options)))
+ if (!create_share_name (mount) ||
+ !create_smb_context ((char *)mount->credentials->domain,
+ (char *)mount->credentials->username, &ctx))
{
- perror ("Cannot allocate mountpoint");
- return EXIT_FAILURE;
- }
-
- {
- bool ret = fix_up_conf_details (mount);
+ mount_destroy (mount);
conffile_destroy (cf);
-
- if (false == ret)
- return EXIT_FAILURE;
- }
-
- if (!create_smb_context (domain, username, &ctx))
return EXIT_FAILURE;
+ }
+ DEBUG (fprintf (stderr, "Username: %s\\%s\n",
+ mount->credentials->domain, mount->credentials->username));
show_about (stdout);
int fuse_argc;
char **fuse_argv;
- build_fuse_args (options, mountpoint, &fuse_argc, &fuse_argv);
+ build_fuse_args (mount->options, mount->mountpoint, &fuse_argc, &fuse_argv);
int ret = fuse_main (fuse_argc, fuse_argv, &fuse_ops);
smbc_free_context (ctx, 1);
-
- xfree (mountpoint);
- xfree (options);
- xfree (domain);
- xfree (username);
- xfree (password);
- xfree (share);
+ mount_destroy (mount);
+ conffile_destroy (cf);
return ret;
}
(DIR) diff --git a/usmb.h b/usmb.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/usmb_dir.c b/usmb_dir.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/usmb_dir.h b/usmb_dir.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/usmb_file.c b/usmb_file.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/usmb_file.h b/usmb_file.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/utils.c b/utils.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/utils.h b/utils.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
(DIR) diff --git a/version.c b/version.c
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@ void show_about (FILE *fp)
{
fputs ("usmb - mount SMB shares via FUSE and Samba\n"
"\n"
- "Copyright (C) 2006 Geoff Johnstone.\n"
+ "Copyright (C) 2006-2007 Geoff Johnstone.\n"
"Licensed under the GNU General Public License.\n"
"usmb comes with ABSOLUTELY NO WARRANTY; for details please see\n"
"http://www.gnu.org/licenses/gpl.txt\n", fp);
(DIR) diff --git a/version.h b/version.h
@@ -1,5 +1,5 @@
/* usmb - mount SMB shares via FUSE and Samba
- * Copyright (C) 2006 Geoff Johnstone
+ * Copyright (C) 2006-2007 Geoff Johnstone
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
#include <stdio.h>
- #define USMB_VERSION 0x20060708
+ #define USMB_VERSION 0x20070102
// a - alpha, b - beta, p - pre-release, s - stable
#define USMB_VERSION_STATUS 'a'
(DIR) diff --git a/xml.c b/xml.c
@@ -0,0 +1,158 @@
+#include <libxml/xmlreader.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include "xml.h"
+#include "utils.h"
+
+
+bool xml_validate_relaxng (xmlDocPtr doc, const char *schema)
+{
+ xmlRelaxNGParserCtxtPtr rngpcptr = NULL;
+ xmlRelaxNGPtr rngptr = NULL;
+ xmlRelaxNGValidCtxtPtr rngvptr = NULL;
+ bool ret = false;
+
+ assert (NULL != doc);
+
+ if ((rngpcptr = xmlRelaxNGNewMemParserCtxt (schema, strlen (schema))) &&
+ ((rngptr = xmlRelaxNGParse (rngpcptr))) &&
+ ((rngvptr = xmlRelaxNGNewValidCtxt (rngptr))))
+ {
+ ret = (0 == xmlRelaxNGValidateDoc (rngvptr, doc));
+ }
+
+ if (NULL != rngvptr)
+ xmlRelaxNGFreeValidCtxt (rngvptr);
+
+ if (NULL != rngptr)
+ xmlRelaxNGFree (rngptr);
+
+ if (NULL != rngpcptr)
+ xmlRelaxNGFreeParserCtxt (rngpcptr);
+
+ return ret;
+}
+
+
+bool xml_xpath_attr_value (xmlXPathContextPtr ctx,
+ xmlChar *xpath,
+ const xmlChar *attribute,
+ xmlChar **out)
+{
+ xmlXPathObjectPtr obj;
+ xmlChar *tmp;
+
+ assert (NULL != ctx);
+ assert (NULL != xpath);
+ assert (NULL != out);
+
+ *out = NULL;
+
+ obj = xmlXPathEval (xpath, ctx);
+ if (NULL == obj)
+ {
+ DEBUG (fputs ("XPath evaluation error\n", stderr));
+ return false;
+ }
+
+ do {
+ if (XPATH_NODESET != obj->type)
+ {
+ DEBUG (fputs ("XPath evaluation didn't return a nodeset\n", stderr));
+ break;
+ }
+
+ if (NULL == obj->nodesetval)
+ {
+ DEBUG (fputs ("nodesetval is NULL\n", stderr));
+ break;
+ }
+
+ if (1 != obj->nodesetval->nodeNr)
+ {
+ DEBUG (fprintf (stderr, "Nodeset has %d elements\n",
+ obj->nodesetval->nodeNr));
+ break;
+ }
+
+ tmp = xmlGetProp (obj->nodesetval->nodeTab[0], attribute);
+ if (NULL == tmp)
+ break;
+
+ *out = (xmlChar *)xstrdup ((char *)tmp);
+ if (NULL == *out)
+ break;
+
+ xmlXPathFreeObject (obj);
+ return true;
+ /*NOTREACHED*/
+ } while (false);
+
+ *out = NULL;
+ xmlXPathFreeObject (obj);
+ return false;
+}
+
+
+bool xml_xpath_text (xmlXPathContextPtr ctx, xmlChar *xpath, xmlChar **out)
+{
+ xmlXPathObjectPtr obj;
+
+ assert (NULL != ctx);
+ assert (NULL != xpath);
+ assert (NULL != out);
+
+ *out = NULL;
+
+ DEBUG (fprintf (stderr, "xml_xpath_text (%s)\n", xpath));
+
+ obj = xmlXPathEval (xpath, ctx);
+ if (NULL == obj)
+ {
+ DEBUG (fputs ("XPath evaluation error\n", stderr));
+ return false;
+ }
+
+ do {
+ if (XPATH_NODESET != obj->type)
+ {
+ DEBUG (fputs ("XPath evaluation didn't return a nodeset\n", stderr));
+ break;
+ }
+
+ if (NULL == obj->nodesetval)
+ {
+ DEBUG (fputs ("nodesetval is NULL\n", stderr));
+ break;
+ }
+
+ if (1 != obj->nodesetval->nodeNr)
+ {
+ DEBUG (fprintf (stderr, "Nodeset has %d elements\n",
+ obj->nodesetval->nodeNr));
+ break;
+ }
+
+ if (NULL == obj->nodesetval->nodeTab[0]->content)
+ {
+ DEBUG (fputs ("Node has no text\n", stderr));
+ break;
+ }
+
+ *out = (xmlChar *)xstrdup ((char *)obj->nodesetval->nodeTab[0]->content);
+ if (NULL == *out)
+ break;
+
+ xmlXPathFreeObject (obj);
+ return true;
+ /*NOTREACHED*/
+ } while (false);
+
+ *out = NULL;
+ xmlXPathFreeObject (obj);
+ return false;
+}
+
(DIR) diff --git a/xml.h b/xml.h
@@ -0,0 +1,35 @@
+/* usmb - mount SMB shares via FUSE and Samba
+ * Copyright (C) 2006-2007 Geoff Johnstone
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XML_H
+ #define XML_H
+
+ #include <libxml/xmlreader.h>
+ #include <libxml/xpath.h>
+ #include <libxml/xpathInternals.h>
+ #include <stdbool.h>
+
+
+ bool xml_validate_relaxng (xmlDocPtr doc, const char *schema);
+ bool xml_xpath_attr_value (xmlXPathContextPtr ctx,
+ xmlChar *xpath,
+ const xmlChar *attribute,
+ xmlChar **out);
+ bool xml_xpath_text (xmlXPathContextPtr ctx, xmlChar *xpath, xmlChar **out);
+
+#endif