Plan 9 from Bell Labs’s /usr/web/sources/contrib/fgb/root/sys/src/cmd/nd/nd.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * nd.c - a small program for WebDAV operations.
 *
 * Copyright (c) 2002 Yuuichi Teranishi <[email protected]>
 * For license terms, see the file COPYING in this directory.
 *
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */

#include <ctype.h>

#include <libxml/xmlversion.h>
#include <libxml/xmlmemory.h>
#include <libxml/nanohttp.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlerror.h>

#include "nd.h"

ndNodeInfoPtr
ndNodeInfoNew ()
{
  ndNodeInfoPtr ret = xmlMalloc (sizeof (ndNodeInfo));
  if (ret == NULL) return (NULL);
  memset(ret, 0, sizeof(ndNodeInfo));
  return(ret);
}

void
ndNodeInfoFree (info)
     ndNodeInfoPtr info;
{
  if (info == NULL) return;
  if (info->name) xmlFree(info->name);
  if (info->date) xmlFree(info->date);
  if (info->cdate) xmlFree(info->cdate);
  if (info->size) xmlFree(info->size);
  if (info->content) xmlFree(info->content);
  if (info->restype) xmlFree(info->restype);
  if (info->props) ndPropListFree(info->props);
  if (info->lock) ndLockInfoListFree (info->lock);
  xmlFree (info);
}

void
ndNodeInfoListFree (info)
     ndNodeInfoPtr info;
{
  ndNodeInfoPtr cur = info;

  while (cur != NULL)
    {
      info = info->next;
      ndNodeInfoFree(cur);
      cur = info;
    }
}

void
ndNodeInfoPrint(fp, info, format)
     FILE *fp;
     ndNodeInfoPtr info;
     int format;
{
  if (format == ND_PRINT_AS_HEADER)
    {
      if (info == NULL)
	return;
      fprintf (fp, "Name: %s\n", info->name? info->name: "");
      fprintf (fp, "Status: %d\n", info->status);
      fprintf (fp, "Last-Modified: %s\n", info->date? info->date: "");
      fprintf (fp, "Created: %s\n", info->cdate? info->cdate: "");
      fprintf (fp, "Size: %s\n", info->size? info->size: "");
      fprintf (fp, "Content-Type: %s\n", info->content? info->content: "");
      fprintf (fp, "Resource-Type: %s\n", info->restype? info->restype: "");
      if (info->props)
	{
	  ndPropListPrint (fp, info->props, format);
	}
      if (info->lock)
	{
	  ndLockInfoListPrint (fp, info->lock, format);
	}
      fprintf (fp, "\n");
    }
  else if (format == ND_PRINT_AS_SEXP)
    {
      if (info == NULL)
	return;
      fprintf (fp, "(\"%s\"", info->name? info->name: "");
      if (info->date)
	fprintf (fp, " (last-modified \"%s\")", info->date);
      if (info->cdate)
	fprintf (fp, " (created \"%s\")", info->cdate);
      if (info->status)
	fprintf (fp, " (status %d)", info->status);
      if (info->size)
	fprintf (fp, " (size %s)", info->size);
      if (info->content)
	fprintf (fp, " (content-type \"%s\")", info->content);
      if (info->restype)
	fprintf (fp, " (resourcetype \"%s\")", info->restype);
      if (info->props)
	{
	  fprintf (fp, " ");
	  ndPropListPrint (fp, info->props, format);
	}
      if (info->lock)
	{
	  fprintf (fp, " ");
	  ndLockInfoListPrint (fp, info->lock, format);
	}
      fprintf (fp, ")");
    }
}

void
ndNodeInfoListPrint (fp, info, format)
     FILE *fp;
     ndNodeInfoPtr info;
     int format;
{
  ndNodeInfoPtr cur = info;

  if (format == ND_PRINT_AS_SEXP)
    fprintf (fp, "(");
  while (cur != NULL)
    {
      info = info->next;
      ndNodeInfoPrint(fp, cur, format);
      cur = info;
      if (format == ND_PRINT_AS_SEXP && cur != NULL) fprintf (fp, " ");
    }
  if (format == ND_PRINT_AS_SEXP)
    fprintf (fp, ")\n");
}

ndLockInfoPtr
ndLockInfoNew ()
{
  ndLockInfoPtr ret = xmlMalloc (sizeof (ndLockInfo));
  if (ret == NULL) return (NULL);
  memset (ret, 0, sizeof(ndLockInfo));
  return (ret);
}

void
ndLockInfoFree (lock)
     ndLockInfoPtr lock;
{
  if (lock == NULL) return;
  if (lock->scope) xmlFree(lock->scope);
  if (lock->type) xmlFree(lock->type);
  if (lock->owner_href) xmlFree(lock->owner_href);
  if (lock->token) xmlFree(lock->token);
  if (lock->timeout) xmlFree(lock->timeout);
  xmlFree (lock);
}

void
ndLockInfoListFree (info)
     ndLockInfoPtr info;
{
  ndLockInfoPtr cur = info;

  while (cur != NULL)
    {
      info = info->next;
      ndLockInfoFree(cur);
      cur = info;
    }
}

void
ndLockInfoPrint (fp, lock, format)
     FILE *fp;
     ndLockInfoPtr lock;
     int format;
{
  if (format == ND_PRINT_AS_HEADER)
    {
      if (lock == NULL) return;
      fprintf (fp, "Lock: ");
      if (lock->token) fprintf (fp, "token=\"%s\",\n", lock->token);
      fprintf (fp, "      scope=\"%s\",\n", lock->scope? lock->scope : "");
      fprintf (fp, "      owner-href=\"%s\",\n",
	       lock->owner_href? lock->owner_href : "");
      fprintf (fp,
	       "      timeout=\"%s\"\n", lock->timeout? lock->timeout : "");
    }
  else if (format == ND_PRINT_AS_SEXP)
    {
      fprintf (fp, "(lock");
      if (lock->token) fprintf (fp, " (token \"%s\")", lock->token);
      fprintf (fp, " (scope %s)", lock->scope? lock->scope : "");
      fprintf (fp, " (owner-href \"%s\")",
	       lock->owner_href? lock->owner_href : "");
      fprintf (fp, " (timeout \"%s\"))",
	       lock->timeout? lock->timeout : "");
    }
}

