~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/mod_dav_svn/merge.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
 * merge.c: handle the MERGE response processing
 
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
#include <apr_pools.h>
 
20
#include <apr_buckets.h>
 
21
#include <apr_xml.h>
 
22
#include <apr_hash.h>
 
23
 
 
24
#include <httpd.h>
 
25
#include <util_filter.h>
 
26
 
 
27
#include "svn_pools.h"
 
28
#include "svn_fs.h"
 
29
#include "svn_props.h"
 
30
 
 
31
#include "dav_svn.h"
 
32
 
 
33
 
 
34
/* #################################################################
 
35
 
 
36
   These functions are currently *VERY* SVN specific.
 
37
 
 
38
   * we don't check prop_elem for what the client requested
 
39
   * we presume a baseline was checked out into the activity, and is
 
40
     part of the MERGE
 
41
   * we presume that all "changed" files/dirs were checked out into
 
42
     the activity and are part of the MERGE
 
43
     (not sure if this is SVN specific; I can't see how a file/dir
 
44
      would be part of the new revision if a working resource had
 
45
      not been created for it)
 
46
   * we return some props for some resources, and a different set for
 
47
     other resources (to keep the wire smaller for now)
 
48
 
 
49
   At some point in the future, we'll want to make this "real". Especially
 
50
   for proper interoperability.
 
51
 
 
52
   #################################################################
 
53
*/
 
54
 
 
55
 
 
56
 
 
57
/* -------------------------------------------------------------------------
 
58
   PRIVATE HELPER FUNCTIONS
 
59
*/
 
60
 
 
61
/* send a response to the client for this baton */
 
62
static svn_error_t *send_response(const dav_svn_repos *repos,
 
63
                                  svn_fs_root_t *root,
 
64
                                  const char *path,
 
65
                                  svn_boolean_t is_dir,
 
66
                                  ap_filter_t *output,
 
67
                                  apr_bucket_brigade *bb,
 
68
                                  apr_pool_t *pool)
 
69
{
 
70
  const char *href;
 
71
  const char *vsn_url;
 
72
  apr_status_t status;
 
73
  svn_revnum_t rev_to_use;
 
74
 
 
75
  href = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_PUBLIC,
 
76
                           SVN_IGNORED_REVNUM, path, 0 /* add_href */, pool);
 
77
  rev_to_use = dav_svn_get_safe_cr(root, path, pool);
 
78
  vsn_url = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_VERSION,
 
79
                              rev_to_use, path, 0 /* add_href */, pool);
 
80
  status = ap_fputstrs(output, bb,
 
81
                       "<D:response>" DEBUG_CR
 
82
                       "<D:href>", 
 
83
                       apr_xml_quote_string(pool, href, 1),
 
84
                       "</D:href>" DEBUG_CR
 
85
                       "<D:propstat><D:prop>" DEBUG_CR,
 
86
                       is_dir 
 
87
                         ? "<D:resourcetype><D:collection/></D:resourcetype>"
 
88
                         : "<D:resourcetype/>", 
 
89
                       DEBUG_CR,
 
90
                       "<D:checked-in><D:href>",
 
91
                       apr_xml_quote_string(pool, vsn_url, 1),
 
92
                       "</D:href></D:checked-in>" DEBUG_CR
 
93
                       "</D:prop>" DEBUG_CR
 
94
                       "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
 
95
                       "</D:propstat>" DEBUG_CR
 
96
                       "</D:response>" DEBUG_CR,
 
97
                       NULL);
 
98
  if (status != APR_SUCCESS)
 
99
    return svn_error_wrap_apr(status, "Can't write response to output");
 
100
 
 
101
  return SVN_NO_ERROR;
 
102
}
 
103
 
 
104
 
 
105
static svn_error_t *do_resources(const dav_svn_repos *repos,
 
106
                                 svn_fs_root_t *root, 
 
107
                                 svn_revnum_t revision, 
 
108
                                 ap_filter_t *output,
 
109
                                 apr_bucket_brigade *bb,
 
110
                                 apr_pool_t *pool)
 
