~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/mod_dav_svn/liveprops.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * liveprops.c: mod_dav_svn live property provider functions for Subversion
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
7
 * This software is licensed as described in the file COPYING, which
 
8
 * you should have received as part of this distribution.  The terms
 
9
 * are also available at http://subversion.tigris.org/license-1.html.
 
10
 * If newer versions of this license are posted there, you may use a
 
11
 * newer version instead, at your option.
 
12
 *
 
13
 * This software consists of voluntary contributions made by many
 
14
 * individuals.  For exact contribution history, see the revision
 
15
 * history and logs, available at http://subversion.tigris.org/.
 
16
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
 
 
21
#include <httpd.h>
 
22
#include <util_xml.h>
 
23
#include <apr_tables.h>
 
24
#include <apr_md5.h>
 
25
#include <mod_dav.h>
 
26
 
 
27
#include "dav_svn.h"
 
28
 
 
29
#include "svn_pools.h"
 
30
#include "svn_time.h"
 
31
#include "svn_dav.h"
 
32
#include "svn_md5.h"
 
33
#include "svn_props.h"
 
34
 
 
35
 
 
36
/*
 
37
** The namespace URIs that we use. This list and the enumeration must
 
38
** stay in sync.
 
39
*/
 
40
static const char * const dav_svn_namespace_uris[] =
 
41
{
 
42
    "DAV:",
 
43
    SVN_DAV_PROP_NS_DAV,
 
44
 
 
45
    NULL        /* sentinel */
 
46
};
 
47
enum {
 
48
    DAV_SVN_NAMESPACE_URI_DAV,  /* the DAV: namespace URI */
 
49
    DAV_SVN_NAMESPACE_URI       /* the dav<->ra_dav namespace URI */
 
50
};
 
51
 
 
52
#define SVN_RO_DAV_PROP(name) \
 
