Pārlūkot izejas kodu

RLS modified to be usable not only with XCAP server, but with standard
web server too, with short description in presence handbook about how
to simulate XCAP server with web server

Vaclav Kubart 20 gadi atpakaļ
vecāks
revīzija
27d7cdeb84

+ 187 - 4
doc/presence/xcap.xml

@@ -202,7 +202,7 @@ conn.close()
 # store resource-list document for user
 #
 
-uri = "/xcap-root/resource-lists/users/smith"
+uri = "/xcap-root/resource-lists/users/smith/resource-list.xml"
 headers = {"Content-Type": "application/resource-lists+xml"}
 bf = file("list.xml", "r")
 body = bf.read(65536);
@@ -263,17 +263,17 @@ conn.close()
 This document can contain references to users buddy lists like
 <quote>[email protected]</quote> which points to buddy list for user smith
 named <quote>default</quote> and can contain such lists directly.
-<caution><para>EyeBeam stores user's <quote>buddy list</quote> in 
+<!--<caution><para>EyeBeam stores user's <quote>buddy list</quote> in 
 <filename>&lt;XCAP-root&gt;/users/&lt;username&gt;/resource-list.xml</filename>, 
 not in
 <filename>&lt;XCAP-root&gt;/users/&lt;username&gt;</filename> which was used in 
-these examples.</para></caution>
+these examples.</para></caution>-->
 </para>
 <programlisting>
 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
 &lt;rls-services&gt;
 	&lt;service uri="sip:[email protected]"&gt;
-		&lt;resource-list&gt;http://localhost/xcap-root/resource-lists/users/smith/~~/resource-lists/list[@name=%22default%22]&lt;/resource-list&gt;
+		&lt;resource-list&gt;http://localhost/xcap-root/resource-lists/users/smith/resource-list.xml/~~/resource-lists/list[@name=%22default%22]&lt;/resource-list&gt;
 		&lt;packages&gt;
 			&lt;package&gt;presence&lt;/package&gt;
 		&lt;/packages&gt;
@@ -345,6 +345,189 @@ exist extensions for particular types of configuration - for purpose of this
 document only configuration from presence area will be mentioned.
 </para>-->
 