111
{
 
112
  apr_hash_t *changes;
 
113
  apr_hash_t *sent = apr_hash_make(pool);
 
114
  apr_hash_index_t *hi;
 
115
  apr_pool_t *subpool = svn_pool_create(pool);
 
116
 
 
117
  /* Fetch the paths changed in this revision.  This will contain
 
118
     everything except otherwise-unchanged parent directories of added
 
119
     and deleted things.  Also, note that deleted things don't merit
 
120
     responses of their own -- they are considered modifications to
 
121
     their parent.  */
 
122
  SVN_ERR( svn_fs_paths_changed(&changes, root, pool) );
 
123
 
 
124
  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
 
125
    {
 
126
      const void *key;
 
127
      void *val;
 
128
      const char *path;
 
129
      svn_fs_path_change_t *change;
 
130
      svn_boolean_t send_self;
 
131
      svn_boolean_t send_parent;
 
132
 
 
133
      svn_pool_clear(subpool);
 
134
      apr_hash_this(hi, &key, NULL, &val);
 
135
      path = key;
 
136
      change = val;
 
137
 
 
138
      /* Figure out who needs to get sent. */
 
139
      switch (change->change_kind)
 
140
        {
 
141
        case svn_fs_path_change_delete:
 
142
          send_self = FALSE;
 
143
          send_parent = TRUE;
 
144
          break;
 
145
 
 
146
        case svn_fs_path_change_add:
 
147
        case svn_fs_path_change_replace:
 
148
          send_self = TRUE;
 
149
          send_parent = TRUE;
 
150
          break;
 
151
 
 
152
        case svn_fs_path_change_modify:
 
153
        default:
 
154
          send_self = TRUE;
 
155
          send_parent = FALSE;
 
156
          break;
 
157
        }
 
158
 
 
159
      if (send_self)
 
160
        {
 
161
          /* If we haven't already sent this path, send it (and then
 
162
             remember that we sent it). */
 
163
          if (! apr_hash_get(sent, path, APR_HASH_KEY_STRING))
 
164
            {
 
165
              svn_node_kind_t kind;
 
166
              SVN_ERR( svn_fs_check_path(&kind, root, path, subpool) );
 
167
              SVN_ERR( send_response(repos, root, path,
 
168
                                     kind == svn_node_dir ? TRUE : FALSE, 
 
169
                                     output, bb, subpool) );
 
170
              apr_hash_set(sent, path, APR_HASH_KEY_STRING, (void *)1);
 
171
            }
 
172
        }
 
173
      if (send_parent)
 
174
        {
 
175
          /* If it hasn't already been sent, send the parent directory
 
176
             (and then remember that you sent it).  Allocate parent in
 
177
             pool, not subpool, because it stays in the sent hash
 
178
             afterwards. */
 
179
          const char *parent = svn_path_dirname(path, pool);
 
180
          if (! apr_hash_get(sent, parent, APR_HASH_KEY_STRING))
 
181
            {
 
182
              SVN_ERR( send_response(repos, root, parent, 
 
183
                                     TRUE, output, bb, subpool) );
 
184
              apr_hash_set(sent, parent, APR_HASH_KEY_STRING, (void *)1);
 
185
            }
 
186
        }
 
187
    }
 
188
 
 
189
  svn_pool_destroy(subpool);
 
190
 
 
191
  return SVN_NO_ERROR;
 
192
}
 
193
 
 
194
 
 
195
 
 
196
/* -------------------------------------------------------------------------
 
197
   PUBLIC FUNCTIONS
 
198
*/
 
199
 
 
200
dav_error * dav_svn__merge_response(ap_filter_t *output,
 
201
                                    const dav_svn_repos *repos,
 
202
                                    svn_revnum_t new_rev,
 
203
                                    apr_xml_elem *prop_elem,
 
204
                                    svn_boolean_t disable_merge_response,
 
205
                                    apr_pool_t *pool)
 
206
{
 
207
  apr_bucket_brigade *bb;
 
208
  svn_fs_root_t *root;
 
209
  svn_error_t *serr;
 
210
  const char *vcc;
 
211
  const char *rev;
 
212
  svn_string_t *creationdate, *creator_displayname;
 
213
 
 
214
  serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool);
 
215
  if (serr != NULL)
 
216
    {
 
217
      return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
 
218
                                 "Could not open the FS root for the "
 
219
                                 "revision just committed.",
 
220
                                 repos->pool);
 
221
    }
 
222
 
 
223
  bb = apr_brigade_create(pool, output->c->bucket_alloc);
 
224
 
 
225
  /* prep some strings */
 
226
  
 
227
  /* the HREF for the baseline is actually the VCC */
 
228
  vcc = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_VCC, SVN_IGNORED_REVNUM,
 
229
                          NULL, 0 /* add_href */, pool);
 