void
ndLockInfoListPrint (fp, info, format)
     FILE *fp;
     ndLockInfoPtr info;
     int format;
{
  ndLockInfoPtr cur = info;

  if (format == ND_PRINT_AS_SEXP)
    fprintf (fp, "(lock-list ");
  while (cur != NULL)
    {
      info = info->next;
      ndLockInfoPrint(fp, cur, format);
      cur = info;
      if (format == ND_PRINT_AS_SEXP && cur != NULL) fprintf (fp, " ");
    }
  if (format == ND_PRINT_AS_SEXP)
    fprintf (fp, ")\n");
}

ndPropPtr
ndPropNew ()
{
  ndPropPtr ret = xmlMalloc (sizeof (ndProp));
  if (ret == NULL) return (NULL);
  memset (ret, 0, sizeof(ndProp));
  return (ret);
}

void
ndPropFree (prop)
     ndPropPtr prop;
{
  if (prop == NULL) return;
  if (prop->name) xmlFree(prop->name);
  if (prop->value) xmlFree(prop->value);
  if (prop->ns) xmlFree(prop->ns);
  xmlFree (prop);
}

void
ndPropListFree (prop)
     ndPropPtr prop;
{
  ndPropPtr cur = prop;

  while (cur != NULL)
    {
      prop = prop->next;
      ndPropFree(cur);
      cur = prop;
    }
}

void
ndPropPrint (fp, prop, format)
     FILE *fp;
     ndPropPtr prop;
     int format;
{
  if (format == ND_PRINT_AS_HEADER)
    {
      if (prop == NULL) return;
      if (prop->name)
	{
	  if (prop->value && *prop->value == '\"')
	    {
	      fprintf (fp, "Property: %s=%s", prop->name, prop->value);
	    }
	  else
	    {
	      if (prop->value)
		fprintf (fp, "Property: %s=\"%s\"", prop->name, prop->value);
	    }
	}
      if (prop->ns)
	{
	  fprintf (fp, "; ns=\"%s\"\n", prop->ns);
	}
      else
	{
	  fprintf (fp, "\n");
	}
    }
  else if (format == ND_PRINT_AS_SEXP)
    {
      fprintf (fp, "(property");
      fprintf (fp, " (name \"%s\")", prop->name? prop->name : "");
      if (prop->value && *prop->value == '\"')
	{
	  fprintf (fp, " (value %s)", prop->value);
	}
      else
	{
	  fprintf (fp, " (value \"%s\")", prop->value? prop->value : "");
	}
      if (prop->ns)
	fprintf (fp, " (ns \"%s\")", prop->ns);
      fprintf (fp, ")");
    }
}

void
ndPropListPrint (fp, prop, format)
     FILE *fp;
     ndPropPtr prop;
     int format;
{
  ndPropPtr cur = prop;

  while (cur != NULL)
    {
      prop = prop->next;
      ndPropPrint(fp, cur, format);
      cur = prop;
      if (format == ND_PRINT_AS_SEXP && cur != NULL) fprintf (fp, " ");
    }
}

void*
ndHTTPMethod (URL, auth, method, input, contentType, headers, ilen)
     const char *URL;
     ndAuthCtxtPtr auth;
     const char *method;
     const char *input;
     char **contentType;
     const char *headers;
     int ilen;
{
  void *ctxt;
  int returnCode;
  char *auth_header = NULL;
  char *proxy_auth_header = NULL;
  xmlBufferPtr header_buf = NULL;
  char line [ND_HEADER_LINE_MAX];
  xmlBufferPtr temp_buf = NULL;

  header_buf = xmlBufferCreate ();
  if (header_buf == NULL) return NULL;

  if (auth && (auth->pauth_realm))
    {
      if (snprintf (line, ND_HEADER_LINE_MAX,
		    "Basic realm=\"%s\"", auth->pauth_realm) >=
	  ND_HEADER_LINE_MAX)
	{
	  return NULL;
	}
      if (ndAuthCreateHeader (line, auth->auth_cb, &temp_buf, 1) < 0)
	return NULL;
      if (temp_buf == NULL) return NULL;
      xmlBufferAdd (header_buf, xmlBufferContent (temp_buf), -1);
      xmlBufferFree (temp_buf);
    }
  if (auth && (auth->auth_realm))
    {
      if (snprintf (line, ND_HEADER_LINE_MAX,
		    "Basic realm=\"%s\"", auth->auth_realm) >=
	  ND_HEADER_LINE_MAX)
	{
	  return NULL;
	}
      if (ndAuthCreateHeader (line, auth->auth_cb, &temp_buf, 0) < 0)
	return NULL;
      if (temp_buf == NULL) return NULL;
      xmlBufferAdd (header_buf, xmlBufferContent (temp_buf), -1);
      xmlBufferFree (temp_buf);
    }

  if (headers) xmlBufferAdd (header_buf, (xmlChar *)headers, -1);

  ctxt = xmlNanoHTTPMethod (URL, method, input, contentType,
			    xmlBufferLength (header_buf) == 0 ?
			    NULL : (const char *)xmlBufferContent (header_buf), ilen);
  xmlBufferFree (header_buf);

  if (ctxt == NULL) return(NULL);
  returnCode = xmlNanoHTTPReturnCode(ctxt);
  while (returnCode == 401 || returnCode == 407)
    {
      auth_header = (char *) xmlNanoHTTPAuthHeader (ctxt);
      if (!auth || auth->auth_cb == NULL)
	return ctxt;
      if (ndAuthCreateHeader (auth_header, auth->auth_cb, &header_buf,
			      (returnCode == 407)) < 0)
	return NULL;
      if (header_buf == NULL) return (NULL);
      if (returnCode == 407)
	{
	  if (proxy_auth_header) xmlFree (proxy_auth_header);
	  proxy_auth_header = xmlMemStrdup ((const char *)xmlBufferContent (header_buf));
	  xmlBufferFree (header_buf);
	  header_buf = xmlBufferCreate ();
	}
      if (headers)
	xmlBufferAdd (header_buf, (xmlChar *)headers, -1);
      if (proxy_auth_header)
	xmlBufferAdd (header_buf, (xmlChar *)proxy_auth_header, -1);
      /* XXX If HTTP/1.1 and Keep-Alive, no need to close here. */
      xmlNanoHTTPClose (ctxt);
      ctxt = xmlNanoHTTPMethod(URL, method, input, contentType,
			       (const char *)xmlBufferContent (header_buf), ilen);
      xmlBufferFree (header_buf);
      if (ctxt == NULL) return(NULL);
      returnCode = xmlNanoHTTPReturnCode(ctxt);
    }
  if (proxy_auth_header) xmlFree (proxy_auth_header);
  if (auth && auth->notify_cb) auth->notify_cb (ctxt);
  return ctxt;
}