+<section><title>XCAP server simulation</title>
+<para>XCAP server is a HTTP server with some features like document validation
+or ability of working with parts of stored documents. If you have no XCAP
+server, you can simulate it using standard web server. There are not many XCAP
+servers available today, thus the simulation may be interesting for - at least -
+demonstration or testing purposes.
+</para>
+
+<para>Depending on your needs you can
+<itemizedlist>
+	<listitem><para>create hierarchical directory structure of XML documents according to
+	<xref linkend="pres_draft_xcap"/></para></listitem>
+	<listitem><para>allow upload (handle HTTP PUT method) which stores documents into the 
+	directory structure</para></listitem>
+	<listitem><para>improve upload to validate documents according to schema
+	(every sort of XCAP document should have their XSD published)</para></listitem>
+	<listitem><para>allow document removing (handle DELETE method)</para></listitem>
+	<listitem><para>process HTTP GET requests with a CGI-script so it processes
+	queries for partial documents</para></listitem>
+</itemizedlist>
+</para>
+
+<section><title>Directory structure</title>
+<para>Presence modules use XCAP documents stored in structure like this:
+<itemizedlist><title>xcap-root</title>
+	<listitem><para><filename>pres-rules</filename></para>
+	<itemizedlist>
+		<listitem><para><filename>users</filename></para>
+		<itemizedlist>
+			<listitem><para><filename>smith</filename></para>
+			<itemizedlist>
+				<listitem><para><filename>presence-rules.xml</filename> (file
+				containg presence authorization rules for user smith)</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para><filename>joe</filename></para>
+			<itemizedlist>
+				<listitem><para><filename>presence-rules.xml</filename> (file containing
+				presence authorization rules for user joe)</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>... (directories for other users)</para></listitem>
+		</itemizedlist>
+		</listitem>
+	</itemizedlist></listitem>
+	
+	<listitem><para><filename>resource-lists</filename></para>
+	<itemizedlist>
+		<listitem><para><filename>users</filename></para>
+		<itemizedlist>
+			<listitem><para><filename>smith</filename></para>
+			<itemizedlist>
+				<listitem><para><filename>resource-list.xml</filename> (file
+				containing resources lists for user smith)</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para><filename>joe</filename></para>
+			<itemizedlist>
+				<listitem><para><filename>resource-list.xml</filename> (file
+				containing resource lists for user joe)</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>... (directories for other users)</para></listitem>
+		</itemizedlist>
+		</listitem>
+		<!-- rls-services globals used instead this
+		<listitem><para><filename>global</filename></para>
+		<itemizedlist>
+			<listitem><para><filename>index</filename> (file containing global
+			resource lists)</para></listitem>
+		</itemizedlist>
+		</listitem>-->
+	</itemizedlist></listitem>
+	
+	<listitem><para><filename>rls-services</filename></para>
+	<itemizedlist>
+		<listitem><para><filename>global</filename></para>
+		<itemizedlist>
+			<listitem><para><filename>index</filename> (file containing global
+			rls-services documents)</para></listitem>
+		</itemizedlist>
+		</listitem>
+	</itemizedlist></listitem>
+	
+</itemizedlist>
+</para>
+</section> <!-- directory structure -->
+
+<section><title>Usage with SER</title>
+<para>You don't need a full XCAP server for presence authorization documents -
+these are read as standalone documents from directories of standalone users.
+</para>
+
+<para>For resource lists you have to install a full XCAP server. Only if you use
+<quote>simple</quote> mode of list subscription processing (see <link
+linkend="rls.parameters">RLS module parameters</link>) you do not need it.
+</para>
+</section>
+
+<section><title>XCAP simulation examples</title>
+<para>Examples presented here can be used as simple XCAP server simulation. It
+is able to handle PUT method (for whole XML documents).
+</para>
+
+<example><title>Apache2 configuration</title>
+<programlisting><![CDATA[
+...
+Alias /xcap-root /var/simulated-xcap-root
+<Directory /var/simulated-xcap-root>
+	Options Indexes FollowSymLinks MultiViews
+	Script PUT /cgi-bin/upload
+	<Limit PUT DELETE GET>
+		Order Allow,Deny
+		Deny from none
+		Allow from all
+	</Limit>
+</Directory>
+...
+]]></programlisting>
+</example>
+
+<example><title>Simple (and dangerous) cgi-script for upload</title>
+<para>This code is written in C and it is able to create directories if needed, but its usage in
+presented form is realy unsafe! You have to compile it and put into directory
+with other CGI scripts.</para>
+<programlisting><![CDATA[
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void copy_file(const char *filename)
+{
+	char buf[2048];
+	int r;
+	FILE *f;
+	
+	f = fopen(filename, "wb");
+	if (f) {
+		while (!feof(stdin)) {
+			r = fread(buf, 1, sizeof(buf), stdin);
+			fwrite(buf, 1, r, f);
+		}
+		fclose(f);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	char *filename, *x;
+	char tmp[1024];
+	int res = 0;
+	
+	filename = getenv ("PATH_TRANSLATED");
+
+	strcpy(tmp, filename);
+	x = strrchr(tmp, '/');
+	if (x) {
+		*x = 0;
+		res = mkdir(tmp, 0755);		/* ! dangerous ! */
+	}
+	else {
+		printf("Status: 500\n");
+		printf("Content-Type: text/html\n\n");
+		printf("<html><head/>\n<body>Incorrect filename</body></html>\n");	
+		return -1;
+	}
+	
+	copy_file(filename); /* ! dangerous ! */
+		
+	printf("Status: 200\n");
+	printf("Content-Type: text/html\n\n");
+	printf("<html><head><title>Upload</title>\n</head>\n<body>Finished...</body></html>\n");
+	
+	return 0;
+}
+]]></programlisting>
+</example>
+
+</section><!-- XCAP simulation examples -->
 
+</section> <!-- XCAP server simulation -->
 
 </section>

+ 120 - 3
lib/xcap/resource_list.c

@@ -108,6 +108,31 @@ char *xcap_uri_for_rls_resource(const char *xcap_root, const str_t *uri)
 	return dst;
 }
 
+char *xcap_uri_for_rls_services(const char *xcap_root)
+{
+	dstring_t s;
+	int l;
+	char *dst = NULL;
+
+	if (!xcap_root) return NULL;
+	l = strlen(xcap_root);
+	dstr_init(&s, 2 * l + 32);
+	dstr_append(&s, xcap_root, l);
+	if (xcap_root[l - 1] != '/') dstr_append(&s, "/", 1);
+	dstr_append_zt(&s, "rls-services/global/index");
+	
+	l = dstr_get_data_length(&s);
+	if (l > 0) {
+		dst = (char *)cds_malloc(l + 1);
+		if (dst) {
+			dstr_get_data(&s, dst);
+			dst[l] = 0;
+		}
+	}
+	dstr_destroy(&s);
+	return dst;
+}
+
 void free_flat_list(flat_list_t *list)
 {
 	flat_list_t *f, *e;
@@ -492,6 +517,21 @@ static int verify_package(service_t *srv, const str_t *package)
 	return 0;
 }
 
