[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

ra_dav auth

From: Ben Collins-Sussman <sussman_at_collab.net>
Date: 2001-09-18 00:45:39 CEST

Gee whiz, all I had to do was rewrite libsvn_ra_dav/session.c, and
suddenly I can do a dav checkout and it NO LONGER pre-emptively asks
me for a username/password. What a Great Thing!

Greg and Joe, this new session.c is working for me. I thought maybe
you could give it a quick look-over. I'm only 90% certain of what I'm
doing. :-)

In the meantime, I now have the tricky task of making ra_dav decide
exactly when to store auth info. I'm not sure where to poke around,
but here's what I want:

   * for checkouts, svn_ra_dav__close(session) should call the auth
     storage callback. This ensure that the working copy exists
     first.

   * for updates, the storage should happen *after* ra_dav calls
     update_editor->close_edit(). This ensures that any newly-added
     directories will get the info during the recursive store.

   * for commits, the storage can happen anytime after authentication
     succeeds, doesn't really matter.

   * for imports, the storage should *never* happen.

I'm a bit worried, because the RA interface doesn't distinguish
between commits and imports; they both use RA->get_commit_editor().
The answer here is that the client can simply set the storage callback
to NULL during an import, which means "don't call me". (I've already
documented that semantic.)

===================================================================

/*
 * session.c : routines for maintaining sessions state (to the DAV server)
 *
 * ====================================================================
 * Copyright (c) 2000-2001 CollabNet. All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals. For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */


#include <apr_pools.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_general.h>

#include <ne_socket.h>
#include <ne_request.h>
#include <ne_uri.h>
#include <ne_auth.h>

#include "svn_error.h"
#include "svn_ra.h"
#include "svn_version.h"

#include "ra_dav.h"

/* a cleanup routine attached to the pool that contains the RA session
   baton. */
static apr_status_t cleanup_session(void *sess)
{
  ne_session_destroy(sess);
  return APR_SUCCESS;
}

/* A routine that neon calls to 'pull' authentication data from the
   neon session. In turn, this routine should 'pull' the data from
   the client callbacks as needed. */
static int request_auth(void *userdata, const char *realm,
                        char **username, char **password)
{
  apr_size_t l;
  void *a, *auth_baton;
  char *uname, *pword;
  svn_ra_simple_password_authenticator_t *authenticator = NULL;
  svn_ra_session_t *ras = userdata;

  /* ### my only worry is that we're not catching any svn_errors from
     get_authenticator, get_username, get_password... */
  
  if (! ras->username)
    {
      /* pull the username from the client */
      ras->callbacks->get_authenticator (&a, &auth_baton,
                                         SVN_RA_AUTH_SIMPLE_PASSWORD,
                                         ras->callback_baton, ras->pool);
      authenticator = (svn_ra_simple_password_authenticator_t *) a;
      authenticator->get_username (&uname, auth_baton, ras->pool);
      ras->username = uname;
    }

  if (! ras->password)
    {
      if (! authenticator)
        {
          ras->callbacks->get_authenticator (&a, &auth_baton,
                                             SVN_RA_AUTH_SIMPLE_PASSWORD,
                                             ras->callback_baton, ras->pool);
          authenticator = (svn_ra_simple_password_authenticator_t *) a;
        }
      authenticator->get_password (&pword, auth_baton, ras->pool);
      ras->password = pword;
    }

  /* send a malloc'd copy of the username to neon */
  l = strlen(ras->username) + 1;
  *username = malloc(l);
  memcpy(*username, ras->username, l);
  
  /* send a malloc'd copy of the password to neon */
  l = strlen(ras->password) + 1;
  *password = malloc(l);
  memcpy(*password, ras->password, l);

  return 0;
}

/* ### need an ne_session_dup to avoid the second gethostbyname
 * call and make this halfway sane. */

static svn_error_t *
svn_ra_dav__open (void **session_baton,
                  svn_stringbuf_t *repos_URL,
                  svn_ra_callbacks_t *callbacks,
                  void *callback_baton,
                  apr_pool_t *pool)
{
  const char *repository = repos_URL->data;
  apr_size_t len;
  ne_session *sess, *sess2;
  struct uri uri = { 0 };
  svn_ra_session_t *ras;

  /* Sanity check the URI */
  if (uri_parse(repository, &uri, NULL)
      || uri.host == NULL || uri.path == NULL)
    {
      return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, 0, NULL, pool,
                              "illegal URL for repository");
    }

  /* Can we initialize network? */
  if (sock_init() != 0) {
    return svn_error_create(SVN_ERR_RA_SOCK_INIT, 0, NULL, pool,
                            "network socket initialization failed");
  }

  /* Create two neon session objects, and set their properties... */
  sess = ne_session_create();
  sess2 = ne_session_create();