/* Generic DAV request function. */
int
nd_dav_request (method, url, auth, header, content, length,
		buf_return)
     char *method;
     char *url;
     ndAuthCtxtPtr auth;
     char *header;
     char *content;
     int length;
     xmlBufferPtr *buf_return;
{
  void *ctxt;
  char *contentType = "text/xml; charset=\"utf-8\"";
  int len;
  xmlChar buffer [1024];
  xmlBufferPtr output;
  int returnCode;

  if (url == NULL) return -1;

  ctxt = ndHTTPMethod(url, auth,
		      method, content, &contentType, header,
		      content? strlen (content): 0);
  if (ctxt == NULL)
    {
      return -1;
    }
  returnCode = xmlNanoHTTPReturnCode(ctxt);
  if ((returnCode >= 300) ||
      ((contentType != NULL) && (strncmp(contentType, "text/xml", 8)))) {
    xmlNanoHTTPClose(ctxt);
    if (contentType != NULL) xmlFree(contentType);
    return returnCode;
  }
  if (contentType != NULL) xmlFree(contentType);

  output = xmlBufferCreate ();
  if (output == NULL) return -1;

  while ( (len = xmlNanoHTTPRead (ctxt, buffer, sizeof (buffer))) > 0)
    {
      xmlBufferAdd (output, buffer, len);
    }
  xmlNanoHTTPClose(ctxt);
  *buf_return = output;
  return returnCode;
}

/*
 * PROPFIND(ALLPROP)
 */
int
nd_propfind_all_query (url, auth, depth, buf_return)
     char *url;
     ndAuthCtxtPtr auth;
     int depth;
     xmlBufferPtr *buf_return;
{
  char *verboseQuery =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
<D:propfind xmlns:D=\"DAV:\">\
    <D:allprop/>\
</D:propfind>";
  char depth_header [ND_HEADER_LINE_MAX];
  char *depth_str;

  switch (depth)
    {
    case ND_DEPTH_0:
      depth_str = "0";
      break;
    case ND_DEPTH_1:
      depth_str = "1";
      break;
    case ND_DEPTH_INFINITE:
      depth_str = "Infinity";
      break;
    }
  sprintf (depth_header, "Depth: %s\r\n", depth_str);
  return nd_dav_request ("PROPFIND", url, auth,
			 depth_header, verboseQuery,
			 strlen (verboseQuery), buf_return);
  /*
  if (url[strlen(url) - 1] == '/')
    return nd_dav_request ("PROPFIND", url, fn, "Depth: 1\r\n", NULL, 0,
			   buf_return);
  else
    return nd_dav_request ("PROPFIND", url, fn, "Depth: 0\r\n", verboseQuery,
			   strlen (verboseQuery),
			   buf_return);
  */
}

/*
 * PROPFIND
 */
int
nd_propfind_query (url, auth, prop, ns, depth, buf_return)
     char *url;
     ndAuthCtxtPtr auth;
     char *prop;
     char *ns;
     int depth;
     xmlBufferPtr *buf_return;
{
  char depth_header [ND_HEADER_LINE_MAX];
  char propfind_request [ND_REQUEST_MAX];
  char *depth_str;

  if (ns == NULL)
    {
      ns = "DAV:";
    }
  switch (depth)
    {
    case ND_DEPTH_0:
      depth_str = "0";
      break;
    case ND_DEPTH_1:
      depth_str = "1";
      break;
    case ND_DEPTH_INFINITE:
      depth_str = "Infinity";
      break;
    }
  sprintf (depth_header, "Depth: %s\r\n", depth_str);
  if (snprintf (propfind_request, ND_REQUEST_MAX,
		"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
<D:propfind xmlns:D=\"DAV:\">\
  <D:prop xmlns:ns=\"%s\"><ns:%s/></D:prop>\
</D:propfind>", ns, prop) >= ND_REQUEST_MAX)
    {
      return -1;
    }
  return nd_dav_request ("PROPFIND", url, auth,
			 depth_header, propfind_request,
			 strlen (propfind_request), buf_return);
}

int
nd_dav_name_equal (node, name)
     xmlNodePtr node;
     char *name;
{
  if (node->ns && node->ns->href && (!strcmp ((char *)node->ns->href, "DAV:")))
    return !strcmp ((char *) node->name, name);
  return 0;
}

