/*
* lock-cmd.c -- Lock files exclusively
*
* ====================================================================
* Copyright (c) 2000-2004 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/.
* ====================================================================
*/

/* ==================================================================== */



/*** Includes. ***/

#include "svn_private_config.h"
#include "svn_cmdline.h"
#include "svn_wc.h"
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_error.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_private_config.h"
#include "svn_auth.h"
#include "svn_ra.h"
#include "svn_base64.h"
#include "cl.h"

#include "svn_private_config.h"


/*** Code. ***/

/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__lock (apr_getopt_t *os,
                  void *baton,
                  apr_pool_t *pool)
{
  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) 
baton)->opt_state;
  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;

  apr_pool_t *subpool = svn_pool_create (pool);
  const char *pname, *pname_utf8, *URL;
  apr_array_header_t *args, *targets, *condensed_targets;
  int i, j;

  /* ra stuff */
  void *ra_baton, *session, *report_baton;
  svn_ra_plugin_t *ra_lib;
  const char* reposPath;
  svn_revnum_t rev;
  svn_string_t *cur_lock_owner;


  /* Suck up all the remaining arguments into a targets array */
  SVN_ERR (svn_opt_args_to_target_array (&targets, os,
                                         opt_state->targets,
                                         &(opt_state->start_revision),
                                         &(opt_state->end_revision),
                                         FALSE, pool));

  /* Add "." if user passed 0 arguments. */
  svn_opt_push_implicit_dot_target (targets, pool);

  /* Remove redundancies from the target list while preserving order. */
  SVN_ERR (svn_path_remove_redundancies (&condensed_targets,
                                         targets,
                                         pool));

  /** init RA layer lib */
  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));

  for (i = 0; i < condensed_targets->nelts; i++)
    {
      const char *target = ((const char **) (targets->elts))[i];
      svn_wc_adm_access_t *adm_access;
      const svn_wc_entry_t *entry;

      svn_pool_clear (subpool);
      SVN_ERR(svn_cl__check_cancel (ctx->cancel_baton));

      /* Make sure we're attempting to lock a versioned resource */
      SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL, target, FALSE,
                                        opt_state->recursive ? -1 : 0,
                                        subpool));

      SVN_ERR (svn_wc_entry (&entry, target, adm_access, FALSE, subpool));
      if (! entry)
        {
          SVN_ERR (svn_cmdline_printf
                    (subpool, _("Could not lock %s: Not a versioned 
resource\n"),
                    svn_path_local_style (target, pool)));
          continue;
        }
      else if (entry->kind == svn_node_dir)
        {
          SVN_ERR(svn_cmdline_printf( subpool,
                                      _("Could not lock '%s'; Directories 
can not be locked\n"),
                                      target));
          continue;
        }

      /* Convert URL */
      SVN_ERR (svn_client_url_from_path (&URL, target, subpool));
      if (URL == NULL)
        {
            SVN_ERR(svn_cmdline_printf(subpool, _("Unable to lock '%s'\n"), 
target));
            continue;
        }



      /* Get the RA library that handles this URL schema. */
      SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, URL, subpool));

      /* Open a repository session to the URL. */
      SVN_ERR ((svn_error_t*)svn_client__open_ra_session (&session, ra_lib, 
URL, NULL,
                                            NULL, NULL, TRUE, TRUE,
                                            ctx, subpool));

      /* Get root part of URL, e.g. http://myserver/svn/myrepos */
      ra_lib->get_repos_root(session, &reposPath, subpool);

      /* Get path relative to repos-root,
        i.e. from 'http://myserver/svn/repos/my/path' to 'my/path' */
      svn_string_t *repos_rel_path = svn_string_create((const 
char*)&URL[strlen(reposPath)+1], subpool);

      /* Base64 encode and get rid of =+\n to make the propname "human 
readable" */
      svn_stringbuf_t *encoded_path = svn_stringbuf_create_from_string(
                                      
svn_base64_encode_string(repos_rel_path, subpool), subpool);

      for (j=0; j < encoded_path->len; j++)
        {
          encoded_path->data[j] = ( encoded_path->data[j] == '\n' ||
                                    encoded_path->data[j] == '='  ||
                                    encoded_path->data[j] == '+')
                                    ? '-' : encoded_path->data[j];
        }

      /* Create property name in the form Flock-BASE64(/path/in/repos)
         The F-prefix is 1 if forcing the lock and 0 if not. The flag byte 
will
         be removed by the hook so the final propname is 
lock-BASE64(/path/in/repos)

         The prefix trick is due to a bug in pre-1.1 that prevents 
pre-revprop-change
         scripts of getting hold of the propvalue. Once the fix for that
         is released, a nicer solution might be to pass the force
         flag as the propvalue. */
      svn_stringbuf_t *lock_token = svn_stringbuf_create((opt_state->force ? 
"F" : "N"), subpool);
      svn_stringbuf_appendcstr(lock_token, "lock-");

      /* Append the encoded path, and we're done constructing the lock token 
*/
      svn_stringbuf_appendstr(lock_token, encoded_path);

      /* For the sake of completeness */
      SVN_ERR (svn_utf_cstring_to_utf8 (&pname_utf8, lock_token->data, 
subpool));

      /* Lock tokens are stored in rev 0 */
      svn_opt_revision_t rev0;
      rev0.kind = svn_opt_revision_number;
      rev0.value.number = 0;

      /* Fetch the current property */
      svn_error_t *err = svn_client_revprop_get (&pname_utf8[1],  /* drop 
force flag */
                                                 &cur_lock_owner,
                                                 URL, &rev0,
                                                 &rev, ctx, subpool);
      if (err)
        {
          SVN_ERR(svn_cmdline_printf(subpool, _("Getting current lock owner 
failed: '%s'\n"), err->message));
        }

      /* Set lock-owner. This will fail the in pre-revprop-change hook
         if the resource is locked by another user. If authentication is
         OK (or --force), the hook will set the revprop value = username */
      err = svn_client_revprop_set (pname_utf8,
                                    /* must supply a value, or the hook 
won't be called */
                                    svn_string_create( "ignore", subpool),
                                    URL, &rev0,
                                    &rev, TRUE,
                                    ctx, subpool);

      if (err)
        {
          if(!cur_lock_owner || !cur_lock_owner->data)
            SVN_ERR(svn_cmdline_printf(subpool, _("Setting lock failed and 
unable to obtain lock owner for %s (%s): %s\n"), target, &pname_utf8[1], 
err->message));
          else
	          /* this won't happend if the lock is --force'ed */
            SVN_ERR(svn_cmdline_printf(subpool, _("Could not lock %s: 
Already locked by '%s'\n"), target, cur_lock_owner->data));
        }
      else
        {
          SVN_ERR(svn_cmdline_printf(subpool, _("Successfully locked %s"), 
target));

          /* Print lock token in verbose mode */
          if(opt_state->verbose)
            SVN_ERR(svn_cmdline_printf(subpool, _(" (%s)\n"), 
&pname_utf8[1]));
          else
            SVN_ERR(svn_cmdline_printf(subpool, _("\n"), target));

        }

  }/* for each target */

  svn_pool_destroy (subpool);

  return SVN_NO_ERROR;
}