#if 0
  /* #### enable this block for debugging output on stderr. */
  ne_debug_init(stderr, NE_DBG_HTTP|NE_DBG_HTTPBODY);
#endif

  /* ### persistent connection problems in Apache 2.0? seeing some
   * "Could not read status line" errors, and connections being closed
   * prematurely, etc. Turn them off for now. */
  ne_set_persist(sess, 0);

  /* make sure we will eventually destroy the session */
  apr_pool_cleanup_register(pool, sess, cleanup_session, apr_pool_cleanup_null);
  apr_pool_cleanup_register(pool, sess2, cleanup_session, apr_pool_cleanup_null);

  ne_set_useragent(sess, "SVN/" SVN_VERSION);
  ne_set_useragent(sess2, "SVN/" SVN_VERSION);

  /* we want to know if the repository is actually somewhere else */
  /* ### not yet: http_redirect_register(sess, ... ); */

  if (strcasecmp(uri.scheme, "https") == 0)
    {
      if (uri.port == -1)
        {
          uri.port = 443;
        }
      if (ne_set_secure(sess, 1))
        {
          return svn_error_create(SVN_ERR_RA_SOCK_INIT, 0, NULL, pool,
                                  "SSL is not supported");
        }

      ne_set_secure(sess2, 1);
    }
#if 0
  else
    {
      /* accept server-requested TLS upgrades... useless feature
       * currently since there is no server support yet. */
      (void) ne_set_accept_secure_upgrade(sess, 1);
    }
#endif

  if (uri.port == -1)
    {
      uri.port = 80;
    }

  if (ne_session_server(sess, uri.host, uri.port))
    {
      return svn_error_createf(SVN_ERR_RA_HOSTNAME_LOOKUP, 0, NULL, pool,
                               "Hostname not found: %s", uri.host);
    }

  ne_session_server(sess2, uri.host, uri.port);

  /* clean up trailing slashes from the URL */
  len = strlen(uri.path);
  if (len > 1 && uri.path[len - 1] == '/')
    uri.path[len - 1] = '\0';

  /* Register an authentication 'pull' callback with the neon sessions */
  ne_set_server_auth(sess, request_auth, ras);
  ne_set_server_auth(sess2, request_auth, ras);

  /* Create and fill a session_baton. */
  ras = apr_pcalloc(pool, sizeof(*ras));
  ras->pool = pool;
  ras->root = uri;
  ras->sess = sess;
  ras->sess2 = sess2;
  ras->callbacks = callbacks;
  ras->callback_baton = callback_baton;

  /* note that ras->username and ras->password are still NULL at this
     point. */

  *session_baton = ras;

  return SVN_NO_ERROR;
}

static svn_error_t *svn_ra_dav__close (void *session_baton)
{
  svn_ra_session_t *ras = session_baton;

  (void) apr_pool_cleanup_run(ras->pool, ras->sess, cleanup_session);
  (void) apr_pool_cleanup_run(ras->pool, ras->sess2, cleanup_session);
  return NULL;
}

static const svn_ra_plugin_t dav_plugin = {
  "ra_dav",
  "Module for accessing a repository via WebDAV (DeltaV) protocol.",
  svn_ra_dav__open,
  svn_ra_dav__close,
  svn_ra_dav__get_latest_revnum,
  svn_ra_dav__get_dated_revision,
  svn_ra_dav__get_commit_editor,
  svn_ra_dav__do_checkout,
  svn_ra_dav__do_update
};

svn_error_t *svn_ra_dav_init(int abi_version,
                             apr_pool_t *pconf,
                             apr_hash_t *hash)
{
  /* ### need a version number to check here... */
  if (abi_version != 0)
    ;

  apr_hash_set (hash, "http", APR_HASH_KEY_STRING, &dav_plugin);

#ifdef NEON_SSL
  /* Only add this if neon is compiled with SSL support. */
  apr_hash_set (hash, "https", APR_HASH_KEY_STRING, &dav_plugin);
#endif

  return NULL;
}


/*
 * local variables:
 * eval: (load-file "../svn-dev.el")
 * end:
 */

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Oct 21 14:36:41 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.