ndLockInfoPtr
nd_parse_activelock (cur)
     xmlNodePtr cur;
{
  ndLockInfoPtr ret = NULL;

  ret = ndLockInfoNew ();
  cur = cur->children;

  /*
   * activelock (lockscpoe, locktype, depth, owner?, timeout? locktoken?)
   */
  while (cur != NULL)
    {
      if (nd_dav_name_equal (cur, "lockscope"))
	{
	  if (cur->children != NULL)
	    {
	      ret->scope = xmlMemStrdup ((char *) cur->children->name);
	    }
	}
      else if (nd_dav_name_equal (cur, "owner"))
	{
	  if (cur->children != NULL)
	    {
	      if (nd_dav_name_equal (cur->children, "href"))
		ret->owner_href = (char *) xmlNodeGetContent (cur->children);
	    }
	}
      else if (nd_dav_name_equal (cur, "locktype"))
	{
	  ret->type = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "locktoken"))
	{
	  char *curp, *start;
	  char *tmp = (char *) xmlNodeGetContent(cur);
	  if (tmp)
	    {
	      curp = tmp;
	      while (isspace(*curp))
		{
		  curp++;
		}
	      start = curp;
	      while (!isspace(*curp))
		{
		  curp++;
		}
	      if (*curp != '\0')
		*curp = '\0';
	      ret->token = xmlMemStrdup (start);
	      xmlFree (tmp);
	    }
	}
      else if (nd_dav_name_equal (cur, "depth"))
	{
	  ret->depth = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "timeout"))
	{
	  ret->timeout = (char *) xmlNodeGetContent(cur);
	}
      cur = cur->next;
    }
  return(ret);
}

ndLockInfoPtr
nd_parse_lockdiscovery (cur)
     xmlNodePtr cur;
{
  ndLockInfoPtr ret, last, res;

  ret = last = NULL;
  cur = cur->children;
  while (cur != NULL)
    {
      if (nd_dav_name_equal (cur, "activelock"))
	{
	  res = nd_parse_activelock (cur);
	  if (res != NULL)
	    {
	      if (ret == NULL) ret = last = res;
	      else last->next = res;
	      while (last->next != NULL) last = last->next;
	    }
	}
      cur = cur->next;
    }
  return ret;
}

void
nd_parse_prop (cur, node)
     xmlNodePtr cur;
     ndNodeInfoPtr node;
{
  cur = cur->children;
  /*
   * prop ANY
   */
  while (cur != NULL)
    {
      if (nd_dav_name_equal (cur, "lockdiscovery"))
	{
	  node->lock = nd_parse_lockdiscovery (cur);
	}
      else if (nd_dav_name_equal (cur, "supportedlock"))
	{
	  // not supported yet.
	}
      else if (nd_dav_name_equal (cur, "getetag"))
	{
	  // not supported yet.
	}
      else if (nd_dav_name_equal (cur, "getlastmodified"))
	{
	  node->date = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "creationdate"))
	{
	  node->cdate = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "getcontenttype"))
	{
	  node->content = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "getcontentlength"))
	{
	  node->size = (char *) xmlNodeGetContent(cur);
	}
      else if (nd_dav_name_equal (cur, "resourcetype"))
	{
	  if (cur->children != NULL)
	    {
	      node->restype = xmlMemStrdup ((char *) cur->children->name);
	    }
	}
      else if (strcmp((char *)cur->name,"text")) /* XXX is this true? */
	{
	  ndPropPtr prop;
	  prop = node->props;
	  node->props = ndPropNew();
	  node->props->name = xmlMemStrdup((char *) cur->name);
	  node->props->value = (char *) xmlNodeGetContent(cur);
	  if (cur->ns && cur->ns->href && strcmp((char *)cur->ns->href, "DAV:"))
	    {
	      node->props->ns = xmlMemStrdup((char *) cur->ns->href);
	    }
	  // ndPropPrint (stderr, node->props, ND_PRINT_AS_SEXP);
	  node->props->next = prop;
	}
      cur = cur->next;
    }
}

int
nd_parse_propstat (cur, node)
     xmlNodePtr cur;
     ndNodeInfoPtr node;
{
  int code = -1;
  cur = cur->children;

  /*
   * propstat (prop, status, responsedescription?)
   */
  while (cur != NULL)
    {
      if (nd_dav_name_equal (cur, "prop"))
	{
	  nd_parse_prop (cur, node);
	}
      else if (nd_dav_name_equal (cur, "status"))
	{
	  /* Property status */
	  char *status = (char *) xmlNodeGetContent (cur);
	  char *cur = status;
	  if (!strncmp(status, "HTTP/", 5))
	    {
	      int version = 0;
	      int ret = 0;

	      cur += 5;
	      while ((*cur >= '0') && (*cur <= '9')) {
		version *= 10;
		version += *cur - '0';
		cur++;
	      }
	      if (*cur == '.') {
		cur++;
		if ((*cur >= '0') && (*cur <= '9')) {
		  version *= 10;
		  version += *cur - '0';
		  cur++;
		}
		while ((*cur >= '0') && (*cur <= '9'))
		  cur++;
	      } else
		version *= 10;
	      if ((*cur != ' ') && (*cur != '\t')) return -1;
	      while ((*cur == ' ') || (*cur == '\t')) cur++;
	      if ((*cur < '0') || (*cur > '9')) return -1;
	      while ((*cur >= '0') && (*cur <= '9')) {
		ret *= 10;
		ret += *cur - '0';
		cur++;
	      }
	      if ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
		code = -1;
	      code = ret;
	    }
	  xmlFree (status);
	}
      else if (nd_dav_name_equal (cur, "responsedescription"))
	{
	}
      cur = cur->next;
    }
  return code;
}

ndNodeInfoPtr
nd_parse_prop_href(cur)
     xmlNodePtr cur;
{
  ndNodeInfoPtr ret;

  ret = ndNodeInfoNew();
  ret->name = (char *) xmlNodeGetContent(cur);
  return (ret);
}