+static service_t *find_service(rls_services_t *rls, const str_t *uri)
+{
+	service_t *srv;
+	
+	if (!rls) return NULL;
+	
+	srv = SEQUENCE_FIRST(rls->rls_services);
+	while (srv) {
+		TRACE_LOG("comparing %s to %.*s\n", srv->uri, FMT_STR(*uri));
+		if (str_strcmp(uri, srv->uri) == 0) return srv;
+		srv = SEQUENCE_NEXT(srv);
+	}
+	return NULL;
+}
+
 /* ------- rls examining ------- */
 
 int get_rls(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params, const str_t *package, flat_list_t **dst)
@@ -543,7 +583,7 @@ int get_rls(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params,
 
 	/* verify the package */
 	if (verify_package(service, package) != 0) {
-		cds_free(service);
+		free_service(service);
 		return RES_BAD_EVENT_PACKAGE_ERR;
 	}
 	
@@ -561,6 +601,81 @@ int get_rls(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params,
 	return RES_OK;
 }
 
+int get_rls_from_full_doc(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params, const str_t *package, flat_list_t **dst)
+{
+	char *data = NULL;
+	int dsize = 0;
+	rls_services_t *rls = NULL;
+	service_t *service = NULL;
+	xcap_query_t xcap;
+	str_t curi;
+	int res;
+
+	if (!dst) return RES_INTERNAL_ERR;
+	
+	if (xcap_params) {
+		xcap = *xcap_params;
+	}
+	else memset(&xcap, 0, sizeof(xcap));
+
+	/* get basic document */
+	xcap.uri = xcap_uri_for_rls_services(xcap_root);
+	TRACE_LOG("XCAP uri \'%s\'\n", xcap.uri ? xcap.uri: "???");
+	res = xcap_query(&xcap, &data, &dsize);
+	if (res != 0) {
+		ERROR_LOG("get_rls(): XCAP problems for uri \'%s\'\n", xcap.uri ? xcap.uri: "???");
+		if (data) {
+			cds_free(data);
+		}
+		if (xcap.uri) cds_free(xcap.uri);
+		return RES_XCAP_QUERY_ERR;
+	}
+	if (xcap.uri) cds_free(xcap.uri);
+	xcap.uri = NULL;
+	
+	/* parse document as a service element in rls-sources */
+	if (parse_rls_services_xml(data, dsize, &rls) != 0) {
+		ERROR_LOG("Parsing problems!\n");
+		if (rls) free_rls_services(rls);
+		if (data) {
+			cds_free(data);
+		}
+		return RES_XCAP_PARSE_ERR;
+	}
+/*	DEBUG_LOG("%.*s\n", dsize, data);*/
+	if (data) cds_free(data);
+
+	/* try to find given service according to uri */
+	canonicalize_uri(uri, &curi);
+	service = find_service(rls, &curi); 
+	str_free_content(&curi);
+	
+	if (!service) {
+		if (rls) free_rls_services(rls);
+		ERROR_LOG("Empty service!\n");
+		return RES_INTERNAL_ERR;
+	}
+
+	/* verify the package */
+	if (verify_package(service, package) != 0) {
+		free_rls_services(rls);
+		return RES_BAD_EVENT_PACKAGE_ERR;
+	}
+	
+	/* create flat document */
+	res = create_flat_list(service, &xcap, xcap_root, dst);
+	if (res != RES_OK) {
+		ERROR_LOG("Flat list creation error\n");
+		free_rls_services(rls);
+		free_flat_list(*dst);
+		*dst = NULL;
+		return res;
+	}
+	free_rls_services(rls);
+	
+	return RES_OK;
+}
+
 char *xcap_uri_for_resource_list(const char *xcap_root, const str_t *user)
 {
 	dstring_t s;
@@ -620,8 +735,8 @@ int get_resource_list_as_rls(const char *xcap_root, const str_t *user, xcap_quer
 	if (xcap.uri) cds_free(xcap.uri);
 	xcap.uri = NULL;
 	
-	/* parse document as a list element in rls-sources */
-	if (parse_list_xml(data, dsize, &list) != 0) {
+	/* parse document as a list element in resource-lists */
+	if (parse_as_list_content_xml(data, dsize, &list) != 0) {
 		ERROR_LOG("Parsing problems!\n");
 		if (list) free_list(list);
 		if (data) {
@@ -631,6 +746,8 @@ int get_resource_list_as_rls(const char *xcap_root, const str_t *user, xcap_quer
 	}
 /*	DEBUG_LOG("%.*s\n", dsize, data);*/
 	if (data) cds_free(data);
+
+	/* rs -> list */
 	
 	if (!list) {
 		ERROR_LOG("Empty resource list!\n");

+ 1 - 0
lib/xcap/resource_list.h

@@ -43,6 +43,7 @@ typedef struct _flat_list_t {
 char *xcap_uri_for_rls_resource(const char *xcap_root, const str_t *uri);
 void canonicalize_uri(const str_t *uri, str_t *dst);
 int get_rls(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params, const str_t *package, flat_list_t **dst);
+int get_rls_from_full_doc(const char *xcap_root, const str_t *uri, xcap_query_t *xcap_params, const str_t *package, flat_list_t **dst);
 int get_resource_list_as_rls(const char *xcap_root, const str_t *user, xcap_query_t *xcap_params, flat_list_t **dst);
 void free_flat_list(flat_list_t *list);
 

+ 28 - 8
lib/xcap/resource_lists_parser.c

@@ -149,7 +149,7 @@ static int read_external(xmlNode *entry_node, external_t **dst)
 	return 0;
 }
 
-int read_list(xmlNode *list_node, list_t **dst)
+int read_list(xmlNode *list_node, list_t **dst, int read_content_only)
 {
 	int res = 0;
 	xmlAttr *a;
@@ -164,10 +164,12 @@ int read_list(xmlNode *list_node, list_t **dst)
 	memset(*dst, 0, sizeof(list_t));
 
 	/* get attributes */
-	a = find_attr(list_node->properties, "name");
-	if (a) {
-		a_val = get_attr_value(a);
-		if (a_val) (*dst)->name = zt_strdup(a_val);
+	if (!read_content_only) {
+		a = find_attr(list_node->properties, "name");
+		if (a) {
+			a_val = get_attr_value(a);
+			if (a_val) (*dst)->name = zt_strdup(a_val);
+		}
 	}
 
 	/* read entries */
@@ -180,7 +182,7 @@ int read_list(xmlNode *list_node, list_t **dst)
 			memset(l, 0, sizeof(*l));
 			
 			if (cmp_node(n, "list", rl_namespace) >= 0) {
-				res = read_list(n, &l->u.list);
+				res = read_list(n, &l->u.list, 0);
 				if (res == 0) {
 					if (l->u.list) {
 						l->type = lct_list;
@@ -266,7 +268,7 @@ static int read_resource_lists(xmlNode *root, resource_lists_t **dst)
 	while (n) {
 		if (n->type == XML_ELEMENT_NODE) {
 			if (cmp_node(n, "list", rl_namespace) >= 0) {
-				res = read_list(n, &l);
+				res = read_list(n, &l, 0);
 				if (res == 0) {
 					if (l) SEQUENCE_ADD(rl->lists, last_l, l);
 				}
@@ -309,7 +311,25 @@ int parse_list_xml(const char *data, int data_len, list_t **dst)
 		return -1;
 	}
 	
-	res = read_list(xmlDocGetRootElement(doc), dst);
+	res = read_list(xmlDocGetRootElement(doc), dst, 0);
+
+	xmlFreeDoc(doc);
+	return res;
+}
+
+int parse_as_list_content_xml(const char *data, int data_len, list_t **dst)
+{
+	int res = 0;
+	xmlDocPtr doc; /* the resulting document tree */
+
+	if (dst) *dst = NULL;
+	doc = xmlReadMemory(data, data_len, NULL, NULL, xml_parser_flags);
+	if (doc == NULL) {
+		ERROR_LOG("can't parse document\n");
+		return -1;
+	}
+	
+	res = read_list(xmlDocGetRootElement(doc), dst, 1);
 
 	xmlFreeDoc(doc);
 	return res;

+ 6 - 1
lib/xcap/resource_lists_parser.h

@@ -90,11 +90,16 @@ typedef struct {
 
 int parse_resource_lists_xml(const char *data, int data_len, resource_lists_t **dst);
 int parse_list_xml(const char *data, int data_len, list_t **dst);
+int parse_as_list_content_xml(const char *data, int data_len, list_t **dst);
 int parse_entry_xml(const char *data, int data_len, entry_t **dst);
 void free_resource_lists(resource_lists_t *rl);
 void free_list(list_t *l);
 void free_entry(entry_t *e);
 void free_display_names(display_name_t *sequence_first);
-int read_list(xmlNode *list_node, list_t **dst);
+
+/* if read_content_only set then the root element need not to be a list element
+ * it may be whatever, but content is parsed as if it is list (useful for reading
+ * resource-list as list */
+int read_list(xmlNode *list_node, list_t **dst, int read_content_only);
 
 #endif

+ 1 - 1
lib/xcap/rls_services_parser.c

@@ -130,7 +130,7 @@ int read_service(xmlNode *list_node, service_t **dst)
 			if (first_node) {
 				/* element must be list or resource-list */
 				if (cmp_node(n, "list", rls_namespace) >= 0) {
-					res = read_list(n, &(*dst)->content.list);
+					res = read_list(n, &(*dst)->content.list, 0);
 					if ( (res == 0) && ((*dst)->content.list) ) {
 						(*dst)->content_type = stc_list;
 					}