53
        { DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##name, 0 }
 
54
#define SVN_RW_DAV_PROP(name) \
 
55
        { DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##name, 1 }
 
56
#define SVN_RO_DAV_PROP2(sym,name) \
 
57
        { DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##sym, 0 }
 
58
#define SVN_RW_DAV_PROP2(sym,name) \
 
59
        { DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##sym, 1 }
 
60
 
 
61
#define SVN_RO_SVN_PROP(sym,name) \
 
62
        { DAV_SVN_NAMESPACE_URI, #name, SVN_PROPID_##sym, 0 }
 
63
#define SVN_RW_SVN_PROP(sym,name) \
 
64
        { DAV_SVN_NAMESPACE_URI, #name, SVN_PROPID_##sym, 1 }
 
65
 
 
66
 
 
67
enum {
 
68
  SVN_PROPID_baseline_relative_path = 1,
 
69
  SVN_PROPID_md5_checksum,
 
70
  SVN_PROPID_repository_uuid
 
71
};
 
72
 
 
73
static const dav_liveprop_spec dav_svn_props[] =
 
74
{
 
75
  /* ### don't worry about these for a bit */
 
76
#if 0
 
77
  /* WebDAV properties */
 
78
  SVN_RO_DAV_PROP(getcontentlanguage),  /* ### make this r/w? */
 
79
#endif
 
80
  SVN_RO_DAV_PROP(getcontentlength),
 
81
  SVN_RO_DAV_PROP(getcontenttype),      /* ### make this r/w? */
 
82
  SVN_RO_DAV_PROP(getetag),
 
83
  SVN_RO_DAV_PROP(creationdate),
 
84
  SVN_RO_DAV_PROP(getlastmodified),
 
85
 
 
86
  /* DeltaV properties */
 
87
  SVN_RO_DAV_PROP2(baseline_collection, baseline-collection),
 
88
  SVN_RO_DAV_PROP2(checked_in, checked-in),
 
89
  SVN_RO_DAV_PROP2(version_controlled_configuration,
 
90
                   version-controlled-configuration),
 
91
  SVN_RO_DAV_PROP2(version_name, version-name),
 
92
  SVN_RO_DAV_PROP2(creator_displayname, creator-displayname),
 
93
  SVN_RO_DAV_PROP2(auto_version, auto-version),
 
94
 
 
95
  /* SVN properties */
 
96
  SVN_RO_SVN_PROP(baseline_relative_path, baseline-relative-path),
 
97
  SVN_RO_SVN_PROP(md5_checksum, md5-checksum),
 
98
  SVN_RO_SVN_PROP(repository_uuid, repository-uuid),
 
99
 
 
100
  { 0 } /* sentinel */
 
101
};
 
102
 
 
103
static const dav_liveprop_group dav_svn_liveprop_group =
 
104
{
 
105
    dav_svn_props,
 
106
    dav_svn_namespace_uris,
 
107
    &dav_svn_hooks_liveprop
 
108
};
 
109
 
 
110
/* Set *PROPVAL to the value for the revision property PROPNAME on
 
111
   COMMITTED_REV, in the repository identified by RESOURCE, if
 
112
   RESOURCE's path is readable.  If it is not readable, set *PROPVAL
 
113
   to NULL and return SVN_NO_ERROR.  Use POOL for temporary
 
114
   allocations and the allocation of *PROPVAL.
 
115
 
 
116
   Note that this function does not check the readability of the
 
117
   revision property, but the readability of a path.  The true
 
118
   readability of a revision property is determined by investigating
 
119
   the readability of all changed paths in the revision.  For certain
 
120
   revision properties (e.g. svn:author and svn:date) to be readable,
 
121
   it is enough if at least one changed path is readable.  When we
 
122
   already have a changed path, we can skip the check for the other
 
123
   changed paths in the revision and save a lot of work.  This means
 
124
   that we will make a mistake when our path is unreadable and another
 
125
   changed path is readable, but we will at least only hide too much
 
126
   and not leak any protected properties.
 
127
 
 
128
   WARNING: This method of only checking the readability of a path is
 
129
   only valid to get revision properties for which it is enough if at
 
130
   least one changed path is readable.  Using this function to get
 
131
   revision properties for which all changed paths must be readable
 
132
   might leak protected information because we will only test the
 
133
   readability of a single changed path.
 
134
*/
 
135
static svn_error_t *dav_svn_get_path_revprop(svn_string_t **propval,
 
136
                                             const dav_resource *resource,
 
137
                                             svn_revnum_t committed_rev,
 
138
                                             const char *propname,
 
139
                                             apr_pool_t *pool)
 
140
{
 
141
  dav_svn_authz_read_baton arb;
 
142
  svn_boolean_t allowed;
 
143
  svn_fs_root_t *root;
 
144
 
 
145
  *propval = NULL;
 
146
 
 
147
  arb.r = resource->info->r;
 
148
  arb.repos = resource->info->repos;
 
149
  SVN_ERR(svn_fs_revision_root(&root,
 
150
                               resource->info->repos->fs,
 
151
                               committed_rev, pool));
 
152
  SVN_ERR(dav_svn_authz_read(&allowed,
 
153
                             root,
 
154
                             resource->info->repos_path,
 
155
                             &arb, pool));
 
156
 
 
157
  if (! allowed)
 
158
    return SVN_NO_ERROR;
 
159
 
 
160
  /* Get the property of the created revision. The authz is already
 
161
     performed, so we don't need to do it here too. */
 
162
  return svn_repos_fs_revision_prop(propval,
 
163
                                    resource->info->repos->repos,
 
164
                                    committed_rev,
 
165
                                    propname,
 
166
                                    NULL, NULL, pool);
 
167
}
 
168
 
 
169
static dav_prop_insert dav_svn_insert_prop(const dav_resource *resource,
 
170
                                           int propid, dav_prop_insert what,
 
171
                                           apr_text_header *phdr)
 
172
{
 
173
  const char *value = NULL;
 
174
  const char *s;
 
175
  apr_pool_t *response_pool = resource->pool;
 
176
  apr_pool_t *p = resource->info->pool;
 
177
  const dav_liveprop_spec *info;
 
178
  int global_ns;
 
179
  svn_error_t *serr;
 
180
 
 
181
  /*
 
182
  ** Almost none of the SVN provider properties are defined if the
 
183
  ** resource does not exist.  We do need to return the one VCC
 
184
  ** property and baseline-relative-path on lock-null resources,
 
185
  ** however, so that svn clients can run 'svn unlock' and 'svn info'
 
186
  ** on these things.
 
187
  **
 
188
  ** Even though we state that the SVN properties are not defined, the
 
189
  ** client cannot store dead values -- we deny that thru the is_writable
 
190
  ** hook function.
 
191
  */
 
192
  if ((! resource->exists)
 
193
      && (propid != DAV_PROPID_version_controlled_configuration)
 
194
      && (propid != SVN_PROPID_baseline_relative_path))
 
195
    return DAV_PROP_INSERT_NOTSUPP;
 
196
 
 
197
  /* ### we may want to respond to DAV_PROPID_resourcetype for PRIVATE
 
198
     ### resources. need to think on "proper" interaction with mod_dav */
 
199
 
 
200
  switch (propid)
 
201
    {
 
202
    case DAV_PROPID_getlastmodified:
 
203
    case DAV_PROPID_creationdate:
 
204
      {
 
205
        /* In subversion terms, the date attached to a file's CR is
 
206
           the true "last modified" time.  However, we're defining
 
207
           creationdate in the same way.  IMO, the "creationdate" is
 
208
           really the date attached to the revision in which the item
 
209
           *first* came into existence; this would found by tracing
 
210
           back through the log of the file -- probably via
 
211
           svn_fs_revisions_changed.  gstein, is it a bad thing that
 
212
           we're currently using 'creationdate' to mean the same thing
 
213
           as 'last modified date'?  */
 
214
        const char *datestring;
 
215
        apr_time_t timeval;
 
216
        enum dav_svn_time_format format;
 
217
 
 
218
        /* ### for now, our global VCC has no such property. */
 
219
        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
220
            && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
221
          {
 
222
            return DAV_PROP_INSERT_NOTSUPP;
 
223
          }
 
224
       
 
225
        if (propid == DAV_PROPID_creationdate)
 
226
          {
 
227
            /* Return an ISO8601 date; this is what the svn client
 
228
               expects, and rfc2518 demands it. */
 
229
            format = dav_svn_time_format_iso8601;
 
230
          }
 
231
        else /* propid == DAV_PROPID_getlastmodified */
 
232
          {
 
233
            format = dav_svn_time_format_rfc1123;
 
234
          }
 
235
 
 
236
        if (0 != dav_svn_get_last_modified_time (&datestring, &timeval,
 
237
                                                 resource, format, p))
 
238
          {
 
239
            return DAV_PROP_INSERT_NOTDEF;
 
240
          }
 
241
 
 
242
        value = apr_xml_quote_string(p, datestring, 1);
 
243
        break;
 
244
      }
 
245
 
 
246
    case DAV_PROPID_creator_displayname:
 
247
      {        
 
248
        svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
 
249
        svn_string_t *last_author = NULL;
 
250
 
 
251
        /* ### for now, our global VCC has no such property. */
 
252
        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
253
            && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
254
          {
 
255
            return DAV_PROP_INSERT_NOTSUPP;
 
256
          }
 
257
 
 
258
        if (resource->baselined && resource->type == DAV_RESOURCE_TYPE_VERSION)
 
259
          {
 
260
            /* A baseline URI. */
 
261
            committed_rev = resource->info->root.rev;
 
262
          }
 
263
        else if (resource->type == DAV_RESOURCE_TYPE_REGULAR
 
264
                 || resource->type == DAV_RESOURCE_TYPE_WORKING
 
265
                 || resource->type == DAV_RESOURCE_TYPE_VERSION)
 
266
          {
 
267
            /* Get the CR field out of the node's skel.  Notice that the
 
268
               root object might be an ID root -or- a revision root. */
 
269
            serr = svn_fs_node_created_rev(&committed_rev,
 
270
                                           resource->info->root.root,
 
271
                                           resource->info->repos_path, p);
 
272
            if (serr != NULL)
 
273
              {
 
274
                /* ### what to do? */
 
275
                svn_error_clear(serr);
 
276
                value = "###error###";
 
277
                break;
 
278
              }
 
279
          }        
 
280
        else
 
281
          {
 
282
            return DAV_PROP_INSERT_NOTSUPP;
 
283
          }
 
284
 
 
285
        serr = dav_svn_get_path_revprop(&last_author,
 
286
                                        resource,
 
287
                                        committed_rev,
 
288
                                        SVN_PROP_REVISION_AUTHOR,
 
289
                                        p);
 
290
        if (serr)
 
291
          {
 
292
            /* ### what to do? */
 
293
            svn_error_clear(serr);
 
294
            value = "###error###";
 
295
            break;
 
296
          }
 
297
 
 
298
        if (last_author == NULL)
 
299
          return DAV_PROP_INSERT_NOTDEF;
 
300
 
 
301
        value = apr_xml_quote_string(p, last_author->data, 1);
 
302
        break;
 
303
      }
 
304
 
 
305
    case DAV_PROPID_getcontentlanguage:
 
306
      /* ### need something here */
 
307
      return DAV_PROP_INSERT_NOTSUPP;
 
308
      break;
 
309
 
 
310
    case DAV_PROPID_getcontentlength:
 
311
      {
 
312
        svn_filesize_t len = 0;
 
313
        
 
314
        /* our property, but not defined on collection resources */
 
315
        if (resource->collection || resource->baselined)
 
316
          return DAV_PROP_INSERT_NOTSUPP;
 
317
 
 
318
        serr = svn_fs_file_length(&len, resource->info->root.root,
 
319
                                  resource->info->repos_path, p);
 
320
        if (serr != NULL)
 
321
          {
 
322
            svn_error_clear(serr);
 
323
            value = "0";  /* ### what to do? */
 
324
            break;
 
325
          }
 
326
 
 
327
        value = apr_psprintf(p, "%" SVN_FILESIZE_T_FMT, len);
 
328
        break;
 
329
      }
 
330
 
 
331
    case DAV_PROPID_getcontenttype:
 
332
      {
 
333
        /* The subversion client assumes that any file without an
 
334
           svn:mime-type property is of type text/plain.  So it seems
 
335
           safe (and consistent) to assume the same on the server.  */
 
336
        svn_string_t *pval;
 
337
        const char *mime_type = NULL;
 
338
 
 
339
        if (resource->baselined && resource->type == DAV_RESOURCE_TYPE_VERSION)
 
340
          return DAV_PROP_INSERT_NOTSUPP;
 
341
 
 
342
        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
343
            && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
344
          {
 
345
            return DAV_PROP_INSERT_NOTSUPP;
 
346
          }
 
347
 
 
348
        if (resource->collection) /* defaults for directories */
 
349
          {
 
350
            if (resource->info->repos->xslt_uri)
 
351
              mime_type = "text/xml";
 
352
            else
 
353
              mime_type = "text/html; charset=UTF-8";
 
354
          }
 
355
        else
 
356
          {
 
357
            if ((serr = svn_fs_node_prop (&pval, resource->info->root.root,
 
358
                                          resource->info->repos_path,
 
359
                                          SVN_PROP_MIME_TYPE, p)))
 
360
              {
 
361
                svn_error_clear(serr);
 
362
                pval = NULL;
 
363
              }
 
364
 
 
365
            if (pval)
 
366
              mime_type = pval->data;
 
367
            else if ((! resource->info->repos->is_svn_client) 
 
368
                     && resource->info->r->content_type)
 
369
              mime_type = resource->info->r->content_type;
 
370
            else
 
371
              mime_type = "text/plain"; /* default for file */
 
372
 
 
373
            if ((serr = svn_mime_type_validate (mime_type, p)))
 
374
              {
 
375
                /* Probably serr->apr == SVN_ERR_BAD_MIME_TYPE, but
 
376
                   there's no point even checking.  No matter what the
 
377
                   error is, we can't claim to have a mime type for
 
378
                   this resource. */
 
379
                svn_error_clear(serr);
 
380
                return DAV_PROP_INSERT_NOTDEF;
 
381
              }
 
382
          }
 
383
 
 
384
        value = mime_type;
 
385
        break;
 
386
      }
 
387
 
 
388
    case DAV_PROPID_getetag:
 
389
      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
390
          && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
391
        {
 
392
          return DAV_PROP_INSERT_NOTSUPP;
 
393
        }
 
394
 
 
395
      value = dav_svn_getetag(resource, p);
 
396
      break;
 
397
 
 
398
    case DAV_PROPID_auto_version:
 
399
      /* we only support one autoversioning behavior, and thus only
 
400
         return this one static value; someday when we support
 
401
         locking, there are other possible values/behaviors for this. */
 
402
      if (resource->info->repos->autoversioning)
 
403
        value = "DAV:checkout-checkin";
 
404
      else
 
405
        return DAV_PROP_INSERT_NOTDEF;
 
406
      break;
 
407
 
 
408
    case DAV_PROPID_baseline_collection:
 
409
      /* only defined for Baselines */
 
410
      /* ### whoops. also defined for a VCC. deal with it later. */
 
411
      if (resource->type != DAV_RESOURCE_TYPE_VERSION || !resource->baselined)
 
412
        return DAV_PROP_INSERT_NOTSUPP;
 
413
      value = dav_svn_build_uri(resource->info->repos, DAV_SVN_BUILD_URI_BC,
 
414
                                resource->info->root.rev, NULL,
 
415
                                1 /* add_href */, p);
 
416
      break;
 
417
 
 
418
    case DAV_PROPID_checked_in:
 
419
      /* only defined for VCRs (in the public space and in a BC space) */
 
420
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
 
421
      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
422
          && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
423
        {
 
424
          svn_revnum_t revnum;
 
425
 
 
426
          serr = svn_fs_youngest_rev(&revnum, resource->info->repos->fs, p);
 
427
          if (serr != NULL)
 
428
            {
 
429
              /* ### what to do? */
 
430
              svn_error_clear(serr);
 
431
              value = "###error###";
 
432
              break;
 
433
            }
 
434
          s = dav_svn_build_uri(resource->info->repos,
 
435
                                DAV_SVN_BUILD_URI_BASELINE, 
 
436
                                revnum, NULL, 0 /* add_href */, p);
 
437
          value = apr_psprintf(p, "<D:href>%s</D:href>", 
 
438
                               apr_xml_quote_string(p, s, 1));
 
439
        }
 
440
      else if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
 
441
        {
 
442
          /* not defined for this resource type */
 
443
          return DAV_PROP_INSERT_NOTSUPP;
 
444
        }
 
445
      else
 
446
        {
 
447
          svn_revnum_t rev_to_use =
 
448
            dav_svn_get_safe_cr(resource->info->root.root,
 
449
                                resource->info->repos_path, p);
 
450
 
 
451
          s = dav_svn_build_uri(resource->info->repos,
 
452
                                DAV_SVN_BUILD_URI_VERSION,
 
453
                                rev_to_use, resource->info->repos_path,
 
454
                                0 /* add_href */, p);
 
455
          value = apr_psprintf(p, "<D:href>%s</D:href>", 
 
456
                               apr_xml_quote_string(p, s, 1));
 
457
        }
 
458
      break;
 
459
 
 
460
    case DAV_PROPID_version_controlled_configuration:
 
461
      /* only defined for VCRs */
 
462
      /* ### VCRs within the BC should not have this property! */
 
463
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
 
464
      if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
 
465
        return DAV_PROP_INSERT_NOTSUPP;
 
466
      value = dav_svn_build_uri(resource->info->repos, DAV_SVN_BUILD_URI_VCC,
 
467
                                SVN_IGNORED_REVNUM, NULL, 
 
468
                                1 /* add_href */, p);
 
469
      break;
 
470
 
 
471
    case DAV_PROPID_version_name:
 
472
      /* only defined for Version Resources and Baselines */
 
473
      /* ### whoops. also defined for VCRs. deal with it later. */
 
474
      if ((resource->type != DAV_RESOURCE_TYPE_VERSION)
 
475
          && (! resource->versioned))
 
476
        return DAV_PROP_INSERT_NOTSUPP;
 
477
 
 
478
      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
 
479
          && resource->info->restype == DAV_SVN_RESTYPE_VCC)
 
480
        {
 
481
          return DAV_PROP_INSERT_NOTSUPP;
 
482
        }
 
483
 
 
484
      if (resource->baselined)
 
485
        {
 
486
          /* just the revision number for baselines */
 
487
          value = apr_psprintf(p, "%ld",
 
488
                               resource->info->root.rev);
 
489
        }
 
490
      else
 
491
        {
 
492
          svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
 
493
          
 
494
          /* Get the CR field out of the node's skel.  Notice that the
 
495
             root object might be an ID root -or- a revision root. */
 
496
          serr = svn_fs_node_created_rev(&committed_rev,
 
497
                                         resource->info->root.root,
 
498
                                         resource->info->repos_path, p);
 
499
          if (serr != NULL)
 
500
            {
 
501
              /* ### what to do? */
 
502
              svn_error_clear(serr);
 
503
              value = "###error###";
 
504
              break;
 
505
            }
 
506
          
 
507
          /* Convert the revision into a quoted string */
 
508
          s = apr_psprintf(p, "%ld", committed_rev);
 
509
          value = apr_xml_quote_string(p, s, 1);
 
510
        }
 
511
      break;
 
512
 
 
513
    case SVN_PROPID_baseline_relative_path:
 
514
      /* only defined for VCRs */
 
515
      /* ### VCRs within the BC should not have this property! */
 
516
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
 
517
      if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
 
518
        return DAV_PROP_INSERT_NOTSUPP;
 
519
 
 
520
      /* drop the leading slash, so it is relative */
 
521
      s = resource->info->repos_path + 1;
 
522
      value = apr_xml_quote_string(p, s, 1);
 
523
      break;
 
524
 
 
525
    case SVN_PROPID_md5_checksum:
 
526
      if ((! resource->collection)
 
527
          && (! resource->baselined)
 
528
          && (resource->type == DAV_RESOURCE_TYPE_REGULAR
 
529
              || resource->type == DAV_RESOURCE_TYPE_WORKING
 
530
              || resource->type == DAV_RESOURCE_TYPE_VERSION))
 
531
        {
 
532
          unsigned char digest[APR_MD5_DIGESTSIZE];
 
533
 
 
534
          serr = svn_fs_file_md5_checksum(digest,
 
535
                                          resource->info->root.root,
 
536
                                          resource->info->repos_path, p);
 
537
          if (serr != NULL)
 
538
            {
 
539
              /* ### what to do? */
 
540
              svn_error_clear(serr);
 
541
              value = "###error###";
 
542
              break;
 
543
            }
 
544
 
 
545
          value = svn_md5_digest_to_cstring (digest, p);
 
546
 
 
547
          if (! value)
 
548
            return DAV_PROP_INSERT_NOTSUPP;
 
549
        }
 
550
      else
 
551
        return DAV_PROP_INSERT_NOTSUPP;
 
552
 
 
553
      break;
 
554
 
 
555
    case SVN_PROPID_repository_uuid:
 
556
      serr = svn_fs_get_uuid(resource->info->repos->fs, &value, p);
 
557
      if (serr != NULL)
 
558
        {
 
559
          /* ### what to do? */
 
560
          svn_error_clear(serr);
 
561
          value = "###error###";
 
562
          break;
 
563
        }
 
564
      break;
 
565
 
 
566
    default:
 
567
      /* ### what the heck was this property? */
 
568
      return DAV_PROP_INSERT_NOTDEF;
 
569
    }
 
570
 
 
571
  /* assert: value != NULL */
 
572
 
 
573
  /* get the information and global NS index for the property */
 
574
  global_ns = dav_get_liveprop_info(propid, &dav_svn_liveprop_group, &info);
 
575
 
 
576
  /* assert: info != NULL && info->name != NULL */
 
577
 
 
578
  if (what == DAV_PROP_INSERT_NAME
 
579
      || (what == DAV_PROP_INSERT_VALUE && *value == '\0')) {
 
580
    s = apr_psprintf(response_pool, "<lp%d:%s/>" DEBUG_CR, global_ns,
 
581
                     info->name);
 
582
  }
 
583
  else if (what == DAV_PROP_INSERT_VALUE) {
 
584
    s = apr_psprintf(response_pool, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
 
585
                     global_ns, info->name, value, global_ns, info->name);
 
586
  }
 
587
  else {
 
588
    /* assert: what == DAV_PROP_INSERT_SUPPORTED */
 
589
    s = apr_psprintf(response_pool,
 
590
                     "<D:supported-live-property D:name=\"%s\" "
 
591
                     "D:namespace=\"%s\"/>" DEBUG_CR,
 
592
                     info->name, dav_svn_namespace_uris[info->ns]);
 
593
  }
 
594
  apr_text_append(response_pool, phdr, s);
 
595
 
 
596
  /* we inserted whatever was asked for */
 
597
  return what;
 
598
}
 
599
 
 
600
static int dav_svn_is_writable(const dav_resource *resource, int propid)
 
601
{
 
602
  const dav_liveprop_spec *info;
 
603
 
 
604
  (void) dav_get_liveprop_info(propid, &dav_svn_liveprop_group, &info);
 
605
  return info->is_writable;
 
606
}
 
607
 
 
608
static dav_error * dav_svn_patch_validate(const dav_resource *resource,
 
609
                                          const apr_xml_elem *elem,
 
610
                                          int operation, void **context,
 
611
                                          int *defer_to_dead)
 
612
{
 
613
  /* NOTE: this function will not be called unless/until we have
 
614
     modifiable (writable) live properties. */
 
615
  return NULL;
 
616
}
 
617
 
 
618
static dav_error * dav_svn_patch_exec(const dav_resource *resource,
 
619
                                      const apr_xml_elem *elem,
 
620
                                      int operation, void *context,
 
621
                                      dav_liveprop_rollback **rollback_ctx)
 
622
{
 
623
  /* NOTE: this function will not be called unless/until we have
 
624
     modifiable (writable) live properties. */
 
625
  return NULL;
 
626
}
 
627
 
 
628
static void dav_svn_patch_commit(const dav_resource *resource,
 
629
                                 int operation, void *context,
 
630
                                 dav_liveprop_rollback *rollback_ctx)
 
631
{
 
632
  /* NOTE: this function will not be called unless/until we have
 
633
     modifiable (writable) live properties. */
 
634
}
 
635
 
 
636
static dav_error * dav_svn_patch_rollback(const dav_resource *resource,
 
637
                                          int operation, void *context,
 
638
                                          dav_liveprop_rollback *rollback_ctx)
 
639
{
 
640
  /* NOTE: this function will not be called unless/until we have
 
641
     modifiable (writable) live properties. */
 
642
  return NULL;
 
643
}
 
644
 
 
645
const dav_hooks_liveprop dav_svn_hooks_liveprop = {
 
646
  dav_svn_insert_prop,
 
647
  dav_svn_is_writable,
 
648
  dav_svn_namespace_uris,
 
649
  dav_svn_patch_validate,
 
650
  dav_svn_patch_exec,
 
651
  dav_svn_patch_commit,
 
652
  dav_svn_patch_rollback,
 
653
};
 
654
 
 
655
void dav_svn_gather_propsets(apr_array_header_t *uris)
 
656
{
 
657
  /* ### what should we use for a URL to describe the available prop set? */
 
658
  /* ### for now... nothing. we will *only* have DAV properties */
 
659
#if 0
 
660
    *(const char **)apr_array_push(uris) =
 
661
        "<http://subversion.tigris.org/dav/propset/svn/1>";
 
662
#endif
 
663
}
 
664
 
 
665
int dav_svn_find_liveprop(const dav_resource *resource,
 
666
                          const char *ns_uri, const char *name,
 
667
                          const dav_hooks_liveprop **hooks)
 
668
{
 
669
  /* don't try to find any liveprops if this isn't "our" resource */
 
670
  if (resource->hooks != &dav_svn_hooks_repos)
 
671
    return 0;
 
672
 
 
673
  return dav_do_find_liveprop(ns_uri, name, &dav_svn_liveprop_group, hooks);
 
674
}
 
675
 
 
676
void dav_svn_insert_all_liveprops(request_rec *r, const dav_resource *resource,
 
677
                                  dav_prop_insert what, apr_text_header *phdr)
 
678
{
 
679
    const dav_liveprop_spec *spec;
 
680
    apr_pool_t *pool;
 
681
    apr_pool_t *subpool;
 
682
 
 
683
    /* don't insert any liveprops if this isn't "our" resource */
 
684
    if (resource->hooks != &dav_svn_hooks_repos)
 
685
        return;
 
686
 
 
687
    if (!resource->exists) {
 
688
        /* a lock-null resource */
 
689
        /*
 
690
        ** ### technically, we should insert empty properties. dunno offhand
 
691
        ** ### what part of the spec said this, but it was essentially thus:
 
692
        ** ### "the properties should be defined, but may have no value".
 
693
        */
 
694
        return;
 
695
    }
 
696
 
 
697
    pool = resource->info->pool;
 
698
    subpool = svn_pool_create(pool);
 
699
    resource->info->pool = subpool;
 
700
 
 
701
    for (spec = dav_svn_props; spec->name != NULL; ++spec)
 
702
      {
 
703
        svn_pool_clear(subpool);
 
704
        (void) dav_svn_insert_prop(resource, spec->propid, what, phdr);
 
705
      }
 
706
 
 
707
    resource->info->pool = pool;
 
708
    svn_pool_destroy(subpool);
 
709
 
 
710
    /* ### we know the others aren't defined as liveprops */
 
711
}
 
712
 
 
713
void dav_svn_register_uris(apr_pool_t *p)
 
714
{
 
715
    /* register the namespace URIs */
 
716
    dav_register_liveprop_group(p, &dav_svn_liveprop_group);
 
717
}
 
718
 
 
719
 
 
720
int dav_svn_get_last_modified_time (const char **datestring,
 
721
                                    apr_time_t *timeval,
 
722
                                    const dav_resource *resource,
 
723
                                    enum dav_svn_time_format format,
 
724
                                    apr_pool_t *pool)
 
725
{
 
726
  svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
 
727
  svn_string_t *committed_date = NULL;
 
728
  svn_error_t *serr;
 
729
  apr_time_t timeval_tmp;
 
730
 
 
731
  if ((datestring == NULL) && (timeval == NULL))
 
732
    return 0;
 
733
 
 
734
  if (resource->baselined && resource->type == DAV_RESOURCE_TYPE_VERSION)
 
735
    {
 
736
      /* A baseline URI. */
 
737
      committed_rev = resource->info->root.rev;
 
738
    }
 
739
  else if (resource->type == DAV_RESOURCE_TYPE_REGULAR
 
740
           || resource->type == DAV_RESOURCE_TYPE_WORKING
 
741
           || resource->type == DAV_RESOURCE_TYPE_VERSION)
 
742
    {
 
743
      serr = svn_fs_node_created_rev(&committed_rev,
 
744
                                     resource->info->root.root,
 
745
                                     resource->info->repos_path, pool);
 
746
      if (serr != NULL)
 
747
        {
 
748
          svn_error_clear(serr);
 
749
          return 1;
 
750
        }
 
751
    }
 
752
  else
 
753
    {
 
754
      /* unsupported resource kind -- has no mod-time */
 
755
      return 1;
 
756
    }
 
757
 
 
758
  serr = dav_svn_get_path_revprop(&committed_date,
 
759
                                  resource,
 
760
                                  committed_rev,
 
761
                                  SVN_PROP_REVISION_DATE,
 
762
                                  pool);
 
763
  if (serr)
 
764
    {
 
765
      svn_error_clear(serr);
 
766
      return 1;
 
767
    }
 
768
 
 
769
  if (committed_date == NULL)
 
770
    return 1;
 
771
 
 
772
  /* return the ISO8601 date as an apr_time_t */
 
773
  serr = svn_time_from_cstring(&timeval_tmp, committed_date->data, pool);
 
774
  if (serr != NULL)
 
775
    {
 
776
      svn_error_clear(serr);
 
777
      return 1;
 
778
    }
 
779
 
 
780
  if (timeval)
 
781
    memcpy(timeval, &timeval_tmp, sizeof(*timeval));
 
782
 
 
783
  if (! datestring)
 
784
    return 0;
 
785
 
 
786
  if (format == dav_svn_time_format_iso8601)
 
787
    {
 
788
      *datestring = committed_date->data;
 
789
    }
 
790
  else if (format == dav_svn_time_format_rfc1123)
 
791
    {
 
792
      apr_time_exp_t tms;
 
793
      apr_status_t status;
 
794
      
 
795
      /* convert the apr_time_t into an apr_time_exp_t */
 
796
      status = apr_time_exp_gmt(&tms, timeval_tmp);
 
797
      if (status != APR_SUCCESS)
 
798
        return 1;
 
799
              
 
800
      /* stolen from dav/fs/repos.c   :-)  */
 
801
      *datestring = apr_psprintf(pool, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
 
802
                                 apr_day_snames[tms.tm_wday],
 
803
                                 tms.tm_mday, apr_month_snames[tms.tm_mon],
 
804
                                 tms.tm_year + 1900,
 
805
                                 tms.tm_hour, tms.tm_min, tms.tm_sec);      
 
806
    }
 
807
  else /* unknown time format */
 
808
    {
 
809
      return 1;
 
810
    }
 
811
 
 
812
  return 0;
 
813
}