ndNodeInfoPtr
nd_parse_response(cur)
     xmlNodePtr cur;
{
  ndNodeInfoPtr ret, last, res = NULL;

  ret = last = NULL;
  cur = cur->children;

  /*
   * response (href, ((href*, status)|(propstat+)), responsedescription?
   */
  while (cur != NULL)
    {
      if (nd_dav_name_equal (cur, "href"))
	{
	  res = nd_parse_prop_href (cur);
	  if (res != NULL)
	    {
	      if (ret == NULL) ret = last = res;
	      else last->next = res;
	      while (last->next != NULL) last = last->next;
	    }
	}
      else if (nd_dav_name_equal (cur, "propstat"))
	{
	  if (res)
	    {
	      res->status = nd_parse_propstat(cur, res);
	    }
	}
      else if (nd_dav_name_equal (cur, "status"))
	{
	  if (res)
	    {
	      char *status = (char *) xmlNodeGetContent (cur);
	      char *cur = status;
	      if (!strncmp(status, "HTTP/", 5))
		{
		  int version = 0;
		  int ret = 0;

		  cur += 5;
		  while ((*cur >= '0') && (*cur <= '9')) {
		    version *= 10;
		    version += *cur - '0';
		    cur++;
		  }
		  if (*cur == '.') {
		    cur++;
		    if ((*cur >= '0') && (*cur <= '9')) {
		      version *= 10;
		      version += *cur - '0';
		      cur++;
		    }
		    while ((*cur >= '0') && (*cur <= '9'))
		      cur++;
		  } else
		    version *= 10;
		  if ((*cur != ' ') && (*cur != '\t')) return NULL;
		  while ((*cur == ' ') || (*cur == '\t')) cur++;
		  if ((*cur < '0') || (*cur > '9')) return NULL;
		  while ((*cur >= '0') && (*cur <= '9')) {
		    ret *= 10;
		    ret += *cur - '0';
		    cur++;
		  }
		  if ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
		    return NULL;
		  res->status = ret;
		}
	      xmlFree (status);
	    }
	}
      else if (nd_dav_name_equal (cur, "responsedescription"))
	{
	}
      cur = cur->next;
    }
  return (ret);
}

ndNodeInfoPtr
nd_parse_multistatus (cur)
     xmlNodePtr cur;
{
  ndNodeInfoPtr ret = NULL, last, res;

  if (nd_dav_name_equal (cur, "multistatus"))
    {
      ret = last = NULL;
      cur = cur->children;
      while (cur != NULL)
	{
	  if (nd_dav_name_equal (cur, "response"))
	    {
	      res = nd_parse_response (cur);
	      if (res != NULL)
		{
		  if (ret == NULL) ret = last = res;
		  else last->next = res;
		  while (last->next != NULL) last = last->next;
		}
	    }
	  cur = cur->next;
	}
    }
  return ret;
}

int
ndPropFind (url, auth, prop, ns, depth, ni_return)
     char *url;
     ndAuthCtxtPtr auth;
     char *prop;
     char *ns;
     int depth;
     ndNodeInfoPtr *ni_return;
{
  ndNodeInfoPtr ret;
  xmlDocPtr doc;
  xmlBufferPtr buf = NULL;
  int code;

  if (prop == NULL)
    {
      code = nd_propfind_all_query (url, auth, depth, &buf);
    }
  else
    {
      code = nd_propfind_query (url, auth, prop, ns, depth, &buf);
    }
  if (code == -1)
    return -1;
  if (buf == NULL)
    return code;
  // fprintf (stderr, "%s\n", xmlBufferContent(buf));
  doc = xmlParseMemory((const char *)xmlBufferContent(buf), xmlBufferLength(buf));
  if (doc == NULL)
    {
      xmlBufferFree (buf);
      return -1;
    }
  ret = nd_parse_multistatus (doc->children);
  xmlFreeDoc(doc);
  xmlBufferFree(buf);
  *ni_return = ret;

  return code;
}

/*
 * PROPPATCH
 */