230
 
 
231
  /* the version-name of the baseline is the revision number */
 
232
  rev = apr_psprintf(pool, "%ld", new_rev);
 
233
 
 
234
  /* get the creationdate and creator-displayname of the new revision, too. */
 
235
  serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev,
 
236
                              SVN_PROP_REVISION_DATE, pool);
 
237
  if (serr != NULL)
 
238
    {
 
239
      return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
 
240
                                 "Could not get date of newest revision",
 
241
                                 repos->pool); 
 
242
    }
 
243
  serr = svn_fs_revision_prop(&creator_displayname, repos->fs, new_rev,
 
244
                              SVN_PROP_REVISION_AUTHOR, pool);
 
245
  if (serr != NULL)
 
246
    {
 
247
      return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
 
248
                                 "Could not get author of newest revision",
 
249
                                 repos->pool); 
 
250
    }
 
251
 
 
252
 
 
253
  (void) ap_fputstrs(output, bb,
 
254
                     DAV_XML_HEADER DEBUG_CR
 
255
                     "<D:merge-response xmlns:D=\"DAV:\">" DEBUG_CR
 
256
                     "<D:updated-set>" DEBUG_CR
 
257
 
 
258
                     /* generate a response for the new baseline */
 
259
                     "<D:response>" DEBUG_CR
 
260
                     "<D:href>", 
 
261
                     apr_xml_quote_string(pool, vcc, 1),
 
262
                     "</D:href>" DEBUG_CR
 
263
                     "<D:propstat><D:prop>" DEBUG_CR
 
264
                     /* ### this is wrong. it's a VCC, not a baseline. but
 
265
                        ### we need to tell the client to look at *this*
 
266
                        ### resource for the version-name. */
 
267
                     "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR
 
268
                     "<D:version-name>", rev, "</D:version-name>" DEBUG_CR,
 
269
                     NULL);
 
270
  if (creationdate)
 
271
    {
 
272
      (void) ap_fputstrs(output, bb,
 
273
                         "<D:creationdate>", 
 
274
                         apr_xml_quote_string(pool, creationdate->data, 1),
 
275
                         "</D:creationdate>" DEBUG_CR,
 
276
                         NULL);
 
277
    }
 
278
  if (creator_displayname)
 
279
    {
 
280
      (void) ap_fputstrs(output, bb,
 
281
                         "<D:creator-displayname>", 
 
282
                         apr_xml_quote_string(pool, 
 
283
                                              creator_displayname->data, 1),
 
284
                         "</D:creator-displayname>" DEBUG_CR,
 
285
                         NULL);
 
286
    }
 
287
  (void) ap_fputstrs(output, bb,
 
288
                     "</D:prop>" DEBUG_CR
 
289
                     "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
 
290
                     "</D:propstat>" DEBUG_CR
 
291
                     "</D:response>" DEBUG_CR,
 
292
 
 
293
                     NULL);
 
294
 
 
295
  /* ONLY have dir_delta drive the editor if the caller asked us to
 
296
     generate a full MERGE response.  svn clients can ask us to
 
297
     suppress this walk by sending specific request headers. */
 
298
  if (! disable_merge_response)
 
299
    {
 
300
      /* Now we need to generate responses for all the resources which
 
301
         changed.  This is done through a delta of the two roots.
 
302
         
 
303
         Note that a directory is not marked when open_dir is seen
 
304
         (since it typically is used just for changing members in that
 
305
         directory); instead, we want for a property change (the only
 
306
         reason the client would need to fetch a new directory).
 
307
         
 
308
         ### we probably should say something about the dirs, so that
 
309
         ### we can pass back the new version URL */
 
310
      
 
311
      /* and go make me proud, boy! */
 
312
      serr = do_resources(repos, root, new_rev, output, bb, pool);
 
313
      if (serr != NULL)
 
314
        {
 
315
          return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
 
316
                                     "Error constructing resource list.",
 
317
                                     repos->pool);
 
318
        }
 
319
    }
 
320
 
 
321
  /* wrap up the merge response */
 
322
  (void) ap_fputs(output, bb,
 
323
                  "</D:updated-set>" DEBUG_CR
 
324
                  "</D:merge-response>" DEBUG_CR);
 
325
 
 
326
  /* send whatever is left in the brigade */
 
327
  (void) ap_pass_brigade(output, bb);
 
328
 
 
329
  return SVN_NO_ERROR;
 
330
}