int
ndPropPatch (url, auth, prop, value, ns, lock_token, ni_return)
     char *url;
     ndAuthCtxtPtr auth;
     char *prop;
     char *value;
     char *ns;
     char *lock_token;
     ndNodeInfoPtr *ni_return;
{
  char proppatch_request [ND_REQUEST_MAX];
  char hstr [ND_HEADER_LINE_MAX];
  ndNodeInfoPtr ret;
  xmlBufferPtr buf = NULL;
  xmlDocPtr doc;
  int code;

  if (ns == NULL)
    ns = "DAV:";
  if (lock_token)
    {
      if (snprintf (hstr, ND_HEADER_LINE_MAX, "If: <%s> (<%s>)\r\n",
		    url, lock_token) >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
    }
  else
    {
      hstr[0] = '\0';
    }
  if (value)
    {
      if (snprintf (proppatch_request, ND_REQUEST_MAX,
		    "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n\
<D:propertyupdate xmlns:D=\"DAV:\">\n\
<D:set>\n\
<D:prop><ns:%s xmlns:ns=\"%s\">%s</ns:%s></D:prop>\n\
</D:set>\n\
 </D:propertyupdate>", prop, ns, value, prop) >= ND_REQUEST_MAX)
	{
	  return -1;
	}
    }
  else
    { /* No value at all. REMOVE. */
      if (snprintf (proppatch_request, ND_REQUEST_MAX,
		    "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n\
<D:propertyupdate xmlns:D=\"DAV:\">\n\
<D:remove>\n\
<D:prop><ns:%s xmlns:ns=\"%s\"/></D:prop>\n\
</D:remove>\n\
 </D:propertyupdate>", prop, ns) >= ND_REQUEST_MAX)
	{
	  return -1;
	}
    }
  code = nd_dav_request ("PROPPATCH", url, auth, hstr, proppatch_request,
			 strlen (proppatch_request), &buf);
  if (code == -1)
    return -1;
  if (code == 207) /* Multi status. */
    {
      if (buf == NULL)
	return -1;
      doc = xmlParseMemory((const char*)xmlBufferContent(buf), xmlBufferLength(buf));
      if (doc == NULL)
	{
	  xmlBufferFree (buf);
	  return -1;
	}
      ret = nd_parse_multistatus (doc->children);
      xmlFreeDoc(doc);
      xmlBufferFree(buf);
      *ni_return = ret;
    }
  return code;
}

/*
 * PUT
 */
int
ndPut (url, auth, content, length, token, ni_return)
     char *url;
     ndAuthCtxtPtr auth;
     char *content;
     int   length;
     char *token;
     ndNodeInfoPtr *ni_return;
{
  void *ctxt;
  char if_header[ND_HEADER_LINE_MAX];
  int code;
  ndNodeInfoPtr ret;
  xmlChar s [1024];
  xmlBufferPtr buf;
  xmlDocPtr doc;
  int len;

  if (url == NULL) return -1;
  if_header [0] = '\0';
  if (token)
    {
      if (snprintf (if_header, ND_HEADER_LINE_MAX,
		    "If: <%s> (<%s>)\r\n", url, token) >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
    }
  ctxt = ndHTTPMethod (url, auth, "PUT", content, NULL,
		       token? if_header : NULL,
		       length);
  if (ctxt == NULL) {
    return -1;
  }
  code = xmlNanoHTTPReturnCode(ctxt);
  if (code == 207) /* Multi status. */
    {
      buf = xmlBufferCreate ();
      if (buf == NULL) return -1;
      while ( (len = xmlNanoHTTPRead (ctxt, s, sizeof (s))) > 0)
	{
	  xmlBufferAdd (buf, s, len);
	}

      doc = xmlParseMemory((const char*)xmlBufferContent(buf), xmlBufferLength(buf));
      if (doc == NULL)
	{
	  xmlBufferFree (buf);
	  return -1;
	}
      ret = nd_parse_multistatus (doc->children);
      xmlFreeDoc(doc);
      xmlBufferFree (buf);
      *ni_return = ret;
    }
  xmlNanoHTTPClose (ctxt);
  return code;
}

/*
 * POST
 */

int
ndPost (url, auth, content, length, content_type, buf_return)
     char *url;
     ndAuthCtxtPtr auth;
     char *content;
     int length;
     char **content_type;
     xmlBufferPtr *buf_return;
{
  void *ctxt;
  int len;
  xmlChar buffer [1024];
  xmlBufferPtr output;
  int returnCode;

  if (url == NULL) return -1;
  ctxt = ndHTTPMethod(url, auth, "POST", content, content_type, NULL,
		      length);
  if (ctxt == NULL) {
    return -1;
  }
  returnCode = xmlNanoHTTPReturnCode(ctxt);
  if (returnCode >= 300)
    {
      xmlNanoHTTPClose(ctxt);
      if (content_type != NULL) xmlFree(content_type);
      return returnCode;
    }
  output = xmlBufferCreate ();
  if (output == NULL) return -1;
  while ((len = xmlNanoHTTPRead (ctxt, buffer, sizeof (buffer))) > 0)
    {
      xmlBufferAdd (output, buffer, len);
    }
  xmlNanoHTTPClose(ctxt);
  *buf_return = output;
  return returnCode;
}

int
ndPostPrint (url, auth, content, length, content_type, outfp)
     char *url;
     ndAuthCtxtPtr auth;
     char *content;
     int length;
     char **content_type;
     FILE *outfp;
{
  void *ctxt;
  int len;
  xmlChar buffer [32];
  int returnCode;

  if (url == NULL) return -1;

  ctxt = ndHTTPMethod(url, auth, "POST", content, content_type, NULL,
		      length);
  if (ctxt == NULL) return -1;

  returnCode = xmlNanoHTTPReturnCode(ctxt);
  if (returnCode >= 300)
    {
      xmlNanoHTTPClose(ctxt);
      if (content_type != NULL) xmlFree(content_type);
      return returnCode;
    }
  while ((len = xmlNanoHTTPRead (ctxt, buffer, sizeof (buffer))) > 0)
    {
      fwrite (buffer, len, sizeof (xmlChar), outfp);
    }
  xmlNanoHTTPClose(ctxt);
  return returnCode;
}

/*
 * DELETE
 */
int
ndDelete (url, auth, token)
     char *url;
     ndAuthCtxtPtr auth;
     char *token;
{
  void *ctxt;
  char if_header[ND_HEADER_LINE_MAX];
  int code;

  if (url == NULL) return -1;
  if_header [0] = '\0';
  if (token)
    {
      if (snprintf (if_header, ND_HEADER_LINE_MAX,
		    "If: <%s> (<%s>)\r\n", url, token) >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
    }
  ctxt = ndHTTPMethod (url, auth, "DELETE", NULL, NULL,
		       token? if_header : NULL,
		       0);
  if (ctxt == NULL) {
    return(-1);
  }
  code = xmlNanoHTTPReturnCode(ctxt);
  xmlNanoHTTPClose (ctxt);
  return code;
}

ndLockInfoPtr
nd_parse_lock_answer (cur)
     xmlNodePtr cur;
{
  ndLockInfoPtr ret = NULL;

  if (nd_dav_name_equal (cur, "prop"))
    {
      ret = NULL;
      cur = cur->children;
      while (cur != NULL)
	{
	  if (nd_dav_name_equal (cur, "lockdiscovery"))
	    {
	      ret = nd_parse_lockdiscovery (cur);
	    }
	  cur = cur->next;
	}
    }
  return (ret);
}

int
nd_lock_request (url, auth, depth, owner, scope, timeout, buf_return)
     char *url;
     ndAuthCtxtPtr auth;
     int  depth;
     char *owner;
     int   scope;
     char *timeout;
     xmlBufferPtr *buf_return;
{
  char lock_request[ND_REQUEST_MAX];
  char scope_str[16];
  char hstr [ND_HEADER_LINE_MAX];
  char *depth_str;

  if (!owner)
    {
      owner = "unknown";
    }
  switch (scope)
    {
    case ND_LOCK_SCOPE_EXCLUSIVE:
      sprintf (scope_str, "exclusive");
      break;
    case ND_LOCK_SCOPE_SHARED:
      sprintf (scope_str, "shared");
      break;
    default:
      sprintf (scope_str, "none");
      break;
    }
  if (snprintf (lock_request, ND_REQUEST_MAX,
		"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
<D:lockinfo xmlns:D='DAV:'>\
<D:lockscope><D:%s/></D:lockscope>\
<D:locktype><D:write/></D:locktype>\
<D:owner>\
<D:href>%s</D:href>\
</D:owner>\
</D:lockinfo>", scope_str, owner) >= ND_REQUEST_MAX)
    {
      return -1;
    }
  switch (depth)
    {
    case ND_DEPTH_0:
    case ND_DEPTH_1: /* DEPTH 1 is not allowed. */
      depth_str = "0";
      break;
    case ND_DEPTH_INFINITE:
      depth_str = "Infinity";
      break;
    }
  if (timeout)
    {
      if (snprintf (hstr, ND_HEADER_LINE_MAX,
		    "Timeout: %s\r\nDepth: %s\r\n", timeout, depth_str)
	  >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
    }
  else
    sprintf (hstr, "Depth: %s\r\n", depth_str);
  return nd_dav_request ("LOCK", url, auth, hstr, lock_request,
			 strlen (lock_request), buf_return);
}

int
ndLock (url, auth, depth, owner, scope, timeout, li_return)
     char *url;
     ndAuthCtxtPtr auth;
     int   depth;
     char *owner;
     int   scope;
     char *timeout;
     ndLockInfoPtr *li_return;
{
  ndLockInfoPtr ret;
  xmlDocPtr doc;
  xmlBufferPtr buf = NULL;
  int code;

  code = nd_lock_request (url, auth,
			  depth, owner, scope, timeout, &buf);
  if (buf != NULL)
    {
      doc = xmlParseMemory((const char*)xmlBufferContent(buf), xmlBufferLength(buf));
      if (doc == NULL)
	{
	  xmlBufferFree (buf);
	  return -1;
	}
      ret = nd_parse_lock_answer (doc->children);
      xmlFreeDoc(doc);
      xmlBufferFree (buf);
      *li_return = ret;
    }
  return code;

}

int
ndUnlock (url, auth, depth, token)
     char *url;
     ndAuthCtxtPtr auth;
     int  depth;
     char *token;
{
  void *ctxt;
  char hstr [ND_HEADER_LINE_MAX];
  int code;
  char *depth_str;

  switch (depth)
    {
    case ND_DEPTH_0:
      depth_str = "0";
      break;
    case ND_DEPTH_1:
      depth_str = "1";
      break;
    case ND_DEPTH_INFINITE:
      depth_str = "Infinity";
      break;
    }

  if (url == NULL || token == NULL) return -1;
  if (snprintf (hstr, ND_HEADER_LINE_MAX,
		"Lock-token: <%s>\r\nDepth: %s\r\n", token, depth_str)
      >= ND_HEADER_LINE_MAX)
    {
      return -1;
    }
  ctxt = ndHTTPMethod (url, auth, "UNLOCK", NULL, NULL,
		       hstr, 0);
  if (ctxt == NULL)
    {
      return -1;
    }
  code = xmlNanoHTTPReturnCode(ctxt);
  xmlNanoHTTPClose (ctxt);
  return code;
}

int
ndMkCol (url, auth, token)
     char *url;
     ndAuthCtxtPtr auth;
     char *token;
{
  void *ctxt;
  char if_header[ND_HEADER_LINE_MAX];
  int code;

  if (url == NULL) return -1;
  if_header [0] = '\0';
  if (token)
    {
      if (snprintf (if_header, ND_HEADER_LINE_MAX,
		    "If: <%s> (<%s>)\r\n", url, token) >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
    }
  ctxt = ndHTTPMethod (url, auth, "MKCOL", NULL, NULL,
		       token? if_header : NULL, 0);
  if (ctxt == NULL)
    {
      return -1;
    }
  code = xmlNanoHTTPReturnCode(ctxt);
  xmlNanoHTTPClose (ctxt);
  return code;
}

int
ndGet (url, auth, ct_return, buf_return)
     char *url;
     ndAuthCtxtPtr auth;
     char **ct_return;
     xmlBufferPtr *buf_return;
{
  void *ctxt;
  char *contentType;
  int len;
  xmlChar buffer [1024];
  xmlBufferPtr output;
  int returnCode;

  if (url == NULL) return -1;

  ctxt = ndHTTPMethod(url, auth, "GET", NULL, &contentType, NULL, 0);
  if (ctxt == NULL) {
    return -1;
  }

  returnCode = xmlNanoHTTPReturnCode(ctxt);
  if (returnCode >= 300)
    {
      xmlNanoHTTPClose(ctxt);
      if (contentType != NULL) xmlFree(contentType);
      return returnCode;
    }
  *ct_return = contentType;
  output = xmlBufferCreate ();
  if (output == NULL) return -1;
  while ((len = xmlNanoHTTPRead (ctxt, buffer, sizeof (buffer))) > 0)
    {
      xmlBufferAdd (output, buffer, len);
    }
  xmlNanoHTTPClose(ctxt);
  *buf_return = output;
  return returnCode;
}


int
ndGetPrint (url, auth, ct_return, outfp)
     char *url;
     ndAuthCtxtPtr auth;
     char **ct_return;
     FILE *outfp;
{
  void *ctxt;
  char *contentType = NULL;
  int len;
  xmlChar buffer [32];
  int returnCode;

  if (url == NULL) return -1;

  ctxt = ndHTTPMethod(url, auth, "GET", NULL, &contentType, NULL, 0);
  if (ctxt == NULL) return -1;

  returnCode = xmlNanoHTTPReturnCode(ctxt);
  if (returnCode >= 300)
    {
      xmlNanoHTTPClose(ctxt);
      if (contentType != NULL) xmlFree(contentType);
      return returnCode;
    }
  *ct_return = contentType;

  while ((len = xmlNanoHTTPRead (ctxt, buffer, sizeof (buffer))) > 0)
    {
      fwrite (buffer, len, sizeof (xmlChar), outfp);
    }
  xmlNanoHTTPClose(ctxt);
  return returnCode;
}

int
ndMove (url, auth, dest_url, overwrite, lock_token)
     char *url;
     ndAuthCtxtPtr auth;
     char *dest_url;
     int overwrite;
     char *lock_token;
{
  void *ctxt;
  char hstr [ND_HEADER_LINE_MAX*3];
  char hstr_1 [ND_HEADER_LINE_MAX];
  int code;

  if (url == NULL || dest_url == NULL) return -1;
  if (snprintf (hstr, ND_HEADER_LINE_MAX*3,
		"Destination: %s\r\nOverwrite: %c\r\n", dest_url,
		overwrite?  'T' : 'F') >= ND_HEADER_LINE_MAX*3)
    {
      return -1;
    }
  if (lock_token)
    {
      if (snprintf (hstr_1, ND_HEADER_LINE_MAX,
		    "If: <%s> (<%s>)\r\n", dest_url, lock_token)
	  >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
      if ((strlen (hstr) + strlen (hstr_1) + 1) > ND_HEADER_LINE_MAX * 3)
	{
	  return -1;
	}
      strcat (hstr, hstr_1);
    }
  ctxt = ndHTTPMethod (url, auth, "MOVE", NULL, NULL, hstr, 0);
  if (ctxt == NULL) {
    return(-1);
  }
  code = xmlNanoHTTPReturnCode(ctxt);
  xmlNanoHTTPClose (ctxt);
  return code;
}

int
ndCopy (url, auth, dest_url, overwrite, lock_token)
     char *url;
     ndAuthCtxtPtr auth;
     char *dest_url;
     int overwrite;
     char *lock_token;
{
  void *ctxt;
  char hstr [ND_HEADER_LINE_MAX*3];
  char hstr_1 [ND_HEADER_LINE_MAX];
  int code;

  if (url == NULL || dest_url == NULL) return -1;
  if (snprintf (hstr, ND_HEADER_LINE_MAX*3,
		"Destination: %s\r\nOverwrite: %c\r\n", dest_url,
		overwrite?  'T' : 'F') >= ND_HEADER_LINE_MAX*3)
    {
      return -1;
    }
  if (lock_token)
    {
      if (snprintf (hstr_1, ND_HEADER_LINE_MAX,
		    "If: <%s> (<%s>)\r\n", dest_url, lock_token)
	  >= ND_HEADER_LINE_MAX)
	{
	  return -1;
	}
      if ((strlen (hstr) + strlen (hstr_1) + 1) > ND_HEADER_LINE_MAX * 3)
	{
	  return -1;
	}
      strcat (hstr, hstr_1);
    }
  ctxt = ndHTTPMethod (url, auth, "COPY", NULL, NULL, hstr, 0);
  if (ctxt == NULL) {
    return(-1);
  }
  code = xmlNanoHTTPReturnCode(ctxt);
  xmlNanoHTTPClose (ctxt);
  return code;
}

char *
ndReasonPhrase (code)
     int code;
{
  switch (code)
    {
    case 100:
      return ND_CODE_100;
    case 101:
      return ND_CODE_101;
    case 102:
      return ND_CODE_102;
    case 200:
      return ND_CODE_200;
    case 201:
      return ND_CODE_201;
    case 202:
      return ND_CODE_202;
    case 203:
      return ND_CODE_203;
    case 204:
      return ND_CODE_204;
    case 205:
      return ND_CODE_205;
    case 206:
      return ND_CODE_206;
    case 207:
      return ND_CODE_207;
    case 300:
      return ND_CODE_300;
    case 301:
      return ND_CODE_301;
    case 302:
      return ND_CODE_302;
    case 303:
      return ND_CODE_303;
    case 304:
      return ND_CODE_304;
    case 305:
      return ND_CODE_305;
    case 307:
      return ND_CODE_307;
    case 400:
      return ND_CODE_400;
    case 401:
      return ND_CODE_401;
    case 402:
      return ND_CODE_402;
    case 403:
      return ND_CODE_403;
    case 404:
      return ND_CODE_404;
    case 405:
      return ND_CODE_405;
    case 406:
      return ND_CODE_406;
    case 407:
      return ND_CODE_407;
    case 408:
      return ND_CODE_408;
    case 409:
      return ND_CODE_409;
    case 410:
      return ND_CODE_410;
    case 411:
      return ND_CODE_411;
    case 412:
      return ND_CODE_412;
    case 413:
      return ND_CODE_413;
    case 414:
      return ND_CODE_414;
    case 415:
      return ND_CODE_415;
    case 416:
      return ND_CODE_416;
    case 417:
      return ND_CODE_417;
    case 422:
      return ND_CODE_422;
    case 423:
      return ND_CODE_423;
    case 424:
      return ND_CODE_424;
    case 500:
      return ND_CODE_500;
    case 501:
      return ND_CODE_501;
    case 502:
      return ND_CODE_502;
    case 503:
      return ND_CODE_503;
    case 504:
      return ND_CODE_504;
    case 505:
      return ND_CODE_505;
    case 507:
      return ND_CODE_507;
    }
  return ND_CODE_OTHER;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].